第1章 跑马灯实验
第一章 跑马灯实验
1. 导入
任何一个单片机, 最简单的外设莫过于 IO 口的高低电平控制了,本章将通过一个经典的跑马灯程序,带大家开启 STM32F4 之旅, 通过本章的学习, 你将了解到 STM32F4 的 IO 口作为输出使用的方法。 在本章中, 我们将通过代码控制 ALIENTEK 探索者 STM32F4 开发板上的两个 LED: DS0 和 DS1 交替闪烁,实现类似跑马灯的效果。
2. 硬件设计
本章用到的硬件只有 LED(DS0 和 DS1)。其电路在 ALIENTEK 探索者 STM32F4 开发板上默认是已经连接好了的。 DS0 接 PF9, DS1 接 PF10。所以在硬件上不需要动任何东西。
![](file://C:\Users\qiu\AppData\Roaming\marktext\images\2024-08-30-16-26-57-image.png?msec=1727420090967)
3. 软件设计
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
int main(void)
{
HAL_Init(); // 初始化HAL库
Stm32_Clock_Init(336,8,2,7);// 设置时钟,168Mhz
delay_init(168); // 初始化延时函数
LED_Init(); // 初始化LED
while(1)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);// LED0对应引脚PF9拉低,亮,等同于LED0(0)
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_SET); // LED1对应引脚PF10拉高,灭,等同于LED1(1)
delay_ms(500); // 延时500ms
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9,GPIO_PIN_SET); // LED0对应引脚PF9拉高,灭,等同于LED0(1)
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10,GPIO_PIN_RESET);// LED1对应引脚PF10拉低,亮,等同于LED1(0)
delay_ms(500); // 延时500ms
}
}
代码包含了#include "led.h"这句,使得 LED0、 LED1、 LED_Init 等能在 main()函数里被调用。这里我们需要重申的是,在 HAL 库中,系统在启动的时候会调用 system_stm32f4xx.c 中的函数SystemInit()对系统时钟进行初始化,在时钟初始化完毕之后会调用 main()函数。 所以我们不需要再在 main()函数中调用 SystemInit()函数。当然如果有需要重新设置时钟系统,可以写自己的时钟设置代码, SystemInit()只是将时钟系统初始化为默认状态。
main()函数非常简单,先调用 delay_init()初始化延时,接着就是调用 LED_Init()来初始化GPIOF.9 和 GPIOF.10 为输出。最后在死循环里面实现 LED0 和 LED1 交替闪烁,间隔为 500ms。
上面是通过库函数来实现的 IO 操作,我们也可以修改 main()函数,直接通过位带操作达到同样的效果,大家不妨试试。 位带操作的代码如下:
int main(void)
{
HAL_Init(); //初始化 HAL 库
Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz
delay_init(168); //初始化延时函数
LED_Init(); //初始化 LED
while(1)
{
LED0 = 0; //LED0 亮
LED1 = 1; //LED1 灭
delay_ms(500);
LED0 = 1; //LED0 灭
LED1 = 0; //LED1 亮
delay_ms(500);
}
}
当然我们也可以通过直接操作相关寄存器的方法来设置 IO,我们只需要将主函数修改为如下内容:
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhz
delay_init(168); //初始化延时函数
LED_Init(); //初始化LED
while(1)
{
GPIOF->BSRR =GPIO_PIN_9; // LED0亮
GPIOF->BSRR = GPIO_PIN_10<<16; // LED1灭
delay_ms(500);
GPIOF->BSRR = GPIO_PIN_9<<16; // LED0灭
GPIOF->BSRR = GPIO_PIN_10; // LED1亮
delay_ms(500);
}
4. 小结
作为我们第一个HAL库的十一程序,我们有必要解释的详细一点,并且与标准库进行一下比较:
代码解释
- 头文件引入
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
sys.h
可能包含了系统级的初始化函数,例如时钟配置。delay.h
提供了延时函数的声明。usart.h
可能包含了串口通信相关的函数声明(在这段代码中并未使用)。led.h
包含了LED控制相关的函数声明。
- 主函数
int main(void)
{
HAL_Init(); // 初始化HAL库
Stm32_Clock_Init(336,8,2,7); // 设置时钟,168MHz
delay_init(168); // 初始化延时函数
LED_Init(); // 初始化LED
while(1)
{
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9, GPIO_PIN_RESET); // LED0对应引脚PF9拉低,亮,等同于LED0(0)
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10, GPIO_PIN_SET); // LED1对应引脚PF10拉高,灭,等同于LED1(1)
delay_ms(500); // 延时500ms
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET); // LED0对应引脚PF9拉高,灭,等同于LED0(1)
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET); // LED1对应引脚PF10拉低,亮,等同于LED1(0)
delay_ms(500); // 延时500ms
}
}
HAL_Init()
:初始化 STM32 的 HAL 库,这为后续的 HAL 函数调用做好准备。Stm32_Clock_Init(336,8,2,7)
:配置系统时钟,可能将系统时钟设置为 168 MHz。delay_init(168)
:初始化延时函数,设置延时时钟源。LED_Init()
:初始化 LED 控制相关的 GPIO 引脚。while(1)
:主循环中,LED0 和 LED1 交替闪烁,每个状态持续 500ms。
标准库改造
- 主函数
#include "stm32f4xx.h" // 包含 STM32F4 系列微控制器的头文件
#include "delay.h" // 包含延时函数的头文件
// LED 引脚初始化函数
void LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct; // 定义 GPIO 初始化结构体
// 使能 GPIOF 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
// 配置 PF9 和 PF10 为推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // 选择 PF9 和 PF10 引脚
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; // 设置为输出模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 输出速度设置为 50 MHz
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // 输出类型设置为推挽输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; // 不使用上下拉电阻
GPIO_Init(GPIOF, &GPIO_InitStruct); // 初始化 GPIOF
}
int main(void) {
// 系统时钟初始化
SystemInit();
// 延时函数初始化,设置系统时钟频率(假设为 168 MHz)
delay_init(168);
// LED 初始化
LED_Init();
while (1) {
// 点亮 PF9 (LED0)
GPIO_SetBits(GPIOF, GPIO_Pin_9);
// 熄灭 PF10 (LED1)
GPIO_ResetBits(GPIOF, GPIO_Pin_10);
delay_ms(500); // 延时 500 毫秒
// 熄灭 PF9 (LED0)
GPIO_ResetBits(GPIOF, GPIO_Pin_9);
// 点亮 PF10 (LED1)
GPIO_SetBits(dIOF, GPIO_Pin_10);
delay_ms(500); // 延时 500 毫秒
}
}
- delay.c
#include "delay.h" // 包含延时函数的头文件
// 延时计数变量
static __IO uint32_t TimingDelay;
// 延时函数初始化
void delay_init(uint8_t SYSCLK) {
// 配置 SysTick 定时器,用于产生毫秒级别的延时
if (SysTick_Config(SystemCoreClock / 1000)) {
// 如果配置失败,可以在这里进行错误处理
while (1); // 进入死循环,表示错误
}
}
// 毫秒级别的延时函数
void delay_ms(uint32_t ms) {
TimingDelay = ms; // 设置延时的毫秒数
while (TimingDelay != 0); // 等待直到计数器减少到零
}
// SysTick 中断处理函数
void SysTick_Handler(void) {
if (TimingDelay != 0) {
TimingDelay--; // 每毫秒减少计数器值
}
}
- delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f4xx.h" // 包含 STM32F4 系列微控制器的头文件
// 函数声明
void delay_init(uint8_t SYSCLK); // 延时初始化函数
void delay_ms(uint32_t ms); // 毫秒级别延时函数
#endif
main.c
:
- 头文件包含: 包含了 STM32F4 系列的头文件以及延时函数的头文件。
LED_Init()
: 使能 GPIOF 时钟,配置 PF9 和 PF10 为推挽输出模式。main()
: 初始化系统时钟和延时函数。进入无限循环中交替点亮和熄灭两个 LED,引脚的状态由GPIO_SetBits()
和GPIO_ResetBits()
控制。
delay.c
:
delay_init()
: 配置 SysTick 定时器以每毫秒产生一次中断。SysTick_Config()
函数根据系统时钟频率进行配置。delay_ms()
: 根据TimingDelay
变量的值进行延时,直到TimingDelay
减少到零。SysTick_Handler()
: SysTick 中断处理程序,每毫秒减少TimingDelay
变量的值。
delay.h
:
- 定义了延时函数的声明,确保
delay.c
文件中的函数可以被其他文件引用。
2024.9.25 第一次修订,后期不再维护