STM32F103用IO口和DS1302模块通信

昨天把github上面arduino驱动DS1302的代码移植到了STM32的程序当中。网址是https://github.com/msparks/arduino-ds1302

今天早上上机发现没数据,于是只能调试代码。

目录

1、硬件连接:

2、DS1302规格书注意点

3、代码

4、结果


1、硬件连接:

STM32F103_PA10  --->    DS1302_RST(时能信号)

STM32F103_PA9  ------->  DS1302_I/O(输入输出信号)

STM32F103_PB13 --------> DS1302_SCLK(时钟信号)

由于板子引出的引脚有限,引出的IO口只有UART1的PA10和PA9,PB13是板上和LED指示灯连接的,要飞线出来和DS1302模块连接。

 

2、DS1302规格书注意点

第一是命令格式。要想把时间信息写入IC,或者从IC中读取时间信息。需要把IC中寄存器的值读出来。首先是要在命令中,加入寄存器的地址且说明是读操作还是写操作。数据要从低位开始传输。即如果传输一个控制命令,要先传输b0,而后再分别是b1、b2、b3、b4、b5、b6、b7。

STM32F103用IO口和DS1302模块通信_第1张图片

 

 

第二是对RTC(real time clock)寄存器:有9个寄存器。每个寄存器位数是8位。如果要读秒这个寄存器,需要把控制命令置为0x81,然后读取1个字节的数据。

STM32F103用IO口和DS1302模块通信_第2张图片

第三是读写寄存器的时序。对于DS1302这款IC来说,时钟上升沿时候,DS1302读取I/O的引脚的电压。STM32编程时候,最好当I/O电压稳定时,即SCLK上升沿到下一个下降沿之间,再让STM32读取I/O引脚电压。

而若要让DS1302输出寄存器的数据,要把时钟引脚拉低,DS1302会根据寄存器的数据,操作I/O引脚的电压。STM32编程时候,要在SCLK在高电平时候,把I/O引脚设为输入,在SCLK下降沿到下一个上升沿之间,读取I/O口数据。

STM32F103用IO口和DS1302模块通信_第3张图片

 

3、代码

tb店上面模块附送的代码是51的。初步一看,读和写中,SCLK、IO口两线的时序不太正确。

而arduino上面用clock burst的方式,IO口信号一直为0.

于是将代码修改为单次只读取一个寄存器的数据。

3.1 DS1302Drv.c

/*
 * author: xxJian
 * date: 2018-9-3 
 * ported from https://github.com/msparks/arduino-ds1302
 * 
 */

#include "DS1302Drv.h"


uint8_t time_buf_reg[8] = {0x20,0x10,0x09,0x14,0x23,0x59,0x50,0x02};//³õʼʱ¼ä
uint32_t b_date, b_month, b_year, b_hour, b_minute, b_second, b_day;



// Returns the decoded decimal value from a binary-coded decimal (BCD) byte.
// Assumes 'bcd' is coded with 4-bits per digit, with the tens place digit in
// the upper 4 MSBs.
uint8_t bcdToDec(const uint8_t bcd) 
{
  return (10 * ((bcd & 0xF0) >> 4) + (bcd & 0x0F));
}

// Returns the binary-coded decimal of 'dec'. Inverse of bcdToDec.
uint8_t decToBcd(const uint8_t dec) 
{  
  uint8_t tens, ones;
  
  tens = dec / 10;
  ones = dec % 10;
  return (tens << 4) | ones;
}

// Returns the hour in 24-hour format from the hour register value.
uint8_t hourFromRegisterValue(const uint8_t value) {
  uint8_t adj;
  if (value & 128)  // 12-hour mode
    adj = 12 * ((value & 32) >> 5);
  else           // 24-hour mode
    adj = 10 * ((value & (32 + 16)) >> 4);
  return (value & 15) + adj;
}

void DS1302_writeOut(const uint8_t value, uint8_t readAfter) 
{  
  GPIO_InitTypeDef GPIO_InitStruct;
  uint32_t i;
  
  //pinMode(io_pin_, OUTPUT);  
  GPIO_InitStruct.Pin = DS1302_IO_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(DS1302_IO_GPIO_Port, &GPIO_InitStruct);    
  
  for (i = 0; i < 8; i++) {  
    //digitalWrite(io_pin_, (value >> i) & 1);
    if( (value >> i) & 1 ) {
      HAL_GPIO_WritePin(DS1302_IO_GPIO_Port, DS1302_IO_Pin, GPIO_PIN_SET);        
    } else {
      HAL_GPIO_WritePin(DS1302_IO_GPIO_Port, DS1302_IO_Pin, GPIO_PIN_RESET);    
    }
    //HAL_Delay(1);
    HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_RESET);    //digitalWrite(sclk_pin_, HIGH);
    HAL_Delay(2);

    if (readAfter && i == 7) {
      // We're about to read data -- ensure the pin is back in input mode
      // before the clock is lowered.
      //pinMode(io_pin_, INPUT);    
      HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_SET);
      HAL_Delay(1);  
      HAL_GPIO_WritePin(DS1302_IO_GPIO_Port, DS1302_IO_Pin, GPIO_PIN_RESET);  
      GPIO_InitStruct.Pin = DS1302_IO_Pin;
      GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      HAL_GPIO_Init(DS1302_IO_GPIO_Port, &GPIO_InitStruct);
      //HAL_Delay(1);  
    } else {
      HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_SET);  //digitalWrite(sclk_pin_, LOW);
      HAL_Delay(2);                                                                //delayMicroseconds(1);
    }
  }
}

uint8_t DS1302_readIn(void) 
{
  uint8_t input_value;
  uint8_t bit;
  uint32_t i;
  
  input_value = 0;
  bit = 0;
  
  GPIO_InitTypeDef GPIO_InitStruct;
  HAL_GPIO_WritePin(DS1302_IO_GPIO_Port, DS1302_IO_Pin, GPIO_PIN_RESET);    
  //pinMode(io_pin_, INPUT);
  GPIO_InitStruct.Pin = DS1302_IO_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(DS1302_IO_GPIO_Port, &GPIO_InitStruct);

  // Bits from the DS1302 are output on the falling edge of the clock
  // cycle. This is called after readIn (which will leave the clock low) or
  // writeOut(..., true) (which will leave it high).
  for (i = 0; i < 8; i++) {
    
    HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_SET);    //digitalWrite(sclk_pin_, HIGH);
    HAL_Delay(2);                                                                //delayMicroseconds(1);
    HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_RESET);  //digitalWrite(sclk_pin_, LOW);
    HAL_Delay(1);                                                                //delayMicroseconds(1);
    bit = HAL_GPIO_ReadPin(DS1302_IO_GPIO_Port, DS1302_IO_Pin);                  //bit = digitalRead(io_pin_);
    HAL_Delay(1);  
    input_value |= (bit << i);  // Bits are read LSB first.
  }

  return input_value;
}

uint8_t DS1302_readRegister(const uint8_t reg) 
{
  //const SPISession s(ce_pin_, io_pin_, sclk_pin_);

  uint8_t cmd_byte;
  uint8_t result;  
  
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_SET);
  
  
  cmd_byte = (0x81 | (reg << 1));
  DS1302_writeOut(cmd_byte, DEF_true);
  result = DS1302_readIn();
  
  
  HAL_Delay(1);
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_RESET);
  
  
  return result;
}

void DS1302_writeRegister(const uint8_t reg, const uint8_t value) 
{
  //const SPISession s(ce_pin_, io_pin_, sclk_pin_);
  
  uint8_t cmd_byte;
  
  
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_SET);
  
  
  cmd_byte = (0x80 | (reg << 1));
  DS1302_writeOut(cmd_byte, DEF_false);
  DS1302_writeOut(value, DEF_false);  
  
  HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_RESET);
}

void DS1302_writeProtect(const uint8_t enable) 
{
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_SET);
  DS1302_writeRegister(kWriteProtectReg, (enable << 7));
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_RESET);
}

void DS1302_halt(const uint8_t enable) 
{
  uint8_t sec;
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_SET);
  sec = DS1302_readRegister(kSecondReg);
  sec &= ~(1 << 7);
  sec |= (enable << 7);
  DS1302_writeRegister(kSecondReg, sec);
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_RESET);
}


void DS1302_writeRam(const uint8_t address, const uint8_t value) 
{
  if (address >= kRamSize) {
    return;
  }

  DS1302_writeRegister(kRamAddress0 + address, value);
}

uint8_t DS1302_readRam(const uint8_t address) 
{
  if (address >= kRamSize) {
    return 0;
  }

  return DS1302_readRegister(kRamAddress0 + address);
}

void DS1302_writeRamBulk(const uint8_t* const data, int len) 
{
  if (len <= 0) {
    return;
  }
  if (len > kRamSize) {
    len = kRamSize;
  }

  //const SPISession s(ce_pin_, io_pin_, sclk_pin_);

  DS1302_writeOut(kRamBurstWrite, DEF_false);
  for (int i = 0; i < len; ++i) {
    DS1302_writeOut(data[i], DEF_false);
  }
}

void DS1302_readRamBulk(uint8_t* const data, int len) 
{
  if (len <= 0) {
    return;
  }
  if (len > kRamSize) {
    len = kRamSize;
  }

  //const SPISession s(ce_pin_, io_pin_, sclk_pin_);

  DS1302_writeOut(kRamBurstRead, DEF_true);
  for (int i = 0; i < len; ++i) {
    data[i] = DS1302_readIn();
  }
}


void DS1302_timeRead() 
{
  //const SPISession s(ce_pin_, io_pin_, sclk_pin_);
  
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_SET);
  
  //Time t(2099, 1, 1, 0, 0, 0, Time::kSunday);
  //DS1302_writeOut(kClockBurstRead, DEF_true);
  
  
  time_buf_reg[7] = DS1302_readRegister(kSecondReg)& 0x7F;  //sec
  time_buf_reg[6] = DS1302_readRegister(kMinuteReg);  //min
  time_buf_reg[5] = DS1302_readRegister(kHourReg);    //hr
  time_buf_reg[4] = DS1302_readRegister(kDateReg);    //date
  time_buf_reg[3] = DS1302_readRegister(kMonthReg);    //mon
  time_buf_reg[2] = DS1302_readRegister(kDayReg);      //day
  time_buf_reg[1] = DS1302_readRegister(kYearReg);    //yr
  /*
  time_buf_reg[7] = DS1302_readIn() & 0x7F;  //sec
  time_buf_reg[6] = DS1302_readIn();          //min
  time_buf_reg[5] = DS1302_readIn();          //hr
  time_buf_reg[4] = DS1302_readIn();          //date
  time_buf_reg[3] = DS1302_readIn();          //mon
  time_buf_reg[2] = DS1302_readIn();          //day
  time_buf_reg[1] = DS1302_readIn();          //yr
    */  
    
  b_year  = 2000 + bcdToDec(time_buf_reg[1]);
  b_day   = bcdToDec(time_buf_reg[2]);
  b_month = bcdToDec(time_buf_reg[3]);
  b_date  = bcdToDec(time_buf_reg[4]);
  b_hour  = hourFromRegisterValue(time_buf_reg[5]);
  b_minute = bcdToDec(time_buf_reg[6]);
  b_second = bcdToDec(time_buf_reg[7]);
  
  HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_RESET);
  
}

void DS1302_timeWrite(void) 
{
  
  // We want to maintain the Clock Halt flag if it is set.
  uint8_t ch_value;  
  
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_SET);
  
  ch_value = DS1302_readRegister(kSecondReg) & 0x80;

  //const SPISession s(ce_pin_, io_pin_, sclk_pin_);

  //DS1302_writeRegister(kSecondReg, decToBcd(b_second));      //DS1302¸Õ°²×°Éϵç³Ø£¬ÔËÐÐÕâÐС£·ñÔòÔËÐÐÏÂÃæÄÇÐÐ
  DS1302_writeRegister(kSecondReg,ch_value | decToBcd(b_second));
  DS1302_writeRegister(kMinuteReg,decToBcd(b_minute));
  DS1302_writeRegister(kHourReg,decToBcd(b_hour));
  DS1302_writeRegister(kDateReg,decToBcd(b_date));
  DS1302_writeRegister(kMonthReg,decToBcd(b_month));
  DS1302_writeRegister(kDayReg ,decToBcd(b_day));
  DS1302_writeRegister(kYearReg,decToBcd(b_year - 2000));
  /*
  DS1302_writeOut(kClockBurstWrite, DEF_false);
  DS1302_writeOut(ch_value | decToBcd(b_second), DEF_false);
  DS1302_writeOut(decToBcd(b_minute), DEF_false);
  DS1302_writeOut(decToBcd(b_hour), DEF_false);
  DS1302_writeOut(decToBcd(b_date), DEF_false);
  DS1302_writeOut(decToBcd(b_month), DEF_false);
  DS1302_writeOut(decToBcd(b_day), DEF_false);
  DS1302_writeOut(decToBcd(b_year - 2000), DEF_false);
  // All clock registers *and* the WP register have to be written for the time
  // to be set.
  DS1302_writeOut(0, DEF_false);  // Write protection register.
  DS1302_writeOut(0, DEF_false);  // Trickle Charge register.
  */
  
  HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_RESET);
}

3.2 DS1302Drv.h

#ifndef __DS1302DRV_H__
#define __DS1302DRV_H__




#include "stm32f1xx_hal.h"


#define DEF_true 	1
#define DEF_false 0


#define kRamSize 31

#define DS1302_SCLK_GPIO_Port 	LED5_GPIO_Port
#define DS1302_SCLK_Pin 				LED5_Pin
typedef enum
{
  kSecondReg       = 0,
  kMinuteReg       = 1,
  kHourReg         = 2,
  kDateReg         = 3,
  kMonthReg        = 4,
  kDayReg          = 5,
  kYearReg         = 6,
  kWriteProtectReg = 7,
  kChargeReg			 = 8,

  // The RAM register space follows the clock register space.
  kRamAddress0     = 32
} DS1302_Register;

typedef enum
{
  kClockBurstRead  = 0xBF,
  kClockBurstWrite = 0xBE,
  kRamBurstRead    = 0xFF,
  kRamBurstWrite   = 0xFE
} DS1302_Command;

typedef enum 
{
    kSunday    = 1,
    kMonday    = 2,
    kTuesday   = 3,
    kWednesday = 4,
    kThursday  = 5,
    kFriday    = 6,
    kSaturday  = 7
} DS1302_Day;		 




extern uint8_t time_buf_reg[8];



uint8_t bcdToDec(const uint8_t bcd) ;
uint8_t decToBcd(const uint8_t dec) ;
uint8_t hourFromRegisterValue(const uint8_t value);
void DS1302_writeOut(const uint8_t value, uint8_t readAfter) ;
uint8_t DS1302_readIn(void) ;
uint8_t DS1302_readRegister(const uint8_t reg) ;
void DS1302_writeRegister(const uint8_t reg, const uint8_t value) ;
void DS1302_writeProtect(const uint8_t enable) ;
void DS1302_halt(const uint8_t enable) ;
void DS1302_writeRam(const uint8_t address, const uint8_t value) ;
uint8_t DS1302_readRam(const uint8_t address) ;
void DS1302_writeRamBulk(const uint8_t* const data, int len) ;
void DS1302_readRamBulk(uint8_t* const data, int len) ;

//Top layer function
void DS1302_timeRead(void) ;
void DS1302_timeWrite(void) ;


#endif /* __DS1302DRV_H__ */

需要注意的是,在主函数我们需要使用的两个函数为:DS1302_timeRead()和DS1302_timeWrite()

4、结果

这张图是用逻辑分析仪观察了DS1302_timeRead()函数的数据波形。

STM32F103用IO口和DS1302模块通信_第4张图片

第一通道是时钟SCLK。第二通道是IO,第三通道是CE/RST。程序中,每读写完一个字节,即拉低了CE。让IC重新到位。

读取到的寄存器分别是:秒(0-59)、分(0-59)、时(0-23)、日期(1-31)、月份(1-12)、星期(1-7)、年份(00-99)

以下是读取秒寄存器的时序。

STM32F103用IO口和DS1302模块通信_第5张图片

首先控制命令是0x81,SCLK是上升沿有效。而8位后是DS1302控制IO口,是SCLK下降沿IO口电压变化。顺序上,数据是10001100。由于数据传输是LSB开始,因此需要转换一下,是00110001即0x31。即当时是31秒。

由于DS1302数据存储,是用BCD码。

time_buf_reg[8]这个数据中,分别存放了DS1302中7个寄存器的读取值。见一下代码。

    time_buf_reg[7] = DS1302_readRegister(kSecondReg);    //sec
    time_buf_reg[6] = DS1302_readRegister(kMinuteReg);    //min
    time_buf_reg[5] = DS1302_readRegister(kHourReg);      //hr
    time_buf_reg[4] = DS1302_readRegister(kDateReg);      //date
    time_buf_reg[3] = DS1302_readRegister(kMonthReg);      //mon
    time_buf_reg[2] = DS1302_readRegister(kDayReg);        //day
    time_buf_reg[1] = DS1302_readRegister(kYearReg);      //yr

time_buf_reg[0]存放的是0x20。主要因为DS1302用1个8位的寄存器存放年份,范围只有0-99。也就是配合了0x20,所表达的范围是2000-2099。

下图是实时仿真观察到time_buf_reg的数据。今天是2018年星期二,9月4日,当时时间为15:13:31。可以见到,DS1302存放数据的格式,是BCD码。用16进制,肉眼很方便的看到当前的日期。

STM32F103用IO口和DS1302模块通信_第6张图片

用github那套代码,使用了bcdToDec()函数,可方便的转换为16进制数据。便于计算、也便于转为字符串发送到上位机显示。

下图中,时间已经变为了14:39:56

STM32F103用IO口和DS1302模块通信_第7张图片

 

看来三天内搞好这个也是有点压力的。好在根据时序图来编,没出现大问题。另外由于不是做产品,就先用IO口,暂时没用SPI。

 

 

2019年5月11日更新:有一段时间DS1302模块没有安装纽扣电池,重新使用本次代码发现有点问题。经调试发现,DS1302_timeWrite() 函数内,有一行代码是专门判断Clock Halt(时间停止)标志位是否设置的。估计是DS1302初安装纽扣电池上电时,这个时钟是停止计算的。因此,上纽扣电池的第一次执行,需要使用这行:

DS1302_writeRegister(kSecondReg, decToBcd(b_second));		

随后,才可以使用这行:

DS1302_writeRegister(kSecondReg,ch_value | decToBcd(b_second));

 

你可能感兴趣的:(单片机)