M-Arch(10)第九个示例:SPI与DS1302

前言

回顾下之前的章节:

  • 第一章节中我们描述了整个框架的核心设计思路以及主要的文件架构

  • 第二章节中我们基于一个简单的定时器OS实现了串口的数据打印,并完成了通用crc模块的设计和测试

  • 第三章节中我们给出了真随机数和伪随机数的概念和代码示例,并在架构上对接口进行了重构

  • 第四章节中我们回顾了FMC的基本知识,并给出了示例,后面我们将在设计IAP的时候再次使用到FMC

  • 第五章节中我们使用ADC和DMA搭建了一个通用的采样框架,并通过串口给出了采样的数据示例

  • 第六章节中我们总结了DAC的基本使用方法,并通过DAC生成了任意频率的正弦波,三角波和方波

  • 第七章节中我们总结下时钟的概念,并给出了获取系统中各模块的时钟频率的代码

  • 第八章节中我们介绍了如何通过串口的DMA来实现串口数据的收发

  • 第九章节中我们介绍了定时器的使用,以及如何产生普通占空比PWM以及互补带死区的PWM,这在控制中十分重要

本文我们将介绍下SPI的概念,以及如何使用三线SPI的时序驱动DS1302时钟芯片,同时我们也将给出用模拟IO方式驱动DS1302的方法。

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

SPI

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

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

b425cf56939af627684eb2e855b540d6.png
常规SPI信号描述

DS1302

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

DS1302的管脚配置:0c5b496d7218c94a54987a17db84ce82.png

DS1302的时序:

b9aa1cb946556777467c3906aae18f71.png
DS1302时序

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

282ba815fee14c8499c49948d654d99e.png
半双工SPI

DS1302的寄存器配置:

c29fd8a6af66cd5e42cb27462928d099.png
DS1302寄存器

DS1302的时钟频率:

2d39088984fd70bea52cd5c6229d0954.png
DS1302时钟频率

三线SPI驱动DS1302

半双工SPI的基本初始化:

  • 单线发送模式

  • 数据8位

  • 主机模式

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

  • NSS Soft

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

  • LSB

发送流程:

  • 设置为发送模式

  • CE使能

  • 发送地址

  • 发送数据

  • CE失能

接收流程:

  • 设置为发送模式

  • CE使能

  • 发送地址

  • 设置为接收模式

  • 等待数据接收完成

  • CE失能

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

  • 读数据,等待SPI收工

模拟IO驱动DS1302

从网上找的代码,其原理就是用IO口反转来模拟高低电平。

一坨代码

GD32代码:

#include "io.h"
#include "io_gd32.h"
#include "common.h"

//#define DS1302_USING_SPI

/* PF7-SCK PF8-CE PF9-DATA */

#define CURRENT_SPI     SPI4

#define RTC_RCU         RCU_GPIOF
#define RTC_GPIO        GPIOF
#define RTC_SCK_PIN     GPIO_PIN_7
#define RTC_CE_PIN      GPIO_PIN_8
#define RTC_DATA_PIN    GPIO_PIN_9

#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

#define READ_MODE   gpio_init_input_mode(RTC_RCU, RTC_GPIO, RTC_DATA_PIN);
#define WRITE_MODE  gpio_init_output_mode(RTC_RCU, RTC_GPIO, RTC_DATA_PIN, GPIO_OSPEED_50MHZ, 0);

#define CE_HIGH     gpio_bit_set(RTC_GPIO, RTC_CE_PIN);
#define CE_LOW      gpio_bit_reset(RTC_GPIO, RTC_CE_PIN);
#define CLK_HIGH    gpio_bit_set(RTC_GPIO, RTC_SCK_PIN);
#define CLK_LOW     gpio_bit_reset(RTC_GPIO, RTC_SCK_PIN);
#define DATA_HIGH   gpio_bit_set(RTC_GPIO, RTC_DATA_PIN);
#define DATA_LOW    gpio_bit_reset(RTC_GPIO, RTC_DATA_PIN);
#define DATA        gpio_input_bit_get(RTC_GPIO, RTC_DATA_PIN)

#endif

#define WRITE_FLAG_ADDR     0xC0
#define READ_FLAG_ADDR      0xC1
#define RTC_INIT_FLAG       0xA5

#define YEAR_BASE           2021
/* 默认起始时间2021年1月1日,星期五 */
time_t init_time = {YEAR_BASE, 1, 1, 0, 0, 0, 5};

void spi1_init(){}

#ifdef DS1302_USING_SPI

void spi5_init(void)
{
    spi_parameter_struct spi_init_struct;

    spi_i2s_deinit(CURRENT_SPI);
    rcu_periph_clock_enable(RCU_SPI4);

    /* SPI_MOSI pf9 */
    gpio_init_af_mode(RTC_RCU, RTC_GPIO, RTC_DATA_PIN, GPIO_OSPEED_50MHZ, GPIO_AF_5);
    /* SPI_SCK pf7 */
    gpio_init_af_mode(RTC_RCU, RTC_GPIO, RTC_SCK_PIN, GPIO_OSPEED_50MHZ, GPIO_AF_5);
    /* 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_64;
    spi_init_struct.endian               = SPI_ENDIAN_LSB;
    spi_init(CURRENT_SPI, &spi_init_struct);

    /* enable */
    spi_enable(CURRENT_SPI);
}

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;
}

#else

void spi5_init() {
    gpio_init_output_mode(RTC_RCU, RTC_GPIO, RTC_SCK_PIN,  GPIO_OSPEED_2MHZ, 0);
    gpio_init_output_mode(RTC_RCU, RTC_GPIO, RTC_DATA_PIN,  GPIO_OSPEED_2MHZ, 0);
    gpio_init_output_mode(RTC_RCU, RTC_GPIO, RTC_CE_PIN,  GPIO_OSPEED_50MHZ, 0);
}

static void rtc_delay(int count) {
    while (count) {
        count--;
    }
}

static void spi_rtc_send_byte(uint8_t value)
{
    uint8_t index;
    WRITE_MODE;
    for (index = 0; index < 8; index++)
    {
        CLK_LOW;
        if (value&0x01)
        {
            DATA_HIGH;
        }
        else
        {
            DATA_LOW;
        }
        rtc_delay(1);
        CLK_HIGH;
        rtc_delay(1);  
        value >>= 1;
    }
    //CLK_LOW;
}

static void spi_rtc_recive_byte(u8 *value) {
    uint8_t index;
    READ_MODE;
    for (index = 0; index < 8; index++)
    {
        *value >>= 1;
        CLK_HIGH;
        rtc_delay(1);
        CLK_LOW;
        rtc_delay(1);
        if (DATA == 1) {
            *value |= 0x80;
        } else {
            *value &= 0x7F;
        }
    }
}

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

static uint8_t read_ds1302(uint8_t address)
{
    uint8_t data = 0;
    
    CLK_LOW;
    CE_HIGH;
    spi_rtc_send_byte(address);
    spi_rtc_recive_byte(&data);
    CE_LOW;
    return data;
}
#endif

STM32代码:

#include "io.h"
#include "io_stm32.h"
#include "common.h"

//#define DS1302_USING_SPI

/* PA5-SCK PA6-CE PA7-DATA */

#define CURRENT_SPI     SPI1

#define RTC_RCC         RCC_APB2Periph_GPIOA
#define RTC_GPIO        GPIOA
#define RTC_SCK_PIN     GPIO_Pin_5
#define RTC_CE_PIN      GPIO_Pin_6
#define RTC_DATA_PIN    GPIO_Pin_7

#ifdef DS1302_USING_SPI

#define READ_MODE   SPI_Cmd(CURRENT_SPI, DISABLE); \
                    SPI_BiDirectionalLineConfig(CURRENT_SPI, SPI_Direction_Rx); \
                    SPI_Cmd(CURRENT_SPI, ENABLE);
                 
#define WRITE_MODE  SPI_Cmd(CURRENT_SPI, DISABLE); \
                    SPI_BiDirectionalLineConfig(CURRENT_SPI, SPI_Direction_Tx); \
                    SPI_Cmd(CURRENT_SPI, ENABLE);

#define  CE_HIGH    GPIO_SetBits(RTC_GPIO, RTC_CE_PIN);
#define  CE_LOW     GPIO_ResetBits(RTC_GPIO, RTC_CE_PIN);

#else

#define READ_MODE   gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_IPU, GPIO_Speed_50MHz, RTC_DATA_PIN);
#define WRITE_MODE  gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_Out_PP, GPIO_Speed_50MHz, RTC_DATA_PIN);

#define CE_HIGH     GPIO_SetBits(RTC_GPIO, RTC_CE_PIN);
#define CE_LOW      GPIO_ResetBits(RTC_GPIO, RTC_CE_PIN);
#define CLK_HIGH    GPIO_SetBits(RTC_GPIO, RTC_SCK_PIN);
#define CLK_LOW     GPIO_ResetBits(RTC_GPIO, RTC_SCK_PIN);
#define DATA_HIGH   GPIO_SetBits(RTC_GPIO, RTC_DATA_PIN);
#define DATA_LOW    GPIO_ResetBits(RTC_GPIO, RTC_DATA_PIN);
#define DATA        GPIO_ReadInputDataBit(RTC_GPIO, RTC_DATA_PIN)

#endif



#define WRITE_FLAG_ADDR     0xC0
#define READ_FLAG_ADDR      0xC1
#define RTC_INIT_FLAG       0x35

#define YEAR_BASE           2021
/* 默认起始时间2021年1月1日,星期五 */
time_t init_time = {YEAR_BASE, 1, 1, 1, 0, 0, 5};

void spi5_init(void){}

#ifdef DS1302_USING_SPI
void spi1_init(void)
{
    SPI_InitTypeDef SPI_InitStructure;

    SPI_I2S_DeInit(CURRENT_SPI);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    
    /* SPI_MOSI PA7 */
    gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_AF_PP, GPIO_Speed_50MHz, RTC_DATA_PIN);
    /* SPI_SCK PA5 */
    gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_AF_PP, GPIO_Speed_50MHz, RTC_SCK_PIN);
    /* RTC CE */
    gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_Out_PP, GPIO_Speed_50MHz, RTC_CE_PIN);
    CE_LOW;

    /* SPI parameter config */
    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(CURRENT_SPI, &SPI_InitStructure);
    SPI_Cmd(CURRENT_SPI, ENABLE);
}

static void spi_rtc_send_byte(uint8_t byte)
{
    /* loop while data register in not empty */
    while(RESET == SPI_I2S_GetFlagStatus(CURRENT_SPI, SPI_I2S_FLAG_TXE));
    /* send byte through the SPI peripheral */
    SPI_I2S_SendData(CURRENT_SPI, byte);    
    while(SPI_I2S_GetFlagStatus(CURRENT_SPI, SPI_I2S_FLAG_BSY));
}

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_GetFlagStatus(CURRENT_SPI, SPI_I2S_FLAG_RXNE));    
    CE_LOW;
    WRITE_MODE;
    data = SPI_I2S_ReceiveData(CURRENT_SPI);
    while(SPI_I2S_GetFlagStatus(CURRENT_SPI, SPI_I2S_FLAG_BSY));

    return data;
}

#else

void spi1_init() {
    gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_Out_PP, GPIO_Speed_50MHz, RTC_DATA_PIN);
    gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_Out_PP, GPIO_Speed_50MHz, RTC_CE_PIN);
    gpio_init(RTC_RCC, RTC_GPIO, GPIO_Mode_Out_PP, GPIO_Speed_50MHz, RTC_SCK_PIN);
 
    GPIO_ResetBits(RTC_GPIO, RTC_DATA_PIN | RTC_SCK_PIN | RTC_CE_PIN);
}

static void rtc_delay(int count) {
    while (count) {
        count--;
    }
}

static void spi_rtc_send_byte(uint8_t value)
{
    uint8_t index;
    WRITE_MODE;
    for (index = 0; index < 8; index++)
    {
        CLK_LOW;
        if (value&0x01)
        {
            DATA_HIGH;
        }
        else
        {
            DATA_LOW;
        }
        rtc_delay(1);
        CLK_HIGH;
        rtc_delay(1);  
        value >>= 1;
    }
}

static void spi_rtc_receive_byte(u8 *value) {
    uint8_t index;
    READ_MODE;
    for (index = 0; index < 8; index++)
    {
        *value >>= 1;
        CLK_HIGH;
        rtc_delay(1);
        CLK_LOW;
        rtc_delay(1);
        if (DATA == 1) {
            *value |= 0x80;
        } else {
            *value &= 0x7F;
        }
    }
}

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

static uint8_t read_ds1302(uint8_t address)
{
    uint8_t data = 0;
    
    CLK_LOW;
    CE_HIGH;
    spi_rtc_send_byte(address);
    spi_rtc_receive_byte(&data);
    CE_LOW;
    return data;
}
#endif

对外接口代码:

#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);
    }
}

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;
}

例行结果展示

串口打印时间

ab9e42b22c7db8853b605af5f437d7c4.gif
DS1302时间打印

GD32 SPI驱动DS1302 示波器数据

ca21d04c5ad5f97a257a1d6a8576b4d4.png
GD32三线SPI驱动DS1302
88f21cf351a3258945295a88bc200a36.png
GD32三线SPI驱动DS1302 WEEK数据

GD32 IO口模拟驱动DS1302 示波器数据

2fff08c23d1e5fc7761f3300928b7bf2.png
GD32 IO口模拟驱动DS1302
5d166d272cb8ac8ff640dd6cfd56af3f.png
GD32 IO口模拟驱动DS1302 WEEK数据

STM32 SPI驱动DS1302 示波器数据

f4142c490b0175fb58e514d2ae99bb07.png
STM32三线SPI驱动DS1302
805090546f130bb91e6adbf7cd5a966b.png
STM32三线SPI驱动DS1302 WEEK数据

STM32 IO口模拟驱动DS1302 示波器数据

628517f90b0b92ade4dca100cd0e36e7.png
STM32 IO口模拟驱动DS1302
2575493ceace3b644b08c821bb405bae.png
STM32 IO口模拟驱动DS1302 WEEK数据

--EOF--

例行求粉,谢谢!

091755efc847c00172e71beb92b4b215.png
求粉
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值