基于STM8的IIC协议--实例篇--时钟模块(DS3231)读取
1. 綜述
由上篇博客可知道IIC協(xié)議如何用代碼實(shí)現(xiàn),本篇博客就不涉及協(xié)議內(nèi)容,只講解如何使用。
本次的實(shí)驗(yàn)傳感為:DS3231(時(shí)鐘模塊),對于時(shí)鐘模塊的具體信息我也就不多介紹,大家可以自行度娘,具體功能無非就是讓單片機(jī)中能夠起到獲取時(shí)間的作用。該模塊是可以由IIC協(xié)議去驅(qū)動(dòng)的,再加上所要的操作也是比較簡單,部分剛接觸IIC協(xié)議的小伙伴可以拿來練手的一個(gè)模塊。
2. 明確任務(wù)順序
個(gè)人習(xí)慣,在每驅(qū)動(dòng)一個(gè)新傳感的時(shí)候,我會(huì)將我要完成的傳感分為幾個(gè)任務(wù)點(diǎn)。接下來就展示一下我在寫DS3231模塊時(shí)的任務(wù)欄。
一.硬件部分 1.完成排針焊接 2.完成傳感引角和單片機(jī)引角的接線 二.軟件部分 1. 找到該傳感的官方手冊并認(rèn)真閱讀 2. 找到并定義該傳感的地址(7位地址、8位地址)
3. 找到并定義該傳感器的寄存器地址 4. 完成讀寫函數(shù)的代碼 5. 測試傳感是否有響應(yīng)
6. 查看手冊,了解傳感的工作模式
7. 完成傳感參數(shù)初始化 8. 傳感數(shù)據(jù)的獲取
根據(jù)上面這些小任務(wù)一一解決,這樣一來,大多數(shù)傳感就能成功驅(qū)動(dòng)了,這里在給一個(gè)建議,驅(qū)動(dòng)傳感器的代碼最好不要一次性全部寫完再進(jìn)行測試,這樣成功率不高,并且會(huì)大大增加你找問題的難度,將每個(gè)功能函數(shù)測試完再進(jìn)行下一個(gè)功能函數(shù)的編寫,會(huì)大大加大你的效率。
3. 具體任務(wù)實(shí)現(xiàn)步驟
3.1. 硬件部分
在STM8S103芯片中,我所定義的IIC協(xié)議中的SDA引角為PD3,SCL引角為PD4,該時(shí)鐘模塊所需提供的電源為3.3V。所以接線圖如下。
3.2. 軟件部分
(1)官方手冊
我這里提供的是全英版的官方手冊,多看點(diǎn)英文對你有好處的。
鏈接: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.
這句話的大意為,從機(jī)的7位地址為1101000,后跟一位讀寫位,讀寫位為0時(shí)是寫位。即7位地址的為0x68,8位地址為0xD0。
(3)寄存器地址
DS3231的寄存器地址都在這了,大家可根據(jù)所需要用的功能定義相應(yīng)的寄存器。
以下是我在頭文件所定義的格式,因?yàn)槲抑恍枰@取時(shí)間,不需要進(jìn)行其他操作,所以部分寄存器沒有使用。
1 #define DS3231_Address 0xD0 //設(shè)備地址 2 #define Seconds_Register 0x00 //秒鐘寄存器 3 #define Minutes_Register 0x01 //分鐘寄存器 4 #define Hour_Register 0x02 //小時(shí)寄存器 5 #define Day_Register 0x03 //星期寄存器 6 #define Date_Register 0x04 //日期寄存器 7 #define Month_Register 0x05 //月份寄存器(第七位是世紀(jì)位) 8 #define Year_Register 0x06 //年份寄存器
4. 例程
4.1 編譯環(huán)境
我的編譯環(huán)境是IAR,這款軟件是現(xiàn)在STM8的主流平臺,比較推薦。不過我打算等到STCubeMX更新出比較方便的版本后再去使用Keil5,因?yàn)槲以谟肧TM32的時(shí)候就是利用Keil5,的確很方便,你們也可以學(xué)著用一下。
4.2 主芯片
我的主芯片是STM8S系列中的103,其中STM8S的003、005、和103、105,配置一樣(外設(shè)和CPU頻率,F(xiàn)LASH),在代碼相同的情況下均可進(jìn)行燒寫。
4.3 庫文件的添加
我們的工程可以在IAR中的官方例程中復(fù)制,操作過程:打開STM8S_StdPeriph_Lib(這是一個(gè)官方的庫文件,下載IAR STM8包的時(shí)候就攜帶,里面有庫文件和相對應(yīng)的例程),將Libraries文件復(fù)制到你工程所在的文件下,并將有關(guān)于ADC的庫文件添加到你的工程列表當(dāng)中。添加完成后,就可以開始編寫代碼了(如果你將全部的庫文件都添加進(jìn)來的話,編譯程序后庫文件還有紅點(diǎn)報(bào)錯(cuò)的話,這是因?yàn)槟氵x的芯片上沒有該功能,你需要將其刪掉才能不報(bào)錯(cuò)。)如圖。
4.4 代碼
4.4.1 SDA、SCL引角的定義
我這里將SDA、SCL都設(shè)置為了推挽輸出,具體為什么可與參考上一片IIC協(xié)議講解。
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句柄定義
為了能夠方便數(shù)據(jù)的管理,所以我定義了DS3231的一個(gè)句柄。
1 typedef struct DS3231
2 {
3 uint8_t uSeconds; //秒
4 uint8_t uMinutes; //分鐘
5 uint8_t uHour; //小時(shí)
6 uint8_t uDay; //星期
7 uint8_t uData; //日期
8 uint8_t uMouth; //月份
9 uint8_t uyear; //年份
10 uint8_t uTime[3]; //將秒、分鐘、小時(shí)、日期包括在內(nèi)
11
12
13 }DS3231_HandleTypeDef;
4.4.3 BCD格式和B格式轉(zhuǎn)換
在手冊中可以看到,寫入到DS3231中的格式是BCD格式,而讀取到的卻是B格式,所以我們需要兩個(gè)函數(shù)將其轉(zhuǎn)換,也方便我們察看數(shù)據(jù)的結(jié)果。
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讀寫函數(shù)編寫
讀寫函數(shù)我們?nèi)绻蝗タ词謨哉f明的話是無法編寫代碼的,所以我們需要去看手冊,根據(jù)手冊中的IIC命令順序進(jìn)行編寫代碼。
這幅圖為 IIC數(shù)據(jù)傳輸概述。
寫函數(shù)流程圖:
這個(gè)流程圖是在DS3231中的官方圖,根據(jù)圖中的命令可以寫出代碼,其中,代碼中出現(xiàn)IIC_HandleTypedef * iicHandle等參數(shù),是IIC的句柄,具體可看上一篇博客,下面所出現(xiàn)的也是一樣的意思。
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); 發(fā)送(設(shè)備地址)告訴總線即將操作的設(shè)備
6
7 if(!bIIC_ReadACK(iicHandle)) //3. IIC_ReadAck ; 等待響應(yīng)
8 {
9 vIIC_Stop_Signal(iicHandle);
10 return FALSE;
11 }
12
13 vIIC_SendByte(iicHandle, Register_Address); //4. IIC_Send Register Address ; 發(fā)送(寄存器) 告訴設(shè)備我們即將操作的寄存器
14 bIIC_ReadACK(iicHandle); //5. IIC_ReadAck ; 等待響應(yīng)
15 vIIC_SendByte(iicHandle, Data_Byte); //6. IIC_Send the data to Reg ; 發(fā)送(數(shù)據(jù)) 寫入數(shù)據(jù)到指定設(shè)備的寄存器中
16 bIIC_ReadACK(iicHandle); //7. IIC_ReadAck ; 等待響應(yīng)
17 vIIC_Stop_Signal(iicHandle); //8. IIC_Stop ; 結(jié)束信號
18
19
20 return TRUE;
21
22 }
讀函數(shù)流程圖:
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); 發(fā)送(設(shè)備地址)告訴總線即將操作的設(shè)備
7
8 if(!bIIC_ReadACK(iicHandle)) //3. IIC_ReadAck ; 等待響應(yīng)
9 {
10 vIIC_Stop_Signal(iicHandle);
11 return FALSE;
12 }
13
14 vIIC_SendByte(iicHandle, Register_Address); //4. IIC_Send Register Address ; 發(fā)送(寄存器) 告訴設(shè)備我們即將操作的寄存器
15 vIIC_Ack(iicHandle); //5. IIC_Ack ; 主動(dòng)響應(yīng)
16 vIIC_Start_Signal(iicHandle); //6. IIC_Start ; 起始信號
17 vIIC_SendByte(iicHandle, Slave_Address+1); //7. IIC_Send Device Address(R); 發(fā)送(設(shè)備地址)告訴總線即將操作的設(shè)備
18 vIIC_Ack(iicHandle); //8. IIC_Ack ; 主動(dòng)響應(yīng)
19 uRev_Register_Data = uIIC_RecvByte(iicHandle); //9. IIC_ReadByte ; 讀取寄存器中的數(shù)據(jù)
20 vIIC_NAck(iicHandle); //10. IIC_Nack ; 主動(dòng)不應(yīng)答
21 vIIC_Stop_Signal(iicHandle); //11. IIC_Stop ; 結(jié)束信號
22
23 return uRev_Register_Data;
24
25 }
完成讀寫函數(shù)的編寫后,那么我們就可測試傳感是否通信成功,具體可檢測的方法有挺多,最直觀的是用示波器察看波形,若在讀寫函數(shù)發(fā)送器件地址后,有接下來的發(fā)送寄存器地址和數(shù)據(jù)等操作波形的話,即有返回到ACK,則單片機(jī)與傳感通信成功。這里再說一個(gè)不需要用到的示波器的操作,同樣的,在讀寫函數(shù)發(fā)送器件地址后,用Uart發(fā)送功能,在串口助手上打印一個(gè)數(shù),若有數(shù)顯示,則沒有return 跳出函數(shù),證明SDA的電平被器件拉低,也是有ACK返回的情況,則單片機(jī)于傳感通信成功。
4.4.5 修改時(shí)間
我們在讀取時(shí)間之前需要對時(shí)間進(jìn)行一個(gè)手動(dòng)輸入的方法去給予時(shí)鐘模塊一個(gè)初始值,這樣才能開始計(jì)時(shí),這個(gè)函數(shù)可以當(dāng)作修改時(shí)間的功能,具體代碼可以自行編寫,我這里的代碼就寫成全部修改。
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)); //修改小時(shí)
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 獲取時(shí)間
獲取時(shí)間就直接讀取相應(yīng)的寄存器獲取數(shù)值即可,最后將其輸出就行。
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)); //獲取小時(shí)
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協(xié)議句柄
12
13
14 // 年份 月份 日期 星期 時(shí)鐘 分鐘 秒鐘
15 vSen_DS3231_Modify_Time(iicHandle, 19, 5, 10, 5, 21, 45, 00); //DS3231初始化時(shí)間
16
17 }
4.5 成果展示
因?yàn)槲疫@里用的是數(shù)碼管顯示,但數(shù)碼管格數(shù)不夠,就只顯示了時(shí)分秒三個(gè)數(shù)值。
5. 結(jié)尾
DS3231模塊已經(jīng)成功驅(qū)動(dòng)了,畢竟這個(gè)模塊是比較容易的一個(gè)IIC傳感,基本不用配置什么寄存器,直接寫入再直接讀取即可,算是一個(gè)IIC小入門難度,不過其他傳感也大同小異,都非常類似。
對STM8的I2C傳感模塊驅(qū)動(dòng)講解到這里結(jié)束,感謝各位看官的點(diǎn)擊。
如果覺得有所收獲請點(diǎn)下推薦,若認(rèn)為該博客中存在錯(cuò)誤的說明或者對博客中某方面有疑問請留言。
作 者:浩宇99✌ 出 處:https://www.cnblogs.com/zhenghaoyu/p/10841542.html
版權(quán)聲明:本文原創(chuàng)發(fā)表于 博客園,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則視為侵權(quán)。
總結(jié)
以上是生活随笔為你收集整理的基于STM8的IIC协议--实例篇--时钟模块(DS3231)读取的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 刺激战场军团有什么用
- 下一篇: 第十二届蓝桥杯JavaB组省赛H题 杨辉