#include "head_task.h" // FreeRTOS includes #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "semphr.h" #include "ltimers.h" #include "indicate_modes_task.h" #include "nixie_driver_process.h" #include "button_handler.h" #include "stm32f0xx_rtc.h" // Номера функций меню typedef enum { MENU_FUNC_SIMPLE_TIME_VIEW = 0, MENU_ADJ_TIME, MAX_MENU_FUNCTIONS } MenuFunctionsNums_t; // Состояния режима меню AdjTime typedef enum { STATE_MENU_ADJ_TIME_START, STATE_MENU_ADJ_TIME_TIMER_OUT } MenuAdjTimeStates_t; static MenuFunctionsNums_t g_curr_menu_func = MENU_FUNC_SIMPLE_TIME_VIEW; // Прототипы функций меню static void MenuFunc_SimpleTimeView ( void ); static void MenuFunc_AdjTime ( void ); // Массив указателей на функции-меню typedef void ( *MenuFunctions_t ) ( void ); static const MenuFunctions_t MenuFunctions[MAX_MENU_FUNCTIONS] = { MenuFunc_SimpleTimeView, MenuFunc_AdjTime }; static void GetCurrTime ( DataToIndicate_t* indic_data ); static void SetTime ( const DataToIndicate_t* indic_data ); static void AdjTimeChangeTube ( ButtonCombName_t but_comb_name, uint8_t *position ); static void AdjTimeChangeValue ( ButtonCombName_t but_comb_name, IndicModesMsgBlink_t* data, uint8_t position_mask ); static void ChangeValue ( uint8_t increase, uint8_t *data, uint8_t limit ); static DataToIndicate_t g_indic_data; extern QueueHandle_t queue_data_to_indic; // indicate_modes_task.c extern QueueHandle_t queue_but_comb; // button_handler.c extern QueueHandle_t queue_data_to_blink_mode; QueueHandle_t queue_switch_indic_mode; static MenuAdjTimeStates_t state_adj_time = STATE_MENU_ADJ_TIME_START; // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- void HeadTaskInit ( void ) { queue_switch_indic_mode = xQueueCreate ( 1, sizeof (IndicModesNums_t) ); configASSERT( queue_switch_indic_mode ); g_curr_menu_func = MENU_FUNC_SIMPLE_TIME_VIEW; } // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- void MenuFunc_SimpleTimeView ( void ) { RTC_TimeTypeDef RTC_TimeStructure; /* Get the current Time */ RTC_GetTime(RTC_Format_BCD, &RTC_TimeStructure); GetCurrTime ( &g_indic_data ); xQueueSend ( queue_data_to_indic, &g_indic_data, 0 ); } // ---------------------------------------------------------------------------- // Ф-я-меню настройки времени // ---------------------------------------------------------------------------- static void MenuFunc_AdjTime ( void ) { static const uint32_t TIME_ADJ_OUT = 5000; //ms static IndicModesMsgBlink_t blink_data_struct; static ButtonCombName_t but_comb_name; IndicModesNums_t indic_mode_num; static uint8_t position_mask; // - при этом нужно постоянно слать текущее время на вывод, то есть // при настройке времени оно тикает как в обычном режиме, только // пользователь может его изменять при этом switch ( state_adj_time ) { case STATE_MENU_ADJ_TIME_START: indic_mode_num = INDIC_MODE_BLINK; // Отправляем сообщение на переключение режима индикации xQueueSend ( queue_switch_indic_mode, &indic_mode_num, 0 ); // Отправляем сообщение с данными для индикации // Но сначала, сотрем, вдруг прошлое собщение не было прочитано xQueueReceive ( queue_data_to_blink_mode, &blink_data_struct, 0 ); GetCurrTime ( &blink_data_struct.data ); blink_data_struct.mask_byte = 32; position_mask = blink_data_struct.mask_byte; xQueueSend ( queue_data_to_blink_mode, &blink_data_struct, 0 ); state_adj_time = STATE_MENU_ADJ_TIME_TIMER_OUT; StartLTimer ( LTIMER_MENU_ADJ_TIME_OUT ); break; case STATE_MENU_ADJ_TIME_TIMER_OUT: if ( GetLTimer (LTIMER_MENU_ADJ_TIME_OUT) >= TIME_ADJ_OUT ) { state_adj_time = STATE_MENU_ADJ_TIME_START; // Выходим из этого меню режима g_curr_menu_func = MENU_FUNC_SIMPLE_TIME_VIEW; indic_mode_num = INDIC_MODE_STANDART; xQueueSend ( queue_switch_indic_mode, &indic_mode_num, 0 ); // При выходе из настройки времени нужно послать сообщ. на обновление // данных в простой режим, т.к. фаза в моргающем режиме могла быть // отрицательной, а данные обновляются только раз в секунду, и поэтому // лампа может быть потухшей 1 секунду. // - тут лучше сделать завершение фазы моргания GetCurrTime ( &g_indic_data ); xQueueSend ( queue_data_to_indic, &g_indic_data, 0 ); } else { // Если жмакнули кнопушку, то сбрасываем таймер, затем смотрим // какую кнопушку жмакнули if ( pdPASS == xQueueReceive ( queue_but_comb, &but_comb_name, 0 ) ) { StartLTimer ( LTIMER_MENU_ADJ_TIME_OUT ); // Тут в зависимости от того, какую кнопушку жмакнули switch ( but_comb_name ) { case BUTTON_SINGLE_FORWARD: case BUTTON_SINGLE_BACKWARD: // Меняем позицию времени AdjTimeChangeTube (but_comb_name, &position_mask); GetCurrTime ( &blink_data_struct.data ); blink_data_struct.mask_byte = position_mask; xQueueSend ( queue_data_to_blink_mode, &blink_data_struct, 0 ); break; case BUTTON_HOLD_FORWARD: case BUTTON_HOLD_BACKWARD: // Меняем значение времени GetCurrTime ( &blink_data_struct.data ); AdjTimeChangeValue ( but_comb_name, &blink_data_struct, position_mask ); SetTime ( &blink_data_struct.data ); xQueueSend ( queue_data_to_blink_mode, &blink_data_struct, 0 ); break; default: break; } } else { // - тут, если время изменилось, то отправляем его на вывод GetCurrTime ( &blink_data_struct.data ); xQueueSend ( queue_data_to_blink_mode, &blink_data_struct, 0 ); } } break; default: break; } } // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- static void ProcessFSM_Head ( void ) { // - тут можно совершать внешние переключения функций меню: // - ловить сообщения от внешних источников для перехода в нужный пункт // меню (сообщения от gps модуля, wifi и тд.) // 1. Ловим сообщение из модуля-обработчика нажатий кнопушек с командой // на переход в меню настройки (две кнопки зажать на 3 сек) // - перед отправкой сообщения в режим индикации сначала стираем сообщение static ButtonCombName_t but_comb_name; if ( pdPASS == xQueuePeek ( queue_but_comb, &but_comb_name, 0 ) ) { if ( but_comb_name == BUTTON_LONG ) { // - при изменении состояния меню или режима, нужно также сбрасывать // внутреннее состояние текущего режима xQueueReceive ( queue_but_comb, &but_comb_name, 0 ); g_curr_menu_func = MENU_ADJ_TIME; } } MenuFunctions [g_curr_menu_func](); taskYIELD(); } // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- static void GetCurrTime ( DataToIndicate_t *indic_data ) { RTC_TimeTypeDef RTC_TimeStructure; /* Get the current Time */ RTC_GetTime ( RTC_Format_BCD, &RTC_TimeStructure ); indic_data->indic_1 = RTC_TimeStructure.RTC_Hours>>4; indic_data->indic_2 = RTC_TimeStructure.RTC_Hours&0x0F; indic_data->indic_3 = RTC_TimeStructure.RTC_Minutes>>4; indic_data->indic_4 = RTC_TimeStructure.RTC_Minutes&0x0F; indic_data->indic_5 = RTC_TimeStructure.RTC_Seconds>>4; indic_data->indic_6 = RTC_TimeStructure.RTC_Seconds&0x0F; } // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- static void SetTime ( const DataToIndicate_t* indic_data ) { RTC_TimeTypeDef RTC_TimeStructure; RTC_TimeStructure.RTC_Hours = indic_data->indic_1 << 4; RTC_TimeStructure.RTC_Hours |= indic_data->indic_2 & 0x0F; RTC_TimeStructure.RTC_Minutes = indic_data->indic_3 << 4; RTC_TimeStructure.RTC_Minutes |= indic_data->indic_4 & 0x0F; RTC_TimeStructure.RTC_Seconds = indic_data->indic_5 << 4; RTC_TimeStructure.RTC_Seconds |= indic_data->indic_6 & 0x0F; RTC_SetTime ( RTC_Format_BCD, &RTC_TimeStructure ); } // ---------------------------------------------------------------------------- // Ф-я изменения текущего положения моргающей лампы при настройке времени // Ф-я вызывается из MenuFunc_AdjTime // Номер лампы соотносится с номером лампы ( левый бит - нулевой, и лампа тоже ) // ---------------------------------------------------------------------------- static void AdjTimeChangeTube ( ButtonCombName_t but_comb_name, uint8_t *position_mask ) { if ( but_comb_name == BUTTON_SINGLE_FORWARD ) { if ( !(*position_mask & ( 1 << 0 )) ) { *position_mask >>= 1; } else { *position_mask = 32; } // 0010 0000 } else { if ( *position_mask & ( 1 << 5 ) ) { *position_mask = 1; } else { *position_mask <<= 1; } } } // ---------------------------------------------------------------------------- // Ф-я изменения значения цифры в текущем положении // То есть тут настраиваем время "поцифирно" // ---------------------------------------------------------------------------- static void AdjTimeChangeValue ( ButtonCombName_t but_comb_name, IndicModesMsgBlink_t *data_struct, uint8_t position_mask ) { uint8_t increase; if ( but_comb_name == BUTTON_HOLD_FORWARD ) { increase = 1; } else { increase = 0; } switch ( position_mask & 0x3F ) { case 0x01: // Секунды единицы ChangeValue ( increase, &data_struct->data.indic_6, 9 ); break; case 0x04: // Минуты единицы ChangeValue ( increase, &data_struct->data.indic_4, 9 ); break; case 0x02: // Секунды десятки ChangeValue ( increase, &data_struct->data.indic_5, 5 ); break; case 0x08: // Минуты десятки ChangeValue ( increase, &data_struct->data.indic_3, 5 ); break; case 0x10: // Часы единицы if ( data_struct->data.indic_1 == 2 ) { ChangeValue ( increase, &data_struct->data.indic_2, 3 ); } else { ChangeValue ( increase, &data_struct->data.indic_2, 9 ); } break; case 0x20: // Часы десятки if ( data_struct->data.indic_2 <= 3 ) { ChangeValue ( increase, &data_struct->data.indic_1, 2 ); } else { ChangeValue ( increase, &data_struct->data.indic_1, 1 ); } break; } } // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- static void ChangeValue ( uint8_t increase, uint8_t *data, uint8_t limit ) { if ( increase ) { if ( *data < limit ) { *data += 1; } else { *data = 0; } } else { if ( *data > 0 ) { *data -= 1; } else { *data = limit; } } } // ---------------------------------------------------------------------------- // Задача ОС, реализующая головную задачу программы NixieClockSimply // ---------------------------------------------------------------------------- void Head_Task ( void *pvParameters ) { while(1)ProcessFSM_Head (); }