【STM32】IAP升级02关闭总中断,检测栈顶指针

IAP升级方法

IAP升级时需要关闭总中断

TM32在使用时有时需要禁用全局中断,比如MCU在升级过程中需禁用外部中断,防止升级过程中外部中断触发导致升级失败。

ARM MDK中提供了如下两个接口来禁用和开启总中断:

__disable_irq(); // 关闭总中断

__enable_irq(); // 开启总中断 但测试发现这样一个问题,在关闭总中断后,如果有中断触发,虽然此时不会引发中断,但在调用__enable_irq()开启总中断后,MCU会立即处理之前触发的中断。

这说明__disable_irq()只是禁止CPU去响应中断,没有真正的去屏蔽中断的触发,中断发生后,相应的寄存器会将中断标志置位,在__enable_irq()开启中断后,由于相应的中断标志没有清空,因而还会触发中断。

所以要想禁止所有中断,必须对逐个模块的中断进行 Disable操作,由于每个模块中断源有很多,对逐个中断Disable的话比较复杂,较为简单的方法是通过 XXX_ClearITPendingBit()清除中断标志或者直接通过XXX_DeInit()来清除寄存器的状态。这样在 __enable_irq()开启总中断后,MCU就不会响应之前触发的中断了。

检测栈顶指针

bootloader跳跃到APP程序的时候,会进行栈顶指针的检测,栈顶指针也就是我们程序能够运行的最大空间

if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)//检测栈顶指针
    { 
      /* Jump to user application */
      /*ApplicationAddress为用户程序的栈地址,+4便为用户程序的复位中断向量地址*/  
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      /*执行用户空间的复位中断向量函数,里面主要是进行系统时钟配置,执行用户空间的main函数数*/  
      Jump_To_Application();
    }

在保存了一个完整的 APP 到了对应的位置后,我们需要对栈顶进行检查操作,初步检查程序设置正确再进行跳转。我们以 Flash APP 为例,用 bin 文件查看工具。可以看到 bin 的内容默认为小端结构
在这里插入图片描述
0x2000 0818就是栈顶指针
0x0801 0289是app的Reset_Handler函数地址

程序在运行过程中,0x20000000是我们SRAM运行的起始地址,空间是128KB,也就是0x20000000-0X2001FFFF这个范围。 所以*(__IO uint32_t*)USBD_DFU_APP_DEFAULT_ADD) ,这句话的意思是取得用户地址空间存放的数据,这个空间就是0x08010000,当APP运行的程序栈顶指针落在SRAM区域时,也就是0x20000000到0X2001FFFF这个区间时候,这个时候即是有效的栈顶指针,由于开始的地址是0x20000000,而结束的地址是0X2001FFFF,我们只要判断它的高16位,即是确定这个地址范围在从0x2000开始。

### 使用 `__disable_irq` 函数禁用中断STM32F103C8 微控制器中,可以通过调用 CMSIS 提供的核心功能函数来管理全局中断状态。具体来说,`__disable_irq()` 是一个用于禁用全局中断的函数[^5]。该函数的作用是清除 Cortex-M3 处理器中的 PRIMASK 寄存器位,从而阻止任何可屏蔽中断的发生。 以下是关于如何正确使用此函数的一些说明: #### 1. **禁用中断** 当需要临时停止处理所有可屏蔽中断时,可以调用 `__disable_irq()` 来完成这一操作。这通常发生在临界区代码执行期间,以防止中断干扰数据一致性或硬件资源访问。 ```c // 禁用全局中断 __disable_irq(); ``` #### 2. **恢复中断** 为了确保系统的正常运行,在完成必要的操作之后应重新启用中断。CMSIS 同样提供了一个对应的函数 `__enable_irq()`,它会重置 PRIMASK 寄存器并允许再次接收中断请求。 ```c // 恢复全局中断 __enable_irq(); ``` #### 3. **保存当前中断状态** 有时可能希望仅在特定条件下关闭中断,并且稍后能够返回到原始的状态而无需手动调整。在这种情况下,可以利用 `__get_PRIMASK()` 和 `__set_PRIMASK()` 函数分别获取和设置 PRIMASK 的值。 ```c uint32_t primask; primask = __get_PRIMASK(); // 获取当前PRIMASK状态 if (!primask) { __disable_irq(); // 如果未被禁用,则禁用中断 } // 执行关键部分代码... if (!primask) { __enable_irq(); // 只有之前不是已禁用的情况下才重新开启 } ``` 以上方法有助于保护共享变量免受并发修改的影响,同时保持程序逻辑清晰简洁[^6]。 #### 示例场景:保护共享资源 假设存在一个多线程环境(例如 RTOS),其中多个任务都需要访问同一个外设寄存器组。为了避免竞争条件发生,可以在每次访问前先暂停中断服务例程(ISR),待更新完成后立即解除封锁。 ```c void critical_section_access() { uint32_t primask; primask = __get_PRIMASK(); if (!primask) { __disable_irq(); // 关闭中断以防冲突 } // 访问共享资源... GPIOA->ODR ^= (1 << 5); // 切换PA5引脚电平作为例子 if (!primask) { __enable_irq(); // 若先前处于打开则恢复正常工作流程 } } ``` 这种做法特别适用于那些对时间敏感的操作场合下,比如实时控制系统内的精确计时测量或是通信协议的数据帧同步过程等。 ### 注意事项 尽管上述技术有效解决了某些编程难题,但也需要注意过度依赖它们可能导致性能下降甚至死锁现象出现。因此建议尽可能缩短锁定时间段长度以及考虑采用其他更高级别的机制如信号量(semaphore)或者互斥体(mutex)替代简单粗暴的方式来进行进程间协调管理工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值