整体构建流程:先通过CubeMX创建一个只含有时钟的打底工程,在创建一个一个源工程(方便打底工程移植初始化代码)。
一、led模块
在源工程中的cubeMX构建led初始化,八个引脚,一个锁存器引脚,移植到打底工程中,并在打底工程中建立lLed_Disp(char ucLed)函数,此函数通过(ucLed<<8)得到对应的led引脚,通过8位ucLed分别控制。注意:控制顺序是先写led寄存器,在开锁存器,关锁存器。
二、lcd模块
没啥注意的,copy历程就好了。
三、key模块
按键消抖需要用到定时器,在移植工程中需注意:需要用到定时器溢出中断功能,开启中断,移植it文件中的IRQhand函数,初始化后记得手动添加HAL_TIM_Base_Start_IT定时器开启函数,此函数中断也会开启,然后在HAL_TIM_PeriodElapsedCallback回调函数中写按键消抖逻辑。
运用状态机,按键功能分为短按键和长按键,状态分别为①检测按下,②消抖状态()③检测松开(定时器设置为10ms中断,如果大于一定时间段为长按,小于则为短按)注意:在长按后得到长按标志位后不能恢复状态为0(状态机的state),此处不用管,只要得到状态就行。(目前不知道为啥,测的这样可以用)
练习代码
#include "stdbool.h"
typedef struct{
unsigned char Judge_State;
bool Key_State;
bool Key_Short_Flag;
bool Key_Long_Flag;
int Key_Time;
}KEY;
KEY key[4];
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
key[0].Key_State=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
key[1].Key_State=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].Key_State=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].Key_State=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3);
for(int i=0;i<4;i++)
{
switch(key[i].Judge_State)
{
case 0:
if(key[i].Key_State==0)//检测按下
{
key[i].Judge_State=1;
}
break;
case 1:
if(key[i].Key_State==0)//检测消抖,过了10ms后如果还是按下就消除抖动
{
key[i].Judge_State=2;
}
else//为抖动,返回初状态
{
key[i].Judge_State=0;
}
break;
case 2:
if(key[i].Key_State==1)//检测松开
{
key[i].Judge_State=0;
if(key[i].Key_Time<80)
{
key[i].Key_Short_Flag=1;//短按
}
}
else//还没松手
{
key[i].Key_Time++;
if(key[i].Key_Time>=80)//800ms为一次长按
{
key[i].Key_Long_Flag=1;//长按
//在此处不能把key[i].Judge_State设置为0
//如果设置为0现在一直是按下的状态,松开直接会跑到case 2:的短按状态
}
}
break;
}
}
}
}
想明白了:短按中的key[i].Judge_State=0;会随着按键的抖动重新回归初始态,长按时不要管。
四、输入捕获
此处捕获测频率使用的是测周法测频率,设置为上升沿或下降沿进中断都行(一个周期),在中断回调函数中,读取捕获到的值(HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)),注意:读完记得清零cnt。最后,通过设定的最小周期乘以捕获的个数得到周期,再取倒数。
提示:测周法中用于测量指定周期的最小周期越小(频率越大),误差越小,但不能太小,太小捕获值会变大,指定测量的频率也不要太大(周期小)。
问:定时器生成指定频率时是由两个参数决定,一个是分频系数,一个是重装值。这个频率能和重装值有关系呢?其实这个也是测周法:重装值指定一个周期,以分频后的频率为基准,倒数为一个小周期,小周期乘以重装值为总时间,再取倒数就是频率。
测试时:得出发出频率的定时器要调整重装值,测量的定时器频率月大越好,过大,测的值会比平常多。仅提供调时的一个方向。