TA的每日心情 | 开心 2020-7-25 10:37 |
---|
签到天数: 40 天 连续签到: 1 天 [LV.5]常住居民I
|
常用的DS3102需使用外置晶振,且没有温度补偿,因此误差较大。DS3231内置晶振且有内部温度补偿,误差可做到1分钟每年。
DS3231是一款高精度I2C实时时钟器件,具有集成的温度补偿晶体振荡器。该器件包含电池输入端,断开主电源时仍可保持精确计时。集成的晶体振荡器可提高器件的长期精确度。DS3231的寄存器能保存秒、分、时、星期、日期、月、年和闹钟设置等信息。少于31天的月份,可自动调整月末日期,包括闰年补偿。时钟的工作格式为24小时或带AM/PM指示的12小时格式。DS3231提供两个可编程日历闹钟和一路可编程方波输出。DS3231与单片机通过I2C双向串行总线传输地址与数据。
DS3231典型引用电路及内部基本框图如下图所示:
DS3231的时钟和日历RTC
可以通过读取适当的寄存器字节获得时钟和日历信息。通过写入适当的寄存器字节设定或者初始化时钟和日历数据。时钟和日历寄存器的内容采用二-十进制编码(BCD)格式。DS3231运行于12小时或者24小时模式。小时寄存器的第6位定义为12或24小时模式选择位。该位为高时,选择12小时模式。在12小时模式下,第5位为AM/PM指示位,逻辑高时为PM。
DS3231的闹钟和报警功能
DS3231包含2个定时/日期闹钟。闹钟1可通过写入寄存器07h~0Ah设定。闹钟2可通过写入寄存器0Bh~0Dh设定。可对闹钟进行编程(通过控制寄存器的闹钟使能位和INTCN位),从而在闹钟匹配条件下触发INT/SQW输出。每个定时/日期闹钟寄存器的第7位是屏蔽位。当每个闹钟的屏蔽位均为逻辑0时,只有当计时寄存器中的值与存储于定时/日期闹钟寄存器中的对应值相匹配时才会告警。闹钟也可以编程为每秒、分、时、星期或日期重复告警。当RTC寄存器值与闹钟寄存器的设定值相匹配时,相应的闹钟标志位A1F或A2F置为逻辑1。如果对应的闹钟中断使能位A1IE或A2IE也置为逻辑1,并且INTCN位置为逻辑1,闹钟条件将会触发INT/SQW信号。RTC在时间和日期寄存器每秒更新时都会检测匹配情况。
更多的详细介绍大家可以去查看数据手册。
接下来建立Vivado工程,这次实验以之前axi_gpio的工程作为模板建立工程,在ZYNQ IP核中选上MIO,如下图所示,因为IIC两个管脚接到了FPGA的MIO50和MIO51。
接下来建立SDK工程,并编写代码如下:
首先需要一个IIC驱动文件,具体如下,这里使用了GPIO模拟IIC:
- #include "myiic.h"
- XGpioPs Gpio_iic;
- //初始化IIC
- void IIC_Init(void)
- {
- int Status;
- XGpioPs_Config *ConfigPtr;
- ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
- Status = XGpioPs_CfgInitialize(&Gpio_iic, ConfigPtr,ConfigPtr->BaseAddr);
- if (Status != XST_SUCCESS){
- return XST_FAILURE;
- }
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSCL, 1);
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 1);
- XGpioPs_SetOutputEnablePin(&Gpio_iic, MIOSCL, 1);
- XGpioPs_SetOutputEnablePin(&Gpio_iic, MIOSDA, 1);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1);
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x1);
- }
- //产生IIC起始信号
- void IIC_Start(void)
- {
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 1); //sda线输出
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x1); //IIC_SDA=1;
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1); //IIC_SCL=1;
- delay_us(4);
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x0); //IIC_SDA=0;//START:when CLK is high,DATA change form high to low
- delay_us(4);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
- }
- //产生IIC停止信号
- void IIC_Stop(void)
- {
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 1); //SDA_OUT();//sda线输出
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x0); //IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
- delay_us(4);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1); //IIC_SCL=1;
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x1); //IIC_SDA=1;//发送I2C总线结束信号
- delay_us(4);
- }
- //等待应答信号到来
- //返回值:1,接收应答失败
- // 0,接收应答成功
- u8 IIC_Wait_Ack(void)
- {
- u8 ucErrTime=0;
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 0); //SDA_IN(); //SDA设置为输入
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x1); //IIC_SDA=1;
- delay_us(1);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1); //IIC_SCL=1;
- delay_us(1);
- while(XGpioPs_ReadPin(&Gpio_iic, MIOSDA))
- {
- ucErrTime++;
- if(ucErrTime>250)
- {
- IIC_Stop();
- return 1;
- }
- }
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;//时钟输出0
- return 0;
- }
- //产生ACK应答
- void IIC_Ack(void)
- {
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 1); //SDA_OUT();
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x0); //IIC_SDA=0;
- delay_us(2);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1); //IIC_SCL=1;
- delay_us(2);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;
- }
- //不产生ACK应答
- void IIC_NAck(void)
- {
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 1); //SDA_OUT();
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, 0x1); //IIC_SDA=1;
- delay_us(2);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1); //IIC_SCL=1;
- delay_us(2);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;
- }
- //IIC发送一个字节
- //返回从机有无应答
- //1,有应答
- //0,无应答
- void IIC_Send_Byte(u8 txd)
- {
- u8 t;
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 1); //SDA_OUT();
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;//拉低时钟开始数据传输
- for(t=0;t<8;t++)
- {
- XGpioPs_WritePin(&Gpio_iic, MIOSDA, (txd&0x80)>>7);//IIC_SDA=(txd&0x80)>>7;
- txd<<=1;
- delay_us(2); //对TEA5767这三个延时都是必须的
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1); //IIC_SCL=1;
- delay_us(2);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;
- delay_us(2);
- }
- }
- //读1个字节,ack=1时,发送ACK,ack=0,发送nACK
- u8 IIC_Read_Byte(unsigned char ack)
- {
- unsigned char i,receive=0;
- XGpioPs_SetDirectionPin(&Gpio_iic, MIOSDA, 0); //SDA_IN();//SDA设置为输入
- for(i=0;i<8;i++ )
- {
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x0); //IIC_SCL=0;
- delay_us(2);
- XGpioPs_WritePin(&Gpio_iic, MIOSCL, 0x1); //IIC_SCL=1;
- receive<<=1;
- if(XGpioPs_ReadPin(&Gpio_iic, MIOSDA))receive++; //if(READ_SDA)receive++;
- delay_us(1);
- }
- if (!ack)
- IIC_NAck();//发送nACK
- else
- IIC_Ack(); //发送ACK
- return receive;
- }
其次编写针对DS3231的驱动程序,具体如下:
- #include "DS3231.h"
- #include "myiic.h"
- _calendar_obj calendar;
- #define DS3231_WriteAddress 0xD0
- #define DS3231_ReadAddress 0xD1
- u8 BCD2HEX(u8 val)
- {
- u8 i;
- i= val&0x0f;
- val >>= 4;
- val &= 0x0f;
- val *= 10;
- i += val;
- return i;
- }
- u16 B_BCD(u8 val)
- {
- u8 i,j,k;
- i=val/10;
- j=val%10;
- k=j+(i<<4);
- return k;
- }
- void I2cByteWrite(u8 addr,u8 bytedata)
- {
- IIC_Start();
- delay_us(5);
- IIC_Send_Byte(DS3231_WriteAddress);
- IIC_Wait_Ack();
- delay_us(5);
- IIC_Send_Byte(addr);
- IIC_Wait_Ack();
- delay_us(5);
- IIC_Send_Byte(bytedata);
- IIC_Wait_Ack();
- delay_us(5);
- IIC_Stop();
- }
- u8 I2cByteRead(u8 addr)
- {
- u8 Dat=0;
- IIC_Start();
- IIC_Send_Byte(DS3231_WriteAddress);
- IIC_Wait_Ack();
- delay_us(5);
- IIC_Send_Byte(addr);
- IIC_Wait_Ack();
- delay_us(5);
- IIC_Start();
- IIC_Send_Byte(DS3231_ReadAddress);
- IIC_Wait_Ack();
- delay_us(5);
- Dat=IIC_Read_Byte(0);
- IIC_NAck();
- IIC_Stop();
- return Dat;
- }
- void DS3231_Init(void)
- {
- IIC_Init();
- I2cByteWrite(0x0e,0);
- delay_ms(2);
- I2cByteWrite(0x0f,0x0);
- delay_ms(2);
- }
- void DS3231_Get(void)
- {
- calendar.w_year=I2cByteRead(0x06);
- calendar.w_month=I2cByteRead(0x05);
- calendar.w_date=I2cByteRead(0x04);
- calendar.hour=I2cByteRead(0x02);
- calendar.min=I2cByteRead(0x01);
- calendar.sec=I2cByteRead(0x00);
- }
- void DS3231_Set(u8 yea,u8 mon,u8 da,u8 hou,u8 min,u8 sec)
- {
- u8 temp=0;
- temp=B_BCD(yea);
- I2cByteWrite(0x06,temp);
- temp=B_BCD(mon);
- I2cByteWrite(0x05,temp);
- temp=B_BCD(da);
- I2cByteWrite(0x04,temp);
- temp=B_BCD(hou);
- I2cByteWrite(0x02,temp);
- temp=B_BCD(min);
- I2cByteWrite(0x01,temp);
- temp=B_BCD(sec);
- I2cByteWrite(0x00,temp);
- }
- void get_show_time(void)
- {
- calendar.w_year=I2cByteRead(0x06);
- calendar.w_year=BCD2HEX(calendar.w_year);
- calendar.w_month=I2cByteRead(0x05);
- calendar.w_month=BCD2HEX(calendar.w_month);
- calendar.w_date=I2cByteRead(0x04);
- calendar.w_date=BCD2HEX(calendar.w_date);
- calendar.hour=I2cByteRead(0x02);
- calendar.hour&=0x3f;
- calendar.hour=BCD2HEX(calendar.hour);
- calendar.min=I2cByteRead(0x01);
- calendar.min=BCD2HEX(calendar.min);
- calendar.sec=I2cByteRead(0x00);
- calendar.sec=BCD2HEX(calendar.sec);
- }
- float DS3231_temperature(void)
- {
- unsigned char tempH,tempL;
- int temp=0;
- float T;
- tempH=I2cByteRead(0x11);
- //tempH=BCD2HEX(tempH);
- tempL=I2cByteRead(0x12);
- //tempL=BCD2HEX(tempL);
- temp = (tempH*256+tempL)>>6;
- if(temp&0x200)
- {
- temp= (~temp) + 1;
- T = (temp & 0x3ff)*0.25;
- }
- else
- T = (temp & 0x3ff)*0.25;
- return T;
- }
主程序代码,如下:
- int main()
- {
- //init_platform();
- XGpio_Initialize(&Gpio, GPIO_EXAMPLE_DEVICE_ID);
- XGpio_SetDataDirection(&Gpio, LED_CHANNEL, 0);//设置为输出
- DS3231_Init();
- DS3231_Set(19,12,1,16,58,20);
- while(1)
- {
- XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 1);
- sleep(1);
- XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0);
- //sleep(1);
- printf("temperature:%f ",DS3231_temperature());
- get_show_time();
- printf("%4d-%2d-%2d-%2d-%2d-%2d \r\n",(2000 + calendar.w_year),
- calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);
- sleep(1);
- }
- //cleanup_platform();
- return 0;
- }
- 代码还是简单易懂的,就是不断读取DS3231的当前日期和温度,并通过串口将数据打印出来,实现效果如下:
附上我的工程:
MYiR_ds3231.zip
(19.5 MB, 下载次数: 11)
|
|