usosiii时钟节拍

Posted lzd626

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了usosiii时钟节拍相关的知识,希望对你有一定的参考价值。

在APP组里的os_cfg_app.h文件可以设置系统时钟SysTick的时钟节拍频率(还可以设置它的任务优先级,任务栈大小,时间片数组大小),一般使用1000Hz,也就是节拍周期为1ms。

#define  OS_CFG_TICK_RATE_HZ            1000u               /* Tick rate in Hertz (10 to 1000 Hz)                     */
#define  OS_CFG_TICK_TASK_PRIO            10u               /* Priority                                               */
#define  OS_CFG_TICK_TASK_STK_SIZE       128u               /* Stack size (number of CPU_STK elements)                */
#define  OS_CFG_TICK_WHEEL_SIZE           17u               /* Number of ‘spokes‘ in tick  wheel; SHOULD be prime     */

下面我们看看它的配置步骤的关键代码,BSP_CPU_ClkFreq()函数内部其实调用的rcc的库函数来获取参考频率的,OS_CPU_SysTickInit()直接配置SysTick(源码中是把控制寄存器或其它寄存器定义成相关变量(在cpu.h文件里定义),再操作;详情go to definition/reference of ‘something“)。

    cpu_clk_freq = BSP_CPU_ClkFreq();                           /* 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).              */

技术分享图片

蒜了,不要偷懒,还是把代码贴一下,这段代码在os_cpu_c.c文件里,英文注释已经说明它干了什么,如果不清楚某个变量或参数,查找定义就OK了。

void  OS_CPU_SysTickInit (CPU_INT32U  cnts)
{
    CPU_INT32U  prio;
    CPU_REG_NVIC_ST_RELOAD = cnts - 1u;                    //装载的值                                      
/* Set SysTick handler prio. */ prio = CPU_REG_NVIC_SHPRI3; prio &= DEF_BIT_FIELD(24, 0); prio |= DEF_BIT_MASK(OS_CPU_CFG_SYSTICK_PRIO, 24); CPU_REG_NVIC_SHPRI3 = prio; /* Enable timer. */ CPU_REG_NVIC_ST_CTRL |= CPU_REG_NVIC_ST_CTRL_CLKSOURCE | CPU_REG_NVIC_ST_CTRL_ENABLE; /* Enable timer interrupt. */ CPU_REG_NVIC_ST_CTRL |= CPU_REG_NVIC_ST_CTRL_TICKINT; }

还记得之前移植的时候,将启动文件里的系统定时器中断和异常处理中断向量进行改名,改为OS_CPU_SysTickHandler的这个函数的就在上一段代码的上部,上面这个函数是表明特定时间中断一次,来处理系统事务,也就是中断的时候,干了些什么,其实仔细一看就是调用了OSTimeTick()。

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             */
}

 那下面我们就看看这个位于os_time.c文件里的OSTimeTick()干了些什么?

技术分享图片

也就是说上面的函数会发送信号量给OS_TickTask(),它接收到信号量之后就会进入就绪态,准备运行。

void  OS_TickTask (void  *p_arg)
{
    OS_ERR  err;
    CPU_TS  ts;


    p_arg = p_arg;                                          /* Prevent compiler warning                               */

    while (DEF_ON) {
        (void)OSTaskSemPend((OS_TICK  )0,//等待传来的信号量,以跳出循环,继续运行
                            (OS_OPT   )OS_OPT_PEND_BLOCKING,
                            (CPU_TS  *)&ts,
                            (OS_ERR  *)&err);               /* Wait for signal from tick interrupt                    */
        if (err == OS_ERR_NONE) {
            if (OSRunning == OS_STATE_OS_RUNNING) {
                OS_TickListUpdate();                        /* Update all tasks waiting for time                      */
            }
        }
    }
}

又可以看到OS_TickListUpdate的作用是更新所有任务的等待时间;也就是说,任务的执行,就是由前面说的系统时钟节拍按固定周期产生中断,以推送信号量给时基任务,时基任务再调用节拍列表更新函数,来更新所有任务的等待时间(然而这个函数又依据一些算法来实现的,太长了就不贴了)。

 

不急,接着往下看。。。

上一篇已经说过怎么新建任务了,那我们就直接来看app.c

*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/

#include <includes.h>


/*
*********************************************************************************************************
*                                            LOCAL DEFINES
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                                 TCB
*********************************************************************************************************
*/

static  OS_TCB   AppTaskStartTCB;

static  OS_TCB   AppTaskTestTCB;


/*
*********************************************************************************************************
*                                                STACKS
*********************************************************************************************************
*/

static  CPU_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];

static  CPU_STK  AppTaskTestStk [ APP_TASK_TEST_STK_SIZE ];


/*
*********************************************************************************************************
*                                         FUNCTION PROTOTYPES
*********************************************************************************************************
*/

static  void  AppTaskStart  (void *p_arg);

static  void  AppTaskTest   ( void * p_arg );


int  main (void)
{
    OS_ERR  err;


    OSInit(&err);                                                           //初始化 uC/OS-III

      /* 创建起始任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                            //任务控制块地址
                 (CPU_CHAR   *)"App Task Start",                            //任务名称
                 (OS_TASK_PTR ) AppTaskStart,                               //任务函数
                 (void       *) 0,                                          //传递给任务函数(形参p_arg)的实参
                 (OS_PRIO     ) APP_TASK_START_PRIO,                        //任务的优先级
                 (CPU_STK    *)&AppTaskStartStk[0],                         //任务堆栈的基地址
                 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,               //任务堆栈空间剩下1/10时限制其增长
                 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE,                    //任务堆栈空间(单位:sizeof(CPU_STK))
                 (OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数
                 (OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)
                 (void       *) 0,                                          //任务扩展(0表不扩展)
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项
                 (OS_ERR     *)&err);                                       //返回错误类型

    OSStart(&err);                                                          //启动多任务管理(交由uC/OS-III控制)
        
        
}

static  void  AppTaskStart (void *p_arg)
{
    CPU_INT32U  cpu_clk_freq;
    CPU_INT32U  cnts;
    OS_ERR      err;


   (void)p_arg;

    BSP_Init();                                                 //板级初始化
    CPU_Init();                                                 //初始化 CPU 组件(时间戳、关中断时间测量和主机名)

    cpu_clk_freq = BSP_CPU_ClkFreq();                           //获取 CPU 内核时钟频率(SysTick 工作时钟)
    cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;        //根据用户设定的时钟节拍频率计算 SysTick 定时器的计数值
    OS_CPU_SysTickInit(cnts);                                   //调用 SysTick 初始化函数,设置定时器计数值和启动定时器

    Mem_Init();                                                 //初始化内存管理组件(堆内存池和内存池表)

#if OS_CFG_STAT_TASK_EN > 0u                                    //如果使能(默认使能)了统计任务
    OSStatTaskCPUUsageInit(&err);                               //计算没有应用任务(只有空闲任务)运行时 CPU 的(最大)
#endif                                                          //容量(决定 OS_Stat_IdleCtrMax 的值,为后面计算 CPU 
                                                                //使用率使用)。
    CPU_IntDisMeasMaxCurReset();                                //复位(清零)当前最大关中断时间

    
        /* 创建测试任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskTestTCB,                             //任务控制块地址
                 (CPU_CHAR   *)"App Task Test",                             //任务名称
                 (OS_TASK_PTR ) AppTaskTest,                                //任务函数
                 (void       *) 0,                                          //传递给任务函数(形参p_arg)的实参
                 (OS_PRIO     ) APP_TASK_TEST_PRIO,                         //任务的优先级
                 (CPU_STK    *)&AppTaskTestStk[0],                          //任务堆栈的基地址
                 (CPU_STK_SIZE) APP_TASK_TEST_STK_SIZE / 10,                //任务堆栈空间剩下1/10时限制其增长
                 (CPU_STK_SIZE) APP_TASK_TEST_STK_SIZE,                     //任务堆栈空间(单位:sizeof(CPU_STK))
                 (OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数
                 (OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)
                 (void       *) 0,                                          //任务扩展(0表不扩展)
                 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项
                 (OS_ERR     *)&err);                                       //返回错误类型
        

        OSTaskDel ( & AppTaskStartTCB, & err );                     //删除起始任务本身,该任务不再运行
        
        
}


/*
*********************************************************************************************************
*                                          TEST TASK
*********************************************************************************************************
*/

static  void  AppTaskTest ( void * p_arg )
{
    OS_ERR           err;
    CPU_INT32U       cpu_clk_freq;
    CPU_TS           ts_start;
    CPU_TS           ts_end;
    CPU_SR_ALLOC();                                       //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变
                                                        //量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR)
                                                        //,开中断时将该值还原。
 (void)p_arg;


  cpu_clk_freq = BSP_CPU_ClkFreq();                     //获取CPU时钟,时间戳是以该时钟计数
    
    while (DEF_TRUE) {                                    //任务体,通常都写成一个死循环    
        ts_start = OS_TS_GET();                             //获取延时前时间戳
        
        OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err );         //延时1000个时钟节拍(1s)
        
        ts_end = OS_TS_GET() - ts_start;                    //获取延时后的时间戳(以CPU时钟进行计数的一个计数值),并计算延时时间
        
        OS_CRITICAL_ENTER();                                //进入临界段,不希望下面串口打印遭到中断
        
        printf ( "
延时1000个时钟节拍(1s),通过时间戳测得延时 %07d us,即 %04d ms。", 
                  ts_end / ( cpu_clk_freq / 1000000 ),     //将延时时间折算成 us 
                  ts_end / ( cpu_clk_freq / 1000 ) );      //将延时时间折算成 ms 
        
        OS_CRITICAL_EXIT();                                //进入临界段,不希望下面串口打印遭到中断
        
    }
        
        
}

主要就是通过延时1000个时钟节拍,来通过时间戳来计算延时的时间,具体直接看代码及注释就是了,下面是运行结果(由于这是软件延时,所以会有一点点小误差,但已经很精准了),那么就先到这里了。。。

技术分享图片

 


以上是关于usosiii时钟节拍的主要内容,如果未能解决你的问题,请参考以下文章

一个节拍信号的宽度是指 A.机器周期 B.指令周期 C.时钟周期 D存储周期

RT-Thread快速入门-时钟管理

为啥CPU工作需要时钟呀?时钟是如何控制CPU工作的?

STM32H7第15章 ThreadX系统时钟节拍和时间管理(绝对延迟和相对延迟)

STM32F429第15章 ThreadX系统时钟节拍和时间管理(绝对延迟和相对延迟)

嵌入式实时操作系统10——系统时钟节拍