CM3计算板RTC闹钟唤醒系统

1、前言

一个周期性控制系统的核心为CM3计算板,在电池供电情况下要求尽可能提高使用时长。由于系统空闲时长较多,因此在考虑低功耗的情况下将系统关机以进一步降低功耗。需要注意的是,系统关机后需要在指定时间唤醒,继续执行相关任务,这涉及到如何唤醒系统。

系统关机很容易用代码实现功能,一旦关机系统的服务都挂掉,如何保留开机任务?需要借助系统外围设计。

可以进一步抽象该需求,如何定时开机。目前我的设计比较暴力,其一,开机方式通过重置CM3计算板的Reset (RUN)引脚加以实现;其二,定时方式通过外部RTC时钟芯片进行设置,且RTC时钟芯片可以设置闹钟,产生硬件中断等电平触发跳变。

2、硬件

根据前面的描述,硬件连接示意图如下所示。详细电路连接不在此处罗列,以下介绍设计的要求。

  • CM3:提供一组I2C接口用于设置外部RTC
  • RTC:电池供电的实时时钟芯片,用I2C进行通信,具有闹钟功能,能产生闹钟中断
  • MCU:识别RTC闹钟中断信号,输出CM3系统复位信号。

具体地,选用的RTC为DS3231,该RTC的芯片资料可以在这里查看,逻辑框图大概就是这么回事。

本例的MCU作用很单一,检测RTC中断,并复位CM3。所以可以用很简单的单片机,比如51单片机都可以,我这里用的是SOP8封装的STC15W104单片机,STC单片机,你懂得。单片机采用中断还是电平检测都可以,这是由于DS3231产生闹钟中断后,INT管脚在没有被清除闹钟之前一直保持低电平,这很重要。

当然,如果不想对MCU单片机编程,也可以用其他边沿触发电路来代替MCU,比如采用JK触发器实现下降沿的捕获,再配合其他的硬件电路产生一个CM3复位电平即可。CM3的复位管脚Run为电平复位,拉低然后保持一点时间,再松开即可完成复位重启。如下图。

其他需要说明的,在采用MCU方式输出CM3复位信号的方案下,通常不要用MCU管脚直接连接CM3的复位系统,做一次信号隔离和驱动以保证两个系统的耦合性,例如,可以采用以下三极管驱动的方式。SYS_RST为MCU输出的信号,注意,此处需要MCU拉电流,因此配置MCU的相关管脚为强输出,即推挽输出以保证足够的驱动能力。

3、软件

3.1 DS3231驱动软件

DS3231采用标准I2C接口,Linux环境下在Github找到了现成的驱动rtcctl[点击链接]。使用起来非常方便,简单介绍使用方法。

(1) 下载

github地址: https://github.com/bablokb/pi-wake-on-rtc

(2)  安装

cd pi-wake-on-rtc //进入下载的文件夹内
sudo tools/install //执行安装脚本

(3)  使用

命令为rtcctl,详细的命令参数如下所示:

Available commands (date and time are synonyms):
     help                                - dump list of available commands
     init                                - initialize RTC
     show  [date|time|alarm1|alarm2|sys]
                                         - display given type or all
     dump  [control|status|alarm1|alarm1]
                                         - display registers (hex/binary format)
     set   date|time|alarm1|alarm2|sys   - set date/time, alarm1, alarm2 times
                                           Format: dd.mm.YYYY [HH:MM[:SS]] or
                                                   mm/dd.YYYY [HH:MM[:SS]]
                                           (does not turn alarm on!)
     on    alarm1|alarm2                 - turn alarm1/alarm2 on
     off   alarm1|alarm2                 - turn alarm1/alarm2 off
     clear alarm1|alarm2                 - clear alarm1/alarm2-flag

****注意1,该脚本使用的I2C默认挂接到I2C1,需要在系统中提前打开I2C接口,用i2cdetect 识别一下是否存在ID为68的设备。

****注意2,该脚本部分为window环境下编辑,如果执行命令报错,且提示存在"\r\n"错误,需要将该格式全部换成linux下的文件,可以参考这篇博文

rtcctl命令使用起来很简单,如下:

/* rtcctl 初始化 */
rtcctl init
/* rtcctl 查看系统时间 */
rtcctl show sys
/* rtcctl 查看闹钟1信息 */
rtcctl show alarm1
/* rtcctl 启用闹钟1 */
rtcctl on alarm1
/* rtcctl 清除闹钟1 */
rtcctl clear alarm1
/* rtcctl 设置闹钟1时间 2019/06/01 15:30:00 闹钟产生中断*/
rtcctl set 06/01/2019 15:30:00

3.2 MCU软件

MCU主要检测RTC闹钟中断,RTC闹钟产生中断后如果不清除则一直保持低电平状态。简单写的一个边沿识别程序如下:

void main()
{
    uint16_t Alarm1_tick = 0;
    uint8_t isSYSRstWorked = 0;
    uint8_t Alarm_reg0 = 0;
    uint8_t Alarm_reg1 = 0;
    
    /*! I/O configure */
    P3M1 = 0x00;
    P3M0 = 0x0C;
    
    SYS_RST_Out = 0;//init pin state
    
    while(1){
 
        delay_ms(1);//systick

        /*!  handle RTC wake up alarm1  */
        if(isSYSRstWorked == 0){
            Alarm_reg1 = Alarm_reg0;
            Alarm_reg0 = RTC_Alarm1_In;
            /*! check RTC alarm1 fall-edge */
            if((!Alarm_reg0) && Alarm_reg1 == 1){
                isSYSRstWorked = 1;
            }
        }
        else{
            Alarm1_tick++;
            /* ___________|-----|_____________ */
            if(Alarm1_tick < 2000) SYS_RST_Out = 1;
            else{
                Alarm1_tick = 0;
                SYS_RST_Out = 0;
                isSYSRstWorked = 0;
            }
        }
    }
}

可见,只要MCU识别到一个下降沿,就会产生一个CM3复位脉冲,脉冲宽度为2s,经过测试,可以实现CM3复位重启,达到定时开机的要求了。

3.3 CM3执行逻辑

RTC和MCU的外设配置完成后,需要在CM3编写执行逻辑。首先CM3开机后执行清除RTC闹钟(# rtcctl clear alarm1),或者直接对RTC进行初始化(# rtcctl init),其次执行正常监控管理任务,最后在关机之前设置下一次需要唤醒的RTC闹钟时间,推荐采用绝对时间方式,即计算重启时间到1970年1月1日(epoch·time)过了多少秒,再将重启时间的秒数转换为rtcctl命令的时间戳 mm/dd/yyyy HH:MM:SS,即可。

4、最后

总的来说,这个方案容易想到,实现起来也不复杂,简单的外设即可搞定。多谢github作者的rtcctl源码,学习了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章