STM32F1协程框架下高栈消耗ISR独立栈的优化方案问询
基于STM32F1的协程框架ISR独立栈优化方案
问题背景
我在基于STM32F1的嵌入式项目中使用s_task协程框架,该框架简化了应用开发,但每个协程拥有独立栈空间,需按中断嵌套的最坏情况分配栈大小。其中USB通信ISR的栈消耗较大,若为所有协程栈都按最坏情况分配会造成RAM浪费。我已为该ISR实现独立栈,但希望优化实现:一是消除额外的嵌套函数调用,二是让所有ISR共享独立于协程的栈,且不修改现有协程框架。
现有实现代码
#define stringify(s) xstringify(s) #define xstringify(s) #s extern "C" { void USB_LP_CAN1_RX0_IRQHandler_impl(void); #define USB_STACK_SIZE 128 void* _usb_old_sp; void* _usb_stack[USB_STACK_SIZE]; __attribute__((naked)) void USB_LP_CAN1_RX0_IRQHandler(void) { __asm__ volatile(" push {r0, lr} "); __asm__ volatile(" ldr r0, =_usb_old_sp "); __asm__ volatile(" str sp, [r0]"); __asm__ volatile(" ldr sp, =_usb_stack+" stringify(USB_STACK_SIZE) "*4 "); USB_LP_CAN1_RX0_IRQHandler_impl(); __asm__ volatile(" ldr r0, =_usb_old_sp "); __asm__ volatile(" ldr sp, [r0]"); __asm__ volatile(" pop {r0, pc} "); } void USB_LP_CAN1_RX0_IRQHandler_impl(void) { // ...USB ISR处理逻辑... } }
优化思路与建议
1. 消除嵌套函数的简洁实现
将原impl函数内的逻辑直接嵌入裸函数中,通过手动保存/恢复调用寄存器实现C代码执行,避免嵌套调用:
#define stringify(s) xstringify(s) #define xstringify(s) #s extern "C" { #define USB_STACK_SIZE 128 void* _usb_old_sp; void* _usb_stack[USB_STACK_SIZE]; __attribute__((naked)) void USB_LP_CAN1_RX0_IRQHandler(void) { __asm__ volatile(" push {r0, lr} "); __asm__ volatile(" ldr r0, =_usb_old_sp "); __asm__ volatile(" str sp, [r0]"); __asm__ volatile(" ldr sp, =_usb_stack+" stringify(USB_STACK_SIZE) "*4 "); // 手动保存C代码所需的调用寄存器 __asm__ volatile(" push {r1-r3, r12, lr} "); // --- 直接写入原impl函数的USB ISR逻辑 --- // 例如:USB中断处理、数据收发等代码 // ... // 恢复调用寄存器 __asm__ volatile(" pop {r1-r3, r12, lr} "); __asm__ volatile(" ldr r0, =_usb_old_sp "); __asm__ volatile(" ldr sp, [r0]"); __asm__ volatile(" pop {r0, pc} "); } }
2. 所有ISR共享独立栈的最优方案
利用Cortex-M内核的**主栈指针(MSP)与进程栈指针(PSP)**特性,实现协程栈与ISR栈的天然隔离:
- 协程框架通常使用PSP作为线程栈(每个协程独立栈),线程模式下通过CONTROL寄存器的SPSEL位选择PSP
- Cortex-M硬件默认在进入中断时自动切换到MSP,因此所有ISR会自动使用MSP指向的主栈
具体步骤:
- 调整启动文件的主栈配置:在
startup_stm32f10x_xx.s中修改主栈的大小和地址,分配一块足够容纳所有ISR(含中断嵌套)的栈空间:; 示例:分配1KB主栈,可根据实际ISR栈消耗调整 Stack_Size EQU 0x00000400 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp - 确认协程框架使用PSP:s_task框架初始化时会自动设置
CONTROL寄存器的SPSEL位(__set_CONTROL(__get_CONTROL() | (1 << 1))),确保线程模式使用PSP。
此方案无需为每个ISR编写栈切换代码,所有ISR自动共享主栈,协程栈与ISR栈完全隔离,且不需要修改协程框架核心逻辑。
3. 多ISR共享栈的过渡宏实现
若暂时无法修改启动文件,可通过宏定义实现通用的ISR共享栈包装,避免重复代码:
#define stringify(s) xstringify(s) #define xstringify(s) #s // 共享ISR栈配置 #define SHARED_ISR_STACK_SIZE 256 void* _shared_isr_old_sp; void* _shared_isr_stack[SHARED_ISR_STACK_SIZE]; // 通用ISR共享栈包装宏 #define ISR_WITH_SHARED_STACK(ISR_NAME) \ __attribute__((naked)) void ISR_NAME(void) { \ __asm__ volatile(" push {r0, lr} "); \ __asm__ volatile(" ldr r0, =_shared_isr_old_sp "); \ __asm__ volatile(" str sp, [r0]"); \ __asm__ volatile(" ldr sp, =_shared_isr_stack+" stringify(SHARED_ISR_STACK_SIZE) "*4 "); \ __asm__ volatile(" push {r1-r3, r12, lr} "); \ ISR_NAME##_impl(); \ __asm__ volatile(" pop {r1-r3, r12, lr} "); \ __asm__ volatile(" ldr r0, =_shared_isr_old_sp "); \ __asm__ volatile(" ldr sp, [r0]"); \ __asm__ volatile(" pop {r0, pc} "); \ } \ void ISR_NAME##_impl(void) // 使用示例:为USB和USART1 ISR配置共享栈 ISR_WITH_SHARED_STACK(USB_LP_CAN1_RX0_IRQHandler) { // USB ISR处理逻辑 } ISR_WITH_SHARED_STACK(USART1_IRQHandler) { // USART1 ISR处理逻辑 }
内容的提问来源于stack exchange,提问作者user52366
相关产品推荐
相关产品推荐

