Merge pull request #15245 from haukepetersen/add_driver_ds3231
drivers: add support for DS3231 RTC
This commit is contained in:
commit
71d970cb00
1
drivers/ds3231/Makefile
Normal file
1
drivers/ds3231/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
1
drivers/ds3231/Makefile.dep
Normal file
1
drivers/ds3231/Makefile.dep
Normal file
@ -0,0 +1 @@
|
||||
FEATURES_REQUIRED += periph_i2c
|
||||
2
drivers/ds3231/Makefile.include
Normal file
2
drivers/ds3231/Makefile.include
Normal file
@ -0,0 +1,2 @@
|
||||
USEMODULE_INCLUDES_ds3231 := $(LAST_MAKEFILEDIR)/include
|
||||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_ds3231)
|
||||
268
drivers/ds3231/ds3231.c
Normal file
268
drivers/ds3231/ds3231.c
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup drivers_ds3231
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief DS3231 RTC driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bcd.h"
|
||||
#include "ds3231.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
/* some metadata about the devices registers */
|
||||
#define DATE_REG_NUMOF 7U
|
||||
#define A1_REG_NUMOF 4U
|
||||
#define A2_REG_NUMOF 3U
|
||||
|
||||
/* register addresses */
|
||||
#define REG_SEC 0x00
|
||||
#define REG_MIN 0x01
|
||||
#define REG_HOUR 0x02
|
||||
#define REG_DAY 0x03
|
||||
#define REG_DATE 0x04
|
||||
#define REG_MONTH 0x05
|
||||
#define REG_YEAR 0x06
|
||||
#define REG_A1_SEC 0x07
|
||||
#define REG_A1_MIN 0x08
|
||||
#define REG_A1_HOUR 0x09
|
||||
#define REG_A1_DAYDATE 0x0a
|
||||
#define REG_A2_MIN 0x0b
|
||||
#define REG_A2_HOUR 0x0c
|
||||
#define REG_A2_DAYDATE 0x0d
|
||||
#define REG_CTRL 0x0e
|
||||
#define REG_STATUS 0x0f
|
||||
#define REG_AGING_OFFSET 0x10
|
||||
#define REG_TEMP_MSB 0x11
|
||||
#define REG_TEMP_LSB 0x12
|
||||
|
||||
/* general register bitmasks */
|
||||
#define MASK_SEC10 0x70
|
||||
#define MASK_SEC 0x0f
|
||||
#define MASK_MIN10 0x70
|
||||
#define MASK_MIN 0x0f
|
||||
#define MASK_H10 0x10
|
||||
#define MASK_AMPM_H20 0x20
|
||||
#define MASK_H12_H24 0x40
|
||||
#define MASK_H20H10 (MASK_H10 | MASK_AMPM_H20)
|
||||
#define MASK_HOUR 0x0f
|
||||
#define MASK_DAY 0x07
|
||||
#define MASK_DATE10 0x30
|
||||
#define MASK_DATE 0x0f
|
||||
#define MASK_MONTH10 0x10
|
||||
#define MASK_MONTH 0x0f
|
||||
#define MASK_CENTURY 0x80
|
||||
#define MASK_DY_DT 0x60
|
||||
|
||||
/* control register bitmaps */
|
||||
#define CTRL_EOSC 0x80
|
||||
#define CTRL_BBSQW 0x40
|
||||
#define CTRL_CONV 0x20
|
||||
#define CTRL_RS2 0x10
|
||||
#define CTRL_RS1 0x80
|
||||
#define CTRL_RS (CTRL_RS2 | CTRL_RS1)
|
||||
#define CTRL_INTCN 0x40
|
||||
#define CTRL_A2IE 0x20
|
||||
#define CTRL_A1IE 0x10
|
||||
#define CTRL_AIE (CTRL_A2IE | CTRL_A1IE)
|
||||
|
||||
/* status register bitmaps */
|
||||
#define STAT_OSF 0x80
|
||||
#define STAT_EN32KHZ 0x08
|
||||
#define STAT_BSY 0x04
|
||||
#define STAT_A2F 0x02
|
||||
#define STAT_A1F 0x01
|
||||
#define STAT_AF (STAT_A2F | STAT_A1F)
|
||||
|
||||
static int _read(const ds3231_t *dev, uint8_t reg, uint8_t *buf, size_t len,
|
||||
int acquire, int release)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (acquire && i2c_acquire(dev->bus)) {
|
||||
return -EIO;
|
||||
}
|
||||
res = i2c_read_regs(dev->bus, DS3231_I2C_ADDR, reg, buf, len, 0);
|
||||
if (res < 0) {
|
||||
i2c_release(dev->bus);
|
||||
return -EIO;
|
||||
}
|
||||
if (release) {
|
||||
i2c_release(dev->bus);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _write(const ds3231_t *dev, uint8_t reg, uint8_t *buf, size_t len,
|
||||
int acquire, int release)
|
||||
{
|
||||
if (acquire && i2c_acquire(dev->bus)) {
|
||||
return -EIO;
|
||||
}
|
||||
if (i2c_write_regs(dev->bus, DS3231_I2C_ADDR, reg, buf, len, 0) < 0) {
|
||||
i2c_release(dev->bus);
|
||||
return -EIO;
|
||||
}
|
||||
if (release) {
|
||||
i2c_release(dev->bus);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _clrset(const ds3231_t *dev, uint8_t reg,
|
||||
uint8_t clr_mask, uint8_t set_mask, int acquire, int release)
|
||||
{
|
||||
uint8_t old;
|
||||
uint8_t new;
|
||||
|
||||
if (_read(dev, reg, &old, 1, acquire, 0) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
new = ((old &= ~clr_mask) | set_mask);
|
||||
if (_write(dev, reg, &new, 1, 0, release) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ds3231_init(ds3231_t *dev, const ds3231_params_t *params)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* write device descriptor */
|
||||
memset(dev, 0, sizeof(ds3231_t));
|
||||
dev->bus = params->bus;
|
||||
|
||||
/* en or disable 32KHz output */
|
||||
if (params->opt & DS2321_OPT_32KHZ_ENABLE) {
|
||||
res = _clrset(dev, REG_STATUS, 0, STAT_EN32KHZ, 1, 0);
|
||||
}
|
||||
else {
|
||||
res = _clrset(dev, REG_STATUS, STAT_EN32KHZ, 0, 1, 0);
|
||||
}
|
||||
if (res != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* disable interrupts and configure backup battery */
|
||||
uint8_t clr = (CTRL_A1IE | CTRL_A2IE);
|
||||
uint8_t set = 0;
|
||||
/* if configured, start the oscillator */
|
||||
if (params->opt & DS3231_OPT_BAT_ENABLE) {
|
||||
clr |= CTRL_EOSC;
|
||||
}
|
||||
else {
|
||||
set = CTRL_EOSC;
|
||||
}
|
||||
|
||||
return _clrset(dev, REG_CTRL, clr, set, 0, 1);
|
||||
}
|
||||
|
||||
int ds3231_get_time(const ds3231_t *dev, struct tm *time)
|
||||
{
|
||||
uint8_t raw[DATE_REG_NUMOF];
|
||||
|
||||
/* read date registers */
|
||||
if (_read(dev, REG_SEC, raw, DATE_REG_NUMOF, 1, 1) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* convert data to struct tm */
|
||||
time->tm_sec = bcd_to_byte(raw[REG_SEC]);
|
||||
time->tm_min = bcd_to_byte(raw[REG_MIN]);
|
||||
if (raw[REG_HOUR] & MASK_H12_H24) {
|
||||
time->tm_hour = bcd_to_byte(raw[REG_HOUR] & (MASK_HOUR | MASK_H10));
|
||||
if (raw[REG_HOUR] & MASK_AMPM_H20) {
|
||||
time->tm_hour += 12;
|
||||
}
|
||||
}
|
||||
else {
|
||||
time->tm_hour = bcd_to_byte(raw[REG_HOUR] & (MASK_HOUR | MASK_H20H10));
|
||||
}
|
||||
time->tm_mday = bcd_to_byte(raw[REG_DATE]);
|
||||
time->tm_mon = bcd_to_byte(raw[REG_MONTH] & ~MASK_CENTURY) - 1;
|
||||
time->tm_year = bcd_to_byte(raw[REG_YEAR]);
|
||||
if (raw[REG_MONTH] & MASK_CENTURY) {
|
||||
time->tm_year += 100;
|
||||
}
|
||||
time->tm_wday = bcd_to_byte(raw[REG_DAY]) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ds3231_set_time(const ds3231_t *dev, const struct tm *time)
|
||||
{
|
||||
uint8_t raw[DATE_REG_NUMOF];
|
||||
|
||||
/* some validity checks */
|
||||
if (time->tm_year > 200) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* convert struct tm to raw BDC data */
|
||||
raw[REG_SEC] = bcd_from_byte(time->tm_sec);
|
||||
raw[REG_MIN] = bcd_from_byte(time->tm_min);
|
||||
/* note: we always set the hours in 24-hour format */
|
||||
raw[REG_HOUR] = bcd_from_byte(time->tm_hour);
|
||||
raw[REG_DAY] = bcd_from_byte(time->tm_wday + 1);
|
||||
raw[REG_DATE] = bcd_from_byte(time->tm_mday);
|
||||
raw[REG_MONTH] = bcd_from_byte(time->tm_mon + 1);
|
||||
raw[REG_YEAR] = bcd_from_byte(time->tm_year % 100);
|
||||
if (time->tm_year > 100) {
|
||||
raw[REG_MONTH] |= MASK_CENTURY;
|
||||
}
|
||||
|
||||
/* write time to device */
|
||||
if (_write(dev, REG_SEC, raw, DATE_REG_NUMOF, 1, 1) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ds3231_get_aging_offset(const ds3231_t *dev, int8_t *offset)
|
||||
{
|
||||
return _read(dev, REG_AGING_OFFSET, (uint8_t *)offset, 1, 1, 1);
|
||||
}
|
||||
|
||||
int ds3231_set_aging_offset(const ds3231_t *dev, int8_t offset)
|
||||
{
|
||||
return _write(dev, REG_AGING_OFFSET, (uint8_t *)&offset, 1, 1, 1);
|
||||
}
|
||||
|
||||
int ds3231_get_temp(const ds3231_t *dev, int16_t *temp)
|
||||
{
|
||||
uint8_t raw[2];
|
||||
int res = _read(dev, REG_TEMP_MSB, raw, 2, 1, 1);
|
||||
if (res != 0) {
|
||||
return -EIO;
|
||||
}
|
||||
*temp = ((((int16_t)raw[0] << 8) | raw[1]) >> 6) * 25;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ds3231_enable_bat(const ds3231_t *dev)
|
||||
{
|
||||
return _clrset(dev, REG_CTRL, CTRL_EOSC, 0, 1, 1);
|
||||
}
|
||||
|
||||
int ds3231_disable_bat(const ds3231_t *dev)
|
||||
{
|
||||
return _clrset(dev, REG_CTRL, 0, CTRL_EOSC, 1, 1);
|
||||
}
|
||||
55
drivers/ds3231/include/ds3231_params.h
Normal file
55
drivers/ds3231/include/ds3231_params.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup drivers_ds3231
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Default configuration for DS3231 devices
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
#ifndef DS3231_PARAMS_H
|
||||
#define DS3231_PARAMS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Default configuration parameters for the DS3231 driver
|
||||
* @{
|
||||
*/
|
||||
#ifndef DS3231_PARAM_I2C
|
||||
#define DS3231_PARAM_I2C I2C_DEV(0)
|
||||
#endif
|
||||
#ifndef DS3231_PARAM_OPT
|
||||
#define DS3231_PARAM_OPT (DS3231_OPT_BAT_ENABLE)
|
||||
#endif
|
||||
|
||||
#ifndef DS3231_PARAMS
|
||||
#define DS3231_PARAMS { .bus = DS3231_PARAM_I2C, \
|
||||
.opt = DS3231_PARAM_OPT, }
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief DS3231 configuration
|
||||
*/
|
||||
static const ds3231_params_t ds3231_params[] =
|
||||
{
|
||||
DS3231_PARAMS
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DS3231_PARAMS_H */
|
||||
/** @} */
|
||||
162
drivers/include/ds3231.h
Normal file
162
drivers/include/ds3231.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup drivers_ds3231 DS3231 Real Time Clock
|
||||
* @ingroup drivers_sensors
|
||||
* @brief Driver for the Maxim DS3231 extremely accurate RTC
|
||||
*
|
||||
* # About
|
||||
* This module implements a device driver for Maxim DS3231 RTC.
|
||||
*
|
||||
* # Implementation status
|
||||
* The current implementation does only support reading and setting of time
|
||||
* registers as well as reading the temperature register and configuring the
|
||||
* aging offset.
|
||||
*
|
||||
* Setting alarms and configuring the square wave output is not yet supported.
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
* @brief Interface definition for the Maxim DS3231 RTC
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef DS3231_H
|
||||
#define DS3231_H
|
||||
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "periph/i2c.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default address of DS3231 sensors
|
||||
*/
|
||||
#define DS3231_I2C_ADDR 0x68
|
||||
|
||||
/**
|
||||
* @brief Configuration options
|
||||
*/
|
||||
enum {
|
||||
DS3231_OPT_BAT_ENABLE = 0x01, /* enable backup battery on startup */
|
||||
DS2321_OPT_32KHZ_ENABLE = 0x02, /* enable 32KHz output */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Device descriptor for DS3231 devices
|
||||
*/
|
||||
typedef struct {
|
||||
i2c_t bus; /**< I2C bus the device is connected to */
|
||||
} ds3231_t;
|
||||
|
||||
/**
|
||||
* @brief Set of configuration parameters for DS3231 devices
|
||||
*/
|
||||
typedef struct {
|
||||
i2c_t bus; /**< I2C bus the device is connected to */
|
||||
uint8_t opt; /**< additional options */
|
||||
} ds3231_params_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the given DS3231 device
|
||||
*
|
||||
* @param[out] dev device descriptor of the targeted device
|
||||
* @param[in] params device configuration
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO if no DS3231 device was found
|
||||
*/
|
||||
int ds3231_init(ds3231_t *dev, const ds3231_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Get date and time from the device
|
||||
*
|
||||
* @param[in] dev DS3231 device descriptor
|
||||
* @param[out] time current date and time from on device
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on I2C communication error
|
||||
*/
|
||||
int ds3231_get_time(const ds3231_t *dev, struct tm *time);
|
||||
|
||||
/**
|
||||
* @brief Set date and time of the device
|
||||
*
|
||||
* @param[in] dev DS3231 device descriptor
|
||||
* @param[in] time target date and time
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on I2C communication error
|
||||
*/
|
||||
int ds3231_set_time(const ds3231_t *dev, const struct tm *time);
|
||||
|
||||
/**
|
||||
* @brief Get the configured aging offset (see datasheet for more information)
|
||||
*
|
||||
* @param[in] dev DS3231 device descriptor
|
||||
* @param[out] offset aging offset
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on I2C communication error
|
||||
*/
|
||||
int ds3231_get_aging_offset(const ds3231_t *dev, int8_t *offset);
|
||||
|
||||
/**
|
||||
* @brief Set the aging offset (see datasheet for more information)
|
||||
*
|
||||
* @param[in] dev DS3231 device descriptor
|
||||
* @param[in] offset aging offset
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on I2C communication error
|
||||
*/
|
||||
int ds3231_set_aging_offset(const ds3231_t *dev, int8_t offset);
|
||||
|
||||
/**
|
||||
* @brief Get temperature from the device
|
||||
*
|
||||
* @param[in] dev DS3231 device descriptor
|
||||
* @param[out] temp current value of the temperature register [in centi °C]
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on I2C communication error
|
||||
*/
|
||||
int ds3231_get_temp(const ds3231_t *dev, int16_t *temp);
|
||||
|
||||
/**
|
||||
* @brief Enable the backup battery
|
||||
*
|
||||
* @param[in] dev DS3231 device descriptor
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on I2C communication error
|
||||
*/
|
||||
int ds3231_enable_bat(const ds3231_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Disable the backup battery
|
||||
*
|
||||
* @param[in] dev DS3231 device descriptor
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -EIO on I2C communication error
|
||||
*/
|
||||
int ds3231_disable_bat(const ds3231_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DS3231_H */
|
||||
/** @} */
|
||||
11
tests/driver_ds3231/Makefile
Normal file
11
tests/driver_ds3231/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
# Blacklist iotlab boards since a different device has the same i2c address
|
||||
BOARD_BLACKLIST := iotlab-a8-m3 \
|
||||
iotlab-m3
|
||||
|
||||
USEMODULE += ds3231
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += shell
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
12
tests/driver_ds3231/Makefile.ci
Normal file
12
tests/driver_ds3231/Makefile.ci
Normal file
@ -0,0 +1,12 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-l011k4 \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
#
|
||||
284
tests/driver_ds3231/main.c
Normal file
284
tests/driver_ds3231/main.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Test application for the DS3231 RTC driver
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "xtimer.h"
|
||||
#include "ds3231.h"
|
||||
#include "ds3231_params.h"
|
||||
|
||||
#define ISOSTR_LEN (20U)
|
||||
#define TEST_DELAY (2U)
|
||||
|
||||
static ds3231_t _dev;
|
||||
|
||||
/* 2010-09-22T15:10:42 is the author date of RIOT's initial commit */
|
||||
static struct tm _riot_bday = {
|
||||
.tm_sec = 42,
|
||||
.tm_min = 10,
|
||||
.tm_hour = 15,
|
||||
.tm_wday = 3,
|
||||
.tm_mday = 22,
|
||||
.tm_mon = 8,
|
||||
.tm_year = 110
|
||||
};
|
||||
|
||||
/* parse ISO date string (YYYY-MM-DDTHH:mm:ss) to struct tm */
|
||||
static int _tm_from_str(const char *str, struct tm *time)
|
||||
{
|
||||
char tmp[5];
|
||||
|
||||
if (strlen(str) != ISOSTR_LEN - 1) {
|
||||
return -1;
|
||||
}
|
||||
if ((str[4] != '-') || (str[7] != '-') || (str[10] != 'T') ||
|
||||
(str[13] != ':') || (str[16] != ':')) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(time, 0, sizeof(struct tm));
|
||||
|
||||
memcpy(tmp, str, 4);
|
||||
tmp[4] = '\0';
|
||||
str += 5;
|
||||
time->tm_year = atoi(tmp) - 1900;
|
||||
|
||||
memcpy(tmp, str, 2);
|
||||
tmp[2] = '\0';
|
||||
str += 3;
|
||||
time->tm_mon = atoi(tmp) - 1;
|
||||
|
||||
memcpy(tmp, str, 2);
|
||||
str += 3;
|
||||
time->tm_mday = atoi(tmp);
|
||||
|
||||
memcpy(tmp, str, 2);
|
||||
str += 3;
|
||||
time->tm_hour = atoi(tmp);
|
||||
|
||||
memcpy(tmp, str, 2);
|
||||
str += 3;
|
||||
time->tm_min = atoi(tmp);
|
||||
|
||||
memcpy(tmp, str, 2);
|
||||
time->tm_sec = atoi(tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cmd_get(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
char dstr[ISOSTR_LEN];
|
||||
|
||||
struct tm time;
|
||||
ds3231_get_time(&_dev, &time);
|
||||
|
||||
size_t pos = strftime(dstr, ISOSTR_LEN, "%Y-%m-%dT%H:%M:%S", &time);
|
||||
dstr[pos] = '\0';
|
||||
printf("The current time is: %s\n", dstr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cmd_set(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf("usage: %s <iso-date-str YYYY-MM-DDTHH:mm:ss>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strlen(argv[1]) != (ISOSTR_LEN - 1)) {
|
||||
puts("error: input date string has invalid length");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct tm target_time;
|
||||
int res = _tm_from_str(argv[1], &target_time);
|
||||
if (res != 0) {
|
||||
puts("error: unable do parse input date string");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ds3231_set_time(&_dev, &target_time);
|
||||
|
||||
printf("success: time set to %s\n", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cmd_temp(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
int16_t temp;
|
||||
|
||||
int res = ds3231_get_temp(&_dev, &temp);
|
||||
if (res != 0) {
|
||||
puts("error: unable to read temperature");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int t1 = temp / 100;
|
||||
int t2 = temp - (t1 * 100);
|
||||
printf("Current temperature: %i.%02i°C\n", t1, t2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cmd_aging(int argc, char **argv)
|
||||
{
|
||||
int8_t val;
|
||||
int res;
|
||||
|
||||
if (argc == 1) {
|
||||
res = ds3231_get_aging_offset(&_dev, &val);
|
||||
if (res != 0) {
|
||||
puts("error: unable to obtain aging offset");
|
||||
return 1;
|
||||
}
|
||||
printf("Aging offset: %i\n", (int)val);
|
||||
}
|
||||
else {
|
||||
val = atoi(argv[1]);
|
||||
res = ds3231_set_aging_offset(&_dev, val);
|
||||
if (res != 0) {
|
||||
puts("error: unable to set againg offset");
|
||||
return 1;
|
||||
}
|
||||
printf("Success: set aging offset to %i\n", (int)val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cmd_bat(int argc, char **argv)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (argc != 2) {
|
||||
printf("usage: %s <'0' or '1'>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argv[1][0] == '1') {
|
||||
res = ds3231_enable_bat(&_dev);
|
||||
if (res == 0) {
|
||||
puts("success: backup battery enabled");
|
||||
}
|
||||
else {
|
||||
puts("error: unable to enable backup battery");
|
||||
}
|
||||
}
|
||||
else if (argv[1][0] == '0') {
|
||||
res = ds3231_disable_bat(&_dev);
|
||||
if (res == 0) {
|
||||
puts("success: backup battery disabled");
|
||||
}
|
||||
else {
|
||||
puts("error: unable to disable backup battery");
|
||||
}
|
||||
}
|
||||
else {
|
||||
puts("error: unable to parse command");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cmd_test(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
int res;
|
||||
struct tm time;
|
||||
|
||||
puts("testing device now");
|
||||
|
||||
/* set time to RIOT birthdate */
|
||||
res = ds3231_set_time(&_dev, &_riot_bday);
|
||||
if (res != 0) {
|
||||
puts("error: unable to set time");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* read time and compare to initial value */
|
||||
res = ds3231_get_time(&_dev, &time);
|
||||
if (res != 0) {
|
||||
puts("error: unable to read time");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((mktime(&time) - mktime(&_riot_bday)) > 1) {
|
||||
puts("error: device time has unexpected value");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* wait a short while and check if time has progressed */
|
||||
xtimer_sleep(TEST_DELAY);
|
||||
res = ds3231_get_time(&_dev, &time);
|
||||
if (res != 0) {
|
||||
puts("error: unable to read time");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(mktime(&time) > mktime(&_riot_bday))) {
|
||||
puts("error: time did not progress");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const shell_command_t shell_commands[] = {
|
||||
{ "time_get", "init as output (push-pull mode)", _cmd_get },
|
||||
{ "time_set", "init as input w/o pull resistor", _cmd_set },
|
||||
{ "temp", "get temperature", _cmd_temp },
|
||||
{ "aging", "get or set the aging offset", _cmd_aging },
|
||||
{ "bat", "en/disable backup battery", _cmd_bat },
|
||||
{ "test", "test if the device is working properly", _cmd_test},
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
puts("DS3231 RTC test\n");
|
||||
|
||||
/* initialize the device */
|
||||
res = ds3231_init(&_dev, &ds3231_params[0]);
|
||||
if (res != 0) {
|
||||
puts("error: unable to initialize DS3231 [I2C initialization error]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* start the shell */
|
||||
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
19
tests/driver_ds3231/tests/01-run.py
Executable file
19
tests/driver_ds3231/tests/01-run.py
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.sendline("test")
|
||||
child.expect_exact("OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
||||
Loading…
x
Reference in New Issue
Block a user