STM32开发——中断、定时器
·
STM32开发——中断、定时器
一、中断系统
1. 中断控制器对比
NVIC(Nested Vectored Interrupt Controller)
-
适用:Cortex-M内核(M0、M3、M4、M7等)
-
特点:只给Cortex-M单核使用
-
中断数量:Cortex-M3内核最多支持256个中断
-
优先级宽度:固定为4bit(0-15),数值越小优先级越高
GIC(Generic Interrupt Controller)
-
适用:Cortex-A内核(A7、A9、A53等多核处理器)
-
特点:多核使用,管理范围更大
-
优先级宽度:可以自己配置,数值越小优先级越高
-
复杂度:比NVIC更复杂
2. 中断优先级分类
| 优先级类型 | 说明 |
|---|---|
| 抢占优先级(主优先级) | 决定是否可以打断正在执行的中断,数值越小优先级越高 |
| 响应优先级(子优先级) | 当抢占优先级相同时,决定谁先执行 |
| 自然优先级 | 中断向量表中的硬件优先级(数值越小越高) |
3. 优先级规则
text
1. 抢占优先级高的可以打断抢占优先级低的中断 2. 抢占优先级相同,响应优先级高的先执行 3. 抢占优先级和响应优先级都相同,自然优先级高的先执行 4. 所有优先级都是数值越小,优先级越高
二、按键中断配置(CubeMX + Keil)
1. 硬件连接
-
按键K1:PA0引脚
-
按键K2:PC13引脚
2. CubeMX配置步骤
步骤1:GPIO配置外部中断
-
在Pinout视图找到PA0和PC13
-
配置为GPIO_EXTI0和GPIO_EXTI13(外部中断模式)
-
触发方式选择:Rising上升沿触发
步骤2:NVIC配置
-
进入NVIC设置页面
-
找到对应的EXTI线中断(EXTI line0 interrupt、EXTI line13 interrupt)
-
勾选Enabled(使能中断)
-
设置抢占优先级(如P=7)
-
设置响应优先级(如需要)
步骤3:GPIO输出配置
-
配置LED引脚(如PC2、PC3)为输出模式
-
配置蜂鸣器引脚(如PC1)为输出模式
3. Keil程序实现
中断回调函数
/* 外部中断回调函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
UNUSED(GPIO_Pin); // 防止未使用参数警告
/* 根据触发的中断引脚执行不同操作 */
switch(GPIO_Pin)
{
case GPIO_PIN_0: // K1按键按下(PA0)
/* 执行K1对应操作:翻转LED1 */
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
break;
case GPIO_PIN_13: // K2按键按下(PC13)
/* 执行K2对应操作:翻转蜂鸣器 */
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_1);
break;
default:
break;
}
}
主函数
int main(void)
{
/* 初始化HAL库 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 初始化所有已配置的外设 */
MX_GPIO_Init();
/* 无限循环 */
while(1)
{
/* 主循环中可以执行其他任务 */
/* 按键响应已经在中断中处理,无需轮询 */
}
}
三、定时器(TIM)
1. STM32F103定时器资源
STM32F103系列共有8个定时器:TIM1 - TIM8
2. 定时器分类
| 类型 | 定时器 | 位数 | 特点 | 用途 |
|---|---|---|---|---|
| 高级定时器 | TIM1、TIM8 | 16位 | 向上/向下计数,输入捕获,输出比较 | 电机控制、PWM、死区控制 |
| 通用定时器 | TIM2-TIM5 | 16位 | 向上/向下计数,输入捕获,输出比较 | 通用定时、PWM、输入捕获 |
| 基本定时器 | TIM6、TIM7 | 16位 | 向上计数 | 基础定时功能 |
3. CubeMX配置定时器
-
在Pinout中找到TIM3
-
选择时钟源为Internal Clock(内部时钟)
-
配置参数:
-
Prescaler(预分频器):设置分频系数
-
Counter Period(自动重载值):设置计数周期
-
auto-reload preload(自动重载预装载):使能
-
4. Keil程序实现
定时器启动
/* 开启定时器3 */ HAL_TIM_Base_Start(&htim3);
微秒级延时函数
/**
* @brief 微秒级延时函数(使用TIM3)
* @param us 需要延时的微秒数
*/
void HAL_Delayus(uint16_t us)
{
uint16_t tick = us - 1; // 需要计数的时钟周期
uint16_t tcnt = 0; // 已经计数的时钟周期
uint16_t told = 0; // 上一次读取到定时器的计数值
uint16_t tnow = 0; // 当前读取到定时器的计数值
/* 获取定时器3当前计数值 */
told = __HAL_TIM_GET_COUNTER(&htim3);
while(1)
{
/* 循环获取定时器3当前计数值 */
tnow = __HAL_TIM_GET_COUNTER(&htim3);
/* 当前定时器计数值发生了变化 */
if(tnow != told)
{
if(tnow > told)
{
/* tnow>told:定时器没有溢出 */
/* tnow-told获得已经计数的时钟周期 */
tcnt += tnow - told;
}
else
{
/* tnow<=told:定时器溢出 */
/* 计算从told到0xFFFF经历的时钟周期 + 从0到tnow的时钟周期 */
tcnt += 0xFFFF - told + tnow;
}
told = tnow; // 更新上一次计数值
/* tcnt>=tick:累加时间已经到达目标延时时间 */
if(tcnt >= tick)
{
break;
}
}
}
}
5. 延时函数原理分析
定时器时钟频率(如72MHz时,每计数1次约13.9ns)
↓
需要延时us微秒 → 计算出需要计数的次数 tick
↓
tick = us × (定时器时钟频率 / 1000000) - 1
↓
不断读取定时器当前计数值
↓
计算两次读取之间的差值
↓
处理定时器溢出情况
↓
累加已计数的周期
↓
达到tick后退出循环,完成延时
6. 定时器溢出处理
正常情况:tnow > told → 已计数周期 = tnow - told
溢出情况:tnow < told → 已计数周期 = (0xFFFF - told) + tnow
(从told到0xFFFF的周期) + (从0到tnow的周期)
更多推荐



所有评论(0)