摘要:本文以 STM32 驱动 74HC138 译码器点亮 LED 为例,先结合原理图与真值表分析 3 输入 8 输出的译码逻辑,再通过直接操作寄存器完成 RCC 时钟使能、PC3 输出配置与 E3 使能,最终点亮 LED10。随后针对直接寄存器操作晦涩难懂的问题,依次介绍宏定义、结构体与库函数三种程序优化方式,重点讲解宏定义方式下 GPIOA_CRL 寄存器中 MODE 位与 CNF 位的设置/清除逻辑,以及 PA0、PA1、PA4 推挽输出模式的配置方法。
项目从0->1步骤:
-
看实物——明确自己的需求
-
看原理图——1、高电平亮还是低电平亮 2、连到哪个引脚了——PC3
-
看自己的芯片工作原理
-
开启 RCC 时钟使能
-
模式——输入和输出配置——配置为输出
-
配置输出类型——推挽输出(开漏输出)
-
配置输出速度——输出速度如何选择?需要功耗低的场景,并且数据传输速率不高——低速;数据传输速度高时必须用高速
-
输出数据
-
总结——找地址 RCC 的地址——对应位写数据
-
优化:使用结构体封装好寄存器——声明结构体变量的方式定义地址
一、利用74HC138译码器点亮LED
1. 分析原理图(3 输入 8 输出)

138-A、138-B、138-C 对应 PA0、PA1、PA4


2. 结合真值表来辅助判断 LED 亮灭

3. 通过寄存器配置来点亮 LED

代码实现:
RCC 使能:
*(volatile unsigned int *)0x40021018 |=(1<<4);//RCC时钟使能 C

E3 使能:
//PC3输出模式
*(volatile unsigned int *)0x40011000 |=(1<<12); //PC3
*(volatile unsigned int *)0x40011000 &=~(1<<13);//MODE 01
//PC3推挽输出模式
*(volatile unsigned int *)0x40011000 |=~(1<<14); //PC3
*(volatile unsigned int *)0x40011000 &=~(1<<15);//CNF 01
*(volatile unsigned int *)0x4001100C |=(1<<3); // PC3置1 E3使能
输出数据寄存器
总结:
最终点亮 LED10。
二、程序优化
上述直接对寄存器操作的方法比较晦涩难懂。
所以我们可以使用一些方法简化代码,同时提高程序的可读性。
方式:
1. 宏定义方式优化
2. 结构体方式优化
3. 库方式编写
1. 宏定义方式优化
代码实现:
//宏定义:将GPIOA端口配置低寄存器(CRL)的地址0x40010800映射为宏GPIOA_CRL
//CRL寄存器控制PA0~PA7共8个引脚,每个引脚占用4位(MODE[1:0] + CNF[1:0])
#define GPIOA_CRL *(volatile unsigned int *)0x40010800
//==================== PA0 配置 ====================
//PA0输出模式:设置MODE位为01(输出模式,最大速度10MHz)
GPIOA_CRL |=(1<<0); //置位bit0(MODE0),PA0的MODE[1:0] = 01
GPIOA_CRL &=~(1<<1);//清零bit1(MODE1),确保MODE[1:0] = 01
//PA0推挽输出模式:设置CNF位为00(通用推挽输出)
GPIOA_CRL &=~(1<<2); //清零bit2(CNF0),CNF[1:0] = 00
GPIOA_CRL &=~(1<<3);//清零bit3(CNF1),确保CNF[1:0] = 00
//==================== PA1 配置 ====================
//PA1输出模式:PA1的4位配置位于bit4~bit7
GPIOA_CRL |=(1<<4); //置位bit4(MODE0),PA1的MODE[1:0] = 01
GPIOA_CRL &=~(1<<5);//清零bit5(MODE1),确保MODE[1:0] = 01
//PA1推挽输出模式
GPIOA_CRL &=~(1<<6); //清零bit6(CNF0),CNF[1:0] = 00
GPIOA_CRL &=~(1<<7);//清零bit7(CNF1),确保CNF[1:0] = 00
//==================== PA4 配置 ====================
//PA4输出模式:PA4的4位配置位于bit16~bit19
GPIOA_CRL |=(1<<16); //置位bit16(MODE0),PA4的MODE[1:0] = 01
GPIOA_CRL &=~(1<<17);//清零bit17(MODE1),确保MODE[1:0] = 01
//PA4推挽输出模式
GPIOA_CRL &=~(1<<18); //清零bit18(CNF0),CNF[1:0] = 00
GPIOA_CRL &=~(1<<19);//清零bit19(CNF1),确保CNF[1:0] = 00
2. 结构体方式优化
代码实现:
typedef struct
{
volatile unsigned int CRL; /* 0x00 端口配置低寄存器(Pin0~7) */
volatile unsigned int CRH; /* 0x04 端口配置高寄存器(Pin8~15) */
volatile unsigned int IDR; /* 0x08 端口输入数据寄存器 */
volatile unsigned int ODR; /* 0x0C 端口输出数据寄存器 */
volatile unsigned int BSRR; /* 0x10 端口位设置/清除寄存器 */
volatile unsigned int BRR; /* 0x14 端口位清除寄存器 */
volatile unsigned int LCKR; /* 0x18 端口配置锁定寄存器 */
} GPIO_TypeDef;
3.使用库(HAL LL STD)进行编程
下一篇文章单开一个文档进行详解;
三、总结
本文以 STM32 驱动 74HC138 译码器点亮 LED10 为主线,完整走通了从硬件分析到软件实现再到代码优化的全过程,核心步骤可概括为以下三个阶段:
- 原理图与真值表分析:确认 138-A、138-B、138-C 分别对应 PA0、PA1、PA4,结合 3 输入 8 输出的译码逻辑与真值表,判断 LED 的亮灭条件。
- 寄存器直接操作:依次完成 RCC 时钟使能、PC3 输出模式与推挽输出配置、E3 使能,最终通过输出数据寄存器点亮 LED10。
- 程序优化:针对直接操作寄存器晦涩难懂的问题,依次引入宏定义、结构体与库函数三种方式,在简化代码的同时提升可读性与可维护性。
其中宏定义方式重点讲解了 GPIOA_CRL 寄存器中 MODE 位与 CNF 位的设置/清除逻辑,以及 PA0、PA1、PA4 推挽输出模式的配置方法,这也是理解 STM32 GPIO 底层配置的关键。
四、参考资料
- STM32 参考手册(Reference Manual):GPIO 寄存器描述、RCC 时钟使能相关章节。
- 74HC138 数据手册(Datasheet):3 线-8 线译码器真值表与引脚功能说明。
- STM32 标准外设库 / HAL 库 / LL 库官方文档:GPIO 配置接口说明。
五、下篇预告
本文介绍的宏定义与结构体方式虽然已经比直接操作寄存器清晰不少,但仍有大量底层细节需要开发者自行维护。下一篇文章将单开一个文档,详细讲解如何使用库函数(HAL、LL、STD)进行编程,借助官方封装好的接口进一步简化 GPIO 配置流程,让代码更简洁、更易移植,敬请期待。
321



被折叠的 条评论
为什么被折叠?



