TMOS实现长延时,并且不影响蓝牙功能

问题:

    在使用CH58X开发BLE功能时,遇到一个问题,就是如果使用了稍微长一点的延时或者处理耗时的操作时。BLE会断开连接。


原因:

    CH58X的蓝牙功能是基于TMOS轮询的方式去处理各个事件的。因为是单线程,所以如果在代码里使用了延时就会导致蓝牙的事件没能得到及时处理,就会导致断开连接。


解决思路:

    在一个低优先级中断中去执行TMOS的轮询。这样主函数就能无限延时或者处理各种耗时操作。相当于双线程运行。为了保证其它中断不受影响,这个执行TMOS轮询的中断必须是最低优先级的,并且能被所有中断抢断。


接下来我们使用RTC中断来验证我们的思路:

    使用官方BLE_USB的Demo来实现,具体步骤如下:

    首先,把HAL_SLEEP宏打开

    然后main函数需要改成:

int main(void)
{
    ...
    PFIC_DisableIRQ(RTC_IRQn);             //先关闭RTC中断
    CH58X_BLEInit();
    HAL_Init();
    GAPRole_PeripheralInit();
    Peripheral_Init();
    app_usb_init();
    PFIC_EnableIRQ(RTC_IRQn);             //所有初始化工作完成后,打开RTC中断
    RTC_SetTignTime(RTC_GetCycle32k() + 10);    //设置RTC在10个时钟周期后触发中断
    Main_Circulation();
}

   

主循环需要把TMOS_SystemProcess();注释掉,然后改成一个周期打印的功能来测试

void Main_Circulation()
{
    while(1)
    {
        //TMOS_SystemProcess();
        PRINT("123\n");
        DelayMs(1000);
    }
}

 

在SLEEP.c文件的HAL_SleepInit函数中需要增加设置RTC中断优先级成最低优先级

void HAL_SleepInit(void)
{
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
    sys_safe_access_enable();
    R8_SLP_WAKE_CTRL |= RB_SLP_RTC_WAKE; 
    sys_safe_access_enable();
    R8_RTC_MODE_CTRL |= RB_RTC_TRIG_EN; 
    sys_safe_access_disable();              
    PFIC_SetPriority(RTC_IRQn, 0xFF);    //将RTC中断优先级设置到最低,用于跑TMOS事务轮询
#endif
}

然后把CH58X_LowPower函数中的休眠相关的函数注释掉,并且在设置RTC触发时间后把RTC中断标志位清除

uint32_t CH58X_LowPower(uint32_t time)
{
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
    uint32_t time_sleep, time_curr, irq_status;
    SYS_DisableAllIrq(&irq_status);
    time_curr = RTC_GetCycle32k();
    // 检测睡眠时间
    if (time < time_curr) {
        time_sleep = time + (RTC_TIMER_MAX_VALUE - time_curr);
    } else {
        time_sleep = time - time_curr;
    }
    if ((time_sleep  (RTC_TIMER_MAX_VALUE - TMOS_TIME_VALID))) {
        SYS_RecoverIrq(irq_status);
        return 2;
    }
    RTC_SetTignTime(time);
    SYS_RecoverIrq(irq_status);
    R8_RTC_FLAG_CTRL = (RB_RTC_TMR_CLR | RB_RTC_TRIG_CLR);  //清除中断标志位,等待下一次RTC触发
#endif
    return 0;
}



在RTC.c文件中需要修改RTC_IRQHandler函数为:

void RTC_IRQHandler(void) {
    RTCTigFlag = 1;
    TMOS_SystemProcess();    //RTC中断中进行TMOS事务轮询,并且不清除中断标志位。
                             //等TMOS调用CH58X_LowPower时才清除标志,不然一直进中断。
}


到此代码修改完毕,编译、烧录、测试。在主循环中延时1秒钟也不会影响蓝牙的连接、数据收发。并且USB功能正常。


附上工程代码

icon_rar.gifCH58X.rar


注意事项:

1、PRINT在主循环和RTC中断中都会调用到,因为PRINT不是可重入函数,有可能会导致错误。


收藏


中断跑大任务总感觉不可靠,会不会莫名其妙什么栈溢出


认为理想的程序还是不要有阻塞式的延时,大任务可以拆分为多个小任务。

我做的蓝牙案子也没碰到因执行阻塞导致的蓝牙连接断开。


主要是项目用到一个外设的库很耗时,导致蓝牙直接断开了,改外设的库又太麻烦


嗯,TMOS是个支持抢占的RTOS就好了。


这样持续在中断里调用还是有问题的吧,你现在只有两个任务,是不是再多几个任务就乱了套了,这个RTC中断周期可是625us,程序持续进中断了,啥也别干了


TMOS依靠RTC提供时钟信号,整个任务执行时间过长,貌似会导致TMOS乱套。


TMOS并不是基于RTC中断,而是基于RTC的计数的,在RTC中断里执行任务时RTC计数并不会停止,所以不会影响TMOS。

然后RTC中断如果在不设置触发时间的情况下是24小时计数溢出的时候才进一次中断的,并不是625us进一次中断。

目前的逻辑是只有事件触发的时候才会进RTC中断执行TMOS轮询,并不会一直进RTC中断。



嗯,9楼是对的。有定时事件TMOS会设置RTC定时事件,用以唤醒芯片。


之前做过实验在主循环中调用延时函数不能超过625us,否则会影响TMOS_SystemProcess()导致蓝牙中断,这意味着在移动到RTC中断后,其他高优先级中断处理时间也不能超过625us



是的,目前改过的这套代码也是只能在主循环中执行延时或者耗时的操作。其它中断里面或者TMOS的任务还是一样不能延时或者处理耗时任务,不然会影响蓝牙的功能。


请教:这种方法虽然可以长延时,但是否就无法进入休眠状态?


这样的逻辑写程序,只能说疯了…

如果是刚入门,建议先把基础打牢不要走歪路,接着学学单片机OS原理编程,按OS的思维去写多任务程序,以后的路会好走太多太多了。


请教:这种方法虽然可以长延时,但是否就无法进入休眠状态?
回复:不影响进入休眠,在主函数里把
DelayMs改成对应的休眠函数即可


回复14#:TMOS固然好用,但是还是有局限性,很多没法拆分的耗时操作没办法处理。比如我现在用到的ST的NFC库,读卡时就是要这么耗时,你不可能去改ST的NFC库。而像诺迪克的蓝牙SDK就能实现无限的延时也不会影响到蓝牙工作,其实实现的思路也是一样的


只有登录才能回复,可以选择微信账号登录