357 lines
8.2 KiB
C
357 lines
8.2 KiB
C
/**
|
|
* @copyright (c) 2021 Noel Dom https://www.youtube.com/channel/UCINCDcQylATh2wS5BAYIBPg.
|
|
*
|
|
* @brief Device Driver for DS1307 Real-time clock (RTC)
|
|
* @file ds1307.c
|
|
* @version 0.1.0
|
|
* @date 2021
|
|
* @author Noel Dominguez.
|
|
*/
|
|
|
|
|
|
#include "ds1307.h"
|
|
|
|
#define DS1307_ADDRES 0x68
|
|
#define DS1307_SECONDS 0x00
|
|
#define DS1307_MINUTES 0x01
|
|
#define DS1307_HOURS 0x02
|
|
#define DS1307_DAY 0X03
|
|
#define DS1307_DATE 0x04
|
|
#define DS1307_MONTH 0x05
|
|
#define DS1307_YEAR 0x06
|
|
#define DS1307_CONTROL 0x07
|
|
#define DS1307_REG_UTC_HR 0x08
|
|
#define DS1307_REG_UTC_MIN 0x09
|
|
#define DS1307_REG_CENT 0x10
|
|
#define DS1307_REG_RAM 0x11
|
|
#define DS1307_TIMEOUT 1000
|
|
|
|
|
|
#define DS1307_HANDLER hi2c1
|
|
|
|
typedef enum{
|
|
DS1307_1HZ,
|
|
DS1307_4096HZ,
|
|
DS1307_8192HZ,
|
|
DS1307_32768HZ
|
|
}ds1307_rate_t;
|
|
|
|
|
|
static ds1307_err_t ds1307_write_byte(uint8_t ds1307_reg_addres, uint8_t data);
|
|
static uint8_t ds1307_read_byte(uint8_t ds1307_reg_addres);
|
|
static uint8_t ds1307_bcd_decode(uint8_t data);
|
|
static uint8_t ds1307_bcd_encode(uint8_t data);
|
|
|
|
/**
|
|
* @brief Write byte data from an specific address ds1307 RTC
|
|
*
|
|
* @param ds1307_reg_addres: REG Address you can see more in datasheet page 4 figure 2
|
|
* @param data
|
|
* @return ds1307_err_t: 0 if everything is ok
|
|
*/
|
|
static ds1307_err_t ds1307_write_byte(uint8_t ds1307_reg_addres, uint8_t data){
|
|
|
|
uint8_t buff[2] = {ds1307_reg_addres,data};
|
|
ds1307_err_t ret_val;
|
|
ret_val = HAL_I2C_Master_Transmit(&DS1307_HANDLER, DS1307_ADDRES << 1, buff, 2, DS1307_TIMEOUT);
|
|
|
|
return ret_val;
|
|
}
|
|
/**
|
|
* @brief Read byte data from an specific address ds1307 RTC
|
|
*
|
|
* @param ds1307_reg_addres: REG Address you can see more in datasheet page 4 figure 2
|
|
* @return data: data read from reg address
|
|
*/
|
|
static uint8_t ds1307_read_byte(uint8_t ds1307_reg_addres){
|
|
|
|
uint8_t data;
|
|
HAL_I2C_Master_Transmit(&DS1307_HANDLER, DS1307_ADDRES << 1, &ds1307_reg_addres, 1, DS1307_TIMEOUT);
|
|
HAL_I2C_Master_Receive(&DS1307_HANDLER, DS1307_ADDRES << 1, &data, 1, DS1307_TIMEOUT);
|
|
return data;
|
|
}
|
|
/**
|
|
* @brief BCD decode
|
|
*
|
|
* @param data: Value to convert
|
|
* @return uint8_t: data converted
|
|
*/
|
|
static uint8_t ds1307_bcd_decode(uint8_t data){
|
|
return (((data & 0xf0) >> 4) * 10) + (data & 0x0f);
|
|
}
|
|
/**
|
|
* @brief BCD Encode
|
|
*
|
|
* @param data: Value to convert
|
|
* @return uint8_t: data converted
|
|
*/
|
|
static uint8_t ds1307_bcd_encode(uint8_t data){
|
|
return (data % 10 + ((data / 10) << 4));
|
|
}
|
|
|
|
/**
|
|
* @brief Init ds1307
|
|
*
|
|
*/
|
|
void ds1307_init(void){
|
|
ds1307_set_clock_halt(0);
|
|
}
|
|
|
|
/**
|
|
* @brief To start the time and calendar, we must set the stop bit of the clock (CH) in 0, to stop, put the bit in 1
|
|
* more information see datasheet on page 4
|
|
*
|
|
* @param halt: 0 init, 1 stop
|
|
*/
|
|
void ds1307_set_clock_halt(uint8_t halt){
|
|
uint8_t ch = (halt ? 1 << 7 : 0);
|
|
ds1307_write_byte(DS1307_SECONDS, ch | (ds1307_read_byte(DS1307_SECONDS) & 0x7F));
|
|
}
|
|
/**
|
|
* @brief ds1307_get_clock_halt more info datasheet page 4
|
|
*
|
|
* @return uint8_t
|
|
*/
|
|
uint8_t ds1307_get_clock_halt(void){
|
|
return (ds1307_read_byte(DS1307_SECONDS) & 0x80) >> 7;
|
|
}
|
|
|
|
/**
|
|
* @brief ds1307_set_hour
|
|
*
|
|
* @param hour
|
|
* @return ds1307_err_t
|
|
*/
|
|
ds1307_err_t ds1307_set_hour(uint8_t hour){
|
|
return ds1307_write_byte(DS1307_HOURS,ds1307_bcd_encode(hour & 0x3F));
|
|
}
|
|
/**
|
|
* @brief ds1307_get_hour
|
|
*
|
|
* @return uint8_t
|
|
*/
|
|
uint8_t ds1307_get_hour(void){
|
|
return ds1307_bcd_decode(ds1307_read_byte(DS1307_HOURS) & 0x3F);
|
|
}
|
|
|
|
/**
|
|
* @brief ds1307_set_second
|
|
*
|
|
* @param second
|
|
*/
|
|
void ds1307_set_second(uint8_t second){
|
|
uint8_t val = ds1307_get_clock_halt();
|
|
ds1307_write_byte(DS1307_SECONDS, ds1307_bcd_encode(second | val));
|
|
|
|
}
|
|
/**
|
|
* @brief ds1307_get_second
|
|
*
|
|
* @return uint8_t
|
|
*/
|
|
uint8_t ds1307_get_second(void){
|
|
return ds1307_bcd_decode(ds1307_read_byte(DS1307_SECONDS) & 0x7F);
|
|
}
|
|
/**
|
|
* @brief ds1307_set_minutes
|
|
*
|
|
* @param minutes
|
|
*/
|
|
void ds1307_set_minutes(uint8_t minutes){
|
|
ds1307_write_byte(DS1307_MINUTES, ds1307_bcd_encode(minutes));
|
|
}
|
|
/**
|
|
* @brief ds1307_get_minutes
|
|
*
|
|
* @return uint8_t
|
|
*/
|
|
uint8_t ds1307_get_minutes(void){
|
|
return ds1307_bcd_decode(ds1307_read_byte(DS1307_MINUTES));
|
|
|
|
}
|
|
/**
|
|
* @brief ds1307_set_day
|
|
*
|
|
* @param day
|
|
*/
|
|
void ds1307_set_day(uint8_t day){
|
|
ds1307_write_byte(DS1307_DAY, ds1307_bcd_encode(day));
|
|
}
|
|
/**
|
|
* @brief ds1307_get_day
|
|
*
|
|
* @return ds1307_days_t
|
|
*/
|
|
ds1307_days_t ds1307_get_day(void){
|
|
return ds1307_read_byte(ds1307_bcd_decode(DS1307_DAY));
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief ds1307_set_date
|
|
*
|
|
* @param date
|
|
*/
|
|
void ds1307_set_date(uint8_t date){
|
|
ds1307_write_byte(DS1307_DATE, ds1307_bcd_encode(date));
|
|
}
|
|
/**
|
|
* @brief
|
|
*
|
|
* @return uint8_t
|
|
*/
|
|
uint8_t ds1307_get_date(void){
|
|
return ds1307_bcd_decode(ds1307_read_byte(DS1307_DATE));
|
|
}
|
|
|
|
/**
|
|
* @brief ds1307_set_month
|
|
*
|
|
* @param month
|
|
*/
|
|
void ds1307_set_month(ds1307_months_t month){
|
|
ds1307_write_byte(DS1307_MONTH, ds1307_bcd_encode(month));
|
|
}
|
|
/**
|
|
* @brief ds1307_get_month
|
|
*
|
|
* @return ds1307_months_t
|
|
*/
|
|
ds1307_months_t ds1307_get_month(void){
|
|
return ds1307_read_byte(ds1307_bcd_decode(DS1307_MONTH));
|
|
}
|
|
/**
|
|
* @brief ds1307_set_year
|
|
*
|
|
* @param year setup year
|
|
*/
|
|
void ds1307_set_year(uint16_t year){
|
|
ds1307_write_byte(DS1307_REG_CENT, year / 100);
|
|
ds1307_write_byte(DS1307_YEAR, ds1307_bcd_encode(year % 100));
|
|
}
|
|
/**
|
|
* @brief ds1307_get_year
|
|
*
|
|
* @return uint16_t: current year
|
|
*/
|
|
uint16_t ds1307_get_year(void){
|
|
uint16_t cent = ds1307_read_byte(DS1307_REG_CENT) * 100;
|
|
return ds1307_bcd_decode(ds1307_read_byte(DS1307_YEAR)) + cent;
|
|
|
|
}
|
|
/**
|
|
* @brief Set your time zone more info at https://everytimezone.com/
|
|
*
|
|
* @param hr: current hour
|
|
* @param min: current min
|
|
*/
|
|
void ds1307_set_time_zone(int8_t hr, uint8_t min){
|
|
ds1307_write_byte(DS1307_REG_UTC_HR, hr);
|
|
ds1307_write_byte(DS1307_REG_UTC_MIN, min);
|
|
}
|
|
/**
|
|
* @brief Get the actual timezone-hour configured in the rtc
|
|
*
|
|
* @return int8_t actual time zone
|
|
*/
|
|
int8_t ds1307_get_time_zone_hour(void){
|
|
return ds1307_read_byte(DS1307_REG_UTC_HR);
|
|
}
|
|
/**
|
|
* @brief Get the actual timezone-min configured in the rtc
|
|
*
|
|
* @return int8_t: actual time zone
|
|
*/
|
|
int8_t ds1307_get_time_zone_min(void){
|
|
return ds1307_read_byte(DS1307_REG_UTC_MIN);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Update ds1307 data
|
|
*
|
|
* @param dev: ds1307 pointer
|
|
*/
|
|
void ds1307_update(ds1307_dev_t *ds1307_dev){
|
|
|
|
ds1307_dev->seconds = ds1307_get_second();
|
|
ds1307_dev->minutes = ds1307_get_minutes();
|
|
ds1307_dev->hours = ds1307_get_hour();
|
|
ds1307_dev->day = ds1307_get_day();
|
|
ds1307_dev->date = ds1307_get_date();
|
|
ds1307_dev->month = ds1307_get_month();
|
|
ds1307_dev->year = ds1307_get_year();
|
|
ds1307_dev->t_zone_hour = ds1307_get_time_zone_hour();
|
|
ds1307_dev->t_zone_min = ds1307_get_time_zone_min();
|
|
}
|
|
/**
|
|
* @brief Use this function to configure the DS1307, you only need to call this function once on the the first run of your RTC.
|
|
*
|
|
* @param seconds
|
|
* @param minutes
|
|
* @param hours
|
|
* @param day
|
|
* @param date
|
|
* @param month
|
|
* @param year
|
|
* @param t_zone_hour
|
|
* @param t_zone_min
|
|
*/
|
|
void ds1307_config(uint8_t seconds, uint8_t minutes, uint8_t hours,ds1307_days_t day, uint8_t date,
|
|
ds1307_months_t month, uint16_t year, int8_t t_zone_hour, int8_t t_zone_min)
|
|
{
|
|
ds1307_set_second(seconds);
|
|
ds1307_set_minutes(minutes);
|
|
ds1307_set_hour(hours);
|
|
ds1307_set_day(day);
|
|
ds1307_set_date(date);
|
|
ds1307_set_month(month);
|
|
ds1307_set_year(year);
|
|
ds1307_set_time_zone(t_zone_hour, t_zone_min);
|
|
|
|
}
|
|
/**
|
|
* @brief: Display ds1307 info by UART, @todo you need to implement your printf function!
|
|
*
|
|
* @param: ds1307_dev ds1307 struct pointer
|
|
*/
|
|
void ds1307_log_uart(ds1307_dev_t *ds1307_dev){
|
|
DBG_print("%d:%d:%d %d/%d/%d\n",ds1307_dev->hours,ds1307_dev->minutes,ds1307_dev->seconds,
|
|
ds1307_dev->date,ds1307_dev->month,ds1307_dev->year);
|
|
}
|
|
/**
|
|
* @brief This function helps to scan all connected devices on our I2C peripheral
|
|
*
|
|
* @param I2Chnd: I2C Handles
|
|
* @param delay_: Delay between scanning
|
|
*/
|
|
void start_i2c_scan(I2C_HandleTypeDef *I2Chnd, uint32_t delay_){
|
|
|
|
HAL_StatusTypeDef status;
|
|
uint8_t no_devices = 0;
|
|
//DBG_print("\n\r [ I2C Scanner v0.1 ]");
|
|
|
|
for (uint8_t i = 0; i < 128; i++){
|
|
|
|
status = HAL_I2C_IsDeviceReady(I2Chnd , (uint16_t)(i<<1), 2, 2);
|
|
|
|
if ( status != HAL_OK){
|
|
//DBG_print(".\n\r");
|
|
} else {
|
|
//DBG_print("\n\rDevice found! Address: 0x%X",i);
|
|
++no_devices;
|
|
}
|
|
|
|
HAL_Delay(delay_);
|
|
|
|
}
|
|
|
|
if (!no_devices){
|
|
//DBG_print("\n\r No Devices found!");
|
|
} else {
|
|
//DBG_print("\n\r Total Devices found: %d",no_devices);
|
|
|
|
}
|
|
|
|
}
|