基于STM32的DS1302时钟模块驱动程序

您所在的位置:网站首页 电子时钟ds1302实验报告 基于STM32的DS1302时钟模块驱动程序

基于STM32的DS1302时钟模块驱动程序

2024-07-15 01:29| 来源: 网络整理| 查看: 265

目录

1.项目概述

2.DS1032的数据手册解析

   2.1 DS1302的引脚介绍

   2.2 DS1302的通讯协议及时序

   2.3 DS1302的相关寄存器

3.程序代码及其注释

4.结果演示

5.附录:7针0.96寸OLED屏驱动代码(SPI驱动)

1.项目概述 

本程序采用的主控芯片为STM32F103RCT6,通过主控芯片驱动DS1302时钟模块,并将其实时时间显示在7针0.96寸OLED屏上。使用STM32的普通IO口模拟DS1302的通信时序,使用STM32的SPI外设驱动OLED屏。下面从DS1302的数据手册开始完成整个项目。

2.DS1302数据手册解析 2.1DS1302引脚介绍

VCC1,VCC2是电源引脚,VCC1是主供电引脚,VCC2接备用电池,当主供电电源电量不足或者断电时,备用电池会通过VCC2及时供电,保证时钟模块在主供电引脚断电后任然会正常计时。GND是地引脚。X1,X2是有关晶振的引脚,不做深究。

CE引脚是输入引脚,在单片机从DS1302读取数据或者向其写入数据时,CE引脚必须配置为高电平。在芯片内部连接有40K下拉电阻。I/O引脚充当双向数据引脚,即数据的发送和接收都在这条线上完成。SCLK是同步时钟引脚,控制I/O引脚上数据的接收和发送。

2.2 DS1302的通讯协议及时序

指令字节启动每次的数据传输,上图说明了指令字节的构造。①位7必须为逻辑1,位7为逻辑0时指令会失效。②我们使用的不是RAM当中的寄存器及数据,故位6应该为逻辑0。③位1到位5为寄存器地址。④位0为逻辑0时表明要往指定寄存器里面写数据,为逻辑1时要从指定寄存器里面读出数据。指令字节的传输总是从位0(LSB)开始传输。

上图是ds1302通信时序图。

1.CE和时钟控制。

将CE置高将开启数据传输,CE输入提供俩个功能,首先CE开启了通信数据进入移位寄存器的通路,其次CE提供了一个可以终止单个字节或者多个字节的数据传输。

对于ds1302接收数据来说,每当时钟的上升沿时采集由单片机发送的数据位;对于ds1302发送数据来说,每当时钟的下降沿时便向外发送一位数据。如果CE置低,所有数据传输被终止I/O端口变为高阻态。需要注意的是,当CE被从0置为1时,时钟线必须为逻辑0。

2.向ds1302指定寄存器写一字节数据

先将CE拉高开启数据传输通道,之后通过16个时钟周期,在每个时钟上升沿由低位到高位地传输数据位,前8个时钟周期传输写指令字节,后八个时钟周期传输要写入寄存器的内容字节,最后将时钟线、CE拉低,完成数据传输。

3.从ds1302指定寄存器读一字节数据

先将CE拉高开启数据传输通道,之后在前8个时钟周期的每个上升沿由低位到高位传输读指令字节,从第8个时钟周期的下降沿开始,在每个时钟周期的下降沿ds1302由低位到高位地传输指定寄存器的内容字节,内容字节传输完毕后,最后将时钟线、CE拉低,完成数据读取。

4.这显然不是SPI通讯或者IIC通讯,固要拿STM32的GPIO端口模拟通信。

2.3DS1302的相关寄存器

第一列指读取相应寄存器的读指令字节,第二列指写相应寄存器的写指令字节。时间和日历可以通过读取相对应的寄存器的值得到,同时时间和日历还可以通过向对应的寄存器写数据来设置或初始化。时间和日历寄存器内容的存储形式为BCD码。

秒寄存器:bit3-bit0为秒的个位,bit4-bit6为秒的十位,均为BCD码存储形式。bit7CH(the clock halt flag)即时钟停止标志位,bit7置1时,时钟振荡器停止,不再计时;置0时,时钟振荡器起振,开始计时。

分寄存器:bit3-bit0为分的个位,bit4-bit6为分的十位。

时寄存器:bit7为高选择12小时制,为低则选择24小时制。若24小时制,bit3-bit0为小时的个位,bit5-4为小时的十位。若12小时制,bit3-bit0为小时的个位,bit4为小时的十位,bit5为高则为PM,为低则为AM。

日期寄存器:bit3-bit0为日期的个位,bit5-bit4为日期的十位。

月寄存器:bit3-bit0为月的个位,bit4为月的十位。

星期寄存器:bit2-bit0即为星期几。

年寄存器:bit3-bit0为年的个位,bit5-bit4为年的十位。

写保护寄存器:当bit7为高时,会打开写保护,阻止向任何其它寄存器写数据;当bit7为低时,会关闭写保护,这时便可以向其它寄存器写数据。因此当我们想要配置寄存器时先要关闭写保护,写好之后再打开写保护,防止误操作。   

涓流充电寄存器:涓流充电在DS1302上电不做配置时是自动关闭的,不做深究。

3.程序代码及其注释

ds1302.h

#ifndef __ds1302_H #define __ds1302_H #include "sys.h" #define CE_L GPIO_ResetBits(GPIOC,GPIO_Pin_11)//拉低使能位 #define CE_H GPIO_SetBits(GPIOC,GPIO_Pin_11)//拉高使能位 #define SCLK_L GPIO_ResetBits(GPIOC,GPIO_Pin_12)//拉低时钟线 #define SCLK_H GPIO_SetBits(GPIOC,GPIO_Pin_12)//拉高时钟线 #define DATA_L GPIO_ResetBits(GPIOC,GPIO_Pin_10)//拉低数据线 #define DATA_H GPIO_SetBits(GPIOC,GPIO_Pin_10)//拉高数据线 struct TIMEData { u16 year; u8 month; u8 day; u8 hour; u8 minute; u8 second; u8 week; };//创建TIMEData结构体方便存储时间日期数据 extern struct TIMEData TimeData;//全局变量 void ds1302_gpio_init();//ds1302端口初始化 void ds1302_write_onebyte(u8 data);//向ds1302发送一字节数据 void ds1302_wirte_rig(u8 address,u8 data);//向指定寄存器写一字节数据 u8 ds1302_read_rig(u8 address);//从指定寄存器读一字节数据 void ds1032_init();//ds1302初始化函数 void ds1032_DATAOUT_init();//IO端口配置为输出 void ds1032_DATAINPUT_init();//IO端口配置为输入 void ds1032_read_time();//从ds1302读取实时时间(BCD码) void ds1032_read_realTime();//将BCD码转化为十进制数据 #endif

ds1302.c

#include "ds1302.h" #include "sys.h" #include "delay.h" struct TIMEData TimeData; u8 read_time[7]; void ds1302_gpio_init()//CE,SCLK端口初始化 { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PC.11 CE GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出 GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC.11 GPIO_ResetBits(GPIOC,GPIO_Pin_11); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PC.12 SCLK GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出 GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC.12 GPIO_ResetBits(GPIOC,GPIO_Pin_12); } void ds1032_DATAOUT_init()//配置双向I/O端口为输出态 { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PC.10 DATA GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC.10 GPIO_ResetBits(GPIOC,GPIO_Pin_10); } void ds1032_DATAINPUT_init()//配置双向I/O端口为输入态 { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PC.10 DATA GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC.10 } void ds1302_write_onebyte(u8 data)//向DS1302发送一字节数据 { ds1032_DATAOUT_init(); u8 count=0; SCLK_L; for(count=0;count>=1; } } void ds1302_wirte_rig(u8 address,u8 data)//向指定寄存器地址发送数据 { u8 temp1=address; u8 temp2=data; CE_L;SCLK_L;delay_us(1); CE_H;delay_us(2); ds1302_write_onebyte(temp1); ds1302_write_onebyte(temp2); CE_L;SCLK_L;delay_us(2); } u8 ds1302_read_rig(u8 address)//从指定地址读取一字节数据 { u8 temp3=address; u8 count=0; u8 return_data=0x00; CE_L;SCLK_L;delay_us(3); CE_H;delay_us(3); ds1302_write_onebyte(temp3); ds1032_DATAINPUT_init();//配置I/O口为输入 delay_us(2); for(count=0;count>=1; SCLK_H;delay_us(4);//使高电平持续一段时间 SCLK_L;delay_us(14);//延时14us后再去读取电压,更加准确 if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_10)) {return_data=return_data|0x80;} } delay_us(2); CE_L;DATA_L; return return_data; } void ds1032_init() { ds1302_wirte_rig(0x8e,0x00);//关闭写保护 ds1302_wirte_rig(0x80,0x37);//seconds37秒 ds1302_wirte_rig(0x82,0x58);//minutes58分 ds1302_wirte_rig(0x84,0x23);//hours23时 ds1302_wirte_rig(0x86,0x30);//date30日 ds1302_wirte_rig(0x88,0x09);//months9月 ds1302_wirte_rig(0x8a,0x07);//days星期日 ds1302_wirte_rig(0x8c,0x20);//year2020年 ds1302_wirte_rig(0x8e,0x80);//关闭写保护 } void ds1032_read_time() { read_time[0]=ds1302_read_rig(0x81);//读秒 read_time[1]=ds1302_read_rig(0x83);//读分 read_time[2]=ds1302_read_rig(0x85);//读时 read_time[3]=ds1302_read_rig(0x87);//读日 read_time[4]=ds1302_read_rig(0x89);//读月 read_time[5]=ds1302_read_rig(0x8B);//读星期 read_time[6]=ds1302_read_rig(0x8D);//读年 } void ds1032_read_realTime() { ds1032_read_time(); //BCD码转换为10进制 TimeData.second=(read_time[0]>>4)*10+(read_time[0]&0x0f); TimeData.minute=((read_time[1]>>4)&(0x07))*10+(read_time[1]&0x0f); TimeData.hour=(read_time[2]>>4)*10+(read_time[2]&0x0f); TimeData.day=(read_time[3]>>4)*10+(read_time[3]&0x0f); TimeData.month=(read_time[4]>>4)*10+(read_time[4]&0x0f); TimeData.week=read_time[5]; TimeData.year=(read_time[6]>>4)*10+(read_time[6]&0x0f)+2000; }

main.c

#include "delay.h" #include "led.h" #include "sys.h" #include "ds1302.h" #include "oled.h" int main(void) { u8 min=0; delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断分组 LED_Init();//LED灯初始化 ds1302_gpio_init();delay_ms(5); //初始化ds1302端口 ds1032_init();delay_ms(5); //ds1302初始化 ds1032_read_realTime(); //读取实时数据 min=TimeData.minute; //记录此时分钟值 OLED_Init(); //初始化OLED OLED_Clear(); //清屏OLED delay_ms(5); //在OLED屏上显示实时时刻 OLED_ShowString(65,4,":"); OLED_ShowNum(80,4,TimeData.minute,2,16); OLED_ShowNum(40,4,TimeData.hour,2,16); OLED_ShowNum(25,2,TimeData.year,4,16); OLED_ShowNum(75,2,TimeData.month,2,16); OLED_ShowNum(105,2,TimeData.day,2,16); OLED_ShowString(60,2,"-"); OLED_ShowString(95,2,"-"); while(1) { ds1032_read_realTime(); //读取此时时刻 if(min==TimeData.minute)//判断分钟数是否有更新 {delay_ms(5);//没有更新便延时5ms } else{min=TimeData.minute;//分钟数有更新,则将变量min更新 //分钟数更新了使OLED屏更新显示 OLED_Clear(); OLED_ShowNum(80,4,TimeData.minute,2,16); OLED_ShowNum(40,4,TimeData.hour,2,16); OLED_ShowString(65,4,":"); OLED_ShowNum(25,2,TimeData.year,4,16); OLED_ShowNum(75,2,TimeData.month,2,16); OLED_ShowNum(105,2,TimeData.day,2,16); OLED_ShowString(95,2,"-"); OLED_ShowString(60,2,"-"); } LED0=!LED0; delay_ms(500); delay_ms(500);//闪烁灯 } } 4.结果演示

5.附录:7针0.96寸OLED屏驱动代码(SPI驱动)

oled.c

#include "oled.h" #include "stdlib.h" #include "oledfont.h" #include "delay.h" #include "stm32f10x_spi.h" //OLED的显存 //存放格式如下. //[0]0 1 2 3 ... 127 //[1]0 1 2 3 ... 127 //[2]0 1 2 3 ... 127 //[3]0 1 2 3 ... 127 //[4]0 1 2 3 ... 127 //[5]0 1 2 3 ... 127 //[6]0 1 2 3 ... 127 //[7]0 1 2 3 ... 127 //向SSD1106写入一个字节。 //dat:要写入的数据/命令 //cmd:数据/命令标志 0,表示命令;1,表示数据; void OLED_WR_Byte(u8 dat,u8 cmd) { if(cmd) OLED_DC_Set(); else OLED_DC_Clr(); OLED_CS_Clr(); SPI_I2S_SendData(SPI1,dat); while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY)!=RESET); OLED_CS_Set(); OLED_DC_Set(); } void OLED_Set_Pos(unsigned char x, unsigned char y) { OLED_WR_Byte(0xb0+y,OLED_CMD); OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD); OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD); } //开启OLED显示 void OLED_Display_On(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON } //关闭OLED显示 void OLED_Display_Off(void) { OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令 OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF } //清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! void OLED_Clear(void) { u8 i,n; for(i=0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3