文章目录

  • 1. 前言
  • 2. 键盘键值定义
  • 3 状态机的编写
    • 3.1状态机的状态划分
    • 3.2 状态机所需的信号
    • 3.3 状态转换
    • 3.4 输出计算
  • 4. testbench测试

1. 前言

前言部分对全文无关紧要,可直接跳过。

距离1月9日发布的上一篇博客已经过去大约一个月的时间,期间旅游、过年以及各种事项接踵而至,并且更要命的是,矩阵4x4键盘不论如何调试,都会出问题。根据指导人所说的修改方案,即加上上拉电阻,问题依旧存在——状态机的跳转不如所愿,不该出现数据的信号出现了数据等——起初,我以为是我自己的问题,拼命找bug,代码反复修改多次,无疾而终,这便花去了大概五六天的时间;之后用梅雪松(梅哥)的代码加上SignalTap进行调试观察,出人意料的是,他的输出,比如多按几下按键S6,输出的数据会跳变到4或者7,再跳变成6,或者干脆不跳变了,直接是4或者7。比照他的代码,修改,润色,效果依然不理想。这里并不是说梅哥的代码有问题,我相信他的代码肯定是经过自己层层修改并验证过的,只是自己庶竭驽钝。看到配件箱中有个红外键盘,想了想,两者应该能达到同样的效果,并且在我看来,红外键盘比矩阵4x4键盘至少有如下两个好处:

  1. 拿来即用,且在一定范围内可以持续使用,省去接线等不必要的繁琐工作;
  2. 红外键盘按键有21个,比4x4的矩阵键盘多出5个按键,虽然说多出来的不一定能够用到,但是多一些总该是有点好处的;

出于对时间流逝的感到不安、焦虑,又出于想要尽快得到按键正确输出的目的,最终,决定弃用矩阵键盘,改用红外键盘。

这也是为什么上一篇博客分明是与矩阵键盘相关,而此篇博客又叙述红外键盘之内容的原因。

2. 键盘键值定义

虽说换了个按键终端,但是主题还是没有变——依旧是针对DDS的频率控制字fwd以及相位控制字pwd,进行设计。

比如,想让DDS输出的频率为1000Hz,那么在红外键盘上,分别按下1000后,再按OK键,即可得到相应的以1000Hz的频率输出的正弦波。

实际上我们用到的并不只是一个DDS,而是双通道的DDS,也就是说,一个DDS有两个输出通道,可以同时输出两个完全不相关的正弦波信号。可以想象成有两条路,一个车开得快,一个车开得慢这种。而每一个通道的输入都有两个控制字——频率控制字与相位控制字——故双通道就有四个控制字,分别为频率控制字fwd_afwd_b,相位控制字pwd_apwd_b。所以,除了输出相应的频率数据之外,还得对通道A和通道B进行选择。由于按下按键,FPGA开发板并不知道每个按键所对应的操作,这就需要对红外键盘每个按键的输出进行定义。

红外键盘外观如下:

根据《FPGA自学笔记——设计与验证》第5.4章节,编写出相应的RTL设计文件,上板调试并验证可得每个按键所对应的键值,如下表左边两侧所示。

进行状态划分之前,要先定义每个按键所对应的操作。设定如下:

键盘按键 按键操作
EQ OK
CH 设定相位控制字
CH- 选定通道A并设定频率控制字
CH+ 选定通道B并设定频率控制字
- 回退

3 状态机的编写

3.1状态机的状态划分

考虑到按键使用的实际情况,将状态划分如下:

状态 符号
空闲 IDLE 0
接收频率控制字 RECEIVE_FWD 1
过渡 TRANSITION 2
接收相位控制字 RECEIVE_PWD 3
读取数据 READ 4
产生标志信号 FLAG 5

每个状态的状态跳转条件如下:

IDLE状态下,等待通道A或通道B设置按键,若有,则进入RECEIVE_FWD状态;
若在此状态下按回退键,则依然保持IDLE

RECEIVE_FWD状态下,
在按下数字按键之前,按下回退键,则说明按下通道A设置按键按错了,则回到IDLE
在按下数字按键之后,按下回退键,则说明想要清除上一步输入的数据;
等待各种数字按键,
若按下OK键,说明不需要设置相位控制字,直接进入READ状态;
若按下CH键,则说明需要设置相位控制字,进入过渡状态TRANSITION

TARNSITION状态下直接进入RECEIVE_PWD状态;

RECEIVE_PWD状态下,等待各种数字按键,当按下OK后,说明频率控制字和相位控制字已设置完毕,进入READ状态;

READ状态下,读取数据,并进入FLAG状态;

FLAG状态,产生操作完成的out_flag信号,并回到IDLE状态。
其实产生标志信号的操作在READ状态下就能够完成,此处为了理得更清楚,多加一个状态。

3.2 状态机所需的信号

状态机各种信号定义如下:

/*=============================================================================
#     FileName: key_FSM.v
#         Desc:
#       Author: ohliver
#        Email: ohliver@foxmail.com
#     HomePage: https://blog.csdn.net/qq_15062763
#      Version: 0.0.1
#   LastChange: 2020-02-02 20:02:12
#      History:
=============================================================================*/
module key_FSM(//Iclk_50M   ,rstn       ,in_addr        ,//这是红外键盘每个按键所对应的按键地址in_data        ,//这是红外键盘每个按键所对应的按键数据in_flag     ,//这是红外键盘解码完成后所发出的标志信号//Ofwd_a       ,fwd_b       ,pwd_a       ,pwd_b       ,out_flag     //这是状态机跳转完成发出的标志信号
);input          clk_50M    ;
input        rstn       ;
input [15:0] in_addr    ;
input [15:0] in_data    ;
input        in_flag    ;output reg [63:0] fwd_a     ;
output reg [63:0] fwd_b     ;
output reg [18:0] pwd_a     ;  //为什么是19位而不是32位
output reg [18:0] pwd_b     ;  //因为FPGA上没有那么多IO端口,所以必须少分配点。
output reg        out_flag  ;parameter IDLE             = 4'd0;
parameter RECEIVE_FWD   = 4'd1;
parameter TRANSITION    = 4'd2;
parameter RECEIVE_PWD   = 4'd3;
parameter READ          = 4'd4;
parameter FLAG          = 4'd5;reg [ 3:0] state       ;
reg [ 3:0] n_state      ;reg        channel_a    ;
reg        channel_b    ;
reg        in_flag_r    ;
reg [ 3:0] key_value_tmp;reg [31:0] key_value_fout;
reg [31:0] read_fout     ;
reg [31:0] key_value_pout;
reg [31:0] read_pout     ;wire [31:0] key_data = {in_data, in_addr}; /*
说明:这里为什么需要将in_flag信号打一拍?
假设一个时钟周期为20ns(实际上也是20ns);
比如按下一次1键,则对应的key_data == 32'hF30CFF00; 信号key_one拉高
又假设我们按下一次按键需要10ms,而在这10ms内,key_data一直为F30CFF00,也就是说,信号key_one在10ms内一直拉高。
如果直接用key_one信号进行按键1的输入,则对于FPGA来说,它会认为我们按下了10ms/20ns =  500000次按键1,
而我们分明只是按了一次
所以,需要一个时钟周期的脉冲信号,对key_one的高电平进行限制,让我们按下一次按键1,FPGA也就只收到一次按键1.很巧的是,按下按键1之后,红外键盘对应的解码模块在解码完成后,正好产生一个时钟周期的标志信号flag,在此文件中对应输入的信号 input in_flag;
同时,解码模块在产生标志信号flag的下一个时钟周期,才知道按键1被按下,也就是说,key_one是在flag(flag等同于in_flag)信号产生后的下一拍才会被拉高。
故需要将in_flag信号往后延迟一拍,变成in_flag_r信号,并和信号key_one进行逻辑与,产生出我们所需要的一个时钟周期的按键输入。为了节省资源,将数字按键0~9,合起来一起与上in_flag_r,即为:
wire number = in_flag_r && (key_zero  || key_one  || key_two  || key_three || key_four || key_five ||key_six   || key_seven|| key_eight|| key_nine);
*/always @ (posedge clk_50M or negedge rstn) beginif (!rstn)in_flag_r <= 1'b0;else in_flag_r <= in_flag;
endwire key_ch       = in_flag_r && (key_data == 32'hB946FF00);  //CH
wire key_ch_minus = in_flag_r && (key_data == 32'hBA45FF00);  //CH-
wire key_ch_plus  = in_flag_r && (key_data == 32'hB847FF00);  //CH+
wire key_eq       = in_flag_r && (key_data == 32'hF609FF00);  //EQ
wire key_minus    = in_flag_r && (key_data == 32'hF807FF00);  //-
wire key_zero     =             (key_data == 32'hE916FF00);
wire key_one      =             (key_data == 32'hF30CFF00);
wire key_two      =             (key_data == 32'hE718FF00);
wire key_three    =             (key_data == 32'hA15EFF00);
wire key_four     =             (key_data == 32'hF708FF00);
wire key_five     =             (key_data == 32'hE31CFF00);
wire key_six      =             (key_data == 32'hA55AFF00);
wire key_seven    =             (key_data == 32'hBD42FF00);
wire key_eight    =             (key_data == 32'hAD52FF00);
wire key_nine     =             (key_data == 32'hB54AFF00);wire number = in_flag_r && (key_zero  || key_one  || key_two  || key_three || key_four || key_five ||key_six   || key_seven|| key_eight|| key_nine);//对按键所代表的数值进行解码
always @ (*) begincase (key_data)32'hE916FF00: key_value_tmp = 4'd0; 32'hF30CFF00: key_value_tmp = 4'd1; 32'hE718FF00: key_value_tmp = 4'd2;32'hA15EFF00: key_value_tmp = 4'd3;32'hF708FF00: key_value_tmp = 4'd4;32'hE31CFF00: key_value_tmp = 4'd5;32'hA55AFF00: key_value_tmp = 4'd6;32'hBD42FF00: key_value_tmp = 4'd7;32'hAD52FF00: key_value_tmp = 4'd8;32'hB54AFF00: key_value_tmp = 4'd9;default     : key_value_tmp = 4'd0; endcase
endwire fout_zero = (key_value_fout == 0);  //定义key_value_fout=0
wire pout_zero = (key_value_pout == 0);  //定义key_value_pout=0/*
定义寄存器channel_a和channel_b的意义是
对通道A和通道B的fwd和pwd进行赋值
详见最后20行的代码
*/
always @ (posedge clk_50M or negedge rstn) beginif (!rstn) channel_a <= 1'b0;else if (key_ch_minus)channel_a <= 1'b1;else if (state == IDLE)channel_a <= 1'b0;elsechannel_a <= channel_a;
endalways @ (posedge clk_50M or negedge rstn) beginif (!rstn) channel_b <= 1'b0;else if (key_ch_plus)channel_b <= 1'b1;else if (state == IDLE)channel_b <= 1'b0;elsechannel_b <= channel_b;
end
3.3 状态转换

状态转换图如下:
三段式状态机编写如下:

//=================================================
//FSM part one
//=================================================
always @ (posedge clk_50M or negedge rstn) beginif (!rstn)state <= IDLE;elsestate <= n_state;
end//==================================================
//FSM part two
//==================================================
always @ (*) begincase (state)IDLE: if (key_ch_minus || key_ch_plus)n_state = RECEIVE_FWD;else if (key_minus)n_state = IDLE;elsen_state = IDLE;RECEIVE_FWD:if (key_minus && fout_zero) n_state = IDLE;else if (key_minus && (!fout_zero))  n_state = RECEIVE_FWD;else if (key_eq)n_state = READ;else if (key_ch)n_state = TRANSITION;elsen_state = RECEIVE_FWD;TRANSITION: n_state = RECEIVE_PWD;RECEIVE_PWD:if (key_minus && pout_zero)n_state = TRANSITION;else if (key_minus && (!pout_zero))n_state = RECEIVE_PWD;else if (key_eq)n_state = READ;elsen_state = RECEIVE_PWD;READ: n_state = FLAG;FLAG: n_state = IDLE;default: n_state = IDLE;endcase
end//==================================================
//FSM part three
//==================================================
always @ (posedge clk_50M or negedge rstn) beginif (!rstn) beginkey_value_fout   <= 32'b0;read_fout        <= 32'b0;key_value_pout   <= 19'b0;  read_pout        <= 19'b0;  out_flag         <= 1'b0 ;endelse begincase (state)IDLE:beginkey_value_fout   <= 32'b0;key_value_pout   <= 19'b0;  out_flag         <= 1'b0 ;read_fout        <= read_fout;read_pout        <= read_pout;            endRECEIVE_FWD: beginif (number)key_value_fout <= (key_value_fout<<1) + (key_value_fout<<3) + key_value_tmp;else if (key_minus && (!fout_zero))key_value_fout <= (key_value_fout - key_value_tmp)/10;elsekey_value_fout <= key_value_fout;endTRANSITION: begin endRECEIVE_PWD: beginif (number)key_value_pout <= (key_value_pout<<1) + (key_value_pout<<3) + key_value_tmp;else if (key_minus && (!pout_zero))key_value_pout <= (key_value_pout - key_value_tmp)/10;elsekey_value_pout <= key_value_pout;endREAD: beginread_fout <= key_value_fout;read_pout <= key_value_pout;endFLAG: out_flag <= 1'b1;default:beginout_flag         <=  1'b0;key_value_fout   <= 32'b0;read_fout        <= 32'b0;key_value_pout   <= 19'b0;read_pout        <= 19'b0;           endendcaseend
end

说明:

  1. 对于key_value_fout <= (key_value_fout<<1) + (key_value_fout<<3) + key_value_tmp;语句,翻译成数学表达式即为:
    key_value_fout = key_value_fout * 10 + key_value_tmp;
    为了节省寄存器,将数字10拆分成2+8,而key_value_fout乘2或乘8分别对应key_value_fout左移一位和三位的操作,相比于使用乘法器,能够省下一些寄存器。
  2. 对于按下回退按键导致的除法操作:
    key_value_fout <= (key_value_fout - key_value_tmp)/10;
    本身在FPGA中很忌讳使用除法的,想到在硬件设计中,10 = 32/3,故将上式转变为:
    key_value_fout <= ((key_value_fout - key_value_tmp) * 3) >> 5;
    但是在仿真的过程中发现,输入1000,回退一位,最终的结果是93,而非100,故勉为其难地用了除法。不过需要提醒的一点是,不使用除法,误差较大,优点是逻辑资源占用少,为641个;若使用除法,而且还是两处,虽然结果准确,但逻辑占用激增至1315个,两个除法所占用的逻辑数为674个,比我不使用除法写的代码所占用的逻辑资源还要多。
3.4 输出计算
//===========================================================
//caculate and output
//===========================================================
always @ (posedge clk_50M or negedge rstn) beginif (!rstn) beginfwd_a <= 64'b0    ;fwd_b <= 64'b0    ;pwd_a <= 19'b0    ;pwd_b <= 19'b0    ;endelse if (out_flag && channel_a) beginfwd_a <= (1441151880*read_fout) >> 24  ;  //(2882303761*read_fout)>>25pwd_a <= read_pout                     ;fwd_b <= fwd_b                         ;pwd_b <= pwd_b                         ;endelse if (out_flag && channel_b) beginfwd_a <= fwd_a                         ;pwd_a <= pwd_a                         ;fwd_b <= (1441151880*read_fout) >> 24  ;  //(2882303761*read_fout)>>25pwd_b <= read_pout                     ;endelse begin            fwd_a <= fwd_a  ;pwd_a <= pwd_a  ;fwd_b <= fwd_b  ;pwd_b <= pwd_b  ;end
endendmodule

说明:
根据DDS中输出频率以及频率控制字的计算关系可得(详见《FPGA自学笔记——设计与验证》第294页):
F o u t = B ∗ F c l k / 2 n F_{out} = B*F_{clk}/2^n Fout​=B∗Fclk​/2n
其中,n = 32,Fout是输出的频率值,B为频率控制字,在此verilog文件中,用fwd表示。由于开发板使用的时钟频率为50MHz,故频率控制字B的计算方式为:
B = 2 32 ∗ F o u t / F c l k = 2 32 / 50 , 000 , 000 ∗ F o u t B = 2^{32} * F_{out} / F_{clk} = 2^{32}/50,000,000 * F_{out} B=232∗Fout​/Fclk​=232/50,000,000∗Fout​

B = 85.89934592 ∗ F o u t B= 85.89934592 * F_{out} B=85.89934592∗Fout​

而FPGA中是没有浮点小数运算的,也就是说,当输入85.89934592,FPGA会自动截断小数点,将其当做85进行计算,这样导致频率控制字的误差较大。采用如下方式能够避免精度不够的问题:

小数点之后的数89934592,通过计算可知,其二进制表示的数总共27位。

对89.89934592乘上227,既
89.89934592 ∗ 2 27 = 11529215046.06846976 89.89934592*2^{27} = 11529215046.06846976 89.89934592∗227=11529215046.06846976
保留整数11529215046,将频率控制字的计算方式变为
B = ( 2 27 ∗ 85.89934592 ∗ F o u t ) > > 27 B= (2^{27}*85.89934592 * F_{out}) >> 27 B=(227∗85.89934592∗Fout​)>>27
B = ( 11529215046 ∗ F o u t ) > > 27 B= (11529215046* F_{out}) >> 27 B=(11529215046∗Fout​)>>27

这样能很好地避免由于小数点的出现而造成的精度损失,不过缺点便是本来应该为32位的fwd需要扩容成64位。另外,考虑到89.89934592 *227对应的整数,其二进制位数大于32位,故需要调整乘数因子,变为225;而89.89934592 * 227= 2882303761;同时
可见,该数对应的二进制正好是32位,且最高位为1,在Modelsim仿真中会报警告,即Modelsim会将这个二进制数当做有符号数处理,故再次改变乘数因子,调整为224。最终
B = ( 2 24 ∗ 85.89934592 ∗ F o u t ) > > 24 B= (2^{24}*85.89934592 * F_{out}) >> 24 B=(224∗85.89934592∗Fout​)>>24

B = ( 1441151880 ∗ F o u t ) > > 24 B= (1441151880 * F_{out}) >> 24 B=(1441151880∗Fout​)>>24
所以,对应的verilog代码为
fwd_a <= (1441151880*read_fout) >> 24 ;
fwd_b <= (1441151880*read_fout) >> 24 ;

4. testbench测试

/*=============================================================================
#     FileName: key_FSM_tb.v
#         Desc:
#       Author: ohliver
#        Email: ohliver@foxmail.com
#     HomePage: https://blog.csdn.net/qq_15062763
#      Version: 0.0.1
#   LastChange: 2020-02-03 20:58:48
#      History:
=============================================================================*/
`timescale 1ns/1ns
`define p 20module key_FSM_tb;reg clk_50M ;
reg rstn    ;reg [15:0] in_addr  ;
reg [15:0] in_data  ;
reg        in_flag  ;wire [63:0] fwd_a   ;
wire [63:0] fwd_b   ;
wire [31:0] pwd_a   ;
wire [31:0] pwd_b   ;
wire        out_flag;initial         clk_50M = 1'b1      ;
always #(`p/2)  clk_50M = ~clk_50M  ;parameter CHANNEL_A = 5'd10;  //button CH-
parameter CHANNEL_B = 5'd12;  //button CH+
parameter PHASE     = 5'd11;  //button CH
parameter DELETE    = 5'd16;  //button -
parameter OK        = 5'd18;  //button EQ
initial beginrstn    <=  1'b0;in_flag <=  1'b0;in_addr <= 16'b0;in_data <= 16'b0;#(`p*10); rstn <= 1'b1;//=======================================
//通道A输出fwd=1000Hz,pwd=256
//channel_Apress_HT6221(CHANNEL_A);
//frequency = 1000Hzpress_HT6221(1 );   press_HT6221(0 );   press_HT6221(0 );   press_HT6221(0 );
//phase = (256/4096)*2*pi press_HT6221(PHASE);press_HT6221(2    );   press_HT6221(5    );   press_HT6221(6    );
//OKpress_HT6221(OK);#(`p*3000);//======================================
//通道B输出fwd=100Hz,pwd=25press_HT6221(CHANNEL_B);
//frequency = 100Hzpress_HT6221(1 );   press_HT6221(0 );   press_HT6221(0 );   press_HT6221(0 );press_HT6221(DELETE);//phase = (25/4096)*2*pi press_HT6221(PHASE);press_HT6221(2    );   press_HT6221(5    );   press_HT6221(6    );press_HT6221(DELETE);//OKpress_HT6221(OK);#(`p*3000);//=====================================
//通道A输出fwd=10Hz,pwd=2press_HT6221(CHANNEL_B);
//frequency = 10Hzpress_HT6221(1 );   press_HT6221(0 );   press_HT6221(0 );   press_HT6221(0 );press_HT6221(DELETE);press_HT6221(DELETE);//phase = (2/4096)*2*pi press_HT6221(PHASE);press_HT6221(DELETE);press_HT6221(2    );   press_HT6221(5    );   press_HT6221(6    );press_HT6221(DELETE);press_HT6221(DELETE);//OKpress_HT6221(OK);#(`p*3000);//===============================
//什么都不输入,直接按OK键press_HT6221(CHANNEL_B);press_HT6221(OK);#(`p*3000);//===============================
//什么都不输入,直接按回退键press_HT6221(CHANNEL_B);press_HT6221(DELETE);#(`p*3000);$stop;
endtask press_HT6221(input [4:0] key_board);beginin_flag <= 1'b1;#(`p);in_flag <= 1'b0;LUT(key_board);#(`p*100); //20ns *100 = 2us, //waiting 2us and then //push another keyend
endtasktask LUT (input [4:0] key_board_r);begincase(key_board_r)5'd0 : {in_data, in_addr} <= 32'hE916FF00;5'd1 : {in_data, in_addr} <= 32'hF30CFF00;5'd2 : {in_data, in_addr} <= 32'hE718FF00;5'd3 : {in_data, in_addr} <= 32'hA15EFF00;5'd4 : {in_data, in_addr} <= 32'hF708FF00;5'd5 : {in_data, in_addr} <= 32'hE31CFF00;5'd6 : {in_data, in_addr} <= 32'hA55AFF00;5'd7 : {in_data, in_addr} <= 32'hBD42FF00;5'd8 : {in_data, in_addr} <= 32'hAD52FF00;5'd9 : {in_data, in_addr} <= 32'hB54AFF00;5'd10: {in_data, in_addr} <= 32'hBA45FF00;5'd11: {in_data, in_addr} <= 32'hB946FF00;5'd12: {in_data, in_addr} <= 32'hB847FF00;5'd13: {in_data, in_addr} <= 32'hBB44FF00;5'd14: {in_data, in_addr} <= 32'hBF40FF00;5'd15: {in_data, in_addr} <= 32'hBC43FF00;5'd16: {in_data, in_addr} <= 32'hF807FF00;5'd17: {in_data, in_addr} <= 32'hEA15FF00;5'd18: {in_data, in_addr} <= 32'hF609FF00;5'd19: {in_data, in_addr} <= 32'hE619FF00;5'd20: {in_data, in_addr} <= 32'hF20DFF00;default: {in_data, in_addr} <= 32'h0;endcaseend
endtaskkey_FSM key_FSM(.clk_50M      (clk_50M),.rstn         (rstn   ),.in_addr     (in_addr),.in_data   (in_data),.in_flag     (in_flag),.fwd_a        (fwd_a       ),.fwd_b        (fwd_b       ),.pwd_a        (pwd_a       ),.pwd_b        (pwd_b       ),.out_flag     (out_flag    )
);endmodule

经过调试,状态跳转以及数据输出都符合预期,是作此篇。

针对红外键盘HT6221的输出设计按键状态机相关推荐

  1. HDLBits练习汇总-14-时序逻辑设计测试--状态机(二)

    水箱问题(Exams/ece241 2013 q4) 一个大水库的水为几个用户服务.为了保持足够高的水位,三个传感器以5英寸的间隔垂直放置.当水位高于最高传感器S3时,输入流量应为零.当液位低于最低传 ...

  2. 基于STM32F103移植华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机

    华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机 因为在做华为LiteOS任务挂起和恢复需要使用到按键去触发任务挂起和恢复动作,因为我就萌发出使用状态机这种架构做一个按键检测触发.回想已 ...

  3. Atmel跑Linux的arm芯片,Atmel针对Linux的低成本嵌入式设计推出新

    Atmel针对Linux的低成本嵌入式设计推出新 来源:dengzhiyu 作者:华仔 浏览:957 时间:2016-08-10 14:18 标签: 摘要: AtmelCorporation日前宣布推 ...

  4. android界面按钮布局,界面设计按键布局

    界面设计按键布局 百度魔拍 分数评定:7 百度魔拍的主打功能便是自拍美颜,所以进入拍摄界面后应用默认开启前置摄像头,并且自动切换至特效相机,所有功能按键都设计的小巧精致,屏幕上的大部分空间都在取景范围 ...

  5. c语言按键状态机,C语言状态机编程思想

    原标题:C语言状态机编程思想 有限状态机概念 有限状态机是一种概念思想,把复杂的控制逻辑分解成有限个稳定状态,组成闭环系统,通过事件触发,让状态机按设定的顺序处理事务.单片机C语言的状态机编程,是利用 ...

  6. 按键状态机—实现连发

    状态机的编程,最好的地方就是在于,一旦框架出现了,如果需要实现新的功能,只是需要增加新的状态.在c语言中,只需要增加几个简单的变量即可.而在labview中,只需要增加一个分支框图即可.就跟累积积木一 ...

  7. 设计一个状态机,A饮料10分钱,B饮料5分钱,投币分5分钱和10分钱,考虑找零。

    //设计一个自动饮料售卖机,共有两种饮料,其中饮料 A 每个 10 分钱,饮料 B 每个 5 分钱 //硬币有 5 分和 10 分两种,并考虑找零. //要求用状态机实现,定义状态,画出状态转移图,并 ...

  8. 按键精灵 getcursorpos没有用_给你们想要的一键输出II按键精灵脚本开发教程

    按键精灵能为我们做什么 有人会说,按键精灵不就是代替我们按键盘嘛,当我们不玩的时候他替我们不停的按键盘,防止我们暂离掉线. 其实按键精灵能做的远远不止这些,按键精灵的脚本语言是基于VBS的,理论上VB ...

  9. ARM(IMX6U)裸机按键输入实验(BSP+SDK、GPIO输入与输出、按键消抖)

    参考:Linux之ARM(IMX6U)裸机按键输入实验(GPIO的输出与输入) 作者:一只青木呀 发布时间: 2020-08-17 21:43:37 网址:https://blog.csdn.net/ ...

  10. android 按键点击触摸有水印效果_“100例”—优秀产品设计按键细节设计美图

    品索设计,让设计考研 \ 就业 \ 留学更简单欢迎关注「品索设计」按键是产品设计中非常重要的一个细节,它是一个产品中与人零距离接触次数最多的一个地方.设计可不是单单有创意就可以的,要想把创意展现得好, ...

最新文章

  1. 哪些数据可以放进缓存?记录生产环境一次缓存评估的过程
  2. 【问题收录】Ubuntu14.04连接两个双显示器失败的解决方案
  3. 利用GDataXML解析XML文件
  4. centos中bash占用cpu_Docker 多种维度限制容器可用的 CPU
  5. tornado总结2-静态文件设置
  6. der解码规则_DER编码简介
  7. 为什么QueueingConsumer会被Deprecated?
  8. 面试题40:数组中只出现一次的数字
  9. 深入理解ARM体系架构(S3C6410)---认识S3C6410
  10. 通达信自编的选股公式如何使用?
  11. C#如何消除按键提示声音?
  12. 阿里月饼门 vs 阿里价值观
  13. matlab生成轨道不平顺谱程序,用于轨道不平顺复现试验的驱动试验谱生成方法
  14. 5G时代,玖玖星球云算链引领VR技术踏上新台阶
  15. picpick文字竖排了怎么变成横排
  16. 入门铺路——python
  17. 斥资建造全景分割养猪场,AI 养猪,到底靠不靠谱?
  18. RK3399平台开发系列讲解(电源管理篇)11.7、PM callback
  19. 微软hackathon_如何参加编码训练营,聚会,赢得Hackathon彻底改变了我的生活
  20. 影院购票系统座位信息锁定问题

热门文章

  1. 解决VSCode使用微软账户同步设置后,再打开需要重新登录的问题
  2. 计算机设备和办公设备,办公设备
  3. 《12步通关求职面试》学习笔记
  4. golang常用并发编程的几种模式
  5. java游戏开发学习路线简记
  6. 送给孩子的趣味通信课
  7. 一、杰理AD14N芯片烧录
  8. Mac OS X mountain lion中安装GCC:install gcc in Mac OS mountain lion
  9. chatgpt赋能python:Python中*para:使用一个参数解决多个参数
  10. Google搜索指定地区,不跳转