STM32F0xx驱动DS1302时钟芯片
admin 于 2018年03月18日 发表在 嵌入式开发笔记
本文介绍,STM32F0xx系列单片机配置DS1302,以及通过时间戳校准实时时钟的方法。关于DS1302芯片可查看之前的博文《DS1302探讨》或者官方手册(点击下载附件)
1. 相关读写寄存器列表
根据列表可知,秒、分、时、日、月、星期、年分别对应的操作地址,以及含有写保护功能位的0x8E寄存器,只需要通过相关的时序进行读写配置即可。
2. 单字节读时序
注意:尽管DS1302读写和IIC时序相似,但仍有一些不同。
3. 单字节写时序
4. STM32F0xx驱动源码
#include "stm32f0xx.h" #include "delay.h" #include "time.h" /* DS1302对应管脚数据: //RTC_RST --> PB13 //SCLK --> PB15 //DATA --> PB14 */ //存放DS1302的时钟 typedef struct _time { uint8_t year; uint8_t month; uint8_t week; uint8_t day; uint8_t hour; uint8_t minute; uint8_t second; } DS1302Time; DS1302Time SysTIME = {0}; //显示时间的结构体1 DS1302Time OldSysTIME = {0}; //显示时间的结构体2 //DS1302寄存器 static uint8_t read_addr[]= {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; //读的地址 static uint8_t write_addr[]= {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; //写的地址 //系统延迟 void static Delay(uint32_t nbrOfUs) { while(nbrOfUs--) { delay_us(5); //5us } } //DS1302初始化 void DS1302PinInit(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //使能GPIOA的时钟 //对应管脚初始化 PB13,PB15 输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //注意:DS1302数据管脚设置为开漏输出,因此硬件电路必须上拉到3.3V GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } //配置DATA为输入模式 -> PB14 static void DS1302_DAT_INPUT() { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //使能GPIOB的时钟 //对应管脚初始化 PB14输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } //配置DATA为输出模式 static void DS1302_DAT_OUTPUT() { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //使能GPIOB的时钟 //对应管脚初始化 PB14 输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } //配置DS1302的DATA管脚电平 -> PB14 static void DS1302_DAT(uint8_t status) { if(status==1) { GPIO_SetBits(GPIOB, GPIO_Pin_14); //data为高 } else if(status==0) { GPIO_ResetBits(GPIOB, GPIO_Pin_14); //data为低 } else {} } //读取DS1302的数据输入电平 -> PB14 static uint8_t DS1302_DAT_READ() { return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14); //读取DS1302数据 } //配置DS1302的SCLK管脚电平 -> PB13 static void DS1302_CE(uint8_t status) { if(status==1) { GPIO_SetBits(GPIOB, GPIO_Pin_13); //CS为高 } else if(status==0) { GPIO_ResetBits(GPIOB, GPIO_Pin_13); //CS为低 } else {} } //配置DS1302的SCLK管脚电平 -> PB15 static void DS1302_SCLK(uint8_t status) { if(status==1) { GPIO_SetBits(GPIOB, GPIO_Pin_15); //SCLK为高 } else if(status==0) { GPIO_ResetBits(GPIOB, GPIO_Pin_15); //SCLK为低 } else {} } //写一个字节 //数据和地址都是从最低位开始传输 static void ds1302_writebyte(uint8_t byte_1) { uint8_t i = 0; for(i = 0x01; i; i<<=1) { if(byte_1 & i) { DS1302_DAT(1); } else { DS1302_DAT(0); } Delay(2); DS1302_SCLK(1); Delay(2); DS1302_SCLK(0); //上升沿写入 } } //特定地址写入数据 static void ds1302_writedata(uint8_t addr,uint8_t data) { DS1302_SCLK(0); Delay(2); DS1302_CE(0); Delay(2); DS1302_CE(1); //使能片选信号,高电平选中芯片 Delay(2); DS1302_DAT_OUTPUT(); //DS1302 data管脚配置为输出 ds1302_writebyte(addr); //写地址寄存器 ds1302_writebyte(data); //写对应数据 Delay(2); //传送数据结束,失能片选 DS1302_CE(0); //取消选中 Delay(2); //传送数据结束,失能片选 } //读取一个字节,上升沿读取 static uint8_t ds1302_readbyte(void) { uint8_t i = 0; uint8_t data = 0; for(i=0x01; i; i <<=1 ) //这里发现设为8的话输出数据不对 { if(DS1302_DAT_READ()) { data |= i; //低位在前,逐位读取 } Delay(2); DS1302_SCLK(1); Delay(2); DS1302_SCLK(0); } return data; } //读取寄存器的值 static uint8_t ds1302_readdata(uint8_t addr) { uint8_t data = 0; DS1302_CE(0); Delay(2); DS1302_SCLK(0); Delay(2); DS1302_CE(1); //读写操作时CE必须为高有效 Delay(2); DS1302_DAT_OUTPUT(); //DS1302 data管脚配置为输出 ds1302_writebyte(addr); //读时间的命令 DS1302_DAT_INPUT(); //配置Data为输入模式 data = ds1302_readbyte(); //读取数据 Delay(2); DS1302_DAT_OUTPUT(); //DS1302 data管脚配置为输出 DS1302_CE(0); Delay(1); DS1302_SCLK(1); Delay(1); DS1302_DAT(0); //拉低数据 Delay(1); return data; } //DS1302初始化数据 void DS1302_Init(void) { uint8_t i = 0; //初始化时间:秒 分 时 日 月 星期 年;2018-3-12 12:00:00 uint8_t datetime[7] = {00,00,12,12,3,1,18}; DS1302PinInit(); //DS1302控制管脚初始化 i = ds1302_readdata(0x8e); //读取秒寄存器, if((i & 0x80) != 0) //通过判断秒寄存器是否还有数据来决定下次上电的时候是否初始化时间,就是掉电保护 { ds1302_writedata(0x8e,0x00); //撤销写保护,允许写入数据(0x8e,0x00) for(i = 0; i<7; i++) { ds1302_writedata(write_addr[i],(datetime[i]/10)<<4 | (datetime[i]%10)); } } ds1302_writedata(0x8e,0x80);//打开写保护功能,防止干扰造成的数据写入 } //读取芯片时间 //存储在全局变量SysTIME中 void DS1302Readtime(void) { uint8_t i; uint8_t time[7]; //读取数据 for(i = 0; i<7; i++) { time[i] = ds1302_readdata(read_addr[i]); } //BCD码转换ASCII码 SysTIME.year = ((time[6]&0x70)>>4)*10 + (time[6]&0x0f); //高三位加低四位 SysTIME.month = ((time[4]&0x70)>>4)*10 + (time[4]&0x0f); SysTIME.day = ((time[3]&0x70)>>4)*10 + (time[3]&0x0f); SysTIME.week = ((time[5]&0x70)>>4)*10 + (time[5]&0x0f); SysTIME.hour = ((time[2]&0x70)>>4)*10 + (time[2]&0x0f); SysTIME.minute = ((time[1]&0x70)>>4)*10 + (time[1]&0x0f); SysTIME.second = ((time[0]&0x70)>>4)*10 + (time[0]&0x0f); //如果系统使用的UTC时钟,则根据处在的时区进行处理,如:东八区。 //如果校准时,已根据时区配置好事件戳,则此处不调用处理。 if(SysTIME.hour<16) { SysTIME.hour = SysTIME.hour+8; } else { SysTIME.hour = SysTIME.hour-16; } } //调用time.h中函数实现DS1302时钟的设置,对应使用时间戳设置时钟非常有用 //timestamp 必须以S为单位 void CalibrateDS1302Time(uint32_t timestamp) { struct tm * curTime; //系统时间定义 uint8_t datetime[7]= {0}; uint8_t i = 0; //注意:如果是非标准UTC时间戳,建议转换为对应时区timestamp再写入 curTime = localtime(×tamp); //更新标准时间 datetime[0] = curTime ->tm_sec; datetime[1] = curTime ->tm_min; datetime[2] = curTime ->tm_hour; datetime[3] = curTime->tm_mday; datetime[4] = curTime->tm_mon; datetime[5] = curTime ->tm_wday; datetime[6] = curTime->tm_year; //写入新时间 datetime[6] = datetime[6]%100; //更新日期 ds1302_writedata(0x8e,0x00); //撤销写保护,允许写入数据(0x8e,0x00) for(i = 0; i<7; i++) { ds1302_writedata(write_addr[i],(datetime[i]/10)<<4 | (datetime[i]%10)); } ds1302_writedata(0x8e,0x80);//打开写保护功能,防止干扰造成的数据写入。 }