Browse Source

cpu/stm32f0: RTC implementation with external clock

tests: Tests for RTC clock
dev/timer
Lari Lehtomäki 7 years ago
parent
commit
1ecaadab4e
  1. 1
      boards/nucleo-f091/Makefile.features
  2. 12
      boards/nucleo-f091/include/periph_conf.h
  3. 263
      cpu/stm32f0/periph/rtc.c
  4. 8
      tests/periph_rtc/Makefile
  5. 7
      tests/periph_rtc/README.md
  6. 113
      tests/periph_rtc/main.c

1
boards/nucleo-f091/Makefile.features

@ -1,5 +1,6 @@
FEATURES_PROVIDED += cpp
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_uart
FEATURES_MCU_GROUP = cortex_m0

12
boards/nucleo-f091/include/periph_conf.h

@ -156,6 +156,18 @@ extern "C" {
#define GPIO_5_IRQ EXTI4_15_IRQn
/** @} */
/**
* @name RTC configuration
* @{
*/
/**
* Nucleos with MB1136 C-02 or MB1136 C-03 -sticker on it have the required LSE
* oscillator provided on the X2 slot.
* See Nucleo User Manual UM1724 section 5.6.2.
*/
#define RTC_NUMOF (1U)
/** @} */
#ifdef __cplusplus
}
#endif

263
cpu/stm32f0/periph/rtc.c

@ -0,0 +1,263 @@
/*
* Copyright (C) 2015 Lari Lehtomäki
*
* 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 cpu_stm32f0
* @{
* @file
* @brief Low-level RTC driver implementation
* @author Lari Lehtomäki <lari@lehtomaki.fi>
* @}
*/
#include <time.h>
#include "cpu.h"
#include "periph/rtc.h"
#include "periph_conf.h"
#include "sched.h"
#include "thread.h"
/* guard file in case no RTC device was specified */
#if RTC_NUMOF
#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 */
#define MCU_YEAR_OFFSET (100) /**< struct tm counts years since 1900
but RTC has only two-digit year
hence the offset of 100 years. */
typedef struct {
rtc_alarm_cb_t cb; /**< callback called from RTC interrupt */
void *arg; /**< argument passed to the callback */
} rtc_state_t;
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)
{
/* Enable write access to RTC registers */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP;
/* 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_LSE;
/* Enable the RTC */
RCC->BDCR |= RCC_BDCR_RTCEN;
/* Unlock RTC write protection */
RTC->WPR = RTC_WRITE_PROTECTION_KEY1;
RTC->WPR = RTC_WRITE_PROTECTION_KEY2;
/* Enter RTC Init mode */
RTC->ISR = 0;
RTC->ISR |= RTC_ISR_INIT;
while ( (RTC->ISR & RTC_ISR_INITF) == 0 );
/* 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 */
RTC->WPR = 0xff;
}
int rtc_set_time(struct tm *time)
{
/* Enable write access to RTC registers */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP;
/* 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 );
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;
return 0;
}
int rtc_get_time(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->DR & RTC_DR_DT) >> 4) * 10) + ((RTC->DR & RTC_DR_DU) >> 0);
time->tm_hour = (((RTC->TR & RTC_TR_HT) >> 20) * 10) + ((RTC->TR & RTC_TR_HU) >> 16);
if ( RTC->TR & RTC_TR_PM )
time->tm_hour += 12;
time->tm_min = (((RTC->TR & RTC_TR_MNT) >> 12) * 10) + ((RTC->TR & RTC_TR_MNU) >> 8);
time->tm_sec = (((RTC->TR & RTC_TR_ST) >> 4) * 10) + ((RTC->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 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP;
/* 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 );
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);
/* Exit RTC init mode */
RTC->ISR &= (uint32_t)~RTC_ISR_INIT;
/* Enable RTC write protection */
RTC->WPR = 0xFF;
EXTI->IMR |= EXTI_IMR_MR17;
EXTI->RTSR |= EXTI_RTSR_TR17;
NVIC_SetPriority(RTC_IRQn, 10);
NVIC_EnableIRQ(RTC_IRQn);
rtc_callback.cb = cb;
rtc_callback.arg = arg;
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);
return 0;
}
void rtc_clear_alarm(void)
{
/* Disable Alarm A */
RTC->CR &= RTC_CR_ALRAE;
RTC->CR &= RTC_CR_ALRAIE;
rtc_callback.cb = NULL;
rtc_callback.arg = NULL;
}
void rtc_poweron(void)
{
/* RTC on STM32F0 is online even on sleep modes. No need to power on. */
}
void rtc_poweroff(void)
{
/* Enable write access to RTC registers */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_DBP;
/* 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);
}
void isr_rtc(void)
{
if ((RTC->ISR & RTC_ISR_ALRAF) && (rtc_callback.cb != NULL)) {
rtc_callback.cb(rtc_callback.arg);
RTC->ISR &= ~RTC_ISR_ALRAF;
}
if (sched_context_switch_request) {
thread_yield();
}
}
/**
* 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 */

8
tests/periph_rtc/Makefile

@ -0,0 +1,8 @@
export APPLICATION = periph_rtc
include ../Makefile.tests_common
FEATURES_REQUIRED = periph_rtc
DISABLE_MODULE += auto_init
include $(RIOTBASE)/Makefile.include

7
tests/periph_rtc/README.md

@ -0,0 +1,7 @@
Expected result
===============
When everything works as expected, you should see a alarm message after 10 seconds from start-up.
Background
==========
Test for the low-level RTC driver.

113
tests/periph_rtc/main.c

@ -0,0 +1,113 @@
/*
* Copyright (C) 2015 Lari Lehtomäki
*
* 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 for low-level Real Time clock drivers
*
* This test will initialize the real-time timer and trigger an alarm printing
* 'Hello' every 10 seconds
*
* @author Lari Lehtomäki <lari@lehtomaki.fi>
*
* @}
*/
#include <stdio.h>
#include <time.h>
#include "periph_conf.h"
#include "periph/rtc.h"
#include "hwtimer.h"
#define TM_YEAR_OFFSET (1900)
#if RTC_NUMOF < 1
#error "No RTC found. See the board specific periph_conf.h."
#endif
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);
}
int main(void)
{
puts("\nRIOT RTC low-level driver test");
puts("This test will display 'Alarm' in 10 seconds\n");
puts("Initializing the RTC driver");
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);
rtc_set_time(&time);
hwtimer_wait(100);
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);
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);
hwtimer_wait(100);
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);
puts("The alarm should trigger every 10 seconds for 4 times.");
return 0;
}
Loading…
Cancel
Save