zoukankan      html  css  js  c++  java
  • [置顶] 基于stm32f103zet6之UC/OS_II的学习1(初步移植OS点灯大法)

    很久很久都没有写博客了,最近真是比赛一个接着一个,都需要参加,所以stm32的学习一直停滞不前,趁着最近准备模块的时间开始着手ucosII的学习,

    没办法呀,学习还是要继续的。。

    现在开始正式学习,今天的要求不高,只是分析一下移植的时候需要注意的问题,暂且不研究内核代码!(代码移植参照着ST官方源代码)

    也就是资源里面名为 取AN-1018.pdf的文档。

    代码这里可以下载http://download.csdn.net/detail/king_bingge/5353528

    一、uc/OS的实时性是靠什么实现的?

    1、uC/OS的实时性就是靠定时中断来完成。

    2、每个时钟节拍到来,就会产生一次定时中断,中断后进行任务调度,运行就绪表中优先级最高的任务(非抢先型内核中断后继续运行被中断任务)。

    即过一段时间就检测是否有重要任务需要运行,是的就转而运行更重要的任务,从而确保实时性(裸机程序就无法这样做了)。

    当然这里没有把系统调用考虑进去。

    二、首先整体把握一下在M3上运行ucosII的架构


    这就是整个系统各模块之间的关系,好的接下来就按照手册来分析一下移植的时候需要注意的地方

    1、关于OS_CPU.h文件

    #ifndef  OS_CPU_H
    #define  OS_CPU_H
    
    
    #ifdef   OS_CPU_GLOBALS
    #define  OS_CPU_EXT
    #else
    #define  OS_CPU_EXT  extern
    #endif
    


    一个全局变量的声明问题

    2、类型定义

    typedef unsigned char  BOOLEAN;
    typedef unsigned char  INT8U;                    /* Unsigned  8 bit quantity                           */
    typedef signed   char  INT8S;                    /* Signed    8 bit quantity                           */
    typedef unsigned short INT16U;                   /* Unsigned 16 bit quantity                           */
    typedef signed   short INT16S;                   /* Signed   16 bit quantity                           */
    typedef unsigned int   INT32U;                   /* Unsigned 32 bit quantity                           */
    typedef signed   int   INT32S;                   /* Signed   32 bit quantity                           */
    typedef float          FP32;                     /* Single precision floating point                    */
    typedef double         FP64;                     /* Double precision floating point                    */
    
    typedef unsigned int   OS_STK;                   /* Each stack entry is 32-bit wide                    */
    typedef unsigned int   OS_CPU_SR;                /* Define size of CPU status register (PSR = 32 bits) */

    对于常用的编译器,这些都是没有问题的

    3、接下来是两个比较重要的函数,在汇编代码里面

    #define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}
    #define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}

    这两个代码就是进入和退出临界区的两个宏。所以当你使用了这两个宏定义,那么就需要加上这一句OS_CPU_SR  cpu_sr = 0;  进行定义并且初始化。

    所谓临界区:是指一些不能被中断的代码。哪些代码是不能中断的呢,比如我们模拟的入栈操作,再比如当我们在执行系统调用的时候。那么类似于这钟代码就是临界区,而上面这两个宏的作用就是当某些代码为临界代码的时候,那么我们就在开始这段代码的时候加上ENTER宏,在退出这段代码的时候就加上EXIT宏。

    4、接下来继续看看这两个宏做了什么事情吧

    跟踪进去可以发现

    OS_CPU_SR_Save
        MRS     R0, PRIMASK                                         ; Set prio int mask to mask all (except faults)
        CPSID   I
        BX      LR
    
    OS_CPU_SR_Restore
        MSR     PRIMASK, R0
        BX      LR


    就是我们上一步所说的打开和屏蔽中断,注意了,根据ATCPS规则(不知道的可以百度),C和汇编进行混合调用的时候,R0传递着第一个参数,并且R0还是传递返回值的。

    5、接下来就是栈的增长方向,在我们的stm32板子上面,栈是向下增长的,堆是向上增长的

    #define  OS_STK_GROWTH        1                   /* Stack grows from HIGH to LOW memory on ARM        */
    
    #define  OS_TASK_SW()         OSCtxSw()


    第二个宏定义是任务切换的宏,下面会提到

    6、下面涉及到得就是这个文件里面需要修改的代码,首先看源代码,这是函数原型声明

    /*
    *********************************************************************************************************
    *                                              PROTOTYPES
    *********************************************************************************************************
    */
    
    #if OS_CRITICAL_METHOD == 3                       /* See OS_CPU_A.ASM                                  */
    OS_CPU_SR  OS_CPU_SR_Save(void);
    void       OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
    #endif
    
    void       OSCtxSw(void);
    void       OSIntCtxSw(void);
    void       OSStartHighRdy(void);
    
    void       OS_CPU_PendSVHandler(void);
    
                                                      /* See OS_CPU_C.C                                    */
    void       OS_CPU_SysTickHandler(void);
    void       OS_CPU_SysTickInit(void);
    
                                                      /* See BSP.C                                         */
    INT32U     OS_CPU_SysTickClkFreq(void);


    我们需要做的就是把这个三个函数注释掉,因为这是我们自己实现的

                                                      /* See OS_CPU_C.C                                    */
    // void       OS_CPU_SysTickHandler(void);
    // void       OS_CPU_SysTickInit(void);
    
    //                                                   /* See BSP.C                                         */
    // INT32U     OS_CPU_SysTickClkFreq(void);

    至此,第一个文件修改完毕,继续。。

    三、关于os_cfg.h文件

    1、这里都是一些配置,根据我们需要实现的功能来配置我们的OS,当然,如果只是为了点灯,那么我们可以这样做

     #define OS_FLAG_EN                    0   //禁用信号量集    
     #define OS_MBOX_EN                   0   //禁用邮箱    
     #define OS_MEM_EN                     0   //禁用内存管理    
     #define OS_MUTEX_EN                0   //禁用互斥信号量    
     #define OS_Q_EN                        0   //禁用队列    
     #define OS_SEM_EN                     0   //禁用信号量    
     #define OS_TMR_EN                     0   //禁用定时器    
     #define OS_DEBUG_EN               0   //禁用调试  
     
     #define OS_APP_HOOKS_EN           0    336. #define OS_FLAG_EN                0   //禁用信号量集    
     #define OS_MBOX_EN                0   //禁用邮箱    
    #define OS_MEM_EN                 0   //禁用内存管理    
    #define OS_MUTEX_EN               0   //禁用互斥信号量    
     #define OS_Q_EN                   0   //禁用队列    
    #define OS_SEM_EN                 0   //禁用信号量    
     #define OS_TMR_EN                 0   //禁用定时器    
     #define OS_DEBUG_EN               0   //禁用调试  
     #define OS_APP_HOOKS_EN           0    //hook函数也可以注释掉
     #define OS_EVENT_MULTI_EN         0  //多重事件函数也是一样
     #define OS_EVENT_MULTI_EN         0  


    那么,到这里,这个文件中需要修改的内容就是这么多了。

    四、关于os_cpu_c.c文件。

    这个文件是对应于之前的宏开关来说的,我们要把之前三个函数相关的宏开关以及函数的定义注释掉,具体操作如下

    // #define  OS_CPU_CM3_NVIC_ST_CTRL    (*((volatile INT32U *)0xE000E010))   /* SysTick Ctrl & Status Reg. */
    // #define  OS_CPU_CM3_NVIC_ST_RELOAD  (*((volatile INT32U *)0xE000E014))   /* SysTick Reload  Value Reg. */
    // #define  OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018))   /* SysTick Current Value Reg. */
    // #define  OS_CPU_CM3_NVIC_ST_CAL     (*((volatile INT32U *)0xE000E01C))   /* SysTick Cal     Value Reg. */
    // #define  OS_CPU_CM3_NVIC_PRIO_ST    (*((volatile INT8U  *)0xE000ED23))   /* SysTick Handler Prio  Reg. */
    
    // #define  OS_CPU_CM3_NVIC_ST_CTRL_COUNT                    0x00010000     /* Count flag.                */
    // #define  OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC                  0x00000004     /* Clock Source.              */
    // #define  OS_CPU_CM3_NVIC_ST_CTRL_INTEN                    0x00000002     /* Interrupt enable.          */
    // #define  OS_CPU_CM3_NVIC_ST_CTRL_ENABLE                   0x00000001     /* Counter mode.              */
    // #define  OS_CPU_CM3_NVIC_PRIO_MIN                               0xFF     /* Min handler prio.          */


    对应的还有这个函数也需要注释掉

    //void  OS_CPU_SysTickInit (void)
    //{
    //    INT32U  cnts;
    //
    //
    //    cnts = OS_CPU_SysTickClkFreq() / OS_TICKS_PER_SEC;
    //
    //    OS_CPU_CM3_NVIC_ST_RELOAD = (cnts - 1);
    //                                                 /* Set prio of SysTick handler to min prio.           */
    //    OS_CPU_CM3_NVIC_PRIO_ST   = OS_CPU_CM3_NVIC_PRIO_MIN;
    //                                                 /* Enable timer.                                      */
    //    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;
    //                                                 /* Enable timer interrupt.                            */
    //    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN;
    /


    那么这样,这个文件也解决掉了。继续下一个

    五、在OS_dbg.c这个文件中

    修改一个地方,#define  OS_COMPILER_OPT  __root,将后面的__root注释掉,否则会报错。自己可以试试

    六、来到OS_cpu_a.asm这个汇编文件

    1、里面的PUBLIC全改为EXPORT。这是有ARM汇编语言语言规定的。

    2、RSEG CODE:CODE:NOROOT(2)    开辟代码段的格式也是需要修改的

    修改如下:

    		AREA |.text|, CODE , READONLY, ALIGN = 2
    		THUMB
    		REQUIRE8
    		PRESERVE8

    具体解释,看ARM的汇编编程介绍就知道了。

    到这里,这个文件也修改完毕。

    七、关于启动文件

    有一个地方需要修改,那就是中断这部分。把启动代码中所有出现PendSV_Handler的地方替换成OS_CPU_PendSVHandler即可。

    那么这个文件也修改完毕

    到这里,我们的移植也就完成了一大部分,接下来就是编写自己的代码了。

    八、编写几个简单的函数就能实现点灯了

    #include "includes.h"
    
    static OS_STK startup_task_stk[STARTUP_TASK_STK_SIZE];		  //定义栈
      
    int main(void)
    {
      	BSP_Init();
    	OSInit();
    	OSTaskCreate(Task_LED,(void *)0,
    	&startup_task_stk[STARTUP_TASK_STK_SIZE-1], STARTUP_TASK_PRIO);
    
    	OSStart();
        return 0;
     }
     
     /*
     * 函数名:BSP_Init
     * 描述  :时钟初始化、硬件初始化
     * 输入  :无
     * 输出  :无
     */
    void BSP_Init(void)
    {
        LED_GPIO_Config();  /* LED 端口初始化 */
    }
    
    void Task_LED(void *p_arg)
    {
       (void)p_arg;                		// 'p_arg' 并没有用到,防止编译器提示警告
    	SysTick_init();
        while (1)
        {
            LED1( ON );
            OSTimeDlyHMSM(0, 0,0,500);
            LED1( OFF);
            OSTimeDlyHMSM(0, 0,0,500);
        }
    }
     
    /*
     * 函数名:SysTick_init
     * 描述  :配置SysTick定时器
     * 输入  :无
     * 输出  :无
     */
    void SysTick_init(void)
    {
        SysTick_Config(SystemCoreClock /OS_TICKS_PER_SEC);//初始化并使能SysTick定时器
    }

    到这里就实现单任务系统了,OK。点灯完毕!接下来就是仔细分析源码了。

  • 相关阅读:
    Get-CrmSetting返回Unable to connect to the remote server的解决办法
    Dynamics 365中的常用Associate和Disassociate消息汇总
    Dynamics 365 Customer Engagement V9 活动源功能报错的解决方法
    Dynamics Customer Engagement V9版本配置面向Internet的部署时候下一步按钮不可点击的解决办法
    Dynamics 365检查工作流、SDK插件步骤是否选中运行成功后自动删除系统作业记录
    注意,更改团队所属业务部门用Update消息无效!
    Dynamics 365的审核日志分区删除超时报错怎么办?
    Dynamics 365使用Execute Multiple Request删除系统作业实体记录
    Dynamics 365的系统作业实体记录增长太快怎么回事?
    Dynamics CRM日期字段查询使用时分秒的方法
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3073045.html
Copyright © 2011-2022 走看看