前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >M-Arch(10)第九个示例:SPI与DS1302

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

作者头像
滚神大人
发布2021-11-02 16:29:22
7700
发布2021-11-02 16:29:22
举报
文章被收录于专栏:趣Python趣Python趣Python

前言

回顾下之前的章节:

  • 第一章节中我们描述了整个框架的核心设计思路以及主要的文件架构
  • 第二章节中我们基于一个简单的定时器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信号描述如下图:

常规SPI信号描述

DS1302

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

DS1302的管脚配置:

DS1302的时序:

DS1302时序

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

半双工SPI

DS1302的寄存器配置:

DS1302寄存器

DS1302的时钟频率:

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

例行结果展示

串口打印时间

DS1302时间打印

GD32 SPI驱动DS1302 示波器数据

GD32三线SPI驱动DS1302

GD32三线SPI驱动DS1302 WEEK数据

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

GD32 IO口模拟驱动DS1302

GD32 IO口模拟驱动DS1302 WEEK数据

STM32 SPI驱动DS1302 示波器数据

STM32三线SPI驱动DS1302

STM32三线SPI驱动DS1302 WEEK数据

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

STM32 IO口模拟驱动DS1302

STM32 IO口模拟驱动DS1302 WEEK数据

--EOF--

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 趣Python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • SPI
  • DS1302
  • 三线SPI驱动DS1302
  • 模拟IO驱动DS1302
  • 一坨代码
  • 例行结果展示
    • 串口打印时间
      • GD32 SPI驱动DS1302 示波器数据
        • GD32 IO口模拟驱动DS1302 示波器数据
          • STM32 SPI驱动DS1302 示波器数据
            • STM32 IO口模拟驱动DS1302 示波器数据
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档