/*直接复制以下代码,实现,自己在独立研究吸收*/
#include "stm32f10x.h"
#include "bitband_cm3.h"
#include "systick.h"
#define N_key 0 //无键
#define S_key 1 //单键
#define D_key 2 //双键
#define L_key 3 //长键
#define KEY_AN (GPIOA->IDR & 1<<0)
#define BEEF PCout(3)
/**********************************************************************
*函数名:delay_us
*功 能:延迟1us
*参 数:us最大2^24/9=1864135us
*返 回:无
*备 注:无
**********************************************************************/
void delay_us(u16 us)
{
SysTick->LOAD = us * 9; //装载计数值
SysTick->VAL = 0; //清空当前值
SysTick->CTRL |= 1; //使能计数器
while(!(SysTick->CTRL & (1 << 16)));//等待计数结束
SysTick->CTRL &=~ 1;//关闭计数
}
//LED初始化
void LED_Init(void)
{
#if 0
RCC->APB2ENR |= 3<<3;//开启PB/PC口时钟
GPIOB->CRL &=~(0XF<<4*1);//清PB1
GPIOB->CRL |=(0X3<<4*1);//通用输出 50M
GPIOC->CRL &=~(0XF<<4*5);//清PC5
GPIOC->CRL |=(0X3<<4*5);//通用输出 50M
GPIOB->ODR |=(1<<1);//默认给高电平,关灯,
GPIOC->ODR |=(5<<1);
// GPIOB->ODR &=~(1<<1);//点灯
// GPIOC->ODR &=~(5<<1);
#else
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOB,ENABLE);//开启PB/PC口时钟.
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//通用推挽
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//50M
GPIO_Init(GPIOB, &GPIO_InitStruct);//PB1
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
GPIO_Init(GPIOC, &GPIO_InitStruct);//PC5
GPIO_SetBits(GPIOB, GPIO_Pin_1);//默认给高电平,关灯,不能少了这步,因为输出数据寄存器默认值给低电平
GPIO_SetBits(GPIOC, GPIO_Pin_5);
// GPIO_ResetBits(GPIOB, GPIO_Pin_1);//开灯
// GPIO_ResetBits(GPIOC, GPIO_Pin_5);
// PBout(1)=0;
// PCout(5)=0;
#endif
}
//按键初始化
void KEY_Iint(void)
{
RCC->APB2ENR |= 1<<2;//开启PA口时钟
}
//蜂鸣器初始化
void BEEF_Iint(void)
{
RCC->APB2ENR |= 1<<4;//开启PC口时钟
GPIOC->CRL &=~(0XF<<4*3);//清PC3
GPIOC->CRL |=(0X3<<4*3);//通用输出 50M
}
/*
驱动层
1.完成按键的消抖,松手检测
2.把过程细分为一个个状态
3.实现长按与单击功能
按键初始态
按键确认态
按键计时态
等待按键释放态
*/
unsigned char key_driver()
{
static u8 key_state = 0, key_time = 0;
u8 key_return = N_key;
switch (key_state)
{
case 0: // 按键初始态
if (!KEY_AN) key_state = 1; // 键被按下,状态转换到按键消抖和确认状态
break;
case 1: // 按键消抖与确认态
if (!KEY_AN)
{
key_time = 0;
key_state = 2; // 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态,但返回的还是无键事件
}
else
key_state = 0; // 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
break;
case 2: // 按下键时间的计时状态
if(KEY_AN)
{
key_return = S_key; // 此时按键释放,说明是产生一次短操作,回送S_key
key_state = 0; // 转换到按键初始态
}
else if (++key_time >= 100) // 继续按下,计时加10ms(10ms为本函数循环执行间隔)
{
key_return = L_key; // 按下时间>1000ms,此按键为长按操作,返回长键事件
key_state = 3; // 转换到等待按键释放状态
}
break;
case 3: // 等待按键释放状态,此状态只返回无按键事件
if (KEY_AN) key_state = 0; // 按键已释放,转换到按键初始态
break;
}
return key_return;
}
/*
业务逻辑层
1.单击、双击、长按的分配
*/
unsigned char key_read()
{
static u8 key_m = 0, key_time_1 = 0;
u8 key_return = N_key,key_temp;
key_temp = key_driver();
switch(key_m)
{
case 0:
if (key_temp == S_key )
{
key_time_1 = 0; // 第1次单击,不返回,到下个状态判断后面是否出现双击
key_m = 1;
}
else
key_return = key_temp; // 对于无键、长键,返回原事件
break;
case 1:
if (key_temp == S_key) // 又一次单击(间隔肯定<500ms)
{
key_return = D_key; // 返回双击键事件,回初始状态
key_m = 0;
}
else
{ // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
if(++key_time_1 >= 30)
{
key_return = S_key; // 500ms内没有再次出现单键事件,返回上一次的单键事件
key_m = 0; // 返回初始状态
}
}
break;
}
return key_return;
}
/*
单击:300ms~1000ms之间
双击:300ms内
长按:超过1s
单击:控制LED1
双击:控制LED2
长按:控制蜂鸣器
*/
int main(void)
{
LED_Init();
KEY_Iint();
BEEF_Iint();
while(1)
{
switch(key_read())
{
case N_key:
delay_ms(10);
break;
case S_key:
PBout(1)=!PBout(1);
break;
case D_key:
PCout(5)=!PCout(5);
break;
case L_key:
BEEF = !BEEF;
break;
}
}
return 0;
}
|