Skip to content

Commit

Permalink
RTC support for STM32F1
Browse files Browse the repository at this point in the history
Add rtc.c and rtc.h support for the on-board real time clock. Minor additions/corrections to rcc.h. Add a test program as well.

Signed-off-by: Rod Gilchrist rod@visibleassets.com
  • Loading branch information
Rod-Gilchrist committed Jan 11, 2012
1 parent f7e384a commit e23ece8
Show file tree
Hide file tree
Showing 5 changed files with 626 additions and 1 deletion.
90 changes: 90 additions & 0 deletions examples/test-rtc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* test-rtc.c
*
* Example program that sets up the Real Time Clock and then blinks the
* LED in patterns for seconds and alarm interrupts.
*
* Created by Rod Gilchrist on 11-12-24.
*
*/

#include "wirish.h"
#include "rtc.h"
#include "usb.h"

int globAlmCnt = 0;
int globOvCnt = 0;
int globSecCnt = 0;
int specAlmCnt = 0;
int lastGlobAlmCnt = -1;
int lastSpecAlmCnt = -1;

void rtc_sec_intr() { if (rtc_is_second()) globSecCnt++; }
void rtc_ovf_intr() { if (rtc_is_overflow()) globOvCnt++; }
void rtc_glob_alm_intr() { if (rtc_is_alarm()) globAlmCnt++; }
void rtc_spec_alm_intr() { if (rtc_is_alarm()) specAlmCnt++; }

void setup() {
pinMode(BOARD_LED_PIN, OUTPUT);
delay(5000);
SerialUSB.println("begin RTC blink");
delay(1000);

rtc_init(RTCSEL_LSI);
rtc_set_prescaler_load(0x7fff);
rtc_set_count(0);

rtc_attach_interrupt(RTC_SECONDS_INTERRUPT, rtc_sec_intr);
rtc_attach_interrupt(RTC_OVERFLOW_INTERRUPT, rtc_ovf_intr); // expected every 128000 seconds
rtc_attach_interrupt(RTC_ALARM_GLOBAL_INTERRUPT, rtc_glob_alm_intr);
rtc_attach_interrupt(RTC_ALARM_SPECIFIC_INTERRUPT, rtc_spec_alm_intr);
}

void loop() {
int i,n;

SerialUSB.print("Time + interrupt counts: ");
SerialUSB.print(rtc_get_count());
SerialUSB.print(".");
SerialUSB.print(rtc_get_divider());
SerialUSB.print(" (");
SerialUSB.print(globSecCnt);
SerialUSB.print(", ");
SerialUSB.print(globOvCnt);
SerialUSB.print(", ");
SerialUSB.print(globAlmCnt);
SerialUSB.print(", ");
SerialUSB.print(specAlmCnt);
SerialUSB.println(")");

delay(1000);

digitalWrite(BOARD_LED_PIN, 1);
if ((lastSpecAlmCnt != specAlmCnt) || (lastGlobAlmCnt != globAlmCnt)){
lastGlobAlmCnt = globAlmCnt;
lastSpecAlmCnt = specAlmCnt;

SerialUSB.println(" -- alarm -- ");
for (i=0;i<3;i++) { digitalWrite(BOARD_LED_PIN, 0); delay(100); digitalWrite(BOARD_LED_PIN, 1); delay(100);}
n = rtc_get_count() + 5;
rtc_set_alarm(n);
}

delay(1000);
digitalWrite(BOARD_LED_PIN, 0);
}

// Force init to be called *first*, i.e. before static object allocation.
// Otherwise, statically allocated objects that need libmaple may fail.
__attribute__((constructor)) void premain() {
init();
}

int main(void) {
setup();

while (true) {
loop();
}
return 0;
}
50 changes: 49 additions & 1 deletion libmaple/rcc.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
*/

#include "libmaple_types.h"
#include "util.h"
#include "bitband.h"

#ifndef _RCC_H_
#define _RCC_H_
Expand Down Expand Up @@ -348,10 +350,11 @@ typedef struct rcc_reg_map {
#define RCC_BDCR_LSEON_BIT 0

#define RCC_BDCR_BDRST BIT(RCC_BDCR_BDRST_BIT)
#define RCC_BDCR_RTCEN BIT(RCC_BDCR_RTC_BIT)
#define RCC_BDCR_RTCEN BIT(RCC_BDCR_RTCEN_BI)
#define RCC_BDCR_RTCSEL (0x3 << 8)
#define RCC_BDCR_RTCSEL_NONE (0x0 << 8)
#define RCC_BDCR_RTCSEL_LSE (0x1 << 8)
#define RCC_BDCR_RTCSEL_LSI (0x2 << 8)
#define RCC_BDCR_RTCSEL_HSE (0x3 << 8)
#define RCC_BDCR_LSEBYP BIT(RCC_BDCR_LSEBYP_BIT)
#define RCC_BDCR_LSERDY BIT(RCC_BDCR_LSERDY_BIT)
Expand Down Expand Up @@ -562,6 +565,51 @@ typedef enum rcc_ahb_divider {
} rcc_ahb_divider;

void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider);

/**
* @brief Start the low speed internal oscillatior
*/
static inline void rcc_start_lsi(void) {
*bb_perip(&RCC_BASE->CSR, RCC_CSR_LSION_BIT) = 1;
while (*bb_perip(&RCC_BASE->CSR, RCC_CSR_LSIRDY_BIT) == 0);
}

/**
* @brief Stop the low speed internal oscillatior
*/
static inline void rcc_stop_lsi(void) {
*bb_perip(&RCC_BASE->CSR, RCC_CSR_LSION_BIT) = 0;
}

/**
* @brief Start the low speed external oscillatior
*/
static inline void rcc_start_lse(void) {
*bb_perip(&RCC_BASE->BDCR, RCC_BDCR_LSEON) = 1;
while (*bb_perip(&RCC_BASE->BDCR, RCC_CSR_LSIRDY_BIT) == 0);
}

/**
* @brief Stop the low speed external oscillatior
*/
static inline void rcc_stop_lse(void) {
*bb_perip(&RCC_BASE->BDCR, RCC_BDCR_LSEON) = 0;
}

/**
* @brief Start the high speed external oscillatior
*/
static inline void rcc_start_hse(void) {
*bb_perip(&RCC_BASE->CR, RCC_CR_HSEON_BIT) = 1;
while (*bb_perip(&RCC_BASE->BDCR, RCC_CR_HSERDY_BIT) == 0);
}

/**
* @brief Stop the high speed external oscillatior
*/
static inline void rcc_stop_hse(void) {
*bb_perip(&RCC_BASE->CR, RCC_CR_HSEON_BIT) = 0;
}

#ifdef __cplusplus
} // extern "C"
Expand Down
230 changes: 230 additions & 0 deletions libmaple/rtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/******************************************************************************
* The MIT License
*
* Copyright (c) 2012 LeafLabs, LLC.
*
* 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.
*****************************************************************************/

/**
* @file rtc.c
* @author Rod Gilchrist <rod@visibleassets.com>
* @brief Real Time Clock interface
*/

#include "rtc.h"

#define NR_RTC_HANDLERS 4

static rtc_dev rtc = {
.regs = RTC_BASE,
.handlers = { [NR_RTC_HANDLERS - 1] = 0 },
};

rtc_dev *RTC = &rtc;


/**
* Initialize the RTC interface, and enable access to its register map and
* the backup registers.
*/
void rtc_init(rtc_clk_src src) {

bkp_init(); // turn on peripheral clocks to PWR and BKP and reset the backup domain via RCC registers.
// (we reset the backup domain here because we must in order to change the rtc clock source).

bkp_enable_writes(); // enable writes to the backup registers and the RTC registers via the DBP bit in the PWR control register

RCC_BASE->BDCR &= ~RCC_BDCR_RTCSEL;
switch (src) {
case RTCSEL_NONE:
RCC_BASE->BDCR = RCC_BDCR_RTCSEL_NONE;
break;

case RTCSEL_LSE:
rcc_start_lse();
RCC_BASE->BDCR = RCC_BDCR_RTCSEL_LSE;
break;

case RTCSEL_LSI:
case RTCSEL_DEFAULT:
rcc_start_lsi();
RCC_BASE->BDCR = RCC_BDCR_RTCSEL_LSI;
break;

case RTCSEL_HSE: // This selection uses HSE/128 as the RTC source (i.e. 64 kHz with an 8 mHz xtal)
rcc_start_hse();
RCC_BASE->BDCR = RCC_BDCR_RTCSEL_HSE;
break;
}
*bb_perip(&RCC_BASE->BDCR, RCC_BDCR_RTCEN_BIT) = 1; // Enable the RTC

rtc_clear_sync();
rtc_wait_sync();
rtc_wait_finished();
}

/**
* @brief Attach a RTC interrupt.
* @param interrupt Interrupt number to attach to; this may be any rtc_interrupt_id.
* @param handler Handler to attach to the given interrupt.
* @see rtc_interrupt_id
*/
void rtc_attach_interrupt( uint8 interrupt,
voidFuncPtr handler) {
RTC->handlers[interrupt] = handler;
rtc_enable_irq(interrupt);
switch (interrupt) {
case RTC_SECONDS_INTERRUPT: nvic_irq_enable(NVIC_RTC); break;
case RTC_OVERFLOW_INTERRUPT: nvic_irq_enable(NVIC_RTC); break;
case RTC_ALARM_GLOBAL_INTERRUPT: nvic_irq_enable(NVIC_RTC); break;
case RTC_ALARM_SPECIFIC_INTERRUPT: nvic_irq_enable(NVIC_RTCALARM); break; // The alarm specific interrupt can wake us from deep sleep.
}
}

/**
* @brief Detach an rtc interrupt.
* @param interrupt Interrupt number to detach.
* @see rtc_interrupt_id
*/
void rtc_detach_interrupt(uint8 interrupt) {
rtc_disable_irq(interrupt);
RTC->handlers[interrupt] = NULL;
}

/*
* IRQ handlers
*/

/* For dispatch routines which service multiple interrupts. */
#define handle_irq(dier_sr, irq_mask, handlers, iid, handled_irq) do { \
if ((dier_sr) & (irq_mask)) { \
void (*__handler)(void) = (handlers)[iid]; \
if (__handler) { \
__handler(); \
handled_irq |= (irq_mask); \
} \
} \
} while (0)

static inline void dispatch_multiple_rtc_irq() {
rtc_reg_map *regs = RTC->regs;
uint32 dsr = regs->CRH & regs->CRL;
void (**hs)(void) = RTC->handlers;
uint32 handled = 0;

handle_irq(dsr, RTC_CRL_SECF, hs, RTC_SECONDS_INTERRUPT, handled);
handle_irq(dsr, RTC_CRL_ALRF, hs, RTC_ALARM_GLOBAL_INTERRUPT, handled);
handle_irq(dsr, RTC_CRL_OWF, hs, RTC_OVERFLOW_INTERRUPT, handled);

regs->CRL &= ~handled;
}

void __irq_rtc(void) {
dispatch_multiple_rtc_irq();
}

/* A special-case dispatch routine for single-interrupt NVIC lines.
* This function assumes that the interrupt corresponding to `RTC_ALARM_INTERRUPT' has
* in fact occurred (i.e., it doesn't check DIER & SR). */
void __irq_rtcalarm(void) {
void (*handler)(void) = RTC->handlers[RTC_ALARM_SPECIFIC_INTERRUPT];
if (handler) {
handler();
*bb_perip(&EXTI_BASE->PR, EXTI_RTC_ALARM_BIT) = 1;
//asm volatile("nop"); // See comment in exti.c. Doesn't seem to be required.
//asm volatile("nop");
}
}

/**
* @brief Returns the rtc's counter (i.e. time/date) value.
*
* This value is likely to be inaccurate if the counter is running
* with a low prescaler.
*/
uint32 rtc_get_count() {
uint32 h, l;
rtc_clear_sync();
rtc_wait_sync();
rtc_wait_finished();
h = RTC->regs->CNTH & 0xffff;
l = RTC->regs->CNTL & 0xffff;
return (h << 16) | l;
}

/**
* @brief Sets the counter value (i.e. time/date) for the rtc.
* @param value New counter value
*/
void rtc_set_count(uint32 value) {
rtc_clear_sync();
rtc_wait_sync();
rtc_wait_finished();
rtc_enter_config_mode();
RTC->regs->CNTH = (value >> 16) & 0xffff;
RTC->regs->CNTL = value & 0xffff;
rtc_exit_config_mode();
rtc_wait_finished();
}

/**
* @brief Sets the prescaler load value for the rtc.
* @param value New prescaler load value (use 0x7fff to get 1 second period with 32.768 Hz clock).
*/
void rtc_set_prescaler_load(uint32 value) {
rtc_clear_sync();
rtc_wait_sync();
rtc_wait_finished();
rtc_enter_config_mode();
RTC->regs->PRLH = (value >> 16) & 0xffff;
RTC->regs->PRLL = value & 0xffff;
rtc_exit_config_mode();
rtc_wait_finished();
}

/**
* @brief Returns the rtc's prescaler divider (i.e. current divider count) value.
*/
uint32 rtc_get_divider() {
uint32 h, l;
rtc_clear_sync();
rtc_wait_sync();
rtc_wait_finished();
h = RTC->regs->DIVH & 0x000f;
l = RTC->regs->DIVL & 0xffff;
return (h << 16) | l;
}

/**
* @brief Sets the alarm value (i.e. time/date) for the rtc.
* @param value New alarm value
*/
void rtc_set_alarm(uint32 value) {
rtc_clear_sync();
rtc_wait_sync();
rtc_wait_finished();
rtc_enter_config_mode();
RTC->regs->ALRH = (value >> 16) & 0xffff;
RTC->regs->ALRL = value & 0xffff;
rtc_exit_config_mode();
rtc_wait_finished();
}
Loading

3 comments on commit e23ece8

@feurig
Copy link

@feurig feurig commented on e23ece8 Aug 15, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did they at least, pull the bug fixx part of this pull request?

@rodgilchrist
Copy link
Owner

@rodgilchrist rodgilchrist commented on e23ece8 Aug 16, 2012 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@feurig
Copy link

@feurig feurig commented on e23ece8 Aug 16, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant the rcc.h changes required. I thought that was a bug.
I keep trying to separate changes required to libmaple in order to add features like your rtc work.

I totally will either use this or rewrite something like it.

Please sign in to comment.