学习所有用c语言定义stm32的语句

在 STM32 开发中,使用 C 语言定义外设寄存器是底层编程的基础。通常通过结构体将外设的多个寄存器组织在一起,再通过指针指向其基地址,从而实现对寄存器的读写操作。下面将穷举(详细列举)常见的定义方式,包括寄存器地址映射、结构体封装、位操作宏以及实际使用示例。


1. 定义外设基地址

每个外设都有一段连续的地址空间,首先需要定义其起始地址。

c 复制代码
// 外设总线基地址(以 STM32F1 系列为例)
#define PERIPH_BASE           ((uint32_t)0x40000000)   // 外设总线基地址
#define APB1PERIPH_BASE       PERIPH_BASE              // APB1 总线
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)  // APB2 总线
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)  // AHB 总线

// GPIO 外设基地址(位于 APB2 上)
#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
// ... 其他 GPIO

// USART 外设基地址(位于 APB1 上)
#define USART1_BASE           (APB2PERIPH_BASE + 0x3800)  // USART1 在 APB2
#define USART2_BASE           (APB1PERIPH_BASE + 0x4400)
#define USART3_BASE           (APB1PERIPH_BASE + 0x4800)

2. 定义寄存器结构体

将外设的所有寄存器按照数据手册中的偏移顺序定义为结构体成员。

c 复制代码
// GPIO 寄存器结构体
typedef struct {
    volatile uint32_t CRL;    // 端口配置低寄存器,偏移 0x00
    volatile uint32_t CRH;    // 端口配置高寄存器,偏移 0x04
    volatile uint32_t IDR;    // 输入数据寄存器,偏移 0x08
    volatile uint32_t ODR;    // 输出数据寄存器,偏移 0x0C
    volatile uint32_t BSRR;   // 位设置/清除寄存器,偏移 0x10
    volatile uint32_t BRR;    // 位清除寄存器,偏移 0x14
    volatile uint32_t LCKR;   // 配置锁定寄存器,偏移 0x18
} GPIO_TypeDef;

// USART 寄存器结构体
typedef struct {
    volatile uint32_t SR;     // 状态寄存器,偏移 0x00
    volatile uint32_t DR;     // 数据寄存器,偏移 0x04
    volatile uint32_t BRR;    // 波特率寄存器,偏移 0x08
    volatile uint32_t CR1;    // 控制寄存器1,偏移 0x0C
    volatile uint32_t CR2;    // 控制寄存器2,偏移 0x10
    volatile uint32_t CR3;    // 控制寄存器3,偏移 0x14
    volatile uint32_t GTPR;   // 保护时间和预分频寄存器,偏移 0x18
} USART_TypeDef;

3. 将基地址转换为结构体指针

通过强制类型转换,使指针指向外设的基地址,之后就可以通过指针访问寄存器。

c 复制代码
#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define USART1              ((USART_TypeDef *) USART1_BASE)
#define USART2              ((USART_TypeDef *) USART2_BASE)

4. 定义位操作宏

为了方便设置/清除寄存器的某一位,常定义位掩码或位号。

c 复制代码
// GPIO 引脚位定义
#define GPIO_Pin_0          ((uint16_t)0x0001)
#define GPIO_Pin_1          ((uint16_t)0x0002)
// ...
#define GPIO_Pin_15         ((uint16_t)0x8000)

// 寄存器位定义(例如 USART SR 寄存器)
#define USART_SR_TXE        ((uint16_t)0x0080)  // 发送数据寄存器空
#define USART_SR_TC         ((uint16_t)0x0040)  // 发送完成
#define USART_SR_RXNE       ((uint16_t)0x0020)  // 读数据寄存器非空

5. 定义操作寄存器的宏或函数

常用操作如设置位、清除位、读取位等,可以用宏封装。

c 复制代码
// 设置寄存器的某些位
#define SET_BIT(REG, BIT)     ((REG) |= (BIT))

// 清除寄存器的某些位
#define CLEAR_BIT(REG, BIT)   ((REG) &= ~(BIT))

// 读取寄存器的某些位
#define READ_BIT(REG, BIT)    ((REG) & (BIT))

// 写入寄存器的值
#define WRITE_REG(REG, VAL)   ((REG) = (VAL))

// 读取寄存器的值
#define READ_REG(REG)         ((REG))

6. 实际使用示例

6.1 GPIO 初始化与操作

c 复制代码
// 使能 GPIOA 时钟(假设 RCC 寄存器已定义)
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

// 配置 PA0 为推挽输出,最大速度 50MHz
GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0);  // 先清除原有配置
GPIOA->CRL |= GPIO_CRL_MODE0_1 | GPIO_CRL_MODE0_0; // 设置模式为 50MHz 输出
// CNF 保持 00 表示通用推挽输出

// 设置 PA0 输出高电平
GPIOA->BSRR = GPIO_Pin_0;

// 设置 PA0 输出低电平
GPIOA->BRR = GPIO_Pin_0;

// 读取 PA1 输入电平
uint8_t pin_state = (GPIOA->IDR & GPIO_Pin_1) ? 1 : 0;

6.2 USART 发送数据

c 复制代码
// 使能 USART1 时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;

// 配置波特率、数据位等(略)

// 发送一个字节
void USART_SendChar(USART_TypeDef* USARTx, uint8_t ch) {
    // 等待发送数据寄存器空
    while (!(USARTx->SR & USART_SR_TXE));
    USARTx->DR = ch;
}

// 使用
USART_SendChar(USART1, 'A');

7. 更高级的定义方式

7.1 使用位域结构体(不推荐,因为位域的顺序和端序可能因编译器而异,但有时用于精确控制)

c 复制代码
typedef struct {
    volatile uint32_t MODER  : 2;  // 模式
    volatile uint32_t OTYPER : 1;  // 输出类型
    volatile uint32_t OSPEEDR: 2;  // 输出速度
    volatile uint32_t PUPD   : 2;  // 上拉/下拉
    volatile uint32_t IDR    : 1;  // 输入数据
    volatile uint32_t ODR    : 1;  // 输出数据
    // ... 其他位
} GPIO_Pin_TypeDef;

但这种方式不常用,因为 STM32 标准库和 HAL 库都采用宏和结构体方式。

7.2 定义中断向量表

c 复制代码
typedef void (*pFunction)(void);  // 函数指针类型

typedef struct {
    uint32_t     stack_top;       // 栈顶地址
    pFunction    Reset_Handler;   // 复位处理函数
    pFunction    NMI_Handler;     // NMI 处理函数
    pFunction    HardFault_Handler; // 硬错误处理函数
    // ... 其他异常和中断向量
} VectorTable;

// 在链接脚本中分配向量表到起始地址
extern VectorTable __Vectors;

8. 常见的 C 语言语句在 STM32 编程中的应用

除了上述定义外,实际编程中会大量使用以下 C 语言语句:

  • 变量定义uint32_t timeout;
  • 循环while(!(USART1->SR & USART_SR_RXNE));
  • 条件判断if(GPIOA->IDR & GPIO_Pin_0) { ... }
  • 函数定义void delay(volatile uint32_t count) { while(count--); }
  • 宏定义#define LED_ON() GPIOA->BSRR = GPIO_Pin_5
  • 指针操作uint32_t *p = (uint32_t *)0x20000000;
  • 结构体访问GPIOA->CRL = 0x44444444;
  • 位运算TIM2->CR1 |= TIM_CR1_CEN;

总结

在 STM32 的 C 语言编程中,定义外设的核心步骤是:

  1. #define 定义外设的基地址。
  2. typedef struct 定义外设的寄存器映射。
  3. #define 将基地址转换为结构体指针,得到外设访问句柄。
  4. #define 定义各寄存器中的位掩码或功能常量。
  5. 使用上述定义进行寄存器读写和位操作。

这些定义通常放在芯片型号对应的头文件中(如 stm32f10x.h),开发者直接包含即可使用。通过这种方式,C 语言能够简洁且高效地操作 STM32 的硬件资源。

相关推荐
知识分享小能手37 分钟前
R语言入门学习教程,从入门到精通,R语言数据计算与分组统计(9)
开发语言·学习·r语言
lizhihai_991 小时前
股市学习心得—半导体12种核心材料
大数据·人工智能·学习
sakiko_1 小时前
UIKit学习笔记3-布局、滚动视图、隐藏或显示视图
前端·笔记·学习·objective-c·swift·uikit
天诚智能门锁1 小时前
天诚公租房管控平台CAT.1人脸猫眼智能锁助力青神县公租房管理
人工智能·嵌入式硬件·物联网·智能家居·智能硬件
嵌入式-老费2 小时前
瑞芯微soc的学习和应用(题外话之esp32开发)
学习
辰同学ovo3 小时前
从全局登录状态管理学习 Redux
前端·javascript·学习·react.js
ting94520003 小时前
告别无效学习:Scholé 如何用 AI 重构职场学习,让学习直接嵌入工作流
人工智能·学习·重构
xian_wwq3 小时前
【学习笔记】Harness到底是什么
笔记·学习·ai·harness
wuxinyan1233 小时前
大模型学习之路004:RAG 零基础入门教程(第一篇):基础理论与文档处理流水线
人工智能·学习·rag
冯诺依曼的锦鲤3 小时前
从零实现高并发内存池:TCMalloc 核心架构拆解
c++·学习·算法·架构