曾经听学长说过,学点东西最好也写点东西,然后推荐了blog,但是我一直都不以为然,多年后我才认识到知识还是记录的好。于是这次借着自己琢磨的uC/OS-II移植教程开始自己的blog之旅。
开始移植前,推荐看任哲的《嵌入式实时操作系统uC/OS-II原理及应用》,学习uC/OS的知识点,知道这是个什么东西,干什么的,怎么实现的。然后,我开始准备移植。
工具准备:KEIL编译器,uC/OS-II源码(官网Micrium下载),STM32 min版
这里重点介绍uC/OS-II下载包里源码主要在Software文件夹里,其中我们需要的是uC/OS-II中的Ports和Source,以及EvalBoards->STM3210B-EVAL->RVMDK->OS-probe中的 app_cfg.h和os_cfg.h,目前用到就这些。在最初的程序模板中加入这些源码文件,之后再Project中添加这些文件。可以参考:
然后开始移植–uC/OS_SRC中的代码都是无需修改的,这里我们可以根据官网下载包里的AN-1018.PDF对相关代码进行修改。
首先介绍OS_CPU.H,按照所给出的代码进行修改
#define OS_CRITICAL_METHOD 3 //任哲书中有介绍3种方法的不同
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
#define OS_STK_GROWTH 1 //STM32堆栈方式向下
void OSCtxSw(void);
void OSIntCtxSw(void);
void OSStartHighRdy(void);
void OS_CPU_PendSVHandler(void); /* OSPendSV(void); */
// void OS_CPU_SysTickHandler(void);
// void OS_CPU_SysTickInit(void);
// INT32U OS_CPU_SysTickClkFreq(void);
接着是os_cpu_a.asm
将Public 改成 EXTERN
NVIC_INT_CTRL EQU 0xE000ED04 ; Interrupt control state register
;NVIC_SYSPRI2 EQU 0xE000ED20 ; System priority register (2)
NVIC_SYSPRI2 EQU 0xE000ED22 ; System priority register (yan).
;NVIC_PENDSV_PRI EQU 0x00 ; 0xFF00 ; PendSV priority value (highest)
NVIC_PENDSV_PRI EQU 0xFF ; PendSV priority value (LOWEST yan).
NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception
启动文件startup_stm32f10x_md.s中将所有的PendSV_Handler改成OS_CPU_PendSVHandler。
OS_cfg.h是来配置ucos-ii的资源,对其功能进行裁剪,以下功能可以在日后开启
#define OS_APP_HOOKS_EN 0
#define OS_DEBUG_EN 0
#define OS_EVENT_MULTI_EN 0
#define OS_SCHED_LOCK_EN 0
#define OS_TICK_STEP_EN 0
#define OS_TASK_CHANGE_PRIO_EN 0
#define OS_TASK_QUERY_EN 0
#define OS_TASK_STAT_EN 0
#define OS_TASK_STAT_STK_CHK_EN 0
#define OS_TASK_SUSPEND_EN 0
#define OS_FLAG_EN 0
#define OS_MBOX_EN 0
#define OS_TIME_DLY_HMSM_EN 0
#define OS_TIME_DLY_RESUME_EN 0
#define OS_TIME_GET_SET_EN 0
#define OS_TIME_TICK_HOOK_EN 0
然后在 stm32f10x_it.c 中,还需要添加 SysTick 中断的处理代码:
void SysTick_Handler(void)
{
OSIntEnter();
OSTimeTick();
OSIntExit();
}
最后,便是调试以上移植是否正确,可以添加以下代码进行编译下载。
#include "includes.h"
static OS_STK startup_task_stk1[APP_TASK_START_STK_SIZE];
static OS_STK startup_task_stk2[APP_TASK_START_STK_SIZE];
static void systick_init(void)
{
RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks);
SysTick_Config(rcc_clocks.HCLK_Frequency / OS_TICKS_PER_SEC);
}
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_8);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
}
static void Led1_task(void *p_arg)
{
systick_init(); /* Initialize the SysTick. */
for(;;)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_8);//led on
OSTimeDly(500);
GPIO_SetBits(GPIOA,GPIO_Pin_8);//led off
OSTimeDly(500);
}
}
static void Led2_task(void *p_arg)
{
systick_init(); /* Initialize the SysTick. */
for(;;)
{
GPIO_ResetBits(GPIOD,GPIO_Pin_2);//led on
OSTimeDly(500);
GPIO_SetBits(GPIOD,GPIO_Pin_2);//led off
OSTimeDly(500);
}
}
int main(void)
{
LED_Init();
OSInit();
OSTaskCreate(Led1_task, (void *)0,&startup_task_stk1[APP_TASK_START_STK_SIZE - 1],APP_TASK_START_PRIO);
OSTaskCreate(Led2_task, (void *)0,&startup_task_stk2[APP_TASK_START_STK_SIZE - 1],APP_TASK_START_PRIO-1);
OSStart();
return 0;
}
观察STM32min版上红绿两盏灯以1s的频率闪烁。