Add examples making use of the RTC

Add an example using the RTC to help with a lower power design.  This is
a sister example to the existing "button-irq-printf", which is
functionally identical, but uses far less power.

There's more tricks that can be done to lower the power even further,
but this shows a few of the early steps that can be done, using the RTC
wakeup instead of a timer.
This commit is contained in:
Karl Palsson 2013-01-23 00:01:46 +00:00
parent e5b3250382
commit d9fb4f7401
11 changed files with 508 additions and 17 deletions

View File

@ -19,7 +19,7 @@
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/f1/gpio.h>
#include <libopencm3/stm32/f1/rtc.h>
#include <libopencm3/stm32/rtc.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/cm3/nvic.h>

View File

@ -20,7 +20,7 @@
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/f1/gpio.h>
#include <libopencm3/stm32/f1/rtc.h>
#include <libopencm3/stm32/rtc.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/cm3/nvic.h>

View File

@ -0,0 +1,24 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## This library is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This library 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 Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this library. If not, see <http://www.gnu.org/licenses/>.
##
BINARY = main
LDSCRIPT = ../../../../../lib/stm32/l1/stm32l15xxb.ld
include ../../Makefile.include

View File

@ -0,0 +1,18 @@
This is _functionally_ identical to the "button-irq-printf"
example, but has been modified to use some low power features.
There is a 115200@8n1 console on PA2, which prints a tick count every second,
and when the user push button is pressed, the time it is held down for is
timed (in milliseconds)
Instead of free running timers and busy loops, this version uses the RTC
module and attempts to sleep as much as possible, including while the button
is pressed.
Status
~~~~~~
Only very basic power savings are done!
Current consumption, led off/on, 16Mhz HSI: 2.7mA/5.4mA
Current consumption, led off/on, 4Mhz HSI: 1.4mA/?.?mA
Current consumption, led off/on, 4Mhz MSI: 0.9mA/?.?mA

View File

@ -0,0 +1,300 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2012 Karl Palsson <karlp@tweak.net.au>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/stm32/dbgmcu.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/rtc.h>
#include <libopencm3/stm32/l1/rcc.h>
#include <libopencm3/stm32/l1/flash.h>
#include "syscfg.h"
static volatile struct state_t state;
__attribute__((always_inline)) static inline void __WFI(void)
{
__asm volatile ("wfi");
}
void gpio_setup(void)
{
/* green led for ticking, blue for button feedback */
gpio_mode_setup(LED_DISCO_GREEN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_DISCO_GREEN_PIN);
gpio_mode_setup(LED_DISCO_BLUE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_DISCO_BLUE_PIN);
/* Setup GPIO pins for USART2 transmit. */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2);
/* Setup USART2 TX pin as alternate function. */
gpio_set_af(GPIOA, GPIO_AF7, GPIO2);
}
void BUTTON_DISCO_USER_isr(void)
{
exti_reset_request(BUTTON_DISCO_USER_EXTI);
state.pressed = true;
if (state.falling) {
state.falling = false;
exti_set_trigger(BUTTON_DISCO_USER_EXTI, EXTI_TRIGGER_RISING);
state.hold_time = TIM_CNT(TIMER_BUTTON_PRESS);
} else {
state.falling = true;
exti_set_trigger(BUTTON_DISCO_USER_EXTI, EXTI_TRIGGER_FALLING);
state.hold_time = TIM_CNT(TIMER_BUTTON_PRESS) = 0;
}
}
void setup_buttons(void)
{
/* Enable EXTI0 interrupt. */
nvic_enable_irq(BUTTON_DISCO_USER_NVIC);
gpio_mode_setup(BUTTON_DISCO_USER_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, BUTTON_DISCO_USER_PIN);
/* Configure the EXTI subsystem. */
exti_select_source(BUTTON_DISCO_USER_EXTI, BUTTON_DISCO_USER_PORT);
state.falling = false;
exti_set_trigger(BUTTON_DISCO_USER_EXTI, EXTI_TRIGGER_RISING);
exti_enable_request(BUTTON_DISCO_USER_EXTI);
}
void usart_setup(void)
{
usart_set_baudrate(USART_CONSOLE, 115200);
usart_set_databits(USART_CONSOLE, 8);
usart_set_stopbits(USART_CONSOLE, USART_STOPBITS_1);
usart_set_mode(USART_CONSOLE, USART_MODE_TX);
usart_set_parity(USART_CONSOLE, USART_PARITY_NONE);
usart_set_flow_control(USART_CONSOLE, USART_FLOWCONTROL_NONE);
/* Finally enable the USART. */
usart_enable(USART_CONSOLE);
}
/**
* Use USART_CONSOLE as a console.
* @param file
* @param ptr
* @param len
* @return
*/
int _write(int file, char *ptr, int len)
{
int i;
if (file == STDOUT_FILENO || file == STDERR_FILENO) {
for (i = 0; i < len; i++) {
if (ptr[i] == '\n') {
usart_send_blocking(USART_CONSOLE, '\r');
}
usart_send_blocking(USART_CONSOLE, ptr[i]);
}
return i;
}
errno = EIO;
return -1;
}
/*
* Free running ms timer.
*/
void setup_button_press_timer(void)
{
timer_reset(TIMER_BUTTON_PRESS);
timer_set_prescaler(TIMER_BUTTON_PRESS, 3999); // 4Mhz/1000hz - 1
timer_set_period(TIMER_BUTTON_PRESS, 0xffff);
timer_enable_counter(TIMER_BUTTON_PRESS);
}
int setup_rtc(void)
{
/* turn on power block to enable unlocking */
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_PWREN);
pwr_disable_backup_domain_write_protect();
/* reset rtc */
RCC_CSR |= RCC_CSR_RTCRST;
RCC_CSR &= ~RCC_CSR_RTCRST;
/* We want to use the LSE fitted on the discovery board */
rcc_osc_on(LSE);
rcc_wait_for_osc_ready(LSE);
/* Select the LSE as rtc clock */
rcc_rtc_select_clock(RCC_CSR_RTCSEL_LSE);
/* ?! Stdperiph examples don't turn this on until _afterwards_ which
* simply doesn't work. It must be on at least to be able to configure it */
RCC_CSR |= RCC_CSR_RTCEN;
rtc_unlock();
/* enter init mode */
RTC_ISR |= RTC_ISR_INIT;
while ((RTC_ISR & RTC_ISR_INITF) == 0)
;
/* set synch prescaler, using defaults for 1Hz out */
u32 sync = 255;
u32 async = 127;
rtc_set_prescaler(sync, async);
/* load time and date here if desired, and hour format */
/* exit init mode */
RTC_ISR &= ~(RTC_ISR_INIT);
/* and write protect again */
rtc_lock();
/* and finally enable the clock */
RCC_CSR |= RCC_CSR_RTCEN;
/* And wait for synchro.. */
rtc_wait_for_synchro();
return 0;
}
int setup_rtc_wakeup(int period)
{
rtc_unlock();
/* ensure wakeup timer is off */
RTC_CR &= ~RTC_CR_WUTE;
/* Wait until we can write */
while ((RTC_ISR & RTC_ISR_WUTWF) == 0)
;
RTC_WUTR = period - 1;
/* Use the 1Hz clock as source */
RTC_CR &= ~(RTC_CR_WUCLKSEL_MASK << RTC_CR_WUCLKSEL_SHIFT);
RTC_CR |= (RTC_CR_WUCLKSEL_SPRE << RTC_CR_WUCLKSEL_SHIFT);
/* Restart WakeUp unit */
RTC_CR |= RTC_CR_WUTE;
/* interrupt configuration */
/* also, let's have an interrupt */
RTC_CR |= RTC_CR_WUTIE;
/* done with rtc registers, lock them again */
rtc_lock();
nvic_enable_irq(NVIC_RTC_WKUP_IRQ);
// EXTI configuration
/* Configure the EXTI subsystem. */
// not needed, this chooses ports exti_select_source(EXTI20, BUTTON_DISCO_USER_PORT);
exti_set_trigger(EXTI20, EXTI_TRIGGER_RISING);
exti_enable_request(EXTI20);
return 0;
}
void rtc_wkup_isr(void)
{
/* clear flag, not write protected */
RTC_ISR &= ~(RTC_ISR_WUTF);
exti_reset_request(EXTI20);
state.rtc_ticked = true;
}
int process_state(volatile struct state_t *st)
{
if (st->rtc_ticked) {
st->rtc_ticked = 0;
printf("Tick: %x\n", (unsigned int) RTC_TR);
#if FULL_USER_EXPERIENCE
gpio_toggle(LED_DISCO_GREEN_PORT, LED_DISCO_GREEN_PIN);
#else
gpio_clear(LED_DISCO_GREEN_PORT, LED_DISCO_GREEN_PIN);
#endif
}
if (st->pressed) {
st->pressed = false;
if (st->falling) {
gpio_set(LED_DISCO_BLUE_PORT, LED_DISCO_BLUE_PIN);
printf("Pushed down!\n");
} else {
gpio_clear(LED_DISCO_BLUE_PORT, LED_DISCO_BLUE_PIN);
printf("held: %u ms\n", st->hold_time);
}
}
return 0;
}
void reset_clocks(void)
{
/* 4MHz MSI raw range 2*/
clock_scale_t myclock_config = {
.hpre = RCC_CFGR_HPRE_SYSCLK_NODIV,
.ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV,
.ppre2 = RCC_CFGR_PPRE2_HCLK_NODIV,
.voltage_scale = RANGE2,
.flash_config = FLASH_LATENCY_0WS,
.apb1_frequency = 4194000,
.apb2_frequency = 4194000,
.msi_range = RCC_ICSCR_MSIRANGE_4MHZ,
};
rcc_clock_setup_msi(&myclock_config);
/* buttons and uarts */
rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_GPIOAEN);
/* user feedback leds */
rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_GPIOBEN);
/* Enable clocks for USART2. */
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USART2EN);
/* And a timers for button presses */
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM7EN);
}
int main(void)
{
reset_clocks();
gpio_setup();
usart_setup();
setup_buttons();
setup_button_press_timer();
printf("we're awake!\n");
setup_rtc();
setup_rtc_wakeup(1);
while (1) {
PWR_CR |= PWR_CR_LPSDSR;
pwr_set_stop_mode();
__WFI();
reset_clocks();
process_state(&state);
}
return 0;
}

View File

@ -0,0 +1,61 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2012 Karl Palsson <karlp@tweak.net.au>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SYSCFG_H
#define SYSCFG_H
#ifdef __cplusplus
extern "C" {
#endif
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/stm32/usart.h>
#define USART_CONSOLE USART2
#define USE_NASTYLOG 1
#define LED_DISCO_GREEN_PORT GPIOB
#define LED_DISCO_GREEN_PIN GPIO7
#define LED_DISCO_BLUE_PORT GPIOB
#define LED_DISCO_BLUE_PIN GPIO6
#define BUTTON_DISCO_USER_PORT GPIOA
#define BUTTON_DISCO_USER_PIN GPIO0
#define BUTTON_DISCO_USER_EXTI EXTI0
#define BUTTON_DISCO_USER_isr exti0_isr
#define BUTTON_DISCO_USER_NVIC NVIC_EXTI0_IRQ
#define TIMER_BUTTON_PRESS TIM7
struct state_t {
bool falling;
bool pressed;
int rtc_ticked;
int hold_time;
};
#ifdef __cplusplus
}
#endif
#endif /* SYSCFG_H */

View File

@ -1,3 +1,35 @@
* Prints to the screen when the button is pushed/released (irq driven)
115200@8n1 console on PA2 (tx only)
* uses basic timer 6 with overflows to generate a 1ms counter (not an ideal
use, but shows some api methods and can be demoed on the disco board)
* uses basic timer 7 with the exti interrupts to do ghetto input capture.
Not as fast or precise as the real input capture modes, but can be used
on any gpio pin.
No effort at saving power is made here. Current consumption on the Disco board
is ~7.5mA when the green tick led is off, and about 10.5mA when it is on.
example output:
hi guys!
TICK 0
TICK 1
TICK 2
Pushed down!
held: 443 ms
Pushed down!
TICK 3
held: 217 ms
Pushed down!
held: 99 ms
Pushed down!
TICK 4
held: 73 ms
Pushed down!
held: 60 ms
TICK 5
Pushed down!
held: 98 ms
Pushed down!

View File

@ -22,8 +22,9 @@
#include <unistd.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/l1/rcc.h>
#include <libopencm3/stm32/l1/gpio.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/usart.h>
#include "syscfg.h"
@ -32,17 +33,25 @@ static struct state_t state;
void clock_setup(void)
{
rcc_clock_setup_pll(&clock_config[CLOCK_VRANGE1_HSI_PLL_24MHZ]);
/* Lots of things on all ports... */
rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_GPIOAEN);
rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_GPIOBEN);
/* Enable clocks for USART2. */
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USART2EN);
/* And timers. */
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM6EN);
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM7EN);
}
void gpio_setup(void)
{
/* green led for ticking, blue for button feedback */
gpio_mode_setup(LED_DISCO_GREEN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_DISCO_GREEN_PIN);
gpio_mode_setup(LED_DISCO_BLUE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_DISCO_BLUE_PIN);
/* Setup GPIO pins for USART2 transmit. */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2);
@ -92,18 +101,61 @@ void BUTTON_DISCO_USER_isr(void)
{
exti_reset_request(BUTTON_DISCO_USER_EXTI);
if (state.falling) {
gpio_clear(LED_DISCO_BLUE_PORT, LED_DISCO_BLUE_PIN);
state.falling = false;
exti_set_trigger(BUTTON_DISCO_USER_EXTI, EXTI_TRIGGER_RISING);
// ILOG("fell: %d\n", TIM_CNT(TIM7));
puts("fell!\n");
unsigned int x = TIM_CNT(TIM7);
printf("held: %u ms\n", x);
} else {
puts("Rose!\n");
// TIM_CNT(TIM7) = 0;
gpio_set(LED_DISCO_BLUE_PORT, LED_DISCO_BLUE_PIN);
printf("Pushed down!\n");
TIM_CNT(TIM7) = 0;
state.falling = true;
exti_set_trigger(BUTTON_DISCO_USER_EXTI, EXTI_TRIGGER_FALLING);
}
}
static volatile int t6ovf = 0;
void tim6_isr(void)
{
TIM_SR(TIM6) &= ~TIM_SR_UIF;
if (t6ovf++ > 1000) {
printf("TICK %d\n", state.tickcount++);
t6ovf = 0;
gpio_toggle(LED_DISCO_GREEN_PORT, LED_DISCO_GREEN_PIN);
}
}
/*
* Another ms timer, this one used to generate an overflow interrupt at 1ms
* It is used to toggle leds and write tick counts
*/
void setup_tim6(void)
{
timer_reset(TIM6);
// 24Mhz / 10khz -1.
timer_set_prescaler(TIM6, 2399); // 24Mhz/10000hz - 1
// 10khz for 10 ticks = 1 khz overflow = 1ms overflow interrupts
timer_set_period(TIM6, 10);
nvic_enable_irq(NVIC_TIM6_IRQ);
timer_enable_update_event(TIM6); // default at reset!
timer_enable_irq(TIM6, TIM_DIER_UIE);
timer_enable_counter(TIM6);
}
/*
* Free running ms timer.
*/
void setup_tim7(void)
{
timer_reset(TIM7);
timer_set_prescaler(TIM7, 23999); // 24Mhz/1000hz - 1
timer_set_period(TIM7, 0xffff);
timer_enable_counter(TIM7);
}
void setup_buttons(void)
{
/* Enable EXTI0 interrupt. */
@ -120,19 +172,15 @@ void setup_buttons(void)
int main(void)
{
int i;
int j = 0;
clock_setup();
gpio_setup();
usart_setup();
puts("hi guys!\n");
printf("hi guys!\n");
setup_buttons();
setup_tim6();
setup_tim7();
while (1) {
puts("tick:");
putchar('a' + (j++ % 26));
gpio_toggle(GPIOB, GPIO7); /* LED on/off */
for (i = 0; i < 100000; i++) /* Wait a bit. */
__asm__("NOP");
;
}
return 0;

View File

@ -24,8 +24,8 @@
extern "C" {
#endif
#include <libopencm3/stm32/l1/gpio.h>
#include <libopencm3/stm32/l1/nvic.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/exti.h>
#include <libopencm3/stm32/usart.h>
@ -46,6 +46,7 @@ extern "C" {
struct state_t {
bool falling;
int tickcount;
};

View File

@ -444,6 +444,7 @@ void rcc_set_ppre1(u32 ppre1);
void rcc_set_hpre(u32 hpre);
void rcc_set_usbpre(u32 usbpre);
u32 rcc_get_system_clock_source(int i);
void rcc_rtc_select_clock(u32 clock);
void rcc_clock_setup_msi(const clock_scale_t *clock);
void rcc_clock_setup_hsi(const clock_scale_t *clock);
void rcc_clock_setup_pll(const clock_scale_t *clock);

View File

@ -437,6 +437,12 @@ u32 rcc_system_clock_source(void)
return ((RCC_CFGR & 0x000c) >> 2);
}
void rcc_rtc_select_clock(u32 clock)
{
RCC_CSR &= ~(RCC_CSR_RTCSEL_MASK << RCC_CSR_RTCSEL_SHIFT);
RCC_CSR |= (clock << RCC_CSR_RTCSEL_SHIFT);
}
void rcc_clock_setup_msi(const clock_scale_t *clock)
{
/* Enable internal multi-speed oscillator. */