M-Arch(番外10)GD32L233评测-SPI驱动DS1302

本文介绍了如何在GD32微控制器上使用SPI接口驱动DS1302实时时钟芯片。通过详细讲解DS1302的信号描述、管脚配置、时序和寄存器配置,展示了SPI的半双工模式初始化及读写操作。同时,分享了在硬件调试中遇到的问题和解决经验,包括SPI引脚配置错误的教训。最后,给出了DS1302的初始化函数和读写接口实现代码,以及测试结果。
摘要由CSDN通过智能技术生成

前言

架构做得好,高产似母猪。

接口改一改,效果就出来。

开工第二天,搞一搞SPI驱动DS1302。

关键字:GD32,SPI,DS1302,三线SPI,半双工SPI

SPI

串行外设接口(Serial Peripheral Interface,缩写为SPI)提供了基于SPI协议的数据发送和接收功能,可以工作于主机或从机模式。SPI接口支持具有硬件CRC计算和校验的全双工和单工模式。有些SPI口还支持SPI四线主机模式。

常规的SPI信号描述如下图:

32d59ad751636c387b040e95049c593f.png
常规SPI信号描述

DS1302

DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,并且具有闰年补偿等多种功能。(百度百科)

DS1302的管脚配置:

86b95ff5b38c62b005ee7172d645a677.png
DS1302管脚配置

DS1302的时序:

ebc2fdc6ecb2e6e7180a2d6015778597.png
DS1302时序

可以看到:除了CE高低电平不一样,这个时序跟半双工的SPI是一样的。

1ab4770f93d55c50b87ddbe187750af5.png
半双工SPI

DS1302的寄存器配置:

095ba1acaf8097af3edff0fec88d403b.png
DS1302寄存器

DS1302的时钟频率:

4df02ec988130997c90f0056c9bebfdf.png
DS1302时钟频率

三线SPI驱动DS1302

半双工SPI的基本初始化:

  • 单线发送模式

  • 数据8位

  • 主机模式

  • 时钟空闲低电平,第一个边沿读数据

  • NSS Soft

  • 分频:根据DS1302的配置来,一般1M多比较合适。

  • LSB

发送流程:

  • 设置为发送模式

  • CE使能

  • 发送地址

  • 发送数据

  • CE失能

接收流程:

  • 设置为发送模式

  • CE使能

  • 发送地址

  • 设置为接收模式

  • 等待数据接收完成

  • CE失能

  • 切换为发送模式,掐断SPI时钟信号

  • 读数据,等待SPI收工

模拟IO驱动DS1302

略(曾经写过,就不贴了)

SPI驱动DS1302

坑爹的小插曲:

SPI一般是AF5,结果用的SPI1管脚是PB13,PB14,PB15,他们是AF6,被坑了2个小时,真扯。

巨讨厌调试硬件,真希望芯片厂商设计硬件时能更多一些软件工程师的设计idea。

SPI初始化:

#define DS1302_USING_SPI

/* SPI0:PA5-SCK-SPI_SCK PA6-CE-SPI_MISO PA7-DATA-SPI_MOSI */
/* SPI1:PB13-SCK-SPI_SCK PB14-CE-SPI_MISO PB15-DATA-SPI_MOSI */

#define CURRENT_SPI     SPI1
#define CURRENT_SPI_RCU RCU_SPI1

#define RTC_RCU         RCU_GPIOB
#define RTC_GPIO        GPIOB
#define RTC_SCK_PIN     GPIO_PIN_13
#define RTC_CE_PIN      GPIO_PIN_14
#define RTC_DATA_PIN    GPIO_PIN_15

#ifdef DS1302_USING_SPI
#define READ_MODE   spi_disable(CURRENT_SPI); \
                    spi_bidirectional_transfer_config(CURRENT_SPI, SPI_BIDIRECTIONAL_RECEIVE); \
                    spi_enable(CURRENT_SPI);
#define WRITE_MODE  spi_disable(CURRENT_SPI); \
                    spi_bidirectional_transfer_config(CURRENT_SPI, SPI_BIDIRECTIONAL_TRANSMIT); \
                    spi_enable(CURRENT_SPI);

#define  CE_HIGH    gpio_bit_set(RTC_GPIO, RTC_CE_PIN);
#define  CE_LOW     gpio_bit_reset(RTC_GPIO, RTC_CE_PIN);

#else
#endif

void spi1_init(void)
{
    spi_parameter_struct spi_init_struct;

    spi_i2s_deinit(CURRENT_SPI);
    rcu_periph_clock_enable(CURRENT_SPI_RCU);

    /* SPI_MOSI */
    gpio_init_af_mode(RTC_RCU, RTC_GPIO, RTC_DATA_PIN, GPIO_OSPEED_50MHZ, GPIO_AF_6);
    /* SPI_SCK */
    gpio_init_af_mode(RTC_RCU, RTC_GPIO, RTC_SCK_PIN, GPIO_OSPEED_50MHZ, GPIO_AF_6);
    /* RTC CE */
    gpio_init_output_mode(RTC_RCU, RTC_GPIO, RTC_CE_PIN, GPIO_OSPEED_50MHZ, 0);
    CE_LOW;

    /* SPI parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_BDTRANSMIT;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_16;
    spi_init_struct.endian               = SPI_ENDIAN_LSB;
    spi_init(CURRENT_SPI, &spi_init_struct);

    /* enable */
    spi_enable(CURRENT_SPI);
}

DS1302初始化:

#define DS1302_UNLOCK   write_ds1302(0x8e, 0x00);
#define DS1302_LOCK     write_ds1302(0x8e, 0x80);

void rtc_time_init(void)
{
    uint8_t hour;
    uint8_t hour_24 = 0;
    if (read_ds1302(READ_FLAG_ADDR) != RTC_INIT_FLAG)
    {
        write_ds1302(0x84, hour_24);
        set_rtc(init_time);
        return;
    }
    hour = read_ds1302(0x85);
    if (hour > 0x80)
    {
        // 12小时制转为24小时制
        hour_24 = (hour > 0xA0)?(hour&0x1F)|DEC2BCD(12):(hour&0x1F);
        write_ds1302(0x84, hour_24);
    }
}

读写接口:

static void spi_rtc_send_byte(uint8_t byte)
{
    /* loop while data register in not empty */
    while(RESET == spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_TBE));
    /* send byte through the SPI peripheral */
    spi_i2s_data_transmit(CURRENT_SPI, byte);    
    while(spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_TRANS));
}

static void write_ds1302(uint8_t address, uint8_t data)     
{
    WRITE_MODE;
    CE_HIGH;
    spi_rtc_send_byte(address);
    spi_rtc_send_byte(data);
    CE_LOW;
}

static uint8_t read_ds1302(uint8_t address)
{
  uint8_t data;

    /* send addr */
    WRITE_MODE;
    CE_HIGH;
    spi_rtc_send_byte(address);

    /* recive data */
    READ_MODE;
    while(RESET == spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_RBNE));
    CE_LOW;
    WRITE_MODE;
    data = spi_i2s_data_receive(CURRENT_SPI);
    while(spi_i2s_flag_get(CURRENT_SPI, SPI_FLAG_TRANS));

    return data;
}

void set_rtc(time_t t)
{
    uint8_t year = t.year - YEAR_BASE;
    if(year >= 100) year = 0; 

    DS1302_UNLOCK;

    /* 写入已经设置时间标记 */
    write_ds1302(WRITE_FLAG_ADDR, RTC_INIT_FLAG);
    write_ds1302(0x80, DEC2BCD(t.second));
    write_ds1302(0x82, DEC2BCD(t.minute));
    write_ds1302(0x84, DEC2BCD(t.hour));
    write_ds1302(0x86, DEC2BCD(t.day));
    write_ds1302(0x88, DEC2BCD(t.month));
    write_ds1302(0x8c, DEC2BCD(year));
    write_ds1302(0x8a, DEC2BCD(t.week));

    DS1302_LOCK;
}

time_t get_rtc(void)
{
    time_t t;
    uint16_t year = read_ds1302(0x8d);
    uint8_t  month = read_ds1302(0x89);
    uint8_t  day = read_ds1302(0x87);
    uint8_t  hour = read_ds1302(0x85);
    uint8_t  minute = read_ds1302(0x83);
    uint8_t second = read_ds1302(0x81);
    uint8_t  week = read_ds1302(0x8b);

    t.year = BCD2DEC(year) + YEAR_BASE;
    t.month = BCD2DEC(month);
    t.day = BCD2DEC(day);
    t.hour = BCD2DEC(hour);
    t.minute = BCD2DEC(minute);
    t.second = BCD2DEC(second);
    t.week = BCD2DEC(week);

 return t;
}

测试结果

DS1302星期(寄存器0x8B)的SPI波形:

820e61a85e03dced612f95b34413b0f0.png
DS1302星期(寄存器0x8B)

串口数据:

2a8f72edd482039296eae002ea0fbc70.gif ea0139739689ea5809d04b811edb41a3.gif 3f0b3ebc597f77c2d430df65b683fbb2.png 08849e5919fe5d58dd436473831099cb.gif
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值