
6 changed files with 404 additions and 0 deletions
@ -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
|
||||
|
@ -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 */ |
@ -0,0 +1,8 @@
|
||||
export APPLICATION = periph_rtc
|
||||
include ../Makefile.tests_common |
||||
|
||||
FEATURES_REQUIRED = periph_rtc
|
||||
|
||||
DISABLE_MODULE += auto_init
|
||||
|
||||
include $(RIOTBASE)/Makefile.include |
@ -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. |
@ -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…
Reference in new issue