原创 【MC3172】MC3172驱动DS1302时钟设计

2023-1-11 12:36 2301 12 4 分类: MCU/ 嵌入式

【MC3172】MC3172驱动DS1302时钟设计

前言

距离上一次评测MC3172已经过去一段时间。当时本来打算着手实现MC3172驱动DS1302时钟模块实现电子钟。当时由于疫情以及其他事情。导致没能写出驱动功能。现发布这篇博客将DS1302时钟驱动代码补档。

1实现原理

如何实现配置内核定时器编写延时函数以及根据官方串口例程修改成输出需要的显示轮询打印时间的功能已经在我的上一篇博客“原创 【MC3172】基于MC3172串口打印的计时器设计”中有提到。如果不会这一部分配置的。感兴趣的也可以参考博客中的配置讲解。里面也有我配置好的代码包可供大家下载使用。

接下来我主要讲解DS1302时钟的驱动原理。

这里我先说一下我的整体思路。因为MC3172这款芯片是新研发的芯片。相对来说驱动一些外设的资料比较少。所以我们在使用芯片写其他功能的时候就需要参考其他资源多的开发板的驱动程序。将其中的细节研究清楚。并把相关代码修改成MC3172可以使用的代码。就是整个配置流程。不过这需要对单片机功能的细节把控清楚。最终才能保证不出现代码错误的情况下。保证原理正确即可调通结果。

这张图是DS1302时钟时序图。我已经将关键电平时间做了相关标注。被标注的时间是我们可以控制的时间。这一部分时间最好在逻辑分析仪下具体观察每一小段电平时间是否严格符合时序图规定。比如。Tcc这一段时间是1us。就表示在SCLK的上升沿到来之前。我们至少需要在RST拉高后延时1us的时间。其他的标注地方的理解也同理。

时序图还有个关键部分。就是数据线IO除了我们发送数据。还需要从DS1302读回数据。这样就涉及到一个IO切换问题(由读转换为写或者写转换为读)。以及如何确定我们稳定读取到了DS1302时钟芯片返回的数据(有可能我们写时序有误而接收不到返回数据。或者我们读数据时给的时间太少。导致数据不稳定没有读取到)。这两个问题至关重要。也是我之前所说的细节问题。不同的单片机实际上我认为差别就在这些小功能的细节上。而MC3172的IO功能。读写功能互斥。启动一个不能启动另一个。就需要我们把读功能打开并关闭写功能。然后去读数据。读完数据后如果需要写。就需要把写功能打开并关闭读功能。相关代码如下

SDA_SET_READ_EN;
  • SDA_SET_WRITE_UEN;
  • for(i = 0; i < 8; i++) //读数据都是先移动再读取
  • {
  • SCL_L; //时序图上可以看出下降沿后开始数据稳定可以读数据
  • dat >>= 1;
  • delay_us(3);
  • if((SDA_READ & 0x0008) >> 3)
  • {
  • dat |= 0x80;
  • }
  • SCL_H;
  • delay_us(3);
  • }
  • SDA_SET_WRITE_EN;</p><p style="text-align: left; margin-top: 0; margin-bottom: 0;"><span style="text-align: center;">SDA_SET_READ_UEN;
  • 复制代码

    这里我只贴了部分细节。通过SDA_SET_READ_EN;宏定义使能读。SDA_SET_WRITE_EN;宏定义使能写。第二个读数据稳定就体现在了dat >>= 1;这一句后面的延时。这里就是给读取数据的一个时间。因此这里的读取为保证数据稳定。推荐稍长的时间。5~10us均可

    下面贴出DS1302驱动各部分编写的函数

    //写一字节函数
  • void Write_Byte(u8 address)
  • {
  • u8 i;
  • for(i = 0; i < 8; i++)
  • {
  • SCL_L;
  • if(address & 0x01)
  • {
  • SDA_H;
  • }
  • else
  • {
  • SDA_L;
  • }
  • address >>= 1;
  • SCL_H;
  • delay_us(1);
  • }
  • }
  • //指定地址address写数据dat函数
  • void DS1302_WriteByte(u8 address, u8 dat)
  • {
  • // u8 i;
  • RST_L;
  • // NOP();
  • SCL_L;
  • // NOP();
  • RST_H;
  • NOP();NOP();NOP();
  • NOP();NOP();NOP();
  • Write_Byte(address);
  • Write_Byte(((dat / 10) << 4) | (dat % 10));
  • // dat = ((dat / 10) << 4) | (dat % 10);
  • }
  • //指定地址读取数据函数
  • u8 DS1302_ReadByte(u8 address)
  • {
  • u8 dat, i, dat1, dat2;
  • u16 dat_get = 0;
  • RST_L;
  • // NOP();
  • SCL_L;
  • // NOP();
  • RST_H;
  • NOP();NOP();NOP();
  • NOP();NOP();NOP();
  • Write_Byte(address);
  • // _nop_();_nop_();_nop_();
  • // _nop_();_nop_();_nop_();
  • SDA_SET_READ_EN;
  • SDA_SET_WRITE_UEN;
  • for(i = 0; i < 8; i++) //读数据都是先移动再读取
  • {
  • SCL_L; //时序图上可以看出下降沿后开始数据稳定可以读数据
  • dat >>= 1;
  • delay_us(3);
  • if((SDA_READ & 0x0008) >> 3)
  • {
  • dat |= 0x80;
  • }
  • SCL_H;
  • delay_us(3);
  • }
  • SDA_SET_WRITE_EN;
  • SDA_SET_READ_UEN;
  • SDA_L; //为了输出的稳定SDA要置0
  • // dat = dat - 6; //BCD码减去6得到bcd码de十进制数值下的十六进制表示。
  • dat1 = dat / 16; dat2 = dat % 16;
  • dat = dat1 * 10 + dat2;
  • return dat;
  • }
  • //DS1302初始化
  • void DS1302_init()
  • {
  • u8 i, add;
  • add = 0x80;
  • DS1302_WriteByte(0x8E, 0x00);
  • for(i = 0; i < 7; i++)
  • {
  • DS1302_WriteByte(add, init[i]);
  • add += 2;
  • }
  • DS1302_WriteByte(0x8E, 0x80);
  • }
  • //DS1302时间获取
  • void DS1302_Get()
  • {
  • u8 i, add;
  • add = 0x81;
  • DS1302_WriteByte(0x8E, 0x00); //清除保护
  • for(i = 0; i < 3; i++)
  • {
  • time[i] = DS1302_ReadByte(add); //写入时是秒分时往里写读的时候是时分秒外读
  • add += 2;
  • }
  • DS1302_WriteByte(0x8E, 0x80);
  • </p><p style="text-align: center; margin-top: 7.85pt; margin-bottom: 7.85pt;">}
  • 复制代码

    IO引脚宏定义

    #include "../MC3172/MC3172.h"
  • #include "./GPIO_GPCOM_TIMER_Example.c"
  • #define SYS_CORE_CLK_MHZ 48
  • //PD1 CLK SCL PD3 DAT SDA PD5 RST CE
  • #define SCL_H GPIO_SET_OUTPUT_PIN_TO_1(GPIOD_BASE_ADDR ,GPIO_PIN1)
  • #define SCL_L GPIO_SET_OUTPUT_PIN_TO_0(GPIOD_BASE_ADDR ,GPIO_PIN1)
  • #define SDA_H GPIO_SET_OUTPUT_PIN_TO_1(GPIOD_BASE_ADDR ,GPIO_PIN3)
  • #define SDA_L GPIO_SET_OUTPUT_PIN_TO_0(GPIOD_BASE_ADDR ,GPIO_PIN3)
  • #define RST_H GPIO_SET_OUTPUT_PIN_TO_1(GPIOD_BASE_ADDR ,GPIO_PIN5)
  • #define RST_L GPIO_SET_OUTPUT_PIN_TO_0(GPIOD_BASE_ADDR ,GPIO_PIN5)
  • #define SDA_SET_READ_EN GPIO_SET_INPUT_EN_VALUE(GPIOD_BASE_ADDR ,GPIO_PIN3,GPIO_SET_ENABLE) //读使能
  • #define SDA_SET_READ_UEN GPIO_SET_INPUT_EN_VALUE(GPIOD_BASE_ADDR ,GPIO_PIN3,GPIO_SET_DISABLE) //读失能
  • #define SDA_SET_WRITE_EN GPIO_SET_OUTPUT_EN_VALUE(GPIOD_BASE_ADDR ,GPIO_PIN3,GPIO_SET_ENABLE) //写使能
  • #define SDA_SET_WRITE_UEN GPIO_SET_OUTPUT_EN_VALUE(GPIOD_BASE_ADDR ,GPIO_PIN3,GPIO_SET_DISABLE) //写失能
  • #define SDA_READ_FAST GPIO_GET_INPUT_VALUE_FAST(GPIOD_BASE_ADDR)
  • #define SDA_READ GPIO_GET_INPUT_VALUE_SAFE(GPIOD_BASE_ADDR)
  • //#define SDA_SET_PULLDOWN GPIO_SET_PULL_DOWN_VALUE(GPIOD_BASE_ADDR,GPIO_PIN3,0x00000008)
  • u8 init[] = {30, 22, 23, 10, 30, 45, 1}; //秒分时
  • u8 time[3];
  • </p><p style="text-align: center; margin-top: 7.85pt; margin-bottom: 7.85pt;">
  • 复制代码

     

    2主体功能及展示。

    主要功能就是在屏幕上显示Real_Time:23:24:13。这样的格式打印的时间。具体内容我将通过视频演示

    开发板接线实物图如下所示

    PB3是板子TX。接串口模块的RXD。PB2是RX接串口模块的TX。然后PD1到PD3依次接DS1302的时钟线CLK。数据线DAT。复位端RST。

    这里由于核心板的电源没有多余排针引出。因此我用了一块废板的共脚排针。通过共脚排针为DS1302供电。

    3之后计划做的事。

    研究一下这个板子的线程通信方法以及如何像带有中断的单片机一样利用定时器计时。这样之后可以借此移植其他板子的超声波测距函数。这块板子总体来说很不错。我希望大家也多多支持感芯科技。

    作者: xyzzyxaaa, 来源:面包板社区

    链接: https://mbb.eet-china.com/blog/uid-me-3997270.html

    版权声明:本文为博主原创,未经本人允许,禁止转载!

    PARTNER CONTENT

    文章评论0条评论)

    登录后参与讨论
    我要评论
    0
    12
    关闭 站长推荐上一条 /1 下一条