From 9af2b8c7fb2a5bea9888274864241a063dc0475e Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Sat, 17 Dec 2016 00:21:19 +0000 Subject: [PATCH] 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. --- examples/stm32/f1/stm32-h103/timer/README.md | 15 ++ examples/stm32/f1/stm32-h103/timer/timer.c | 142 +++++++++--------- .../f4/stm32f4-discovery/timer/README.md | 6 +- .../stm32/f4/stm32f4-discovery/timer/timer.c | 100 ++++++------ 4 files changed, 143 insertions(+), 120 deletions(-) create mode 100644 examples/stm32/f1/stm32-h103/timer/README.md diff --git a/examples/stm32/f1/stm32-h103/timer/README.md b/examples/stm32/f1/stm32-h103/timer/README.md new file mode 100644 index 0000000..e862d55 --- /dev/null +++ b/examples/stm32/f1/stm32-h103/timer/README.md @@ -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* diff --git a/examples/stm32/f1/stm32-h103/timer/timer.c b/examples/stm32/f1/stm32-h103/timer/timer.c index f228317..7eefcaa 100644 --- a/examples/stm32/f1/stm32-h103/timer/timer.c +++ b/examples/stm32/f1/stm32-h103/timer/timer.c @@ -17,40 +17,50 @@ * along with this library. If not, see . */ +#include #include #include #include -#include -#include +#include -uint16_t frequency_sequence[18] = { - 1000, - 500, - 1000, - 500, - 1000, - 500, - 2000, - 500, - 2000, - 500, - 2000, - 500, - 1000, - 500, - 1000, - 500, - 1000, - 5000, +#ifndef ARRAY_LEN +#define ARRAY_LEN(array) (sizeof((array))/sizeof((array)[0])) +#endif + +#define LED1_PORT GPIOC +#define LED1_PIN GPIO12 + +/* Morse standard timings */ +#define ELEMENT_TIME 500 +#define DIT (1*ELEMENT_TIME) +#define DAH (3*ELEMENT_TIME) +#define INTRA (1*ELEMENT_TIME) +#define INTER (3*ELEMENT_TIME) +#define WORD (7*ELEMENT_TIME) + +uint16_t frequency_sequence[] = { + DIT, + INTRA, + DIT, + INTRA, + DIT, + INTER, + DAH, + INTRA, + DAH, + INTRA, + DAH, + INTER, + DIT, + INTRA, + DIT, + INTRA, + DIT, + WORD, }; int frequency_sel = 0; -uint16_t compare_time; -uint16_t new_time; -uint16_t frequency; -int debug = 0; - static void clock_setup(void) { rcc_clock_setup_in_hse_8mhz_out_72mhz(); @@ -58,14 +68,13 @@ static void clock_setup(void) static void gpio_setup(void) { - /* Enable GPIOC clock. */ + /* Enable GPIO clock for leds. */ rcc_periph_clock_enable(RCC_GPIOC); - /* Set GPIO12 (in GPIO port C) to 'output push-pull'. */ - gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, - GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); - - gpio_set(GPIOC, GPIO12); + /* Enable led as output */ + gpio_set_mode(LED1_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, LED1_PIN); + gpio_set(LED1_PORT, LED1_PIN); } static void tim_setup(void) @@ -76,55 +85,42 @@ static void tim_setup(void) /* Enable TIM2 interrupt. */ nvic_enable_irq(NVIC_TIM2_IRQ); - /* Reset TIM2 peripheral. */ + /* Reset TIM2 peripheral to defaults. */ rcc_periph_reset_pulse(RST_TIM2); /* Timer global mode: * - No divider * - Alignment edge * - 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, - 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. */ timer_disable_preload(TIM2); - - /* Continous mode. */ timer_continuous_mode(TIM2); - /* Period (36kHz). */ + /* count full range, as we'll update compare value continuously */ timer_set_period(TIM2, 65535); - /* Disable outputs. */ - timer_disable_oc_output(TIM2, TIM_OC1); - 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); + /* Set the initual output compare value for OC1. */ + timer_set_oc_value(TIM2, TIM_OC1, frequency_sequence[frequency_sel++]); /* Counter enable. */ timer_enable_counter(TIM2); - /* Enable commutation interrupt. */ + /* Enable Channel 1 compare interrupt to recalculate compare values */ timer_enable_irq(TIM2, TIM_DIER_CC1IE); } @@ -139,18 +135,19 @@ void tim2_isr(void) * Get current timer value to calculate next * compare register value. */ - compare_time = timer_get_counter(TIM2); + uint16_t compare_time = timer_get_counter(TIM2); /* Calculate and set the next compare value. */ - frequency = frequency_sequence[frequency_sel++]; - new_time = compare_time + frequency; + uint16_t frequency = frequency_sequence[frequency_sel++]; + uint16_t new_time = compare_time + frequency; timer_set_oc_value(TIM2, TIM_OC1, new_time); - if (frequency_sel == 18) + if (frequency_sel == ARRAY_LEN(frequency_sequence)) { frequency_sel = 0; + } /* 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(); tim_setup(); - while (1) - __asm("nop"); + /* Loop calling Wait For Interrupt. In older pre cortex ARM this is + * 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; } diff --git a/examples/stm32/f4/stm32f4-discovery/timer/README.md b/examples/stm32/f4/stm32f4-discovery/timer/README.md index 55e1006..5ab17cb 100644 --- a/examples/stm32/f4/stm32f4-discovery/timer/README.md +++ b/examples/stm32/f4/stm32f4-discovery/timer/README.md @@ -1,14 +1,14 @@ # README 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 -> ...---... -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 -the LEDs on the board. +a LED on the board. ## Board connections diff --git a/examples/stm32/f4/stm32f4-discovery/timer/timer.c b/examples/stm32/f4/stm32f4-discovery/timer/timer.c index b9cf04c..dba139c 100644 --- a/examples/stm32/f4/stm32f4-discovery/timer/timer.c +++ b/examples/stm32/f4/stm32f4-discovery/timer/timer.c @@ -22,41 +22,45 @@ #include #include #include -#include - #include #ifndef ARRAY_LEN #define ARRAY_LEN(array) (sizeof((array))/sizeof((array)[0])) #endif -uint16_t frequency_sequence[18] = { - 1000, - 500, - 1000, - 500, - 1000, - 500, - 2000, - 500, - 2000, - 500, - 2000, - 500, - 1000, - 500, - 1000, - 500, - 1000, - 5000, +#define LED1_PORT GPIOD +#define LED1_PIN GPIO12 + +/* Morse standard timings */ +#define ELEMENT_TIME 500 +#define DIT (1*ELEMENT_TIME) +#define DAH (3*ELEMENT_TIME) +#define INTRA (1*ELEMENT_TIME) +#define INTER (3*ELEMENT_TIME) +#define WORD (7*ELEMENT_TIME) + +uint16_t frequency_sequence[] = { + DIT, + INTRA, + DIT, + INTRA, + DIT, + INTER, + DAH, + INTRA, + DAH, + INTRA, + DAH, + INTER, + DIT, + INTRA, + DIT, + INTRA, + DIT, + WORD, }; -uint16_t frequency_sel = 0; - -uint16_t compare_time; -uint16_t new_time; -uint16_t frequency; -int debug = 0; +int frequency_sel = 0; static void clock_setup(void) { @@ -68,12 +72,9 @@ static void gpio_setup(void) /* Enable GPIO clock for leds. */ rcc_periph_clock_enable(RCC_GPIOD); - /* Set GPIO12 (in GPIO port D) to 'output push-pull'. */ - gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, - GPIO_PUPD_NONE, GPIO12 | GPIO13); - - gpio_set(GPIOD, GPIO12); - gpio_clear(GPIOD, GPIO13); + /* Enable led as output */ + gpio_mode_setup(LED1_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED1_PIN); + gpio_set(LED1_PORT, LED1_PIN); } static void tim_setup(void) @@ -95,15 +96,16 @@ static void tim_setup(void) * is strictly unnecessary, but demos the api for alternative settings) */ timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, - 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 - * 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 10kHz - */ - timer_set_prescaler(TIM2, ((rcc_apb1_frequency * 2) / 10000)); + TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); + + /* + * 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. */ timer_disable_preload(TIM2); @@ -113,7 +115,7 @@ static void tim_setup(void) timer_set_period(TIM2, 65535); /* 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. */ timer_enable_counter(TIM2); @@ -133,11 +135,11 @@ void tim2_isr(void) * Get current timer value to calculate next * compare register value. */ - compare_time = timer_get_counter(TIM2); + uint16_t compare_time = timer_get_counter(TIM2); /* Calculate and set the next compare value. */ - frequency = frequency_sequence[frequency_sel++]; - new_time = compare_time + frequency; + uint16_t frequency = frequency_sequence[frequency_sel++]; + uint16_t new_time = compare_time + frequency; timer_set_oc_value(TIM2, TIM_OC1, new_time); if (frequency_sel == ARRAY_LEN(frequency_sequence)) { @@ -145,8 +147,7 @@ void tim2_isr(void) } /* Toggle LED to indicate compare event. */ - gpio_toggle(GPIOD, GPIO12); - gpio_toggle(GPIOD, GPIO13); + gpio_toggle(LED1_PORT, LED1_PIN); } } @@ -164,8 +165,9 @@ int main(void) * an interrupt masked by PRIMASK becomes pending * a Debug Entry request */ - while (1) + while (1) { __WFI(); /* Wait For Interrupt. */ + } return 0; }