UCOS-III与STM32F103C8T6的融合
前些天学校开设了一门实验课,让我对UCOS有了一些了解,不过,实验使用的是飞思卡尔的MC9X128XS的开发板,系统使用的是UCOS-II,实验课结束后,我就寻思着将UCOS移植到我手头的F103C8核心板,上网查看了一波后,发现UCOS-II有点老了,1998年的产品,本着用新不用旧的态度,选择了UCOS-III,这是2009年出来的,目前最新版本是V3.07.03,不过这是我后来知道的,我是移植完成才写的这篇博客,我移植的是V3.03.01,移植过程中找资料才发现还有V3.07.03,但是懒得换了,看更新说明变换还挺大。不过V3.03.01也够了,F103资源本来就不多。
移植教程网上一大堆,但几乎都是基于标准库的,我个人是不用标准库的,但变化不是很大,主要就是系统时钟初始化有区别,还有就是启动文件不一样,HAL库附带的启动文件更简洁一些。
使用HAL库就不得不提及几个工具,STM32CubeMX是ST推出的一个代码生成工具,这个软件使用图形化界面配置,包括系统时钟,外设(ADC,SPI,IIC,UART,TIM,EXIT等等),还可以加入软件包,比如加入FATFS以支持文件系统,FREEOS实时操作系统,USB FS。配置完成,就可以生成工程模板,用相应的IDE打开就行了。支持KEIL,IAR TRUESTUDIO,Make等等一些主流IDE。
但是更新到V5版本之后,ST推出了一个大杀器STM32CubeIDE,这个软件集成了STM32CubeMX,Eclipse,stlink,jlink,如果你用过stm32F系列芯片,就知道这意味着什么。秒天秒地的存在啊。
本次移植也是基于STM32CubeIDE的。所以开始之前,请确保你有STM32CubeIDE,或者keil也可以,不过麻烦一点,或者你熟悉的一款IDE,但是你知道相应操作可以实现同样的效果就可以了。
说明一下
软件 | 版本 |
---|---|
STM32CubeIDE | V1.0.2 |
HAL库 | V1.8.0 |
UCOS-III | v3.03.01 |
以下就是移植步骤:
!!!!移植之前建议看一下《野火-UCOS-III内核实现与应用开发实战-基于STM32》 了解一下UCOS-III
- 使用STM32CubeIDE创建一个裸板工程,点亮一个LED即可。具体操作不再赘述。
- 从micrium官网的下载中心->STMicroelectronics下载一个例程Micrium_uC-Eval-STM32F107_uCOS-III,下载需要注册。
打开例程文件夹
ucOS\Micrium_uC-Eval-STM32F107_uCOS-III\Micrium\Software
,应该是以下结构的:
返回到
ucOS\Micrium_uC-Eval-STM32F107_uCOS-III
,将整个Software
复制到 项目的根目录下,复制完成后,在CubeIDE里应该是这样的目录结构:设置源文件夹,打开项目属性对话框,添加完成后:
设置
include
路径,设置完成后如下图:
添加文件到
Src
文件夹。打开
\Micrium\Software\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III
目录,将以下文件复制到Src
修改文件。
- 修改
includes.h
。 删除stm32f1xx_lib.h
, 添加stm32f1xx_hal.h
和void Error_Handler (void);
。 - 修改启动文件
startup_stm32f103c8tx.s
。将PendSV_Handler
替换成OS_CPU_PendSVHandler
,有三处要替换,SysTick_Handler
替换OS_CPU_SysTickHandler
, 也有三处要替换。 修改
os_app_hooks.c
。在void App_OS_TimeTickHook (void)
添加HAL_IncTick();
。void App_OS_TimeTickHook (void){ HAL_IncTick(); }
- 修改
bsp.c
。与BSP
相关的代码都可以注释掉,只保留与DWT
相关的。 - 修改
aap.c
。将main.c
关于void SystemClock_Config(void)
的声明和定义复制到app.c
;之后就可以删除main.c
和main.h
。接下来开始修改初始化系统和硬件的代码了,容易踩坑也是这里。cubeIEDE生成的代码里,一开始就调用了HAL_Init()
,在这个函数中,调用了HAL_InitTick()
, 这个函数就是坑,原因在于这个函数初始化了Systemtick 也就是所说的滴答时钟,一旦初始化完成,滴答时钟就开始运行,产生定时中断,而这个中断被UCOS接管了(OS_CPU_SysTickHandler()
),这个函数体:
void OS_CPU_SysTickHandler (void) { CPU_SR_ALLOC(); CPU_CRITICAL_ENTER(); OSIntNestingCtr++; /* Tell uC/OS-III that we are starting an ISR */ CPU_CRITICAL_EXIT(); OSTimeTick(); /* Call uC/OS-III's OSTimeTick() */ OSIntExit(); /* Tell uC/OS-III that we are leaving the ISR */ }
- 修改
可以看到,这个函数对
OSIntNestingCtr
进行了++
操作,而在OSInit()
函数里调用了OS_IdleTaskInit()
,OS_IdleTaskInit()
函数调用OSTaskCreate()
创建一个空闲任务,而在OSTaskCreate()
里有这样的语句:#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* ---------- CANNOT CREATE A TASK FROM AN ISR ---------- */ *p_err = OS_ERR_TASK_CREATE_ISR; return; } #endif
明显的
OSIntNestingCtr
大于0
,所以OSInit()
会失败。解决办法有两个(我采用第一种):- 注释掉
HAL_Init()
里面的HAL_InitTick()
- 先调用
OSInit()
后调用HAL_Init()
void SystemClock_Config(void)这个函数最终会初始化SystemTick,所以
void OS_CPU_SysTickInit (CPU_INT32U cnts)
可以不用调用。
最终初始化程序为:int main (void){ OS_ERR err; HAL_Init(); OSInit(&err); /* Init uC/OS-III. */ OSTaskCreate((OS_TCB *)&AppTaskStartTCB, /* Create the start task*/ (CPU_CHAR *)"App Task Start", (OS_TASK_PTR ) AppTaskStart, (void *) 0, (OS_PRIO ) APP_TASK_START_PRIO, (CPU_STK *)&AppTaskStartStk[0], (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10, (CPU_STK_SIZE) APP_TASK_START_STK_SIZE, (OS_MSG_QTY ) 5u, (OS_TICK ) 0u, (void *) 0, (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), (OS_ERR *)&err); OSStart(&err); /* Start multitasking (i.e. give control to uC/OS-III). */ for(;;){} }
static void AppTaskStart (void *p_arg)
static void AppTaskStart (void *p_arg){ // CPU_INT32U cpu_clk_freq; //CPU_INT32U cnts; OS_ERR err; (void)p_arg; SystemClock_Config(); HAL_NVIC_EnableIRQ(SysTick_IRQn); LED_Init(); CPU_Init(); // cpu_clk_freq = HAL_RCC_GetHCLKFreq(); /* Determine SysTick reference freq. */ // cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz; /* Determine nbr SysTick increments */ // OS_CPU_SysTickInit(cnts); /* Init uC/OS periodic time src (SysTick). */ Mem_Init(); /* Initialize Memory Management Module */ #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */ #endif CPU_IntDisMeasMaxCurReset(); #if (APP_CFG_SERIAL_EN == DEF_ENABLED) // BSP_Ser_Init(115200); /* Enable Serial Interface */ #endif AppTaskCreate(); /* Create Application Tasks */ AppObjCreate(); /* Create Application Objects */ while(DEF_TRUE) { /* Task body, always written as an infinite loop. */ HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); //OSTimeDly(100, OS_OPT_TIME_DLY, &err); OSTimeDlyHMSM(0, 0, 0, 100, OS_OPT_TIME_HMSM_STRICT, &err); } }
至此,移植完成。
附上
项目文件- 修改
根据上面的步骤,添加 TIM4
作为 HAL
库的超时检测机制
项目文件