LED: Introduce LED blinking pattern code
It might be useful to display some different blinking patterns to
indicate specific system state (such as DFU mode vs. regular firmware)
diff --git a/firmware/apps/cardem/main.c b/firmware/apps/cardem/main.c
index 7402ab7..7d90eb0 100644
--- a/firmware/apps/cardem/main.c
+++ b/firmware/apps/cardem/main.c
@@ -121,9 +121,8 @@
enum confNum last_simtrace_config = simtrace_config;
unsigned int i = 0;
- LED_Configure(LED_NUM_RED);
- LED_Configure(LED_NUM_GREEN);
- LED_Set(LED_NUM_RED);
+ led_init();
+ led_blink(LED_RED, BLINK_3O_5F);
/* Enable watchdog for 500ms, with no window */
WDT_Enable(WDT, WDT_MR_WDRSTEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT |
@@ -195,8 +194,6 @@
}
} else if (isUsbConnected == 0) {
TRACE_INFO("USB is now configured\n\r");
- LED_Set(LED_NUM_GREEN);
- LED_Clear(LED_NUM_RED);
isUsbConnected = 1;
}
diff --git a/firmware/apps/dfu/main.c b/firmware/apps/dfu/main.c
index 32c855c..6cdad20 100644
--- a/firmware/apps/dfu/main.c
+++ b/firmware/apps/dfu/main.c
@@ -177,10 +177,9 @@
uint8_t isUsbConnected = 0;
unsigned int i = 0;
- LED_Configure(LED_NUM_RED);
- LED_Configure(LED_NUM_GREEN);
- LED_Set(LED_NUM_RED);
-
+ led_init();
+ led_blink(LED_GREEN, BLINK_3O_30F);
+ led_blink(LED_RED, BLINK_3O_30F);
/* Enable watchdog for 500ms, with no window */
WDT_Enable(WDT, WDT_MR_WDRSTEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT |
@@ -243,8 +242,6 @@
}
} else if (isUsbConnected == 0) {
TRACE_INFO("USB is now configured\n\r");
- LED_Set(LED_NUM_GREEN);
- LED_Clear(LED_NUM_RED);
isUsbConnected = 1;
}
diff --git a/firmware/libboard/common/include/board_common.h b/firmware/libboard/common/include/board_common.h
index 02b9e50..aeb0907 100644
--- a/firmware/libboard/common/include/board_common.h
+++ b/firmware/libboard/common/include/board_common.h
@@ -39,11 +39,11 @@
#define BOARD_MCK 48000000
-#define LED_RED PIO_PA17
-#define LED_GREEN PIO_PA18
+#define PIO_LED_RED PIO_PA17
+#define PIO_LED_GREEN PIO_PA17
-#define PIN_LED_RED {LED_RED, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
-#define PIN_LED_GREEN {LED_GREEN, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
+#define PIN_LED_RED {PIO_LED_RED, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
+#define PIN_LED_GREEN {PIO_LED_GREEN, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
#define PINS_LEDS PIN_LED_RED, PIN_LED_GREEN
#define LED_NUM_RED 0
diff --git a/firmware/libboard/common/include/led.h b/firmware/libboard/common/include/led.h
index 87e2fc9..d1db039 100644
--- a/firmware/libboard/common/include/led.h
+++ b/firmware/libboard/common/include/led.h
@@ -1,72 +1,28 @@
-/* ----------------------------------------------------------------------------
- * ATMEL Microcontroller Software Support
- * ----------------------------------------------------------------------------
- * Copyright (c) 2008, Atmel Corporation
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the disclaimer below.
- *
- * Atmel's name may not be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
- * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * ----------------------------------------------------------------------------
- */
+#pragma once
-/**
- * \file
- *
- * \section Purpose
- *
- * Small set of functions for simple and portable LED usage.
- *
- * \section Usage
- *
- * -# Configure one or more LEDs using LED_Configure and
- * LED_ConfigureAll.
- * -# Set, clear and toggle LEDs using LED_Set, LED_Clear and
- * LED_Toggle.
- *
- * LEDs are numbered starting from 0; the number of LEDs depend on the
- * board being used. All the functions defined here will compile properly
- * regardless of whether the LED is defined or not; they will simply
- * return 0 when a LED which does not exist is given as an argument.
- * Also, these functions take into account how each LED is connected on to
- * board; thus, \ref LED_Set might change the level on the corresponding pin
- * to 0 or 1, but it will always light the LED on; same thing for the other
- * methods.
- */
+enum led {
+ LED_RED,
+ LED_GREEN,
+ _NUM_LED
+};
-#ifndef _LED_
-#define _LED_
+enum led_pattern {
+ BLINK_ALWAYS_OFF = 0,
+ BLINK_ALWAYS_ON = 1,
+ BLINK_3O_5F = 2,
+ BLINK_3O_30F = 3,
+ BLINK_3O_1F_3O_30F = 4,
+ BLINK_3O_1F_3O_1F_3O_30F= 5,
+ BLINK_200O_F = 6,
+ BLINK_600O_F = 7,
+ BLINK_CUSTOM = 8,
+ _NUM_LED_BLINK
+};
-#include <stdint.h>
+void led_init(void);
+void led_fini(void);
+void led_stop(void);
+void led_start(void);
-//------------------------------------------------------------------------------
-// Global Functions
-//------------------------------------------------------------------------------
-
-extern uint32_t LED_Configure( uint32_t dwLed ) ;
-
-extern uint32_t LED_Set( uint32_t dwLed ) ;
-
-extern uint32_t LED_Clear( uint32_t dwLed ) ;
-
-extern uint32_t LED_Toggle( uint32_t dwLed ) ;
-
-#endif /* #ifndef LED_H */
-
+void led_blink(enum led led, enum led_pattern blink);
+enum led_pattern led_get(enum led led);
diff --git a/firmware/libboard/common/source/board_lowlevel.c b/firmware/libboard/common/source/board_lowlevel.c
index 5865d1d..98eb74d 100644
--- a/firmware/libboard/common/source/board_lowlevel.c
+++ b/firmware/libboard/common/source/board_lowlevel.c
@@ -127,9 +127,9 @@
SUPC_SMMR_SMRSTEN_ENABLE;
/* enable both LED and green LED */
- PIOA->PIO_PER |= LED_RED | LED_GREEN;
- PIOA->PIO_OER |= LED_RED | LED_GREEN;
- PIOA->PIO_CODR |= LED_RED | LED_GREEN;
+ PIOA->PIO_PER |= PIO_LED_RED | PIO_LED_GREEN;
+ PIOA->PIO_OER |= PIO_LED_RED | PIO_LED_GREEN;
+ PIOA->PIO_CODR |= PIO_LED_RED | PIO_LED_GREEN;
/* Set 3 FWS for Embedded Flash Access */
EFC->EEFC_FMR = EEFC_FMR_FWS(3);
@@ -169,7 +169,7 @@
#endif
/* disable the red LED after main clock initialization */
- PIOA->PIO_SODR = LED_RED;
+ PIOA->PIO_SODR = PIO_LED_RED;
/* "switch" to main clock as master clock source (should already be the case */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~(uint32_t)PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK;
diff --git a/firmware/libboard/common/source/led.c b/firmware/libboard/common/source/led.c
index 1a88e45..e4d2d57 100644
--- a/firmware/libboard/common/source/led.c
+++ b/firmware/libboard/common/source/led.c
@@ -1,168 +1,258 @@
-/* ----------------------------------------------------------------------------
- * ATMEL Microcontroller Software Support
- * ----------------------------------------------------------------------------
- * Copyright (c) 2008, Atmel Corporation
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the disclaimer below.
- *
- * Atmel's name may not be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
- * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * ----------------------------------------------------------------------------
- */
-
-/**
- * \file
- */
-
-/*------------------------------------------------------------------------------
- * Headers
- *------------------------------------------------------------------------------*/
-
-#include "board.h"
-
-/*------------------------------------------------------------------------------
- * Local Variables
- *------------------------------------------------------------------------------*/
-
-#ifdef PINS_LEDS
-static const Pin pinsLeds[] = { PINS_LEDS } ;
-static const uint32_t numLeds = PIO_LISTSIZE( pinsLeds ) ;
-#endif
-
-/*------------------------------------------------------------------------------
- * Global Functions
- *------------------------------------------------------------------------------*/
-
-/**
- * Configures the pin associated with the given LED number. If the LED does
- * not exist on the board, the function does nothing.
- * \param led Number of the LED to configure.
- * \return 1 if the LED exists and has been configured; otherwise 0.
- */
-extern uint32_t LED_Configure( uint32_t dwLed )
-{
-#ifdef PINS_LEDS
- // Check that LED exists
- if ( dwLed >= numLeds)
- {
-
- return 0;
- }
-
- // Configure LED
- return ( PIO_Configure( &pinsLeds[dwLed], 1 ) ) ;
-#else
- return 0 ;
-#endif
-}
-
-/**
- * Turns the given LED on if it exists; otherwise does nothing.
- * \param led Number of the LED to turn on.
- * \return 1 if the LED has been turned on; 0 otherwise.
- */
-extern uint32_t LED_Set( uint32_t dwLed )
-{
-#ifdef PINS_LEDS
- /* Check if LED exists */
- if ( dwLed >= numLeds )
- {
- return 0 ;
- }
-
- /* Turn LED on */
- if ( pinsLeds[dwLed].type == PIO_OUTPUT_0 )
- {
-
- PIO_Set( &pinsLeds[dwLed] ) ;
- }
- else
- {
- PIO_Clear( &pinsLeds[dwLed] ) ;
- }
-
- return 1 ;
-#else
- return 0 ;
-#endif
-}
-
-/**
- * Turns a LED off.
- *
- * \param led Number of the LED to turn off.
- * \return 1 if the LED has been turned off; 0 otherwise.
- */
-extern uint32_t LED_Clear( uint32_t dwLed )
-{
-#ifdef PINS_LEDS
- /* Check if LED exists */
- if ( dwLed >= numLeds )
- {
- return 0 ;
- }
-
- /* Turn LED off */
- if ( pinsLeds[dwLed].type == PIO_OUTPUT_0 )
- {
- PIO_Clear( &pinsLeds[dwLed] ) ;
- }
- else
- {
- PIO_Set( &pinsLeds[dwLed] ) ;
- }
-
- return 1 ;
-#else
- return 0 ;
-#endif
-}
-
-/**
- * Toggles the current state of a LED.
- *
- * \param led Number of the LED to toggle.
- * \return 1 if the LED has been toggled; otherwise 0.
- */
-extern uint32_t LED_Toggle( uint32_t dwLed )
-{
-#ifdef PINS_LEDS
- /* Check if LED exists */
- if ( dwLed >= numLeds )
- {
- return 0 ;
- }
-
- /* Toggle LED */
- if ( PIO_GetOutputDataStatus( &pinsLeds[dwLed] ) )
- {
- PIO_Clear( &pinsLeds[dwLed] ) ;
- }
- else
- {
- PIO_Set( &pinsLeds[dwLed] ) ;
- }
-
- return 1 ;
-#else
- return 0 ;
-#endif
-}
-
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include <osmocom/core/timer.h>
+
+#include "board.h"
+#include "utils.h"
+#include "led.h"
+
+#ifdef PINS_LEDS
+static const Pin pinsLeds[] = { PINS_LEDS } ;
+
+static void led_set(enum led led, int on)
+{
+ ASSERT(led < PIO_LISTSIZE(pinsLeds));
+
+ if (on)
+ PIO_Set(&pinsLeds[led]);
+ else
+ PIO_Clear(&pinsLeds[led]);
+}
+
+/* LED blinking code */
+
+/* a single state in a sequence of blinking */
+struct blink_state {
+ /* duration of the state in ms */
+ uint16_t duration;
+ /* bringhtness of LED during the state */
+ uint8_t on;
+} __attribute__((packed));
+
+static const struct blink_state bs_off[] = {
+ { 0, 0 }
+};
+
+static const struct blink_state bs_on[] = {
+ { 0, 1 }
+};
+
+static const struct blink_state bs_3on_5off[] = {
+ { 300, 1 }, { 500, 0 }
+};
+
+static const struct blink_state bs_3on_30off[] = {
+ { 300, 1 }, { 3000, 0 }
+};
+
+static const struct blink_state bs_3on_1off_3on_30off[] = {
+ { 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 }
+};
+
+static const struct blink_state bs_3on_1off_3on_1off_3on_30off[] = {
+ { 300, 1 }, { 100, 0 }, { 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 }
+};
+static const struct blink_state bs_200on_off[] = {
+ { 20000, 1 }, { 0, 0 },
+};
+static const struct blink_state bs_600on_off[] = {
+ { 60000, 1 }, { 0, 0 },
+};
+
+
+/* a blink pattern is an array of blink_states */
+struct blink_pattern {
+ const struct blink_state *states;
+ uint16_t size;
+};
+
+/* compiled-in default blinking patterns */
+static const struct blink_pattern patterns[] = {
+ [BLINK_ALWAYS_OFF] = {
+ .states = bs_off,
+ .size = ARRAY_SIZE(bs_off),
+ },
+ [BLINK_ALWAYS_ON] = {
+ .states = bs_on,
+ .size = ARRAY_SIZE(bs_on),
+ },
+ [BLINK_3O_5F] = {
+ .states = bs_3on_5off,
+ .size = ARRAY_SIZE(bs_3on_5off),
+ },
+ [BLINK_3O_30F] = {
+ .states = bs_3on_30off,
+ .size = ARRAY_SIZE(bs_3on_30off),
+ },
+ [BLINK_3O_1F_3O_30F] = {
+ .states = bs_3on_1off_3on_30off,
+ .size = ARRAY_SIZE(bs_3on_1off_3on_30off),
+ },
+ [BLINK_3O_1F_3O_1F_3O_30F] = {
+ .states = bs_3on_1off_3on_1off_3on_30off,
+ .size = ARRAY_SIZE(bs_3on_1off_3on_1off_3on_30off),
+ },
+ [BLINK_200O_F] = {
+ .states = bs_200on_off,
+ .size = ARRAY_SIZE(bs_200on_off),
+ },
+ [BLINK_600O_F] = {
+ .states = bs_600on_off,
+ .size = ARRAY_SIZE(bs_600on_off),
+ },
+};
+
+struct led_state {
+ /* which led are we handling */
+ enum led led;
+
+ /* timer */
+ struct osmo_timer_list timer;
+
+ /* pointer and size of blink array */
+ const struct blink_pattern *pattern;
+
+ unsigned int cur_state;
+ unsigned int illuminated;
+
+ /* static allocated space for custom blinking pattern */
+ struct blink_pattern pattern_cust;
+ struct blink_state blink_cust[10];
+};
+
+static unsigned int cur_state_inc(struct led_state *ls)
+{
+ ls->cur_state = (ls->cur_state + 1) % ls->pattern->size;
+ return ls->cur_state;
+}
+
+static const struct blink_state *
+next_blink_state(struct led_state *ls)
+{
+ return &ls->pattern->states[cur_state_inc(ls)];
+}
+
+/* apply the next state to the LED */
+static void apply_blinkstate(struct led_state *ls,
+ const struct blink_state *bs)
+{
+ led_set(ls->led, bs->on);
+ ls->illuminated = bs->on;
+
+ /* re-schedule the timer */
+ if (bs->duration) {
+ uint32_t us = bs->duration * 1000;
+ osmo_timer_schedule(&ls->timer, us / 1000000, us % 1000000);
+ }
+}
+
+static void blink_tmr_cb(void *data)
+{
+ struct led_state *ls = data;
+ const struct blink_state *next_bs = next_blink_state(ls);
+
+ /* apply the next state to the LED */
+ apply_blinkstate(ls, next_bs);
+}
+
+static struct led_state led_state[] = {
+ [LED_GREEN] = {
+ .led = LED_GREEN,
+ .timer.cb = blink_tmr_cb,
+ .timer.data = &led_state[LED_GREEN],
+ },
+ [LED_RED] = {
+ .led = LED_RED,
+ .timer.cb = blink_tmr_cb,
+ .timer.data = &led_state[LED_RED],
+ },
+};
+#endif /* PINS_LEDS */
+
+void led_blink(enum led led, enum led_pattern blink)
+{
+#ifdef PINS_LEDS
+ struct led_state *ls;
+
+ if (led >= ARRAY_SIZE(led_state))
+ return;
+ ls = &led_state[led];
+
+ /* stop previous blinking, if any */
+ osmo_timer_del(&ls->timer);
+ led_set(led, 0);
+ ls->illuminated = 0;
+ ls->pattern = NULL;
+ ls->cur_state = 0;
+
+ switch (blink) {
+ case BLINK_CUSTOM:
+ ls->pattern = &ls->pattern_cust;
+ break;
+ default:
+ if (blink >= ARRAY_SIZE(patterns))
+ return;
+ ls->pattern = &patterns[blink];
+ break;
+ }
+
+ if (ls->pattern && ls->pattern->size > 0)
+ apply_blinkstate(ls, &ls->pattern->states[0]);
+#endif
+}
+
+enum led_pattern led_get(enum led led)
+{
+#ifdef PINS_LEDS
+ struct led_state *ls;
+ unsigned int i;
+
+ if (led >= ARRAY_SIZE(led_state))
+ return -1;
+ ls = &led_state[led];
+
+ if (ls->pattern == &ls->pattern_cust)
+ return BLINK_CUSTOM;
+
+ for (i = 0; i < ARRAY_SIZE(patterns); i++) {
+ if (ls->pattern == &patterns[i])
+ return i;
+ }
+#endif
+ /* default case, shouldn't be reached */
+ return -1;
+}
+
+void led_start(void)
+{
+ led_set(LED_GREEN, led_state[LED_GREEN].illuminated);
+ led_set(LED_RED, led_state[LED_RED].illuminated);
+}
+
+void led_stop(void)
+{
+ led_set(LED_GREEN, 0);
+ led_set(LED_RED, 0);
+}
+
+void led_init(void)
+{
+#ifdef PINS_LEDS
+ PIO_Configure(pinsLeds, PIO_LISTSIZE(pinsLeds));
+ led_set(LED_GREEN, 0);
+ led_set(LED_RED, 0);
+#endif
+}
+
+void led_fini(void)
+{
+#ifdef PINS_LEDS
+ /* we don't actually need to do this, but just in case... */
+ osmo_timer_del(&led_state[LED_RED].timer);
+ osmo_timer_del(&led_state[LED_GREEN].timer);
+ led_set(LED_GREEN, 0);
+ led_set(LED_RED, 0);
+#endif
+}