/*
 * 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};//3?ê?ê±??
uint32_t b_date=12, b_month=6, b_year=2021, b_hour=23, b_minute=59, b_second=50, b_day=2;
 
 
 
// 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,j;
  
  //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);
		j=2000;
		while(j>1)
		{
				j--;
			__nop();
		}
 
    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);  
			
			j=2000;
			while(j>1)
			{
			j--;
			__nop();
			}

		
      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);
			j=2000;
			while(j>1)
			{
				j--;
				__nop();
			}
    }
  }
}
 
uint8_t DS1302_readIn(void) 
{
  uint8_t input_value;
  uint8_t bit;
  uint32_t i,j;
  
  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);
					while(j>1)
			{
				j--;
				__nop();
			}
			
			
    HAL_GPIO_WritePin(DS1302_SCLK_GPIO_Port, DS1302_SCLK_Pin, GPIO_PIN_RESET);  //digitalWrite(sclk_pin_, LOW);
    //HAL_Delay(1);                                                                //delayMicroseconds(1);
			j=2000;
						while(j>1)
			{
				j--;
				__nop();
			}
    bit = HAL_GPIO_ReadPin(DS1302_IO_GPIO_Port, DS1302_IO_Pin);                  //bit = digitalRead(io_pin_);
    //HAL_Delay(1);  
			j=2000;
					while(j>1)
			{
				j--;
				__nop();
			}
			
    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;  
  uint32_t j; 
  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);
				j=2000;
					while(j>1)
			{
				j--;
				__nop();
			}
			
  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_);
 
	
	/*
	2019年5月11日更新：有一段时间DS1302模块没有安装纽扣电池，重新使用本次代码发现有点问题。
	经调试发现，DS1302_timeWrite() 函数内，有一行代码是专门判断Clock Halt（时间停止）标志位是否设置的。
	估计是DS1302初安装纽扣电池上电时，这个时钟是停止计算的。
	因此，上纽扣电池的第一次执行，需要使用这行：

	DS1302_writeRegister(kSecondReg, decToBcd(b_second));		
	随后，才可以使用这行：

	DS1302_writeRegister(kSecondReg,ch_value | decToBcd(b_second));
 
	*/
//DS1302_writeRegister(kSecondReg, decToBcd(b_second));      
    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);
}

