从零开始学嵌入式之STM32——8.流水灯

目录

前言

一、需求

二、硬件电路

三、分析

1.电路原理

[2. 结论](#2. 结论)

四、代码模块化设计思路

前言

本文以 STM32F103 单片机为核心,采用纯寄存器编程方式实现经典的流水灯案例,直接操作寄存器配置 GPIO 外设。通过从底层硬件原理出发,拆解 GPIO 时钟使能、工作模式配置、电平控制的完整流程,帮助读者摆脱 "库函数黑盒",深入理解 STM32 GPIO 外设的工作机制;同时结合模块化编程思想,将 LED 控制、延时功能拆分为独立模块,既兼顾代码的可移植性与可读性,也为嵌入式入门开发者建立规范的底层编程思维。

一、需求

嵌入式入门经典案例 ------ 使用 STM32F103 的 GPIOA0、GPIOA1、GPIOA8 引脚驱动 3 路 LED(LED1/LED2/LED3)实现流水灯效果,要求代码模块化、可移植、易扩展。

二、硬件电路

三、分析

1.电路原理

LED1、LED2、LED3 的阳极通过 2K 排阻接 3.3V 高电平,阴极分别接 GPIOA0、GPIOA1、GPIOA8 引脚:

点亮 LED:对应 GPIO 引脚输出低电平(阴极拉低,形成导通回路);

熄灭 LED:对应 GPIO 引脚输出高电平(阴极拉高,回路断开)。

2. 结论

需要将 GPIOA0、GPIOA1、GPIOA8 配置为推挽输出模式(可选50MHz 最大速度),通过控制引脚高低电平实现 LED 亮灭。

四、代码模块化设计思路

按照结构化的设计对代码进行划分和优化,将代码按照功能进行模块化,按照功能功能包装成函数。这样可以增加代码的可移植性和可读性。

首先,LED控制部分的源代码可以单独封装成一个.c文件:led.c,并且与之匹配一个led.h文件,以便提供相应的接口。

配置端口模式、初始状态的代码部分可以封装成一个初始化函数

led.h文件:

代码中通过使用#define将单片机的GPIO寄存器名称重命名为LED1\2\3,增加了代码的可读性。

cpp 复制代码
#ifndef __LED_H
#define __LED_H
//包含需要用到的头文件
#include "stm32f10x.h"
// LED接口宏定义  增加代码的可读性
#define LED1 GPIO_ODR_ODR0
#define LED2 GPIO_ODR_ODR1
#define LED3 GPIO_ODR_ODR8
// LED初始化
void LED_Init(void);
// 打开或关闭某一个LED
void LED_On(uint16_t led);
void LED_Off(uint16_t led);
// 翻转某一位的LED
void LED_Turn(uint16_t led);
// 全部打开或关闭
void LED_All_On(uint16_t leds[], uint8_t size);
void LED_All_Off(uint16_t leds[], uint8_t size);
#endif

led.c文件

在led.c文件中,直接操作寄存器来配置对应GPIO端口的工作模式和状态,以达到控制LED灯的工作状态。并将对应的操作封装成函数,在整体逻辑中直接调用函数来实现具体的功能。

注意:如果在for循环中定义并初始化循环变量,需要在keil中设置支持C99。 for(uint8_t i = 0; i < led; i++)

cpp 复制代码
#include "led.h"
// LED初始化
void LED_Init(void)
{
    //1.打开GPIOA的时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    //2.设置GPIOA的A1、A2、A3口为推挽输出模式,最高频率为50MHz
    GPIOA->CRL &= ~GPIO_CRL_CNF0;//在寄存器MODE为输出模式的时候,CNF寄存器 0 0 表示推挽输出模式
    GPIOA->CRL |= GPIO_CRL_MODE0;//MODE寄存器 1 1 表示输出模式,最高频率为50MHz
    GPIOA->CRL &= ~GPIO_CRL_CNF1;
    GPIOA->CRL |= GPIO_CRL_MODE1;
    GPIOA->CRH &= ~GPIO_CRH_CNF8;
    GPIOA->CRH |= GPIO_CRH_MODE8;
    LED_Off(LED1);
    LED_Off(LED2);
    LED_Off(LED3);
}
// 打开或关闭某一个LED
void LED_On(uint16_t led)
{
    GPIOA->ODR &= ~led;
}
void LED_Off(uint16_t led)
{
    GPIOA->ODR |= led;
}
// 翻转某一位的LED
void LED_Turn(uint16_t led)
{
    if((GPIOA->IDR & led) == 0)//端口的输出的状态可以在IDR中读取,注意:这里不能判断是否为1,因为是不同的位
    {
        LED_Off(led);
    }
    else
    {
        LED_On(led);
    }
}
// 全部打开或关闭
void LED_All_On(uint16_t leds[], uint8_t led_num)
{
    for(uint8_t i = 0; i < led_num; i++)
    {
        LED_On(leds[i]);
    }
}
void LED_All_Off(uint16_t leds[], uint8_t led_num)
{
    for(uint8_t i = 0; i < led; i++)
    {
        LED_Off(leds[i]);
    }
}

delay.h文件

cpp 复制代码
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void Delay_nms(uint16_t nms);
void Delay_nus(uint16_t nus);
#endif

delay.c文件

cpp 复制代码
#include "delay.h"

void Delay_nus(uint16_t nus)
{
    //使用系统的滴答计时器实现,滴答计时器可以实现一个机器周期减一个数,减到零的时候会将寄存器的SysTick_CTRL_COUNTFLAG位置1
    SysTick->LOAD = nus * 72;   //时钟周期为72MHz,一个机器周期是1/72us
    SysTick->CTRL |= 0x05;            //使用系统时钟(1);计数结束时不产生中断(0);使能定时器
    while((SysTick->CTRL & SysTick_CTRL_COUNTFLAG) == 0)//检测计数是否结束
    {}
    //关闭定时器
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE;
}

void Delay_nms(uint16_t nms)
{
    while(nms--)
    {
        Delay_nus(1000);//产生1ms的延时
    }
}

main.c文件

cpp 复制代码
#include "stm32f10x.h"
#include "delay.h"
#include "led.h"

#define DELAY_TIME 300  //LED等的闪烁间隔

int main(void)
{
    //1.初始化
    LED_Init();
    //2.创建一个LED灯的数组
    uint16_t leds[3] = {LED1, LED2, LED3};
    //3.在一个while循环中实现流水灯
    while(1)
    {
        for(uint16_t i = 0; i < 3; i++)
        {
            LED_On(leds[i]);
            Delay_nms(DELAY_TIME);
            LED_Off(leds[i]);
        }
    }
}
相关推荐
蓬荜生灰10 小时前
STM32(13)-- 位带操作
stm32·单片机·嵌入式硬件
哎呦 你干嘛~12 小时前
plc单片机485通讯
单片机·嵌入式硬件
szxinmai主板定制专家13 小时前
基于 PC 的控制技术+ethercat+linux实时系统,助力追踪标签规模化生产,支持国产化
arm开发·人工智能·嵌入式硬件·yolo·fpga开发
泡泡糖的中文规格书16 小时前
【硬件设计】LM358DR2G中文文档深度解读:高质量翻译与芯片特性全解析
嵌入式硬件·规格说明书·电路设计·运放·中文翻译·datasheet
czwxkn16 小时前
2STM32(stdl)GPIO输入
stm32·单片机·嵌入式硬件
码农三叔17 小时前
(9-1)电源管理与能源系统:电池选择与安全
人工智能·嵌入式硬件·安全·机器人·能源·人形机器人
qq_2466461919 小时前
openclaw快速安装-windows版
windows·stm32·单片机
染予21 小时前
13.AD软件操作:原理图如何导入PCB及导入常见错误
嵌入式硬件