第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库的十一程序,我们有必要解释的详细一点,并且与标准库进行一下比较:

代码解释

  1. 头文件引入
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
  • sys.h 可能包含了系统级的初始化函数,例如时钟配置。
  • delay.h 提供了延时函数的声明。
  • usart.h 可能包含了串口通信相关的函数声明(在这段代码中并未使用)。
  • led.h 包含了LED控制相关的函数声明。
  1. 主函数
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
  1. main.c:
  • 头文件包含: 包含了 STM32F4 系列的头文件以及延时函数的头文件。
  • LED_Init(): 使能 GPIOF 时钟,配置 PF9 和 PF10 为推挽输出模式。
  • main(): 初始化系统时钟和延时函数。进入无限循环中交替点亮和熄灭两个 LED,引脚的状态由 GPIO_SetBits() 和 GPIO_ResetBits() 控制。
  1. delay.c:
  • delay_init(): 配置 SysTick 定时器以每毫秒产生一次中断。SysTick_Config() 函数根据系统时钟频率进行配置。
  • delay_ms(): 根据 TimingDelay 变量的值进行延时,直到 TimingDelay 减少到零。
  • SysTick_Handler(): SysTick 中断处理程序,每毫秒减少 TimingDelay 变量的值。
  1. delay.h:
  • 定义了延时函数的声明,确保 delay.c 文件中的函数可以被其他文件引用。

2024.9.25 第一次修订,后期不再维护

posted @ 2024-09-27 14:55  hazy1k  阅读(14)  评论(0编辑  收藏  举报