1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #7504 from haukepetersen/opt_stm32_rtc

cpu/stm32: reworked RTC implementation
This commit is contained in:
Hauke Petersen 2017-08-24 15:46:23 +02:00 committed by GitHub
commit 75333b638f
26 changed files with 342 additions and 363 deletions

View File

@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -2,6 +2,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -4,6 +4,7 @@ FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -4,6 +4,7 @@ FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -3,10 +3,11 @@ FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart
FEATURES_PROVIDED += periph_pwm
# load the common Makefile.features for Nucleo boards
include $(RIOTBOARD)/nucleo-common/Makefile.features

View File

@ -32,6 +32,9 @@ extern "C" {
**/
#define CLOCK_HSI (16000000U) /* frequency of internal oscillator */
#define CLOCK_CORECLOCK (32000000U) /* targeted core clock frequency */
/* 0: no external low speed crystal available,
* 1: external crystal available (always 32.768kHz) */
#define CLOCK_LSE (1)
/* configuration of PLL prescaler and multiply values */
/* CORECLOCK := HSI / CLOCK_PLL_DIV * CLOCK_PLL_MUL */
#define CLOCK_PLL_DIV RCC_CFGR_PLLDIV2

View File

@ -2,6 +2,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -5,6 +5,7 @@ FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -2,6 +2,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -2,6 +2,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -2,6 +2,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -2,6 +2,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -2,6 +2,7 @@
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_flashpage
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -4,6 +4,7 @@ FEATURES_PROVIDED += periph_dac
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -6,6 +6,7 @@ FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart

View File

@ -28,7 +28,23 @@ extern "C" {
#endif
/**
* @brief Linker script provided symbol for CPUID location
* @brief CPU specific LSI clock speed
*/
#if defined(CPU_FAM_STM32F0) || defined (CPU_FAM_STM32F1) || \
defined(CPU_FAM_STM32F3)
#define CLOCK_LSI (40000U)
#elif defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L0) || \
defined(CPU_FAM_STM32L1)
#define CLOCK_LSI (37000U)
#elif defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) || \
defined(CPU_FAM_STM32L4)
#define CLOCK_LSI (32000U)
#else
#error "error: LSI clock speed not defined for your target CPU"
#endif
/**
* @brief Linker script provided symbol for CPUID location
*/
extern uint32_t _cpuid_address;
/**

View File

@ -2,6 +2,7 @@
* Copyright (C) 2015 Lari Lehtomäki
* 2016 Laksh Bhatia
* 2016-2017 OTA keys S.A.
* 2017 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
@ -9,8 +10,7 @@
*/
/**
* @ingroup cpu_cortexm_common
* @ingroup drivers_periph_rtc
* @ingroup cpu_stm32_common
* @{
* @file
* @brief Low-level RTC driver implementation
@ -18,360 +18,308 @@
* @author Lari Lehtomäki <lari@lehtomaki.fi>
* @author Laksh Bhatia <bhatialaksh3@gmail.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @}
*/
#include <time.h>
#include "cpu.h"
#include "stmclk.h"
#include "periph/rtc.h"
#include "periph_conf.h"
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F2) || \
defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32F4) || \
defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L0) || \
defined(CPU_FAM_STM32L1) || defined(CPU_FAM_STM32L4)
/* guard file in case no RTC device was specified */
#if RTC_NUMOF
#if defined(RTC) && !defined(CPU_FAM_STM32F1)
#define RTC_WRITE_PROTECTION_KEY1 (0xCA)
#define RTC_WRITE_PROTECTION_KEY2 (0x53)
#define RTC_SYNC_PRESCALER (0xff) /**< prescaler for 32.768 kHz oscillator */
#define RTC_ASYNC_PRESCALER (0x7f) /**< prescaler for 32.768 kHz oscillator */
/* map some CPU specific register names */
#if defined (CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
#define EN_REG (RCC->CSR)
#define EN_BIT (RCC_CSR_RTCEN)
#define CLKSEL_MASK (RCC_CSR_RTCSEL)
#define CLKSEL_LSE (RCC_CSR_RTCSEL_LSE)
#define CLKSEL_LSI (RCC_CSR_RTCSEL_LSI)
#else
#define EN_REG (RCC->BDCR)
#define EN_BIT (RCC_BDCR_RTCEN)
#define CLKSEL_MASK (RCC_BDCR_RTCSEL_0 | RCC_BDCR_RTCSEL_1)
#define CLKSEL_LSE (RCC_BDCR_RTCSEL_0)
#define CLKSEL_LSI (RCC_BDCR_RTCSEL_1)
#endif
#define MCU_YEAR_OFFSET (100) /**< struct tm counts years since 1900
but RTC has only two-digit year
hence the offset of 100 years. */
/* interrupt line name mapping */
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0)
#define IRQN (RTC_IRQn)
#define ISR_NAME isr_rtc
#else
#define IRQN (RTC_Alarm_IRQn)
#define ISR_NAME isr_rtc_alarm
#endif
typedef struct {
/* EXTI bitfield mapping */
#if defined(CPU_FAM_STM32L4)
#define EXTI_IMR_BIT (EXTI_IMR1_IM18)
#define EXTI_FTSR_BIT (EXTI_FTSR1_FT18)
#define EXTI_RTSR_BIT (EXTI_RTSR1_RT18)
#define EXTI_PR_BIT (EXTI_PR1_PIF18)
#else
#if defined(CPU_FAM_STM32L0)
#define EXTI_IMR_BIT (EXTI_IMR_IM17)
#else
#define EXTI_IMR_BIT (EXTI_IMR_MR17)
#endif
#define EXTI_FTSR_BIT (EXTI_FTSR_TR17)
#define EXTI_RTSR_BIT (EXTI_RTSR_TR17)
#define EXTI_PR_BIT (EXTI_PR_PR17)
#endif
/* write protection values */
#define WPK1 (0xCA)
#define WPK2 (0x53)
/* define TR, DR, and ALRMAR position and masks */
#define TR_H_MASK (RTC_TR_HU | RTC_TR_HT)
#define TR_M_MASK (RTC_TR_MNU | RTC_TR_MNT)
#define TR_S_MASK (RTC_TR_SU | RTC_TR_ST)
#define DR_Y_MASK (RTC_DR_YU | RTC_DR_YT)
#define DR_M_MASK (RTC_DR_MU | RTC_DR_MT)
#define DR_D_MASK (RTC_DR_DU | RTC_DR_DT)
#define ALRM_D_MASK (RTC_ALRMAR_DU | RTC_ALRMAR_DT)
#define ALRM_H_MASK (RTC_ALRMAR_HU | RTC_ALRMAR_HT)
#define ALRM_M_MASK (RTC_ALRMAR_MNU | RTC_ALRMAR_MNT)
#define ALRM_S_MASK (RTC_ALRMAR_SU | RTC_ALRMAR_ST)
#ifndef RTC_DR_YU_Pos
#define RTC_DR_YU_Pos (16U)
#endif
#ifndef RTC_DR_MU_Pos
#define RTC_DR_MU_Pos (8U)
#endif
#ifndef RTC_DR_DU_Pos
#define RTC_DR_DU_Pos (0U)
#endif
#ifndef RTC_TR_HU_Pos
#define RTC_TR_HU_Pos (16U)
#endif
#ifndef RTC_TR_MNU_Pos
#define RTC_TR_MNU_Pos (8U)
#endif
#ifndef RTC_TR_SU_Pos
#define RTC_TR_SU_Pos (0U)
#endif
#ifndef RTC_ALRMAR_DU_Pos
#define RTC_ALRMAR_DU_Pos (24U)
#endif
#ifndef RTC_ALRMAR_HU_Pos
#define RTC_ALRMAR_HU_Pos (16U)
#endif
#ifndef RTC_ALRMAR_MNU_Pos
#define RTC_ALRMAR_MNU_Pos (8U)
#endif
#ifndef RTC_ALRMAR_SU_Pos
#define RTC_ALRMAR_SU_Pos (0U)
#endif
/* figure out sync and async prescaler */
#if CLOCK_LSE
#define PRE_SYNC (255)
#define PRE_ASYNC (127)
#elif (CLOCK_LSI == 40000)
#define PRE_SYNC (319)
#define PRE_ASYNC (124)
#elif (CLOCK_LSI == 37000)
#define PRE_SYNC (295)
#define PRE_ASYNC (124)
#elif (CLOCK_LSI == 32000)
#define PRE_SYNC (249)
#define PRE_ASYNC (127)
#else
#error "rtc: unable to determine RTC SYNC and ASYNC prescalers from LSI value"
#endif
/* struct tm counts years since 1900 but RTC has only two-digit year hence the
* offset of 100 years. */
#define YEAR_OFFSET (100)
static struct {
rtc_alarm_cb_t cb; /**< callback called from RTC interrupt */
void *arg; /**< argument passed to the callback */
} rtc_state_t;
} isr_ctx;
static rtc_state_t rtc_callback;
static uint8_t byte2bcd(uint8_t value);
/**
* @brief Initializes the RTC to use LSE (external 32.768 kHz oscillator) as a
* clocl source. Verify that your board has this oscillator. If other clock
* source is used, then also the prescaler constants should be changed.
*/
void rtc_init(void)
static uint32_t val2bcd(int val, int shift, uint32_t mask)
{
/* Enable write access to RTC registers */
#if defined(CPU_FAM_STM32L4)
periph_clk_en(APB1, RCC_APB1ENR1_PWREN);
#else
periph_clk_en(APB1, RCC_APB1ENR_PWREN);
#endif
#if defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L4)
PWR->CR1 |= PWR_CR1_DBP;
#else
PWR->CR |= PWR_CR_DBP;
#endif
uint32_t bcdhigh = 0;
#if defined(CPU_FAM_STM32L1) || defined(CPU_FAM_STM32L0)
if (!(RCC->CSR & RCC_CSR_RTCEN)) {
#else
if (!(RCC->BDCR & RCC_BDCR_RTCEN)) {
#endif
rtc_poweron();
while (val >= 10) {
bcdhigh++;
val -= 10;
}
/* Unlock RTC write protection */
RTC->WPR = RTC_WRITE_PROTECTION_KEY1;
RTC->WPR = RTC_WRITE_PROTECTION_KEY2;
return ((((bcdhigh << 4) | val) << shift) & mask);
}
/* Enter RTC Init mode */
RTC->ISR = 0;
static int bcd2val(uint32_t val, int shift, uint32_t mask)
{
int tmp = (int)((val & mask) >> shift);
return (((tmp >> 4) * 10) + (tmp & 0x0f));
}
static inline void rtc_unlock(void)
{
/* enable backup clock domain */
stmclk_dbp_unlock();
/* unlock RTC */
RTC->WPR = WPK1;
RTC->WPR = WPK2;
/* enter RTC init mode */
RTC->ISR |= RTC_ISR_INIT;
while ((RTC->ISR & RTC_ISR_INITF) == 0) {}
while (!(RTC->ISR & RTC_ISR_INITF)) {}
}
/* Set 24-h clock */
RTC->CR &= ~RTC_CR_FMT;
/* Timestamps enabled */
RTC->CR |= RTC_CR_TSE;
/* Configure the RTC PRER */
RTC->PRER = RTC_SYNC_PRESCALER;
RTC->PRER |= (RTC_ASYNC_PRESCALER << 16);
/* Exit RTC init mode */
RTC->ISR &= (uint32_t) ~RTC_ISR_INIT;
/* Enable RTC write protection */
static inline void rtc_lock(void)
{
/* exit RTC init mode */
RTC->ISR &= ~RTC_ISR_INIT;
while (RTC->ISR & RTC_ISR_INITF) {}
/* lock RTC device */
RTC->WPR = 0xff;
/* disable backup clock domain */
stmclk_dbp_lock();
}
void rtc_init(void)
{
/* enable low frequency clock */
stmclk_enable_lfclk();
/* select input clock and enable the RTC */
stmclk_dbp_unlock();
EN_REG &= ~(CLKSEL_MASK);
#if CLOCK_LSE
EN_REG |= (CLKSEL_LSE | EN_BIT);
#else
EN_REG |= (CLKSEL_LSI | EN_BIT);
#endif
rtc_unlock();
/* reset configuration */
RTC->CR = 0;
RTC->ISR = RTC_ISR_INIT;
/* configure prescaler (RTC PRER) */
RTC->PRER = (PRE_SYNC | (PRE_ASYNC << 16));
rtc_lock();
/* configure the EXTI channel, as RTC interrupts are routed through it.
* Needs to be configured to trigger on rising edges. */
EXTI->FTSR &= ~(EXTI_FTSR_BIT);
EXTI->RTSR |= EXTI_RTSR_BIT;
EXTI->IMR |= EXTI_IMR_BIT;
EXTI->PR |= EXTI_PR_BIT;
/* enable global RTC interrupt */
NVIC_EnableIRQ(IRQN);
}
int rtc_set_time(struct tm *time)
{
/* Enable write access to RTC registers */
#if defined(CPU_FAM_STM32L4)
periph_clk_en(APB1, RCC_APB1ENR1_PWREN);
#else
periph_clk_en(APB1, RCC_APB1ENR_PWREN);
#endif
#if defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L4)
PWR->CR1 |= PWR_CR1_DBP;
#else
PWR->CR |= PWR_CR_DBP;
#endif
/* Unlock RTC write protection */
RTC->WPR = RTC_WRITE_PROTECTION_KEY1;
RTC->WPR = RTC_WRITE_PROTECTION_KEY2;
/* Enter RTC Init mode */
RTC->ISR |= RTC_ISR_INIT;
while ((RTC->ISR & RTC_ISR_INITF) == 0) {}
/* Set 24-h clock */
RTC->CR &= ~RTC_CR_FMT;
RTC->DR = ((((uint32_t)byte2bcd(time->tm_year - MCU_YEAR_OFFSET) << 16) & (RTC_DR_YT | RTC_DR_YU)) |
(((uint32_t)byte2bcd(time->tm_mon + 1) << 8) & (RTC_DR_MT | RTC_DR_MU)) |
(((uint32_t)byte2bcd(time->tm_mday) << 0) & (RTC_DR_DT | RTC_DR_DU)));
RTC->TR = ((((uint32_t)byte2bcd(time->tm_hour) << 16) & (RTC_TR_HT | RTC_TR_HU)) |
(((uint32_t)byte2bcd(time->tm_min) << 8) & (RTC_TR_MNT | RTC_TR_MNU)) |
(((uint32_t)byte2bcd(time->tm_sec) << 0) & (RTC_TR_ST | RTC_TR_SU)));
/* Exit RTC init mode */
RTC->ISR &= (uint32_t) ~RTC_ISR_INIT;
/* Enable RTC write protection */
RTC->WPR = 0xFF;
/* wait till the new calender values are synced and can actually be read from the rtc */
while((RTC->ISR & RTC_ISR_RSF) == 0){}
rtc_unlock();
RTC->DR = (val2bcd((time->tm_year % 100), RTC_DR_YU_Pos, DR_Y_MASK) |
val2bcd(time->tm_mon, RTC_DR_MU_Pos, DR_M_MASK) |
val2bcd(time->tm_mday, RTC_DR_DU_Pos, DR_D_MASK));
RTC->TR = (val2bcd(time->tm_hour, RTC_TR_HU_Pos, TR_H_MASK) |
val2bcd(time->tm_min, RTC_TR_MNU_Pos, TR_M_MASK) |
val2bcd(time->tm_sec, RTC_TR_SU_Pos, TR_S_MASK));
rtc_lock();
while (!(RTC->ISR & RTC_ISR_RSF)) {}
return 0;
}
int rtc_get_time(struct tm *time)
{
/* reading TR locks the content in DR till DR is read to ensure consistency between both */
/* save current time */
uint32_t tr = RTC->TR;
uint32_t dr = RTC->DR;
time->tm_year = bcd2val(dr, RTC_DR_YU_Pos, DR_Y_MASK) + YEAR_OFFSET;
time->tm_mon = bcd2val(dr, RTC_DR_MU_Pos, DR_M_MASK);
time->tm_mday = bcd2val(dr, RTC_DR_DU_Pos, DR_D_MASK);
time->tm_hour = bcd2val(tr, RTC_TR_HU_Pos, TR_H_MASK);
time->tm_min = bcd2val(tr, RTC_TR_MNU_Pos, TR_M_MASK);
time->tm_sec = bcd2val(tr, RTC_TR_SU_Pos, TR_S_MASK);
time->tm_year = MCU_YEAR_OFFSET;
time->tm_year += (((dr & RTC_DR_YT) >> 20) * 10) + ((dr & RTC_DR_YU) >> 16);
time->tm_mon = (((dr & RTC_DR_MT) >> 12) * 10) + ((dr & RTC_DR_MU) >> 8) - 1;
time->tm_mday = (((dr & RTC_DR_DT) >> 4) * 10) + ((dr & RTC_DR_DU) >> 0);
time->tm_hour = (((tr & RTC_TR_HT) >> 20) * 10) + ((tr & RTC_TR_HU) >> 16);
if (tr & RTC_TR_PM) {
/* 12PM is noon */
if (time->tm_hour != 12) {
time->tm_hour += 12;
}
}
else if ((RTC->CR & RTC_CR_FMT) && time->tm_hour == 12) {
/* 12AM is midnight */
time->tm_hour -= 12;
}
time->tm_min = (((tr & RTC_TR_MNT) >> 12) * 10) + ((tr & RTC_TR_MNU) >> 8);
time->tm_sec = (((tr & RTC_TR_ST) >> 4) * 10) + ((tr & RTC_TR_SU) >> 0);
return 0;
}
int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg)
{
/* Enable write access to RTC registers */
#if defined(CPU_FAM_STM32L4)
periph_clk_en(APB1, RCC_APB1ENR1_PWREN);
#else
periph_clk_en(APB1, RCC_APB1ENR_PWREN);
#endif
rtc_unlock();
#if defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L4)
PWR->CR1 |= PWR_CR1_DBP;
#else
PWR->CR |= PWR_CR_DBP;
#endif
/* disable existing alarm (if enabled) */
rtc_clear_alarm();
/* Unlock RTC write protection */
RTC->WPR = RTC_WRITE_PROTECTION_KEY1;
RTC->WPR = RTC_WRITE_PROTECTION_KEY2;
/* save callback and argument */
isr_ctx.cb = cb;
isr_ctx.arg = arg;
/* Enter RTC Init mode */
RTC->ISR |= RTC_ISR_INIT;
while ((RTC->ISR & RTC_ISR_INITF) == 0) {}
/* set wakeup time */
RTC->ALRMAR = (val2bcd(time->tm_mday, RTC_ALRMAR_DU_Pos, ALRM_D_MASK) |
val2bcd(time->tm_hour, RTC_ALRMAR_HU_Pos, ALRM_H_MASK) |
val2bcd(time->tm_min, RTC_ALRMAR_MNU_Pos, ALRM_M_MASK) |
val2bcd(time->tm_sec, RTC_ALRMAR_SU_Pos, ALRM_S_MASK));
RTC->CR &= ~(RTC_CR_ALRAE);
while ((RTC->ISR & RTC_ISR_ALRAWF) == 0) {}
RTC->ALRMAR &= ~(RTC_ALRMAR_MSK1 | RTC_ALRMAR_MSK2 | RTC_ALRMAR_MSK3 | RTC_ALRMAR_MSK4);
RTC->ALRMAR = ((((uint32_t)byte2bcd(time->tm_mday) << 24) & (RTC_ALRMAR_DT | RTC_ALRMAR_DU)) |
(((uint32_t)byte2bcd(time->tm_hour) << 16) & (RTC_ALRMAR_HT | RTC_ALRMAR_HU)) |
(((uint32_t)byte2bcd(time->tm_min) << 8) & (RTC_ALRMAR_MNT | RTC_ALRMAR_MNU)) |
(((uint32_t)byte2bcd(time->tm_sec) << 0) & (RTC_ALRMAR_ST | RTC_ALRMAR_SU)));
/* Enable Alarm A */
RTC->CR |= RTC_CR_ALRAE;
RTC->CR |= RTC_CR_ALRAIE;
RTC->ISR &= ~(RTC_ISR_ALRAF);
RTC->CR |= (RTC_CR_ALRAE | RTC_CR_ALRAIE);
/* Exit RTC init mode */
RTC->ISR &= (uint32_t) ~RTC_ISR_INIT;
/* Enable RTC write protection */
RTC->WPR = 0xFF;
#if defined(CPU_FAM_STM32L0)
EXTI->IMR |= EXTI_IMR_IM17;
#elif defined (CPU_FAM_STM32L4)
EXTI->IMR |= EXTI_IMR1_IM18;
#else
EXTI->IMR |= EXTI_IMR_MR17;
#endif
#if defined (CPU_FAM_STM32L4)
EXTI->RTSR |= EXTI_RTSR1_RT18;
#else
EXTI->RTSR |= EXTI_RTSR_TR17;
#endif
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0)
NVIC_SetPriority(RTC_IRQn, 10);
NVIC_EnableIRQ(RTC_IRQn);
#else
NVIC_SetPriority(RTC_Alarm_IRQn, 10);
NVIC_EnableIRQ(RTC_Alarm_IRQn);
#endif
rtc_callback.cb = cb;
rtc_callback.arg = arg;
rtc_lock();
return 0;
}
int rtc_get_alarm(struct tm *time)
{
time->tm_year = MCU_YEAR_OFFSET;
time->tm_year += (((RTC->DR & RTC_DR_YT) >> 20) * 10) + ((RTC->DR & RTC_DR_YU) >> 16);
time->tm_mon = (((RTC->DR & RTC_DR_MT) >> 12) * 10) + ((RTC->DR & RTC_DR_MU) >> 8) - 1;
time->tm_mday = (((RTC->ALRMAR & RTC_ALRMAR_DT) >> 28) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_DU) >> 24);
time->tm_hour = (((RTC->ALRMAR & RTC_ALRMAR_HT) >> 20) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_HU) >> 16);
if ((RTC->ALRMAR & RTC_ALRMAR_PM) && (RTC->CR & RTC_CR_FMT)) {
time->tm_hour += 12;
}
time->tm_min = (((RTC->ALRMAR & RTC_ALRMAR_MNT) >> 12) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_MNU) >> 8);
time->tm_sec = (((RTC->ALRMAR & RTC_ALRMAR_ST) >> 4) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_SU) >> 0);
uint32_t dr = RTC->DR;
uint32_t alrm = RTC->ALRMAR;
time->tm_year = bcd2val(dr, RTC_DR_YU_Pos, DR_Y_MASK) + YEAR_OFFSET;
time->tm_mon = bcd2val(dr, RTC_DR_MU_Pos, DR_M_MASK);
time->tm_mday = bcd2val(alrm, RTC_ALRMAR_DU_Pos, ALRM_D_MASK);
time->tm_hour = bcd2val(alrm, RTC_ALRMAR_HU_Pos, ALRM_H_MASK);
time->tm_min = bcd2val(alrm, RTC_ALRMAR_MNU_Pos, ALRM_M_MASK);
time->tm_sec = bcd2val(alrm, RTC_ALRMAR_SU_Pos, ALRM_S_MASK);
return 0;
}
void rtc_clear_alarm(void)
{
/* Disable Alarm A */
RTC->CR &= RTC_CR_ALRAE;
RTC->CR &= RTC_CR_ALRAIE;
RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE);
while (!(RTC->ISR & RTC_ISR_ALRAWF)) {}
rtc_callback.cb = NULL;
rtc_callback.arg = NULL;
isr_ctx.cb = NULL;
isr_ctx.arg = NULL;
}
void rtc_poweron(void)
{
#if defined(CPU_FAM_STM32L1) || defined(CPU_FAM_STM32L0)
/* Reset RTC domain */
RCC->CSR |= RCC_CSR_RTCRST;
RCC->CSR &= ~(RCC_CSR_RTCRST);
/* Enable the LSE clock (external 32.768 kHz oscillator) */
RCC->CSR &= ~(RCC_CSR_LSEON);
RCC->CSR &= ~(RCC_CSR_LSEBYP);
RCC->CSR |= RCC_CSR_LSEON;
while ( (RCC->CSR & RCC_CSR_LSERDY) == 0 ) {}
/* Switch RTC to LSE clock source */
RCC->CSR &= ~(RCC_CSR_RTCSEL);
RCC->CSR |= RCC_CSR_RTCSEL_LSE;
/* Enable the RTC */
RCC->CSR |= RCC_CSR_RTCEN;
#else
/* Reset RTC domain */
RCC->BDCR |= RCC_BDCR_BDRST;
RCC->BDCR &= ~(RCC_BDCR_BDRST);
/* Enable the LSE clock (external 32.768 kHz oscillator) */
RCC->BDCR &= ~(RCC_BDCR_LSEON);
RCC->BDCR &= ~(RCC_BDCR_LSEBYP);
RCC->BDCR |= RCC_BDCR_LSEON;
while ((RCC->BDCR & RCC_BDCR_LSERDY) == 0) {}
/* Switch RTC to LSE clock source */
RCC->BDCR &= ~(RCC_BDCR_RTCSEL);
RCC->BDCR |= RCC_BDCR_RTCSEL_0;
/* Enable the RTC */
RCC->BDCR |= RCC_BDCR_RTCEN;
#endif
stmclk_dbp_unlock();
EN_REG |= EN_BIT;
stmclk_dbp_lock();
}
void rtc_poweroff(void)
{
#if defined(CPU_FAM_STM32L1) || defined(CPU_FAM_STM32L0)
/* Reset RTC domain */
RCC->CSR |= RCC_CSR_RTCRST;
RCC->CSR &= ~(RCC_CSR_RTCRST);
/* Disable the RTC */
RCC->CSR &= ~RCC_CSR_RTCEN;
/* Disable LSE clock */
RCC->CSR &= ~(RCC_CSR_LSEON);
#else
/* Reset RTC domain */
RCC->BDCR |= RCC_BDCR_BDRST;
RCC->BDCR &= ~(RCC_BDCR_BDRST);
/* Disable the RTC */
RCC->BDCR &= ~RCC_BDCR_RTCEN;
/* Disable LSE clock */
RCC->BDCR &= ~(RCC_BDCR_LSEON);
#endif
stmclk_dbp_unlock();
EN_REG &= ~EN_BIT;
stmclk_dbp_lock();
}
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0)
void isr_rtc(void)
#else
void isr_rtc_alarm(void)
#endif
void ISR_NAME(void)
{
if (RTC->ISR & RTC_ISR_ALRAF) {
if (rtc_callback.cb != NULL) {
rtc_callback.cb(rtc_callback.arg);
if (isr_ctx.cb != NULL) {
isr_ctx.cb(isr_ctx.arg);
}
RTC->ISR &= ~RTC_ISR_ALRAF;
}
#if defined(CPU_FAM_STM32L4)
EXTI->PR |= EXTI_PR1_PIF18;
#else
EXTI->PR |= EXTI_PR_PR17;
#endif
EXTI->PR |= EXTI_PR_BIT;
cortexm_isr_end();
}
/**
* Convert a number from unsigned to BCD
*
* @param[in] value to be converted
* @return BCD representation of the value
*/
static uint8_t byte2bcd(uint8_t value)
{
uint8_t bcdhigh = 0;
while (value >= 10) {
bcdhigh++;
value -= 10;
}
return ((uint8_t)(bcdhigh << 4) | value);
}
#endif /* RTC_NUMOF */
#endif /* defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F2) || \
defined(CPU_FAM_STM32F3) || defined(CPU_FAM_STM32F4) || \
defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L0) || \
defined(CPU_FAM_STM32L1) || defined(CPU_FAM_STM32L4)*/
#endif /* RTC */

View File

@ -37,6 +37,16 @@
#define BIT_APB_PWREN RCC_APB1ENR_PWREN
#endif
#if defined (CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
#define REG_LSE CSR
#define BIT_LSEON RCC_CSR_LSEON
#define BIT_LSERDY RCC_CSR_LSERDY
#else
#define REG_LSE BDCR
#define BIT_LSEON RCC_BDCR_LSEON
#define BIT_LSERDY RCC_BDCR_LSERDY
#endif
void stmclk_enable_hsi(void)
{
RCC->CR |= RCC_CR_HSION;
@ -58,8 +68,8 @@ void stmclk_enable_lfclk(void)
{
#if CLOCK_LSE
stmclk_dbp_unlock();
RCC->BDCR |= RCC_BDCR_LSEON;
while (!(RCC->BDCR & RCC_BDCR_LSERDY)) {}
RCC->REG_LSE |= BIT_LSEON;
while (!(RCC->REG_LSE & BIT_LSERDY)) {}
stmclk_dbp_lock();
#else
RCC->CSR |= RCC_CSR_LSION;
@ -71,7 +81,8 @@ void stmclk_disable_lfclk(void)
{
#if CLOCK_LSE
stmclk_dbp_unlock();
RCC->BDCR &= ~(RCC_BDCR_LSEON);
RCC->REG_LSE &= ~(BIT_LSEON);
while (!(RCC->REG_LSE & BIT_LSERDY)) {}
stmclk_dbp_lock();
#else
RCC->CSR &= ~(RCC_CSR_LSION);

View File

@ -21,6 +21,7 @@
#include <stdint.h>
#include "cpu.h"
#include "irq.h"
#include "stmclk.h"
#include "periph_conf.h"
#include "periph/init.h"
@ -125,22 +126,11 @@ static void cpu_clock_init(void)
/* configure the low speed clock domain (LSE vs LSI) */
#if CLOCK_LSE
/* allow write access to backup domain */
periph_clk_en(APB1, RCC_APB1ENR1_PWREN);
PWR->CR1 |= PWR_CR1_DBP;
/* enable LSE */
RCC->BDCR = RCC_BDCR_LSEON;
while (!(RCC->BDCR & RCC_BDCR_LSERDY)) {}
/* disable write access to back domain when done */
PWR->CR1 &= ~(PWR_CR1_DBP);
periph_clk_dis(APB1, RCC_APB1ENR1_PWREN);
/* we enable the LSE clock if available for calibrating the MSI clock */
stmclk_enable_lfclk();
/* now we can enable the MSI PLL mode */
RCC->CR |= RCC_CR_MSIPLLEN;
while (!(RCC->CR & RCC_CR_MSIRDY)) {}
#else
RCC->CSR = RCC_CSR_LSION;
while (!(RCC->CSR & RCC_CSR_LSIRDY)) {}
#endif
/* select the MSI clock for the 48MHz clock tree (USB, RNG) */

View File

@ -33,9 +33,6 @@
extern "C" {
#endif
/* guard file in case no RTC device was specified */
#if RTC_NUMOF
/**
* @brief Signature for alarm Callback
*
@ -108,8 +105,6 @@ void rtc_poweron(void);
*/
void rtc_poweroff(void);
#endif /* RTC_NUMOF */
#ifdef __cplusplus
}
#endif

View File

@ -28,84 +28,81 @@
#include "periph/rtc.h"
#include "xtimer.h"
#define TM_YEAR_OFFSET (1900)
#define PERIOD (2U)
#define REPEAT (4U)
#if RTC_NUMOF < 1
#error "No RTC found. See the board specific periph_conf.h."
#endif
#define TM_YEAR_OFFSET (1900)
void cb(void *arg)
static unsigned cnt = 0;
static void print_time(const char *label, const struct tm *time)
{
printf("%s %04d-%02d-%02d %02d:%02d:%02d\n", label,
time->tm_year + TM_YEAR_OFFSET,
time->tm_mon + 1,
time->tm_mday,
time->tm_hour,
time->tm_min,
time->tm_sec);
}
static void inc_secs(struct tm *time, unsigned val)
{
time->tm_sec += val;
if (time->tm_sec >= 60) {
time->tm_sec -= 60;
}
}
static void cb(void *arg)
{
(void)arg;
puts("Alarm!");
struct tm time;
rtc_get_alarm(&time);
time.tm_sec += 10;
if ( time.tm_sec > 60 )
rtc_clear_alarm();
rtc_set_alarm(&time, cb, 0);
if (++cnt < REPEAT) {
struct tm time;
rtc_get_alarm(&time);
inc_secs(&time, PERIOD);
rtc_set_alarm(&time, cb, NULL);
}
}
int main(void)
{
puts("\nRIOT RTC low-level driver test");
puts("This test will display 'Alarm' in 10 seconds\n");
struct tm time = {
.tm_year = 2011 - TM_YEAR_OFFSET, /* years are counted from 1900 */
.tm_mon = 11, /* 0 = January, 11 = December */
.tm_mday = 13,
.tm_hour = 14,
.tm_min = 15,
.tm_sec = 15
};
puts("Initializing the RTC driver");
puts("\nRIOT RTC low-level driver test");
printf("This test will display 'Alarm!' every %u seconds for %u times\n",
PERIOD, REPEAT);
/* initialize RTC */
rtc_init();
struct tm time;
time.tm_year = 2011 - TM_YEAR_OFFSET; // years are counted from 1900
time.tm_mon = 11; // 0 = January, 11 = December
time.tm_mday = 13;
time.tm_hour = 14;
time.tm_min = 15;
time.tm_sec = 15;
printf("Setting clock to %04d-%02d-%02d %02d:%02d:%02d\n",
time.tm_year + TM_YEAR_OFFSET,
time.tm_mon + 1,
time.tm_mday,
time.tm_hour,
time.tm_min,
time.tm_sec);
/* set RTC */
print_time(" Setting clock to ", &time);
rtc_set_time(&time);
/* read RTC to confirm value */
rtc_get_time(&time);
printf("Clock set to %04d-%02d-%02d %02d:%02d:%02d\n",
time.tm_year + TM_YEAR_OFFSET,
time.tm_mon + 1,
time.tm_mday,
time.tm_hour,
time.tm_min,
time.tm_sec);
print_time("Clock value is now ", &time);
/* set initial alarm */
inc_secs(&time, PERIOD);
print_time(" Setting alarm to ", &time);
rtc_set_alarm(&time, cb, NULL);
time.tm_sec += 10;
printf("Setting alarm to %04d-%02d-%02d %02d:%02d:%02d\n",
time.tm_year + TM_YEAR_OFFSET,
time.tm_mon + 1,
time.tm_mday,
time.tm_hour,
time.tm_min,
time.tm_sec);
rtc_set_alarm(&time, cb, 0);
xtimer_usleep(100);
/* verify alarm */
rtc_get_alarm(&time);
printf("Alarm set to %04d-%02d-%02d %02d:%02d:%02d\n",
time.tm_year + TM_YEAR_OFFSET,
time.tm_mon + 1,
time.tm_mday,
time.tm_hour,
time.tm_min,
time.tm_sec);
print_time(" Alarm is set to ", &time);
puts("");
puts("The alarm should trigger every 10 seconds for 4 times.");
return 0;
}