任务要求
通过GPIO的输入输出功能或独立按键输出一串摩斯密码(数字0~7之间的莫斯密码),输入口接收,并翻译出来,对应8盏 led 的亮灭。
对应的摩斯密码与数字关系如下:
实现思路:
通过GPIO的输入功能来获取独立按键上的电平状态,通过两个按键分别来表示莫斯密码里面的圆点和短杠,当有按键按下时,读取当前按键按下的键码值,
并且在完成5次输入后,通过我截取到的按键信息,与标准的莫斯密码比较,完成解码,通过GPIO的输出功能,控制LED的亮起,显示解码出来的数字,同时通过GPIO的输出功能,控制蜂鸣器想起,模拟处莫斯密码真实的通信情况(蜂鸣器响起的时长来表示摩斯密码的圆点和短杠)
硬件准备:
正点原子 NANO STM32F411 V2 开发板教程
具体实物图如下:
软件准备
- keil 5
- CubeMX
代码实现
这里我选择的引脚有:
PC0~PC7 ——点亮LED需要用到
PC8~PC9 ——获取按键的先按下状态
PB8 ——用于控制蜂鸣器
具体配置如下:
系统时钟我这里是选用的外部时钟HSE,
配置的是72MHZ:
void SystemClock_Config(void);
int KEY_Flag ; //存放按键是否按下的状态值 1为按下
int key_Number = 0; //存放按键按下的数码值 0为短按 1为长按
int password[5] ; //用来存放5次按下的数码值 模拟莫斯密码
int password_map[15] = {_long,_long,_long,_long,_long, //存放密码图鉴 _long为长按, _short为短按
_short,_short,_short,_short,_short,
_long,_long,_long,_long,_long}; //_long _short 定义在main.h
int main(void)
{
int i = 0;
KEY_Flag = 0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init(); //GPIO初始化
LED_Run(); //到这里表示初始化结束 //流水灯
while (1)
{
key_Number = key_scan(); //扫描按键是否按下
if(KEY_Flag == 1) //如果按下则为1
{
if(key_Number)
{
password[i] = _long; //key1按下为长
}
else
{
password[i] = _short; //key0按下为短
}
i++;
KEY_Flag = 0;
if( i >= 5) //i>5表示已经存满了,可以进行密码比对
{
BEEP_Transmit(password); //将密码翻译成长短不同的声响
LED_Check(password,password_map); //显示模斯密码翻译后出来的结果(0~7内表示解码成功)
i = 0;
}
}
}
}
这里代码的思想很简单,就是在while(1)里面不断扫描按键的状态,当确认捕获到按键的电平状态时对捕获到的内容进行检验,完成相应的动作:
对了,这里的_long 和_short 是我通过宏定义定义好的这样做的好处是可以方便的区分莫斯密码中短杠和圆点具体实现如下:
#define _long 100
#define _short 20s
流水灯函数,没啥用就是玩,哈哈哈。
void LED_Run(void) //流水灯函数
{
int i = 0;
for(i = 0;i<7;i++) //从LED0到LED7依次亮
{
HAL_GPIO_WritePin(GPIOC,LED0_Pin <<i,GPIO_PIN_RESET);
HAL_Delay(50);
}
for(i = 0;i<7;i++) //从LED7到LED0依次灭
{
HAL_GPIO_WritePin(GPIOC,LED7_Pin >>i,GPIO_PIN_SET);
HAL_Delay(50);
}
for(i =0 ;i<2;i++) //LED闪烁
{
HAL_GPIO_WritePin(GPIOC,LED0_Pin|LED1_Pin|LED2_Pin|LED3_Pin|LED4_Pin|LED5_Pin|LED7_Pin|LED7C7_Pin,GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOC,LED0_Pin|LED1_Pin|LED2_Pin|LED3_Pin|LED4_Pin|LED5_Pin|LED7_Pin|LED7C7_Pin,GPIO_PIN_SET);
HAL_Delay(100);
}
}
按键扫描函数,用来捕获按键是否有按下
int key_scan(void) //按键扫描函数
{
if( (1 - HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) |
(1 - HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)) //第一次判断按键是否按下
)
{
HAL_Delay(20); //消抖
if( (1 - HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) |
(1 - HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)) //再次确认按键是否按下
)
{
KEY_Flag = 1;
if(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)) //判断是否为按键0按下
{
while((1-HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin))); //等待按键0松开
return 0;
}
if(1-HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)) //判断是否为按键1按下
{
while(1-HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)); //等待按键1松开
return 1;
}
}
}
return 3;
}
将获取到的按键信息通过蜂鸣器响起对应的时间来表示
void BEEP_Transmit(int*p) //密码解码函数
{
int i = 0;
for(i = 0; i<5;i++)
{
HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_RESET);
HAL_Delay(*(p+i)); //让BEEP响password[i]对应的时长,_short为20ms,_long为100ms
HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET);
HAL_Delay(150); //短暂停顿,关掉蜂鸣器
}
}
LED显示函数,显示对应的LED数量(密码为0则LED0亮起,密码为7则LED0到LED7亮起)
这里我是通过学习了双指针后启发,得到了现在的解法,如果通过对每一个密码进行比对来检验密码的正确性也可以,但是这样代码会显得比较冗余,作为爱动脑子的工程师,当时是会选择前者啦 [狗头]
int LED_Check(int*p,int* p_map)
{
int i = 0;
int j = 0;
int count = 0;
for(i = 0;i<8;i++) //这个for循环是用来遍历password_map的内容和0到7的莫斯密码之间相互比较的
{
for(j = 0;j<5;j++) //分别对password_map与password中对应位进行比较
{
if(*(p+j) == *(p_map+10-i+j))
{
count++;
}
}
if(count >= 5) //count >=5表示得到了正确的密码
{
for(j = 0;j<8;j++) //清除LED的状态(熄灭所有LED)
{
HAL_GPIO_WritePin(GPIOC,LED0_Pin <<j,GPIO_PIN_SET);
}
for(j = 0;j<i+1;j++) //亮起对用盏数的LED
{
HAL_GPIO_WritePin(GPIOC,LED0_Pin <<j,GPIO_PIN_RESET);
}
return i;
}
else //在count <5时将count清零,
{
count = 0;
}
}
return 8;
}
到这里整个项目也完成啦,虽然没有用到很深入的知识,但是毕竟是第一次用32写代码,也就浅浅的记录一下吧
下面有整个项目的工程,需要的话可以自行下载:
https://pan.baidu.com/s/1nxkLZyIE_U4pClnZ3VCcog
提取码:0000
完结撒花,啦啦啦。