完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
转rtx操作系统
本章节为大家讲解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模式的代码框架如下: 复制代码 __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模式: u os_suspend u os_resume 关于这两个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件 下面我们也对这2个函数依次进行讲解: |
|
|
|
|
|
24.3.1 函数os_suspend
函数原型: 复制代码 U32 os_suspend (void); 函数描述: 函数os_suspend用于获取系统可以处于低功耗模式的时钟节拍个数。此函数必须配套函数os_resume一起使用。调用了函数os_suspend后,此函数会关闭调度器,即关闭任务切换,调用了函数os_resume会恢复任务调度。 u 函数返回系统可以处于低功耗模式的时钟节拍个数。 使用这个函数要注意以下问题: 1. 只能在空闲任务里面调用这个函数。 2. 当系统进入到低功耗模式后,系统滴答定时器停止运行,因为调用了函数os_suspend,此函数会关闭滴答定时器。 使用举例: 下面举的例子是RTX官方提供的基于STM32F2的低功耗设计代码,用于STM32F4也是可以,因为STM32F2和STM32F4的RTX时钟基本一样。下面的例子是通过RTC的低功耗唤醒功能实现。 复制代码 #include __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 */ } } |
|
|
|
|
|
24.3.2 函数os_resume
函数原型: 复制代码 void os_resume ( 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配置向导详情如下: 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个任务都是采用自定义堆栈方式。 |
|
|
|
|
|
程序设计:
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 系统栈大小分配: |
|
|
|
|
|
外设初始化:
注意新加的函数初始化函数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)); /* 任务栈大小,单位字节数 */ } |
|
|
|
|
|
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("lowpowerrn"); /* 清除唤醒标志 */ 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); } |
|
|
|
|
|
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.测低功耗的时候,一定不要连接调试器,更不能边调试边测电流。 |
|
|
|
|
|
《DNESP32S3使用指南-IDF版_V1.6》第二章 常用的C语言知识点
377 浏览 0 评论
【RA-Eco-RA2E1-48PIN-V1.0开发板试用】(第三篇)ADC采集+PWM输出
536 浏览 0 评论
《DNK210使用指南 -CanMV版 V1.0》第四十五章 人脸识别实验
540 浏览 0 评论
950 浏览 0 评论
如何用OpenCV的相机捕捉视频进行人脸检测--基于米尔NXP i.MX93开发板
1403 浏览 0 评论
【youyeetoo X1 windows 开发板体验】少儿AI智能STEAM积木平台
11760 浏览 31 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 06:24 , Processed in 0.751882 second(s), Total 72, Slave 63 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号