STM32 HAL库函数原理解析

server/2025/3/26 9:31:41/

从 51 单片机到 STM32:以串口外设为例剖析 HAL 库的层层封装

在 51 单片机的学习过程中,我们就已熟知寄存器是控制单片机外设功能的核心。当过渡到 STM32 单片机时,会发现每个外设内部都存在大量功能各异且相互独立的寄存器。正是这些不同类型寄存器的协同工作,让单片机能够依据开发者的需求实现 “自定义” 功能。

单片机厂商为了高效管理,将模块的处理单元封装成不同类型的寄存器。比如,有专门负责数据管理的数据寄存器、监测外设状态的状态寄存器以及控制功能的功能寄存器。深入探究单个寄存器,尽管 STM32 是 32 位单片机,寄存器为 32 位,但多数寄存器会预留 16 位作为保留位,实际操作主要通过 16 个位来完成。所以,本质上开发者是在和寄存器中的比特位进行交互。

ST 公司在寄存器和比特位的封装上展现出了精妙的设计。它运用宏定义的方式,将比特位与实际地址一一对应。以 USART(通用同步异步收发传输器)状态寄存器(SR)为例,以下是相关的宏定义:

#define  USART_SR_PE                         ((uint16_t)0x0001)            /*!< 奇偶校验错误 */
#define  USART_SR_FE                         ((uint16_t)0x0002)            /*!< 帧错误 */
#define  USART_SR_NE                         ((uint16_t)0x0004)            /*!< 噪声错误标志 */
#define  USART_SR_ORE                        ((uint16_t)0x0008)            /*!< 溢出错误 */
#define  USART_SR_IDLE                       ((uint16_t)0x0010)            /*!< 检测到空闲线路 */
#define  USART_SR_RXNE                       ((uint16_t)0x0020)            /*!< 接收数据寄存器非空 */
#define  USART_SR_TC                         ((uint16_t)0x0040)            /*!< 传输完成 */
#define  USART_SR_TXE                        ((uint16_t)0x0080)            /*!< 发送数据寄存器为空 */
#define  USART_SR_LBD                        ((uint16_t)0x0100)            /*!< LIN 中断检测标志 */
#define  USART_SR_CTS                        ((uint16_t)0x0200)            /*!< CTS 标志 */

为了准确定位具体的外设,采用了外设基地址加上偏移量的方法。这种方式使得开发者能够方便地找到目标外设对应的寄存器地址。

接着,ST 公司使用结构体对每个外设的所有寄存器进行封装。以 USART 为例,其结构体定义如下:   

typedef struct
{__IO uint16_t SR;uint16_t  RESERVED0;__IO uint16_t DR;uint16_t  RESERVED1;__IO uint16_t BRR;uint16_t  RESERVED2;__IO uint16_t CR1;uint16_t  RESERVED3;__IO uint16_t CR2;uint16_t  RESERVED4;__IO uint16_t CR3;uint16_t  RESERVED5;__IO uint16_t GTPR;uint16_t  RESERVED6;
} USART_TypeDef;

其中,__IO 表示该成员是可读写的,RESERVED 代表保留位。通过这种结构体封装,开发者可以更便捷地访问和操作外设的各个寄存器。

此外,对于 USART 的工作模式,也进行了宏定义:

/** @defgroup USART_Mode * @{*/ #define USART_Mode_Rx                        ((uint16_t)0x0004)
#define USART_Mode_Tx                        ((uint16_t)0x0008)
#define IS_USART_MODE(MODE) ((((MODE) & (uint16_t)0xFFF3) == 0x00) && ((MODE) != (uint16_t)0x00))
/*** @}

同时,为了方便初始化 USART 外设,定义了一个初始化结构体:

typedef struct
{uint32_t USART_BaudRate;            /*!< 该成员用于配置 USART 通信波特率。波特率通过以下公式计算:- 整数部分 = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))- 小数部分 = ((整数部分 - ((u32) 整数部分)) * 16) + 0.5 */uint16_t USART_WordLength;          /*!< 指定一帧中传输或接收的数据位数。该参数可以是 @ref USART_Word_Length 中的一个值 */uint16_t USART_StopBits;            /*!< 指定传输的停止位数量。该参数可以是 @ref USART_Stop_Bits 中的一个值 */uint16_t USART_Parity;              /*!< 指定奇偶校验模式。该参数可以是 @ref USART_Parity 中的一个值@note 当启用奇偶校验时,计算得到的奇偶校验位会插入到传输数据的最高有效位(当字长设置为 9 位数据时为第 9 位;当字长设置为 8 位数据时为第 8 位)。 */uint16_t USART_Mode;                /*!< 指定接收或发送模式是启用还是禁用。该参数可以是 @ref USART_Mode 中的一个值 */uint16_t USART_HardwareFlowControl; /*!< 指定硬件流控制模式是启用还是禁用。该参数可以是 @ref USART_Hardware_Flow_Control 中的一个值 */
} USART_InitTypeDef;

最后,ST 公司将这些底层的寄存器操作进一步封装成一个又一个经典的库函数,形成了 HAL(硬件抽象层)库。这个库为开发者提供了一个简单、高效的开发接口,让开发者无需深入了解底层寄存器的操作细节,就能轻松实现各种外设的功能。可以说,HAL 库是无数开发者开启单片机开发之旅的起点,大大提高了开发效率和代码的可维护性。以串口外设为例,这种层层封装的方式使得开发者能够更加专注于应用程序的开发,而不必花费大量时间去研究底层硬件的复杂细节。

USART_Mode :  
#define USART_Mode_Rx                        ((uint16_t)0x0004)   为例子

2. USART_Mode_Rx 宏定义的意义

USART_Mode_Rx 这个宏定义代表着 USART 的接收模式。((uint16_t)0x0004) 实际上是一个掩码,它对应着 CR1 寄存器里特定的某一位或者某几位。当把这个掩码应用到 CR1 寄存器时,就可以开启或者关闭接收功能。

3. 具体的对应关系

一般而言,USART_Mode_Rx 对应的是 CR1 寄存器里的 RE(接收使能)位。假定 RE 位在 CR1 寄存器的第 2 位(从 0 开始计数),那么 0x0004 刚好是 0b0000 0000 0000 0100,这就意味着只有第 2 位被置为 1,其他位都是 0。


http://www.ppmy.cn/server/178712.html

相关文章

Python---数据分析(Pandas七:二维数组DataFrame中元素的索引与访问,其他常用方法)

一、DataFrame中元素的索引与访问 1.1、 使用列名访问 对于DataFrame来说&#xff0c;可以直接使用列名来访问某一列的数据&#xff0c;返回的是一个Series 对象。 import pandas as pddata {姓名: [小明, 小红, 小刚],年龄: [20, 18, 22],成绩: [85, 90, 88] }df pd.DataF…

XSS-Labs 靶场 Level 0-10关卡 WriteUp

今天&#xff0c;我花了大量时间完成了 XSS-Labs 的所有十关挑战。通过这些练习&#xff0c;我深入学习了如何发现和利用不同类型的跨站脚本&#xff08;XSS&#xff09;漏洞。以下是我每一关的具体操作和心得。 第一关&#xff1a;基础反射型 XSS 在第一关中&#xff0c;目标是…

DeepSeek引爆AIoT革命:华奥系科技领跑“万物智联”时代

2025年&#xff0c;全球AIoT&#xff08;人工智能物联网&#xff09;产业迎来关键拐点——中国AI大模型DeepSeek的横空出世&#xff0c;以“低成本、轻量化、边缘智能”三大特性&#xff0c;彻底打破传统物联网的算力与效率瓶颈。从智慧社区到智慧农业&#xff0c;从工业产线到…

【Go 】Go 语言中的 channel介绍

Go 语言中的 channel 是基于 CSP&#xff08;Communicating Sequential Processes&#xff09;思想实现的通信机制。其核心理念是&#xff1a; 通过通信共享内存&#xff0c;而不是通过共享内存通信 也就是说&#xff0c;应当让不同 goroutine 之间通过传递数据来共享状态&…

Golang开发棋牌游戏中的坑

开发棋牌游戏时&#xff0c;Go 语言的高效并发和简洁语法是非常适合的。然而&#xff0c;在实际开发中仍然会遇到一些常见的“坑”。以下是开发棋牌游戏时可能遇到的问题及其解决方案&#xff1a; 1. 并发与同步问题 问题描述 棋牌游戏通常需要处理大量并发连接和实时交互&…

第13章贪心算法

贪心算法 局部最优求得总体最优 适用于桌上有6张纸币&#xff0c;面额为100 100 50 50 50 10&#xff0c;问怎么能拿走3张纸币&#xff0c;总面额最大&#xff1f;—拿单位价值最高的 只关注局部最优----关注拿一张的最大值拆解-----拿三次最大的纸币 不适用于桌面三件物品&am…

c++高精度减法

我们在计算高精度减法的时候其实和高精度加法是有着异曲同工之处的&#xff0c;唯一需要改变的就是借位的逻辑还有数字大小的逻辑 下来我们来写一写高精度减法 刚开始呢我们还是和高精度加法一样高精度加法&#xff0c;我们需要先反转再计算 代码如下: int main() {string …

selenium(鼠标操作、页面操作、用例设计)

ActionChains类&#xff08;鼠标操作&#xff09; 常用于模拟鼠标的行为&#xff0c;比如单击、双击、拖拽等行为 click(on_elementNone) --- 鼠标单击 double_click(on_elementNone) --- 双击 context_click(on_elementNone) --- 右击…