STM32初学---RCC / NVIC / EXTI

一、RCC_CFGR 寄存器(Reset and Clock Control - Clock Configuration Register)

1、概念

  • RCC (Reset and Clock Control):这是一个专门负责控制 复位时钟 的外设模块。
  • CFGR (Configuration Register): 这是一个配置寄存器

作用解释:RCC_CFGRRCC\_CFGRRCC_CFGR 寄存器就是用来配置和选择 STM32 中各个总线和外设的时钟源和分频系数的。它是时钟系统配置中最重要的寄存器之一。

  • 时钟源选择: 决定了系统时钟(SYSCLK)从哪里来(例如,是内部高速时钟 HSI、外部高速时钟 HSE 还是 PLL 锁相环)。
  • 分频设置: 决定了系统时钟如何分配给不同的总线(AHB、APB1、APB2)和内核(Cortex System Timer 等),以控制它们运行的速度。

2、 RCC_CFGRRCC\_CFGRRCC_CFGR 寄存器中的两个相关字段:

  • SWSWSW(System Clock Switch): 这是一个控制位,您写入这个字段来选择您希望使用哪个时钟源作为 SYSCLKSYSCLKSYSCLK。
  • SWSSWSSWS(System Clock Switch Status): 这是一个状态位,您读取这个字段来确认系统是否已经成功切换到 SWSWSW 所选择的时钟源。

SWSSWSSWS 用于指示系统时钟 (SYSCLKSYSCLKSYSCLK) 当前使用的是哪个时钟源。

SWS 值 含义(当前系统时钟源)
00 HSI (High-speed Internal Clock) - 内部高速时钟
01 HSE (High-speed External Clock) - 外部高速时钟
10 PLL (Phase-Locked Loop) - 锁相环输出时钟
11 预留/其他(依具体型号而定)

简单来说:

  • SWSWSW →\rightarrow→ 你告诉 MCU 用哪个时钟 (Write);
  • SWSSWSSWS →\rightarrow→ MCU 告诉你它正在用哪个时钟 (Read)。

在代码中进行时钟初始化时,通常会配置 SWSWSW 字段 ,然后等待(或检查) SWSSWSSWS 字段,直到它变为期望的值,这代表时钟切换成功。

3、RCC_ClocksTypeDef结构体

RCC_ClocksTypeDef结构体中定义:SYSCLK_Frequency、HCLK_Frequency、PCLK1_Frequency、PCLK2_Frequency。这四个成员变量,是 STM32 时钟树(Clock Tree)和速度分配的关键。它们定义了芯片内部几个最重要的时钟频率。

变量名 对应时钟 全称 作用/含义
SYSCLK_Frequency SYSCLK (System Clock) 系统时钟 MCU 的主时钟。 它驱动 CPU 内核(Cortex-M)和 AHB 总线。它的频率决定了 MCU 的基本运行速度。SYSCLK 的来源可以是 HSI、HSE 或 PLLCLK。
HCLK_Frequency HCLK (AHB Clock) AHB 总线时钟 高速外设和存储器的时钟。 它驱动 AHB (Advanced High-performance Bus) 总线、SRAM 存储器、Flash 存储器和所有连接到 AHB 总线上的高速外设(例如 DMA、GPIO 等)。HCLK 是由 SYSCLK 经过 AHB 预分频器 (AHB Prescaler) 分频后得到的。
PCLK1_Frequency PCLK1 (APB1 Clock) APB1 低速外设时钟 低速外设的时钟。 它驱动连接到 APB1 (Advanced Peripheral Bus 1) 总线上的外设。通常用于低速且对频率要求不高的外设(例如 Timer 2/3/4/5/6/7,USART 2/3/4/5,I2C 1/2/3 等)。PCLK1 是由 HCLK 经过 APB1 预分频器 分频后得到的。
PCLK2_Frequency PCLK2 (APB2 Clock) APB2 高速外设时钟 高速外设的时钟。 它驱动连接到 APB2 (Advanced Peripheral Bus 2) 总线上的外设。通常用于高速或对时序有严格要求的外设(例如 Timer 1/8,USART 1/6,SPI 1/4,ADC 等)。PCLK2 是由 HCLK 经过 APB2 预分频器 分频后得到的。

二、NvicConfig:外设中断配置函数

主要包含4类操作:

① 设置中断优先级分组(Priority Grouping)

说明:

决定抢占/子优先级结构:
先占优先级(preemption priority)
响应 / 子优先级(sub priority)

例如(STM32):

javascript 复制代码
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

分组决定优先级的划分方式。

NVIC_PriorityGroup决定 优先级字段里多少位用于抢占优先级,多少位用于子优先级。

在 Cortex-M 内核中,NVIC 的中断优先级一般是 4 位(0~15)。

这些 4 位会被拆分成两部分:

Priority Group 抢占位数 子优先级位数
Group 0 0 位 4 位
Group 1 1 位 3 位
Group 2 2 位 2 位
Group 3 3 位 1 位
Group 4 4 位 0 位

📌 Group 数字越大 → 抢占优先级越多 → 子优先级越少

为什么很多工程默认用 NVIC_PriorityGroup_4?

因为简单!只需要关心一个优先级(抢占优先级),中断只按"谁优先级更高"来打断,没有子优先级这种细节,适合大部分 STM32 项目。

javascript 复制代码
NVIC_PriorityGroup_4
表示:
抢占优先级占 4 位
子优先级占 0 位(没有子优先级概念)
所以:
✔ 你可以设置抢占优先级 0 ~ 15
✔ 所有中断没有平级排序,全靠抢占优先级决定先后

深入理解:

假设优先级分组是 NVIC_PriorityGroup_2(2 位抢占,2 位子优先级):

中断 抢占优先级 子优先级
EXTI0 1 0
USART1 2 0
TIM3 1 1

结论:

javascript 复制代码
EXTI0(1,0) 和 TIM3(1,1)
→ 抢占一样
→ 子优先级 0 < 1
→ EXTI0 先执行

USART1(2,x) 抢占级低
→ 不能打断 EXTI0 或 TIM3
→ 永远排最后

② 为某个外设选择中断优先级

典型步骤:

javascript 复制代码
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

设置内容:

中断线号(IRQ)

抢占优先级

响应/子优先级

打开 NVIC 中断通道

③ 使能(Enable)NVIC 中断通道

例如:

javascript 复制代码
NVIC_EnableIRQ(USART1_IRQn);

或者在初始化结构里 Cmd = ENABLE。

④ 可选:清除挂起中断(Pending Flag)

说明:防止进入旧中断

确保启动前中断状态是干净的:

javascript 复制代码
NVIC_ClearPendingIRQ(EXTI0_IRQn);

三、EXTI 和 NVIC

1、概念

EXTI 负责"外部事件/引脚 → 产生中断请求"

NVIC 负责"多个中断请求 → 排队、抢占、执行"

2、NVIC(Nested Vectored Interrupt Controller)

NVIC 是 Cortex-M 内核的一部分,作用是:管理所有中断(外设 / 内部 / 外部)

决定:

  • 谁先执行
  • 谁能打断谁
  • 谁在等

3、EXTI(External Interrupt / Event Controller)

EXTI 是 芯片外设模块,负责:把 GPIO 引脚或内部事件,转换成中断请求

常见用途:

  • 按键中断
  • 外部传感器触发
  • 外部同步信号

EXTI 本身不直接连 GPIO,

中间有个 EXTI Line(中断线):

javascript 复制代码
GPIO 引脚
   ↓
EXTI Line (0~15)
   ↓
NVIC (EXTI0_IRQn, EXTI9_5_IRQn...)

3.1 EXTI 配置代码

① GPIO 配置为输入
javascript 复制代码
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
② GPIO → EXTI Line 映射
javascript 复制代码
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
③ EXTI 配置
javascript 复制代码
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
④ NVIC 配置
javascript 复制代码
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
⑤ 中断服务函数(ISR)

void EXTI0_IRQHandler(void)

{

if (EXTI_GetITStatus(EXTI_Line0) != RESET)

{

// 用户处理代码

复制代码
    EXTI_ClearITPendingBit(EXTI_Line0);
}

}

⚠️ 必须清 pending 位,否则会一直进中断

3.2 NVIC 和 EXTI 的关系(重点)

模块 作用
GPIO 物理引脚
EXTI 把引脚变化变成中断请求
NVIC 决定是否执行、何时执行
ISR 真正执行代码的地方
相关推荐
chao18984411 小时前
STM32 HAL库驱动AT24C02 EEPROM例程
stm32·单片机·嵌入式硬件
猫猫的小茶馆14 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32
feifeigo12314 小时前
STM32矩阵键盘驱动(库函数版)实现
stm32·矩阵·计算机外设
嵌入式小站15 小时前
STM32 零基础可移植教程 05:按键消抖,为什么按一次会触发好几次
chrome·stm32·嵌入式硬件
拾知_H16 小时前
STM32/Delay延时函数编程思路
stm32·单片机·时钟·延时
2zcode17 小时前
基于STM32的智能扫地机器人设计与实现
stm32·嵌入式硬件·机器人
jllllyuz17 小时前
单相并网逆变器控制代码实现(STM32版)
stm32·单片机·嵌入式硬件
追兮兮20 小时前
告别手动搭建Keil工程!MCUQuickStart一键生成STM32/GD32工程模板,打开即编译
stm32·单片机·嵌入式硬件·gd32·keil5
山木嵌入式21 小时前
STM32串口不定长数据接收:超时解析法+DMA+空闲中断法(附完整代码)
stm32·单片机·串口·uart
深圳市晨芯阳科技有限公司1 天前
HC7253晨芯阳高端电流检测降压LED恒流驱动器
stm32·单片机·嵌入式硬件·驱动ic·深圳市晨芯阳科技有限公司