#include "app/led/led.h" #include "core/csect.h" #include "core/main.h" #include "core/config.h" #include "core/config_pins.h" #include "app/rseq/rseq.h" //#define LED_LOWBAT_FLARE_INTERVAL 5000 //#define LED_LOWBAT_FLARE_TIME 10 //#define LED_POLARITY_INVERTED 0 #define LED_LOWBAT_FLARE_INTERVAL CONFIG_LED_LOWBAT_FLASH_INTERVAL #define LED_LOWBAT_FLARE_TIME CONFIG_LED_LOWBAT_FLASH_TIME #define LED_POLARITY_INVERTED CONFIG_LED_POLARITY_INVERTED #define LED_OKBAT_FLARE_ENABLE CONFIG_LED_OKBAT_FLASH_ENABLE #define LED_OKBAT_FLARE_INTERVAL CONFIG_LED_OKBAT_FLASH_INTERVAL #define LED_OKBAT_FLARE_TIME CONFIG_LED_OKBAT_FLASH_TIME #define LED_SIGNALING_PERIOD CONFIG_LED_SIGNALING_PERIOD #define LED_SIGNALING_TIME CONFIG_LED_SIGNALING_TIME #define LED_AUTOMATALARM_COMBINED 0 static volatile eLedMode_t g_LedMode = eLedMode_Idle; static volatile bool g_bHarmUpEnable = false; static volatile uint32_t g_nHarmUpTimestamp = 0ul; // ----------- #pragma pack(push, 1) typedef struct { struct { uint32_t gp_timer_counter; uint32_t gp_timer_maximum; uint32_t gp_counter_signaling; } counters; struct { bool bLowBatterySignal; bool bUSBActiveSignal; bool bSignalStage; // false during flashing, true during waiting next flash bool bAutomatAlarm; bool bHarmupComplete; // false during harmup interval since startup, the it becomes true } state; } sData_t; #pragma pack(pop) // ----------- static bool seqirq_state_startup( void * arg ); static bool seqirq_state_signaling( void * arg ); static bool seqirq_state_signaling_rollover( void * arg ); static bool seqirq_state_timer_nextstate( void * arg ); #if LED_OKBAT_FLARE_ENABLE == 0 static bool seqirq_state_check_lowbat( void * arg ); #endif #if 0 // useless static bool seqirq_state_timer_prevstate( void * arg ); static bool seqirq_state_lowbat_serve( void * arg ); static bool seqirq_state_check_usbactive( void * arg ); static bool seqirq_state_update_usbactive( void * arg ); #endif static bool seqirq_state_update_ledstatus( void * arg ); // ----------- static fRoutine_t * rseq_timer_list[ 5 ]; static sRoutineSequence_t rseq_timer; static sData_t g_sData; static bool IFace_Led_Init(); static void IFace_Led_Tick(); static bool IFace_Led_SetMode( eLedMode_t mode ); static void IFace_Led_SetLowBattery( bool batteryLow ); static void IFace_Led_SetUSBConnectivity( bool usbActive ); static void IFace_Led_SetAutomatAlarm( bool automatAlarm ); static void IFace_Led_SetHarmupStatus( bool harmupCompleted ); static bool IFace_Led_DeInit(); static void harmup_init(); static void harmup_serve(); static void harmup_stop(); const sLED_Handle_t LEDHandle = { .Init = IFace_Led_Init, .Tick = IFace_Led_Tick, .SetMode = IFace_Led_SetMode, .SetLowBattery = IFace_Led_SetLowBattery, .SetUSBConnectivity = IFace_Led_SetUSBConnectivity, .SetAutomatAlarm = IFace_Led_SetAutomatAlarm, .SetHarmupStatus = IFace_Led_SetHarmupStatus, .DeInit = IFace_Led_DeInit, .harmup_init = harmup_init, .harmup_serve = harmup_serve, .harmup_stop = harmup_stop }; static inline bool seqTake( void * arg ) { DI(); return true; } static inline bool seqFree( void * arg ) { EI(); return true; } //#if CONFIG_HARMUP_INTERVAL > 0 static void harmup_init() { g_bHarmUpEnable = true; g_nHarmUpTimestamp = HAL_GetTick(); LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); } static void harmup_serve() { if( g_bHarmUpEnable ) { if( (HAL_GetTick() - g_nHarmUpTimestamp)/1000 > (CONFIG_HARMUP_INTERVAL) ) { g_bHarmUpEnable = false; LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); } } } static void harmup_stop() { g_bHarmUpEnable = false; LEDHandle.SetHarmupStatus( !g_bHarmUpEnable ); } //#endif void ledGreen( bool state ) { #if LED_POLARITY_INVERTED HAL_GPIO_WritePin( CONFIG_PORT__LED_GREEN, CONFIG_PIN__LED_GREEN, (state)?GPIO_PIN_RESET:GPIO_PIN_SET ); #else HAL_GPIO_WritePin( CONFIG_PORT__LED_GREEN, CONFIG_PIN__LED_GREEN, (state)?GPIO_PIN_SET:GPIO_PIN_RESET ); #endif } void ledRed( bool state ) { #if LED_POLARITY_INVERTED HAL_GPIO_WritePin( CONFIG_PORT__LED_RED, CONFIG_PIN__LED_RED, (state)?GPIO_PIN_RESET:GPIO_PIN_SET ); #else HAL_GPIO_WritePin( CONFIG_PORT__LED_RED, CONFIG_PIN__LED_RED, (state)?GPIO_PIN_SET:GPIO_PIN_RESET ); #endif } void setLedColor( eLed_color_t color ) { switch ( color ) { case eLed_color_off: { ledRed(false); ledGreen(false); } break; case eLed_color_red: { ledRed(true); ledGreen(false); } break; case eLed_color_grn: { ledRed(false); ledGreen(true); } break; case eLed_color_orange: { ledRed(true); ledGreen(true); } break; } } static bool IFace_Led_Init() { ledGreen( false ); ledRed( false ); #if LED_POLARITY_INVERTED { GPIO_InitTypeDef GPIO_InitStruct = {0}; // Configure pin: OpenDrain with no pulls GPIO_InitStruct.Pin = CONFIG_PIN__LED_RED; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(CONFIG_PORT__LED_RED, &GPIO_InitStruct); } { GPIO_InitTypeDef GPIO_InitStruct = {0}; // Configure pin: OpenDrain with no pulls GPIO_InitStruct.Pin = CONFIG_PIN__LED_GREEN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(CONFIG_PORT__LED_GREEN, &GPIO_InitStruct); } #else { GPIO_InitTypeDef GPIO_InitStruct = {0}; // Configure pin: OpenDrain with no pulls GPIO_InitStruct.Pin = CONFIG_PIN__LED_RED; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(CONFIG_PORT__LED_RED, &GPIO_InitStruct); } { GPIO_InitTypeDef GPIO_InitStruct = {0}; // Configure pin: OpenDrain with no pulls GPIO_InitStruct.Pin = CONFIG_PIN__LED_GREEN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; HAL_GPIO_Init(CONFIG_PORT__LED_GREEN, &GPIO_InitStruct); } #endif ledRed(false); ledGreen(false); seqTake(&g_sData); g_sData.counters.gp_counter_signaling = 0; g_sData.counters.gp_timer_counter = 0; g_sData.counters.gp_timer_maximum = 0; g_sData.state.bLowBatterySignal = false; g_sData.state.bSignalStage = false; g_sData.state.bUSBActiveSignal = false; g_sData.state.bAutomatAlarm = false; g_sData.state.bHarmupComplete = false; seqFree(&g_sData); rsa_sequence_init( &rseq_timer, &g_sData, rseq_timer_list, sizeof(rseq_timer_list)/sizeof(*rseq_timer_list) ); rsa_sequence_setlockers( &rseq_timer, seqTake, seqFree ); IFace_Led_SetMode( eLedMode_Signaling ); rsa_sequence_insert_routine( &rseq_timer, &seqirq_state_startup ); return true; } eLed_color_t GET_LED_COLOR_STATE( int number ) { eLed_color_t led_color = eLed_color_off; // switch (number) // { // case LED1: // { // led_color |= HAL_GPIO_ReadPin( CONFIG_PORT_28LED_RED, CONFIG_PIN_28LED_RED ) == GPIO_PIN_RESET ? eLed_color_red : eLed_color_off; // led_color |= HAL_GPIO_ReadPin( CONFIG_PORT_28LED_GREEN, CONFIG_PIN_28LED_GREEN) == GPIO_PIN_RESET ? eLed_color_grn : eLed_color_off; // }break; // case LED2: // { // led_color |= HAL_GPIO_ReadPin( CONFIG_PORT__LED_GREEN, CONFIG_PIN__LED_GREEN)== GPIO_PIN_RESET ? eLed_color_grn : eLed_color_off; // led_color |= HAL_GPIO_ReadPin( CONFIG_PORT__LED_RED, CONFIG_PIN__LED_RED ) == GPIO_PIN_RESET ? eLed_color_red : eLed_color_off; // }break; // } return led_color; } static void IFace_Led_Tick() { if( rsa_sequence_icall( &rseq_timer ) ) { rsa_sequence_ireset( &rseq_timer ); } } static bool IFace_Led_SetMode( eLedMode_t mode ) { bool bSet = true; switch( mode ) { case eLedMode_Idle: { rsa_sequence_clear( &rseq_timer ); ledGreen( false ); ledRed( false ); } break; case eLedMode_Signaling: { seqTake(&g_sData); rsa_sequence_iclear( &rseq_timer ); rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_signaling ); rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_timer_nextstate ); rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_signaling_rollover ); g_sData.counters.gp_counter_signaling = 1 + (LED_SIGNALING_TIME / LED_SIGNALING_PERIOD); g_sData.state.bSignalStage = false; seqFree(&g_sData); ledGreen( false ); ledRed( false ); } break; case eLedMode_Normal: { rsa_sequence_clear( &rseq_timer ); rsa_sequence_insert_routine( &rseq_timer, &seqirq_state_startup ); } break; default: bSet = false; } if( bSet ) { __DI__ g_LedMode = mode; __EI__ } return bSet; } static void IFace_Led_SetLowBattery( bool batteryLow ) { seqTake(&g_sData); if( g_sData.state.bLowBatterySignal ^ batteryLow ) { rsa_sequence_ireset( &rseq_timer ); // make @seqirq_state_update_usbactive to be called g_sData.state.bLowBatterySignal = batteryLow; g_sData.state.bSignalStage = false; } seqFree(&g_sData); } static void IFace_Led_SetAutomatAlarm( bool automatAlarm ) { seqTake(&g_sData); if( g_sData.state.bAutomatAlarm ^ automatAlarm ) { rsa_sequence_ireset( &rseq_timer ); // make @seqirq_state_update_usbactive to be called g_sData.state.bAutomatAlarm = automatAlarm; g_sData.state.bSignalStage = false; } seqFree(&g_sData); } static void IFace_Led_SetUSBConnectivity( bool usbActive ) { seqTake(&g_sData); if( g_sData.state.bUSBActiveSignal ^ usbActive ) { rsa_sequence_ireset( &rseq_timer ); // make @seqirq_state_update_usbactive to be called g_sData.state.bUSBActiveSignal = usbActive; } seqFree(&g_sData); } static void IFace_Led_SetHarmupStatus( bool harmupCompleted ) { seqTake(&g_sData); if( g_sData.state.bHarmupComplete ^ harmupCompleted ) { rsa_sequence_ireset( &rseq_timer ); // make @seqirq_state_update_usbactive to be called g_sData.state.bHarmupComplete = harmupCompleted; } seqFree(&g_sData); } static bool IFace_Led_DeInit() { seqTake(&g_sData); rsa_sequence_iclear( &rseq_timer ); seqFree(&g_sData); ledGreen( false ); ledRed( false ); { GPIO_InitTypeDef GPIO_InitStruct = {0}; // Configure the pin muxing GPIO_InitStruct.Pin = CONFIG_PIN__LED_RED; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(CONFIG_PORT__LED_RED, &GPIO_InitStruct); } { GPIO_InitTypeDef GPIO_InitStruct = {0}; // Configure the pin muxing GPIO_InitStruct.Pin = CONFIG_PIN__LED_GREEN; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(CONFIG_PORT__LED_GREEN, &GPIO_InitStruct); } return true; } //------------------------------------------------------------------------------ // {IFace_Led_SetMode} => [seqirq_state_startup] /* IRQ */static bool seqirq_state_startup( void * arg ) { sData_t * pData = (sData_t*)(arg); ledGreen( false ); ledRed( false ); rsa_sequence_iclear( &rseq_timer ); // Note: seqirq_state_update_ledstatus is responsible for LED indicator control. // LED Indicator algorythm: see @seqirq_state_update_ledstatus routine for details rsa_sequence_iinsert_routine( &rseq_timer, &seqirq_state_update_ledstatus ); // never ends (void)pData; return false; // return false to prevent calling next routine } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // {IFace_Led_SetMode} => [seqirq_state_signaling] /* IRQ */static bool seqirq_state_signaling( void * arg ) { sData_t * pData = (sData_t*)(arg); ledGreen( pData->state.bSignalStage ); ledRed( !pData->state.bSignalStage ); pData->counters.gp_timer_counter = 0; pData->counters.gp_timer_maximum = LED_SIGNALING_PERIOD; return true; // go to the timer routine } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // {IFace_Led_SetMode} => [seqirq_state_signaling_rollover] /* IRQ */static bool seqirq_state_signaling_rollover( void * arg ) { sData_t * pData = (sData_t*)(arg); if( pData->counters.gp_counter_signaling > 0 ) { pData->counters.gp_counter_signaling --; } if( pData->counters.gp_counter_signaling > 0 ) { rsa_sequence_ireset( &rseq_timer ); pData->state.bSignalStage = !pData->state.bSignalStage; } else { IFace_Led_SetMode( eLedMode_Normal ); } return false; // go to the timer routine } //------------------------------------------------------------------------------ #if 0 // useless // {seqirq_state_startup => seqirq_state_update_usbactive} /* IRQ */ static bool seqirq_state_update_usbactive( void * arg ) { sData_t * pData = (sData_t*)(arg); #if LED_AUTOMATALARM_COMBINED == 0 // Single mode: check for USB-active first if( pData->state.bUSBActiveSignal ) { // If USB-active is asserted: ledRed( false ); ledGreen( true ); // Show GREEN indicator } else // Then check for Alarm if( pData->state.bAutomatAlarm ) { // If Automat Alarm is asserted: ledRed( true ); // Show RED indicator ledGreen( false ); } else { ledRed( false ); // Blow both indicators ledGreen( false ); } #else // Combined mode: check for USB-active flag only. // Check if USB-Active signal is asserted and update USB-activity LED ledGreen( pData->state.bUSBActiveSignal ); #endif return true; // let the next routine to be called } #endif //------------------------------------------------------------------------------ // {seqirq_state_startup => seqirq_state_update_usbactive} /* IRQ */ static bool seqirq_state_update_ledstatus( void * arg ) { sData_t * pData = (sData_t*)(arg); if( //pData->state.bLowBatterySignal // LOW BATTERY //|| //pData->state.bAutomatAlarm // AUTOMAT SIGNAL ALARM //|| (!pData->state.bHarmupComplete) // DEVICE IS STILL HARMING ) { ledRed( true ); // SHOW RED INDICATOR ledGreen( false ); } else { ledRed( false ); ledGreen( true ); // SHOW GREEN INDICATOR } return false; // false: never leave this routine } //------------------------------------------------------------------------------ #if 0 // useless // {seqirq_state_startup => seqirq_state_update_usbactive => seqirq_state_check_usbactive} /* IRQ */ static bool seqirq_state_check_usbactive( void * arg ) { sData_t * pData = (sData_t*)(arg); #if LED_AUTOMATALARM_COMBINED == 0 // Single mode: show only one indicator - either USB activity, or Alarm, or battery mode // Check if USB-Active or Automat Alarm signals are asserted if( pData->state.bUSBActiveSignal || pData->state.bAutomatAlarm ) { // If asserted, return false to prevent calling next routine return false; } #else // Combined mode: wait for USB disconnection to enter combined indication mode // Check if USB-Active signal is asserted if( pData->state.bUSBActiveSignal ) { // If asserted, return false to prevent calling next routine return false; } #endif return true; // let the next routine to be called } #endif //------------------------------------------------------------------------------ #if LED_OKBAT_FLARE_ENABLE == 0 // {seqirq_state_startup => seqirq_state_update_usbactive => seqirq_state_check_usbactive => seqirq_state_check_lowbat} /* IRQ */ static bool seqirq_state_check_lowbat( void * arg ) { sData_t * pData = (sData_t*)(arg); // If LowBattery signal is not asserted, stay in this routine // If LowBattery signal is asserted, @bLowBatterySignal is true, so // returning 'true' causes the next routine to be called. return pData->state.bLowBatterySignal; } #endif //------------------------------------------------------------------------------ #if 0 // useless // {seqirq_state_startup => seqirq_state_update_usbactive => seqirq_state_check_usbactive => seqirq_state_check_lowbat => seqirq_state_lowbat_serve} /* IRQ */ static bool seqirq_state_lowbat_serve( void * arg ) { sData_t * pData = (sData_t*)(arg); #if LED_OKBAT_FLARE_ENABLE == 0 if( ! pData->state.bLowBatterySignal ) { // the program should not reach this point if works correctly return false; // stay in this routine } #endif if( ! pData->state.bSignalStage ) { #if LED_OKBAT_FLARE_ENABLE == 0 pData->counters.gp_timer_counter = 0; pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_TIME; pData->state.bSignalStage = true; ledRed( true ); // Turn RED LED ON #else if( pData->state.bLowBatterySignal ) { pData->counters.gp_timer_counter = 0; pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_TIME; pData->state.bSignalStage = true; #if LED_AUTOMATALARM_COMBINED ledRed( !pData->state.bAutomatAlarm ); // Turn RED LED ON #else ledRed( true ); // Turn RED LED ON #endif } else { pData->counters.gp_timer_counter = 0; #if LED_AUTOMATALARM_COMBINED pData->counters.gp_timer_maximum = ((pData->state.bAutomatAlarm)?3:1)*LED_OKBAT_FLARE_TIME; #else pData->counters.gp_timer_maximum = LED_OKBAT_FLARE_TIME; #endif pData->state.bSignalStage = true; #if LED_AUTOMATALARM_COMBINED ledRed( false ); #endif ledGreen( true ); // Turn GREEN LED ON } #endif } else { #if LED_OKBAT_FLARE_ENABLE == 0 pData->counters.gp_timer_counter = 0; pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_INTERVAL; pData->state.bSignalStage = false; ledRed( false ); // Turn RED LED OFF, and wait for next flash #else if( pData->state.bLowBatterySignal ) { pData->counters.gp_timer_counter = 0; pData->counters.gp_timer_maximum = LED_LOWBAT_FLARE_INTERVAL; pData->state.bSignalStage = false; #if LED_AUTOMATALARM_COMBINED ledRed( pData->state.bAutomatAlarm ); // Turn RED LED OFF, and wait for next flash #else ledRed( false ); // Turn RED LED OFF, and wait for next flash #endif } else { pData->counters.gp_timer_counter = 0; pData->counters.gp_timer_maximum = LED_OKBAT_FLARE_INTERVAL; pData->state.bSignalStage = false; #if LED_AUTOMATALARM_COMBINED ledRed( pData->state.bAutomatAlarm ); #endif ledGreen( false ); // Turn GREEN LED OFF, and wait for next flash } #endif } return true; // let the next routine to be called } #endif //------------------------------------------------------------------------------ // {seqirq_state_timer_nextstate} /* IRQ */ static bool seqirq_state_timer_nextstate( void * arg ) { sData_t * pData = (sData_t*)(arg); if( pData->counters.gp_timer_counter < pData->counters.gp_timer_maximum ) { pData->counters.gp_timer_counter++; return false; } // Useless: // rsa_sequence_iskip_routine( &rseq_timer ); // next timer routine // It is enough to return 'true' return true; } //------------------------------------------------------------------------------ #if 0 // useless // {seqirq_state_timer_prevstate} /* IRQ */ static bool seqirq_state_timer_prevstate( void * arg ) { sData_t * pData = (sData_t*)(arg); if( pData->counters.gp_timer_counter < pData->counters.gp_timer_maximum ) { pData->counters.gp_timer_counter++; return false; } rsa_sequence_iback_routine( &rseq_timer ); // prev timer routine return false; // need to return 'false' to avoid calling next routine } #endif