#include "indicate_modes_task.h" #include "ltimers/ltimers.h" // FreeRTOS includes #include "FreeRTOS.h" #include "task.h" #include "queue.h" #include "semphr.h" typedef enum { STATE_BLINK_START_OFF, STATE_BLINK_TIMER_OFF, STATE_BLINK_TIMER_ON } IndicModesBlinkStates_t; static IndicModesBlinkStates_t state_blink = STATE_BLINK_TIMER_OFF; static IndicModesNums_t curr_indic_mode = INDIC_MODE_STANDART; QueueHandle_t queue_data_to_indic; QueueHandle_t queue_data_to_blink_mode; extern QueueHandle_t queue_switch_indic_mode; // Прототипы функций меню static void IndicMode_Standart ( void ); static void IndicMode_Blink ( void ); void DataCopyToArray ( uint8_t *data_arr, DataToIndicate_t *data_struct ); void DataCopyToStruct ( DataToIndicate_t *data_struct, uint8_t *data_arr ); void WriteOffByte ( uint8_t *array, uint8_t mask ); // Массив указателей на функции-меню typedef void ( *IndicModes_t ) ( void ); static const IndicModes_t IndicModes[MAX_INDIC_MODES] = { IndicMode_Standart, IndicMode_Blink }; // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- void IndicateModesInit ( void ) { queue_data_to_indic = xQueueCreate ( 1, sizeof (DataToIndicate_t) ); configASSERT( queue_data_to_indic ); queue_data_to_blink_mode = xQueueCreate ( 1, sizeof (IndicModesMsgBlink_t) ); configASSERT( queue_data_to_indic ); } // ---------------------------------------------------------------------------- // Режим инжикации "Стандартный", когда время выводится без эффектов // ---------------------------------------------------------------------------- static void IndicMode_Standart ( void ) { ; // Важное замечание! // При передаче указателя массива через очередь ОС нужно передавать весь // массив целиком, чтобы полученный экземпляр данных не менялся. Для передачи // шести байт можно сделать небольшую структуру // - тут проверяем сообщение из очереди ОС на обновление выводимых данных // и если они изменились, то отправляем их в модуль Никси static DataToIndicate_t data_struct; if ( pdPASS == xQueueReceive ( queue_data_to_indic, &data_struct, 0 ) ) { NixieDriver_SendValue2 ( data_struct ); } } // ---------------------------------------------------------------------------- // Режим "моргания" индикаторов // В сообщении из очереди принимаются данные, которые нужно вывести, и // маска-байт, которая соответствует номеру индикатора, который должен // моргать. Если байт установлен, то индикатор моргает. Могут моргать сразу // несколько индикаторов. Моргание синхронное. // Останавливать задачу в этих функция нельзя // - тут лучше было бы сделать ожидание завершения фазы моргания // ---------------------------------------------------------------------------- static void IndicMode_Blink ( void ) { ; // - принимаем сообщение с данными и маской, и на номер установленного // байта записываем пустой символ, запоминая его. Затем запускаем таймер // и отправляем массив данных с "дыркой" на вывод. После таймера // записываем сохраненное значение и выводим с таймером "чистые" значения static const uint32_t TIME_BLINK_OFF = 250; // ms static const uint32_t TIME_BLINK_ON = 250; // ms static DataToIndicate_t data_struct; static IndicModesMsgBlink_t data_new_struct; static uint8_t indic_data_off [MAX_TUBES]; static uint8_t prev_indic_data_off [MAX_TUBES]; static uint8_t new_indic_data_off [MAX_TUBES]; static uint8_t prev_mask_byte = 0; static uint8_t is_blink_tube = 0; if ( pdPASS == xQueueReceive ( queue_data_to_blink_mode, &data_new_struct, 0 ) ) { DataCopyToArray ( &new_indic_data_off[0], &data_new_struct.data ); DataCopyToArray ( &indic_data_off[0], &data_new_struct.data ); WriteOffByte ( &indic_data_off[0], data_new_struct.mask_byte ); // теперь можно вместо пустой цифры отправлять точку DataCopyToStruct ( &data_struct, &indic_data_off[0] ); // Если изменилась только маска байта, то начинаем моргать с отрицательной // фазы if ( prev_mask_byte != data_new_struct.mask_byte ) { prev_mask_byte = data_new_struct.mask_byte; NixieDriver_SendValue2 ( data_struct ); StartLTimer ( LTIMER_INDIC_MODE_BLINK_OFF ); state_blink = STATE_BLINK_TIMER_OFF; } else { // А если изменились данные моргающей лампы, то начинать с // положительной фазы for ( uint8_t tube_num = 0; tube_num < MAX_TUBES; tube_num++ ) { if ( (prev_indic_data_off[tube_num] != new_indic_data_off[tube_num]) )// В этой лампе данные не равны? { if ( data_new_struct.mask_byte & ( 32 >> tube_num ) ) // И эта лампа моргающая? { is_blink_tube = 1; break; } } } // end for if ( is_blink_tube == 1 ) { NixieDriver_SendValue2 ( data_new_struct.data ); StartLTimer ( LTIMER_INDIC_MODE_BLINK_ON ); state_blink = STATE_BLINK_TIMER_ON; is_blink_tube = 0; } else { if ( state_blink == STATE_BLINK_TIMER_OFF ) { NixieDriver_SendValue2 ( data_struct ); } else { NixieDriver_SendValue2 ( data_new_struct.data ); } } } DataCopyToArray ( &prev_indic_data_off[0], &data_new_struct.data ); } switch ( state_blink ) { case STATE_BLINK_START_OFF: break; case STATE_BLINK_TIMER_OFF: if ( GetLTimer ( LTIMER_INDIC_MODE_BLINK_OFF ) >= TIME_BLINK_OFF ) { NixieDriver_SendValue2 ( data_new_struct.data ); state_blink = STATE_BLINK_TIMER_ON; StartLTimer ( LTIMER_INDIC_MODE_BLINK_ON ); } break; case STATE_BLINK_TIMER_ON: if ( GetLTimer ( LTIMER_INDIC_MODE_BLINK_ON ) >= TIME_BLINK_ON ) { NixieDriver_SendValue2 ( data_struct ); state_blink = STATE_BLINK_TIMER_OFF; StartLTimer ( LTIMER_INDIC_MODE_BLINK_OFF ); } break; } } // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- void DataCopyToStruct ( DataToIndicate_t *data_struct, uint8_t *data_arr ) { data_struct->indic_1 = data_arr[0]; data_struct->indic_2 = data_arr[1]; data_struct->indic_3 = data_arr[2]; data_struct->indic_4 = data_arr[3]; data_struct->indic_5 = data_arr[4]; data_struct->indic_6 = data_arr[5]; } // ---------------------------------------------------------------------------- // // ---------------------------------------------------------------------------- void DataCopyToArray ( uint8_t *data_arr, DataToIndicate_t *data_struct ) { data_arr[0] = data_struct->indic_1; data_arr[1] = data_struct->indic_2; data_arr[2] = data_struct->indic_3; data_arr[3] = data_struct->indic_4; data_arr[4] = data_struct->indic_5; data_arr[5] = data_struct->indic_6; } // ---------------------------------------------------------------------------- // Ф-я записи в массив "темной" лампы // ---------------------------------------------------------------------------- void WriteOffByte ( uint8_t *array, uint8_t mask ) { for ( uint8_t tube_num = 0; tube_num < MAX_TUBES; tube_num++ ) { if ( mask & ( 32 >> tube_num ) ) { //array [tube_num] = TUBE_EMPTY_VALUE; // - тут цифра 10 это TUBE_DIGIT_EMPTY // Можно поробовать здесь отправлять точку вместо пустого символа array [tube_num] = 12; // Шлем номер значения цифры из массива tube_digits [MAX_DIGITS] } } } // ---------------------------------------------------------------------------- // Режимы работы индикации (моргать, плавно переключаться и тд.) // ---------------------------------------------------------------------------- void ProcessFSM_IndicateModes ( void ) { // - тут нужно принимать сообщения из модуля Меню и переходить в нужный режим // индикации // При этом нужно помнить, что режим индикации может иметь много состояний, // поэтому нужно сбрасывать это состояние и, возможно, другие переменные // перед переключением в другой режим. IndicModesNums_t indic_mode_num; if ( pdPASS == xQueueReceive ( queue_switch_indic_mode, &indic_mode_num, 0 ) ) { // Сначала переведем состояние отключаемого режима в начальное switch ( curr_indic_mode ) { case INDIC_MODE_STANDART: break; case INDIC_MODE_BLINK: state_blink = STATE_BLINK_TIMER_OFF; break; case MAX_INDIC_MODES: break; } curr_indic_mode = indic_mode_num; } IndicModes [curr_indic_mode](); taskYIELD(); } // ---------------------------------------------------------------------------- // Задача ОС, реализующая головную задачу программы NixieClockSimply // ---------------------------------------------------------------------------- void IndicateModes_Task ( void *pvParameters ) { (void)pvParameters; while(1)ProcessFSM_IndicateModes (); }