打印
[应用相关]

RTX操作系统-RTX低功耗之tickles模式

[复制链接]
1818|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
追逐浪花|  楼主 | 2016-2-22 19:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
RTX低功耗之tickless模式
    本章节为大家讲解RTX本身支持的低功耗模式tickless实现方法,tickless低功耗机制是当前小型RTOS所采用的通用低功耗方法,比如embOS,FreeRTOS和uCOS-III(类似方法)都有这种机制。
    本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。
24.1 tickless低功耗模式介绍
24.2 RTX实现tickless模式的框架
24.3 tickless模式的API函数
24.4 实验例程说明
24.5      总结

24.1  tickless低功耗模式介绍
    tickless低功耗机制是当前小型RTOS所采用的通用低功耗方法,比如embOS,FreeRTOS和uCOS-III(类似方法)都有这种机制。
    RTX的低功耗也是采用的这种方式,那么tickless又是怎样一种模式呢,仅从字母上看tick是滴答时钟的意思,less是tick的后缀,表示较少的,这里的含义可以表示为无滴答时钟。整体看这个字母就是表示滴答时钟节拍停止运行的情况。
    反映在RTX上,tickless又是怎样一种情况呢?我们都知道,当用户任务都被挂起时,最低优先级的空闲任务会得到执行。那么STM32支持的睡眠模式,停机模式就可以放在空闲任务里面实现。为了实现低功耗最优设计,我们还不能直接把睡眠或者停机模式直接放在空闲任务就可以了。进入空闲任务后,首先要计算可以执行低功耗的最大时间,也就是求出下一个要执行的高优先级任务还剩多少时间。然后就是把低功耗的唤醒时间设置为这个求出的时间,时间到后系统会从低功耗模式被唤醒,继续执行多任务。这个就是所谓的tickless模式。从上面的讲解中可以看出,实现tickless模式最麻烦是低功耗可以执行的时间如何获取。关于这个问题,RTX已经为我们做好了,调用函数os_suspend即可。


沙发
追逐浪花|  楼主 | 2016-2-22 19:21 | 只看该作者
24.2  RTX实现tickless模式的框架
    RTX实现低功耗tickless模式的代码框架如下:
__task void os_idle_demon (void) {
  uint32_t sleep;

  ...
  /* 第1步:配置系统深度睡眠模式 */
  ...

  /* 第2步:通过函数os_suspend获取系统可以处于低功耗模式的时钟节拍个数,此函数会关闭调度器 */
  for (;;) {
    sleep = os_suspend ();        

    if (sleep) {
      ...
    /* 第3步:创建一个新的定时器,专门用于将系统从低功耗模式唤醒,并将唤醒时间设置为
函数os_suspend返回值sleep */
      ...

    /* 第4步:调用低功耗指令进入低功耗模式 */
      __WFE ();                     

    /* 第5步:系统从低功耗模式唤醒,调整系统实际处于低功耗状态的时钟节拍个数*/
    /* Adjust actual sleep time (in case of any event) */
      sleep = ...
}
/* 第6步:重新使能调度器并调整系统时间,继续多任务的执行 */
    os_resume (sleep);                  /* OS Resume                          */
  }
}
24.3  tickless模式的API函数
    使用如下2个函数可以实现RTX的tickless模式:
os_suspend
os_resume
关于这两个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件
                              

使用特权

评论回复
板凳
追逐浪花|  楼主 | 2016-2-22 19:21 | 只看该作者
下面我们也对这2个函数依次进行讲解:

24.3.1 函数os_suspend
函数原型:
U32 os_suspend (void);
函数描述:
函数os_suspend用于获取系统可以处于低功耗模式的时钟节拍个数。此函数必须配套函数os_resume一起使用。调用了函数os_suspend后,此函数会关闭调度器,即关闭任务切换,调用了函数os_resume会恢复任务调度。
    函数返回系统可以处于低功耗模式的时钟节拍个数。

使用这个函数要注意以下问题:
1.     只能在空闲任务里面调用这个函数。
2.     当系统进入到低功耗模式后,系统滴答定时器停止运行,因为调用了函数os_suspend,此函数会关闭滴答定时器。
使用举例:
    下面举的例子是RTX官方提供的基于STM32F2的低功耗设计代码,用于STM32F4也是可以,因为STM32F2和STM32F4的RTX时钟基本一样。下面的例子是通过RTC的低功耗唤醒功能实现。
#include <rtl.h>

__task void os_idle_demon (void) {
  uint32_t sleep;

  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;    /* Configure Cortex-M3 for deep sleep */
  PWR->CR &= ~PWR_CR_PDDS;              /* Enter Stop mode when in deepsleep  */
  PWR->CR  |= PWR_CR_LPDS;              /* Voltage regulator in low-power     */

  /* Enable LSI clock and wait until ready */
  RCC->CSR |= RCC_CSR_LSION;
  while ((RCC->CSR & RCC_CSR_LSIRDY) == 0);

  /* Enable power interface clock */
  RCC->APB1ENR |= RCC_APB1ENR_PWREN;

  /* Disable backup domain write protection */
  PWR->CR |= PWR_CR_DBP;

  /* Select LSI as clock source for RTC and enable RTC */
  RCC->BDCR &= ~RCC_BDCR_RTCSEL;
  RCC->BDCR |=  RCC_BDCR_RTCSEL_1;
  RCC->BDCR |=  RCC_BDCR_RTCEN;

  /* Disable the write protection for RTC registers */
  RTC->WPR   = 0xCA;
  RTC->WPR   = 0x53;

  /* Configure RTC auto-wakeup mode */
  RTC->ISR  &= ~RTC_ISR_WUTF;           /* Clear wakeup timer flag            */
  RTC->CR   &= ~RTC_CR_WUCKSEL;         /* Set RTC clock to 2kHz              */
  RTC->CR   |=  RTC_CR_WUTIE;           /* Enable RTC wakeup timer interrupt  */

  /* Configure EXTI line 22 for wakeup on rising edge */
  EXTI->EMR  |= (1 << 22);              /* Event request is not masked        */
  EXTI->RTSR |= (1 << 22);              /* Rising trigger enabled             */

  NVIC_EnableIRQ (RTC_WKUP_IRQn);       /* Enable RTC WakeUp IRQ              */

  for (;;) {
    /* HERE: include optional user code to be executed when no task runs.     */
    sleep = os_suspend ();              /* OS Suspend                         */

    if (sleep) {
      RTC->ISR &= ~RTC_ISR_WUTF;        /* Clear timer wakeup flag            */
      RTC->CR &= ~RTC_CR_WUTE;          /* Disable wakeup timer               */
      while ((RTC->ISR & RTC_ISR_WUTWF) == 0);

      /* RTC clock is @2kHz, set wakeup time for OS_TICK >= 1ms */
      RTC->WUTR = (sleep * (OS_TICK / 1000) * 2);

      RTC->CR |=  RTC_CR_WUTE;          /* Enable wakeup timer                */
      __WFE ();                         /* Enter STOP mode                    */

      /* After Wake-up */
      if ((RTC->ISR & RTC_ISR_WUTF) == 0) {
        sleep = 0;                      /* We didn't enter Stop mode          */
      }
    }
    os_resume (sleep);                  /* OS Resume                          */
  }
}


使用特权

评论回复
地板
追逐浪花|  楼主 | 2016-2-22 19:22 | 只看该作者
24.3.2 函数os_resume
函数原型:
void os_resume (
    U32 sleep_time );    /* 系统处于低功耗模式的时钟节拍个数 */

函数描述:
函数os_resume用于恢复任务调度器的运行。此函数必须配套函数os_suspend一起使用。调用了函数os_suspend后,此函数会关闭调度器,即关闭任务切换,调用函数os_resume会恢复任务调度。
   参数填写系统处于低功耗模式的时钟节拍个数。

使用这个函数要注意以下问题:
1.     只能在空闲任务里面调用这个函数。
2.     当系统进入到低功耗模式后,系统滴答定时器停止运行,因为调用了函数os_suspend,此函数会关闭滴答定时器。
使用举例:
参考上面函数os_suspend的举例。
24.4 实验例程说明
24.4.1 STM32F103开发板实验
配套例子:
    V4-424_RTX实验_低功耗(tickless模式)
实验目的:
    1.     学习RTX的低功耗(tickless模式)。
    2.     tickless模式低功耗的实现采用了停机模式,使用RTC的闹钟中断进行唤醒
    3.     tickless模式的实现在源文件RTX_Conf_CM.C文件中的空闲任务os_idle_demon函数里面。
实验内容:
    1.K1按键按下,串口打印。
    2.K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro。
      任务AppTaskMsgPro接收到消息后进行消息处理。
    3.各个任务实现的功能如下:
    AppTaskUserIF任务   :按键消息处理。
    AppTaskLED任务     :LED闪烁。
    AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的信号量同步信号。
    AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
设计低功耗主要从以下几个方面着手:
    1.     用户需要根据最低电源消耗、最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式可以使用的低功耗方式有休眠模式,待机模式,停机模式。
    2.     选择了低功耗方式后就是关闭可以关闭的外设时钟。
    3.     降低系统主频。
    4.     注意I/O的状态。
    如果此I/O口带上拉,请设置为高电平输出或者高阻态输入;
    如果此I/O口带下拉,请设置为低电平输出或者高阻态输入;
      a.在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态。
      b.在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。
      c.在待机模式下,所有的I/O引脚处于高阻态,除了以下的引脚:
        ● 复位引脚(始终有效)。
        ● 当被设置为防侵入或校准输出时的TAMPER引脚。
        ● 被使能的唤醒引脚。
    5.注意I/O和外设IC的连接。
    6.测低功耗的时候,一定不要连接调试器,更不能边调试边测电流。


使用特权

评论回复
5
追逐浪花|  楼主 | 2016-2-22 19:23 | 只看该作者
RTX配置:
    RTX配置向导详情如下:
                              
Task Configuration
Number of concurrent running tasks
  允许创建4个任务,实际创建了如下四个任务:
                AppTaskUserIF任务   :按键消息处理。
                AppTaskLED任务     :LED闪烁。
                AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
        Number of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
    在休眠模式下,无法动态的查看任务的调试信息。下面的是单步调试时状态查看:
程序设计:
任务栈大小分配:
staticuint64_t AppTaskUserIFStk[512/8];   /* 任务栈 */
    staticuint64_t AppTaskLEDStk[256/8];      /* 任务栈 */
    staticuint64_t AppTaskMsgProStk[512/8];  /* 任务栈 */
    staticuint64_t AppTaskStartStk[512/8];     /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
系统栈大小分配:

使用特权

评论回复
6
追逐浪花|  楼主 | 2016-2-22 19:24 | 只看该作者
外设初始化:
    注意新加的函数初始化函数DBGMCU_Config(DBGMCU_STOP, ENABLE);保证停机模式下调试器正常连接使用。
/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
*             全局变量。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
     /* 保证停机模式下调试器继续可以连接使用 */
     DBGMCU_Config(DBGMCU_STOP, ENABLE);
   
     /* 优先级分组设置为4, 优先配置好NVIC */
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

     bsp_InitUart();    /* 初始化串口 */
     bsp_InitLed();     /* 初始LED指示灯端口 */
     bsp_InitKey();     /* 初始化按键 */
}

RTX初始化:
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: 标准c程序入口。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
int main (void)
{   
     /* 初始化外设 */
     bsp_Init();
   
     /* 创建启动任务 */
     os_sys_init_user (AppTaskStart,             /* 任务函数 */
                       4,                        /* 任务优先级 */
                       &AppTaskStartStk,         /* 任务栈 */
                       sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
     while(1);
}

RTX任务创建:
/*
*********************************************************************************************************
*    函 数 名: AppTaskCreate
*    功能说明: 创建应用任务
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
                                           1,                         /* 任务优先级 */
                                           &AppTaskUserIFStk,         /* 任务栈 */
                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
   
     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */
                                        2,                       /* 任务优先级 */
                                        &AppTaskLEDStk,          /* 任务栈 */
                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */
   
     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
                                           3,                         /* 任务优先级 */
                                           &AppTaskMsgProStk,         /* 任务栈 */
                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
}


使用特权

评论回复
7
追逐浪花|  楼主 | 2016-2-22 19:25 | 只看该作者
tickless模式在空闲任务实现,即配置向导文件RTX_Conf_CM.c文件中
/*--------------------------- os_idle_demon ---------------------------------*/
#include "stm32f10x.h"
__task void os_idle_demon (void)
{
     EXTI_InitTypeDef  EXTI_InitStructure;
     NVIC_InitTypeDef NVIC_InitStructure;
     uint32_t sleep;
   
     /* 使能PWR和BKP时钟 */
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

     /* 允许写入RTC和后备寄存器 */
     PWR_BackupAccessCmd(ENABLE);

     /* 复位后备寄存器 */
     BKP_DeInit();

     /* 使能LSI */
     RCC_LSICmd(ENABLE);

     /* 等待直到LSI就绪 */
     while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);

     /* 选择LSI作为RTC的时钟 */
     RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);

     /* 使能RTC时钟 */
     RCC_RTCCLKCmd(ENABLE);

     /*
        在APB1总线复位或者停止后重新开启,RTC的任何读取前得等待RTC寄存器
        (RTC_CNT, RTC_ALR and RTC_PRL)跟RTC APB时钟同步。
     */
     RTC_WaitForSynchro();

     /* 等待RTC寄存器写操作完成 */
     RTC_WaitForLastTask();

     /*
       1. LSI的频率典型值是40KHz(30KHz到60KHz)
       2. 这里按40KHz来计算
          RTC 周期 = RTCCLK / RTC_PR = (40 KHz)/(19 + 1) = 2KHz
     */
     RTC_SetPrescaler(19);

     /* 等待RTC寄存器写操作完成 */
     RTC_WaitForLastTask();

     /* EXTI线17连接到RTC闹钟事件,使能中断 */
     EXTI_ClearITPendingBit(EXTI_Line17);
     EXTI_InitStructure.EXTI_Line = EXTI_Line17;
     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
     EXTI_Init(&EXTI_InitStructure);

     /* 设置闹钟中断的NVIC */
     NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     NVIC_Init(&NVIC_InitStructure);

     for (;;)
     {
         /* 挂起OS, 并返回可以执行低功耗的时长 */
         sleep = os_suspend ();
         
         if (sleep)
         {
              /* RTC计数设置 */
              RTC_SetCounter(0);
              RTC_WaitForLastTask();

              /* 设置闹钟时间 */
              RTC_SetAlarm(sleep*(OS_TICK/1000)*2);
              RTC_WaitForLastTask();
            
              /* 使能闹钟中断 */
              RTC_ITConfig(RTC_IT_ALR, ENABLE);
              RTC_WaitForLastTask();
            
              /* 进入停机模式 */
              PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFE);
            
              /*
                1、当一个中断或唤醒事件导致退出停止模式时,HSI RC振荡器被选为系统时钟。
                2、退出低功耗的停机模式后,需要重新配置使用HSE和HSE
              */
              SystemInit();
            
              /*
                 在APB1总线复位或者停止后重新开启,RTC的任何读取前得等待RTC寄存器
                 (RTC_CNT, RTC_ALR and RTC_PRL)跟RTC APB时钟同步。
              */
              RTC_WaitForSynchro();
            
              /* 检查唤醒标志是否设置 */
              if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET)
              {
                   /* 用户可以在这里加入相关串口打印等函数来检测是否进入停机模式 */
                   //printf("lowpower\r\n");
                   /* 清除唤醒标志 */
                   PWR_ClearFlag(PWR_FLAG_WU);
              }
              else
              {
                   sleep = 0;
              }
            
         }
        
         /* 恢复OS */
         os_resume (sleep);           
     }
}

/*--------------------------- RTC闹钟中断 ----------------------------------*/
void RTCAlarm_IRQHandler(void)
{
     if (RTC_GetITStatus(RTC_IT_ALR) != RESET)
     {
         /* 禁止RTC的闹钟中断 */
         RTC_ITConfig(RTC_IT_ALR, DISABLE);
         RTC_WaitForLastTask();
        
         /* 清除中断标志 */
         EXTI_ClearITPendingBit(EXTI_Line17);
        
         /* 清除中断标志 */
         RTC_ClearITPendingBit(RTC_IT_ALR);
         RTC_WaitForLastTask();
     }   
}
信号量的创建:
static OS_SEM semaphore;

/*
*********************************************************************************************************
*    函 数 名: AppObjCreate
*    功能说明: 创建任务通信机制
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
     /* 创建信号量计数值是0, 用于任务同步 */
     os_sem_init (&semaphore, 0);
}
四个RTX任务的实现:
/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理     
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
     uint8_t ucKeyCode;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
        
         if (ucKeyCode != KEY_NONE)
         {
              switch (ucKeyCode)
              {
                   /* K1键按下,打印调试说明 */
                   case KEY_DOWN_K1:
                       printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
                       break;  

                   /* K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro */
                   case KEY_DOWN_K2:
                       printf("K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro\r\n");
                       os_sem_send (&semaphore);
                       break;

                   /* 其他的键值不处理 */
                   default:                    
                       break;
              }
         }
        
         os_dly_wait(20);
     }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskLED
*    功能说明: LED闪烁。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 2
*********************************************************************************************************
*/
__task void AppTaskLED(void)
{
     const uint16_t usFrequency = 200; /* 延迟周期 */
   
     /* 设置延迟周期 */
     os_itv_set(usFrequency);
   
    while(1)
    {
         bsp_LedToggle(2);
         bsp_LedToggle(3);

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
         os_itv_wait();
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskMsgPro
*    功能说明: 消息处理,等待任务AppTaskUserIF发来的信号量同步信号
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 3
*********************************************************************************************************
*/
__task void AppTaskMsgPro(void)
{
     OS_RESULT xResult;
     const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
   
    while(1)
    {
         xResult = os_sem_wait (&semaphore, usMaxBlockTime);
        
         switch (xResult)
         {
              /* 无需等待接受到信号量同步信号 */
              case OS_R_OK:
                   printf("无需等待接受到信号量同步信号\r\n");
                   break;  

              /* 信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号 */
              case OS_R_SEM:
                   printf("信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号\r\n");
                   break;

              /* 超时 */
              case OS_R_TMO:
                   bsp_LedToggle(1);
                   bsp_LedToggle(4);
                   break;
            
              /* 其他值不处理 */
              default:                    
                   break;
         }   
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskStart
*    功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 4
*********************************************************************************************************
*/
__task void AppTaskStart(void)
{
     /* 创建任务 */
     AppTaskCreate();
   
     /* 创建任务通信机制 */
     AppObjCreate();
   
    while(1)
    {
         /* 按键扫描 */
         bsp_KeyScan();
        os_dly_wait(10);
    }
}


使用特权

评论回复
8
追逐浪花|  楼主 | 2016-2-22 19:26 | 只看该作者
24.4.2   STM32F407开发板实验
配套例子:
    V5-424_RTX实验_低功耗(tickless模式)
实验目的:
    1.     学习RTX的低功耗(tickless模式)。
    2.     tickless模式低功耗的实现采用了停机模式,使用RTC的唤醒中断进行唤醒。这个和前面STM32F1开发板采用的闹钟中断唤醒不同。
    3.     tickless模式的实现在源文件RTX_Conf_CM.C文件中的空闲任务os_idle_demon函数里面。
实验内容:
    1.K1按键按下,串口打印。
    2.K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro。
    任务AppTaskMsgPro接收到消息后进行消息处理。
    3.各个任务实现的功能如下:
    AppTaskUserIF任务   :按键消息处理。
    AppTaskLED任务     :LED闪烁。
    AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的信号量同步信号。
    AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
设计低功耗主要从以下几个方面着手:
     1.     用户需要根据最低电源消耗、最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式可以使用的低功耗方式有休眠模式,待机模式,停机模式。
     2.     选择了低功耗方式后就是关闭可以关闭的外设时钟。
     3.     降低系统主频。
     4.     注意I/O的状态。
     如果此I/O口带上拉,请设置为高电平输出或者高阻态输入;
    如果此I/O口带下拉,请设置为低电平输出或者高阻态输入;
    a.在睡眠模式下,所有的I/O引脚都保持它们在运行模式时的状态。
    b.在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。
    c.在待机模式下,所有的I/O引脚处于高阻态,除了以下的引脚:
         ● 复位引脚(仍可用)。
         ● RTC_AF1 引脚 (PC13)(如果针对入侵、时间戳、 RTC 闹钟输出或 RTC 时钟校准输出进行了配置)。
         ● WKUP 引脚 (PA0)(如果使能)。
     5.注意I/O和外设IC的连接。
     6.测低功耗的时候,一定不要连接调试器,更不能边调试边测电流。
RTX配置:
    RTX配置向导详情如下:
                              
Task Configuration
Number of concurrent running tasks
  允许创建4个任务,实际创建了如下四个任务:
                AppTaskUserIF任务   :按键消息处理。
                AppTaskLED任务     :LED闪烁。
                AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
        Number of tasks with user-provided stack
    创建的4个任务都是采用自定义堆栈方式。

使用特权

评论回复
9
追逐浪花|  楼主 | 2016-2-22 19:26 | 只看该作者
RTX任务调试信息:
程序设计:
任务栈大小分配:
staticuint64_t AppTaskUserIFStk[512/8];   /* 任务栈 */
    staticuint64_t AppTaskLEDStk[256/8];      /* 任务栈 */
    staticuint64_t AppTaskMsgProStk[512/8];  /* 任务栈 */
    staticuint64_t AppTaskStartStk[512/8];     /* 任务栈 */
将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
系统栈大小分配:

使用特权

评论回复
10
追逐浪花|  楼主 | 2016-2-22 19:28 | 只看该作者
外设初始化:
    注意新加的函数初始化函数DBGMCU_Config(DBGMCU_STOP, ENABLE);保证停机模式下调试器正常连接使用。
/*
/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
*             全局变量。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
     /* 保证停机模式下调试器继续可以连接使用 */
     DBGMCU_Config(DBGMCU_STOP, ENABLE);
   
     /* 优先级分组设置为4, 优先配置好NVIC */
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
   
     bsp_InitUart();    /* 初始化串口 */
     bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
     bsp_InitLed();     /* 初始LED指示灯端口 */
}

RTX初始化:
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: 标准c程序入口。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
int main (void)
{   
     /* 初始化外设 */
     bsp_Init();
   
     /* 创建启动任务 */
     os_sys_init_user (AppTaskStart,             /* 任务函数 */
                       4,                        /* 任务优先级 */
                       &AppTaskStartStk,         /* 任务栈 */
                       sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
     while(1);
}

RTX任务创建:
/*
*********************************************************************************************************
*    函 数 名: AppTaskCreate
*    功能说明: 创建应用任务
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
     HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
                                           1,                         /* 任务优先级 */
                                           &AppTaskUserIFStk,         /* 任务栈 */
                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
   
     HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */
                                        2,                       /* 任务优先级 */
                                        &AppTaskLEDStk,          /* 任务栈 */
                                        sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */
   
     HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
                                           3,                         /* 任务优先级 */
                                           &AppTaskMsgProStk,         /* 任务栈 */
                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
}


使用特权

评论回复
11
追逐浪花|  楼主 | 2016-2-22 19:28 | 只看该作者
tickless模式在空闲任务实现,即配置向导文件RTX_Conf_CM.c文件中
/*----------------------------------------------------------------------------
*      Global Functions
*---------------------------------------------------------------------------*/
#include "stm32f4xx.h"
extern void SetSysClock(void);
void RTC_WKUP_IRQHandler (void) {
  EXTI->PR = (1 << 22);                 /* Clear pending EXTI interrupt      */
  RTC->CR &= ~RTC_CR_WUTE;              /* Disable wakeup timer              */
}

/*--------------------------- os_idle_demon ---------------------------------*/

__task void os_idle_demon (void) {
//  /* The idle demon is a system task, running when no other task is ready */
//  /* to run. The 'os_xxx' function calls are not allowed from this task.  */
  uint32_t sleep;

  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;    /* Configure Cortex-M3 for deep sleep */
  PWR->CR &= ~PWR_CR_PDDS;              /* Enter Stop mode when in deepsleep  */
  PWR->CR  |= PWR_CR_LPDS;              /* Voltage regulator in low-power     */

  /* Enable LSI clock and wait until ready */
  RCC->CSR |= RCC_CSR_LSION;
  while ((RCC->CSR & RCC_CSR_LSIRDY) == 0);

  /* Enable power interface clock */
  RCC->APB1ENR |= RCC_APB1ENR_PWREN;

  /* Disable backup domain write protection */
  PWR->CR |= PWR_CR_DBP;

  /* Select LSI as clock source for RTC and enable RTC */
  RCC->BDCR &= ~RCC_BDCR_RTCSEL;
  RCC->BDCR |=  RCC_BDCR_RTCSEL_1;
  RCC->BDCR |=  RCC_BDCR_RTCEN;

  /* Disable the write protection for RTC registers */
  RTC->WPR   = 0xCA;
  RTC->WPR   = 0x53;

  /* Configure RTC auto-wakeup mode */
  RTC->ISR  &= ~RTC_ISR_WUTF;           /* Clear wakeup timer flag            */
  RTC->CR   &= ~RTC_CR_WUCKSEL;         /* Set RTC clock to 2kHz              */
  RTC->CR   |=  RTC_CR_WUTIE;           /* Enable RTC wakeup timer interrupt  */

  /* Configure EXTI line 22 for wakeup on rising edge */
  EXTI->EMR  |= (1 << 22);              /* Event request is not masked        */
  EXTI->RTSR |= (1 << 22);              /* Rising trigger enabled             */

  NVIC_EnableIRQ (RTC_WKUP_IRQn);       /* Enable RTC WakeUp IRQ              */

  for (;;) {
    /* HERE: include optional user code to be executed when no task runs.     */
    sleep = os_suspend ();              /* OS Suspend                         */
   
    if (sleep) {
      RTC->ISR &= ~RTC_ISR_WUTF;        /* Clear timer wakeup flag            */
      RTC->CR &= ~RTC_CR_WUTE;          /* Disable wakeup timer               */
      while ((RTC->ISR & RTC_ISR_WUTWF) == 0);

      /* RTC clock is @2kHz, set wakeup time for OS_TICK >= 1ms */
      RTC->WUTR = (sleep * (OS_TICK / 1000) * 2);

      RTC->CR |=  RTC_CR_WUTE;          /* Enable wakeup timer                */
      __WFE ();                         /* Enter STOP mode                    */

      /* After Wake-up */
      if ((RTC->ISR & RTC_ISR_WUTF) == 0) {
        sleep = 0;                      /* We didn't enter Stop mode          */
      }
      
    }
     os_resume (sleep);                  /* OS Resume                          */
   
     /*
        下面的代码由用户添加。
        1、当一个中断或唤醒事件导致退出停止模式时,HSI RC振荡器被选为系统时钟。
        2、退出低功耗的停机模式后,需要重新配置使用HSE和HSE。
        3、测试发现时钟配置函数SetSysClock不能放在os_suspend和os_resume,要不会有问题。
     */
     /* Disable IRQs */
     __disable_irq();
   
     SetSysClock();
   
     /* Enable IRQs */
    __enable_irq();
  }
}
信号量的创建:
static OS_SEM semaphore;

/*
*********************************************************************************************************
*    函 数 名: AppObjCreate
*    功能说明: 创建任务通信机制
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
     /* 创建信号量计数值是0, 用于任务同步 */
     os_sem_init (&semaphore, 0);
}


使用特权

评论回复
12
追逐浪花|  楼主 | 2016-2-22 19:29 | 只看该作者
四个RTX任务的实现:
/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理     
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
     uint8_t ucKeyCode;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
        
         if (ucKeyCode != KEY_NONE)
         {
              switch (ucKeyCode)
              {
                   /* K1键按下,打印调试说明 */
                   case KEY_DOWN_K1:
                       printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
                       break;  

                   /* K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro */
                   case KEY_DOWN_K2:
                       printf("K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro\r\n");
                       os_sem_send (&semaphore);
                       break;

                   /* 其他的键值不处理 */
                   default:                    
                       break;
              }
         }
        
         os_dly_wait(20);
     }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskLED
*    功能说明: LED闪烁。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 2
*********************************************************************************************************
*/
__task void AppTaskLED(void)
{
     const uint16_t usFrequency = 200; /* 延迟周期 */
   
     /* 设置延迟周期 */
     os_itv_set(usFrequency);
   
    while(1)
    {
         bsp_LedToggle(2);
         bsp_LedToggle(3);

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
         os_itv_wait();
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskMsgPro
*    功能说明: 消息处理,等待任务AppTaskUserIF发来的信号量同步信号
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 3
*********************************************************************************************************
*/
__task void AppTaskMsgPro(void)
{
     OS_RESULT xResult;
     const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
   
    while(1)
    {
         xResult = os_sem_wait (&semaphore, usMaxBlockTime);
        
         switch (xResult)
         {
              /* 无需等待接受到信号量同步信号 */
              case OS_R_OK:
                   printf("无需等待接受到信号量同步信号\r\n");
                   break;  

              /* 信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号 */
              case OS_R_SEM:
                   printf("信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号\r\n");
                   break;

              /* 超时 */
              case OS_R_TMO:
                   bsp_LedToggle(1);
                   bsp_LedToggle(4);
                   break;
            
              /* 其他值不处理 */
              default:                    
                   break;
         }   
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskStart
*    功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 4
*********************************************************************************************************
*/
__task void AppTaskStart(void)
{
     /* 创建任务 */
     AppTaskCreate();
   
     /* 创建任务通信机制 */
     AppObjCreate();
   
    while(1)
    {
         /* 按键扫描 */
         bsp_KeyScan();
        os_dly_wait(10);
    }
}
24.5 总结
    本章节主要为大家讲解了RTX本身支持的低功耗tickless模式,对于初学者来说,刚开始学习可能不是特别理解,随着后面自己有了一定的经验后就比较容易理解了,特别是tickless的实现方法。另外tickless模式不限制用户必须采用停机模式,采用睡眠模式也是可以的,官方只是为用户提供了一种参考方法。

使用特权

评论回复
13
Brand2| | 2016-2-22 20:21 | 只看该作者
实现tickless模式最麻烦是低功耗可以执行的时间如何获取

使用特权

评论回复
14
haov000| | 2018-3-13 09:57 | 只看该作者
如果在睡眠的时候,有外部中断的话,tickless模式就不能适用了吧

使用特权

评论回复
15
zhanxiao| | 2020-6-5 10:22 | 只看该作者
如果tickless的下一个任务不知道何时才能来呢(比如需要外部中断唤醒,外部中断可能1年后才被触发呢)

使用特权

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

本版积分规则

25

主题

256

帖子

1

粉丝