硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4417|回复: 1
收起左侧

[RL-RTX] 【RTX操作系统教程】第24章    RTX低功耗之tickless模式

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-1-13 15:34:55 | 显示全部楼层 |阅读模式
第24章    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即可。

24.2 RTX实现tickless模式的框架

    RTX实现低功耗tickless模式的代码框架如下:
  1. __task void os_idle_demon (void) {
  2.   uint32_t sleep;
  3.   ...
  4.   /* 第1步:配置系统深度睡眠模式 */
  5.   ...
  6.   /* 第2步:通过函数os_suspend获取系统可以处于低功耗模式的时钟节拍个数,此函数会关闭调度器 */
  7.   for (;;) {
  8.     sleep = os_suspend ();        
  9.     if (sleep) {
  10.       ...
  11.     /* 第3步:创建一个新的定时器,专门用于将系统从低功耗模式唤醒,并将唤醒时间设置为
  12. 函数os_suspend返回值sleep */
  13.       ...
  14.     /* 第4步:调用低功耗指令进入低功耗模式 */
  15.       __WFE ();                     
  16.     /* 第5步:系统从低功耗模式唤醒,调整系统实际处于低功耗状态的时钟节拍个数*/
  17.     /* Adjust actual sleep time (in case of any event) */
  18.       sleep = ...
  19. }
  20. /* 第6步:重新使能调度器并调整系统时间,继续多任务的执行 */
  21.     os_resume (sleep);                  /* OS Resume                          */
  22.   }
  23. }
复制代码

24.3 tickless模式的API函数

     使用如下2个函数可以实现RTX的tickless模式:
u os_suspend
u os_resume
关于这两个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件
24.1.png

下面我们也对这2个函数依次进行讲解:

24.3.1 函数os_suspend

函数原型:
  1. U32 os_suspend (void);
复制代码
函数描述:
函数os_suspend用于获取系统可以处于低功耗模式的时钟节拍个数。此函数必须配套函数os_resume一起使用。调用了函数os_suspend后,此函数会关闭调度器,即关闭任务切换,调用了函数os_resume会恢复任务调度。
u 函数返回系统可以处于低功耗模式的时钟节拍个数。
使用这个函数要注意以下问题:
1.     只能在空闲任务里面调用这个函数。
2.     当系统进入到低功耗模式后,系统滴答定时器停止运行,因为调用了函数os_suspend,此函数会关闭滴答定时器。
使用举例:
    下面举的例子是RTX官方提供的基于STM32F2的低功耗设计代码,用于STM32F4也是可以,因为STM32F2和STM32F4的RTX时钟基本一样。下面的例子是通过RTC的低功耗唤醒功能实现。
  1. #include <rtl.h>
  2. __task void os_idle_demon (void) {
  3.   uint32_t sleep;
  4.   SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;    /* Configure Cortex-M3 for deep sleep */
  5.   PWR->CR &= ~PWR_CR_PDDS;              /* Enter Stop mode when in deepsleep  */
  6.   PWR->CR  |= PWR_CR_LPDS;              /* Voltage regulator in low-power     */
  7.   /* Enable LSI clock and wait until ready */
  8.   RCC->CSR |= RCC_CSR_LSION;
  9.   while ((RCC->CSR & RCC_CSR_LSIRDY) == 0);
  10.   /* Enable power interface clock */
  11.   RCC->APB1ENR |= RCC_APB1ENR_PWREN;
  12.   /* Disable backup domain write protection */
  13.   PWR->CR |= PWR_CR_DBP;
  14.   /* Select LSI as clock source for RTC and enable RTC */
  15.   RCC->BDCR &= ~RCC_BDCR_RTCSEL;
  16.   RCC->BDCR |=  RCC_BDCR_RTCSEL_1;
  17.   RCC->BDCR |=  RCC_BDCR_RTCEN;
  18.   /* Disable the write protection for RTC registers */
  19.   RTC->WPR   = 0xCA;
  20.   RTC->WPR   = 0x53;
  21.   /* Configure RTC auto-wakeup mode */
  22.   RTC->ISR  &= ~RTC_ISR_WUTF;           /* Clear wakeup timer flag            */
  23.   RTC->CR   &= ~RTC_CR_WUCKSEL;         /* Set RTC clock to 2kHz              */
  24.   RTC->CR   |=  RTC_CR_WUTIE;           /* Enable RTC wakeup timer interrupt  */
  25.   /* Configure EXTI line 22 for wakeup on rising edge */
  26.   EXTI->EMR  |= (1 << 22);              /* Event request is not masked        */
  27.   EXTI->RTSR |= (1 << 22);              /* Rising trigger enabled             */
  28.   NVIC_EnableIRQ (RTC_WKUP_IRQn);       /* Enable RTC WakeUp IRQ              */
  29.   for (;;) {
  30.     /* HERE: include optional user code to be executed when no task runs.     */
  31.     sleep = os_suspend ();              /* OS Suspend                         */
  32.     if (sleep) {
  33.       RTC->ISR &= ~RTC_ISR_WUTF;        /* Clear timer wakeup flag            */
  34.       RTC->CR &= ~RTC_CR_WUTE;          /* Disable wakeup timer               */
  35.       while ((RTC->ISR & RTC_ISR_WUTWF) == 0);
  36.       /* RTC clock is @2kHz, set wakeup time for OS_TICK >= 1ms */
  37.       RTC->WUTR = (sleep * (OS_TICK / 1000) * 2);
  38.       RTC->CR |=  RTC_CR_WUTE;          /* Enable wakeup timer                */
  39.       __WFE ();                         /* Enter STOP mode                    */
  40.       /* After Wake-up */
  41.       if ((RTC->ISR & RTC_ISR_WUTF) == 0) {
  42.         sleep = 0;                      /* We didn't enter Stop mode          */
  43.       }
  44.     }
  45.     os_resume (sleep);                  /* OS Resume                          */
  46.   }
  47. }
复制代码

24.3.2 函数os_resume

函数原型:
  1. void os_resume (
  2.     U32 sleep_time );    /* 系统处于低功耗模式的时钟节拍个数 */
复制代码
函数描述:
函数os_resume用于恢复任务调度器的运行。此函数必须配套函数os_suspend一起使用。调用了函数os_suspend后,此函数会关闭调度器,即关闭任务切换,调用函数os_resume会恢复任务调度。
u 参数填写系统处于低功耗模式的时钟节拍个数。
使用这个函数要注意以下问题:
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.测低功耗的时候,一定不要连接调试器,更不能边调试边测电流。
RTX配置:
RTX配置向导详情如下:
24.2.png

u  Task Configuration
l Number of concurrent running tasks
    允许创建4个任务,实际创建了如下四个任务:
                     AppTaskUserIF任务   :按键消息处理。
                     AppTaskLED任务     :LED闪烁。
                    AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                    AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number of tasks with user-provided stack
    创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
    在休眠模式下,无法动态的查看任务的调试信息。下面的是单步调试时状态查看:
24.3.png

程序设计:
u  任务栈大小分配:
    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类型数据运算会出问题。
u  系统栈大小分配:
24.4.png

u  外设初始化:
注意新加的函数初始化函数DBGMCU_Config(DBGMCU_STOP, ENABLE);保证停机模式下调试器正常连接使用。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  5. *             全局变量。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_Init(void)
  11. {
  12.      /* 保证停机模式下调试器继续可以连接使用 */
  13.      DBGMCU_Config(DBGMCU_STOP, ENABLE);
  14.    
  15.      /* 优先级分组设置为4, 优先配置好NVIC */
  16.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  17.      bsp_InitUart();    /* 初始化串口 */
  18.      bsp_InitLed();     /* 初始LED指示灯端口 */
  19.      bsp_InitKey();     /* 初始化按键 */
  20. }
复制代码
u  RTX初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main (void)
  10. {   
  11.      /* 初始化外设 */
  12.      bsp_Init();
  13.    
  14.      /* 创建启动任务 */
  15.      os_sys_init_user (AppTaskStart,             /* 任务函数 */
  16.                        4,                        /* 任务优先级 */
  17.                        &AppTaskStartStk,         /* 任务栈 */
  18.                        sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
  19.      while(1);
  20. }
复制代码
u  RTX任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.      HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
  12.                                            1,                         /* 任务优先级 */
  13.                                            &AppTaskUserIFStk,         /* 任务栈 */
  14.                                            sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
  15.    
  16.      HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */
  17.                                         2,                       /* 任务优先级 */
  18.                                         &AppTaskLEDStk,          /* 任务栈 */
  19.                                         sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */
  20.    
  21.      HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
  22.                                            3,                         /* 任务优先级 */
  23.                                            &AppTaskMsgProStk,         /* 任务栈 */
  24.                                            sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
  25. }
复制代码
u tickless模式在空闲任务实现,即配置向导文件RTX_Conf_CM.c文件中
  1. /*--------------------------- os_idle_demon ---------------------------------*/
  2. #include "stm32f10x.h"
  3. __task void os_idle_demon (void)
  4. {
  5.      EXTI_InitTypeDef  EXTI_InitStructure;
  6.      NVIC_InitTypeDef NVIC_InitStructure;
  7.      uint32_t sleep;
  8.    
  9.      /* 使能PWR和BKP时钟 */
  10.      RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
  11.      /* 允许写入RTC和后备寄存器 */
  12.      PWR_BackupAccessCmd(ENABLE);
  13.      /* 复位后备寄存器 */
  14.      BKP_DeInit();
  15.      /* 使能LSI */
  16.      RCC_LSICmd(ENABLE);
  17.      /* 等待直到LSI就绪 */
  18.      while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
  19.      /* 选择LSI作为RTC的时钟 */
  20.      RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
  21.      /* 使能RTC时钟 */
  22.      RCC_RTCCLKCmd(ENABLE);
  23.      /*
  24.         在APB1总线复位或者停止后重新开启,RTC的任何读取前得等待RTC寄存器
  25.         (RTC_CNT, RTC_ALR and RTC_PRL)跟RTC APB时钟同步。
  26.      */
  27.      RTC_WaitForSynchro();
  28.      /* 等待RTC寄存器写操作完成 */
  29.      RTC_WaitForLastTask();
  30.      /*
  31.        1. LSI的频率典型值是40KHz(30KHz到60KHz)
  32.        2. 这里按40KHz来计算
  33.           RTC 周期 = RTCCLK / RTC_PR = (40 KHz)/(19 + 1) = 2KHz
  34.      */
  35.      RTC_SetPrescaler(19);
  36.      /* 等待RTC寄存器写操作完成 */
  37.      RTC_WaitForLastTask();
  38.      /* EXTI线17连接到RTC闹钟事件,使能中断 */
  39.      EXTI_ClearITPendingBit(EXTI_Line17);
  40.      EXTI_InitStructure.EXTI_Line = EXTI_Line17;
  41.      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  42.      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  43.      EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  44.      EXTI_Init(&EXTI_InitStructure);
  45.      /* 设置闹钟中断的NVIC */
  46.      NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;
  47.      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
  48.      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  49.      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  50.      NVIC_Init(&NVIC_InitStructure);
  51.      for (;;)
  52.      {
  53.          /* 挂起OS, 并返回可以执行低功耗的时长 */
  54.          sleep = os_suspend ();
  55.          
  56.          if (sleep)
  57.          {
  58.               /* RTC计数设置 */
  59.               RTC_SetCounter(0);
  60.               RTC_WaitForLastTask();
  61.               /* 设置闹钟时间 */
  62.               RTC_SetAlarm(sleep*(OS_TICK/1000)*2);
  63.               RTC_WaitForLastTask();
  64.             
  65.               /* 使能闹钟中断 */
  66.               RTC_ITConfig(RTC_IT_ALR, ENABLE);
  67.               RTC_WaitForLastTask();
  68.             
  69.               /* 进入停机模式 */
  70.               PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFE);
  71.             
  72.               /*
  73.                 1、当一个中断或唤醒事件导致退出停止模式时,HSI RC振荡器被选为系统时钟。
  74.                 2、退出低功耗的停机模式后,需要重新配置使用HSE和HSE
  75.               */
  76.               SystemInit();
  77.             
  78.               /*
  79.                  在APB1总线复位或者停止后重新开启,RTC的任何读取前得等待RTC寄存器
  80.                  (RTC_CNT, RTC_ALR and RTC_PRL)跟RTC APB时钟同步。
  81.               */
  82.               RTC_WaitForSynchro();
  83.             
  84.               /* 检查唤醒标志是否设置 */
  85.               if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET)
  86.               {
  87.                    /* 用户可以在这里加入相关串口打印等函数来检测是否进入停机模式 */
  88.                    //printf("lowpower\r\n");
  89.                    /* 清除唤醒标志 */
  90.                    PWR_ClearFlag(PWR_FLAG_WU);
  91.               }
  92.               else
  93.               {
  94.                    sleep = 0;
  95.               }
  96.             
  97.          }
  98.         
  99.          /* 恢复OS */
  100.          os_resume (sleep);           
  101.      }
  102. }
  103. /*--------------------------- RTC闹钟中断 ----------------------------------*/
  104. void RTCAlarm_IRQHandler(void)
  105. {
  106.      if (RTC_GetITStatus(RTC_IT_ALR) != RESET)
  107.      {
  108.          /* 禁止RTC的闹钟中断 */
  109.          RTC_ITConfig(RTC_IT_ALR, DISABLE);
  110.          RTC_WaitForLastTask();
  111.         
  112.          /* 清除中断标志 */
  113.          EXTI_ClearITPendingBit(EXTI_Line17);
  114.         
  115.          /* 清除中断标志 */
  116.          RTC_ClearITPendingBit(RTC_IT_ALR);
  117.          RTC_WaitForLastTask();
  118.      }   
  119. }
复制代码
u  信号量的创建:
  1. static OS_SEM semaphore;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: AppObjCreate
  5. *    功能说明: 创建任务通信机制
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. static void AppObjCreate (void)
  11. {
  12.      /* 创建信号量计数值是0, 用于任务同步 */
  13.      os_sem_init (&semaphore, 0);
  14. }
复制代码
u  四个RTX任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskUserIF
  4. *    功能说明: 按键消息处理     
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskUserIF(void)
  11. {
  12.      uint8_t ucKeyCode;
  13.     while(1)
  14.     {
  15.          ucKeyCode = bsp_GetKey();
  16.         
  17.          if (ucKeyCode != KEY_NONE)
  18.          {
  19.               switch (ucKeyCode)
  20.               {
  21.                    /* K1键按下,打印调试说明 */
  22.                    case KEY_DOWN_K1:
  23.                        printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
  24.                        break;  
  25.                    /* K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro */
  26.                    case KEY_DOWN_K2:
  27.                        printf("K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro\r\n");
  28.                        os_sem_send (&semaphore);
  29.                        break;
  30.                    /* 其他的键值不处理 */
  31.                    default:                    
  32.                        break;
  33.               }
  34.          }
  35.         
  36.          os_dly_wait(20);
  37.      }
  38. }
  39. /*
  40. *********************************************************************************************************
  41. *    函 数 名: AppTaskLED
  42. *    功能说明: LED闪烁。
  43. *    形    参: 无
  44. *    返 回 值: 无
  45. *   优 先 级: 2
  46. *********************************************************************************************************
  47. */
  48. __task void AppTaskLED(void)
  49. {
  50.      const uint16_t usFrequency = 200; /* 延迟周期 */
  51.    
  52.      /* 设置延迟周期 */
  53.      os_itv_set(usFrequency);
  54.    
  55.     while(1)
  56.     {
  57.          bsp_LedToggle(2);
  58.          bsp_LedToggle(3);
  59.          /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
  60.          os_itv_wait();
  61.     }
  62. }
  63. /*
  64. *********************************************************************************************************
  65. *    函 数 名: AppTaskMsgPro
  66. *    功能说明: 消息处理,等待任务AppTaskUserIF发来的信号量同步信号
  67. *    形    参: 无
  68. *    返 回 值: 无
  69. *   优 先 级: 3
  70. *********************************************************************************************************
  71. */
  72. __task void AppTaskMsgPro(void)
  73. {
  74.      OS_RESULT xResult;
  75.      const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
  76.    
  77.     while(1)
  78.     {
  79.          xResult = os_sem_wait (&semaphore, usMaxBlockTime);
  80.         
  81.          switch (xResult)
  82.          {
  83.               /* 无需等待接受到信号量同步信号 */
  84.               case OS_R_OK:
  85.                    printf("无需等待接受到信号量同步信号\r\n");
  86.                    break;  
  87.               /* 信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号 */
  88.               case OS_R_SEM:
  89.                    printf("信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号\r\n");
  90.                    break;
  91.               /* 超时 */
  92.               case OS_R_TMO:
  93.                    bsp_LedToggle(1);
  94.                    bsp_LedToggle(4);
  95.                    break;
  96.             
  97.               /* 其他值不处理 */
  98.               default:                    
  99.                    break;
  100.          }   
  101.     }
  102. }
  103. /*
  104. *********************************************************************************************************
  105. *    函 数 名: AppTaskStart
  106. *    功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
  107. *    形    参: 无
  108. *    返 回 值: 无
  109. *   优 先 级: 4
  110. *********************************************************************************************************
  111. */
  112. __task void AppTaskStart(void)
  113. {
  114.      /* 创建任务 */
  115.      AppTaskCreate();
  116.    
  117.      /* 创建任务通信机制 */
  118.      AppObjCreate();
  119.    
  120.     while(1)
  121.     {
  122.          /* 按键扫描 */
  123.          bsp_KeyScan();
  124.         os_dly_wait(10);
  125.     }
  126. }
复制代码

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配置向导详情如下:
24.5.png

u  Task Configuration
l Number of concurrent running tasks
     允许创建4个任务,实际创建了如下四个任务:
                    AppTaskUserIF任务   :按键消息处理。
                     AppTaskLED任务     :LED闪烁。
                      AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                    AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number of tasks with user-provided stack
     创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
24.6.png

程序设计:
u  任务栈大小分配:
    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类型数据运算会出问题。
u  系统栈大小分配:
24.7.png

u  外设初始化:
    注意新加的函数初始化函数DBGMCU_Config(DBGMCU_STOP, ENABLE);保证停机模式下调试器正常连接使用。
  1. /*
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: bsp_Init
  5. *    功能说明: 初始化硬件设备。只需要调用一次。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。
  6. *             全局变量。
  7. *    形    参: 无
  8. *    返 回 值: 无
  9. *********************************************************************************************************
  10. */
  11. void bsp_Init(void)
  12. {
  13.      /* 保证停机模式下调试器继续可以连接使用 */
  14.      DBGMCU_Config(DBGMCU_STOP, ENABLE);
  15.    
  16.      /* 优先级分组设置为4, 优先配置好NVIC */
  17.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  18.    
  19.      bsp_InitUart();    /* 初始化串口 */
  20.      bsp_InitKey();     /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
  21.      bsp_InitLed();     /* 初始LED指示灯端口 */
  22. }
复制代码
u  RTX初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main (void)
  10. {   
  11.      /* 初始化外设 */
  12.      bsp_Init();
  13.    
  14.      /* 创建启动任务 */
  15.      os_sys_init_user (AppTaskStart,             /* 任务函数 */
  16.                        4,                        /* 任务优先级 */
  17.                        &AppTaskStartStk,         /* 任务栈 */
  18.                        sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
  19.      while(1);
  20. }
复制代码
u  RTX任务创建:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskCreate
  4. *    功能说明: 创建应用任务
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void AppTaskCreate (void)
  10. {
  11.      HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
  12.                                            1,                         /* 任务优先级 */
  13.                                            &AppTaskUserIFStk,         /* 任务栈 */
  14.                                            sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
  15.    
  16.      HandleTaskLED = os_tsk_create_user(AppTaskLED,              /* 任务函数 */
  17.                                         2,                       /* 任务优先级 */
  18.                                         &AppTaskLEDStk,          /* 任务栈 */
  19.                                         sizeof(AppTaskLEDStk));  /* 任务栈大小,单位字节数 */
  20.    
  21.      HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
  22.                                            3,                         /* 任务优先级 */
  23.                                            &AppTaskMsgProStk,         /* 任务栈 */
  24.                                            sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
  25. }
复制代码
u tickless模式在空闲任务实现,即配置向导文件RTX_Conf_CM.c文件中
  1. /*----------------------------------------------------------------------------
  2. *      Global Functions
  3. *---------------------------------------------------------------------------*/
  4. #include "stm32f4xx.h"
  5. extern void SetSysClock(void);
  6. void RTC_WKUP_IRQHandler (void) {
  7.   EXTI->PR = (1 << 22);                 /* Clear pending EXTI interrupt      */
  8.   RTC->CR &= ~RTC_CR_WUTE;              /* Disable wakeup timer              */
  9. }
  10. /*--------------------------- os_idle_demon ---------------------------------*/
  11. __task void os_idle_demon (void) {
  12. //  /* The idle demon is a system task, running when no other task is ready */
  13. //  /* to run. The 'os_xxx' function calls are not allowed from this task.  */
  14.   uint32_t sleep;
  15.   SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;    /* Configure Cortex-M3 for deep sleep */
  16.   PWR->CR &= ~PWR_CR_PDDS;              /* Enter Stop mode when in deepsleep  */
  17.   PWR->CR  |= PWR_CR_LPDS;              /* Voltage regulator in low-power     */
  18.   /* Enable LSI clock and wait until ready */
  19.   RCC->CSR |= RCC_CSR_LSION;
  20.   while ((RCC->CSR & RCC_CSR_LSIRDY) == 0);
  21.   /* Enable power interface clock */
  22.   RCC->APB1ENR |= RCC_APB1ENR_PWREN;
  23.   /* Disable backup domain write protection */
  24.   PWR->CR |= PWR_CR_DBP;
  25.   /* Select LSI as clock source for RTC and enable RTC */
  26.   RCC->BDCR &= ~RCC_BDCR_RTCSEL;
  27.   RCC->BDCR |=  RCC_BDCR_RTCSEL_1;
  28.   RCC->BDCR |=  RCC_BDCR_RTCEN;
  29.   /* Disable the write protection for RTC registers */
  30.   RTC->WPR   = 0xCA;
  31.   RTC->WPR   = 0x53;
  32.   /* Configure RTC auto-wakeup mode */
  33.   RTC->ISR  &= ~RTC_ISR_WUTF;           /* Clear wakeup timer flag            */
  34.   RTC->CR   &= ~RTC_CR_WUCKSEL;         /* Set RTC clock to 2kHz              */
  35.   RTC->CR   |=  RTC_CR_WUTIE;           /* Enable RTC wakeup timer interrupt  */
  36.   /* Configure EXTI line 22 for wakeup on rising edge */
  37.   EXTI->EMR  |= (1 << 22);              /* Event request is not masked        */
  38.   EXTI->RTSR |= (1 << 22);              /* Rising trigger enabled             */
  39.   NVIC_EnableIRQ (RTC_WKUP_IRQn);       /* Enable RTC WakeUp IRQ              */
  40.   for (;;) {
  41.     /* HERE: include optional user code to be executed when no task runs.     */
  42.     sleep = os_suspend ();              /* OS Suspend                         */
  43.    
  44.     if (sleep) {
  45.       RTC->ISR &= ~RTC_ISR_WUTF;        /* Clear timer wakeup flag            */
  46.       RTC->CR &= ~RTC_CR_WUTE;          /* Disable wakeup timer               */
  47.       while ((RTC->ISR & RTC_ISR_WUTWF) == 0);
  48.       /* RTC clock is @2kHz, set wakeup time for OS_TICK >= 1ms */
  49.       RTC->WUTR = (sleep * (OS_TICK / 1000) * 2);
  50.       RTC->CR |=  RTC_CR_WUTE;          /* Enable wakeup timer                */
  51.       __WFE ();                         /* Enter STOP mode                    */
  52.       /* After Wake-up */
  53.       if ((RTC->ISR & RTC_ISR_WUTF) == 0) {
  54.         sleep = 0;                      /* We didn't enter Stop mode          */
  55.       }
  56.       
  57.     }
  58.      os_resume (sleep);                  /* OS Resume                          */
  59.    
  60.      /*
  61.         下面的代码由用户添加。
  62.         1、当一个中断或唤醒事件导致退出停止模式时,HSI RC振荡器被选为系统时钟。
  63.         2、退出低功耗的停机模式后,需要重新配置使用HSE和HSE。
  64.         3、测试发现时钟配置函数SetSysClock不能放在os_suspend和os_resume,要不会有问题。
  65.      */
  66.      /* Disable IRQs */
  67.      __disable_irq();
  68.    
  69.      SetSysClock();
  70.    
  71.      /* Enable IRQs */
  72.     __enable_irq();
  73.   }
  74. }
复制代码
u  信号量的创建:
  1. static OS_SEM semaphore;
  2. /*
  3. *********************************************************************************************************
  4. *    函 数 名: AppObjCreate
  5. *    功能说明: 创建任务通信机制
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. static void AppObjCreate (void)
  11. {
  12.      /* 创建信号量计数值是0, 用于任务同步 */
  13.      os_sem_init (&semaphore, 0);
  14. }
复制代码
u  四个RTX任务的实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: AppTaskUserIF
  4. *    功能说明: 按键消息处理     
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. __task void AppTaskUserIF(void)
  11. {
  12.      uint8_t ucKeyCode;
  13.     while(1)
  14.     {
  15.          ucKeyCode = bsp_GetKey();
  16.         
  17.          if (ucKeyCode != KEY_NONE)
  18.          {
  19.               switch (ucKeyCode)
  20.               {
  21.                    /* K1键按下,打印调试说明 */
  22.                    case KEY_DOWN_K1:
  23.                        printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
  24.                        break;  
  25.                    /* K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro */
  26.                    case KEY_DOWN_K2:
  27.                        printf("K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro\r\n");
  28.                        os_sem_send (&semaphore);
  29.                        break;
  30.                    /* 其他的键值不处理 */
  31.                    default:                    
  32.                        break;
  33.               }
  34.          }
  35.         
  36.          os_dly_wait(20);
  37.      }
  38. }
  39. /*
  40. *********************************************************************************************************
  41. *    函 数 名: AppTaskLED
  42. *    功能说明: LED闪烁。
  43. *    形    参: 无
  44. *    返 回 值: 无
  45. *   优 先 级: 2
  46. *********************************************************************************************************
  47. */
  48. __task void AppTaskLED(void)
  49. {
  50.      const uint16_t usFrequency = 200; /* 延迟周期 */
  51.    
  52.      /* 设置延迟周期 */
  53.      os_itv_set(usFrequency);
  54.    
  55.     while(1)
  56.     {
  57.          bsp_LedToggle(2);
  58.          bsp_LedToggle(3);
  59.          /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
  60.          os_itv_wait();
  61.     }
  62. }
  63. /*
  64. *********************************************************************************************************
  65. *    函 数 名: AppTaskMsgPro
  66. *    功能说明: 消息处理,等待任务AppTaskUserIF发来的信号量同步信号
  67. *    形    参: 无
  68. *    返 回 值: 无
  69. *   优 先 级: 3
  70. *********************************************************************************************************
  71. */
  72. __task void AppTaskMsgPro(void)
  73. {
  74.      OS_RESULT xResult;
  75.      const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
  76.    
  77.     while(1)
  78.     {
  79.          xResult = os_sem_wait (&semaphore, usMaxBlockTime);
  80.         
  81.          switch (xResult)
  82.          {
  83.               /* 无需等待接受到信号量同步信号 */
  84.               case OS_R_OK:
  85.                    printf("无需等待接受到信号量同步信号\r\n");
  86.                    break;  
  87.               /* 信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号 */
  88.               case OS_R_SEM:
  89.                    printf("信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号\r\n");
  90.                    break;
  91.               /* 超时 */
  92.               case OS_R_TMO:
  93.                    bsp_LedToggle(1);
  94.                    bsp_LedToggle(4);
  95.                    break;
  96.             
  97.               /* 其他值不处理 */
  98.               default:                    
  99.                    break;
  100.          }   
  101.     }
  102. }
  103. /*
  104. *********************************************************************************************************
  105. *    函 数 名: AppTaskStart
  106. *    功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
  107. *    形    参: 无
  108. *    返 回 值: 无
  109. *   优 先 级: 4
  110. *********************************************************************************************************
  111. */
  112. __task void AppTaskStart(void)
  113. {
  114.      /* 创建任务 */
  115.      AppTaskCreate();
  116.    
  117.      /* 创建任务通信机制 */
  118.      AppObjCreate();
  119.    
  120.     while(1)
  121.     {
  122.          /* 按键扫描 */
  123.          bsp_KeyScan();
  124.         os_dly_wait(10);
  125.     }
  126. }
复制代码

24.5 总结
    本章节主要为大家讲解了RTX本身支持的低功耗tickless模式,对于初学者来说,刚开始学习可能不是特别理解,随着后面自己有了一定的经验后就比较容易理解了,特别是tickless的实现方法。另外tickless模式不限制用户必须采用停机模式,采用睡眠模式也是可以的,官方只是为用户提供了一种参考方法。

评分

参与人数 1金币 +1 收起 理由
joshuazchen + 1 赞一个!

查看全部评分

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

19

主题

150

回帖

207

积分

高级会员

积分
207
发表于 2019-9-24 10:46:38 | 显示全部楼层
MARK.就是此处了。赞。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-3 15:15 , Processed in 0.300925 second(s), 29 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表