查看: 987|回复: 0

[经验] 米尔MYD-C7Z020开发板试用8---DS3231时钟芯片测试

[复制链接]
  • TA的每日心情
    开心
    2020-7-25 10:37
  • 签到天数: 40 天

    连续签到: 1 天

    [LV.5]常住居民I

    发表于 2019-12-1 22:18:26 | 显示全部楼层 |阅读模式
    分享到:
        常用的DS3102需使用外置晶振,且没有温度补偿,因此误差较大。DS3231内置晶振且有内部温度补偿,误差可做到1分钟每年。
        DS3231是一款高精度I2C实时时钟器件,具有集成的温度补偿晶体振荡器。该器件包含电池输入端,断开主电源时仍可保持精确计时。集成的晶体振荡器可提高器件的长期精确度。DS3231的寄存器能保存秒、分、时、星期、日期、月、年和闹钟设置等信息。少于31天的月份,可自动调整月末日期,包括闰年补偿。时钟的工作格式为24小时或带AM/PM指示的12小时格式。DS3231提供两个可编程日历闹钟和一路可编程方波输出。DS3231与单片机通过I2C双向串行总线传输地址与数据。
        DS3231典型引用电路及内部基本框图如下图所示:
        1.JPG 2.JPG

      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。
    3.JPG
        接下来建立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的当前日期和温度,并通过串口将数据打印出来,实现效果如下:
    5.jpg



    附上我的工程:
    MYiR_ds3231.zip (19.5 MB, 下载次数: 11)
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-4-26 10:31 , Processed in 0.115660 second(s), 16 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.