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.5 成果展示
因為我這裡用的是數碼管顯示,但數碼管格數不夠,就只顯示了時分秒三個數值。
5. 結尾
DS3231模組已經成功驅動了,畢竟這個模組是比較容易的一個IIC感測,基本不用配置什麼暫存器,直接寫入再直接讀取即可,算是一個IIC小入門難度,不過其他感測也大同小異,都非常類似。
對STM8的I2C感測模組驅動講解到這裡結束,感謝各位看官的點選。
如果覺得有所收穫請點下推薦,若認為該部落格中存在錯誤的說明或者對部落格中某方面有疑問請留言。
作 者:浩宇99✌ 出 處:https://www.cnblogs.com/zhenghaoyu/p/10841542.html
版權宣告:本文原創發表於 部落格園,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則視為侵權。