主要内容
上一篇文章中采用了EXTI中断的方式控制小灯的亮灭。其实也可以不采用中断,直接使用GPIO配置即可实现按键对应小灯的状态,且效果几乎相同。同样本次例程试验采用正点原子阿波罗STM32F429IGT6开发板,debug采用STLink(后续的文章中将不再赘述)。
试验的主要内容有:
1.正常的配置流程
2.修改代码实现对功能的优化和模块化设计
cubemx配置
按照相同的步骤,选择芯片,配置时钟树,小灯和按键的配置都和上节相同,不再赘述。
(需要注意的是,延时的操作不可避免的要使用HAL_Delay()函数,故在SYS处Debug勾选Serial Wire确保延时函数的正确使用。同时注意延时函数的优先级,本节例程暂时不会有冲突,但后续的会有,错误的优先级会导致程序卡死,后续再强调)
代码部分
1.普通功能
初始化配置已经在cubemx基本完成,我们的思路是检测按键的电平状态,通过GPIO反转函数令小灯的电平翻转,实现亮灭
主体函数如下所示
while (1)
{
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_RESET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_SET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_SET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
}
通过上电发现,并没有很好地实现功能,按键按下两个灯闪烁不定,此时考虑到没有对按键进行消抖处理。
按键消抖:由于采用的是机械按键,在按下瞬间的电平变化并不是理想的阶跃信号,具体的变化大致如图所示
延时的操作主要就是:在第一个if语句处再加嵌套,插入延时函数HAL_Delay(10)(大多数的前后抖动时长约为10ms,人为造成的刻意抖动也不过四五十ms),此处要求cubemx处配置好systick相关的初始化打开(前面强调过)调整后代码所示:
while (1)
{
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
}
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_SET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_SET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
}
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
}
}
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_SET && flag == 1)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
}
}
烧录进开发板,效果甚至更糟了,分析之后得出,按键的按下不应该仅仅有硬件的配置环节,也需要存在程序中的“标志位”对应进行软件把控,才能实现按键按下瞬间的电平转换。
在cubemx生成的文件中,特定区域进行填写,可以保证工程在cubemx修改引脚初始化之后修改的工程仍可被保存。
/* USER CODE BEGIN 2 */
uint8_t flag=0;
/* USER CODE END 2 */
flag为设定的标志位,当按键被按下的时候,一来检测按键的电平,二来直接检测标志位是否为0(前一状态为没按下),先将标志位置1,随后将小灯电平翻转,反之亦然,这样就不会存在逻辑错误。具体代码不再展示,烧录带开发板现象良好。
2.模块化设计
初学者拿到官方的例程,很多封装处理不好的函数,给阅读和理解造成很大难度,移植到自己的程序里也是bug百出。通过模块化的设计增强可移植性,便于理解和阅读。
将上一步骤最后的程序封装起来即可,具体过程大致思路:
1
在主函数下定义一个名为get_pin的函数,添加了两个个参数,以只点亮PB
具体代码所示:
void get_pin(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
static uint8_t flag= 0;
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag == 0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag== 0)
{
flag= 1;
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
}
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_SET && flag== 1)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_SET && flag== 1)
{
// HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
flag= 0;
}
}
在主函数中 while(1)中调用
/* USER CODE BEGIN WHILE */
while (1)
{
get_pin(GPIOH,GPIO_PIN_2);
}
/* USER CODE END WHILE */
烧录到板子中,没有问题,显示状态和按键对应都很好
2
这种通过构建函数的调用方式有一些鸡肋,也没有很好地起到可读性加强的作用,通过在工程中加入.h文件,通过一些参数的加入和弱定义函数的实现使得程序更简洁
首先在工程目录下加入名为add_key.h文件,并在keil中添加,target -> C/C++处勾选路径,并在主函数声明,如图所示
编译通过,没有报错。
第二步,在.h文件中声明函数,并添加函数成分中结构体的来源:stm32f4xx_hal.h(具体查询可以先输入函数,F12跳转到报错部分添加即可)
需要注意的是,.h文件提供了弱定义函数的整理和存放,最后这些文件的声明都需要在main.c文件下进行人为编写,具体功能类似于中断中的回调函数,在上篇文章中有介绍,下篇文章会详细说一下
具体代码所示:
void get_pin(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin,uint8_t i)
{
static uint8_t flag[2] = {0};
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag[i] == 0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag[i] == 0)
{
flag[i] = 1;
key_up_to_down(i);
}
}
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_SET && flag[i] == 1)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_Pin) == GPIO_PIN_SET && flag[i] == 1)
{
// HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
flag[i] = 0;
key_down_to_up(i);
}
}
if(flag[i] == 0) key_up(i);
if(flag[i] == 1) key_down(i);
此处将两个按键的功能集成到一个函数中,通过数组的选择来实现按键对灯的控制
之后完善弱定义函数部分的代码
void key_up_to_down(uint8_t i)
{
if(i == 0) HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
void key_down_to_up(uint8_t i)
{
if(i == 0) HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
void key_up(uint8_t i)
{
if(i == 1) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
}
void key_down(uint8_t i)
{
if(i == 1) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
}
经过上面一系列的操作,最后在while中实现的就很简单了
while (1)
{
get_pin(GPIOH,GPIO_PIN_2,1);
get_pin(GPIOH,GPIO_PIN_3,0);
}
这样对应的是:PH2(KEY1)–PB1(RED),PH3(KEY0)–PB0(GREEN)
烧录到开发板上,现象很好,实现了预期的功能
而且需要对换对应关系,只需要getpin最后参数1/0即可
遇到的问题
在调试板子的过程中,发现一个小问题。设置了软件消抖,但这样会导致小灯的亮灭出现肉眼可见的频闪,这是超出预期之外的。取消消抖又会由于机械按键的抖动使得点亮状态的对应出现问题。通过外加函数的递归调用,可以消除这个问题,使得二者都达到预期的效果。
为了找到问题的原因,第一次调整了cubemx上对灯识别沿的选项,并没有明显的效果,后来写的弱定义的小灯状态函数,通过主函数的调用将问题解决。这个问题暂时还么有找到原因,后续的学习中会再注意。有可能是硬件的问题,但更多应该是软件的配置出现问题。在检查上一篇文章的代码时也发现了类似的问题,如果有大神可以指点一二,不胜感谢。
(ps:本文是根据露神六鸽的视频自己手打了一遍的学习历程,tql)
版权声明:本文为CSDN博主「赵小森。」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_49720228/article/details/122659842
主要内容
上一篇文章中采用了EXTI中断的方式控制小灯的亮灭。其实也可以不采用中断,直接使用GPIO配置即可实现按键对应小灯的状态,且效果几乎相同。同样本次例程试验采用正点原子阿波罗STM32F429IGT6开发板,debug采用STLink(后续的文章中将不再赘述)。
试验的主要内容有:
1.正常的配置流程
2.修改代码实现对功能的优化和模块化设计
cubemx配置
按照相同的步骤,选择芯片,配置时钟树,小灯和按键的配置都和上节相同,不再赘述。
(需要注意的是,延时的操作不可避免的要使用HAL_Delay()函数,故在SYS处Debug勾选Serial Wire确保延时函数的正确使用。同时注意延时函数的优先级,本节例程暂时不会有冲突,但后续的会有,错误的优先级会导致程序卡死,后续再强调)
代码部分
1.普通功能
初始化配置已经在cubemx基本完成,我们的思路是检测按键的电平状态,通过GPIO反转函数令小灯的电平翻转,实现亮灭
主体函数如下所示
while (1)
{
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_RESET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_SET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_SET)
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
}
通过上电发现,并没有很好地实现功能,按键按下两个灯闪烁不定,此时考虑到没有对按键进行消抖处理。
按键消抖:由于采用的是机械按键,在按下瞬间的电平变化并不是理想的阶跃信号,具体的变化大致如图所示
延时的操作主要就是:在第一个if语句处再加嵌套,插入延时函数HAL_Delay(10)(大多数的前后抖动时长约为10ms,人为造成的刻意抖动也不过四五十ms),此处要求cubemx处配置好systick相关的初始化打开(前面强调过)调整后代码所示:
while (1)
{
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
}
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_SET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) == GPIO_PIN_SET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
}
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
}
}
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_SET && flag == 1)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) == GPIO_PIN_RESET)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
}
}
烧录进开发板,效果甚至更糟了,分析之后得出,按键的按下不应该仅仅有硬件的配置环节,也需要存在程序中的“标志位”对应进行软件把控,才能实现按键按下瞬间的电平转换。
在cubemx生成的文件中,特定区域进行填写,可以保证工程在cubemx修改引脚初始化之后修改的工程仍可被保存。
/* USER CODE BEGIN 2 */
uint8_t flag=0;
/* USER CODE END 2 */
flag为设定的标志位,当按键被按下的时候,一来检测按键的电平,二来直接检测标志位是否为0(前一状态为没按下),先将标志位置1,随后将小灯电平翻转,反之亦然,这样就不会存在逻辑错误。具体代码不再展示,烧录带开发板现象良好。
2.模块化设计
初学者拿到官方的例程,很多封装处理不好的函数,给阅读和理解造成很大难度,移植到自己的程序里也是bug百出。通过模块化的设计增强可移植性,便于理解和阅读。
将上一步骤最后的程序封装起来即可,具体过程大致思路:
1
在主函数下定义一个名为get_pin的函数,添加了两个个参数,以只点亮PB
具体代码所示:
void get_pin(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
static uint8_t flag= 0;
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag == 0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag== 0)
{
flag= 1;
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
}
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_SET && flag== 1)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_SET && flag== 1)
{
// HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
flag= 0;
}
}
在主函数中 while(1)中调用
/* USER CODE BEGIN WHILE */
while (1)
{
get_pin(GPIOH,GPIO_PIN_2);
}
/* USER CODE END WHILE */
烧录到板子中,没有问题,显示状态和按键对应都很好
2
这种通过构建函数的调用方式有一些鸡肋,也没有很好地起到可读性加强的作用,通过在工程中加入.h文件,通过一些参数的加入和弱定义函数的实现使得程序更简洁
首先在工程目录下加入名为add_key.h文件,并在keil中添加,target -> C/C++处勾选路径,并在主函数声明,如图所示
编译通过,没有报错。
第二步,在.h文件中声明函数,并添加函数成分中结构体的来源:stm32f4xx_hal.h(具体查询可以先输入函数,F12跳转到报错部分添加即可)
需要注意的是,.h文件提供了弱定义函数的整理和存放,最后这些文件的声明都需要在main.c文件下进行人为编写,具体功能类似于中断中的回调函数,在上篇文章中有介绍,下篇文章会详细说一下
具体代码所示:
void get_pin(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin,uint8_t i)
{
static uint8_t flag[2] = {0};
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag[i] == 0)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET && flag[i] == 0)
{
flag[i] = 1;
key_up_to_down(i);
}
}
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_SET && flag[i] == 1)
{
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOH,GPIO_Pin) == GPIO_PIN_SET && flag[i] == 1)
{
// HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
flag[i] = 0;
key_down_to_up(i);
}
}
if(flag[i] == 0) key_up(i);
if(flag[i] == 1) key_down(i);
此处将两个按键的功能集成到一个函数中,通过数组的选择来实现按键对灯的控制
之后完善弱定义函数部分的代码
void key_up_to_down(uint8_t i)
{
if(i == 0) HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
void key_down_to_up(uint8_t i)
{
if(i == 0) HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
}
void key_up(uint8_t i)
{
if(i == 1) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
}
void key_down(uint8_t i)
{
if(i == 1) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
}
经过上面一系列的操作,最后在while中实现的就很简单了
while (1)
{
get_pin(GPIOH,GPIO_PIN_2,1);
get_pin(GPIOH,GPIO_PIN_3,0);
}
这样对应的是:PH2(KEY1)–PB1(RED),PH3(KEY0)–PB0(GREEN)
烧录到开发板上,现象很好,实现了预期的功能
而且需要对换对应关系,只需要getpin最后参数1/0即可
遇到的问题
在调试板子的过程中,发现一个小问题。设置了软件消抖,但这样会导致小灯的亮灭出现肉眼可见的频闪,这是超出预期之外的。取消消抖又会由于机械按键的抖动使得点亮状态的对应出现问题。通过外加函数的递归调用,可以消除这个问题,使得二者都达到预期的效果。
为了找到问题的原因,第一次调整了cubemx上对灯识别沿的选项,并没有明显的效果,后来写的弱定义的小灯状态函数,通过主函数的调用将问题解决。这个问题暂时还么有找到原因,后续的学习中会再注意。有可能是硬件的问题,但更多应该是软件的配置出现问题。在检查上一篇文章的代码时也发现了类似的问题,如果有大神可以指点一二,不胜感谢。
(ps:本文是根据露神六鸽的视频自己手打了一遍的学习历程,tql)
版权声明:本文为CSDN博主「赵小森。」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_49720228/article/details/122659842
暂无评论