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 commandsinit - initialize RTCshow [date|time|alarm1|alarm2|sys]- display given type or alldump [control|status|alarm1|alarm1]- display registers (hex/binary format)set date|time|alarm1|alarm2|sys - set date/time, alarm1, alarm2 timesFormat: dd.mm.YYYY [HH:MM[:SS]] ormm/dd.YYYY [HH:MM[:SS]](does not turn alarm on!)on alarm1|alarm2 - turn alarm1/alarm2 onoff alarm1|alarm2 - turn alarm1/alarm2 offclear 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 statewhile(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源码,学习了。