赞
踩
硬件:正点原子探索者 V3 STM32F407 开发板
单片机:STM32F407ZGT6
Keil版本:5.32
STM32CubeMX版本:6.9.2
STM32Cube MCU Packges版本:STM32F4 V1.27.1
之前介绍了很多关于点灯的方法,比如轮询、定时器中断、PWM、按键点灯等方式,这些文章使用的编程方法都不是模块化分层的编写方式,往往会导致代码可读性差、重用性差、扩展性差以及测试和维护困难等问题。为了避免这些问题,我们实际工作中通常会采用模块化分层的编写方法,这样可以确保代码结构清晰、功能明确,提高代码的可读性和可维护性,同时降低功能之间的耦合度,增强代码的重用性(无论是STM32F103还是STM32F407或是STM32H系列等,直接将文件复制使用)和扩展性。模块化分层的编写方式还有助于实现代码的并行开发,提高开发效率,使得整个项目更加易于管理和维护。
基于之前的按键点灯的程序进行修改,我将为您详细阐述如何使用STM32F407的HAL库,并结合STM32CubeMX配置工具,通过模块化分层方法用按键分别控制两个LED灯,即用引脚PE2和PE3按键分别控制PF9和PF10引脚LED。这一简洁而高效的流程将助您迅速掌握LED和按键模块化分层的编写方法。
LED灯
用drv_led.h和drv_led.c作为一个独立的模块,并提供三个LED驱动程序的接口int LedDrvInit(BoardLed led);//初始化指定的LED
int LedDrvWrite(BoardLed led, LedStatus status);//设置指定LED的状态
int LedDrvRead(BoardLed led);//读取指定LED的当前状态
按键
用drv_key.h和drv_key.c作为一个独立的模块,并提供两个KEY驱动程序的接口
int KeyDrvInit(BoardKey key);//用于初始化指定的按键。
int KeyDrvRead(BoardKey key);//用于读取指定按键的状态。
选择时钟源:
新建文件:LED灯的驱动drv_led.h和drv_led.c : drv_led.h
- #ifndef __DRV_LED_H
- #define __DRV_LED_H
-
- typedef enum{
- LED1 = 1,
- LED2
- }BoardLed;
-
- typedef enum{
- led_on = 0,
- led_off = 1
- }LedStatus;
-
- #define LED1_PIN GPIO_PIN_9
- #define LED1_PORT GPIOF
- #define LED2_PIN GPIO_PIN_10
- #define LED2_PORT GPIOF
-
-
- int LedDrvInit(BoardLed led);
- int LedDrvWrite(BoardLed led, LedStatus status);
- int LedDrvRead(BoardLed led);
-
- #endif /* __DRV_LED_H */
drv_led.c
- #include "drv_led.h"
- #include "stm32f4xx_hal.h"
-
- int LedDrvInit(BoardLed led)
- {
- switch(led)
- {
- case LED1:
- {
- break;
- }
- case LED2:
- {
- break;
- }
- default:break;
- }
-
- return 0;
- }
-
- int LedDrvWrite(BoardLed led, LedStatus status)
- {
- switch(led)
- {
- case LED1:
- {
- HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, (GPIO_PinState)status);
- break;
- }
- case LED2:
- {
- HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, (GPIO_PinState)status);
- break;
- }
-
- default:break;
- }
-
- return 0;
- }
-
- int LedDrvRead(BoardLed led)
- {
- LedStatus status = led_on;
- switch(led)
- {
- case LED1:
- {
- status = (LedStatus)HAL_GPIO_ReadPin(LED1_PORT, LED1_PIN);
- break;
- }
- case LED2:
- {
- status = (LedStatus)HAL_GPIO_ReadPin(LED2_PORT, LED2_PIN);
- break;
- }
- default:break;
- }
-
- return status;
- }
- // #ifndef __DRV_KEY_H 是预处理指令,用于防止头文件的内容在一个编译单元中被多次包含。
- // 如果__DRV_KEY_H还没有被定义,则继续处理此头文件的内容;如果已经定义了,则忽略。
- #ifndef __DRV_KEY_H
- #define __DRV_KEY_H
-
- // 定义一个名为BoardKey的枚举类型,用于表示不同的按键。
- typedef enum{
- K1 = 1,// K1键,其值为1
- K2, // K2键,其值为2(因为K1为1,所以K2自动为2)
- K3,
- K4
- }BoardKey;
-
- // 定义一个名为KeyStatus的枚举类型,用于表示按键的状态。
- typedef enum{
- isPressed = 0, // 按键被按下,其值为0
- isReleased = 1 // 按键被释放,其值为1
- }KeyStatus;
-
- // 定义了一系列的宏,用于表示按键对应的GPIO引脚和端口。
- // 例如,K1_PIN代表K1键连接的GPIO引脚,而K1_PORT代表该引脚所在的GPIO端口。
- #define K1_PIN GPIO_PIN_0
- #define K1_PORT GPIOA
- #define K2_PIN GPIO_PIN_2
- #define K2_PORT GPIOE
- #define K3_PIN GPIO_PIN_3
- #define K3_PORT GPIOE
- #define K4_PIN GPIO_PIN_4
- #define K4_PORT GPIOE
-
- int KeyDrvInit(BoardKey key);//用于初始化指定的按键。
- int KeyDrvRead(BoardKey key);//用于读取指定按键的状态。
-
- #endif /* __DRV_KEY_H */
drv_key.c - #include "drv_key.h"
- #include "stm32f4xx_hal.h"
-
- int KeyDrvInit(BoardKey key)
- {
- switch(key)
- {
- case K1:
- {
- break;
- }
- case K2:
- {
- break;
- }
- case K3:
- {
- break;
- }
- case K4:
- {
- break;
- }
- default:break;
- }
-
- return 0;
- }
-
- int KeyDrvRead(BoardKey key)
- {
- KeyStatus status = isReleased;
- switch(key)
- {
- case K1:
- {
- status = (KeyStatus)HAL_GPIO_ReadPin(K1_PORT, K1_PIN);
-
- break;
- }
- case K2:
- {
- status = (KeyStatus)HAL_GPIO_ReadPin(K2_PORT, K2_PIN);
- break;
- }
- case K3:
- {
- status = (KeyStatus)HAL_GPIO_ReadPin(K3_PORT, K3_PIN);
- break;
- }
- case K4:
- {
- status = (KeyStatus)HAL_GPIO_ReadPin(K4_PORT, K4_PIN);
- break;
- }
- default:break;
- }
-
- return status;
- }
- #include "drv_led.h"
- #include "drv_key.h"
- /* USER CODE BEGIN 2 */
- LedStatus d1_s = led_off; //灯状态
- LedStatus d2_s = led_off;
- LedDrvInit(LED1);
- LedDrvInit(LED2);
- KeyDrvInit(K2);
- KeyDrvInit(K3);
- /* USER CODE END 2 */
-
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
-
- /* USER CODE BEGIN 3 */
- if(KeyDrvRead(K2) == isPressed)/* 检测按键的状态 */
- {
- HAL_Delay(100);/* 消抖处理 */
- if(KeyDrvRead(K2) == isPressed)
- {
- d1_s =!d1_s; /* 切换LED1状态 */
- LedDrvWrite(LED1, d1_s); /* 更新LED1的显示状态 */
- }
- }
- if(KeyDrvRead(K3) == isPressed) /* 检测按键的状态 */
- {
- HAL_Delay(100);/* 消抖处理 */
- if(KeyDrvRead(K3) == isPressed)
- {
- d2_s =!d2_s;/* 切换LED1状态 */
- LedDrvWrite(LED2, d2_s);/* 检测按键的状态 */
- }
- }
- }
烧录程序:将编译好的程序用ST-LINK烧录到STM32微控制器中。
观察结果:一旦程序烧录完成并运行,你应该能看到按不同的按键会点亮不同的LED灯。如果一切正常,恭喜你,你现在已经是一个掌握模块化分层的编写“点灯大师”了!
模块化分层的编写方式对之前的代码封装了一层,提供了一个与LED和按键硬件交互的接口,使得软件开发者可以在不直接操作硬件的情况下控制LED和KEY,可以直接用到STM32F103中,如果引脚不一样,只需修改引脚即可。通过上面的代码,希望你更多的采用模块化分层的编写方式,确保代码结构清晰、功能明确,提高可读性和可维护性,降低功能耦合,增强重用和扩展性,也促进并行开发,提升效率,便于项目管理和维护。
1.确保你的开发环境和工具链已经正确安装和配置。
2.在STM32CubeMX中配置GPIO时,注意选择正确的引脚和模式。
3.在编写代码时,确保使用正确的GPIO端口和引脚宏定义。
4.LED没有按预期点亮,按一下复位键,检查代码、连接和电源是否正确。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。