DS3231时钟模块使用,IIC协议实践。(基于STM32)
文章目錄
- 寫在前面
- 文件結構,方便擴展 和 代碼管理。
- 關于DS3231
- DS3231 寄存器設置
- 代碼
- `softwareIIC.h`
- `software.c`
- `DS3231.h`
- `DS3231.c`
- 總結不易,若對你有幫助,希望點贊收藏是😉👍
寫在前面
因為畢業設計,需要用到記錄時間的功能,對時鐘模塊一直只聞其名,卻從未用過。之前的在使用其他模塊的時候,總是一個勁的拿來主義,經常是在別人現成的代碼上改改宏定義就直接進行使用,一旦項目需要用到的模塊多了起來,每個模塊別人寫好一個庫,在不清楚其基本原理的情況下整個項目就變得臃腫不堪了。(據我所知很多同學本科階段使用模塊都是這樣一個模式)。
那么現在在剛回顧和復習完IIC通訊協議下,在這里我將IIC協議實踐在DS3231時鐘模塊上。
文件結構,方便擴展 和 代碼管理。
這里建立兩個庫: softwareIIC.h 和DS3231.h
DS3231.h 即存儲DS3231的寄存器定義,時間變量結構體定義,等等。
softwareIIC.h即存儲相關IIC的函數定義,軟件 IIC 占用的 IO口定義,等等。
在使用上,只需要在DS3231.c中 #include "softwareIIC.h"即可。
同理,因為我們將模塊和 IIC 協議進行了分離, 在項目中需要加到其他使用IIC協議的模塊時,也只需要在對應模塊的文件中 調用軟件IIC的頭文件 即可。
那么在之后其他項目也可以使用這個時候寫好的 軟件IIC 庫, 只需要改改softwareIIC.h 里對于占用端口的宏定義即可, 十分方便代碼的移植😉👍。
若你是初學者(雖然我也是),希望你能養成這樣的一個模式、習慣,這對之后的幫助異常的大。
關于DS3231
DS3231是低成本、高精度的I2C實時時鐘(RTC)。該器件包括電池輸入端,斷開主電源時,仍可保持精準計時。
RTC保存秒、分、時、星期、月和年信息。少于31天的月份,將自動調整月末的日期,包括閏年修正。時鐘格式可以時24小時或帶AM/PM的12小時格式。提供兩個可設置的日歷鬧鐘和1Hz輸出。
在這里我用的是在某寶上購買的模塊,已經預留出了VCC、GND、SDA、SCL。
DS3231 寄存器設置
第一眼看上去可能有點不明所以,但其實很簡單。DS3231采用8421BCD碼用來存儲時間、日期等數據。(什么是8421BCD碼? 你可以簡單理解為,一般用四位二進制表示一位數字,比如十進制數字58,對應的8421BCD碼即:0101 1000 , 這里1000代表個位數的8 , 0101代表十位數的 5 。)
比如Seconds(秒),寄存器地址是0x00,我們需要讀取和設置 秒 ,也只需要對這個寄存器進行讀寫即可。
秒范圍是 00 - 59,故第四位用來存儲個位數0 - 9,高三位用來存儲十位數 0 - 5 ( 000 - 101)。
其他寄存器同理。
另外關于采用 24小時制 還是 12小時制度,取決于Hours寄存器(0x02)中的第6位。
代碼
歐克,關于IIC 借助我上一篇博客相信已經有所掌握,現在我們直接將其使用在和DS3231的通訊和設置中。
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 //上面已經定義過了 #define IIC_SCL_RCCPeriphClockcmd RCC_APB2PeriphClockCmd#define IIC_SCLSDA_GPIO_RCC RCC_APB2Periph_GPIOA //使用4線串行接口時使用 #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); //產生起始條件(符) void IIC_Stop(void); //產生結束條件(符) 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;//開啟時鐘IIC_SDA_RCCPeriphClockcmd(IIC_SDA_GPIO_RCC, ENABLE); //or IIC_SCL_GPIOx ,因為 sda scl 使用的是同一個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) //產生起始條件(符) {SDA_OUT();IIC_SDA=1; //先確保 SDA為高IIC_SCL=1; //拉高SCLSysTick_Delay_us(4);IIC_SDA=0; //產生下降沿SysTick_Delay_us(4);IIC_SCL=0; //將時鐘線拉低,只有時鐘線拉低才運行SDA數據變化有效SysTick_Delay_us(4); }void IIC_Stop(void) //產生結束條件(符) {SDA_OUT();IIC_SCL = 0;IIC_SDA=0; //先讓SDA 為低電平IIC_SCL=1; //將SCL拉高SysTick_Delay_us(4); // 延時,保證時長IIC_SDA=1; //SDA拉高 產生上升沿SysTick_Delay_us(4); // 結束后釋放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; //釋放數據總線,交由從機控制SysTick_Delay_us(4);IIC_SCL = 1;SysTick_Delay_us(1);while (GPIO_ReadInputDataBit(IIC_SDA_GPIOx,IIC_SDA_GPIO_Pin)) //讀到 0 ,即接收到ACK,循環跳出{tempTime++;if(tempTime > 300){IIC_Stop();return 1; //超時返回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; } //主機不產生應答信號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個字節,ack=1時,發送ACK,ack=0,發送nACK u8 IIC_Read_Byte(unsigned char ack) {unsigned char i,receive=0;SDA_IN();//SDA設置為輸入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();//發送nACKelseIIC_Ack(); //發送ACK return receive; }上面是軟件IIC的代碼,是對于IIC總線的操作,不具體針對某一個模塊,可移植性高。
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(); //產生起始位IIC_WriteByte(DS3231_ADDRESS_Write); //發送從機地址(寫模式)if(IIC_Wait_Ask()) //等待響應return 1;IIC_WriteByte(ReadAddr); //發送寄存器地址if(IIC_Wait_Ask()) //等待響應return 2;IIC_Start(); //重復起始位IIC_WriteByte(DS3231_ADDRESS_Read); //發送從機地址(讀模式)if(IIC_Wait_Ask()) //等待響應return 3;data = IIC_Read_Byte(0); //讀取數據,參數設為0 --- NACK*Receive = data; //將結果賦值給接收位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; }總結不易,若對你有幫助,希望點贊收藏是😉👍
總結
以上是生活随笔為你收集整理的DS3231时钟模块使用,IIC协议实践。(基于STM32)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JMeter场景设置与监控
- 下一篇: Uva 1625 - Color Len