OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5282|回复: 5

FREEROS tickless 低功耗 求助

[复制链接]

13

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
82
金钱
82
注册时间
2016-9-5
在线时间
15 小时
发表于 2017-5-30 19:22:56 | 显示全部楼层 |阅读模式
10金钱
需求描述:
    我采用的是STM32F071这个芯片,采用FREERTOS,使用的电池供电,所以要求低功耗,我采用的TICKLESS官方推荐的方式,配置  #define configUSE_TICKLESS_IDLE          1  ,然后编写函数 vPortMySuppressTicksAndSleep  在这里 进入STOP模式,采用RTC的闹钟中断唤醒,和 外部中断唤醒;   但是 经过测试,在外部中断中发送任务信号量(不是xSemaphoreTake创建的 而是任务自带的内部信号量),可以唤醒对应的任务,   然后测试 xSemaphoreTake 创建的独立信号量的方式,测试代码为 创建一个信号量,然后一个线程设置5秒等待这个信号量,然后输出调试信息,但是一旦唤醒,系统就死机了 然后不再进入SYSTICK中断了,,,,调试了 快一个月了,,,实在没办法了,,论坛里 希望有使用过 这种 低功耗方式的,可以指导一下,非常感谢!

最佳答案

查看完整内容[请看2#楼]

自己解决了 采用官方 CUBE生成的 HAL库的方式使用的,估计是我采用标准库 移植 或者系统移植移植的 哪个地方出了问题,采用官方生成的代码就OK了
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

13

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
82
金钱
82
注册时间
2016-9-5
在线时间
15 小时
 楼主| 发表于 2017-5-30 19:22:57 | 显示全部楼层
自己解决了  采用官方 CUBE生成的 HAL库的方式使用的,估计是我采用标准库 移植 或者系统移植移植的 哪个地方出了问题,采用官方生成的代码就OK了
回复

使用道具 举报

27

主题

713

帖子

0

精华

版主

Rank: 7Rank: 7Rank: 7

积分
11221
金钱
11221
注册时间
2015-11-5
在线时间
2020 小时
发表于 2017-5-31 11:27:47 | 显示全部楼层
你写的有点乱。。。TICKLESS机制在中断唤醒后不是立刻进入你的中断
在进入低功耗前已经把中断关闭了,中断唤醒后执行了一些必要的操作后才开启中断,这时系统开始正常运作
至于你说的不再进入SYSTICK中断,是不是你写的TICKLESS代码有问题呢?还是说API调用有问题?
拿来长岛冰茶换我半晚安睡
回复

使用道具 举报

13

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
82
金钱
82
注册时间
2016-9-5
在线时间
15 小时
 楼主| 发表于 2017-5-31 13:39:10 | 显示全部楼层
FreeRTOS 发表于 2017-5-31 11:27
你写的有点乱。。。TICKLESS机制在中断唤醒后不是立刻进入你的中断
在进入低功耗前已经把中断关闭了,中断 ...

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); //打开中断  测试下
                }
        }
}
回复

使用道具 举报

13

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
82
金钱
82
注册时间
2016-9-5
在线时间
15 小时
 楼主| 发表于 2017-5-31 13:40:06 | 显示全部楼层
FreeRTOS 发表于 2017-5-31 11:27
你写的有点乱。。。TICKLESS机制在中断唤醒后不是立刻进入你的中断
在进入低功耗前已经把中断关闭了,中断 ...

这是 我在 F103上移植实现的代码  经过测试  独立的 消息队列和信号量 ,在中断函数中发送都是没有问题的,任务内部信号量也是可以的;  就是 在F072上有问题
回复

使用道具 举报

16

主题

232

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1392
金钱
1392
注册时间
2017-3-1
在线时间
257 小时
发表于 2019-4-3 18:16:29 | 显示全部楼层
能否问下,我也是使用Cube。首先使能了configUSE_TICKLESS_IDLE,然后编写PreSleepProcessing(关灯)和PostSleepProcessing(开灯),另外开的两个任务都是切换任务的延时2s以上,但是下载后看不到任何开灯和关灯现象,仿真时也进不了编写的两个函数,可能会是什么问题?
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-3-28 21:17

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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