DS1302驱动补充——突发模式

DS1302驱动补充——突发模式

几个月前我发布了一篇关于讲解DS1302驱动方式的文章,其中无论是写数据还是读取数据都采用的是单字节传输方式。

然而在项目中实际使用时却遇到了一个问题——跳时,分析这个bug出现的原因如下。

在实际项目中,会每隔30秒从DS1302获取一下时间,这个获取时间的函数内容为

void DS1302_Read_Time()
{
    if (SysGetLapseTick(g_rtctick) > 30000)
    {
        SysSetCurrentTick(&g_rtctick);
        TimeData_t.second = DS1302_Read_rig(0x81); //读秒
        TimeData_t.minute = DS1302_Read_rig(0x83); //读分
        TimeData_t.hour = DS1302_Read_rig(0x85);   //读时
        TimeData_t.day = DS1302_Read_rig(0x87);    //读日
        TimeData_t.month = DS1302_Read_rig(0x89);  //读月
        TimeData_t.week = DS1302_Read_rig(0x8B);   //读星期
        TimeData_t.year = DS1302_Read_rig(0x8D);   //读年
    }
}

即依次读取DS1302各个寄存器的值,并将读到的值赋值给结构体。

这看起来没有什么问题,只要时序不出差错总能读到正确的时间,然而实际上由于主芯片和DS1302并非一个串行系统,即主芯片在读取数据时,DS1302也是在工作的。

这将导致一个问题,假设主芯片先读取到秒寄存器的数据为59,当它再去读取分寄存器时,由于DS1302一直时处于工作状态,这个时候时间可能已经从59s变成了1min,即主芯片读到的分寄存器的值为1.

这样就导致了主芯片认为现在的时间为1min59s,然而实际的DS1302时间却为1min00s。

这里只是以秒分为例,实际上其他寄存器也有这样的问题,当处于一个临界时间时,主芯片读取到的时间和DS1302的时间将会有巨大的偏差,直到第二次读取时间才会被矫正。

虽然由于主芯片超高的运行速度,出现这个问题的概率极小,我在每秒读取一次时间的情况下测试了24小时,共出现过一次这种现象,但是对某些项目来说,时间出错可能会带来很大问题。

为了彻底杜绝这个现象,建议采用突发模式/多字节方式来读取数据。

突发模式描述:

如下是数据手册中,对突发模式的描述

在这里插入图片描述

乍一看有些难以理解,我们一句一句分析找出想要的内容:

如图是主机向DS1302发送命令的格式,
在这里插入图片描述

结合这个我们再来看

通过对31(十进制)位地址寻址(地址/命令位于1至5=逻辑1),可以把时钟/日历或RAM寄存器规定为多字节方式

也就是说,如果我们想要实现多字节方式传输,需要通过31这个地址。

这里的31为十进制表达,化为二进制则是0001 1111,不过DS1302规定从bit1到bit5才是地址即A4-A0的位置,也就是说我们需要把他往前移移。

现在命令为 1x11 111x,其中x是未知,需要从别的语句中获取它们的含义。

位6规定时钟或RAM

根据上面的图片可以看到,bit6为1时操控的是RAM,为0时操控时钟,由于我们是要操作时钟,所以将bit6设置为0 ——1011 111x

位0规定读或写

这里我们需要读取时间,所以bit0 为1 ———1011 1111(0XBF)

知道了要发送的指令后,剩下的就是水到渠成的事,多字节的传输由地址0开始,即从秒开始发送,我们需要定义一个数组来接收这些数据。

代码

void ds1302_burst_read(uint8_t *_buf)
{
    uint8_t i, j, suf, temp, value;
    temp = 0xBF;
    CE_H;                  // RET=1;
                           // 写地址
    DS1302_DATAOUT_Init(); // 配置IO为输出,恢复正常状态
    for (i = 0; i < 8; i++)
    {
        if (temp & 0x01)
        {
            DATA_H;
        } // IO=1;
        else
        {
            DATA_L;
        } // IO=0;
        temp = temp >> 1;
        Delay_us(5);
        SCLK_H; // CLK=1;
        Delay_us(5);
        if (i == 7)
        {
            DS1302_DATAINPUT_Init(); // 配置IO为输入
        }
        SCLK_L; // CLK=0;
    }
    // 读数据
    for (j = 0; j < 8; j++)
    {
        for (i = 0; i < 8; i++)
        {
            Delay_us(5);
            suf = suf >> 1;                                // 读数据变量
            if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12)) // IO=1
            {
                suf = suf | 0x80;
            }
            else // IO=0
            {
                suf = suf & 0x7f;
            }

            SCLK_H; // CLK=1;
            Delay_us(5);
            SCLK_L; // CLK=0;
        }
        _buf[j] = suf;
        suf = 0;
    }
     CE_L;                  // RET=0;
    DS1302_DATAOUT_Init(); // 配置IO为输出,恢复正常状态
}
uint8_t timedata[8]={0};
uint8_t hextodec(uint8_t _hex)
{
    uint8_t mm, nn, value;
    // 数据处理转化十进制
    mm = _hex / 16;
    nn = _hex % 16;
    value = mm * 10 + nn;
    return value;
}
void DS1302_Read_Time()
{
    uint32_t temp;
    static uint32_t pretemp;
    static uint8_t flag = 0;
    if (SysGetLapseTick(g_rtctick) > 1000)
    {
        SysSetCurrentTick(&g_rtctick);
        ds1302_burst_read(timedata);
        TimeData_t.second = hextodec(timedata[0]); // 读秒
        TimeData_t.minute = hextodec(timedata[1]); // 读分
        TimeData_t.hour = hextodec(timedata[2]);   // 读时
        TimeData_t.day = hextodec(timedata[3]);    // 读日
        TimeData_t.month = hextodec(timedata[4]);  // 读月
        TimeData_t.week = hextodec(timedata[5]);   // 读星期
        TimeData_t.year = hextodec(timedata[6]);   // 读年
    }
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值