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源碼,學習了。