基於STM8的IIC協議--實例篇--時鍾模塊(DS3231)讀取


1. 綜述

  由上篇博客可知道IIC協議如何用代碼實現,本篇博客就不涉及協議內容,只講解如何使用。

  本次的實驗傳感為:DS3231(時鍾模塊),對於時鍾模塊的具體信息我也就不多介紹,大家可以自行度娘,具體功能無非就是讓單片機中能夠起到獲取時間的作用。該模塊是可以由IIC協議去驅動的,再加上所要的操作也是比較簡單,部分剛接觸IIC協議的小伙伴可以拿來練手的一個模塊。

 

2. 明確任務順序

  個人習慣,在每驅動一個新傳感的時候,我會將我要完成的傳感分為幾個任務點。接下來就展示一下我在寫DS3231模塊時的任務欄。

一.硬件部分 1.完成排針焊接
2.完成傳感引角和單片機引角的接線
二.軟件部分 1. 找到該傳感的官方手冊並認真閱讀
2. 找到並定義該傳感的地址(7位地址、8位地址)
3. 找到並定義該傳感器的寄存器地址
4. 完成讀寫函數的代碼 5. 測試傳感是否有響應
6. 查看手冊,了解傳感的工作模式
7. 完成傳感參數初始化 8. 傳感數據的獲取

   根據上面這些小任務一一解決,這樣一來,大多數傳感就能成功驅動了,這里在給一個建議,驅動傳感器的代碼最好不要一次性全部寫完再進行測試,這樣成功率不高,並且會大大增加你找問題的難度,將每個功能函數測試完再進行下一個功能函數的編寫,會大大加大你的效率。

 

3. 具體任務實現步驟

  3.1. 硬件部分

  在STM8S103芯片中,我所定義的IIC協議中的SDA引角為PD3,SCL引角為PD4,該時鍾模塊所需提供的電源為3.3V。所以接線圖如下。

  

  3.2. 軟件部分

 (1)官方手冊

  我這里提供的是全英版的官方手冊,多看點英文對你有好處的。

鏈接:https://pan.baidu.com/s/1Oo6o68SoVr7gt8tPZIoQxw 
提取碼:uzih 

 

  (2)器件地址

  在說明手冊中,我們可以在第16頁的右下角找到下面這句話:

  The slave address byte contains the 7-bit DS3231 address, which is 1101000, followed by the direction bit (R/W), which is 0 for a write.

  這句話的大意為,從機的7位地址為1101000,后跟一位讀寫位,讀寫位為0時是寫位。即7位地址的為0x68,8位地址為0xD0。

 

  (3)寄存器地址

  

  DS3231的寄存器地址都在這了,大家可根據所需要用的功能定義相應的寄存器。

  以下是我在頭文件所定義的格式,因為我只需要獲取時間,不需要進行其他操作,所以部分寄存器沒有使用。

1 #define DS3231_Address           0xD0                           //設備地址
2 #define Seconds_Register         0x00                           //秒鍾寄存器
3 #define Minutes_Register         0x01                           //分鍾寄存器
4 #define Hour_Register            0x02                           //小時寄存器
5 #define Day_Register             0x03                           //星期寄存器
6 #define Date_Register            0x04                           //日期寄存器
7 #define Month_Register           0x05                           //月份寄存器(第七位是世紀位)
8 #define Year_Register            0x06                           //年份寄存器

 

4. 例程

4.1 編譯環境

  我的編譯環境是IAR,這款軟件是現在STM8的主流平台,比較推薦。不過我打算等到STCubeMX更新出比較方便的版本后再去使用Keil5,因為我在用STM32的時候就是利用Keil5,的確很方便,你們也可以學着用一下。

 

4.2 主芯片

  我的主芯片是STM8S系列中的103,其中STM8S的003、005、和103、105,配置一樣(外設和CPU頻率,FLASH),在代碼相同的情況下均可進行燒寫。

 

4.3 庫文件的添加

  我們的工程可以在IAR中的官方例程中復制,操作過程:打開STM8S_StdPeriph_Lib(這是一個官方的庫文件,下載IAR STM8包的時候就攜帶,里面有庫文件和相對應的例程),將Libraries文件復制到你工程所在的文件下,並將有關於ADC的庫文件添加到你的工程列表當中。添加完成后,就可以開始編寫代碼了(如果你將全部的庫文件都添加進來的話,編譯程序后庫文件還有紅點報錯的話,這是因為你選的芯片上沒有該功能,你需要將其刪掉才能不報錯。)如圖。

                   

 

4.4 代碼

4.4.1 SDA、SCL引角的定義

  我這里將SDA、SCL都設置為了推挽輸出,具體為什么可與參考上一片IIC協議講解。

1   //IIC引腳 
2   GPIO_Init(IIC_SCL_GPIO_Port, IIC_SCL_Pin, GPIO_MODE_OUT_PP_HIGH_FAST);
3   GPIO_Init(IIC_SDA_GPIO_Port, IIC_SDA_Pin, GPIO_MODE_OUT_PP_HIGH_FAST);

 

4.4.2 DS3231句柄定義

  為了能夠方便數據的管理,所以我定義了DS3231的一個句柄。

 1 typedef struct DS3231
 2 {    
 3   uint8_t uSeconds;                     //
 4   uint8_t uMinutes;                     //分鍾
 5   uint8_t uHour;                        //小時
 6   uint8_t uDay;                         //星期
 7   uint8_t uData;                        //日期
 8   uint8_t uMouth;                       //月份
 9   uint8_t uyear;                        //年份
10   uint8_t uTime[3];                     //將秒、分鍾、小時、日期包括在內
11   
12     
13 }DS3231_HandleTypeDef; 

 

4.4.3 BCD格式和B格式轉換

  在手冊中可以看到,寫入到DS3231中的格式是BCD格式,而讀取到的卻是B格式,所以我們需要兩個函數將其轉換,也方便我們察看數據的結果。

 1 uint8_t Byte_Transform_BCD(uint8_t uData)
 2 {
 3   uint8_t i, j, uBCD_Code;
 4   
 5   i = uData / 10;
 6   j = uData % 10;
 7   uBCD_Code = j + ( i << 4 );
 8   
 9   return uBCD_Code;
10   
11 }
12 
13 uint8_t BCD_Transform_Byte(uint8_t uData)
14 {
15     uint8_t uByte_Code;
16 
17     uByte_Code  = (uData & 0x0f);
18     uData     >>= 4;
19     uData      &= 0x0f;
20     uData      *= 10;
21     uByte_Code += uData;
22 
23     return uByte_Code;
24     
25 }

 

4.4.4讀寫函數編寫

  讀寫函數我們如果不去看手冊說明的話是無法編寫代碼的,所以我們需要去看手冊,根據手冊中的IIC命令順序進行編寫代碼。

    這幅圖為 IIC數據傳輸概述。

 

    寫函數流程圖:

    

  這個流程圖是在DS3231中的官方圖,根據圖中的命令可以寫出代碼,其中,代碼中出現IIC_HandleTypedef * iicHandle等參數,是IIC的句柄,具體可看上一篇博客,下面所出現的也是一樣的意思。

 1 uint8_t vSen_DS3231_Write_Bytes(IIC_HandleTypedef * iicHandle, uint8_t Register_Address, uint8_t Data_Byte)
 2 {
 3   
 4   vIIC_Start_Signal(iicHandle);                                                 //1.  IIC_Start                 ;  起始信號                               
 5   vIIC_SendByte(iicHandle, Slave_Address);                                      //2.  IIC_Send Device Address(W);  發送(設備地址)告訴總線即將操作的設備   
 6   
 7   if(!bIIC_ReadACK(iicHandle))                                                  //3.  IIC_ReadAck               ;  等待響應
 8   {                                                                                   
 9     vIIC_Stop_Signal(iicHandle);                                                   
10     return FALSE;                                                                     
11   }
12   
13   vIIC_SendByte(iicHandle, Register_Address);                                   //4.  IIC_Send Register Address ; 發送(寄存器)   告訴設備我們即將操作的寄存器
14   bIIC_ReadACK(iicHandle);                                                      //5.  IIC_ReadAck               ; 等待響應
15   vIIC_SendByte(iicHandle, Data_Byte);                                          //6.  IIC_Send the data to Reg  ; 發送(數據)     寫入數據到指定設備的寄存器中
16   bIIC_ReadACK(iicHandle);                                                      //7.  IIC_ReadAck               ; 等待響應
17   vIIC_Stop_Signal(iicHandle);                                                  //8.  IIC_Stop                  ; 結束信號
18   
19 
20   return TRUE;
21   
22 }

 

  讀函數流程圖:

 1 uint8_t vSen_DS3231_Read_Bytes(IIC_HandleTypedef * iicHandle, uint8_t Register_Address)
 2 {
 3   uint8_t uRev_Register_Data = 0x00;
 4   
 5   vIIC_Start_Signal(iicHandle);                                                 //1.  IIC_Start                 ;  起始信號                                                                                                                                                  
 6   vIIC_SendByte(iicHandle, Slave_Address);                                      //2.  IIC_Send Device Address(W);  發送(設備地址)告訴總線即將操作的設備       
 7                                                                                          
 8   if(!bIIC_ReadACK(iicHandle))                                                  //3.  IIC_ReadAck               ;  等待響應
 9   {                                                                                      
10     vIIC_Stop_Signal(iicHandle);                                                      
11     return FALSE;                                                                        
12   }                                                                                      
13                                                                                          
14   vIIC_SendByte(iicHandle, Register_Address);                                   //4.  IIC_Send Register Address ; 發送(寄存器)   告訴設備我們即將操作的寄存器
15   vIIC_Ack(iicHandle);                                                          //5.  IIC_Ack                   ; 主動響應
16   vIIC_Start_Signal(iicHandle);                                                 //6.  IIC_Start                 ; 起始信號
17 vIIC_SendByte(iicHandle, Slave_Address+1); //7. IIC_Send Device Address(R); 發送(設備地址)告訴總線即將操作的設備 18 vIIC_Ack(iicHandle); //8. IIC_Ack ; 主動響應 19 uRev_Register_Data = uIIC_RecvByte(iicHandle); //9. IIC_ReadByte ; 讀取寄存器中的數據 20 vIIC_NAck(iicHandle); //10. IIC_Nack ; 主動不應答 21 vIIC_Stop_Signal(iicHandle); //11. IIC_Stop ; 結束信號 22 23 return uRev_Register_Data; 24 25 }

  

  完成讀寫函數的編寫后,那么我們就可測試傳感是否通信成功,具體可檢測的方法有挺多,最直觀的是用示波器察看波形,若在讀寫函數發送器件地址后,有接下來的發送寄存器地址和數據等操作波形的話,即有返回到ACK,則單片機與傳感通信成功。這里再說一個不需要用到的示波器的操作,同樣的,在讀寫函數發送器件地址后,用Uart發送功能,在串口助手上打印一個數,若有數顯示,則沒有return 跳出函數,證明SDA的電平被器件拉低,也是有ACK返回的情況,則單片機於傳感通信成功。

 

4.4.5 修改時間

  我們在讀取時間之前需要對時間進行一個手動輸入的方法去給予時鍾模塊一個初始值,這樣才能開始計時,這個函數可以當作修改時間的功能,具體代碼可以自行編寫,我這里的代碼就寫成全部修改。

 1 void vSen_DS3231_Modify_Time(IIC_HandleTypedef * iicHandle, uint8_t uyear, uint8_t uMouth, uint8_t uData, uint8_t uDay, uint8_t uHour, uint8_t uMinutes, uint8_t uSeconds)
 2 {
 3 
 4   vSen_DS3231_Write_Bytes(iicHandle, Year_Register,    Byte_Transform_BCD(uyear));                //修改年份
 5                   
 6   vSen_DS3231_Write_Bytes(iicHandle, Month_Register,   Byte_Transform_BCD(uMouth));               //修改月份
 7                   
 8   vSen_DS3231_Write_Bytes(iicHandle, Date_Register,    Byte_Transform_BCD(uData));                //修改日期
 9       
10   vSen_DS3231_Write_Bytes(iicHandle, Day_Register,     Byte_Transform_BCD(uDay));                 //修改星期
11       
12   vSen_DS3231_Write_Bytes(iicHandle, Hour_Register,    Byte_Transform_BCD(uHour));                //修改小時
13                   
14   vSen_DS3231_Write_Bytes(iicHandle, Minutes_Register, Byte_Transform_BCD(uMinutes));             //修改分鍾
15             
16   vSen_DS3231_Write_Bytes(iicHandle, Seconds_Register, Byte_Transform_BCD(uSeconds));             //修改秒鍾
17   
18 }

 

4.4.6 獲取時間

  獲取時間就直接讀取相應的寄存器獲取數值即可,最后將其輸出就行。

 1 void vSen_DS3231_Get_Times(IIC_HandleTypedef * iicHandle, DS3231_HandleTypeDef * hDS3231)
 2 {
 3   hDS3231->uyear    =  BCD_Transform_Byte(vSen_DS3231_Read_Bytes(iicHandle, Year_Register));      //獲取年份
 4 
 5   hDS3231->uMouth   =  BCD_Transform_Byte(vSen_DS3231_Read_Bytes(iicHandle, Month_Register));     //獲取月份 
 6   
 7   hDS3231->uData    =  BCD_Transform_Byte(vSen_DS3231_Read_Bytes(iicHandle, Date_Register));      //獲取日期  
 8   
 9   hDS3231->uDay     =  BCD_Transform_Byte(vSen_DS3231_Read_Bytes(iicHandle, Day_Register));       //獲取星期  
10   
11   hDS3231->uHour    =  BCD_Transform_Byte(vSen_DS3231_Read_Bytes(iicHandle, Hour_Register));      //獲取小時   
12      
13   hDS3231->uMinutes =  BCD_Transform_Byte(vSen_DS3231_Read_Bytes(iicHandle, Minutes_Register));   //獲取分鍾
14   
15   hDS3231->uSeconds =  BCD_Transform_Byte(vSen_DS3231_Read_Bytes(iicHandle, Seconds_Register));   //獲取秒鍾  
16                                                                               
17 }

 

4.4.7 DS3231初始化

 1 /*******************************************************************************
 2 * Function Name  : vSen_DS3231_IIC_Init
 3 * Description    : DS3231 Init
 4 * Input          : IIC_HandleTypedef * iicHandle, GPIO_TypeDef * pSCL_Port, uint16_t uSCL_Pin, GPIO_TypeDef * pSDA_Port, uint16_t uSDA_Pin
 5 * Output         : None
 6 * Return         : None
 7 ********************************************************************************/
 8 void vSen_DS3231_Init(IIC_HandleTypedef * iicHandle, DS3231_HandleTypeDef * hDS3231, GPIO_TypeDef * pSCL_Port, uint16_t uSCL_Pin, GPIO_TypeDef * pSDA_Port, uint16_t uSDA_Pin)
 9 {
10   
11   vIIC_Handle_Init(iicHandle, pSCL_Port, uSCL_Pin, pSDA_Port, uSDA_Pin);        //初始化IIC協議句柄
12   
13   
14 // 年份 月份 日期 星期 時鍾 分鍾 秒鍾 15 vSen_DS3231_Modify_Time(iicHandle, 19, 5, 10, 5, 21, 45, 00); //DS3231初始化時間 16 17 }

 

 4.5 成果展示

  因為我這里用的是數碼管顯示,但數碼管格數不夠,就只顯示了時分秒三個數值。

 

5. 結尾

  DS3231模塊已經成功驅動了,畢竟這個模塊是比較容易的一個IIC傳感,基本不用配置什么寄存器,直接寫入再直接讀取即可,算是一個IIC小入門難度,不過其他傳感也大同小異,都非常類似。

  對STM8的I2C傳感模塊驅動講解到這里結束,感謝各位看官的點擊。

  如果覺得有所收獲請點下推薦,若認為該博客中存在錯誤的說明或者對博客中某方面有疑問請留言。

 

 

作 者:浩宇99✌
出 處:https://www.cnblogs.com/zhenghaoyu/p/10841542.html
版權聲明:本文原創發表於 博客園,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則視為侵權。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM