STM32入门实践系列——GPIO输入输出(模拟摩斯密码)

本文介绍了使用STM32微控制器通过GPIO输入摩斯密码并解码的过程。作者利用两个独立按键模拟摩斯电码的短点和长划,通过读取按键状态并存储5次输入,然后与预设密码对照,通过蜂鸣器发出不同长度的声音进行解码,并用LED灯显示解码结果。项目中涉及到了GPIO的输入输出配置、时钟设置、按键扫描、蜂鸣器控制及LED显示等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任务要求

通过GPIO的输入输出功能或独立按键输出一串摩斯密码(数字0~7之间的莫斯密码),输入口接收,并翻译出来,对应8盏 led 的亮灭。
对应的摩斯密码与数字关系如下:
摩斯密码

实现思路:

通过GPIO的输入功能来获取独立按键上的电平状态,通过两个按键分别来表示莫斯密码里面的圆点和短杠,当有按键按下时,读取当前按键按下的键码值,
并且在完成5次输入后,通过我截取到的按键信息,与标准的莫斯密码比较,完成解码,通过GPIO的输出功能,控制LED的亮起,显示解码出来的数字,同时通过GPIO的输出功能,控制蜂鸣器想起,模拟处莫斯密码真实的通信情况(蜂鸣器响起的时长来表示摩斯密码的圆点和短杠)

硬件准备:

正点原子 NANO STM32F411 V2 开发板教程

具体实物图如下:
在这里插入图片描述

软件准备

  • keil 5
  • CubeMX

代码实现

  • CubeMX 配置

在这里插入图片描述
这里我选择的引脚有:
PC0~PC7 ——点亮LED需要用到
PC8~PC9 ——获取按键的先按下状态
PB8 ——用于控制蜂鸣器

具体配置如下:
在这里插入图片描述
在这里插入图片描述
系统时钟我这里是选用的外部时钟HSE,
配置的是72MHZ:
在这里插入图片描述

  • Keil 5编写

  • 代码概览

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

完结撒花,啦啦啦。
请添加图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不想写代码的我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值