STM32F103用IO口和DS1302模块通信

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

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

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。

 

 

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

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

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

 

3、代码

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

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

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


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

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

	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);	
    HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_RESET);	//digitalWrite(sclk_pin_, LOW);
    HAL_Delay(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) 
{
	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) 
{
	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_timeRead() 
{

	HAL_GPIO_WritePin(DS1302_CE_GPIO_Port, DS1302_CE_Pin, GPIO_PIN_SET);

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

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

 

 

4、结果

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

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

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

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

首先控制命令是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进制,肉眼很方便的看到当前的日期。

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

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

 

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

猜你喜欢

转载自blog.csdn.net/qq_27158179/article/details/82384230