From: Nathael Pajani Date: Mon, 2 Feb 2015 01:39:24 +0000 (+0100) Subject: Add support for RTC Tested on Sub1GHz module because it's the only module i have... X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=0e6e32024ddb1573c4e9ac5efa978486d1eeb623;p=soft%2Flpc122x%2Fcore Add support for RTC Tested on Sub1GHz module because it's the only module i have with RTC oscilator. --- diff --git a/drivers/rtc.c b/drivers/rtc.c new file mode 100644 index 0000000..9c5b089 --- /dev/null +++ b/drivers/rtc.c @@ -0,0 +1,218 @@ +/**************************************************************************** + * drivers/rtc.c + * + * Copyright 2012 Nathael Pajani + * + * 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 2 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 . + * + *************************************************************************** */ + +/***************************************************************************** */ +/* RTC and RTC Interrupts */ +/***************************************************************************** */ + + + +#include +#include "core/lpc_regs_12xx.h" +#include "core/lpc_core_cm0.h" +#include "core/system.h" +#include "core/pio.h" +#include "drivers/rtc.h" + + +static uint32_t match_first = 0; +static uint32_t rtc_match_period = 0; +static uint8_t periodic_match = 0; + +/***************************************************************************** */ +/* Return the number of RTC ticks from system power on. + * This count is from power being present, even if resets occured. + * The calls maded during the first three seconds after RTC timer start will return 0. + */ +uint32_t rtc_get_count(void) +{ + struct lpc_rtc* rtc = LPC_RTC; + static uint8_t rtc_start_ok = 0; + + /* Is the count valid ? */ + if (rtc_start_ok == 1) { + return rtc->data; + } + /* Not started for sufficient time to have a reliable value (see UM10441 section 16.6.1) */ + if (is_systick_running() == 0) { + systick_start(); + return 0; + } else { + uint32_t ticks = systick_get_tick_count(); + uint32_t ms_period = systick_get_tick_ms_period(); + if ((ticks * ms_period) < 3000) { + return 0; + } + rtc_start_ok = 1; + return 0; + } +} + + +/***************************************************************************** */ +/* RTC setup */ + +/* In case someone wants the RTC to count something different from seconds */ +void rtc_clk_src_select(uint8_t source, uint8_t clk_div) +{ + struct lpc_sys_control* sysctrl = LPC_SYS_CONTROL; + struct lpc_pm_unit* pm_unit = LPC_PMU; + uint32_t tmp = 0; + + /* Turn off RTC controll regs */ + subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 0); + /* Change clock source. Warning : do not write ones to reserved bits. */ + tmp = (pm_unit->system_config & LPC_WAKEUP_PIN_HYST_MASK); + pm_unit->system_config = (tmp | ((source & 0x0F) << LPC_RTC_CLK_SRC_SHIFT)); + /* Change the RTC Clock divider if source is PCLK */ + if (source == LPC_RTC_CLK_PCLK) { + sysctrl->rtc_clk_div = clk_div; + } else { + sysctrl->rtc_clk_div = 0; + } +} + +/* Start the RTC. Once the RTC has been started using this call, it cannot be stopped. */ +void rtc_on(void) +{ + struct lpc_rtc* rtc = LPC_RTC; + /* Provide power to RTC control block */ + subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 1); + rtc->control = LPC_RTC_START; +} +/* Disable the RTC control block. Note that once started from software the RTC cannot be stopped. */ +void rtc_ctrl_off(void) +{ + /* Remove power from RTC control blockxs */ + subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 0); +} +/* This will disable the RTC. This is only possible if the RTC has not been started before. */ +void rtc_disable(void) +{ + struct lpc_rtc* rtc = LPC_RTC; + subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 1); + rtc->control = LPC_RTC_DISABLE; + subsystem_power(LPC_SYS_ABH_CLK_CTRL_RTC, 0); +} + +/* Change the RTC value */ +void rtc_set_date(uint32_t date) +{ + struct lpc_rtc* rtc = LPC_RTC; + rtc->load = date; +} + + +/***************************************************************************** */ +/* Set the match value for the RTC + * If periodic is 1 then the rtc match register will be updated after the interrupt + * has been handled by adding the offset. + * If date is set, the match register will be set to date. (regardless of the periodic + * argument, wich allows to have the first interrupt at a given date, and the following + * ones at a period set by the offset argument. + * If date is 0, then the match register is set to current date + offset. + * return -1 if RTC is not started and synced + */ +int rtc_set_match(uint32_t date, uint32_t offset, uint8_t periodic) +{ + struct lpc_rtc* rtc = LPC_RTC; + + if (rtc_get_count() == 0) { + return -1; + } + + periodic_match = periodic; + rtc_match_period = offset; + if (date != 0) { + rtc->match = date; + } else { + rtc->match = rtc_get_count() + offset; + } + return 0; +} + +/* RTC Interrupts Callbacks */ +static void (*rtc_calback) (uint32_t); + +void setup_callback(uint32_t ticks) +{ + uint32_t date = rtc_get_count(); + if (date != 0) { + if (date >= match_first) { + match_first = 0; /* First match missed, forget about it */ + } + rtc_set_match(match_first, rtc_match_period, periodic_match); + remove_systick_callback(&setup_callback); + } +} + +/* Register a callback for the RTC + * 'when' should be in the future (according to RTC counter) and 'period' can be used to get + * periodic interrupts. 'period' is a number of RTC counter increments. + * Return a positive integer if registration is OK and callback has a chance of being called. + * Return a negative integer if the match configuration was not possible. + */ +int set_rtc_callback(void (*callback) (uint32_t), uint32_t when, uint32_t period) +{ + struct lpc_rtc* rtc = LPC_RTC; + int ret = 0; + + /* Register the callback */ + rtc_calback = callback; + if (rtc_get_count() != 0) { + rtc_set_match(when, period, ((period == 0) ? 0 : 1)); + } else { + /* Add callback for "config later" */ + ret = add_systick_callback(&setup_callback, (3000 / systick_get_tick_ms_period())); + match_first = when; + periodic_match = ((period == 0) ? 0 : 1); + rtc_match_period = period; + } + rtc->intr_control = LPC_RTC_INT_ENABLE; + NVIC_EnableIRQ(RTC_IRQ); + return ret; +} +void remove_rtc_callback() +{ + struct lpc_rtc* rtc = LPC_RTC; + + /* Remove the handler */ + rtc_calback = NULL; + rtc->intr_control = LPC_RTC_INT_DISABLE; + /* And disable the interrupt */ + NVIC_DisableIRQ(RTC_IRQ); +} + +/* Interrupt Handler */ +void RTC_Handler(void) +{ + struct lpc_rtc* rtc = LPC_RTC; + /* Call interrupt handler */ + if (rtc_calback != NULL) { + rtc_calback(rtc->data); + } + rtc->intr_clear = LPC_RTC_CLEAR_INTR; + if (periodic_match == 1) { + rtc->match = rtc->data + rtc_match_period; + } +} + + + diff --git a/include/core/lpc_regs_12xx.h b/include/core/lpc_regs_12xx.h index d4cdff2..e993de7 100644 --- a/include/core/lpc_regs_12xx.h +++ b/include/core/lpc_regs_12xx.h @@ -323,6 +323,41 @@ struct lpc_system_tick { +/***************************************************************************** */ +/* Cortex-M0 RTC (Real-Time Clock) */ +/***************************************************************************** */ +/* Cortex-M0 RTC Registers */ +struct lpc_rtc { + volatile uint32_t data; /* 0x000 : Data register (R/-) */ + volatile uint32_t match; /* 0x004 : Match register (R/W) */ + volatile uint32_t load; /* 0x008 : Load register (R/W) */ + volatile uint32_t control; /* 0x00C : Control register (R/W) */ + volatile uint32_t intr_control; /* 0x010 : Interrupt control set/clear register (R/W) */ + volatile uint32_t raw_intr_status; /* 0x014 : Raw interrupt status register (R/-) */ + volatile uint32_t masked_intr_status; /* 0x018 : Masked interrupt status register (R/-) */ + volatile uint32_t intr_clear; /* 0x01C : Interrupt clear register (-/W) */ +}; +#define LPC_RTC ((struct lpc_rtc*) LPC_RTC_BASE) /* SysTick configuration struct */ + +/* RTC Clock source selection */ +#define LPC_RTC_CLK_1HZ (0) +#define LPC_RTC_CLK_1HZ_DELAYED (0x01) +#define LPC_RTC_CLK_1KHZ (0x0A) +#define LPC_RTC_CLK_PCLK (0x04) /* Main clock divided by RTC clock divider value */ + +/* RTC control register */ +#define LPC_RTC_START (1UL << 0) +#define LPC_RTC_DISABLE (0) + +/* RTC interrupt control register */ +#define LPC_RTC_INT_ENABLE (1UL << 0) +#define LPC_RTC_INT_DISABLE (0) + +/* RTC interrupt clear register */ +#define LPC_RTC_CLEAR_INTR (1UL << 0) + + + /***************************************************************************** */ /* Power Management Unit */ /***************************************************************************** */ @@ -336,6 +371,12 @@ struct lpc_pm_unit }; #define LPC_PMU ((struct lpc_pm_unit *) LPC_PMU_BASE) +/* System config register */ +#define LPC_WAKEUP_PIN_HYST_MASK (0x01 << 10) +#define LPC_RTC_CLK_SRC_SHIFT 11 +#define LPC_RTC_CLK_SRC_MASK (0x0F << LPC_RTC_CLK_SRC_SHIFT) +/* See RTC section above for RTC Clock source selection bits */ + /***************************************************************************** */ /* IO Control */ diff --git a/include/drivers/rtc.h b/include/drivers/rtc.h new file mode 100644 index 0000000..4540184 --- /dev/null +++ b/include/drivers/rtc.h @@ -0,0 +1,81 @@ +/**************************************************************************** + * drivers/rtc.h + * + * Copyright 2012 Nathael Pajani + * + * 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 2 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 . + * + *************************************************************************** */ + +/***************************************************************************** */ +/* RTC and RTC Interrupts */ +/***************************************************************************** */ + +#include + + +/* Return the number of RTC ticks from system power on. + * This count is from power being present, even if resets occured. + * The calls maded during the first three seconds after RTC timer start will return 0. + */ +uint32_t rtc_get_count(void); + + +/***************************************************************************** */ +/* RTC setup */ + +/* In case someone wants the RTC to count something different from seconds + * No need to call this function if you only want to count seconds, this is the default. + * source selection values are defined in lpc_regs_12xx.h + * clk_div is only usefull when selected clock source is PCLK (peripheral clock). Use value + * betwween 1 and 255 included. + */ +void rtc_clk_src_select(uint8_t source, uint8_t clk_div); + +/* Start the RTC. Once the RTC has been started using this call, it cannot be stopped. */ +void rtc_on(void); + +/* Disable the RTC control block. Note that once started from software the RTC cannot be stopped. */ +void rtc_ctrl_off(void); + +/* This will disable the RTC. This is only possible if the RTC has not been started before. */ +void rtc_disable(void); + +/* Change the RTC value */ +void rtc_set_date(uint32_t date); + + +/***************************************************************************** */ +/* Set the match value for the RTC + * If periodic is 1 then the rtc match register will be updated after the interrupt + * has been handled by adding the offset. + * If date is set, the match register will be set to date. (regardless of the periodic + * argument, wich allows to have the first interrupt at a given date, and the following + * ones at a period set by the offset argument. + * If date is 0, then the match register is set to current date + offset. + * return -1 if RTC is not started and synced. + */ +int rtc_set_match(uint32_t date, uint32_t offset, uint8_t periodic); + +/* Register a callback for the RTC + * 'when' should be in the future (according to RTC counter) and 'period' can be used to get + * periodic interrupts. 'period' is a number of RTC counter increments. + * Return a positive integer if registration is OK and callback has a chance of being called. + * Return a negative integer if the match configuration was not possible. + */ +int set_rtc_callback(void (*callback) (uint32_t), uint32_t when, uint32_t period); +void remove_rtc_callback(); + + +