news 2026/4/25 11:25:20

volatile在stm32下freertos里面的使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
volatile在stm32下freertos里面的使用
#ifndef_EXTI_H#define_EXTI_H#include"../Driver/SYSTEM/sys/sys.h"/******************************************************************************************//* 引脚 和 中断编号 & 中断服务函数 定义 */#defineALARM_INT_GPIO_PORTGPIOB#defineALARM_INT_GPIO_PINGPIO_PIN_7#defineALARM_INT_IRQnEXTI9_5_IRQn#defineALARM_INT_IRQHandlerEXTI9_5_IRQHandler/******************************************************************************************//******************************************************************************************//* 引脚 定义 */#defineALARM_GPIO_PORTGPIOB#defineALARM_GPIO_PINGPIO_PIN_7/******************************************************************************************/#defineALARMHAL_GPIO_ReadPin(ALARM_GPIO_PORT,ALARM_GPIO_PIN)/* 读取ALARM引脚 */#defineALARM_PRES7/* ALARM相关的SQW拉低引脚电平 */voidextix_init(void);/* 外部中断初始化 */#endif#include"../Driver/BSP/exti/exti.h"#include"../Driver/SYSTEM/sys/sys.h"#include"../Driver/SYSTEM/delay/delay.h"#include"../Driver/BSP/LED/led.h"#include"../Driver/BSP/beep/beep.h"#include"../Driver/BSP/key/key.h"/** * @brief ALARM 外部中断服务程序 * @param 无 * @retval 无 */voidALARM_INT_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(ALARM_INT_GPIO_PIN);/* 调用中断处理公用函数 清除ALARM所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */__HAL_GPIO_EXTI_CLEAR_IT(ALARM_INT_GPIO_PIN);/* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */}/** * @brief 中断服务程序中需要做的事情 在HAL库中所有的外部中断服务函数都会调用此函数 * @param GPIO_Pin:中断引脚号 * @retval 无 */voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){delay_ms(10);/* 消抖 */switch(GPIO_Pin){caseALARM_INT_GPIO_PIN:if(ALARM==0){BEEP_TOGGLE();/* 蜂鸣器状态取反 */LED1_TOGGLE();/* LED1状态取反 */}break;default:break;}}/** * @brief 外部中断初始化程序 * @param 无 * @retval 无 */voidextix_init(void){GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_GPIOB_CLK_ENABLE();// 使能GPIOB时钟__HAL_RCC_AFIO_CLK_ENABLE();// 关键:关闭 JTAG,只保留 SWD,释放 PB3/PB4/PB7__HAL_AFIO_REMAP_SWJ_NOJTAG();gpio_init_struct.Pin=ALARM_GPIO_PIN;gpio_init_struct.Mode=GPIO_MODE_IT_FALLING;/* 下升沿触发 */gpio_init_struct.Pull=GPIO_PULLUP;//上拉HAL_GPIO_Init(ALARM_GPIO_PORT,&gpio_init_struct);/* ALARM配置为下降沿触发中断 */HAL_NVIC_SetPriority(ALARM_INT_IRQn,10,0);/* 抢占10,子优先级0 */HAL_NVIC_EnableIRQ(ALARM_INT_IRQn);/* 使能中断线5_9 */}这个EXTI移植到freertos里面,你看该怎么改?

移到 FreeRTOS 关键修改:中断里绝对不能用 delay_ms(),必须用全局标志位 + 任务处理

我直接给你改好、能直接用、FreeRTOS 安全版代码,只改关键地方,不破坏你原有逻辑。


一、核心问题(必须改)

你原来代码有 2 个FreeRTOS 致命错误

  1. 中断服务函数里用了 delay_ms(10)→ 会卡死系统
  2. 中断里直接操作蜂鸣器、LED→ 虽然能跑,但不规范、不安全

FreeRTOS 正确做法:

中断只做一件事:置位一个全局 uint8_t 标志位
真正的消抖、蜂鸣器、LED 全部放到FreeRTOS 任务里执行!


二、我帮你改好的代码(直接替换)

1. exti.h 新增全局标志位(你要的 uint8 原子变量)

#ifndef_EXTI_H#define_EXTI_H#include"../Driver/SYSTEM/sys/sys.h"/******************************************************************************************//* 引脚 和 中断编号 & 中断服务函数 定义 */#defineALARM_INT_GPIO_PORTGPIOB#defineALARM_INT_GPIO_PINGPIO_PIN_7#defineALARM_INT_IRQnEXTI9_5_IRQn#defineALARM_INT_IRQHandlerEXTI9_5_IRQHandler/******************************************************************************************//******************************************************************************************//* 引脚 定义 */#defineALARM_GPIO_PORTGPIOB#defineALARM_GPIO_PINGPIO_PIN_7/******************************************************************************************/#defineALARMHAL_GPIO_ReadPin(ALARM_GPIO_PORT,ALARM_GPIO_PIN)/* 读取ALARM引脚 *//* 新增:FreeRTOS 中断安全 全局标志位(uint8_t 单写单读,原子安全) */externuint8_tg_alarm_trigger;voidextix_init(void);/* 外部中断初始化 */#endif

2. exti.c 改成 FreeRTOS 安全版

#include"../Driver/BSP/exti/exti.h"#include"../Driver/SYSTEM/sys/sys.h"#include"../Driver/BSP/LED/led.h"#include"../Driver/BSP/beep/beep.h"/* 定义全局标志位(中断里写,任务里读,单字节原子安全) */uint8_tg_alarm_trigger=0;/** * @brief ALARM 外部中断服务程序 * @retval 无 */voidALARM_INT_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(ALARM_INT_GPIO_PIN);__HAL_GPIO_EXTI_CLEAR_IT(ALARM_INT_GPIO_PIN);}/** * @brief 中断回调(只置标志位,不做任何耗时操作) * @param GPIO_Pin * @retval 无 */voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){if(GPIO_Pin==ALARM_INT_GPIO_PIN){if(ALARM==0){/* 只置位标志位!!! */g_alarm_trigger=1;}}}/** * @brief 外部中断初始化程序 * @retval 无 */voidextix_init(void){GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_AFIO_CLK_ENABLE();__HAL_AFIO_REMAP_SWJ_NOJTAG();gpio_init_struct.Pin=ALARM_GPIO_PIN;gpio_init_struct.Mode=GPIO_MODE_IT_FALLING;gpio_init_struct.Pull=GPIO_PULLUP;HAL_GPIO_Init(ALARM_GPIO_PORT,&gpio_init_struct);/* FreeRTOS 中断优先级必须 ≥ 配置的最低优先级(如 10 11 12 13 14 15) */HAL_NVIC_SetPriority(ALARM_INT_IRQn,10,0);HAL_NVIC_EnableIRQ(ALARM_INT_IRQn);}

三、你必须在 FreeRTOS 里建一个处理任务(关键)

任务代码(随便放 main 或 task.c 里)

/* 外部引用标志位 */externuint8_tg_alarm_trigger;/* ALARM 处理任务 */voidalarm_task(void*pvParameters){while(1){/* 检测标志位 */if(g_alarm_trigger==1){/* 任务里可以安全用 消抖、操作硬件 */delay_ms(10);if(ALARM==0)// 再确认一次{BEEP_TOGGLE();LED1_TOGGLE();}g_alarm_trigger=0;// 清除标志}/* 任务延时,让出CPU */vTaskDelay(20);}}

四、为什么这样改?(对应你刚才问的知识点)

  1. g_alarm_trigger = 1
    单字节、全局、单次写 →原子安全,不用信号量

  2. if(g_alarm_trigger == 1)
    单字节、单次读 →原子安全,不用信号量

  3. 中断只做“标记”,任务做“处理”
    这是FreeRTOS 最标准、最安全的中断处理模型。

  4. delay_ms 只能放任务里,不能放中断里
    中断里不能阻塞,否则系统直接崩溃。


五、最终总结(你可以直接记住)

移到 FreeRTOS 只改 3 点:

  1. 中断里删掉 delay_ms()、BEEP、LED
  2. 中断里只给 uint8_t 全局标志位置 1
  3. 建一个 FreeRTOS 任务,读标志位 → 消抖 → 控制外设

安全规则:

  • 中断:只写标志位(uint8_t 原子)
  • 任务:只读标志位 + 处理业务
  • 完全不需要信号量

必须加!一定要加 volatile!

我直接给你讲清楚为什么、加在哪、怎么加,这是嵌入式/FreeRTOS 最容易踩的巨坑。

1. 最终正确写法(直接复制)

/* 正确:必须加 volatile */externvolatileuint8_tg_alarm_trigger;
/* 定义处也必须加 volatile */volatileuint8_tg_alarm_trigger=0;

2. 为什么 uint8_t 原子了,还需要 volatile?

你之前理解的uint8_t 全局变量 = 原子操作,只对了一半:

  • 原子性:保证读写不会被切成两半
  • volatile禁止编译器优化,保证每次都去内存读最新值

这是两个完全不同的东西!


编译器会自作聪明干坏事(不加 volatile 必出 bug)

编译器看到你的任务代码:

while(1){if(g_alarm_trigger==1)// 不加 volatile{...}}

编译器会优化成

寄存器=g_alarm_trigger;// 只读一次while(1){if(寄存器==1){...}}

结果:中断把 g_alarm_trigger 改成 1 了,任务永远不知道!
因为它只看寄存器,不看内存里的最新值


3. volatile 的作用(一句话)

告诉编译器:这个变量会在中断/别的任务突然改变,不许优化,每次都必须去内存读最新值!


4. 总结 3 条铁律(嵌入式中断 + FreeRTOS 必背)

  1. 中断与任务共享的全局变量 = 必须加 volatile
  2. 单字节 uint8_t + volatile = 读写原子,不需要信号量
  3. 多字节/多步骤操作 = 必须信号量/临界区

你这个场景完美符合第 2 条:

volatileuint8_tg_alarm_trigger;

✅ 安全
✅ 原子
✅ 不卡死
✅ 编译器不瞎优化


5. 最终修改版(直接用)

exti.h

/* 中断安全共享标志位:volatile + uint8_t 黄金组合 */externvolatileuint8_tg_alarm_trigger;

exti.c

volatileuint8_tg_alarm_trigger=0;

终极结论

volatile = 强制读最新值
uint8_t = 保证读写不被切碎

两个一起用 = 中断与任务之间传递标志位的最完美方案!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 11:23:49

探究 libhv Socketpair 在 clumsy 模拟延迟下的“超时”之谜

前言在进行网络编程开发时,我们经常使用 libhv 这种高性能的网络库,并利用 clumsy 等工具模拟弱网环境。最近在 Windows 环境下,当开启 clumsy 的 inbound 和 outbound 双向延迟(20ms)时,发现 libhv 的 Soc…

作者头像 李华
网站建设 2026/4/25 11:21:18

如何永久保存微信聊天记录:WeChatMsg终极指南与完整教程

如何永久保存微信聊天记录:WeChatMsg终极指南与完整教程 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeC…

作者头像 李华
网站建设 2026/4/25 11:16:02

WeDLM-7B-Base企业应用:与GitLab CI集成实现模型更新自动部署

WeDLM-7B-Base企业应用:与GitLab CI集成实现模型更新自动部署 1. 项目背景与技术优势 WeDLM-7B-Base是一款基于扩散机制的高性能基座语言模型,拥有70亿参数规模。该模型在标准因果注意力机制基础上实现了并行掩码恢复技术,能够一次生成多个…

作者头像 李华
网站建设 2026/4/25 11:14:31

避开HSE计算的那些坑:从收敛失败到能带错乱,我的VASP实战调试记录

HSE计算实战避坑指南:从参数调优到结果解析 第一次用HSE06算完能带,打开EIGENVAL文件时我愣住了——价带顶和导带底怎么跑到不同k点去了?这和我预想的半导体能带结构完全不同。后来才发现,原来HSE计算中有这么多隐藏的"坑&qu…

作者头像 李华