STM32F4芯片标准库配置
1、按键初始化
/* 判断按键按下函数 */
uint8_t IsKeyDown1(void) {if ((GPIOE->IDR & GPIO_Pin_6) == 0) return 0;else return 1;}//UP
uint8_t IsKeyDown2(void) {if ((GPIOC->IDR & GPIO_Pin_15) == 0) return 0;else return 2;}//DOWN
uint8_t IsKeyDown3(void) {if ((GPIOC->IDR & GPIO_Pin_13) == 0) return 0;else return 4;}//MENU
uint8_t IsKeyDown4(void) {if ((GPIOC->IDR & GPIO_Pin_14) == 0) return 0;else return 8;}//SET
uint8_t IsKeyDown5(void) {if ((GPIOE->IDR & GPIO_Pin_5) == 0) return 0;else return 16;}//ON/OFF
/*硬件初始化*/
static void bsp_InitKeyHard(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 第1步:打开GPIO时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOE,ENABLE); //使能GPIO时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //PC13 14 15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; /* 设为输入口 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; /* IO口最大速度 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6; //PE5 PE6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
2、系统时钟中断函数
其他时钟的中断函数也可以,1ms中断
#define MAX_KEY_TIME 40 //max read key time
extern uint32_t keydouble_time;
extern uint8_t keydouble_number;
void SysTick_Handler(void)
{
if(keydouble_time) keydouble_time--;
else keydouble_number=0;
if(--_Key.TimeCount==0)
{
_Key.TimeCount = MAX_KEY_TIME;
_Key.Reflash = 1;
}
}
3、主函数
int main(void)
{
SysTick_Config(SystemCoreClock / 1000);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC,ENABLE);//使能CRC时钟,时钟恢复电路
bsp_InitKeyHard(); //按键GPIO初始化 PC13、PC14、PC15、PE6、PE7
while(1)
{
keyscan(&_Key.Reflash); //定时器中断,40ms进入一次
}
}
4、按键扫描函数
uint8_t keyvalue_old; //保存上一次按键数据
uint8_t keyvalue_press; //上一次按键按下的记录
uint8_t key_debounce_count=0;//按键消抖计数
uint32_t menu_press_count=0; //menu按键按下时触发(包括单击和长按)
uint8_t keypress_flag; //按键按下1,按键松开0
uint32_t free_count; //按键松开时触发
uint8_t key_longflag=0; //按键长按标志
uint32_t key_longcount; //长按计数
uint8_t keydouble_number; //记录双击第几次按下
uint32_t keydouble_time; //记录在多少时间内双击有效
uint32_t keydouble_count; //完成双击时触发
void keyscan(uint8_t *read_time)//单击按下,单击松开,双击,长按
{
uint8_t i;
if(*read_time)//累计40ms把标志位拉高一次
{
*read_time = 0;
i = (IsKeyDown1()|IsKeyDown2()|IsKeyDown3()|IsKeyDown4()|IsKeyDown5())&0X1F;//读按键
if(i == NO_KEY_NUM)//如果没有按键按下
{
if(keyvalue_press != NO_KEY_NUM)//上一次按键值不为空//
{
keyvalue_press = NO_KEY_NUM;//无按键按下
switch(keyvalue_old)//上一次按下的按键
{
case MENU_NUM:
free_count++; /*code 按键单击松开时触发*/
break;
default:break;
}
}
}
else//按键按下时
{
if(keyvalue_old != i)//如果上一次按键和本次按键不一样
{
keyvalue_old = i; //上一次按键 = 本次按键
key_debounce_count=0; //消抖计数值清零
}
else//如果上一次按键和本次按键一样
{
if(++key_debounce_count>2)//按键防抖
{
key_debounce_count=0;//消抖计数值清零
switch(i)
{
case MENU_NUM://MODE KEY
if(i == keyvalue_press)//如果上一次按键和本次一样
{
keypress_flag=0;//不进入单击计数//否则可能按一下按键,会进入两次
if(key_longflag)//长按使能标志 1使能,0失能
{
if(++key_longcount > 10)//长按计数超过10次
{
keypress_flag=1;//若是使能长按,依旧可以进行单击计数
}
}
}
else//如果上一次按键和本次不一样
{
key_longcount = 0;//长按计数清零
keypress_flag=1;//进行单击计数
}
if(keypress_flag)
{
keyvalue_press = i;
menu_press_count++; /*code 按键单击按下时触发,以及使能长按功能后触发*/
if(key_longflag==0)//双击功能在长按功能不生效时启用
{
if(keydouble_number == 0)//单击第一下
{
keydouble_number=1;
keydouble_time = 1000;//定时器中递减
}
else if(keydouble_number==1 && keydouble_time)//在时间内单击第二下
{
keydouble_number=0;
keydouble_time=0;
keydouble_count++; /*code 按键双击时触发*/
}
}
}
break;
default:break;
}
}
}
}
}
}
代码以一个按键为例,可以继续封装到函数中,功能大概是这样子。
变量有点多,还是需要理一理思路。