下面,我们移植下ucosii,虽说比较老了,但是稳定性还是可以的。
更新的版本更复杂,能够支持的任务数也更多,但是意味需要更大的空间和更快的速度。
当然能运行UCOSII,首先得满足一下几个条件:
废话不多说:
先下载UCOSII源码包
UCOSII.rar
(94.36 KB)
,我的这里年代久远,不知具体版本了。
并包含其头文件路径:
浮点数选择上:
///////////////////
UCOSII的整个系统时钟,使用SYSTICK滴答定时器,开启中断:
之前用在delay函数了,重写delay函数:
delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "NuMicro.h"
#include "clk.h"
void UCOS_DelayInit(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif
delay.c
#include "delay.h"
#include "includes.h" //ucos 使用
//void delay_us(u32 nus)
//{
// CLK_SysTickDelay(nus);
//}
//void delay_ms(u16 nms)
//{
// while(nms--)
// {
// CLK_SysTickDelay(1000);
// }
//}
/*代表ucos可以延时的最少单位*/
uint32_t fac_ms=0;
/****************************************
*函数名称:UCOS_DelayInit
*输 入:无
*输 出:无
*功 能:UCOS延时初始化
******************************************/
void UCOS_DelayInit(void)
{
fac_ms=1000/OS_TICKS_PER_SEC;
/*UCOS每秒钟的计数次数*/
SysTick->LOAD = fac_ms * CyclesPerUs *1000;
/*为NVIC SysTick中断设置优先级*/
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
/*清零计数器*/
SysTick->VAL = 0;
/*使用外部12MHz时钟,并使能SysTick中断*/
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
}
/****************************************
*函数名称:Delayus
*输 入:无
*输 出:无
*功 能:微秒延时
******************************************/
void delay_us(u32 nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
/* 要加载的值 */
uint32_t reload=SysTick->LOAD;
/* 计算需要完成的节拍数 */
ticks=nus*CyclesPerUs;
tcnt=0;
/* 阻止ucos调度,防止打断微秒延时 */
OSSchedLock();
/* 刚进入时的计数器值 */
told=SysTick->VAL;
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
/* 这里注意一下SYSTICK是一个递减的计数器就可以了 */
if(tnow<told)tcnt+=told-tnow;
else tcnt+=reload-tnow+told;
told=tnow;
/* 时间超过/等于要延迟的时间,则退出 */
if(tcnt>=ticks)break;
}
}
/* 开启UCOS调度 */
OSSchedUnlock();
}
/****************************************
*函数名称:Delayms
*输 入:无
*输 出:无
*功 能:毫秒延时
******************************************/
void delay_ms(u16 nms)
{
/* 如果OS已经在跑了 */
if(OSRunning==OS_TRUE&&OSLockNesting==0)
{
/* 延时的时间大于OS的最少时间周期 */
if(nms>=fac_ms)
{
/* OS延时 */
OSTimeDly(nms/fac_ms);
}
/* OS已经无法提供这么小的延时了,采用普通方式延时 */
nms%=fac_ms;
}
/* 普通方式延时 */
delay_us((uint32_t)(nms*1000));
}
/****************************************
*函数名称:SysTick_Handler
*输 入:无
*输 出:无
*功 能:系统滴答中断服务函数
******************************************/
void SysTick_Handler(void)
{
/* 进入中断 */
OSIntEnter();
/* 调用ucos的时钟服务程序 */
OSTimeTick();
/* 触发任务切换软中断 */
OSIntExit();
}
在systick中断中,调用ucos的时钟服务程序,核心函数~
具体真正的任务切换,其实写在汇编语言os_cpu_a.asm里面,如果不感兴趣,可以直接跳过:
;/*********************** (C) COPYRIGHT 2010 Libraworks *************************
;* File Name : os_cpu_a.asm
;* Author : Librae
;* Version : V1.0
;* Date : 06/10/2010
;* Description : μCOS-II asm port for STM32
;*******************************************************************************/
IMPORT OSRunning ; External references
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSTCBCur
IMPORT OSTCBHighRdy
IMPORT OSIntNesting
IMPORT OSIntExit
IMPORT OSTaskSwHook
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT OS_CPU_SR_Save ; Functions declared in this file
EXPORT OS_CPU_SR_Restore
EXPORT PendSV_Handler
NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制寄存器
NVIC_SYSPRI2 EQU 0xE000ED22 ; 系统优先级寄存器(2)
NVIC_PENDSV_PRI EQU 0xFFFF ; PendSV中断和系统节拍中断
; (都为最低,0xff).
NVIC_PENDSVSET EQU 0x10000000 ; 触发软件中断的值.
PRESERVE8
AREA |.text|, CODE, READONLY
THUMB
;********************************************************************************************************
; CRITICAL SECTION METHOD 3 FUNCTIONS
;
; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
; into the CPU's status register.
;
; Prototypes : OS_CPU_SR OS_CPU_SR_Save(void);
; void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
;
;
; Note(s) : 1) These functions are used in general like this:
;
; void Task (void *p_arg)
; {
; #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
; OS_CPU_SR cpu_sr;
; #endif
;
; :
; :
; OS_ENTER_CRITICAL(); /* cpu_sr = OS_CPU_SaveSR(); */
; :
; :
; OS_EXIT_CRITICAL(); /* OS_CPU_RestoreSR(cpu_sr); */
; :
; :
; }
;********************************************************************************************************
OS_CPU_SR_Save
MRS R0, PRIMASK ;读取PRIMASK到R0,R0为返回值
CPSID I ;PRIMASK=1,关中断(NMI和硬件FAULT可以响应)
BX LR ;返回
OS_CPU_SR_Restore
MSR PRIMASK, R0 ;读取R0到PRIMASK中,R0为参数
BX LR ;返回
;/**************************************************************************************
;* 函数名称: OSStartHighRdy
;*
;* 功能描述: 使用调度器运行第一个任务
;*
;* 参 数: None
;*
;* 返 回 值: None
;**************************************************************************************/
OSStartHighRdy
LDR R4, =NVIC_SYSPRI2 ; 设置PendSV的优先级
LDR R5, =NVIC_PENDSV_PRI
STR R5, [R4]
MOV R4, #0 ; 设置PSP为0用于初始化上下文切换
MSR PSP, R4
LDR R4, =OSRunning ; 设置OSRunning为1
MOV R5, #1
STRB R5, [R4]
;切换到最高优先级的任务
LDR R4, =NVIC_INT_CTRL
LDR R5, =NVIC_PENDSVSET
STR R5, [R4] ;触发上下文切换
CPSIE I ;开中断
OSStartHang
B OSStartHang ;死循环,程序不会执行到这
;/**************************************************************************************
;* 函数名称: OSCtxSw
;*
;* 功能描述: 任务级上下文切换
;*
;* 参 数: None
;*
;* 返 回 值: None
;***************************************************************************************/
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常
LDR R5, =NVIC_PENDSVSET
STR R5, [R4] ;向NVIC_INT_CTRL写入NVIC_PENDSVSET触发PendSV中断
POP {R4, R5}
BX LR
;/**************************************************************************************
;* 函数名称: OSIntCtxSw
;*
;* 功能描述: 中断级任务切换
;*
;* 参 数: None
;*
;* 返 回 值: None
;***************************************************************************************/
OSIntCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;触发PendSV异常
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5} ;向NVIC_INT_CTRL写入NVIC_PENDSVSET触发PendSV中断
BX LR
NOP
;/**************************************************************************************
;* 函数名称: OSPendSV
;*
;* 功能描述: OSPendSV is used to cause a context switch.
;*
;* 参 数: None
;*
;* 返 回 值: None
;***************************************************************************************/
PendSV_Handler
CPSID I ; 关中断,任务切换期间要关中断
MRS R0, PSP ; 如果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈
CBZ R0, PendSV_Handler_Nosave ; 如果PSP为0就跳转到PendSV_Handler_Nosave
TST R14, #0x10 ; 如果当前任务使用到FPU的话就保存S16-S31寄存器
IT EQ
VSTMDBEQ R0!, {S16-S31}
SUBS R0, R0, #0x20 ; R0-=0x20
STM R0, {R4-R11} ; 保存剩下的R4-R11寄存器
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1]
PendSV_Handler_Nosave
;OSTaskSwHook用于扩展任务切换代码的功能
PUSH {R14} ; 保存R14的值,因为接着需要调用函数
LDR R0, =OSTaskSwHook ; 获取OSTaskSwHook函数地址保存到R0
BLX R0 ; 调用OSTaskSwHook
POP {R14} ; 恢复R14
;指向当前最高优先级的任务
LDR R0, =OSPrioCur ; 将OSPrioCur变量地址保存到R0
LDR R1, =OSPrioHighRdy ; 将OSPrioHighRdy变量地址保存到R1
LDRB R2, [R1] ; 获取OSPrioHighRdy的值保存到R2
STRB R2, [R0] ; 将R2的值写入OSPrioCur变量中
;指向就绪的最高优先级的任务控制块
LDR R0, =OSTCBCur ; 将OSTCBCur变量地址保存到R0
LDR R1, =OSTCBHighRdy ; 将OSTCBHighRdy变量地址保存到R1
LDR R2, [R1] ; 获取OSTCBHighRdy的值保存到R2
STR R2, [R0] ; 将R2的值写入OSTCBCur变量中
LDR R0, [R2] ; R0=*R2,即R0=OSTCBHighRdy,R0是新任务的SP(堆栈)
LDM R0, {R4-R11} ; 从堆栈中恢复R4-R11
ADDS R0, R0, #0x20 ; R0+=20
;任务如果使用FPU的话就将S16-S31从堆栈中恢复出来
TST R14, #0x10
TST R14, #0x10
IT EQ
VLDMIAEQ R0!, {S16-S31}
MSR PSP, R0 ; PSP=R0,用新任务的SP加载PSP
ORR LR, LR, #0x04 ; 确保LR的位2为1,返回后使用进程堆栈
CPSIE I ; 开中断
BX LR ; 中断返回
NOP
end
移植后,我们创建3个任务:
定义堆栈及任务名
/* START 任务 */
/* 设置任务优先级 */
#define START_TASK_PRIO 10
/* 设置任务堆栈大小 */
#define START_STK_SIZE 64
/* 创建任务堆栈空间 */
OS_STK START_TASK_STK[START_STK_SIZE];
/* 任务函数接口 */
void StartTask(void *pdata);
/* LED1 任务 */
/* 设置任务优先级 */
#define LED1_TASK_PRIO 7
/* 设置任务堆栈大小 */
#define LED1_STK_SIZE 64
/* 创建任务堆栈空间 */
OS_STK LED1_TASK_STK[LED1_STK_SIZE];
/* 任务函数接口 */
void Led1Task(void *pdata);
/* printf 任务 */
/* 设置任务优先级 */
#define PRINTF_TASK_PRIO 6
/* 设置任务堆栈大小 */
#define PRINTF_STK_SIZE 64
/* 创建任务堆栈空间 */
OS_STK PRINTF_TASK_STK[PRINTF_STK_SIZE];
/* 任务函数接口 */
void PrintfTask(void *pdata);
具体任务函数:
/****************************************
*函数名称:StartTask
*输 入:pdata -传入的参数
*输 出:无
*功 能:起始任务
******************************************/
void StartTask(void *pdata)
{
OS_CPU_SR cpu_sr=0;
pdata = pdata;
/* 初始化统计任务.这里会延时1秒钟左右 */
OSStatInit();
/* 进入临界区(无法被中断打断) */
OS_ENTER_CRITICAL();
/* 创建led1任务 */
OSTaskCreate(Led1Task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);
/* 创建printf任务 */
OSTaskCreate(PrintfTask,(void *)0,(OS_STK*)&PRINTF_TASK_STK[PRINTF_STK_SIZE-1],PRINTF_TASK_PRIO);
/* 挂起起始任务 */
OSTaskSuspend(START_TASK_PRIO);
/* 退出临界区(可以被中断打断) */
OS_EXIT_CRITICAL();
}
/****************************************
*函数名称:Led1Task
*输 入:pdata -传入的参数
*输 出:无
*功 能:Led1任务
******************************************/
void Led1Task(void *pdata)
{
printf("This is Led1 Task\r\n");
while(1)
{
LED0=1;
delay_ms(100);
LED0=0;
delay_ms(100);
}
}
/****************************************
*函数名称:PrintfTask
*输 入:pdata -传入的参数
*输 出:无
*功 能:printf任务
******************************************/
void PrintfTask(void *pdata)
{
u32 count=0;
printf("This is Printf Task\r\n");
while(1)
{
printf("count=%d\r\n",count++);
delay_ms(500);
}
}
在main函数里面,初始化SYSTICK,并调用:
/* UCOS延时初始化*/
UCOS_DelayInit();
/* OS初始化 */
OSInit();
/* 创建起始任务 */
OSTaskCreate(StartTask,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );
/* 执行任务调度 */
OSStart();
下载,并执行:
同时LED闪烁:
好了UCOSII的移植就到了,M471的测试目前先就这几个,下面可能会忙一阵子。
好了,谢谢观看~
|
|