Merge pull request #168 from bhuvanchandra/ds3231-v3

DS3231 v3
This commit is contained in:
Johan Kanflo 2016-08-05 23:38:30 +02:00 committed by GitHub
commit 14f79a4857
6 changed files with 558 additions and 0 deletions

View file

@ -0,0 +1,3 @@
PROGRAM=ds3231_test
EXTRA_COMPONENTS = extras/ds3231 extras/i2c
include ../../common.mk

View file

@ -0,0 +1,41 @@
/* Test code for DS3231 high precision RTC module
*
* Part of esp-open-rtos
* Copyright (C) 2016 Bhuvanchandra DV <bhuvanchandra.dv@gmail.com>
* MIT Licensed as described in the file LICENSE
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "i2c/i2c.h"
#include "ds3231/ds3231.h"
void task1(void *pvParameters)
{
struct tm time;
float tempFloat;
while(1) {
vTaskDelay(100);
ds3231_getTime(&time);
ds3231_getTempFloat(&tempFloat);
printf("TIME:%d:%d:%d, TEMPERATURE:%.2f DegC\r\n", time.tm_hour, time.tm_min, time.tm_sec, tempFloat);
}
}
void user_init(void)
{
const int scl = 5, sda = 4;
uart_set_baud(0, 115200);
printf("\n");
printf("SDK version : %s\n", sdk_system_get_sdk_version());
printf("GIT version : %s\n", GITSHORTREV);
ds3231_Init(scl, sda);
xTaskCreate(task1, (signed char *)"tsk1", 256, NULL, 2, NULL);
}

22
extras/ds3231/LICENSE Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Richard A Burton <richardaburton@gmail.com>
Copyright (c) 2016 Bhuvanchandra DV <bhuvanchandra.dv@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,9 @@
# Component makefile for extras/ds3231
# expected anyone using bmp driver includes it as 'ds3231/ds3231.h'
INC_DIRS += $(ds3231_ROOT)..
# args for passing into compile rule generation
ds3231_SRC_DIR = $(ds3231_ROOT)
$(eval $(call component_compile_rules,ds3231))

296
extras/ds3231/ds3231.c Normal file
View file

@ -0,0 +1,296 @@
/* Driver for DS3231 high precision RTC module
*
* Part of esp-open-rtos
* Copyright (C) 2015 Richard A Burton <richardaburton@gmail.com>
* Copyright (C) 2016 Bhuvanchandra DV <bhuvanchandra.dv@gmail.com>
* MIT Licensed as described in the file LICENSE
*/
#include "ds3231.h"
#include "espressif/esp_common.h"
#include "espressif/sdk_private.h"
#include "esp8266.h"
#include "i2c/i2c.h"
/* Convert normal decimal to binary coded decimal */
static inline uint8_t decToBcd(uint8_t dec)
{
return(((dec / 10) * 16) + (dec % 10));
}
/* Convert binary coded decimal to normal decimal */
static inline uint8_t bcdToDec(uint8_t bcd)
{
return(((bcd / 16) * 10) + (bcd % 16));
}
/* Send a number of bytes to the rtc over i2c
* returns true to indicate success
*/
static inline bool ds3231_send(uint8_t *data, uint8_t len)
{
return i2c_slave_write(DS3231_ADDR, data, len);
}
/* Read a number of bytes from the rtc over i2c
* returns true to indicate success
*/
static inline bool ds3231_recv(uint8_t *data, uint8_t len)
{
return i2c_slave_read(DS3231_ADDR, data[0], data, len);
}
bool ds3231_setTime(struct tm *time)
{
uint8_t data[8];
/* start register */
data[0] = DS3231_ADDR_TIME;
/* time/date data */
data[1] = decToBcd(time->tm_sec);
data[2] = decToBcd(time->tm_min);
data[3] = decToBcd(time->tm_hour);
data[4] = decToBcd(time->tm_wday + 1);
data[5] = decToBcd(time->tm_mday);
data[6] = decToBcd(time->tm_mon + 1);
data[7] = decToBcd(time->tm_year - 100);
return ds3231_send(data, 8);
}
bool ds3231_setAlarm(uint8_t alarms, struct tm *time1, uint8_t option1, struct tm *time2, uint8_t option2)
{
int i = 0;
uint8_t data[8];
/* start register */
data[i++] = (alarms == DS3231_ALARM_2 ? DS3231_ADDR_ALARM2 : DS3231_ADDR_ALARM1);
/* alarm 1 data */
if (alarms != DS3231_ALARM_2) {
data[i++] = (option1 >= DS3231_ALARM1_MATCH_SEC ? decToBcd(time1->tm_sec) : DS3231_ALARM_NOTSET);
data[i++] = (option1 >= DS3231_ALARM1_MATCH_SECMIN ? decToBcd(time1->tm_min) : DS3231_ALARM_NOTSET);
data[i++] = (option1 >= DS3231_ALARM1_MATCH_SECMINHOUR ? decToBcd(time1->tm_hour) : DS3231_ALARM_NOTSET);
data[i++] = (option1 == DS3231_ALARM1_MATCH_SECMINHOURDAY ? (decToBcd(time1->tm_wday + 1) & DS3231_ALARM_WDAY) :
(option1 == DS3231_ALARM1_MATCH_SECMINHOURDATE ? decToBcd(time1->tm_mday) : DS3231_ALARM_NOTSET));
}
/* alarm 2 data */
if (alarms != DS3231_ALARM_1) {
data[i++] = (option2 >= DS3231_ALARM2_MATCH_MIN ? decToBcd(time2->tm_min) : DS3231_ALARM_NOTSET);
data[i++] = (option2 >= DS3231_ALARM2_MATCH_MINHOUR ? decToBcd(time2->tm_hour) : DS3231_ALARM_NOTSET);
data[i++] = (option2 == DS3231_ALARM2_MATCH_MINHOURDAY ? (decToBcd(time2->tm_wday + 1) & DS3231_ALARM_WDAY) :
(option2 == DS3231_ALARM2_MATCH_MINHOURDATE ? decToBcd(time2->tm_mday) : DS3231_ALARM_NOTSET));
}
return ds3231_send(data, i);
}
/* Get a byte containing just the requested bits
* pass the register address to read, a mask to apply to the register and
* an uint* for the output
* you can test this value directly as true/false for specific bit mask
* of use a mask of 0xff to just return the whole register byte
* returns true to indicate success
*/
bool ds3231_getFlag(uint8_t addr, uint8_t mask, uint8_t *flag)
{
uint8_t data[1];
/* get register */
data[0] = addr;
if (ds3231_send(data, 1) && ds3231_recv(data, 1)) {
/* return only requested flag */
*flag = (data[0] & mask);
return true;
}
return false;
}
/* Set/clear bits in a byte register, or replace the byte altogether
* pass the register address to modify, a byte to replace the existing
* value with or containing the bits to set/clear and one of
* DS3231_SET/DS3231_CLEAR/DS3231_REPLACE
* returns true to indicate success
*/
bool ds3231_setFlag(uint8_t addr, uint8_t bits, uint8_t mode)
{
uint8_t data[2];
data[0] = addr;
/* get status register */
if (ds3231_send(data, 1) && ds3231_recv(data+1, 1)) {
/* clear the flag */
if (mode == DS3231_REPLACE)
data[1] = bits;
else if (mode == DS3231_SET)
data[1] |= bits;
else
data[1] &= ~bits;
if (ds3231_send(data, 2)) {
return true;
}
}
return false;
}
bool ds3231_getOscillatorStopFlag(bool *flag)
{
uint8_t f;
if (ds3231_getFlag(DS3231_ADDR_STATUS, DS3231_STAT_OSCILLATOR, &f)) {
*flag = (f ? true : false);
return true;
}
return false;
}
inline bool ds3231_clearOscillatorStopFlag()
{
return ds3231_setFlag(DS3231_ADDR_STATUS, DS3231_STAT_OSCILLATOR, DS3231_CLEAR);
}
inline bool ds3231_getAlarmFlags(uint8_t *alarms)
{
return ds3231_getFlag(DS3231_ADDR_STATUS, DS3231_ALARM_BOTH, alarms);
}
inline bool ds3231_clearAlarmFlags(uint8_t alarms)
{
return ds3231_setFlag(DS3231_ADDR_STATUS, alarms, DS3231_CLEAR);
}
inline bool ds3231_enableAlarmInts(uint8_t alarms)
{
return ds3231_setFlag(DS3231_ADDR_CONTROL, DS3231_CTRL_ALARM_INTS | alarms, DS3231_SET);
}
inline bool ds3231_disableAlarmInts(uint8_t alarms)
{
/* Just disable specific alarm(s) requested
* does not disable alarm interrupts generally (which would enable the squarewave)
*/
return ds3231_setFlag(DS3231_ADDR_CONTROL, alarms, DS3231_CLEAR);
}
inline bool ds3231_enable32khz()
{
return ds3231_setFlag(DS3231_ADDR_STATUS, DS3231_STAT_32KHZ, DS3231_SET);
}
inline bool ds3231_disable32khz()
{
return ds3231_setFlag(DS3231_ADDR_STATUS, DS3231_STAT_32KHZ, DS3231_CLEAR);
}
inline bool ds3231_enableSquarewave()
{
return ds3231_setFlag(DS3231_ADDR_CONTROL, DS3231_CTRL_ALARM_INTS, DS3231_CLEAR);
}
inline bool ds3231_disableSquarewave()
{
return ds3231_setFlag(DS3231_ADDR_CONTROL, DS3231_CTRL_ALARM_INTS, DS3231_SET);
}
bool ds3231_setSquarewaveFreq(uint8_t freq)
{
uint8_t flag = 0;
if (ds3231_getFlag(DS3231_ADDR_CONTROL, 0xff, &flag)) {
/* clear current rate */
flag &= ~DS3231_CTRL_SQWAVE_8192HZ;
/* set new rate */
flag |= freq;
return ds3231_setFlag(DS3231_ADDR_CONTROL, flag, DS3231_REPLACE);
}
return false;
}
bool ds3231_getRawTemp(int16_t *temp)
{
uint8_t data[2];
data[0] = DS3231_ADDR_TEMP;
if (ds3231_send(data, 1) && ds3231_recv(data, 2)) {
*temp = (int16_t)(int8_t)data[0] << 2 | data[1] >> 6;
return true;
}
return false;
}
bool ds3231_getTempInteger(int8_t *temp)
{
int16_t tInt;
if (ds3231_getRawTemp(&tInt)) {
*temp = tInt >> 2;
return true;
}
return false;
}
bool ds3231_getTempFloat(float *temp)
{
int16_t tInt;
if (ds3231_getRawTemp(&tInt)) {
*temp = tInt * 0.25;
return true;
}
return false;
}
bool ds3231_getTime(struct tm *time)
{
uint8_t data[7];
/* start register address */
data[0] = DS3231_ADDR_TIME;
if (!ds3231_send(data, 1)) {
return false;
}
/* read time */
if (!ds3231_recv(data, 7)) {
return false;
}
/* convert to unix time structure */
time->tm_sec = bcdToDec(data[0]);
time->tm_min = bcdToDec(data[1]);
if (data[2] & DS3231_12HOUR_FLAG) {
/* 12H */
time->tm_hour = bcdToDec(data[2] & DS3231_12HOUR_MASK);
/* AM/PM? */
if (data[2] & DS3231_PM_FLAG) time->tm_hour += 12;
} else {
/* 24H */
time->tm_hour = bcdToDec(data[2]);
}
time->tm_wday = bcdToDec(data[3]) - 1;
time->tm_mday = bcdToDec(data[4]);
time->tm_mon = bcdToDec(data[5] & DS3231_MONTH_MASK) - 1;
time->tm_year = bcdToDec(data[6]) + 100;
time->tm_isdst = 0;
// apply a time zone (if you are not using localtime on the rtc or you want to check/apply DST)
//applyTZ(time);
return true;
}
void ds3231_Init(uint8_t scl, uint8_t sda)
{
i2c_init(scl, sda);
}

187
extras/ds3231/ds3231.h Normal file
View file

@ -0,0 +1,187 @@
/* Driver for DS3231 high precision RTC module
*
* Part of esp-open-rtos
* Copyright (C) 2015 Richard A Burton <richardaburton@gmail.com>
* Copyright (C) 2016 Bhuvanchandra DV <bhuvanchandra.dv@gmail.com>
* MIT Licensed as described in the file LICENSE
*/
#ifndef __DS3231_H__
#define __DS3231_H__
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#define DS3231_ADDR 0x68
#define DS3231_STAT_OSCILLATOR 0x80
#define DS3231_STAT_32KHZ 0x08
#define DS3231_STAT_BUSY 0x04
#define DS3231_STAT_ALARM_2 0x02
#define DS3231_STAT_ALARM_1 0x01
#define DS3231_CTRL_OSCILLATOR 0x80
#define DS3231_CTRL_SQUAREWAVE_BB 0x40
#define DS3231_CTRL_TEMPCONV 0x20
#define DS3231_CTRL_SQWAVE_4096HZ 0x10
#define DS3231_CTRL_SQWAVE_1024HZ 0x08
#define DS3231_CTRL_SQWAVE_8192HZ 0x18
#define DS3231_CTRL_SQWAVE_1HZ 0x00
#define DS3231_CTRL_ALARM_INTS 0x04
#define DS3231_CTRL_ALARM2_INT 0x02
#define DS3231_CTRL_ALARM1_INT 0x01
#define DS3231_ALARM_WDAY 0x40
#define DS3231_ALARM_NOTSET 0x80
#define DS3231_ADDR_TIME 0x00
#define DS3231_ADDR_ALARM1 0x07
#define DS3231_ADDR_ALARM2 0x0b
#define DS3231_ADDR_CONTROL 0x0e
#define DS3231_ADDR_STATUS 0x0f
#define DS3231_ADDR_AGING 0x10
#define DS3231_ADDR_TEMP 0x11
#define DS3231_12HOUR_FLAG 0x40
#define DS3231_12HOUR_MASK 0x1f
#define DS3231_PM_FLAG 0x20
#define DS3231_MONTH_MASK 0x1f
enum {
DS3231_SET = 0,
DS3231_CLEAR,
DS3231_REPLACE
};
enum {
DS3231_ALARM_NONE = 0,
DS3231_ALARM_1,
DS3231_ALARM_2,
DS3231_ALARM_BOTH
};
enum {
DS3231_ALARM1_EVERY_SECOND = 0,
DS3231_ALARM1_MATCH_SEC,
DS3231_ALARM1_MATCH_SECMIN,
DS3231_ALARM1_MATCH_SECMINHOUR,
DS3231_ALARM1_MATCH_SECMINHOURDAY,
DS3231_ALARM1_MATCH_SECMINHOURDATE
};
enum {
DS3231_ALARM2_EVERY_MIN = 0,
DS3231_ALARM2_MATCH_MIN,
DS3231_ALARM2_MATCH_MINHOUR,
DS3231_ALARM2_MATCH_MINHOURDAY,
DS3231_ALARM2_MATCH_MINHOURDATE
};
/* Set the time on the rtc
* timezone agnostic, pass whatever you like
* I suggest using GMT and applying timezone and DST when read back
* returns true to indicate success
*/
bool ds3231_setTime(struct tm *time);
/* Set alarms
* alarm1 works with seconds, minutes, hours and day of week/month, or fires every second
* alarm2 works with minutes, hours and day of week/month, or fires every minute
* not all combinations are supported, see DS3231_ALARM1_* and DS3231_ALARM2_* defines
* for valid options you only need to populate the fields you are using in the tm struct,
* and you can set both alarms at the same time (pass DS3231_ALARM_1/DS3231_ALARM_2/DS3231_ALARM_BOTH)
* if only setting one alarm just pass 0 for tm struct and option field for the other alarm
* if using DS3231_ALARM1_EVERY_SECOND/DS3231_ALARM2_EVERY_MIN you can pass 0 for tm stuct
* if you want to enable interrupts for the alarms you need to do that separately
* returns true to indicate success
*/
bool ds3231_setAlarm(uint8_t alarms, struct tm *time1, uint8_t option1, struct tm *time2, uint8_t option2);
/* Check if oscillator has previously stopped, e.g. no power/battery or disabled
* sets flag to true if there has been a stop
* returns true to indicate success
*/
bool ds3231_getOscillatorStopFlag(bool *flag);
/* Clear the oscillator stopped flag
* returns true to indicate success
*/
bool ds3231_clearOscillatorStopFlag();
/* Check which alarm(s) have past
* sets alarms to DS3231_ALARM_NONE/DS3231_ALARM_1/DS3231_ALARM_2/DS3231_ALARM_BOTH
* returns true to indicate success
*/
bool ds3231_getAlarmFlags(uint8_t *alarms);
/* Clear alarm past flag(s)
* pass DS3231_ALARM_1/DS3231_ALARM_2/DS3231_ALARM_BOTH
* returns true to indicate success
*/
bool ds3231_clearAlarmFlags(uint8_t alarm);
/* enable alarm interrupts (and disables squarewave)
* pass DS3231_ALARM_1/DS3231_ALARM_2/DS3231_ALARM_BOTH
* if you set only one alarm the status of the other is not changed
* you must also clear any alarm past flag(s) for alarms with
* interrupt enabled, else it will trigger immediately
* returns true to indicate success
*/
bool ds3231_enableAlarmInts(uint8_t alarms);
/* Disable alarm interrupts (does not (re-)enable squarewave)
* pass DS3231_ALARM_1/DS3231_ALARM_2/DS3231_ALARM_BOTH
* returns true to indicate success
*/
bool ds3231_disableAlarmInts(uint8_t alarms);
/* Enable the output of 32khz signal
* returns true to indicate success
*/
bool ds3231_enable32khz();
/* Disable the output of 32khz signal
* returns true to indicate success
*/
bool ds3231_disable32khz();
/* Enable the squarewave output (disables alarm interrupt functionality)
* returns true to indicate success
*/
bool ds3231_enableSquarewave();
/* Disable the squarewave output (which re-enables alarm interrupts, but individual
* alarm interrupts also need to be enabled, if not already, before they will trigger)
* returns true to indicate success
*/
bool ds3231_disableSquarewave();
/* Set the frequency of the squarewave output (but does not enable it)
* pass DS3231_SQUAREWAVE_RATE_1HZ/DS3231_SQUAREWAVE_RATE_1024HZ/DS3231_SQUAREWAVE_RATE_4096HZ/DS3231_SQUAREWAVE_RATE_8192HZ
* returns true to indicate success
*/
bool ds3231_setSquarewaveFreq(uint8_t freq);
/* Get the raw value
* returns true to indicate success
*/
bool ds3231_getRawTemp(int16_t *temp);
/* Get the temperature as an integer
* returns true to indicate success
*/
bool ds3231_getTempInteger(int8_t *temp);
/* Get the temerapture as a float (in quarter degree increments)
* returns true to indicate success
*/
bool ds3231_getTempFloat(float *temp);
/* Get the time from the rtc, populates a supplied tm struct
* returns true to indicate success
*/
bool ds3231_getTime(struct tm *time);
void ds3231_Init(uint8_t scl, uint8_t sda);
#endif