初级会员
- 积分
- 123
- 金钱
- 123
- 注册时间
- 2016-9-5
- 在线时间
- 21 小时
|
楼主 |
发表于 2017-5-31 13:39:10
|
显示全部楼层
void vPortMySuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
u32 t0=0;
u32 t1=0;
u32 tt=0;
eSleepModeStatus eReturn ;
printf("\r\n daozheli xExpectedIdleTime = %d *",xExpectedIdleTime);
RTC_WaitForSynchro();
t0=RTC_GetCounter();//读取 当前的RTC计时值 没49天一复位 RTC
if(xExpectedIdleTime>MAX_ALARM_TIME)//限制最大为 定时器RTC单一循环所能表示的时间
{
xExpectedIdleTime=MAX_ALARM_TIME;
}
printf("\r\n shiji wei= xExpectedIdleTime = %d *",xExpectedIdleTime);
portENTER_CRITICAL();
eReturn = eTaskConfirmSleepModeStatus();
portEXIT_CRITICAL();
//需要调用系统的函数去 判断是否需要进入低功耗 休眠
if( eReturn == eAbortSleep )//不用休眠 就直接返回即可
{
printf("\r\n eReturn == eAbortSleep hu lue xiu mian *\r\n");
return ;
}
else //标准休眠 和 没有任务会等待超时(都在无线等待的状态 是需要配置外部中断唤醒源的 否则会休眠到定时器最大值时唤醒)
{
if(eNoTasksWaitingTimeout==eReturn)//这里 所有任务都无限等待 那就休眠一辈子 ,前提是 配置外部中断的线程要把配置唤醒中断的代码执行了 这样才会
{//可能产生所有任务都无限等待的情况 等待 唤醒中断发送的 系统对象
//直接进入休眠即可
printf("\r\n zhijie xiumian stop *\r\n");
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; /* 关闭滴答定时器 */
//EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位
RTC_ITConfig(RTC_IT_ALR, DISABLE);//禁止闹钟中断 直到需要时
RTC_ClearITPendingBit(RTC_IT_OW|RTC_IT_ALR|RTC_IT_SEC);
//这里如果中断导致 任务切换 ,这里不会被其他任务抢占 因为 这个函数执行前 系统调度器已经被挂起 此函数执行完了 才会切换
//进入 STOPMode 低功耗模式
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFE); //如果这里有中断标志 会直接跳过这天指令
//__set_PRIMASK(1); //关闭中断 一直到最后 唤醒CPU的中断还是会先执行的 然后才执行关闭中断
//portENTER_CRITICAL();
RCC_HSEConfig(RCC_HSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET){}
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08){}
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 使能滴答定时器 不能再没有更新系统滴答延时就*/
//portEXIT_CRITICAL();
delay_ms_tickless_shiyong(2);
printf("\r\n vPortMySuppressTicksAndSleep *\r\n");
//EXTI_ClearITPendingBit(EXTI_Line0);
// __set_PRIMASK(0); //打开中断 测试下
}
else
{
printf("\r\n xiu mian need dalay *\r\n");
//没有单独执行 全部都是 无限等待 的分支 是没有影响的,后果就是 会最大时间后唤醒一次,如果执行了这个分支 那么可实现 连续 一辈子休眠 除非有外部中断唤醒
//执行STOP休眠
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; /* 关闭滴答定时器 */
//这里提前设置好 闹钟定时 设置唤醒源
RTC_EntryAlarmMode_StopModeLowPower(xExpectedIdleTime);//休眠时间 毫秒MS为单位
//EXTI_ClearITPendingBit(EXTI_Line0); //清除中断标志位
//RTC_ClearITPendingBit(RTC_IT_OW|RTC_IT_ALR|RTC_IT_SEC);
//进入 STOPMode 低功耗模式
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFE); //如果这里有中断标志 会直接跳过这天指令
portENTER_CRITICAL();
//但是如果有中断标志 之前就已经执行了 所以出现此种情况的机会比较小,处理了中断 才会执行到这里 所以之前都不用 关闭总中断
//之后可以选择关闭中断 测试下
//__set_PRIMASK(1); //关闭中断 一直到最后 唤醒CPU的中断还是会先执行的 然后才执行关闭中断
//portENTER_CRITICAL();
//这里是采用的 将系统管理的 中断都给禁止了 是采用的硬件支持的 中断屏蔽寄存器的方式 如果在进入低功耗前就执行 会把闹钟中断也禁止了 就无法唤醒 所以只能在这里执行
/*
1、当一个中断或唤醒事件导致退出停止模式时, HSI RC振荡器被选为系统时钟。
2、退出低功耗的停机模式后,需要重新配置使用HSE。
*/
RCC_HSEConfig(RCC_HSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET){}
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08){}
/* 唤醒后 会立即执行 闹钟中断 ,然后才会从 唤醒处执行 所以需要先同步 rtc,然后才能读取 rtc值
第一: 闹钟唤醒,也是需要 得到休眠时间,然后修正 这里也可以执行一次 关闭RTC中断的代码
第二: 被其他外部中断唤醒,需要先关闭闹钟中断,读取休眠失眠 然后修正即可
所以RTC的中断代码只是防止系统跑飞,同时关闭定时器
所以总的执行流程是:
中断:只执行关闭的代码中断和清除中断标志的代码
空闲代码中: 读取当前时间,设置闹钟关闭滴答,执行休眠,......被唤醒(执行了中断代码), 进入临界段, 打开MCU外部高速时钟HSE 等待稳定可用,等待同步RTC完成,关闭RTC中断,清除RTC中断标志,
读取RTC值,计算出休眠时间(RTC CNT循环计数),修正时间,打开滴答,退出临界段
*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_ITConfig(RTC_IT_ALR, DISABLE);//禁止闹钟中断 直到需要时
RTC_WaitForLastTask();
RTC_ClearITPendingBit(RTC_IT_OW|RTC_IT_ALR|RTC_IT_SEC);
RTC_WaitForLastTask();
//修正 系统滴答的代码
RTC_WaitForSynchro();
t1=RTC_GetCounter();//读取 唤醒后的 RTC 及时时间点
//计算出睡眠的了多长时间 要修正到系统个任务控制块的 结构体元素中
if(t1>=t0)
{
tt=t1-t0;
}
else
{
tt=MAX_ALARM_TIME-t0 + t1;
}
if(tt>=xExpectedIdleTime)
{
tt=xExpectedIdleTime-1;//防止 刚刚是休眠的时间值 不知道内部是如何处理的 所以规避一下 最后一个时钟不休眠 实际是加一个时钟
}
printf("\r\n daozheli 实际休眠值为 tt = %d \r\n",tt);
//执行修正 这里临界执行
//portENTER_CRITICAL();
vTaskStepTick( tt );//这里面有 陷阱指令 保证 递进修正的时间只能小于 传进来需要休眠的时间 所以需要对 tt做修正 如果相等就减去 1 即可
portEXIT_CRITICAL();
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 使能滴答定时器 不能再没有更新系统滴答延时就*/
//printf("\r\n portEXIT_CRITICAL ");
//__set_PRIMASK(0); //打开中断 测试下
}
}
} |
|