打印
[应用方案]

【NuMaker-M471测评】+ucosii移植

[复制链接]
2589|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
下面,我们移植下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的测试目前先就这几个,下面可能会忙一阵子。
好了,谢谢观看~

使用特权

评论回复
沙发
foxsbig| | 2021-8-19 16:47 | 只看该作者
都开始上系统了,可以可以~

使用特权

评论回复
板凳
jimmhu| | 2021-9-4 17:52 | 只看该作者
这个占用多大的ram呢   

使用特权

评论回复
地板
xiaoyaozt| | 2021-9-4 17:52 | 只看该作者
ucos II与ucgui的关系?  

使用特权

评论回复
5
lihuami| | 2021-9-4 17:53 | 只看该作者
ucos-ii是怎样移植到Keil C上的?  

使用特权

评论回复
6
10299823| | 2021-9-4 17:53 | 只看该作者
ucos_ii有哪些开发工具

使用特权

评论回复
7
primojones| | 2021-9-4 17:53 | 只看该作者
ucos-ii源码怎么官方下载  

使用特权

评论回复
8
alvpeg| | 2021-9-4 17:53 | 只看该作者
哪儿有ucos ii视频教程?   

使用特权

评论回复
9
sesefadou| | 2021-9-4 17:53 | 只看该作者
移植UCOSII和UCGUI,需要多大的空间资源  

使用特权

评论回复
10
cehuafan| | 2021-9-4 17:54 | 只看该作者
ucos ii最多几个任务   

使用特权

评论回复
11
uptown| | 2021-9-4 17:54 | 只看该作者
ucos-ii 支持哪些语言编程

使用特权

评论回复
12
alvpeg| | 2021-9-4 17:55 | 只看该作者
ucos ii和ucos iii用哪个

使用特权

评论回复
13
tail066| | 2021-9-10 14:10 | 只看该作者
我们一直用的III,
其实不知道II和III的差别
回头按照楼主的方法,试试III行不行

使用特权

评论回复
14
一刀一级| | 2021-9-10 16:26 | 只看该作者
ucos ii和ucos iii怎么选

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

111

主题

627

帖子

2

粉丝