昨天把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。