/* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /** library to communicate with the Maxim DS1307 I2C RTC IC (code) * @file rtc_ds1307.c * @author King Kévin * @date 2016-2017 * @note peripherals used: I2C @ref i2c_master_i2c */ /* standard libraries */ #include // standard integer types #include // standard I/O facilities #include // general utilities /* STM32 (including CM3) libraries */ #include // real-time control clock library #include // general purpose input output library #include // I2C library #include "global.h" // global utilities #include "rtc_ds1307.h" // RTC header and definitions #include "i2c_master.h" // i2c utilities #define RTC_DS1307_I2C_ADDR 0x68 /**< DS1307 I2C address (fixed to 0b1101000) */ void rtc_ds1307_setup(void) { // configure I2C peripheral i2c_master_setup(false); // DS1307 only supports normal mode (up to 100 kHz) } bool rtc_ds1307_oscillator_disabled(void) { uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x00}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing CH value return false; } return data[0]&0x80; // return CH bit value to indicate if oscillator is disabled } uint16_t rtc_ds1307_read_square_wave(void) { uint16_t to_return = 0; // square wave frequency to return (in Hz) uint8_t data[1] = {0}; // to read data over I2C const uint16_t rtc_ds1307_rs[] = {1, 4096, 8192, 32768}; // RS1/RS0 values const uint8_t address[] = {0x07}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing control register return 0xffff; // error occurred } if (data[0]&0x10) { // verify if the square wave is enabled (SQWE) to_return = rtc_ds1307_rs[data[0]&0x03]; // read RS1/RS0 and get value } else { to_return = 0; // square wave output is disabled } return to_return; } uint8_t rtc_ds1307_read_seconds(void) { uint8_t to_return = 0; // seconds to return uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x00}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value return 0xff; } to_return = ((data[0]&0x70)>>4)*10+(data[0]&0x0f); // convert BCD coding into seconds return to_return; } uint8_t rtc_ds1307_read_minutes(void) { uint8_t to_return = 0; // minutes to return uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x01}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value return 0xff; } to_return = (data[0]>>4)*10+(data[0]&0x0f); // convert BCD coding into minutes return to_return; } uint8_t rtc_ds1307_read_hours(void) { uint8_t to_return = 0; // hours to return uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x02}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value return 0xff; } if (data[0]&0x40) { // 12 hour mode if (data[0]&0x02) { // PM to_return += 12; // add the 12 hours } to_return += ((data[0]&0x10)>>4)*10; // convert BCD coding into hours (first digit) } else { to_return = ((data[0]&0x30)>>4)*10; // convert BCD coding into hours (first digit) } to_return += (data[0]&0x0f); // convert BCD coding into hours (second digit) return to_return; } uint8_t rtc_ds1307_read_day(void) { uint8_t to_return = 0; // day to return uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x03}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value return 0xff; } to_return = (data[0]&0x07); // convert BCD coding into days return to_return; } uint8_t rtc_ds1307_read_date(void) { uint8_t to_return = 0; // date to return uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x04}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value return 0xff; } to_return = ((data[0]&0x30)>>4)*10+(data[0]&0x0f); // convert BCD coding into date return to_return; } uint8_t rtc_ds1307_read_month(void) { uint8_t to_return = 0; // month to return uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x05}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value return 0xff; } to_return = ((data[0]&0x10)>>4)*10+(data[0]&0x0f); // convert BCD coding into month return to_return; } uint8_t rtc_ds1307_read_year(void) { uint8_t data[1] = {0}; // to read data over I2C const uint8_t address[] = {0x06}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value return 0xff; } uint8_t to_return = ((data[0]&0xf0)>>4)*10+(data[0]&0x0f); // convert BCD coding into year return to_return; } uint8_t* rtc_ds1307_read_time(void) { static uint8_t time[7] = {0}; // store time {seconds, minutes, hours, day, date, month, year} uint8_t data[7] = {0}; // to read data over I2C const uint8_t address[] = {0x00}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read all time bytes return NULL; // error occurred } time[0] = ((data[0]&0x70)>>4)*10+(data[0]&0x0f); // convert seconds from BCD time[1] = (data[1]>>4)*10+(data[1]&0x0f); // convert minutes from BCD time[2] = 0; // re-initialize hours if (data[2]&0x40) { // 12 hour mode if (data[2]&0x02) { // PM time[2] += 12; // add the 12 hours } time[2] += ((data[2]&0x10)>>4)*10; // convert BCD coding into hours (first digit) } else { time[2] = ((data[2]&0x30)>>4)*10; // convert BCD coding into hours (first digit) } time[2] += (data[2]&0x0f); // convert BCD coding into hours (second digit) time[3] = (data[3]&0x07); // convert BCD coding into days time[4] = ((data[4]&0x30)>>4)*10+(data[4]&0x0f); // convert BCD coding into date time[5] = ((data[5]&0x10)>>4)*10+(data[5]&0x0f); // convert BCD coding into month time[6] = ((data[6]&0xf0)>>4)*10+(data[6]&0x0f); // convert BCD coding into year return time; } bool rtc_ds1307_read_ram(uint8_t* data, uint8_t start, uint8_t length) { // sanity checks if (data==NULL || length==0) { // nothing to read return false; } if (start>55 || start+length>56) { // out of bounds RAM return false; } const uint8_t address[] = {0x08+start}; // memory address for data return i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // read RAM (starting at 0x08) } bool rtc_ds1307_oscillator_disable(void) { uint8_t data[1] = {0}; // to write CH value data over I2C const uint8_t address[] = {0x00}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read seconds with CH value return false; } data[0] |= 0x80; // set CH to disable oscillator return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with CH value } bool rtc_ds1307_oscillator_enable(void) { uint8_t data[1] = {0}; // to write CH value data over I2C const uint8_t address[] = {0x00}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read seconds with CH value return false; } data[0] &= 0x7f; // clear CH to enable oscillator return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with CH value } bool rtc_ds1307_write_square_wave(uint16_t frequency) { uint8_t data[1] = {0}; // to write control register value data over I2C switch (frequency) { // set RS1/RS0 based on frequency case 0: data[0] = 0; break; case 1: data[0] = 0|(1<<4); break; case 4096: data[0] = 1|(1<<4); break; case 8192: data[0] = 2|(1<<4); break; case 32768: data[0] = 3|(1<<4); break; default: // unspecified frequency return false; } const uint8_t address[] = {0x07}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with CH value } bool rtc_ds1307_write_seconds(uint8_t seconds) { if (seconds>59) { return false; } uint8_t data[1] = {0}; // to read CH value data and write seconds value over I2C const uint8_t address[] = {0x00}; // memory address for data if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read seconds with CH value return false; } data[0] &= 0x80; // only keep CH flag data[0] |= (((seconds/10)%6)<<4)+(seconds%10); // encode seconds in BCD format return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with previous CH value } bool rtc_ds1307_write_minutes(uint8_t minutes) { if (minutes>59) { return false; } uint8_t data[1] = {0}; // to write time value data[0] = (((minutes/10)%6)<<4)+(minutes%10); // encode minutes in BCD format const uint8_t address[] = {0x01}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC } bool rtc_ds1307_write_hours(uint8_t hours) { if (hours>24) { return false; } uint8_t data[1] = {0}; // to write time value data[0] = (((hours/10)%3)<<4)+(hours%10); // encode hours in BCD 24h format const uint8_t address[] = {0x02}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC } bool rtc_ds1307_write_day(uint8_t day) { if (day<1 || day>7) { return false; } uint8_t data[1] = {0}; // to write time value data[0] = (day%8); // encode day in BCD format const uint8_t address[] = {0x03}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC } bool rtc_ds1307_write_date(uint8_t date) { if (date<1 || date>31) { return false; } uint8_t data[1] = {0}; // to write time value data[0] = (((date/10)%4)<<4)+(date%10); // encode date in BCD format const uint8_t address[] = {0x04}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC } bool rtc_ds1307_write_month(uint8_t month) { if (month<1 || month>12) { return false; } uint8_t data[1] = {0}; // to write time value data[0] = (((month/10)%2)<<4)+(month%10); // encode month in BCD format const uint8_t address[] = {0x05}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC } bool rtc_ds1307_write_year(uint8_t year) { if (year>99) { return false; } uint8_t data[1] = {0}; // to write time value data[0] = (((year/10)%10)<<4)+(year%10); // encode year in BCD format const uint8_t address[] = {0x06}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC } bool rtc_ds1307_write_time(uint8_t seconds, uint8_t minutes, uint8_t hours, uint8_t day, uint8_t date, uint8_t month, uint8_t year) { uint8_t data[7] = {0}; // to write all time values const uint8_t address[] = {0x00}; // memory address for data // seconds if (seconds>59) { return false; } if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, 1)) { // read seconds with CH value return false; } data[0] &= 0x80; // only keep CH flag data[0] |= (((seconds/10)%6)<<4)+(seconds%10); // encode seconds in BCD format // minutes if (minutes>59) { return false; } data[1] = (((minutes/10)%6)<<4)+(minutes%10); // encode minutes in BCD format // hours if (hours>24) { return false; } data[2] = (((hours/10)%3)<<4)+(hours%10); // encode hours in BCD 24h format // day if (day<1 || day>7) { return false; } data[3] = (day%8); // encode day in BCD format // date if (date<1 || date>31) { return false; } data[4] = (((date/10)%4)<<4)+(date%10); // encode date in BCD format // month if (month<1 || month>12) { return false; } data[5] = (((month/10)%2)<<4)+(month%10); // encode month in BCD format // year if (year>99) { return false; } data[6] = (((year/10)%10)<<4)+(year%10); // encode year in BCD format return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC } bool rtc_ds1307_write_ram(uint8_t* data, uint8_t start, uint8_t length) { // sanity checks if (data==NULL || length==0) { // nothing to read return false; } if (start>55 || start+length>56) { // out of bounds RAM return false; } const uint8_t address[] = {0x08+start}; // memory address for data return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write RAM (starting at 0x08) }