8266+DS3231開發個時鐘遇到的N個坑

weixin_45499326發表於2020-12-07

最近玩這個8266上癮了,正好手上有一塊DS3231,就想著做一個網際網路時鐘玩玩,哪知道本以為很簡單的一個東西卻做了我一週的時間,踩平了N個坑。既然掉坑裡了又爬了出來就會有所收穫,趕快過來記錄一下,備忘備忘。
第一個坑,關於I2C引腳的問題。
我用的是nodeMCU 8266-E 的開發板,所查了一下樂鑫原廠的技術資料,資料上清楚的寫著如下:
在這裡插入圖片描述
我也沒多想,就把DS3231的SCL和SDA直接接上了IO4 和IO2。結果就是死活讀不到資料。後來抱著試試的心態想著程式沒錯的話應該就是硬體的問題,上度娘上查了前人的文章,發現有兩個博主連線的是GPIO5 和GPIO4 ,於是抱著試試的態度果斷打出如下程式碼。
#define SCL 5 //d1 I2C-SCL
#define SDA 4 //d2 I2C-SDA
結果一執行,立刻搞定。。。就這麼一個低階坑,就坑了我一天時間。要不是自已有懷疑精神,這個坑估計能把我埋了。

第二個坑更深了,但現在看來簡單一句話就是DS3231在啟動後,至少20秒內不能用setYear(),setHour()等這些個設定語句。
這裡補充一下,我用的是arduino 的IDE環境。因此自然使用的是arduino提供的官方下載的DS3231驅動。https://www.arduino.cc/reference/en/libraries/ds3231/
我的時鐘原本就是在聯上網後會有一個自動對時功能,從NTP上取下網路時間,然後設定給DS3231,就為了這個功能,惡夢開始了,程式一執行,怎麼都無法讀出準確的時間,反而讀出一堆亂碼。我又開始了自查程式。查了N遍怎麼都不可能錯了,然後開始分析官方提供的驅動,發現也沒有問題。然後很偶然的把一開機就設定時間的功能去掉,發現一切正常上了。經過反覆比較,反覆檢視DS3231的技術資料,都沒有對這一情況進行說明。這裡權猜測是因為DS3231在開機上電的一段時間裡暫存器初始化等作業不允許用設定指定吧。但可以用讀取指令。

第三個坑最深。。為了解決這個坑,上網各種搜查都沒能找到好的辦法,最後還是根據驅動程式原始碼併合理猜測才得以解決。這個坑是關於鬧鐘設定並輸出中斷的功能的。功能就是通過APP設定鬧鐘寫入DS3231裡,時間到後,必須從INT/SQW這個埠輸出中斷訊號給8266,8266再去處理這個中斷。但是我遇到幾個問題。一是每次一設完鬧鐘引數,3231就直接輸出了一個低電平,也就是說設完鬧鐘中斷就啟動了,不管時間到沒到。二是我對8266讀中斷的模式設為LOW也就是低電平觸發,但這裡死活都沒辦法觸發。。。這兩個問題中的第一個必須涉及到暫存器的解釋,
在這裡插入圖片描述
另一個是狀態暫存器
在這裡插入圖片描述
詳細的內容網上有大把的資料,需要的話可以去查,這裡只講和這個坑有關的內容。
根據上面的兩個暫存器解釋。要想讓低電平有效的中斷在鬧鐘啟動後觸發,必須完成這幾個暫存器置位。
1 控制暫存器的INTCN置1
2 控制暫存器的A1IE和A2IE置1
3 鬧鐘時間到時對狀態暫存器的A1F或A2F的置位
這樣就能使中斷觸發。
可是我設定了1,2兩部後,才用setA1Time()命令後,INT-SQW就一直處於低電平,也就是鬧鐘時間沒到,中斷就有效了。這讓我很無語,又是一通各種查,把驅動原始碼又看了N遍。最好才明白了,3231會自已偷偷的置位A1F或A2F。也就是說,3231不會自已清除A1F或A2F的標誌,必須手工去清除。搞清楚這點後,一切就簡單 了。如下:

//時鐘初始化   同步鬧鐘
   //enableOscillator(bool TF, bool battery, byte frequency)
   //當第一位TF為true 控制暫存器的 ~EOSC to 0  and INTCN to 0.,
   //EOSC置0則啟動振盪器INTCN置0(第二位battery為true VCC<VPF時會輸出1HZ方波)
   RTC.enableOscillator(true,false,0);
   RTC.checkIfAlarm(1);  //該函式檢測完後狀態暫存器A1F或A2F就把標誌位清0
   RTC.checkIfAlarm(2);
   

   ReadData(EEPROMBase1,Alarm1);
   ReadData(EEPROMBase2,Alarm2);
      
   if (Alarm1.flag==1){
      RTC.setA1Time(Alarm1.ADay,Alarm1.AHour,Alarm1.AMinute,Alarm1.ASecond,Alarm1.AlarmBits1,Alarm1.ADy,Alarm1.AH12,Alarm1.APM);
      //把控制暫存器的INTCN位和A1IE位置1,使能鬧鐘一的中斷。一旦狀態暫存器的A1F因為鬧鐘時間到而置1則在INTCN輸出中斷
      RTC.turnOnAlarm(1); 
      if (RTC.checkAlarmEnabled(1)){   
        Serial.println("鬧鐘1啟動,定時為每日的:"+String(Alarm1.AHour)+":"+String(Alarm1.AMinute));  
         DebugeTime(1,Temp); 
      };  
   }else {
    RTC.turnOffAlarm(1);
    
   }
   
   if (Alarm2.flag==1){
      RTC.setA2Time(Alarm2.ADay,Alarm2.AHour,Alarm2.AMinute,Alarm2.AlarmBits2,Alarm2.ADy,Alarm2.AH12,Alarm2.APM);
      //把控制暫存器的INTCN位和A2IE位置1,使能鬧鐘一的中斷。一旦狀態暫存器的A2F因為鬧鐘時間到而置1則在INTCN輸出中斷
      RTC.turnOnAlarm(2);
      if (RTC.checkAlarmEnabled(2)){   
        Serial.println("鬧鐘2啟動,定時為每日的:"+String(Alarm1.AHour)+":"+String(Alarm1.AMinute));   
        DebugeTime(2,Temp); 
      };  
   }else{
    RTC.turnOffAlarm(2);
     
   }

上面的關鍵就是那幾個RTC物件相關的方法的執行順序。
第二個問題就是關於8266中斷觸發的問題。由於用的是arduino IDE環境 所以經常會把arduino的語句與8266混用,儘管大多是一樣的,但還是有區別。比如,中斷的觸發。arduino可以用(LOW, HIGH ,FALLING,CHANGE,RISING).而8266就只有三種(FALLING,CHANGE,RISING)。
先寫到這裡,詳細的驅動程式 的分析明天再寫。

相關文章