DS3231时钟模块使用,IIC协议实践。(基于STM32)
文章目錄
- 寫在前面
- 文件結(jié)構(gòu),方便擴(kuò)展 和 代碼管理。
- 關(guān)于DS3231
- DS3231 寄存器設(shè)置
- 代碼
- `softwareIIC.h`
- `software.c`
- `DS3231.h`
- `DS3231.c`
- 總結(jié)不易,若對(duì)你有幫助,希望點(diǎn)贊收藏是😉👍
寫在前面
因?yàn)楫厴I(yè)設(shè)計(jì),需要用到記錄時(shí)間的功能,對(duì)時(shí)鐘模塊一直只聞其名,卻從未用過。之前的在使用其他模塊的時(shí)候,總是一個(gè)勁的拿來主義,經(jīng)常是在別人現(xiàn)成的代碼上改改宏定義就直接進(jìn)行使用,一旦項(xiàng)目需要用到的模塊多了起來,每個(gè)模塊別人寫好一個(gè)庫(kù),在不清楚其基本原理的情況下整個(gè)項(xiàng)目就變得臃腫不堪了。(據(jù)我所知很多同學(xué)本科階段使用模塊都是這樣一個(gè)模式)。
那么現(xiàn)在在剛回顧和復(fù)習(xí)完IIC通訊協(xié)議下,在這里我將IIC協(xié)議實(shí)踐在DS3231時(shí)鐘模塊上。
文件結(jié)構(gòu),方便擴(kuò)展 和 代碼管理。
這里建立兩個(gè)庫(kù): softwareIIC.h 和DS3231.h
DS3231.h 即存儲(chǔ)DS3231的寄存器定義,時(shí)間變量結(jié)構(gòu)體定義,等等。
softwareIIC.h即存儲(chǔ)相關(guān)IIC的函數(shù)定義,軟件 IIC 占用的 IO口定義,等等。
在使用上,只需要在DS3231.c中 #include "softwareIIC.h"即可。
同理,因?yàn)槲覀儗⒛K和 IIC 協(xié)議進(jìn)行了分離, 在項(xiàng)目中需要加到其他使用IIC協(xié)議的模塊時(shí),也只需要在對(duì)應(yīng)模塊的文件中 調(diào)用軟件IIC的頭文件 即可。
那么在之后其他項(xiàng)目也可以使用這個(gè)時(shí)候?qū)懞玫?軟件IIC 庫(kù), 只需要改改softwareIIC.h 里對(duì)于占用端口的宏定義即可, 十分方便代碼的移植😉👍。
若你是初學(xué)者(雖然我也是),希望你能養(yǎng)成這樣的一個(gè)模式、習(xí)慣,這對(duì)之后的幫助異常的大。
關(guān)于DS3231
DS3231是低成本、高精度的I2C實(shí)時(shí)時(shí)鐘(RTC)。該器件包括電池輸入端,斷開主電源時(shí),仍可保持精準(zhǔn)計(jì)時(shí)。
RTC保存秒、分、時(shí)、星期、月和年信息。少于31天的月份,將自動(dòng)調(diào)整月末的日期,包括閏年修正。時(shí)鐘格式可以時(shí)24小時(shí)或帶AM/PM的12小時(shí)格式。提供兩個(gè)可設(shè)置的日歷鬧鐘和1Hz輸出。
在這里我用的是在某寶上購(gòu)買的模塊,已經(jīng)預(yù)留出了VCC、GND、SDA、SCL。
DS3231 寄存器設(shè)置
第一眼看上去可能有點(diǎn)不明所以,但其實(shí)很簡(jiǎn)單。DS3231采用8421BCD碼用來存儲(chǔ)時(shí)間、日期等數(shù)據(jù)。(什么是8421BCD碼? 你可以簡(jiǎn)單理解為,一般用四位二進(jìn)制表示一位數(shù)字,比如十進(jìn)制數(shù)字58,對(duì)應(yīng)的8421BCD碼即:0101 1000 , 這里1000代表個(gè)位數(shù)的8 , 0101代表十位數(shù)的 5 。)
比如Seconds(秒),寄存器地址是0x00,我們需要讀取和設(shè)置 秒 ,也只需要對(duì)這個(gè)寄存器進(jìn)行讀寫即可。
秒范圍是 00 - 59,故第四位用來存儲(chǔ)個(gè)位數(shù)0 - 9,高三位用來存儲(chǔ)十位數(shù) 0 - 5 ( 000 - 101)。
其他寄存器同理。
另外關(guān)于采用 24小時(shí)制 還是 12小時(shí)制度,取決于Hours寄存器(0x02)中的第6位。
代碼
歐克,關(guān)于IIC 借助我上一篇博客相信已經(jīng)有所掌握,現(xiàn)在我們直接將其使用在和DS3231的通訊和設(shè)置中。
softwareIIC.h
#ifndef ___SOFTWARE_IIC #define ___SOFTWARE_IIC#include "stm32f10x.h" #include "./sys/sys.h"#define IIC_SDA_GPIOx GPIOA #define IIC_SDA_GPIO_Pin GPIO_Pin_11 #define IIC_SDA_GPIO_RCC RCC_APB2Periph_GPIOA #define IIC_SDA_RCCPeriphClockcmd RCC_APB2PeriphClockCmd#define IIC_SCL_GPIOx GPIOA #define IIC_SCL_GPIO_Pin GPIO_Pin_12 #define IIC_SCL_GPIO_RCC RCC_APB2Periph_GPIOA //上面已經(jīng)定義過了 #define IIC_SCL_RCCPeriphClockcmd RCC_APB2PeriphClockCmd#define IIC_SCLSDA_GPIO_RCC RCC_APB2Periph_GPIOA //使用4線串行接口時(shí)使用 #define IIC_SDA PAout(11) #define IIC_SCL PAout(12)#define READ_SDA GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)void IIC_Init(void); void IIC_Start(void); //產(chǎn)生起始條件(符) void IIC_Stop(void); //產(chǎn)生結(jié)束條件(符) uint8_t IIC_Wait_Ask(void); void IIC_Ack(void); void IIC_NAck(void); void IIC_WriteByte(u8 data); u8 IIC_Read_Byte(unsigned char ack); #endif // ___SOFTWARE_IICsoftware.c
#include "./softwareIIC/softwareIIC.h" #include "./systick/bsp_systick.h"void IIC_Init(void) {GPIO_InitTypeDef GPIO_InitStructer;//開啟時(shí)鐘IIC_SDA_RCCPeriphClockcmd(IIC_SDA_GPIO_RCC, ENABLE); //or IIC_SCL_GPIOx ,因?yàn)?sda scl 使用的是同一個(gè)gpiox//定義pinGPIO_InitStructer.GPIO_Pin=IIC_SDA_GPIO_Pin | IIC_SCL_GPIO_Pin; //10--SCL 11--SDA //PB10 PB11//定義頻率GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;//定義IO模式GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP;//初始化GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer); }static void SDA_OUT(void) {GPIO_InitTypeDef GPIO_InitStructer;GPIO_InitStructer.GPIO_Pin= IIC_SDA_GPIO_Pin;GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructer.GPIO_Mode=GPIO_Mode_Out_PP; //推挽輸出模式GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer); }static void SDA_IN(void) {GPIO_InitTypeDef GPIO_InitStructer;GPIO_InitStructer.GPIO_Pin= IIC_SDA_GPIO_Pin;GPIO_InitStructer.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructer.GPIO_Mode=GPIO_Mode_IPU; //上拉輸入模式GPIO_Init(IIC_SDA_GPIOx, &GPIO_InitStructer); }void IIC_Start(void) //產(chǎn)生起始條件(符) {SDA_OUT();IIC_SDA=1; //先確保 SDA為高IIC_SCL=1; //拉高SCLSysTick_Delay_us(4);IIC_SDA=0; //產(chǎn)生下降沿SysTick_Delay_us(4);IIC_SCL=0; //將時(shí)鐘線拉低,只有時(shí)鐘線拉低才運(yùn)行SDA數(shù)據(jù)變化有效SysTick_Delay_us(4); }void IIC_Stop(void) //產(chǎn)生結(jié)束條件(符) {SDA_OUT();IIC_SCL = 0;IIC_SDA=0; //先讓SDA 為低電平IIC_SCL=1; //將SCL拉高SysTick_Delay_us(4); // 延時(shí),保證時(shí)長(zhǎng)IIC_SDA=1; //SDA拉高 產(chǎn)生上升沿SysTick_Delay_us(4); // 結(jié)束后釋放SDA , SDA變高 }// uint8_t IIC_Wait_Ask(void) //{ // SDA_IN(); // IIC_SCL=1; // SysTick_Delay_us(4); // IIC_SCL=0; // SysTick_Delay_us(4); // return 0; //} uint8_t IIC_Wait_Ask(void) {uint16_t tempTime = 0;SDA_IN();IIC_SDA = 1; //釋放數(shù)據(jù)總線,交由從機(jī)控制SysTick_Delay_us(4);IIC_SCL = 1;SysTick_Delay_us(1);while (GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)) //讀到 0 ,即接收到ACK,循環(huán)跳出{tempTime++;if(tempTime > 300){IIC_Stop();return 1; //超時(shí)返回1}}IIC_SCL = 0;return 0; //接收到 ACK 返回0 }void IIC_Ack(void) {IIC_SCL = 0 ;SDA_OUT();IIC_SDA = 0;SysTick_Delay_us(2);IIC_SCL = 1;SysTick_Delay_us(5);IIC_SCL = 0; } //主機(jī)不產(chǎn)生應(yīng)答信號(hào)NACK void IIC_NAck(void) {IIC_SCL = 0;SDA_OUT();IIC_SDA = 1;SysTick_Delay_us(2);IIC_SCL = 1;SysTick_Delay_us(5);IIC_SCL = 0; }void IIC_WriteByte(u8 data) {u8 i;SDA_OUT();for(i=0;i<8;i++){IIC_SCL=0;SysTick_Delay_us(4);if(data & 0x80)IIC_SDA=1;elseIIC_SDA=0;IIC_SCL=1;SysTick_Delay_us(4);IIC_SCL=0;data<<=1;} }//讀1個(gè)字節(jié),ack=1時(shí),發(fā)送ACK,ack=0,發(fā)送nACK u8 IIC_Read_Byte(unsigned char ack) {unsigned char i,receive=0;SDA_IN();//SDA設(shè)置為輸入for(i=0;i<8;i++ ){IIC_SCL=0; SysTick_Delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++; SysTick_Delay_us(2); } if (!ack)IIC_NAck();//發(fā)送nACKelseIIC_Ack(); //發(fā)送ACK return receive; }上面是軟件IIC的代碼,是對(duì)于IIC總線的操作,不具體針對(duì)某一個(gè)模塊,可移植性高。
DS3231.h
#ifndef DS3231_H #define DS3231_H#include <stm32f10x.h>#define DS3231_ADDRESS 0x68 //I2C Slave address #define DS3231_ADDRESS_Write 0xD0 #define DS3231_ADDRESS_Read 0xD1/* DS3231 Registers. Refer Sec 8.2 of application manual */ #define DS3231_SEC_REG 0x00 // 秒 #define DS3231_MIN_REG 0x01 // #define DS3231_HOUR_REG 0x02 #define DS3231_WDAY_REG 0x03 #define DS3231_MDAY_REG 0x04 #define DS3231_MONTH_REG 0x05 #define DS3231_YEAR_REG 0x06#define DS3231_AL1SEC_REG 0x07 #define DS3231_AL1MIN_REG 0x08 #define DS3231_AL1HOUR_REG 0x09 #define DS3231_AL1WDAY_REG 0x0A#define DS3231_AL2MIN_REG 0x0B #define DS3231_AL2HOUR_REG 0x0C #define DS3231_AL2WDAY_REG 0x0D#define DS3231_CONTROL_REG 0x0E #define DS3231_STATUS_REG 0x0F #define DS3231_AGING_OFFSET_REG 0x0F #define DS3231_TMP_UP_REG 0x11 #define DS3231_TMP_LOW_REG 0x12#define EverySecond 0x01 #define EveryMinute 0x02 #define EveryHour 0x03typedef struct DateTImeStruct{uint8_t second;uint8_t minute;uint8_t hour;uint8_t dayofmonth;uint8_t month;uint16_t year;uint8_t dayOfWeek; /*Su=0 Mo=1 Tu=3 We=4 Th=5 Fr=6 Sa=7 */ }DateTime;uint8_t DS3231_setDate(uint8_t year,uint8_t mon,uint8_t day); uint8_t DS3231_setTime(uint8_t hour , uint8_t min , uint8_t sec); uint8_t DS3231_getdate(DateTime* ans); uint8_t DS3231_gettime(DateTime* ans);#endifDS3231.c
#include "./DS3231/DS3231.h" #include "./softwareIIC/softwareIIC.h" #include "./usart/bsp_usart.h"void DS3231_Init(void){IIC_Init(); }uint8_t IIC_DS3231_ByteWrite(uint8_t WriteAddr , uint8_t date) {IIC_Start();IIC_WriteByte(DS3231_ADDRESS_Write);if(IIC_Wait_Ask())return 1;IIC_WriteByte(WriteAddr);if(IIC_Wait_Ask())return 2;IIC_WriteByte(date);if(IIC_Wait_Ask())return 3;IIC_Stop();return 0; }uint8_t IIC_DS3231_ByteRead(uint8_t ReadAddr,uint8_t* Receive) {uint8_t data = 0;IIC_Start(); //產(chǎn)生起始位IIC_WriteByte(DS3231_ADDRESS_Write); //發(fā)送從機(jī)地址(寫模式)if(IIC_Wait_Ask()) //等待響應(yīng)return 1;IIC_WriteByte(ReadAddr); //發(fā)送寄存器地址if(IIC_Wait_Ask()) //等待響應(yīng)return 2;IIC_Start(); //重復(fù)起始位IIC_WriteByte(DS3231_ADDRESS_Read); //發(fā)送從機(jī)地址(讀模式)if(IIC_Wait_Ask()) //等待響應(yīng)return 3;data = IIC_Read_Byte(0); //讀取數(shù)據(jù),參數(shù)設(shè)為0 --- NACK*Receive = data; //將結(jié)果賦值給接收位IIC_Stop();return 0; }uint8_t DS3231_setDate(uint8_t year,uint8_t mon,uint8_t day) {uint8_t temp_H , temp_L;temp_L = year%10;temp_H = year/10;year = (temp_H << 4) + temp_L;if(IIC_DS3231_ByteWrite(DS3231_YEAR_REG,year)) //set year{printf("set year error\r\n");return 1;} temp_L = mon%10;temp_H = mon/10;mon = (temp_H << 4) + temp_L; if(IIC_DS3231_ByteWrite(DS3231_MONTH_REG,mon)) //set mon{printf("set month error\r\n");return 2;}temp_L = day%10;temp_H = day/10;day = (temp_H << 4) + temp_L; if(IIC_DS3231_ByteWrite(DS3231_MDAY_REG,day)) //set day{printf("set day error\r\n");return 3;}return 0; }uint8_t DS3231_setTime(uint8_t hour , uint8_t min , uint8_t sec) {uint8_t temp_H , temp_L;temp_L = hour%10;temp_H = hour/10;hour = (temp_H << 4) + temp_L;if(IIC_DS3231_ByteWrite(DS3231_HOUR_REG,hour)) //set hourreturn 1;temp_L = min%10;temp_H = min/10;min = (temp_H << 4) + temp_L;if(IIC_DS3231_ByteWrite(DS3231_MIN_REG,min)) //SET minreturn 2; temp_L = sec%10;temp_H = sec/10;sec = (temp_H << 4) + temp_L; if(IIC_DS3231_ByteWrite(DS3231_SEC_REG,sec)) //SET secreturn 3;return 0; }static uint8_t bcdToDec(uint8_t byte) {uint8_t temp_H , temp_L;temp_L = byte & 0x0f;temp_H = (byte & 0xf0) >> 4;return ( temp_H * 10 )+ temp_L; }uint8_t DS3231_gettime(DateTime* ans) {uint8_t receive = 0;if(IIC_DS3231_ByteRead(DS3231_HOUR_REG,&receive))return 1;ans->hour = bcdToDec(receive);if(IIC_DS3231_ByteRead(DS3231_MIN_REG,&receive))return 2;ans->minute = bcdToDec(receive);if(IIC_DS3231_ByteRead(DS3231_SEC_REG,&receive))return 3;ans->second = bcdToDec(receive);return 0; }uint8_t DS3231_getdate(DateTime* ans) {uint8_t receive = 0;if(IIC_DS3231_ByteRead(DS3231_YEAR_REG,&receive))return 1;ans->year = bcdToDec(receive) + 2000;if(IIC_DS3231_ByteRead(DS3231_MONTH_REG,&receive))return 2;ans->month = bcdToDec(receive);if(IIC_DS3231_ByteRead(DS3231_MDAY_REG,&receive))return 3;ans->dayofmonth = bcdToDec(receive);return 0; }總結(jié)不易,若對(duì)你有幫助,希望點(diǎn)贊收藏是😉👍
總結(jié)
以上是生活随笔為你收集整理的DS3231时钟模块使用,IIC协议实践。(基于STM32)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JMeter场景设置与监控
- 下一篇: Uva 1625 - Color Len