stm32: morse timer: standardize example

Simplify, clarify and synchronize the two morse LED blinker examples.
Prepare for one day extracting the core as common morse example code.
This commit is contained in:
Karl Palsson
2016-12-17 00:21:19 +00:00
parent d2abd471a5
commit 9af2b8c7fb
4 changed files with 143 additions and 120 deletions

View File

@@ -0,0 +1,15 @@
# README
This example demonstrates the use of timers to trigger an interrupt. This
example will toggle a LED spelling out the following morse code:
SOS -> ...---...
using international morse timing, with a dot element of 100ms
It's intended for the olimex stm32-h103 eval board. It should blink
a LED on the board.
## Board connections
*none required*

View File

@@ -17,40 +17,50 @@
* along with this library. If not, see <http://www.gnu.org/licenses/>. * along with this library. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h> #include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/timer.h> #include <libopencm3/stm32/timer.h>
#include <libopencm3/cm3/nvic.h> #include <libopencmsis/core_cm3.h>
#include <libopencm3/stm32/exti.h>
uint16_t frequency_sequence[18] = { #ifndef ARRAY_LEN
1000, #define ARRAY_LEN(array) (sizeof((array))/sizeof((array)[0]))
500, #endif
1000,
500, #define LED1_PORT GPIOC
1000, #define LED1_PIN GPIO12
500,
2000, /* Morse standard timings */
500, #define ELEMENT_TIME 500
2000, #define DIT (1*ELEMENT_TIME)
500, #define DAH (3*ELEMENT_TIME)
2000, #define INTRA (1*ELEMENT_TIME)
500, #define INTER (3*ELEMENT_TIME)
1000, #define WORD (7*ELEMENT_TIME)
500,
1000, uint16_t frequency_sequence[] = {
500, DIT,
1000, INTRA,
5000, DIT,
INTRA,
DIT,
INTER,
DAH,
INTRA,
DAH,
INTRA,
DAH,
INTER,
DIT,
INTRA,
DIT,
INTRA,
DIT,
WORD,
}; };
int frequency_sel = 0; int frequency_sel = 0;
uint16_t compare_time;
uint16_t new_time;
uint16_t frequency;
int debug = 0;
static void clock_setup(void) static void clock_setup(void)
{ {
rcc_clock_setup_in_hse_8mhz_out_72mhz(); rcc_clock_setup_in_hse_8mhz_out_72mhz();
@@ -58,14 +68,13 @@ static void clock_setup(void)
static void gpio_setup(void) static void gpio_setup(void)
{ {
/* Enable GPIOC clock. */ /* Enable GPIO clock for leds. */
rcc_periph_clock_enable(RCC_GPIOC); rcc_periph_clock_enable(RCC_GPIOC);
/* Set GPIO12 (in GPIO port C) to 'output push-pull'. */ /* Enable led as output */
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, gpio_set_mode(LED1_PORT, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); GPIO_CNF_OUTPUT_PUSHPULL, LED1_PIN);
gpio_set(LED1_PORT, LED1_PIN);
gpio_set(GPIOC, GPIO12);
} }
static void tim_setup(void) static void tim_setup(void)
@@ -76,55 +85,42 @@ static void tim_setup(void)
/* Enable TIM2 interrupt. */ /* Enable TIM2 interrupt. */
nvic_enable_irq(NVIC_TIM2_IRQ); nvic_enable_irq(NVIC_TIM2_IRQ);
/* Reset TIM2 peripheral. */ /* Reset TIM2 peripheral to defaults. */
rcc_periph_reset_pulse(RST_TIM2); rcc_periph_reset_pulse(RST_TIM2);
/* Timer global mode: /* Timer global mode:
* - No divider * - No divider
* - Alignment edge * - Alignment edge
* - Direction up * - Direction up
* (These are actually default values after reset above, so this call
* is strictly unnecessary, but demos the api for alternative settings)
*/ */
timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT,
TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
/* Reset prescaler value. */ /*
timer_set_prescaler(TIM2, 36000); * Please take note that the clock source for STM32 timers
* might not be the raw APB1/APB2 clocks. In various conditions they
* are doubled. See the Reference Manual for full details!
* In our case, TIM2 on APB1 is running at double frequency, so this
* sets the prescaler to have the timer run at 5kHz
*/
timer_set_prescaler(TIM2, ((rcc_apb1_frequency * 2) / 5000));
/* Disable preload. */ /* Disable preload. */
timer_disable_preload(TIM2); timer_disable_preload(TIM2);
/* Continous mode. */
timer_continuous_mode(TIM2); timer_continuous_mode(TIM2);
/* Period (36kHz). */ /* count full range, as we'll update compare value continuously */
timer_set_period(TIM2, 65535); timer_set_period(TIM2, 65535);
/* Disable outputs. */ /* Set the initual output compare value for OC1. */
timer_disable_oc_output(TIM2, TIM_OC1); timer_set_oc_value(TIM2, TIM_OC1, frequency_sequence[frequency_sel++]);
timer_disable_oc_output(TIM2, TIM_OC2);
timer_disable_oc_output(TIM2, TIM_OC3);
timer_disable_oc_output(TIM2, TIM_OC4);
/* -- OC1 configuration -- */
/* Configure global mode of line 1. */
timer_disable_oc_clear(TIM2, TIM_OC1);
timer_disable_oc_preload(TIM2, TIM_OC1);
timer_set_oc_slow_mode(TIM2, TIM_OC1);
timer_set_oc_mode(TIM2, TIM_OC1, TIM_OCM_FROZEN);
/* Set the capture compare value for OC1. */
timer_set_oc_value(TIM2, TIM_OC1, 1000);
/* ---- */
/* ARR reload enable. */
timer_disable_preload(TIM2);
/* Counter enable. */ /* Counter enable. */
timer_enable_counter(TIM2); timer_enable_counter(TIM2);
/* Enable commutation interrupt. */ /* Enable Channel 1 compare interrupt to recalculate compare values */
timer_enable_irq(TIM2, TIM_DIER_CC1IE); timer_enable_irq(TIM2, TIM_DIER_CC1IE);
} }
@@ -139,18 +135,19 @@ void tim2_isr(void)
* Get current timer value to calculate next * Get current timer value to calculate next
* compare register value. * compare register value.
*/ */
compare_time = timer_get_counter(TIM2); uint16_t compare_time = timer_get_counter(TIM2);
/* Calculate and set the next compare value. */ /* Calculate and set the next compare value. */
frequency = frequency_sequence[frequency_sel++]; uint16_t frequency = frequency_sequence[frequency_sel++];
new_time = compare_time + frequency; uint16_t new_time = compare_time + frequency;
timer_set_oc_value(TIM2, TIM_OC1, new_time); timer_set_oc_value(TIM2, TIM_OC1, new_time);
if (frequency_sel == 18) if (frequency_sel == ARRAY_LEN(frequency_sequence)) {
frequency_sel = 0; frequency_sel = 0;
}
/* Toggle LED to indicate compare event. */ /* Toggle LED to indicate compare event. */
gpio_toggle(GPIOC, GPIO12); gpio_toggle(LED1_PORT, LED1_PIN);
} }
} }
@@ -160,8 +157,17 @@ int main(void)
gpio_setup(); gpio_setup();
tim_setup(); tim_setup();
while (1) /* Loop calling Wait For Interrupt. In older pre cortex ARM this is
__asm("nop"); * just equivalent to nop. On cortex it puts the cpu to sleep until
* one of the three occurs:
*
* a non-masked interrupt occurs and is taken
* an interrupt masked by PRIMASK becomes pending
* a Debug Entry request
*/
while (1) {
__WFI(); /* Wait For Interrupt. */
}
return 0; return 0;
} }

View File

@@ -1,14 +1,14 @@
# README # README
This example demonstrates the use of timers to trigger an interrupt. This This example demonstrates the use of timers to trigger an interrupt. This
example will toggle two LEDs spelling out the following morse code: example will toggle a LED spelling out the following morse code:
SOS -> ...---... SOS -> ...---...
Where dots are .1s, dashes .2s, gaps .05s and the word pause .5s. using international morse timing, with a dot element of 100ms
It's intended for the ST STM32F4DISCOVERY eval board. It should blink It's intended for the ST STM32F4DISCOVERY eval board. It should blink
the LEDs on the board. a LED on the board.
## Board connections ## Board connections

View File

@@ -22,41 +22,45 @@
#include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h> #include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/timer.h> #include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/exti.h>
#include <libopencmsis/core_cm3.h> #include <libopencmsis/core_cm3.h>
#ifndef ARRAY_LEN #ifndef ARRAY_LEN
#define ARRAY_LEN(array) (sizeof((array))/sizeof((array)[0])) #define ARRAY_LEN(array) (sizeof((array))/sizeof((array)[0]))
#endif #endif
uint16_t frequency_sequence[18] = { #define LED1_PORT GPIOD
1000, #define LED1_PIN GPIO12
500,
1000, /* Morse standard timings */
500, #define ELEMENT_TIME 500
1000, #define DIT (1*ELEMENT_TIME)
500, #define DAH (3*ELEMENT_TIME)
2000, #define INTRA (1*ELEMENT_TIME)
500, #define INTER (3*ELEMENT_TIME)
2000, #define WORD (7*ELEMENT_TIME)
500,
2000, uint16_t frequency_sequence[] = {
500, DIT,
1000, INTRA,
500, DIT,
1000, INTRA,
500, DIT,
1000, INTER,
5000, DAH,
INTRA,
DAH,
INTRA,
DAH,
INTER,
DIT,
INTRA,
DIT,
INTRA,
DIT,
WORD,
}; };
uint16_t frequency_sel = 0; int frequency_sel = 0;
uint16_t compare_time;
uint16_t new_time;
uint16_t frequency;
int debug = 0;
static void clock_setup(void) static void clock_setup(void)
{ {
@@ -68,12 +72,9 @@ static void gpio_setup(void)
/* Enable GPIO clock for leds. */ /* Enable GPIO clock for leds. */
rcc_periph_clock_enable(RCC_GPIOD); rcc_periph_clock_enable(RCC_GPIOD);
/* Set GPIO12 (in GPIO port D) to 'output push-pull'. */ /* Enable led as output */
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, gpio_mode_setup(LED1_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED1_PIN);
GPIO_PUPD_NONE, GPIO12 | GPIO13); gpio_set(LED1_PORT, LED1_PIN);
gpio_set(GPIOD, GPIO12);
gpio_clear(GPIOD, GPIO13);
} }
static void tim_setup(void) static void tim_setup(void)
@@ -95,15 +96,16 @@ static void tim_setup(void)
* is strictly unnecessary, but demos the api for alternative settings) * is strictly unnecessary, but demos the api for alternative settings)
*/ */
timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT,
TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
/*
* Please take note that the clock source for STM32F4 timers /*
* might not be the raw APB1/APB2 clocks. In various conditions they * Please take note that the clock source for STM32 timers
* are doubled. See the Reference Manual for full details! * might not be the raw APB1/APB2 clocks. In various conditions they
* In our case, TIM2 on APB1 is running at double frequency, so this * are doubled. See the Reference Manual for full details!
* sets the prescaler to have the timer run at 10kHz * In our case, TIM2 on APB1 is running at double frequency, so this
*/ * sets the prescaler to have the timer run at 5kHz
timer_set_prescaler(TIM2, ((rcc_apb1_frequency * 2) / 10000)); */
timer_set_prescaler(TIM2, ((rcc_apb1_frequency * 2) / 5000));
/* Disable preload. */ /* Disable preload. */
timer_disable_preload(TIM2); timer_disable_preload(TIM2);
@@ -113,7 +115,7 @@ static void tim_setup(void)
timer_set_period(TIM2, 65535); timer_set_period(TIM2, 65535);
/* Set the initual output compare value for OC1. */ /* Set the initual output compare value for OC1. */
timer_set_oc_value(TIM2, TIM_OC1, 1000); timer_set_oc_value(TIM2, TIM_OC1, frequency_sequence[frequency_sel++]);
/* Counter enable. */ /* Counter enable. */
timer_enable_counter(TIM2); timer_enable_counter(TIM2);
@@ -133,11 +135,11 @@ void tim2_isr(void)
* Get current timer value to calculate next * Get current timer value to calculate next
* compare register value. * compare register value.
*/ */
compare_time = timer_get_counter(TIM2); uint16_t compare_time = timer_get_counter(TIM2);
/* Calculate and set the next compare value. */ /* Calculate and set the next compare value. */
frequency = frequency_sequence[frequency_sel++]; uint16_t frequency = frequency_sequence[frequency_sel++];
new_time = compare_time + frequency; uint16_t new_time = compare_time + frequency;
timer_set_oc_value(TIM2, TIM_OC1, new_time); timer_set_oc_value(TIM2, TIM_OC1, new_time);
if (frequency_sel == ARRAY_LEN(frequency_sequence)) { if (frequency_sel == ARRAY_LEN(frequency_sequence)) {
@@ -145,8 +147,7 @@ void tim2_isr(void)
} }
/* Toggle LED to indicate compare event. */ /* Toggle LED to indicate compare event. */
gpio_toggle(GPIOD, GPIO12); gpio_toggle(LED1_PORT, LED1_PIN);
gpio_toggle(GPIOD, GPIO13);
} }
} }
@@ -164,8 +165,9 @@ int main(void)
* an interrupt masked by PRIMASK becomes pending * an interrupt masked by PRIMASK becomes pending
* a Debug Entry request * a Debug Entry request
*/ */
while (1) while (1) {
__WFI(); /* Wait For Interrupt. */ __WFI(); /* Wait For Interrupt. */
}
return 0; return 0;
} }