micropython/ports/renesas-ra/machine_rtc.c

350 lines
11 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
* Copyright (c) 2021 Renesas Electronics Corporation
*
* 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.
*/
#include <stdio.h>
#include "py/runtime.h"
#include "shared/timeutils/timeutils.h"
#include "extint.h"
#include "rtc.h"
#include "irq.h"
#if defined(RA4M1) | defined(RA4M3) | defined(RA4W1) | defined(RA6M1) | defined(RA6M2) | defined(RA6M3)
#include "ra_rtc.h"
#endif
#define RTC_INIT_YEAR 2019
#define RTC_INIT_MONTH 1
#define RTC_INIT_DATE 1
#define RTC_INIT_WEEKDAY 2 /* Tuesday */
#define RTC_INIT_HOUR 0
#define RTC_INIT_MINUTE 0
#define RTC_INIT_SECOND 0
/// \moduleref pyb
/// \class RTC - real time clock
///
/// The RTC is and independent clock that keeps track of the date
/// and time.
///
/// Example usage:
///
/// rtc = pyb.RTC()
/// rtc.datetime((2014, 5, 1, 4, 13, 0, 0, 0))
/// print(rtc.datetime())
#define HAL_StatusTypeDef void
// rtc_info indicates various things about RTC startup
// it's a bit of a hack at the moment
static mp_uint_t rtc_info;
STATIC uint32_t rtc_startup_tick;
STATIC bool rtc_need_init_finalise = false;
STATIC uint32_t rtc_wakeup_param;
STATIC void rtc_calendar_config(void) {
ra_rtc_t tm;
tm.year = RTC_INIT_YEAR - 2000;
tm.month = RTC_INIT_MONTH;
tm.date = RTC_INIT_DATE;
tm.weekday = RTC_INIT_WEEKDAY;
tm.hour = RTC_INIT_HOUR;
tm.minute = RTC_INIT_MINUTE;
tm.second = RTC_INIT_SECOND;
ra_rtc_set_time(&tm);
}
void rtc_get_time(RTC_TimeTypeDef *time) {
ra_rtc_t dt;
ra_rtc_get_time(&dt);
time->DayLightSaving = 0;
time->Hours = dt.hour;
time->Minutes = dt.minute;
time->SecondFraction = 0;
time->Seconds = dt.second;
// time->StoreOperation;
time->SubSeconds = 0;
time->TimeFormat = 0;
}
void rtc_get_date(RTC_DateTypeDef *date) {
ra_rtc_t dt;
ra_rtc_get_time(&dt);
date->Date = dt.date;
date->Month = dt.month;
date->WeekDay = dt.weekday;
date->Year = (uint8_t)(dt.year - 2000);
}
void rtc_init_start(bool force_init) {
/* Configure RTC prescaler and RTC data registers */
#if (MICROPY_HW_RTC_SOURCE == 1)
// clock source is LOCO
ra_rtc_init(1);
#else
// clock source is subclock
ra_rtc_init(0);
#endif
rtc_need_init_finalise = false;
if (!force_init) {
// So far, this case (force_init == false) is not called.
rtc_info |= 0x40000;
// or rtc_info |= 0x80000;
return;
}
rtc_startup_tick = HAL_GetTick();
rtc_info = 0x3f000000 | (rtc_startup_tick & 0xffffff);
rtc_need_init_finalise = true;
}
void rtc_init_finalise(void) {
if (!rtc_need_init_finalise) {
return;
}
rtc_info = 0x20000000;
rtc_info |= (HAL_GetTick() - rtc_startup_tick) & 0xffff;
rtc_calendar_config();
rtc_need_init_finalise = false;
}
uint64_t mp_hal_time_ns(void) {
uint64_t ns = 0;
#if MICROPY_HW_ENABLE_RTC
// Get current according to the RTC.
rtc_init_finalise();
RTC_TimeTypeDef time;
RTC_DateTypeDef date;
rtc_get_time(&time);
rtc_get_date(&date);
ns = timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds);
ns *= 1000000000ULL;
#endif
return ns;
}
/******************************************************************************/
// MicroPython bindings
typedef struct _machine_rtc_obj_t {
mp_obj_base_t base;
} machine_rtc_obj_t;
STATIC const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}};
/// \classmethod \constructor()
/// Create an RTC object.
STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 0, 0, false);
// return constant object
return MP_OBJ_FROM_PTR(&machine_rtc_obj);
}
// force rtc to re-initialise
mp_obj_t machine_rtc_init(mp_obj_t self_in) {
rtc_init_start(true);
rtc_init_finalise();
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(machine_rtc_init_obj, machine_rtc_init);
/// \method info()
/// Get information about the startup time and reset source.
///
/// - The lower 0xffff are the number of milliseconds the RTC took to
/// start up.
/// - Bit 0x10000 is set if a power-on reset occurred.
/// - Bit 0x20000 is set if an external reset occurred
mp_obj_t machine_rtc_info(mp_obj_t self_in) {
return mp_obj_new_int(rtc_info);
}
MP_DEFINE_CONST_FUN_OBJ_1(machine_rtc_info_obj, machine_rtc_info);
/// \method datetime([datetimetuple])
/// Get or set the date and time of the RTC.
///
/// With no arguments, this method returns an 8-tuple with the current
/// date and time. With 1 argument (being an 8-tuple) it sets the date
/// and time.
///
/// The 8-tuple has the following format:
///
/// (year, month, day, weekday, hours, minutes, seconds, subseconds)
///
/// `weekday` is 1-7 for Monday through Sunday.
///
/// `subseconds` counts down from 255 to 0
#define MEG_DIV_64 (1000000 / 64)
#define MEG_DIV_SCALE ((RTC_SYNCH_PREDIV + 1) / 64)
#if defined(MICROPY_HW_RTC_USE_US) && MICROPY_HW_RTC_USE_US
uint32_t rtc_subsec_to_us(uint32_t ss) {
return ((RTC_SYNCH_PREDIV - ss) * MEG_DIV_64) / MEG_DIV_SCALE;
}
uint32_t rtc_us_to_subsec(uint32_t us) {
return RTC_SYNCH_PREDIV - (us * MEG_DIV_SCALE / MEG_DIV_64);
}
#else
#define rtc_us_to_subsec
#define rtc_subsec_to_us
#endif
mp_obj_t machine_rtc_datetime(size_t n_args, const mp_obj_t *args) {
rtc_init_finalise();
if (n_args == 1) {
ra_rtc_t time;
ra_rtc_get_time(&time);
mp_obj_t tuple[8] = {
mp_obj_new_int(time.year),
mp_obj_new_int(time.month),
mp_obj_new_int(time.date),
mp_obj_new_int(time.weekday),
mp_obj_new_int(time.hour),
mp_obj_new_int(time.minute),
mp_obj_new_int(time.second),
mp_obj_new_int(0),
};
return mp_obj_new_tuple(8, tuple);
} else {
// set date and time
mp_obj_t *items;
mp_obj_get_array_fixed_n(args[1], 8, &items);
ra_rtc_t tm;
tm.year = mp_obj_get_int(items[0]);
tm.month = mp_obj_get_int(items[1]);
tm.date = mp_obj_get_int(items[2]);
tm.weekday = mp_obj_get_int(items[3]);
tm.hour = mp_obj_get_int(items[4]);
tm.minute = mp_obj_get_int(items[5]);
tm.second = mp_obj_get_int(items[6]);
ra_rtc_set_time(&tm);
return mp_const_none;
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime);
// wakeup(None)
// wakeup(ms, callback=None) - ms should be between 4ms - 2000ms
// wakeup(wucksel, wut, callback) - not implemented
mp_obj_t machine_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
bool enable = false;
mp_int_t ms;
mp_obj_t callback = mp_const_none;
uint32_t period = 0;
if (args[1] == mp_const_none) {
// disable wakeup
} else {
// time given in ms
ms = mp_obj_get_int(args[1]);
if (ms <= 4) {
period = 6;
} else if (ms <= 8) {
period = 7;
} else if (ms <= 16) {
period = 8;
} else if (ms <= 32) {
period = 9;
} else if (ms <= 63) {
period = 10;
} else if (ms <= 125) {
period = 11;
} else if (ms <= 250) {
period = 12;
} else if (ms <= 500) {
period = 13;
} else if (ms <= 1000) {
period = 14;
} else if (ms <= 2000) {
period = 15;
}
enable = true;
}
if (n_args >= 2) {
callback = args[2];
}
// set the callback
MP_STATE_PORT(pyb_extint_callback)[EXTI_RTC_WAKEUP] = callback;
pyb_extint_callback_arg[EXTI_RTC_WAKEUP] = MP_OBJ_NEW_SMALL_INT(EXTI_RTC_WAKEUP);
rtc_wakeup_param = EXTI_RTC_WAKEUP;
if (enable) {
if (callback != mp_const_none) {
ra_rtc_set_period_func((void *)extint_callback, (void *)&rtc_wakeup_param);
} else {
ra_rtc_set_period_func((void *)NULL, (void *)NULL);
}
ra_rtc_set_period_time(period);
ra_rtc_period_on();
} else {
ra_rtc_period_off();
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_wakeup_obj, 2, 3, machine_rtc_wakeup);
// calibration(None)
// calibration(cal)
// When an integer argument is provided, check that it falls in the range [-63(s) to 63(s)]
// and set the calibration value; otherwise return calibration value
mp_obj_t machine_rtc_calibration(size_t n_args, const mp_obj_t *args) {
rtc_init_finalise();
mp_int_t cal;
if (n_args == 2) {
cal = mp_obj_get_int(args[1]);
if (cal < -63 || cal > 63) {
mp_raise_ValueError(MP_ERROR_TEXT("calibration value out of range"));
} else {
ra_rtc_set_adjustment(cal, 0); // calibration for second
}
return mp_const_none;
} else {
// get calibration register
cal = (mp_int_t)ra_rtc_get_adjustment();
return mp_obj_new_int(cal);
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_calibration_obj, 1, 2, machine_rtc_calibration);
STATIC const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_rtc_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_rtc_info_obj) },
{ MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) },
{ MP_ROM_QSTR(MP_QSTR_wakeup), MP_ROM_PTR(&machine_rtc_wakeup_obj) },
{ MP_ROM_QSTR(MP_QSTR_calibration), MP_ROM_PTR(&machine_rtc_calibration_obj) },
};
STATIC MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table);
const mp_obj_type_t machine_rtc_type = {
{ &mp_type_type },
.name = MP_QSTR_RTC,
.make_new = machine_rtc_make_new,
.locals_dict = (mp_obj_dict_t *)&machine_rtc_locals_dict,
};