一、为什么 STM32 外设都需要初始化?
STM32 内部不仅有 CPU,还集成了大量外设:
GPIO、USART、ADC、CAN、DMA、RTC、定时器、FSMC......
这些外设复位后通常不会自动按照我们的需求工作。程序必须告诉芯片:
我要使用哪个外设
外设连接到哪些引脚
采用什么工作模式
运行速度是多少
是否使用中断或DMA
什么时候正式启动
因此,所谓"初始化",本质上就是:
将外设从复位后的默认状态,配置成当前项目需要的工作状态。
大多数 STM32 外设初始化,都可以概括为以下流程:
确认硬件连接
↓
开启相关时钟
↓
配置GPIO及复用功能
↓
复位外设并清除旧状态
↓
配置外设核心参数
↓
配置DMA、过滤器或中断
↓
清除状态标志
↓
使能外设
↓
进行最小功能测试
二、第一步永远是看硬件连接
写初始化代码前,不能只看函数名,首先要看原理图和芯片手册。
例如 USART3 使用:
PB10 → USART3_TX
PB11 → USART3_RX
ADC 电位器使用:
PB1 → ADC1_IN9
CAN2 使用:
PB13 → CAN2_TX
PB12 → CAN2_RX
液晶屏可能使用:
D0~D15
CS、WR、RD、DC
RST、BK
三、为什么所有外设都要先开时钟?
STM32 为了降低功耗,复位后通常关闭大部分外设时钟。
例如使用 GPIOB 前要执行:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
使用 USART3 前要执行:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
使用 ADC1 前要执行:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC 全称:
Reset and Clock Control
复位与时钟控制
它相当于 STM32 内部的"时钟和复位管理中心"。
可以把开启时钟理解成:
RCC开时钟:给外设接通电源
初始化寄存器:设置外设工作方式
外设使能:按下启动按钮
四、如何判断使用 AHB1、APB1 还是 APB2?
不同外设挂在不同总线上。
常见关系如下:
| 总线 | 常见外设 |
|---|---|
| AHB1 | GPIO、DMA、CRC |
| APB1 | USART2/3、UART4/5、CAN、PWR、I²C |
| APB2 | USART1/6、ADC、SPI1、部分高级定时器 |
| AHB3 | FSMC |
所以:
GPIOB → RCC_AHB1PeriphClockCmd()
USART3 → RCC_APB1PeriphClockCmd()
ADC1 → RCC_APB2PeriphClockCmd()
FSMC → RCC_AHB3PeriphClockCmd()
初始化时函数用错总线,外设通常不会工作。
五、如何判断使用 AHB1、APB1 还是 APB2?
不同外设挂在不同总线上。
常见关系如下:
| 总线 | 常见外设 |
|---|---|
| AHB1 | GPIO、DMA、CRC |
| APB1 | USART2/3、UART4/5、CAN、PWR、I²C |
| APB2 | USART1/6、ADC、SPI1、部分高级定时器 |
| AHB3 | FSMC |
所以:
GPIOB → RCC_AHB1PeriphClockCmd()
USART3 → RCC_APB1PeriphClockCmd()
ADC1 → RCC_APB2PeriphClockCmd()
FSMC → RCC_AHB3PeriphClockCmd()
初始化时函数用错总线,外设通常不会工作。
六、初始化函数的通用逻辑
int Peripheral_Init(void)
{
/* 1. 定义并初始化配置结构体 */
GPIO_InitTypeDef GPIO_InitStructure;
Peripheral_InitTypeDef InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
Peripheral_StructInit(&InitStructure);
/* 2. 开启GPIO和外设时钟 */
RCC_xxxPeriphClockCmd(GPIO_CLK, ENABLE);
RCC_xxxPeriphClockCmd(PERIPHERAL_CLK, ENABLE);
/* 3. 配置GPIO */
GPIO_InitStructure.GPIO_Pin = PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(PORT, &GPIO_InitStructure);
/* 4. 配置AF映射 */
GPIO_PinAFConfig(PORT, PIN_SOURCE, GPIO_AF_xxx);
/* 5. 复位外设 */
RCC_xxxPeriphResetCmd(PERIPHERAL_CLK, ENABLE);
RCC_xxxPeriphResetCmd(PERIPHERAL_CLK, DISABLE);
/* 6. 配置外设核心参数 */
InitStructure.xxx = xxx;
Peripheral_Init(PERIPHERAL, &InitStructure);
/* 7. 清除状态标志 */
Peripheral_ClearFlag(PERIPHERAL, FLAGS);
/* 8. 配置中断或DMA */
/* ... */
/* 9. 使能外设 */
Peripheral_Cmd(PERIPHERAL, ENABLE);
/* 10. 检查是否启动成功 */
return 0;
}