FPGA 20个例程篇:1.不同按键控制不同LED亮灭

 一、典型基础入门,小试牛刀 :

1.不同按键控制不同LED亮灭

      大家不妨回忆下从上学到工作,有趣的是仿佛在学习任何一款嵌入式芯片的编程时,几乎不约而同地都是先从按键、发光二极管、蜂鸣器入门的,这些外设虽然看上去很基础但也具有很强的代表性,所以第一章里笔者通过两个基础例程,帮助大家自然而然地入门FPGA设计,闲话不多说我们直接步入正题。

         按键和发光二极管可以说是嵌入式设备中最为常见的外设了,本身应用场景有很多,例如发光二极管从电路上通过CPU控制高低电平的输出可以用来指示通信传输速度、设备运行状态等,按键从电路上分为独立式和扫描式可以用来控制少数普通按键、编码器以及行列式矩阵按键。

         如图1和图2所示,分别是独立式和扫描式按键电路设计,大家可以清楚地看到对CPU而言,每个独立式按键都需要一个IO口对其进行连接,而扫描式按键则不需要IO口在数量上和按键进行逐一匹配,只用在同一时刻通过对按键矩阵进行扫描,即可判断出当前时刻哪个按键被按下,硬件上节约了主CPU的IO口,软件上也简化了代码逻辑,在这里我们不对扫描式的代码设计展开详细的叙述,例程中只对项目中用得比较多的独立式按键进行分析讲解。

        图1 独立式的按键电路

 图2 扫描式的按键电路

        通过FPGA基础知识专栏的实践学习,其实大家已经掌握了按键消抖处理设计,如果忘记的话,那么我们在这里再回过头简单地复习一下,如图3所示是豌豆开发板Artix7上按键电路,非常普遍的硬件设计,按键一端接地,而另外一端接一个上拉电阻再连到Artix7芯片的引脚上,所以当按键断开时是高电平,闭合时是低电平,但是因为按键具有机械特性,在断开和闭合的时候都会有抖动有误触发的情况,这个时间通常是小于10ms的,如图4所示是按键闭合、断开以及稳定时的电平变化示意图,所以不管是MCU还是FPGA程序设计里都会对按键检测去做20ms的消抖处理。

      如表1为这个模块的信号列表,本模块的输入信号有:clk系统时钟信号、rst_n系统复位信号、外部4个按键key_in引脚输入信号;输出信号有:key _vld按键消抖有效信号、key_value按键键值信号,在整体工程的设计中,通过例化key_vld和key_value两个输出信号,即可得到外部按键哪个按键被按下了,其他模块再据此去做后续的处理。

 图3 豌豆开发板Artix7上按键电路

 图4 按键闭合和断开抖动电平变换示意图

信号列表

信号名

I/O

位宽

clk

I

1

rst_n

I

1

key_in

I

4

key_vld

O

1

key_value

O

4

表1 key_scan模块信号列表

       为了方便理解,我们可以把这个模块的整体流程用一个流程示意图来表示,如图5所示,大家再根据这个流程示意图去思考下,我们应该如何去合理划分这个模块中状态机的状态,以及不同状态之间应该怎么去跳转。

       首先把整个流程提取出来,可以分为四个状态,即IDLE空闲状态、CHECK_KEYIN检测按键闭合状态、KEEP_KEYIN按键持续闭合状态、CHECK_KEYOUT检测按键断开状态。然后设计debounce_cnt消抖的计数器,用来计时20ms的按键闭合断开的消抖时间,显然在状态CHECK_KEYIN和CHECK_KEYOUT的时候,消抖计数器再去计时20ms时间,所以把debounce_cnt的加一条件设置为:state_c == CHECK_KEYIN || state_c == CHECK_KEYOUT,把结束条件设置为add_debounce_cnt && debounce_cnt== TIM_20MS-1。

     接着再去考虑状态跳转,根据图5的按键消抖处理设计流程图,IDLE状态可以跳转到CHECK_KEYIN状态;CHECK_KEYIN状态在消抖20ms后通过判断key_in的输入值,则可以跳转到IDLE状态或者KEEP_KEYIN状态;KEEP_KEYIN状态在检测到key_in的输入值不为4'b1111时便会跳转到CHECK_KEYOUT状态;CHECK_KEYOUT状态在消抖20ms后通过判断key_in的输入值,则可以跳转到IDLE状态或者KEEP_KEYIN状态。

      最后我们需要去注意输出信号key_vld以及key_value的设计,大家想一想这两个关键输出信号应该什么时候被置位和赋值,通过图5的设计流程图可以看出来,我们应该在CHECK_KEYIN状态跳转到KEEP_KEYIN状态的时刻,把key_value用key_in的逐位取反来赋值,这时key_value记录下当前哪个按键被按下,当用户断开按键时即在CHECK_KEYIN跳转到IDLE状态的时刻,再去置高一个周期的key_vld把key_value的值送到其他处理模块,key_vld和key_value都用时序逻辑去产生,在搞清楚整个模块的逻辑以后,把代码写漂亮已经不困难了,如图6所示是其对应的代码设计供参考,当然也可以有不同的状态机划分方法,只有符合预期设计要求即可,答案完全不唯一。

 图5 按键消抖处理设计流程图

     如图7所示,在把key_scan模块产生的key_value和key_vld信号例化到顶层模块即可,通过key_value和key_vld再对LED进行点灯操作。

图6 按键消抖处理模块的代码设计

图7 按键控制LED亮灭顶层文件的例化
 

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个基于STM32的外部中断和GPIO口实现按键控制LED亮灭的简单例程。 首先需要配置GPIO口和外部中断。以下是初始化代码: ```c GPIO_InitTypeDef GPIO_InitStruct = {0}; EXTI_InitTypeDef EXTI_InitStruct = {0}; NVIC_InitTypeDef NVIC_InitStruct = {0}; // 使能GPIO时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); // 配置按键引脚为输入 GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 使能外部中断时钟 __HAL_RCC_SYSCFG_CLK_ENABLE(); // 配置外部中断线路 EXTI_InitStruct.Line = EXTI_LINE_0; EXTI_InitStruct.Mode = EXTI_MODE_INTERRUPT; EXTI_InitStruct.Trigger = EXTI_TRIGGER_FALLING; EXTI_InitStruct.Pull = EXTI_PULLUP; HAL_EXTI_SetConfigLine(&EXTI_InitStruct, EXTI_CONFIG_PORT_A); // 配置中断优先级 NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; HAL_NVIC_Init(&NVIC_InitStruct); ``` 以上配置将PA0引脚配置为输入模式,并且使用上拉电阻使其默认为高电平。然后配置外部中断触发条件为下降沿触发,并且使能该中断的优先级。 接下来是中断处理函数的实现: ```c void EXTI0_IRQHandler(void) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 切换LED灯的状态 HAL_Delay(100); // 延时一段时间,消除按键抖动 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // 清除中断标志 } ``` 中断处理函数中,首先使用`HAL_GPIO_TogglePin()`函数切换LED灯的状态,然后使用`HAL_Delay()`函数延时一段时间,消除按键的抖动。最后使用`HAL_GPIO_EXTI_IRQHandler()`函数清除中断标志位。 最后,在`main()`函数中使用以下代码启动程序: ```c int main(void) { HAL_Init(); SystemClock_Config(); GPIO_Init(); while (1) { } } ``` `GPIO_Init()`函数中可以初始化LED灯的输出口: ```c GPIO_InitTypeDef GPIO_InitStruct = {0}; // 使能GPIO时钟 __HAL_RCC_GPIOC_CLK_ENABLE(); // 配置LED引脚为输出 GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); // 初始状态关闭LED灯 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); ``` 以上就是按键控制LED亮灭的简单例程。当按下按键时,LED灯会切换亮灭状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值