STM32CUBEMX+MDK5实现按键以及LED灯

1 概述

1.1 资源概述

开发板:正点原子STM32F103 Nano开发板
CUBEMX版本:1.3.0
MDK版本:5.23
主控芯片型号:STM32F103RBT6
正点原子开发板

1.2 实现功能

使用KEY0~KEY3分别控制LED0到LED3的亮灭,KEY_UP控制LED4的亮灭.按一次按键对应的灯亮,再按一次对应的灯灭。另外长按KEY0可以实现蜂鸣器的鸣响。

2 CUBEMX的配置

2.1 GPIO口配置

2.1.1 端口配置图

(部分引脚配置此次实验未用,但是不影响程序的编译,不会报错)
芯片GPIO脚的配置

2.1.2 端口资源汇总表

查阅开发板的电路图确认实验用到的GPIO口配置资源如下。

GPIO 引脚配置资源
LED0PC0
LED1PC1
LED2PC2
LED3PC3
KEYUPPA0
KEY0PC8
KEY1PC9
KEY2PD2
BEEPPB8
USART1_TXPA9
USART1_RXPA10

2.1.3 通用GPIO口的配置

KEY_UP是输出高有效,因此初始化时,配置输入下拉。其余LED端口或者按键均是低有效,所以配置为上拉。这里定义User Label(用户标签),方便后续程序的移植。
在这里插入图片描述

2.2 时钟配置

2.2.1时钟RCC配置图

使用外部HSE,PLL选择9倍频,系统时钟选择PLL,HCLK配置为72MHz,APB1设置为2分频。
RCC时钟设置

2.2.2晶振选择

选择外部晶振,高速晶振为8MHz,低速晶振为32.768KHz。
晶振选择

2.3 调试端口

采用SW方式进行调试烧录。
SWD配置

2.4 CUBEMX工程管理配置

1、选择Advanced、MDK-ARM、V5。
工程配置
2、代码生成配置
勾选生成外设C文件和H文件。否则这些端口的配置都会存在于main函数中。
代码生成配置
3、高级设置
全部勾选HAL函数。HAL函数的全称是Hardware abstract layer(硬件抽象层),这里还可以选择LL即Low layer(底层),函数和HAL完全不一样,优点是直接操作底层,程序占用空间小。咨询ST原厂FAE工程师,后续新开发芯片均不在支持标准库函数,仅支持HAL和LL函数。比如最新的H7系列芯片,就没有标准库函数的支持。
在这里插入图片描述

2.3 选择生成代码

点击Generate code,生成软件代码,在之后的提示框中点击打开工程。
在这里插入图片描述

3 main函数增加代码

3.1 端口初始化函数

在生成的main函数中增加相应的功能函数,端口初始化函数,给各个GPIO口赋值初值,防止上电时状态不确定。写入串口打印函数,提示信息。

/* USER CODE BEGIN 2 */
 HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET);
 HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET);
 HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET);
 HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET);
 HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,GPIO_PIN_SET);
 HAL_GPIO_WritePin(LED5_GPIO_Port,LED5_Pin,GPIO_PIN_SET);
 HAL_GPIO_WritePin(LED6_GPIO_Port,LED6_Pin,GPIO_PIN_SET);
 HAL_GPIO_WritePin(LED7_GPIO_Port,LED7_Pin,GPIO_PIN_SET);//初始化LED灯的状态,全部为灭
 HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET);//初始化BEEP,不响
 
 uint8_t temp[]="Press a key\r\n ";//向串口输入提示信息

  /* USER CODE END 2 */

3.2 功能实现函数

在while(1)函数中增加下述函数,实现不间断的按键检测

while (1)
{
	/* USER CODE END WHILE */

	/* USER CODE BEGIN 3 */

	if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET)//判定KEY0按键是否有按下
	{
		HAL_Delay(10);								//延时,用于按键消抖
		if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET) //判定KEY0按键是否有按下
		{
			HAL_Delay(500);//判定KEY0按键是否长按
			if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==RESET) //KEY0按键被长按
			{
				HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_RESET);//开启蜂鸣器
				HAL_Delay(1000);//开启蜂鸣器1000ms
				HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);//向串口输出提示信息
				HAL_GPIO_WritePin(BEEP_GPIO_Port,BEEP_Pin,GPIO_PIN_SET);//关闭蜂鸣器
			}
			else 	 
			  HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);//KEY0按键为短按,LED0灯状态翻转
			HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
		}
	}
	else  if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==RESET)
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==RESET)
		{
			HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
			HAL_Delay(200);
			HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
		}
	}
	else   if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==RESET)
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==RESET)
		{
			HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
			HAL_Delay(200);
			HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
		}
	}
	else   if(HAL_GPIO_ReadPin(KEYUP_GPIO_Port,KEYUP_Pin)==SET)
	{
		HAL_Delay(10);
		if(HAL_GPIO_ReadPin(KEYUP_GPIO_Port,KEYUP_Pin)==SET)
		{
			HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);
			HAL_Delay(200);
			HAL_UART_Transmit(&huart1,temp,sizeof(temp),50);
		}
	}
}
/* USER CODE END 3 */

4 实验结果

4.1 串口打印

使用串口调试助手,按下一次按键后,串口会打印一个Press a key的字符
串口实验结果

4.2 硬件实现

将程序下载到开发板中,验证软件功能。全部功能实现完成。
通过按键将全部的灯点亮
按键全亮
通过按键熄灭LED1和LED2
熄灭LED1和LED2
硬件实现视频

STM32 按键和LED以及蜂鸣器实验(使用CUBEMX和MDK5)

5 按键检测程序之魔鬼数字优化

参考正点原子的官方例程,按键这一段是这么写的

u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY0==0)return KEY0_PRES;
		else if(KEY1==0)return KEY1_PRES;
		else if(KEY2==0)return KEY2_PRES;
		else if(WK_UP==1)return WKUP_PRES; 
	}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1; 	     
	return 0;// 无按键按下
}

初次看感觉非常绕,云里雾里,一会0一会1的,传递的全部是数值,非常不利于阅读,于是增加了一些宏定义,消除掉这些魔鬼数字。将key_up的变量名进行了更改。
增加的宏定义

#define KEYOFF 1
#define KEYON 0

#define SINGLE_CLICK 0
#define CONTINUOUS_CLICK 1
#define NOKEY_PRESS 0

依据宏定义改后的程序

u8 KEY_Scan(u8 mode)
{	 
	static u8 keyState=KEYOFF;//按键状态为OFF
	if(mode==CONTINUOUS_CLICK)
		keyState=KEYOFF;  //按键初始态为OFF
	if((keyState==KEYOFF)&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
	{
		delay_ms(10);//去抖动 
		keyState=KEYON;
		if(KEY0==0)
			return KEY0_PRES;
		else if(KEY1==0)
			return KEY1_PRES;
		else if(WK_UP==1)
			return WKUP_PRES; 
		else if(KEY2==0)
			return KEY2_PRES;
	}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0) 
	 {
		 keyState=KEYOFF; 
	 }   
	return NOKEY_PRESS;// 无按键按下

主函数中引用的地方原来为

key=KEY_Scan(0;            	//扫描按键
        if((Key_Queue!=NULL)&&key)   	//消息队列Key_Queue创建成功,并且按键被按下

更改后为

key=KEY_Scan(SINGLE_CLICK);            	//扫描按键
        if((Key_Queue!=NULL)&&(key!=NOKEY_PRESS))   	//消息队列Key_Queue创建成功,并且按键被按下

这样就可以知道函数在主函数中调用方式为单次按有效,按键函数的逻辑也很清楚了,就是初次进入函数,静态变量keystate为keyoff,第二项条件式不满足,跳转到按键检测中,当有按键被按下时,keystate为keyon,并返回键值。如果按键没有松手,那么第二次进入这个函数时,if((keyStateKEYOFF)&&(KEY00||KEY10||KEY20||WK_UP==1))条件不满足,无法对按键进行检测。直到按键松手,keyState状态为Keyoff后,才可再次进去按键检测状态。同理可以理解连续按时的实现逻辑。

整个按键的功能的实现如下流程图

在这里插入图片描述

  • 10
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于STM32CubeMX和HAL库的stm32f103c8t6按键流水代码: 1. 打开STM32CubeMX,选择“New Project”,选择MCU型号为“STM32F103C8Tx”,选择工具链为“SW4STM32”或者“MDK-ARM”,点击“Generate Code”生成工程代码。 2. 在Pinout选项卡中配置IO口: - PB0为按键输入,配置为GPIO Input Pull-up。 - PA0-PA7为LED输出,配置为GPIO Output。 3. 在Configuration选项卡中配置系统时钟: - 配置HSE为8MHz,PLL时钟为72MHz。 4. 在main.c中编写代码: ```c #include "main.h" #include "stm32f1xx_hal.h" /* 定义按键LED的IO口 */ #define KEY_GPIO_Port GPIOB #define KEY_Pin GPIO_PIN_0 #define LED_GPIO_Port GPIOA #define LED_Pin GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7 /* 定义按键状态 */ uint8_t KeyState = 0; /* 按键中断回调函数 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == KEY_Pin) { KeyState = 1; } } int main(void) { /* 初始化HAL库 */ HAL_Init(); /* 配置系统时钟 */ SystemClock_Config(); /* 配置按键IO口 */ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = KEY_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY_GPIO_Port, &GPIO_InitStruct); /* 配置LED IO口 */ GPIO_InitStruct.Pin = LED_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct); /* 定义LED流水的状态 */ uint8_t LED_State = 0x01; while (1) { /* 判断按键状态 */ if(KeyState == 1) { HAL_Delay(10); if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { /* 按键按下 */ LED_State <<= 1; // 移位操作,将LED流水状态左移一位 if(LED_State == 0) { LED_State = 0x01; } HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, LED_State); // 更新LED输出状态 } KeyState = 0; } /* 流水效果 */ uint8_t i; for(i=0; i<8; i++) { HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, LED_State); LED_State <<= 1; if(LED_State == 0) { LED_State = 0x01; } HAL_Delay(100); } } } /* 配置系统时钟 */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* 错误处理函数 */ void Error_Handler(void) { while(1) { } } ``` 以上代码实现按键控制LED流水的效果,按下按键LED状态左移一位,松开按键LED流水效果继续。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值