IIC总线设计⑥——时钟模块DS1302
目錄
一、模塊介紹
(一)基本信息
(二)芯片信息及模塊原理圖
(1)芯片信息
(2)原理圖
(三)指令格式及寄存器介紹
(1)指令格式
(2)寄存器介紹
(四)單字節傳輸時序(非突發模式)
二、程序控制
(一)ds1302.h
(二)ds1302.c
(1)GPIO初始化及DSIO管腳模式更改
(2)向DS1302指定寄存器寫入一個字節數據
(3)從DS1302指定寄存器讀一個字節數據
(4)設置時間
(5)讀取時間,串口打印
(三)主函數
三、實驗結果
(一)24小時制
(二)12小時制
四、開發過程遇到的問題
(一)時序問題
(二)DSIO數據管腳的模式問題
程序下載:
????????32版本:https://pan.baidu.com/s/1kTgFImQ-TQM65AY-Gfns0w提取碼:9534
? ? ? ? 51版本:
一、模塊介紹
(一)基本信息
①DS1302實時時鐘能夠計算2100年之前的秒、分、日、日期、星期、月、年,還有閏年的調整能力。
②內部含有31個字節靜態RAM用于數據存儲。
③采用串行數據傳輸方式,簡單的SPI 3線接口。
④工作電壓范圍:2.0~5.5V。
⑤工作電流:2.0V時,小于300nA。
⑥單字節或多字節(突發模式)讀取或寫入內存。
⑦采用主電源和備份電源雙電源供應。備份電源可用電池供電。
⑧兼容DS1202?
DS1302 芯片包含一個實時時鐘/日歷和 31 字節的靜態內存。它通過一個簡單的串行接口與微處理器通信。實時時鐘/日歷提供秒、分鐘、小時、星期、日期、月份和年份信息。自動調整少于 31 天的月份,包括閏年的更正。時鐘帶 AM/PM 指示器的 24 小時或 12 小時格式。使用同步串行通信簡化了 DS1302 與微處理器的連接。與時鐘/RAM 通信只需要三根線:(1) RST(復位),(2) I/O(數據線),(3) SCLK(串行時鐘)。數據可以一次從RAM傳輸1個字節或最多 31 個字節,DS1302 旨在以極低功耗運行并保留數據和時鐘信息。
(二)芯片信息及模塊原理圖
(1)芯片信息
| X1,X2 | 接32.768 kHz晶振 |
| GND | 電源地 |
| RST | 復位引腳,低電平有效 |
| I/O | 數據輸入或輸出 |
| SCLK | 串行時鐘 |
| Vcc1,Vcc2 | Vcc1:備份電源,Vcc2:主要電源 |
RST管腳功能:
①打開控制邏輯,允許讀寫寄存器。
②可終止正在傳輸的數據。
(2)原理圖
(三)指令格式及寄存器介紹
(1)指令格式
?Bit7:寫保護位,為1時,芯片可寫入數據;為0時,芯片不可寫入數據。
?Bit6:為0時,指定對時鐘/日歷寄存器控制讀寫操作;為1時,RAM 區數據的控制讀寫操。
?Bit1~?Bit5:指定需要輸入或輸出的寄存器
?Bit0:讀寫控制,為0時,選擇寫,數據方向為輸入;為1時,選擇讀,數據方向為輸出。
(2)寄存器介紹
寄存器或RAM的地址及功能參考下圖:
①?時鐘/日歷寄存器:上圖第0~7個寄存器。
SEC寄存器:秒寄存器的位7被定義為時鐘停止標志。當這個位被設置為邏輯1時,時鐘振蕩器停止,DS1302進入低功率待機模式,電流漏極小于100納安。當此位被寫入邏輯0時,時鐘將啟動。Bit4~Bit6存放秒數的十位,Bit0~Bit3存放秒數的個位。
HR寄存器:小時寄存器中的第7位被定義為12小時或24小時模式的選擇位。當這個位被設置為邏輯1時,將選擇12小時模式。在12小時模式下,Bit5是AM/PM位,邏輯高為PM,Bit0~Bit3存放小時數的個位,Bit5存放小時數的十位。在24小時模式下,Bit5是第二個10小時位(20-23小時),Bit0~Bit4存放0~19小時。
②CONTROL寄存器:控制寄存器,用作控制是否寫保護。
③TRICKLE CHARGER
該寄存器控制著DS1302的涓流電荷特性。如下圖簡化原理圖顯示了滴流充電器的基本組件。
涓流充電器選擇(TCS)位(位4-7)控制涓流充電器的選擇。為了防止意外啟用,只有1010的模式將啟用涓流充電器。所有其他模式將禁用涓流充電器。DS1302開機時,禁用滴流充電器。二極管選擇(DS)位(位2-3)選擇在VCC2和VCC1之間是連接一個二極管還是兩個二極管。如果DS為01,則選擇一個二極管,或者如果DS為10,則選擇兩個二極管。如果DS為00或11,則涓流充電器將獨立于TCS而被禁用。RS位(位0-1)選擇連接在VCC2和VCC1之間的電阻器。電阻器選擇(RS)位所選擇的電阻器如下:
?如果RS為00,則將獨立于TCS禁用涓流充電器
?④CLOCK BURST或者RAM BURST寄存器
通過上圖的CLOCK BURST或者RAM BURST兩個寄存器來設置時鐘寄存器的突發模式或者RAM寄存器的突發模式,當以突發模式寫入時鐘寄存器時,必須寫入前八個寄存器,以便傳輸數據。但是,在以突發模式寫入RAM時,不需要寫入所有31個字節來傳輸數據。
(四)單字節傳輸時序(非突發模式)
?
?都是納秒級的,寫程序的時候我們微妙級延時就ok了。
二、程序控制
(一)ds1302.h
#ifndef __DS1302_H #define __DS1302_H#include "stm32f10x.h" #include "usart.h" #include "delay.h"#define DAY_AM 0x80 #define DAY_PM 0xa0 /* 程序修改說明:①若使用12小時制,請開啟宏#define HOUR_12,關閉宏#define HOUR_24修改時間:需要注意的 DAY_AM:上午,DAY_PM:下午 0x11:11點#define HOUR DAY_AM|0x11 //時,上午 | 11點#define HOUR DAY_PM|0x11 //時,下午 | 11點若需要設置的時間為上午,請開啟宏#define HOUR DAY_AM|0x11,關閉宏#define HOUR DAY_PM|0x11若需要設置的時間為下午,請開啟宏#define HOUR DAY_PM|0x11,關閉#define HOUR DAY_AM|0x11②若使用24小時制,請開啟宏#define HOUR_24,關閉宏#define HOUR_12程序編寫:曾老師的熊同學完成時間:2022年-4月-26日程序功能:默認以24小時制讀取時間數據 *///時間模式:12小時制、24小時制 //#define HOUR_12 #define HOUR_24//新時間:2022年-4月-26日-23時-59分-55秒-星期2 //---存儲格式是用BCD碼---// #define SEC 0x55 //秒,55秒 #define MIN 0X59 //分,59分#ifdef HOUR_12 //12小時制#define HOUR DAY_AM|0x11 //時,上午 | 11點 // #define HOUR DAY_PM|0x11 //時,下午 | 11點 #endif#ifdef HOUR_24 #define HOUR 0X23 //時,24小時制,23點 #endif#define DATE 0X26 //日,26日 #define MONTH 0X04 //月,4月 #define DAY 0X02 //星期,星期2 #define YEAR 0X22 //年,2022年/* 定義SPI總線連接的GPIO端口*/ #define DS1302_GPIO_PORT GPIOB /* GPIO端口 */ #define DS1302_RCC_PORT RCC_APB2Periph_GPIOB /* GPIO端口時鐘 */ #define DS1302_DSIO_PIN GPIO_Pin_5 /* 連接到DSIO時鐘線的GPIO */ #define DS1302_RST_PIN GPIO_Pin_6 /* 連接到RST數據線的GPIO */ #define DS1302_SCLK_PIN GPIO_Pin_7 /* 連接到SCLK數據線的GPIO */#define DS1302_DSIO_1() GPIO_SetBits(DS1302_GPIO_PORT, DS1302_DSIO_PIN) /* DSIO = 1 */ #define DS1302_DSIO_0() GPIO_ResetBits(DS1302_GPIO_PORT, DS1302_DSIO_PIN) /* DSIO = 0 */#define DS1302_RST_1() GPIO_SetBits(DS1302_GPIO_PORT, DS1302_RST_PIN) /* RST = 1 */ #define DS1302_RST_0() GPIO_ResetBits(DS1302_GPIO_PORT, DS1302_RST_PIN) /* RST = 0 */#define DS1302_SCLK_1() GPIO_SetBits(DS1302_GPIO_PORT, DS1302_SCLK_PIN) /* SCLK = 1 */ #define DS1302_SCLK_0() GPIO_ResetBits(DS1302_GPIO_PORT, DS1302_SCLK_PIN) /* SCLK = 0 */#define DS1302_DSIO_READ() GPIO_ReadInputDataBit(DS1302_GPIO_PORT, DS1302_DSIO_PIN) /* 讀DSIO口線狀態 *///時鐘寄存器地址 #define SEC_REGISTER_ADDR 0x80 #define MIN_REGISTER_ADDR 0x82 #define HR_REGISTER_ADDR 0x84 #define DATE_REGISTER_ADDR 0x86 #define MONTH_REGISTER_ADDR 0x88 #define DAY_REGISTER_ADDR 0x8a #define YEAR_REGISTER_ADDR 0x8c #define CONTROL_REGISTER_ADDR 0x8e//存儲時間數據 typedef struct {uint8_t sec; //秒uint8_t min; //分uint8_t hour; //時uint8_t date; //日uint8_t mouth; //月uint8_t week_day; //星期uint16_t year; //年 }DS1302_Data_TypeDef; extern DS1302_Data_TypeDef DS1302_Source_Data;//源數據 extern DS1302_Data_TypeDef DS1302_Convert_Data;//轉換后的數據void DS1302_GPIO_Config(void); void DS1302_Set_Time(void); void DS1302_Read_Time(void); #endif /* __DS1302_H */(二)ds1302.c
(1)GPIO初始化及DSIO管腳模式更改
void DS1302_GPIO_Config(void) {GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(DS1302_RCC_PORT,ENABLE);//使能B端口時鐘GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//推挽輸出GPIO_InitStruct.GPIO_Pin = DS1302_DSIO_PIN|DS1302_RST_PIN|DS1302_SCLK_PIN;//選擇引腳GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHzGPIO_Init(DS1302_GPIO_PORT,&GPIO_InitStruct);GPIO_SetBits(DS1302_GPIO_PORT,DS1302_DSIO_PIN|DS1302_RST_PIN|DS1302_SCLK_PIN);//初始化所有引腳為高電平 }/** 函數名:DS1302_Mode_IPU* 描述 :使DS1302_DSIO_PIN引腳變為上拉輸入模式* 輸入 :無* 輸出 :無*/ static void DS1302_Mode_IPU(void) {GPIO_InitTypeDef GPIO_InitStructure;/*選擇要控制的DS1302_PORT引腳*/ GPIO_InitStructure.GPIO_Pin = DS1302_DSIO_PIN;/*設置引腳模式為上拉輸入模式*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; /*調用庫函數,初始化DS1302_PORT*/GPIO_Init(DS1302_GPIO_PORT, &GPIO_InitStructure); }/** 函數名:DS1302_Mode_Out_PP* 描述 :使DS1302_DSIO_PIN引腳變為推挽輸出模式* 輸入 :無* 輸出 :無*/ static void DS1302_Mode_Out_PP(void) {GPIO_InitTypeDef GPIO_InitStructure;/*選擇要控制的DS1302_PORT引腳*/ GPIO_InitStructure.GPIO_Pin = DS1302_DSIO_PIN; /*設置引腳模式為通用推挽輸出*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*設置引腳速率為50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/*調用庫函數,初始化DS1302_PORT*/GPIO_Init(DS1302_GPIO_PORT, &GPIO_InitStructure); }(2)向DS1302指定寄存器寫入一個字節數據
/* ********************************************************************************************************* * 函 數 名: DS1302_Write_Byte * 功能說明: 向DS1302寫入一個字節數據 * 形 參:addr:寄存器地址 dat:寫入的數據 * 返 回 值: 無 ********************************************************************************************************* */ static void DS1302_Write_Byte(uint8_t addr,uint8_t dat) {uint8_t i;DS1302_RST_0();//復位管腳拉低DS1302_SCLK_0();//SCLK置低電平delay_us(10);DS1302_RST_1(); //然后將RST(CE)置高電平。delay_us(10);for (i=0; i<8; i++){DS1302_SCLK_0();//數據在上升沿時,DS1302寫入數據delay_us(10);if(addr & 0x01)DS1302_DSIO_1();//數據從低位開始傳送 else DS1302_DSIO_0(); addr >>= 1; DS1302_SCLK_1();//數據在上升沿時,DS1302讀取數據delay_us(10);}for (i=0; i<8; i++){DS1302_SCLK_0();//數據在上升沿時,DS1302寫入數據delay_us(10);if(dat & 0x01)DS1302_DSIO_1();//數據從低位開始傳送 else DS1302_DSIO_0(); dat >>= 1; DS1302_SCLK_1();//數據在上升沿時,DS1302讀取數據delay_us(10);}DS1302_SCLK_0();//SCLK置低電平delay_us(10);DS1302_RST_0();//復位管腳拉低,傳送數據結束delay_us(10); }(3)從DS1302指定寄存器讀一個字節數據
/******************************************************************************* * 函 數 名 : DS1302_Read_Byte * 函數功能 :讀取一個地址的數據 * 輸 入 : addr:寄存器地址 * 輸 出 : dat:讀取的數據 *******************************************************************************/ static uint8_t DS1302_Read_Byte(uint8_t addr) {uint8_t i=0,dat=0,temp=0;DS1302_RST_0();//復位管腳拉低delay_us(10);DS1302_SCLK_0();//SCLK置低電平delay_us(10);DS1302_RST_1(); //然后將RST(CE)置高電平。delay_us(10);for (i=0; i<8; i++){DS1302_SCLK_0();//數據在上升沿時,DS1302讀取數據delay_us(10);if(addr & 0x01)DS1302_DSIO_1();//數據從低位開始傳送 else DS1302_DSIO_0(); addr >>= 1; DS1302_SCLK_1();//數據在上升沿時,DS1302寫入數據delay_us(10);}DS1302_Mode_IPU();//將DS1302_DSIO管腳設置為上拉輸入模式delay_us(10);for (i=0; i<8; i++){ DS1302_SCLK_1();//在下降沿時,DS1302輸出的數據delay_us(10); DS1302_SCLK_0();//在下降沿時,DS1302輸出的數據delay_us(10);temp = DS1302_DSIO_READ();//讀取端口電平,從最低位開始接收 dat = (dat>>1) | (temp<<7); }DS1302_Mode_Out_PP();//將DS1302_DSIO管腳設置為推挽輸出模式DS1302_RST_0();//復位管腳拉低,傳送數據結束.DS1302_SCLK_0();DS1302_DSIO_0();delay_us(10);return dat; }(4)設置時間
/******************************************************************************* * 函 數 名 : DS1302_Set_Time * 函數功能 : 設置DS1302時間 * 輸 入 : 無 * 輸 出 : 無 *******************************************************************************/ void DS1302_Set_Time(void) {DS1302_Write_Byte(CONTROL_REGISTER_ADDR,0X00); //禁止寫保護,就是關閉寫保護功能DS1302_Write_Byte(SEC_REGISTER_ADDR,SEC); //設置秒DS1302_Write_Byte(MIN_REGISTER_ADDR,MIN); //設置分DS1302_Write_Byte(HR_REGISTER_ADDR,HOUR); //設置時DS1302_Write_Byte(DATE_REGISTER_ADDR,DATE); //設置日DS1302_Write_Byte(MONTH_REGISTER_ADDR,MONTH); //設置月DS1302_Write_Byte(DAY_REGISTER_ADDR,DAY); //設置星期DS1302_Write_Byte(YEAR_REGISTER_ADDR,YEAR); //設置年DS1302_Write_Byte(CONTROL_REGISTER_ADDR,0x80); //打開寫保護功能printf("DS1302 Set Time Success!"); }(5)讀取時間,串口打印
/******************************************************************************* * 函 數 名 : DS1302_Read_Time * 函數功能 : 讀取時鐘信息 * 輸 入 : 無 * 輸 出 : 無 *******************************************************************************/ void DS1302_Read_Time(void) {DS1302_Source_Data.sec =DS1302_Read_Byte(SEC_REGISTER_ADDR|0x01); //秒DS1302_Source_Data.min =DS1302_Read_Byte(MIN_REGISTER_ADDR|0x01); //分DS1302_Source_Data.hour =DS1302_Read_Byte(HR_REGISTER_ADDR|0x01); //時DS1302_Source_Data.date =DS1302_Read_Byte(DATE_REGISTER_ADDR|0x01); //日DS1302_Source_Data.mouth =DS1302_Read_Byte(MONTH_REGISTER_ADDR|0x01); //月DS1302_Source_Data.week_day =DS1302_Read_Byte(DAY_REGISTER_ADDR|0x01); //星期DS1302_Source_Data.year = (uint16_t)DS1302_Read_Byte(YEAR_REGISTER_ADDR|0x01); //年DS1302_Convert_Data.sec = ((DS1302_Source_Data.sec&0X70)>>4)*10+(DS1302_Source_Data.sec&0x0f); //秒DS1302_Convert_Data.min = ((DS1302_Source_Data.min&0X70)>>4)*10+(DS1302_Source_Data.min&0x0f); //分#ifdef HOUR_24 DS1302_Convert_Data.hour = ((DS1302_Source_Data.hour&0X20)>>5)*20+(DS1302_Source_Data.hour&0x1f); //24小時制 #endif#ifdef HOUR_12 DS1302_Convert_Data.hour = ((DS1302_Source_Data.hour&0X10)>>4)*10+(DS1302_Source_Data.hour&0x0f); //12小時制 #endifDS1302_Convert_Data.date = ((DS1302_Source_Data.date&0X70)>>4)*10+(DS1302_Source_Data.date&0x0f); //日DS1302_Convert_Data.mouth = ((DS1302_Source_Data.mouth&0X70)>>4)*10+(DS1302_Source_Data.mouth&0x0f); //月DS1302_Convert_Data.week_day = (DS1302_Source_Data.week_day&0x0f); //星期DS1302_Convert_Data.year = ((DS1302_Source_Data.year&0X70)>>4)*10+(DS1302_Source_Data.year&0x0f)+2000; //年#ifdef HOUR_24 printf("The time is: %d年 - %d月 - %d日 - %d時 - %d分 - %d秒 ,星期%d \r\n",DS1302_Convert_Data.year,\DS1302_Convert_Data.mouth,\DS1302_Convert_Data.date,\DS1302_Convert_Data.hour,\DS1302_Convert_Data.min,\DS1302_Convert_Data.sec,\DS1302_Convert_Data.week_day); #endif #ifdef HOUR_12if(DS1302_Source_Data.hour&0x20)printf("The time is: %d年 - %d月 - %d日 - PM: %d時 - %d分 - %d秒 ,星期%d \r\n", DS1302_Convert_Data.year,\DS1302_Convert_Data.mouth,\DS1302_Convert_Data.date,\DS1302_Convert_Data.hour,\DS1302_Convert_Data.min,\DS1302_Convert_Data.sec,\DS1302_Convert_Data.week_day);elseprintf("The time is: %d年 - %d月 - %d日 - AM: %d時 - %d分 - %d秒 ,星期%d \r\n", DS1302_Convert_Data.year,\DS1302_Convert_Data.mouth,\DS1302_Convert_Data.date,\DS1302_Convert_Data.hour,\DS1302_Convert_Data.min,\DS1302_Convert_Data.sec,\DS1302_Convert_Data.week_day); #endif}(三)主函數
void Init(void) {USART_Config();//串口初始化DS1302_GPIO_Config(); }int main(void) {Init();//DS1302_Set_Time();//設置時間,當完成時間設置后,請注釋掉這一句,否則單片機每次上電或復位都會重新設置一下時間。while(1){DS1302_Read_Time();//讀取時間delay_ms(50);} }三、實驗結果
(一)24小時制
(二)12小時制
四、開發過程遇到的問題
(一)時序問題
讀數據時,在時鐘的下降沿時DS1302輸出數據
?寫數據時,或者寫地址時,在時鐘的上升沿時DS1302讀取數據
(二)DSIO數據管腳的模式問題
在讀取數據時,需要先發送寄存器的地址,發送完成后,此時需要將DSIO的推挽輸出模式更改為(上拉輸入、下拉輸入、浮空輸入任何一種,經檢測3種模式都可以正常讀取數據),數據讀取完成后,需要將DSIO更改為原來的推挽輸出模式,避免影響下一次寫地址。
?以上就是DS1302的所有內容了,感謝大家閱讀,文中若有錯誤,麻煩大家指出!
總結
以上是生活随笔為你收集整理的IIC总线设计⑥——时钟模块DS1302的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ant Design Form.Item
- 下一篇: 【转载】什么是C++虚函数、虚函数的作用