OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 901|回复: 0

《M144Z-M3最小系统板使用指南——STM32F103版》第十章 跑马灯实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4667
金钱
4667
注册时间
2019-5-8
在线时间
1224 小时
发表于 2024-3-26 11:57:30 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2024-3-25 14:32 编辑

第十章 跑马灯实验
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板

2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boar ... _mini_sysboard.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32技术交流QQ群:725095144

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

跑马灯程序是嵌入式开发的一个经典程序,类似于学习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的连接原理图,如下图所示:                              
image001.png
10.1.3.1 LED与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);
该函数的形参描述,如下表所示:
QQ截图20240325143059.png
表10.2.1.1 函数HAL_GPIO_Init()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240325143110.png
表10.2.1.2 函数HAL_GPIO_Init()返回值描述
该函数使用GPIO_InitTypeDef类型的结构体变量传入GPIO引脚的配置参数,该结构体的定义如下所示:
  1. typedef struct
  2. {
  3.     uint32_t Pin;       /* 待配置的引脚 */
  4.     uint32_t Mode;      /* 模式 */
  5.     uint32_t Pull;      /* 上拉/下拉 */
  6.     uint32_t Speed;     /* 速度 */
  7. }GPIO_InitTypeDef;
复制代码
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    GPIO_InitTypeDef gpio_init_struct = {0};
  5.    
  6.     /* 配置PA0引脚为输出模式 */
  7.    gpio_init_struct.Pin = GPIO_PIN_0;
  8.    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
  9.    gpio_init_struct.Pull = GPIO_PULLUP;
  10.    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  11.    HAL_GPIO_Init(GPIOA, &gpio_init_struct);
  12. }
复制代码
②:设置GPIO引脚输出电平
该函数用于设置GPIO引脚输出指定电平(高电平或低电平),其函数原型如下所示:
  1. voidHAL_GPIO_WritePin( GPIO_TypeDef* GPIOx,
  2.                          uint16_t GPIO_Pin,
  3.                          GPIO_PinState PinState);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240325143120.png
表10.2.1.3 函数HAL_GPIO_WritePin()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240325143127.png
表10.2.1.4 函数HAL_GPIO_WritePin()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     /* 设置PA0引脚输出低电平 */
  5.    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
  6.     /* 设置PA0引脚输出高电平 */
  7.    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
  8. }
复制代码
③:翻转GPIO引脚输出电平(补充)
该函数用于翻转GPIO引脚的输出电平,其函数原型如下所示:
  1. voidHAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240325143135.png
表10.2.1.5HAL_GPIO_TogglePin()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240325143141.png
表10.2.1.6HAL_GPIO_TogglePin()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     /* 翻转PA0引脚输出电平 */
  5.    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
  6. }
复制代码

10.2.2 LED驱动
LED驱动主要就是控制GPIO引脚输出高低电平,来控制LED亮起或熄灭。本章实验中,LED的驱动代码包括led.c和led.h两个文件(本书配套实验例程中,器件或外设的驱动代码基本都由一个C源文件和一个对应的头文件组成)。
根据原理图可知,应当将PE5引脚和PE6引脚配置为通用输出模式,并在需要控制LED0(LED1)亮起的时候,设置PE5引脚(PE6引脚)输出低电平,在需要控制LED0(LED1)熄灭的时候,设置PE5引脚(PE6引脚)输出高电平。
LED驱动中,对引脚的定义,如下所示:
  1. #define LED0_GPIO_PORT         GPIOE
  2. #define LED0_GPIO_PIN          GPIO_PIN_5
  3. #define LED0_GPIO_CLK_ENABLE() do { __HAL_RCC_GPIOE_CLK_ENABLE(); } while (0)
  4. #define LED1_GPIO_PORT         GPIOE
  5. #define LED1_GPIO_PIN          GPIO_PIN_6
  6. #define LED1_GPIO_CLK_ENABLE() do { __HAL_RCC_GPIOE_CLK_ENABLE(); } while (0)
复制代码
在后续的实验代码中,基本都会使用上述的宏定义的方式定义GPIO引脚的各种信息(GPIO端口、GPIO引脚号、GPIO端口时钟使能和GPIO复用功能等信息)。
LED驱动中,操作引脚的定义,如下所示:
  1. #define LED0(x)                                                             \
  2.      do { x ?                                                                \
  3.          HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN,GPIO_PIN_SET) :  \
  4.          HAL_GPIO_WritePin(LED0_GPIO_PORT,LED0_GPIO_PIN, GPIO_PIN_RESET); \
  5.      } while (0)
  6. #define LED1(x)                                                             \
  7.      do { x ?                                                                \
  8.          HAL_GPIO_WritePin(LED1_GPIO_PORT,LED1_GPIO_PIN, GPIO_PIN_SET) :  \
  9.          HAL_GPIO_WritePin(LED1_GPIO_PORT,LED1_GPIO_PIN, GPIO_PIN_RESET); \
  10.      } while (0)
  11. #define LED0_TOGGLE()                                      \
  12.      do {                                                    \
  13.          HAL_GPIO_TogglePin(LED0_GPIO_PORT,LED0_GPIO_PIN); \
  14.      } while (0)
  15. #define LED1_TOGGLE()                                      \
  16.      do {                                                    \
  17.          HAL_GPIO_TogglePin(LED1_GPIO_PORT,LED1_GPIO_PIN); \
  18.      } while (0)
复制代码
在后续实验代码中,基本都会使用上述的宏定义的方式定义GPIO引脚的操作(设置GPIO引脚输出高电平或低电平、翻转GPIO引脚输出电平和读取GPIO引脚输入电平等操作)。
LED驱动中,LED的初始化函数,如下所示:
  1. /**
  2. *@brief   初始化LED
  3. *@param   无
  4. *@retval  无
  5. */
  6. void led_init(void)
  7. {
  8.    GPIO_InitTypeDef gpio_init_struct = {0};
  9.    
  10.     /* 使能GPIO端口时钟 */
  11.    LED0_GPIO_CLK_ENABLE();
  12.    LED1_GPIO_CLK_ENABLE();
  13.    
  14.     /* 配置LED0控制引脚 */
  15.    gpio_init_struct.Pin = LED0_GPIO_PIN;
  16.    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
  17.    gpio_init_struct.Pull = GPIO_PULLDOWN;
  18.    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
  19.    HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct);
  20.    
  21.     /* 配置LED1控制引脚 */
  22.    gpio_init_struct.Pin = LED1_GPIO_PIN;
  23.    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
  24.    gpio_init_struct.Pull = GPIO_PULLDOWN;
  25.    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
  26.    HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct);
  27.    
  28.     /* 关闭LED0、LED1 */
  29.    LED0(1);
  30.    LED1(1);
  31. }
复制代码
LED的初始化函数中,使能了LED0和LED1控制引脚的GPIO端口时钟,并将其配置为通用输出模式,最后默认将LED的状态设置为熄灭状态。

10.2.3 实验应用代码
本章实验的应用代码,如下所示:
  1. int main(void)
  2. {
  3.    HAL_Init();                         /* 初始化HAL库 */
  4.    sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
  5.    delay_init(72);                     /* 初始化延时 */
  6.    usart_init(115200);                 /* 初始化串口 */
  7.    led_init();                         /* 初始化LED */
  8.    
  9.     while (1)
  10.     {
  11.          LED0(0);                        /* 开启LED0 */
  12.          LED1(1);                        /* 关闭LED1 */
  13.          delay_ms(500);                  /* 延时500毫秒 */
  14.          LED0(1);                        /* 关闭LED0 */
  15.          LED1(0);                        /* 开启LED1 */
  16.          delay_ms(500);                  /* 延时500毫秒 */
  17.     }
  18. }
复制代码
可以看到,在调用LED初始化之前,会先调用以下函数:
  1. HAL_Init();                             /* 初始化HAL库 */
  2. sys_stm32_clock_init(RCC_PLL_MUL9);    /* 配置时钟,72MHz */
  3. delay_init(72);                         /* 初始化延时 */
  4. usart_init(115200);                    /* 初始化串口 */
复制代码
①:第一行代码用于HAL库的初始化。
②:第二行代码用于配置系统时钟,因为大部分实验例程都无需考虑低功耗应用,因此大部分实验例程配置系统时钟的参数都与本实验一致,将系统主频配置为72MHz。
③:第三行代码用于初始化延时功能,为后续的应用代码提供微秒级和毫秒级的延时的功能。
④:第四行代码用于初始化用于调试功能的USART1,为后续的应用代码提供printf等功能,方面通过串口调试助手查看程序运行情况。
⑤:以上四行代码都是执行一些必要的初始化,基本在后续的每一个实验例程中都会看见,后续不再赘述。
执行完必要的初始化后,紧接着初始化LED,然后在一个while循环中重复地控制板载LED0和LED1轮流亮起和熄灭,轮流时间为500毫秒,以此实现跑马灯的效果。

10.3 下载验证
在完成编译和烧录操作后,可以看到板子上的LED0和LED1轮流亮起和熄灭,轮流的时间大约为500毫秒,与预期的实验效果相符。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 05:55

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表