本帖最后由 正点原子运营 于 2024-3-25 14:32 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
跑马灯程序是嵌入式开发的一个经典程序,类似于学习C语言时,编写的“HelloWorld”程序。跑马灯本质上是控制单片机的GPIO输出高低电平,以此达到控制LED等亮灭状态的切换。通过本章的学习,读者将学习到GPIO输出模式的使用。 本章分为如下几个小节: 10.1 硬件设计 10.2 程序设计 10.3 下载验证
10.1 硬件设计 10.1.1 例程功能 1. LED0和LED1交替闪烁。
10.1.2 硬件资源 1. LED LED0 -PB5 LED1 -PE5
10.1.3 原理图 本章实验用的两个M144Z-M3最小系统板STM32F103版板载LED,分别为LED0(红色)和LED1(绿色),其与板载MCU的连接原理图,如下图所示: 从上面原理图中可以看出,LED0和LED1的正极分别通过一个限流电阻连接到了电源正极,而负极分别与MCU的PB5引脚和PE5引脚相连接,因此只需通过控制PB5引脚或PE5引脚输出低电平,则能分别控制LED0和LED1点亮,反之,则熄灭。
10.2 程序设计 10.2.1 HAL库的GPIO驱动 本章实验中要通过控制GPIO引脚输出高低电平来控制LED的亮灭状态,因此需要以下两个步骤: ①:配置GPIO引脚为输出模式 ②:设置GPIO引脚输出电平 在HAL库中对应的驱动函数如下: ①:配置GPIO引脚 该函数用于配置GPIO引脚的功能和各项参数,其函数原型如下所示: voidHAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); 该函数的形参描述,如下表所示: 表10.2.1.1 函数HAL_GPIO_Init()形参描述 该函数的返回值描述,如下表所示: 表10.2.1.2 函数HAL_GPIO_Init()返回值描述 该函数使用GPIO_InitTypeDef类型的结构体变量传入GPIO引脚的配置参数,该结构体的定义如下所示: - typedef struct
- {
- uint32_t Pin; /* 待配置的引脚 */
- uint32_t Mode; /* 模式 */
- uint32_t Pull; /* 上拉/下拉 */
- uint32_t Speed; /* 速度 */
- }GPIO_InitTypeDef;
复制代码该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- GPIO_InitTypeDef gpio_init_struct = {0};
-
- /* 配置PA0引脚为输出模式 */
- gpio_init_struct.Pin = GPIO_PIN_0;
- gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
- gpio_init_struct.Pull = GPIO_PULLUP;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
- HAL_GPIO_Init(GPIOA, &gpio_init_struct);
- }
复制代码②:设置GPIO引脚输出电平 该函数用于设置GPIO引脚输出指定电平(高电平或低电平),其函数原型如下所示: - voidHAL_GPIO_WritePin( GPIO_TypeDef* GPIOx,
- uint16_t GPIO_Pin,
- GPIO_PinState PinState);
复制代码该函数的形参描述,如下表所示: 表10.2.1.3 函数HAL_GPIO_WritePin()形参描述 该函数的返回值描述,如下表所示: 表10.2.1.4 函数HAL_GPIO_WritePin()返回值描述 该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- /* 设置PA0引脚输出低电平 */
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
- /* 设置PA0引脚输出高电平 */
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
- }
复制代码③:翻转GPIO引脚输出电平(补充) 该函数用于翻转GPIO引脚的输出电平,其函数原型如下所示: - voidHAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
复制代码该函数的形参描述,如下表所示: 表10.2.1.5HAL_GPIO_TogglePin()形参描述 该函数的返回值描述,如下表所示: 表10.2.1.6HAL_GPIO_TogglePin()返回值描述 该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- /* 翻转PA0引脚输出电平 */
- HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
- }
复制代码
10.2.2 LED驱动 LED驱动主要就是控制GPIO引脚输出高低电平,来控制LED亮起或熄灭。本章实验中,LED的驱动代码包括led.c和led.h两个文件(本书配套实验例程中,器件或外设的驱动代码基本都由一个C源文件和一个对应的头文件组成)。 根据原理图可知,应当将PE5引脚和PE6引脚配置为通用输出模式,并在需要控制LED0(LED1)亮起的时候,设置PE5引脚(PE6引脚)输出低电平,在需要控制LED0(LED1)熄灭的时候,设置PE5引脚(PE6引脚)输出高电平。 LED驱动中,对引脚的定义,如下所示: - #define LED0_GPIO_PORT GPIOE
- #define LED0_GPIO_PIN GPIO_PIN_5
- #define LED0_GPIO_CLK_ENABLE() do { __HAL_RCC_GPIOE_CLK_ENABLE(); } while (0)
- #define LED1_GPIO_PORT GPIOE
- #define LED1_GPIO_PIN GPIO_PIN_6
- #define LED1_GPIO_CLK_ENABLE() do { __HAL_RCC_GPIOE_CLK_ENABLE(); } while (0)
复制代码在后续的实验代码中,基本都会使用上述的宏定义的方式定义GPIO引脚的各种信息(GPIO端口、GPIO引脚号、GPIO端口时钟使能和GPIO复用功能等信息)。 LED驱动中,操作引脚的定义,如下所示: - #define LED0(x) \
- do { x ? \
- HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN,GPIO_PIN_SET) : \
- HAL_GPIO_WritePin(LED0_GPIO_PORT,LED0_GPIO_PIN, GPIO_PIN_RESET); \
- } while (0)
- #define LED1(x) \
- do { x ? \
- HAL_GPIO_WritePin(LED1_GPIO_PORT,LED1_GPIO_PIN, GPIO_PIN_SET) : \
- HAL_GPIO_WritePin(LED1_GPIO_PORT,LED1_GPIO_PIN, GPIO_PIN_RESET); \
- } while (0)
- #define LED0_TOGGLE() \
- do { \
- HAL_GPIO_TogglePin(LED0_GPIO_PORT,LED0_GPIO_PIN); \
- } while (0)
- #define LED1_TOGGLE() \
- do { \
- HAL_GPIO_TogglePin(LED1_GPIO_PORT,LED1_GPIO_PIN); \
- } while (0)
复制代码在后续实验代码中,基本都会使用上述的宏定义的方式定义GPIO引脚的操作(设置GPIO引脚输出高电平或低电平、翻转GPIO引脚输出电平和读取GPIO引脚输入电平等操作)。 LED驱动中,LED的初始化函数,如下所示: - /**
- *@brief 初始化LED
- *@param 无
- *@retval 无
- */
- void led_init(void)
- {
- GPIO_InitTypeDef gpio_init_struct = {0};
-
- /* 使能GPIO端口时钟 */
- LED0_GPIO_CLK_ENABLE();
- LED1_GPIO_CLK_ENABLE();
-
- /* 配置LED0控制引脚 */
- gpio_init_struct.Pin = LED0_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
- gpio_init_struct.Pull = GPIO_PULLDOWN;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
- HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct);
-
- /* 配置LED1控制引脚 */
- gpio_init_struct.Pin = LED1_GPIO_PIN;
- gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
- gpio_init_struct.Pull = GPIO_PULLDOWN;
- gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
- HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct);
-
- /* 关闭LED0、LED1 */
- LED0(1);
- LED1(1);
- }
复制代码LED的初始化函数中,使能了LED0和LED1控制引脚的GPIO端口时钟,并将其配置为通用输出模式,最后默认将LED的状态设置为熄灭状态。
10.2.3 实验应用代码 本章实验的应用代码,如下所示: - int main(void)
- {
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
-
- while (1)
- {
- LED0(0); /* 开启LED0 */
- LED1(1); /* 关闭LED1 */
- delay_ms(500); /* 延时500毫秒 */
- LED0(1); /* 关闭LED0 */
- LED1(0); /* 开启LED1 */
- delay_ms(500); /* 延时500毫秒 */
- }
- }
复制代码可以看到,在调用LED初始化之前,会先调用以下函数: - HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
复制代码①:第一行代码用于HAL库的初始化。 ②:第二行代码用于配置系统时钟,因为大部分实验例程都无需考虑低功耗应用,因此大部分实验例程配置系统时钟的参数都与本实验一致,将系统主频配置为72MHz。 ③:第三行代码用于初始化延时功能,为后续的应用代码提供微秒级和毫秒级的延时的功能。 ④:第四行代码用于初始化用于调试功能的USART1,为后续的应用代码提供printf等功能,方面通过串口调试助手查看程序运行情况。 ⑤:以上四行代码都是执行一些必要的初始化,基本在后续的每一个实验例程中都会看见,后续不再赘述。 执行完必要的初始化后,紧接着初始化LED,然后在一个while循环中重复地控制板载LED0和LED1轮流亮起和熄灭,轮流时间为500毫秒,以此实现跑马灯的效果。
10.3 下载验证 在完成编译和烧录操作后,可以看到板子上的LED0和LED1轮流亮起和熄灭,轮流的时间大约为500毫秒,与预期的实验效果相符。 |