之前尝试了移植freertos,参考了文章:【FreeRTOS】FreeRTOS移植stm32详细步骤介绍_freertos 202212.01下载-CSDN博客
版本是2022.12,但是笔者发现根本不能执行rtos,经过不断踩坑,靠着笔者对rtos内核的理解反复调试,终于成功移植。在这里将笔者的踩坑经历分享给大家:
1.注释
首先,在stm32f10x_it.c注释这两个函数
2.时钟
进入delay.c文件,有一些程序的裸机版本这里标注的是os的程序,请把这个文件改成这样:(注意,SYSTEM_SUPPORT_OS的值为1开启rtos,定义在sys.h)
#include "delay.h"
#include "sys.h"
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"
#include "task.h"
#endif
extern void xPortSysTickHandler(void);
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
void delay_init(void)
{
RCC->CR |= RCC_CR_HSEON;
while ((RCC->CR & RCC_CR_HSERDY) == 0)
{
}
RCC->CFGR |= RCC_CFGR_PLLMULL9;
RCC->CFGR |= RCC_CFGR_PLLSRC;
FLASH->ACR |= FLASH_ACR_LATENCY_2;
RCC->CR |= RCC_CR_PLLON;
while ((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
RCC->CFGR &= (~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != 0x08)
{
}
}
void delay_us(uint32_t time)
{
uint8_t i = 0;
while (time--)
{
i = 10;
while (i--)
;
}
}
void delay_ms(uint32_t time)
{
while (time--)
{
delay_us(1000);
}
}
void delay_xms(u32 nms)
{
u32 i;
for(i=0;i<nms;i++) delay_us(1000);
}
3.延时
延时上面已经改了,如果你使用rtos自带的systick时钟,不管是摘取时钟法还是其他方法,程序都容易卡死,笔者也是多次调试才发现。不过读者可以像裸机一样,先获取systick指向的值,再手动装载计数,计数完以后再复原时钟。如果对时钟精度有要求,可以考虑这种方法:
void delay_us(uint32_t us)
{
uint32_t val_temp=SysTick->VAL;
uint32_t load_temp=SysTick->LOAD;
uint16_t ctrl_temp=SysTick->CTRL;
SysTick->LOAD = 72 * us;
SysTick->VAL = 0x00;
SysTick->CTRL = 0x00000001;
while (!(SysTick->CTRL & 0x00010000));
SysTick->LOAD = load_temp;
SysTick->VAL = val_temp;
SysTick->CTRL = ctrl_temp;
}
4.PendSV_Handler
下载程序,发现仍然不能运行,使用软件模拟,发现卡在PendSV_Handler这里,
后面提示weak,意思到可能是这两个函数在搞鬼,搜索了一下,在portmacro.h文件添加
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
5.时钟频率与堆栈大小
现在程序理论上可以进入rtos了,但是笔者调试发现仍然不行,转到FreerRTOSconfig.h文件看了一下,发现分配的堆栈空间是17*1024,下载了elf文件,在linux下使用readelf命令,结果如下:
stm32f103c8t6的ram就是截至到20005000,这已经逼近了,于是笔者把FreeRTOSconfig.h这里修改为10*1024:(大概在40-50行,记得把晶振频率和configTICK_RATE_HZ也改了)
到了这一步,FreeRTOS一般是可以顺利执行了,读者可以进行下载或仿真验证,如果成功了,后面的可以忽略了,如果Freertos还未启动成功,请继续看下文。
6.临界区函数的问题
到这一步,rtos应该是可以使用了,但是笔者发现并没有。经过调试发现,在注释了临界区函数(也就是taskENTER_CRITICAL())后,rtos执行了:
7.系统节拍和优先级
仔细研究,猜测是临界区没有启到屏蔽作用,转到FreeRTOSconfig.h文件的configMAX_SYSCALL_INTERRUPT_PRIORITY一看,发现它的值是191,根据笔者的理解该宏是高四位有效,也就是等于11,但是191并没有起到屏蔽作用,改成11发现成功了,这是因为SysTick定时器被捆绑在NVIC中,用于产生SysTick异常(异常号15),也就是说191并没有取高四位,优先级小于15,临界区的屏蔽就不起作用了。
经过笔者的调试,认为这是大小端序的问题。
于是笔者修改portmarco.h文件的vPortRaiseBASEPRI函数,如下所示:
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
* section. */
mov r1,ulNewBASEPRI
revsh r1,r1
msr basepri, r1
dsb
isb
/* *INDENT-ON* */
}
}
改成这样后,设置为191就能正常执行程序了,当然直接改为11也是一样的。
此时笔者发现rtos仍未执行,猜测可能是被系统时钟滴答计数器中断打断,改为1后也是一样的,我们知道系统延时和阻塞时间都是以系统节拍时钟周期为单位,于是修改#define configTICK_RATE_HZ ( ( TickType_t ) 100 ),改为100,也就是10ms执行一次中断,此时rtos才顺利执行(修改的地方参考第五节标注的位置):