针对红外键盘HT6221的输出设计按键状态机
文章目录
- 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键盘至少有如下两个好处:
- 拿来即用,且在一定范围内可以持续使用,省去接线等不必要的繁琐工作;
- 红外键盘按键有21个,比4x4的矩阵键盘多出5个按键,虽然说多出来的不一定能够用到,但是多一些总该是有点好处的;
出于对时间流逝的感到不安、焦虑,又出于想要尽快得到按键正确输出的目的,最终,决定弃用矩阵键盘,改用红外键盘。
这也是为什么上一篇博客分明是与矩阵键盘相关,而此篇博客又叙述红外键盘之内容的原因。
2. 键盘键值定义
虽说换了个按键终端,但是主题还是没有变——依旧是针对DDS的频率控制字fwd
以及相位控制字pwd
,进行设计。
比如,想让DDS输出的频率为1000Hz,那么在红外键盘上,分别按下1
、0
、0
、0
后,再按OK
键,即可得到相应的以1000Hz的频率输出的正弦波。
实际上我们用到的并不只是一个DDS,而是双通道的DDS,也就是说,一个DDS有两个输出通道,可以同时输出两个完全不相关的正弦波信号。可以想象成有两条路,一个车开得快,一个车开得慢这种。而每一个通道的输入都有两个控制字——频率控制字与相位控制字——故双通道就有四个控制字,分别为频率控制字fwd_a
、fwd_b
,相位控制字pwd_a
、pwd_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
说明:
- 对于
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
左移一位和三位的操作,相比于使用乘法器,能够省下一些寄存器。 - 对于按下回退按键导致的除法操作:
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的输出设计按键状态机相关推荐
- HDLBits练习汇总-14-时序逻辑设计测试--状态机(二)
水箱问题(Exams/ece241 2013 q4) 一个大水库的水为几个用户服务.为了保持足够高的水位,三个传感器以5英寸的间隔垂直放置.当水位高于最高传感器S3时,输入流量应为零.当液位低于最低传 ...
- 基于STM32F103移植华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机
华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机 因为在做华为LiteOS任务挂起和恢复需要使用到按键去触发任务挂起和恢复动作,因为我就萌发出使用状态机这种架构做一个按键检测触发.回想已 ...
- Atmel跑Linux的arm芯片,Atmel针对Linux的低成本嵌入式设计推出新
Atmel针对Linux的低成本嵌入式设计推出新 来源:dengzhiyu 作者:华仔 浏览:957 时间:2016-08-10 14:18 标签: 摘要: AtmelCorporation日前宣布推 ...
- android界面按钮布局,界面设计按键布局
界面设计按键布局 百度魔拍 分数评定:7 百度魔拍的主打功能便是自拍美颜,所以进入拍摄界面后应用默认开启前置摄像头,并且自动切换至特效相机,所有功能按键都设计的小巧精致,屏幕上的大部分空间都在取景范围 ...
- c语言按键状态机,C语言状态机编程思想
原标题:C语言状态机编程思想 有限状态机概念 有限状态机是一种概念思想,把复杂的控制逻辑分解成有限个稳定状态,组成闭环系统,通过事件触发,让状态机按设定的顺序处理事务.单片机C语言的状态机编程,是利用 ...
- 按键状态机—实现连发
状态机的编程,最好的地方就是在于,一旦框架出现了,如果需要实现新的功能,只是需要增加新的状态.在c语言中,只需要增加几个简单的变量即可.而在labview中,只需要增加一个分支框图即可.就跟累积积木一 ...
- 设计一个状态机,A饮料10分钱,B饮料5分钱,投币分5分钱和10分钱,考虑找零。
//设计一个自动饮料售卖机,共有两种饮料,其中饮料 A 每个 10 分钱,饮料 B 每个 5 分钱 //硬币有 5 分和 10 分两种,并考虑找零. //要求用状态机实现,定义状态,画出状态转移图,并 ...
- 按键精灵 getcursorpos没有用_给你们想要的一键输出II按键精灵脚本开发教程
按键精灵能为我们做什么 有人会说,按键精灵不就是代替我们按键盘嘛,当我们不玩的时候他替我们不停的按键盘,防止我们暂离掉线. 其实按键精灵能做的远远不止这些,按键精灵的脚本语言是基于VBS的,理论上VB ...
- ARM(IMX6U)裸机按键输入实验(BSP+SDK、GPIO输入与输出、按键消抖)
参考:Linux之ARM(IMX6U)裸机按键输入实验(GPIO的输出与输入) 作者:一只青木呀 发布时间: 2020-08-17 21:43:37 网址:https://blog.csdn.net/ ...
- android 按键点击触摸有水印效果_“100例”—优秀产品设计按键细节设计美图
品索设计,让设计考研 \ 就业 \ 留学更简单欢迎关注「品索设计」按键是产品设计中非常重要的一个细节,它是一个产品中与人零距离接触次数最多的一个地方.设计可不是单单有创意就可以的,要想把创意展现得好, ...
最新文章
- 哪些数据可以放进缓存?记录生产环境一次缓存评估的过程
- 【问题收录】Ubuntu14.04连接两个双显示器失败的解决方案
- 利用GDataXML解析XML文件
- centos中bash占用cpu_Docker 多种维度限制容器可用的 CPU
- tornado总结2-静态文件设置
- der解码规则_DER编码简介
- 为什么QueueingConsumer会被Deprecated?
- 面试题40:数组中只出现一次的数字
- 深入理解ARM体系架构(S3C6410)---认识S3C6410
- 通达信自编的选股公式如何使用?
- C#如何消除按键提示声音?
- 阿里月饼门 vs 阿里价值观
- matlab生成轨道不平顺谱程序,用于轨道不平顺复现试验的驱动试验谱生成方法
- 5G时代,玖玖星球云算链引领VR技术踏上新台阶
- picpick文字竖排了怎么变成横排
- 入门铺路——python
- 斥资建造全景分割养猪场,AI 养猪,到底靠不靠谱?
- RK3399平台开发系列讲解(电源管理篇)11.7、PM callback
- 微软hackathon_如何参加编码训练营,聚会,赢得Hackathon彻底改变了我的生活
- 影院购票系统座位信息锁定问题