[CW32L083系列] CW32L083评估板学习-5.按键检测与定时器

[复制链接]
 楼主| yuyy1989 发表于 2023-8-18 17:01 | 显示全部楼层 |阅读模式
本帖最后由 yuyy1989 于 2023-8-30 10:17 编辑

#申请原创#  @21小跑堂  

5.按键检测与定时器
5.1按键检测
可以用2种方式来检测按键是否被按下,一种是轮询,即不断的查询按键IO的状态,另一种是中断,按键电平改变时触发中断,机械按键按下时会出现抖动,可以用延时来去抖
实验-用轮询方式检测按键,按键按下修改LED亮灭状态
用两个变量分别存储两个按键的状态,当检测到按键IO为低电平时,通过状态变量判断当前按键是否已经是按键状态,如果是则跳过,如果不是延时10ms再检测按键状态,如果仍然为低电平将状态变量改为按下状态,同时修改对应的LED状态,检测到按键IO为高电平时重置按键的状态变量
  1. int32_t main(void)
  2. {
  3.     uint8_t key1down = 0,key2down = 0;
  4.     RCC_HSE_16M_PLL64M_init();
  5.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  6.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  7.     led_init();
  8.     key_init();
  9.     while(1)
  10.     {
  11.         if(GPIO_ReadPin(CW_GPIOA,GPIO_PIN_4) == RESET)
  12.         {
  13.             if(key1down == 0)
  14.             {
  15.                 yuyy_delay_ms(10);
  16.                 if(GPIO_ReadPin(CW_GPIOA,GPIO_PIN_4) == RESET)
  17.                 {
  18.                     key1down = 1;
  19.                     GPIO_TogglePin(CW_GPIOC,GPIO_PIN_2);
  20.                 }
  21.             }
  22.         }
  23.         else
  24.         {
  25.             key1down = 0;
  26.         }
  27.         if(GPIO_ReadPin(CW_GPIOA,GPIO_PIN_5) == RESET)
  28.         {
  29.             if(key2down == 0)
  30.             {
  31.                 yuyy_delay_ms(10);
  32.                 if(GPIO_ReadPin(CW_GPIOA,GPIO_PIN_5) == RESET)
  33.                 {
  34.                     key2down = 1;
  35.                     GPIO_TogglePin(CW_GPIOC,GPIO_PIN_3);
  36.                 }
  37.             }
  38.         }
  39.         else
  40.         {
  41.             key2down = 0;
  42.         }
  43.     }
  44. }
效果
WeChat_20230816105548 00_00_00-00_00_30.gif
实验-用IO中断检测按键,按键按下修改LED亮灭状态
使用中断时可以使用的中断数字滤波器可对引脚上的输入信号进行数字滤波来实现去抖,两个按键都已上拉处理,这里通过下降沿中断检测按键是否按下,接下来按键1开启中断滤波,按键2不开启
  1. void key_init()
  2. {
  3.     GPIO_InitTypeDef gpiodef;
  4.     __RCC_GPIOA_CLK_ENABLE();
  5.     gpiodef.Pins = GPIO_PIN_4|GPIO_PIN_5;
  6.     gpiodef.IT = GPIO_IT_FALLING;
  7.     gpiodef.Mode = GPIO_MODE_INPUT;
  8.     GPIO_Init(CW_GPIOA,&gpiodef);
  9.     GPIO_ConfigFilter(CW_GPIOA,GPIO_PIN_4,GPIO_FLTCLK_HCLK2);
  10.    
  11.     GPIOA_INTFLAG_CLR(GPIO_PIN_4| GPIO_PIN_5);
  12.     NVIC_EnableIRQ(GPIOA_IRQn);
  13. }

  14. void GPIOA_IRQHandler(void)
  15. {
  16.     if (CW_GPIOA->ISR_f.PIN4)
  17.     {
  18.         GPIOA_INTFLAG_CLR(GPIO_PIN_4);
  19.         GPIO_TogglePin(CW_GPIOC,GPIO_PIN_2);
  20.     }

  21.     if (CW_GPIOA->ISR_f.PIN5)
  22.     {
  23.         GPIOA_INTFLAG_CLR(GPIO_PIN_5);
  24.         GPIO_TogglePin(CW_GPIOC,GPIO_PIN_3);
  25.     }
  26. }

  27. int32_t main(void)
  28. {
  29.     uint8_t key1down = 0,key2down = 0;
  30.     RCC_HSE_16M_PLL64M_init();
  31.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  32.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  33.     led_init();
  34.     key_init();
  35.     while(1)
  36.     {
  37.     }
  38. }
效果
WeChat_20230816105557 00_00_00-00_00_30.gif
还可以同时开启多个中断,例如同时开启下降沿中断和上升沿中断,下面这段代码在按键的中断中翻转LED状态,由于同时开启了上升沿中断和下降沿中断按下和松开时都会翻转状态
  1. void key_init()
  2. {
  3.     GPIO_InitTypeDef gpiodef;
  4.     __RCC_GPIOA_CLK_ENABLE();
  5.     gpiodef.Pins = GPIO_PIN_4;
  6.     gpiodef.IT = GPIO_IT_FALLING|GPIO_IT_RISING;
  7.     gpiodef.Mode = GPIO_MODE_INPUT;
  8.     GPIO_Init(CW_GPIOA,&gpiodef);
  9.     GPIO_ConfigFilter(CW_GPIOA,GPIO_PIN_4,GPIO_FLTCLK_HCLK8);
  10.    
  11.     GPIOA_INTFLAG_CLR(GPIO_PIN_4);
  12.     NVIC_EnableIRQ(GPIOA_IRQn);
  13. }

  14. void GPIOA_IRQHandler(void)
  15. {
  16.     if (CW_GPIOA->ISR_f.PIN4)
  17.     {
  18.         GPIOA_INTFLAG_CLR(GPIO_PIN_4);
  19.         GPIO_TogglePin(CW_GPIOC,GPIO_PIN_2);
  20.     }
  21. }

  22. int32_t main(void)
  23. {
  24.     RCC_HSE_16M_PLL64M_init();
  25.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  26.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  27.     led_init();
  28.     key_init();
  29.     while(1)
  30.     {
  31.         GPIO_TogglePin(CW_GPIOC,GPIO_PIN_3);
  32.         yuyy_delay_ms(250);
  33.     }
  34. }
效果
WeChat_20230816105603 00_00_00-00_00_30.gif
5.2定时器与计数器
CW32L083集成了1个高级定时器、4个通用定时器、3个基本定时器和1个低功耗定时器,都是16位,除基本定时器只能向上计数外,其它的都可以上下计数
定时器与计数器并没有什么不同,本质上都是计数器,只不过作为定时器使用时通过固定周期的时钟源进行计数,作为计数器使用时是通过外部事件触发计数
实验-使用定时器模式实现一个1ms的定时器
选用通用定时器1,让1个LED的闪烁周期为1秒,另外一个为0.5秒,系统时钟为64M,定时器预分频选择64,将计数重载值设为999开启溢出中断,在中断中处理LED的亮灭
  1. void gtimer1_init()
  2. {
  3.     GTIM_InitTypeDef GTIM_InitStruct = {0};
  4.     __RCC_GTIM1_CLK_ENABLE();
  5.     GTIM_InitStruct.Mode = GTIM_MODE_TIME;
  6.     GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV64;
  7.     GTIM_InitStruct.ReloadValue = 1000-1;
  8.     GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct);
  9.     GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);
  10.     NVIC_EnableIRQ(GTIM1_IRQn);
  11.     GTIM_Cmd(CW_GTIM1, ENABLE);
  12. }
  13. uint16_t tim1count = 0;
  14. void GTIM1_IRQHandler(void)
  15. {
  16.     if (GTIM_GetITStatus(CW_GTIM1, GTIM_IT_OV))
  17.     {
  18.         GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
  19.         if(tim1count % 500 == 0)
  20.             GPIO_TogglePin(CW_GPIOC,GPIO_PIN_2);
  21.         if(tim1count % 250 == 0)
  22.             GPIO_TogglePin(CW_GPIOC,GPIO_PIN_3);
  23.         if(tim1count < 999)
  24.             tim1count += 1;
  25.         else
  26.             tim1count = 0;
  27.     }
  28. }
编译的时候又发现了这个优先级的警告
QQ截图20230816083852.png
QQ截图20230816083914.png
和通用定时器的DMA判断有关,顺便改一下吧
效果
WeChat_20230816105610 00_00_00-00_00_30.gif
作为计数器时与其它定时器联合实现更长周期的定时效果,定时器还可以直接输出翻转电平,当发生溢出中断时翻转对应IO的电平,接下来结合gtimer1和gtimer2实现1秒的定时器
这里有个坑,计数器模式时分频值范围与定时器时不一样
QQ截图20230816145823.png
原例程gtim_counter_itr里设置的计数器分频值是GTIM_PRESCALER_DIV1
QQ截图20230816150429.png
这个宏定义的值是0,在计数器模式下是错误的,所以按照这个例程写完后并没有预期输出效果,原例程编译运行也没有readme里写的效果
改正后代码,gtimer1部分的代码不动,初始化gtimer2为计数器模式,PB12和PB13作为gtimer2的翻转输出
  1. void gtimer2_toggleout_init()
  2. {
  3.     GPIO_InitTypeDef gpiodef;
  4.     __RCC_GPIOB_CLK_ENABLE();
  5.     gpiodef.Pins = GPIO_PIN_12|GPIO_PIN_13;
  6.     gpiodef.IT = GPIO_IT_NONE;
  7.     gpiodef.Mode = GPIO_MODE_OUTPUT_PP;
  8.     GPIO_Init(CW_GPIOB,&gpiodef);
  9.     PB12_AFx_GTIM2TOGN();
  10.     PB13_AFx_GTIM2TOGP();
  11. }
  12. void gtimer2_counter_init()
  13. {
  14.     GTIM_InitTypeDef GTIM_InitStruct = {0};
  15.     __RCC_GTIM2_CLK_ENABLE();
  16.     GTIM_InitStruct.Mode = GTIM_MODE_COUNTER;
  17.     GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;;
  18.     GTIM_InitStruct.Prescaler = 1;
  19.     GTIM_InitStruct.ReloadValue = 1000-1;
  20.     GTIM_InitStruct.ToggleOutState = ENABLE;
  21.     GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStruct);
  22.     GTIM2_ITRConfig(ITR_SOURCE_GTIM1);
  23.     GTIM_Cmd(CW_GTIM2, ENABLE);
  24. }

效果
WeChat_20230816164935 00_00_00-00_00_30.gif
5.3PWM输出
利用定时器的输出比较功能可以实现PWM输出功能,通用定时器的输出有6种比较模式
QQ截图20230816104707.png
对应到库文件中的宏定义
QQ截图20230816104856.png
实验-使用PWM输出实现呼吸灯
板载的2个LED对应gtimer2的ch1和ch2
QQ截图20230816102038.png
  1. void led_pwm_init()
  2. {
  3.     GPIO_InitTypeDef gpiodef;
  4.     __RCC_GPIOC_CLK_ENABLE();
  5.     gpiodef.Pins = GPIO_PIN_2|GPIO_PIN_3;
  6.     gpiodef.IT = GPIO_IT_NONE;
  7.     gpiodef.Mode = GPIO_MODE_OUTPUT_PP;
  8.     GPIO_Init(CW_GPIOC,&gpiodef);
  9.     PC02_AFx_GTIM2CH2();
  10.     PC03_AFx_GTIM2CH1();
  11. }
  12. void gtimer2_pwm_init()
  13. {
  14.     GTIM_InitTypeDef GTIM_InitStruct = {0};
  15.     __RCC_GTIM2_CLK_ENABLE();
  16.     GTIM_InitStruct.Mode = GTIM_MODE_TIME;
  17.     GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV64;
  18.     GTIM_InitStruct.ReloadValue = 1000 - 1;
  19.     GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStruct);
  20.     GTIM_OCInit(CW_GTIM2, GTIM_CHANNEL1, GTIM_OC_OUTPUT_PWM_HIGH);
  21.     GTIM_OCInit(CW_GTIM2, GTIM_CHANNEL2, GTIM_OC_OUTPUT_PWM_HIGH);
  22.     GTIM_SetCompare1(CW_GTIM2, 0);
  23.     GTIM_SetCompare2(CW_GTIM2, 0);
  24.     GTIM_Cmd(CW_GTIM2, ENABLE);
  25. }
  26. int32_t main(void)
  27. {
  28.     uint16_t pwm = 0;
  29.     RCC_HSE_16M_PLL64M_init();
  30.     RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
  31.     RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
  32.     led_pwm_init();
  33.     gtimer2_pwm_init();
  34.     while(1)
  35.     {
  36.         if(pwm < 1000)
  37.         {
  38.             GTIM_SetCompare1(CW_GTIM2,pwm);
  39.             GTIM_SetCompare2(CW_GTIM2,1000 - pwm);
  40.         }
  41.         else
  42.         {
  43.             GTIM_SetCompare1(CW_GTIM2,2000-pwm);
  44.             GTIM_SetCompare2(CW_GTIM2,pwm-1000);
  45.         }
  46.         yuyy_delay_ms(1);
  47.         if(pwm < 1999)
  48.             pwm += 1;
  49.         else
  50.             pwm = 0;
  51.     }
  52. }
效果
WeChat_20230816105614 00_00_00-00_00_30.gif
5.4输入捕获
利用输入捕获模式可以捕获外部输入电平的变化,可以用来计算脉冲宽度
实验-利用输入捕获检测按键,按键直接控制LED1,两次按下间隔如果不超过0.5秒切换LED2的状态
  1. void key2_tim2ic_init()
  2. {
  3.     GPIO_InitTypeDef gpiodef;
  4.     __RCC_GPIOA_CLK_ENABLE();
  5.     gpiodef.Pins = GPIO_PIN_5;
  6.     gpiodef.IT = GPIO_IT_NONE;
  7.     gpiodef.Mode = GPIO_MODE_INPUT;
  8.     GPIO_Init(CW_GPIOA,&gpiodef);
  9.     PA05_AFx_GTIM2CH1();
  10. }
  11. void gtimer2_inputcap_init()
  12. {
  13.     GTIM_InitTypeDef GTIM_InitStruct = {0};
  14.     GTIM_ICInitTypeDef GTIM_ICInitStruct = {0};
  15.     __RCC_GTIM2_CLK_ENABLE();

  16.     GTIM_InitStruct.Mode = GTIM_MODE_TIME;
  17.     GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
  18.     GTIM_InitStruct.Prescaler = 64000-1;;
  19.     GTIM_InitStruct.ReloadValue = 0xFFFF;
  20.     GTIM_InitStruct.ToggleOutState = DISABLE;
  21.     GTIM_TimeBaseInit(CW_GTIM2, >IM_InitStruct);

  22.     GTIM_ICInitStruct.CHx = GTIM_CHANNEL1;
  23.     GTIM_ICInitStruct.ICFilter = GTIM_CHx_FILTER_PCLK_N2;
  24.     GTIM_ICInitStruct.ICInvert = GTIM_CHx_INVERT_OFF;
  25.     GTIM_ICInitStruct.ICPolarity = GTIM_ICPolarity_Falling;
  26.     GTIM_ICInit(CW_GTIM2, >IM_ICInitStruct);

  27.     GTIM_ITConfig(CW_GTIM2, GTIM_IT_CC1|GTIM_IT_OV, ENABLE);
  28.     GTIM_Cmd(CW_GTIM2, ENABLE);
  29.     NVIC_EnableIRQ(GTIM2_IRQn);
  30. }
  31. uint16_t lastkeycount = 0;
  32. void GTIM2_IRQHandler(void)
  33. {
  34.     uint16_t ccrtemp = 0;
  35.     if (GTIM_GetITStatus(CW_GTIM2, GTIM_IT_OV))
  36.     {
  37.         GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_OV);
  38.     }
  39.     if(GTIM_GetITStatus(CW_GTIM2, GTIM_IT_CC1))
  40.     {
  41.         ccrtemp = CW_GTIM2->CCR1;
  42.         GTIM_ClearITPendingBit(CW_GTIM2, GTIM_IT_CC1);
  43.         GPIO_TogglePin(CW_GPIOC,GPIO_PIN_3);
  44.         if(ccrtemp > lastkeycount)
  45.         {
  46.             if(ccrtemp - lastkeycount < 500)
  47.             {
  48.                 GPIO_TogglePin(CW_GPIOC,GPIO_PIN_2);
  49.             }
  50.             lastkeycount = ccrtemp;
  51.         }
  52.         else
  53.         {
  54.             if(0x10000-lastkeycount+ccrtemp < 500)
  55.             {
  56.                 GPIO_TogglePin(CW_GPIOC,GPIO_PIN_2);
  57.             }
  58.             lastkeycount = ccrtemp;
  59.         }
  60.     }
  61. }
效果
WeChat_20230816173123 00_00_00-00_00_30.gif

 楼主| yuyy1989 发表于 2023-8-18 17:06 | 显示全部楼层
通用定时器的初始化方法是这样的
QQ截图20230818170243.png
粘到代码框不知道为什么变这样了
QQ截图20230818170448.png
小夏天的大西瓜 发表于 2023-8-26 11:57 | 显示全部楼层
中断溢出是进行中断处理的依据
星辰大海不退缩 发表于 2023-8-27 20:37 | 显示全部楼层
矩阵键盘其实就是依次扫描进行IO输入的
AdaMaYun 发表于 2023-8-27 21:29 | 显示全部楼层
轮询其实还是很实用的查询机制
OKAKAKO 发表于 2023-8-27 22:22 | 显示全部楼层
PWM呼吸灯其实可以用IO控制MOS管实现
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

161

主题

815

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部
认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

161

主题

815

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部