diff --git a/firmware/apps/cardem/main.c b/firmware/apps/cardem/main.c index 7402ab77..7d90eb0d 100644 --- a/firmware/apps/cardem/main.c +++ b/firmware/apps/cardem/main.c @@ -121,9 +121,8 @@ extern int main(void) 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 @@ extern int main(void) } } 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 32c855c6..6cdad208 100644 --- a/firmware/apps/dfu/main.c +++ b/firmware/apps/dfu/main.c @@ -177,10 +177,9 @@ extern int main(void) 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 @@ extern int main(void) } } 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 02b9e502..aeb09079 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 87e2fc97..d1db0393 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 - -//------------------------------------------------------------------------------ -// 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_init(void); +void led_fini(void); +void led_stop(void); +void led_start(void); +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 5865d1dd..98eb74d8 100644 --- a/firmware/libboard/common/source/board_lowlevel.c +++ b/firmware/libboard/common/source/board_lowlevel.c @@ -127,9 +127,9 @@ extern WEAK void LowLevelInit( void ) 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 @@ extern WEAK void LowLevelInit( void ) #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 1a88e45b..e4d2d578 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 +#include +#include + +#include + +#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 +}