Визуальное представление
Описание
Устройство представляет собой программируемый кнопками S1-S3 Двух фазный генератор от 1Гц до 9999Гц, информация выводится на символьный LCD1602. В программном коде используются оба канала 16-битного таймера (см. рисунок 3 — Timer1). Сдвиг фазы устанавливается у канала В. Arduino UNO можно заменить на NANO или любую другую особых изменений не требует, но будьте внимательны!
*будете внимательны https://www.tinkercad.com/ нет библиотеки GyverTimers.h.
В схеме предусмотрена регулировка, с помощью «ШИМ», яркости и контрастности (функция DISPLAY_AND_CONTRAST() — используется по необходимости). Так же программно возможно установить сдвиг фазы у обоих каналов, но частота для обоих каналов устанавливается одна (так как используется 1 таймер), Во время настройки, оба канала выключены, после установки необходимых значений каналы включаются, что свидетельствует изменение в правом верхнем углу «ON» и «OFF».
Для регулировки частоты каждого канала — придется привнести некоторые не значительные изменения:
-
Необходимо для 2 выхода использовать любой другой таймер (рекомендую канал B если не вчитываться в тонкости) ориентируясь по таблице (ссылка от автора GyverTimers.h) — https://alexgyver.ru/gyvertimers/
Рисунок 3 — таблица таймеров ATmega328p *Обратите внимание на минимальную частоту таймеров.
-
Изменить «контент» на LCD, проще использовать LCD1604, или придумать удобное визуальное представление, для ручной настройки частоты и сдвига обоих таймеров.
*Обратите внимание, что сдвиг фазы будет относительно самого таймера (и используемого канала), а не между ними (между таймерами). Хоть программно запуская оба таймера условно можно принять на веру их синхронность, однако это будет не совсем корректно.
-
Изменить карту перемещения курсора, его координаты. Программный код обязан справится с изменениями первоначальных данных, Вы можете описать то представление информации на LCD, какое удобно Вам, а так же перемещение курсора или установка большей частоты.
Для увеличения регулируемого диапазона частоты необходимо увеличить «cегментность» (количество разрядов) или произвести переключение с Гц на КГц, но помните что макс частота 1МГц(см. Рисунок 3)., придется так же изменить незначительно программный код, с теми условиями которые необходимы Вам, к примеру:
-
По нажатию на Hz изменить на KHz,
-
В коде изменить изначально на KHz
-
По вашему усмотрению.
ИТОГ
Простое устройство паразитирующее библиотеку «GyverTimers.h», которая сильно упрощает работу с таймером. Программный код разделен на несколько условных блоков «работа с LCD» и «настройка таймера».
Программный код (Arduino C ):
*Обратите внимание что с вкл LCD, питая всю плату о USB порта компьютера, вполне возможно произойдет ситуация, что Arduino не будет прошиваться, отключите LCD, во время загрузки прошивки.
/****************************************************/ /* 2-х фазный генератор */ /* */ /* */ /* */ /****************************************************/ // подключение библиотек #include <GyverTimers.h> // Библиотека GyverTimers (для работы с таймером И! устанавливать сдвиг фазы) #include <LiquidCrystal.h> // Подключаем стандартную библиотеку LiquidCrystal /****************************************************/ // Инициализируем объект-экран, передаём использованные LiquidCrystal lcd(8, 7, 15, 14, 11, 12); // RS, E, D4, D5, D6, D7 */ /****************************************************/ // Инициализация пинов #define LEFT_BUTTON 4 // левая кнопка #define RIGHT_BUTTON 2 // права кнопка #define CENTRE_BUTTON 3 // центральная кнопка #define DISPLAY_CONTRAST_ON 5 // Управление контрасностью ШИМ #define DISPLAY_LIGHT_ON 6 // Управление подсветкой ШИМ #define CHANNEL_TIMER_A 9 // выход канала А 1 таймера #define CHANNEL_TIMER_B 10 // выход канала В 1 таймера /****************************************************/ //таймер #define AMOUNT_VALUE_FREQUENCY_NUMBER 4 // количество знаков значения частоты обоих каналов #define AMOUNT_VALUE_FREQUENCY_DEVIATION 2 // количество знаков значения отклонения частоты канала В #define ON_TEXT "ON " // Текст на LCD для отображения того что каналлы вкл #define OFF_TEXT "OFF" // Текст на LCD для отображения того что каналлы выкл /****************************************************/ // Положение курсора int cur_col = 0; // текущая координата указателя (столбец) int cur_row = 0; // текущая координата указателя (строка) /****************************************************/ // LCD // для использования другого необходимо отредактировать: #define NUMBER_COL_USE_LCD 16 // количетсво столбцов (установить от 1) но в дальнейшем использовать от 0 #define NUMBER_ROW_USE_LCD 2 // количетсво строк (установить от 1) но в дальнейшем использовать от 0 int number_row[] = // номера строк LCD ( используется для курсора) { 0, 1}; /****************************************************/ // контент LCD // **страница 1** // внешний вид страницы 1 String content_page_one[] = // массив контента 1 страницы { "Ch1:0100Hz ->ON ", // 0 строка "F1:180'"}; // 1 строка int position_cursor_page_one[4][4] = // координтаы по которому необходимо перемещать курсор( 1 индекс = строка) (2 индекс = столбец) { { 4, 5, 6, 7}, // Координаты 1 ряда 1 страницы (0-3) { 3, 4, 5}, // Координаты 2 ряда 1 страницы (4-5) }; int amount_position_page_one[] = // Количество(размер массива) координат на одной линии { 3, 2}; //( так как самого начала он должен быть известен)( sizeoof - работает во время компиляции, а не кода) // Значения(VALUE) используемые на странице 1 (индекс = разряду( начало с конца) int value_content_one_page[][4] = { { 0, 1, 0, 0}, // частота канала A и B { 1, 8, 0}, // сдвиг по фазе канала B }; int amount_value_page_one[] = // Количество(размер массива) изменяемых значений на одной линии { 3, 2}; /****************************************************/ // Символы ASCII начиная с 0 #define translate_to_ASC 48 // 0 в кодировке ASCII /****************************************************/ // Работа со временм #define last_press_For_buttom 200 // условный антидребезг контактов #define micros_work 300 // время 1 цикла (loop)( перемещение курсора или редактирование) unsigned long Work; // переменная для сравнения истекшего отрезка времени в loop unsigned long last_press; // Время Прошлого нажатия /****************************************************/ // Флаги boolean flag_editor_enable ; // включен редактор одного символа на lcd /****************************************************/ /****************************************************/ /**************** РАБОТА С LCD ********************/ /****************************************************/ //--------------------------------------------------// // функция для перевода int в число из кодировки ASCII char func_change_int_to_ASC(int param0){return param0 + translate_to_ASC;} //--------------------------------------------------// // функция для перевода из кодировки ASCII в int int change_ASC_to_int(char param){return param - translate_to_ASC;} //--------------------------------------------------// // функция установки контента на LCD void load_page_to_lcd(String *array_content){ // массив в котором хранится контент lcd.clear(); // очистить LCD for (int i=0; i < NUMBER_ROW_USE_LCD ;i++){ // выводим текст равный ( инициализированному значению страницы для LCD) lcd.setCursor(0,i); lcd.print(array_content[i]); } } //--------------------------------------------------// // функция опроса кнопки boolean ask_button(int number_pin){ boolean is_button_press = false; if (!digitalRead(number_pin) && (millis() - last_press > last_press_For_buttom)){ // проверка на нажатие защита от антидребезг + множетсвенное нажатие is_button_press = true; last_press = millis(); } return is_button_press; } //--------------------------------------------------// // функция установки курсора void set_cursor(int param0,int param1){ // param0 = col, param1 = row lcd.setCursor(param0,param1); lcd.cursor(); // подчеркивание } //--------------------------------------------------// // функция перемещения курсора void move_cursor(){ if(ask_button(LEFT_BUTTON)){ // нажата левая кнопка int cur_index_col = find_index(position_cursor_page_one[cur_row], amount_position_page_one[cur_row],cur_col); // найти текущий индекс числа (столбца) cur_index_col --; // уменьшить текущий индекс if(is_index_exp(amount_position_page_one[cur_row],cur_index_col)){ // если надо перенести строку cur_row --; // опустить курсор выше if(is_index_exp(NUMBER_ROW_USE_LCD - 1, cur_row)){ // надо ли перейти в начало или конец страницы cur_row = number_row[NUMBER_ROW_USE_LCD - 1]; // установить последнюю строку } cur_col = position_cursor_page_one[cur_row][ amount_position_page_one[cur_row]]; // получить индекс последнего элемента ({[текущая строка] [размер массива]) } else{ // если НЕ надо переносить строку и перехоить в начало или конец cur_col= position_cursor_page_one[cur_row][cur_index_col]; // присвоить новое текущее значение } set_cursor(cur_col,cur_row); // установить новые координаты для курсора } else if(ask_button(RIGHT_BUTTON)){ // нажата правая кнопка int cur_index_col = find_index(position_cursor_page_one[cur_row], amount_position_page_one[cur_row],cur_col); // найти текущий индекс числа (столбца) cur_index_col ++; // увеличить текущий индекс if(is_index_exp(amount_position_page_one[cur_row],cur_index_col)){ // если надо перенести строку cur_row ++; // опустить курсор ниже if(is_index_exp(NUMBER_ROW_USE_LCD - 1, cur_row)){ // надо ли перейти в начало или конец страницы cur_row = number_row[0]; // установить на первую строку } cur_col = position_cursor_page_one[cur_row][0]; // индекс первого элемента ({[текущая строка] [размер массива]) } else{ // если НЕ надо переносить строку и перехоить в начало или конец cur_col= position_cursor_page_one[cur_row][cur_index_col]; // присвоить новое текущее значение } set_cursor(cur_col,cur_row); // установить новые координаты для курсора } } //--------------------------------------------------// // функция нахождения индекса значения в массиве int( если не найдет вернет 9999 - условная ошибка) int find_index(int *received_array,int syze_array,int number){ for(int i=0 ;i <= syze_array;i++)if(received_array[i] == number)return i; return 9999; } //--------------------------------------------------// // функция проверки если индекс вышел из диапазона( IndexOutOfRangeException ) boolean is_index_exp(int syze_array, int index){ if(syze_array < index or index < 0) return true; else {return false;} } //--------------------------------------------------// // функция изменения 1 значения(разряда) на LCD void change_one_segment_on_lcd(){ if(ask_button(LEFT_BUTTON)){ // нажата левая кнопка int buff = value_content_one_page[cur_row][ find_index(position_cursor_page_one[cur_row], amount_value_page_one[cur_row],cur_col)]; // взять текущее значение buff++; // увеличить на 1 buff = func_zero_nine(buff); // проверить lcd.print(func_change_int_to_ASC(buff)); // отобразить set_cursor(cur_row,cur_col); // вернуть курсор value_content_one_page[cur_row][ find_index(position_cursor_page_one[cur_row], amount_value_page_one[cur_row],cur_col)] = buff; // сохранить } else if(ask_button(RIGHT_BUTTON)){ // нажата правая кнопка int buff = value_content_one_page[cur_row][ find_index(position_cursor_page_one[cur_row], amount_value_page_one[cur_row],cur_col)]; // взять текущее значение buff--; // уменьшить на 1 buff = func_zero_nine(buff); // проверить lcd.print(func_change_int_to_ASC(buff)); // отобразить set_cursor(cur_row,cur_col); // вернуть курсор value_content_one_page[cur_row][ find_index(position_cursor_page_one[cur_row], amount_value_page_one[cur_row],cur_col)] = buff; // сохранить } } //--------------------------------------------------// // фукция изменения числа(цикла) 0-9 int func_zero_nine(int number){ switch(number){ case 10: return 0; break; case -1: return 9; break; default: return number; break; } } //--------------------------------------------------// void on_off_lcd_for_page_one(String param0){ lcd.setCursor(13,0); lcd.print(param0); } //--------------------------------------------------// // Управление подсветкой и контрастностью ( не используется ) void DISPLAY_AND_CONTRAST() { //analogWrite(DISPLAY_LIGHT_ON,0); // Вкл ШИМ подсветки //analogWrite(DISPLAY_CONTRAST_ON,0); // Вкл ШИМ контрасности } /****************************************************/ /********************* ГЕНЕРАТОР ******************/ /****************************************************/ //--------------------------------------------------// // запуск таймера void start_timer(){ Timer1.enableISR(CHANNEL_A); // запускаем таймер 1 канал А Timer1.enableISR(CHANNEL_B); // запускаем таймер 1 канал B Timer1.setFrequency(get_freq()); // установить частоту Timer1.phaseShift(CHANNEL_B,get_dem_freq()); // установить сдвиг фазы } //--------------------------------------------------// // настройка таймера void set_timer(){ Timer1.outputEnable(CHANNEL_A, TOGGLE_PIN); // в момент срабатывания таймера пин будет переключаться Timer1.outputEnable(CHANNEL_B, TOGGLE_PIN); // в момент срабатывания таймера пин будет переключаться Timer1.outputState(CHANNEL_A, HIGH); // задаём начальное состояние пина 11 Timer1.outputState(CHANNEL_B, HIGH); // задаём начальное состояние пина 3 } //--------------------------------------------------// // прерывание таймера TIMER1_A ISR(TIMER1_A) { // код по обработке прерывания // необходимо объявить даже если тело пустое для GyverTimers.h } //--------------------------------------------------// // прерывание таймера TIMER1_B ISR(TIMER1_B) { // код по обработке прерывания // необходимо объявить даже если тело пустое для GyverTimers.h } //--------------------------------------------------// // получение значения частоты int get_freq(){ return (value_content_one_page[0][0] * 1000 + value_content_one_page[0][1] * 100 + value_content_one_page[0][2] * 10 + value_content_one_page[0][3]) * 2; // неободимо умножать на 2 частоту( если меанд) // причина в работе таймера и GyverTimers.h } // получение значения сдвига фазы //--------------------------------------------------// int get_dem_freq(){ return value_content_one_page[1][0] * 100 + value_content_one_page[1][1] * 10 + value_content_one_page[1][2]; } /****************************************************/ /******************** MAIN ************************/ /****************************************************/ //--------------------------------------------------// // SETUP void setup() { lcd.begin(NUMBER_COL_USE_LCD, NUMBER_ROW_USE_LCD); // Инициализируем дисплей: pinMode(LEFT_BUTTON, INPUT); // Инициализируем кнопки: pinMode(RIGHT_BUTTON, INPUT); pinMode(CENTRE_BUTTON, INPUT); pinMode(DISPLAY_CONTRAST_ON, OUTPUT); // управление подсветкой pinMode(DISPLAY_LIGHT_ON, OUTPUT); // управление контрасностью pinMode(CHANNEL_TIMER_B, OUTPUT); // Выход таймера канал B pinMode(CHANNEL_TIMER_A, OUTPUT); // Выход таймера канал A digitalWrite(14, LOW); // ( перевод в цифровое состояние и записать 0) пина 14(А0) digitalWrite(15, LOW); // ( перевод в цифровое состояние и записать 0) пина 15(А1) set_timer(); // настройка таймера start_timer(); // запуск таймера ( с начальной частотой и отклонением ) load_page_to_lcd(content_page_one); // загрузить 1(начальную) страницу cur_row = number_row[0]; // задать начальные координаты курсора (строка) cur_col = position_cursor_page_one[0][0]; // задать начальные координаты курсора (столбец) set_cursor(cur_col,cur_row); // установить курсор flag_editor_enable = false; // выключить редактирование (включить перемещение курсора) } /****************************************************/ //--------------------------------------------------// // LOOP void loop() { if (micros() - Work > micros_work){ // Выполнять цикл раз в (micros_work) (Работа с LCD дисплеем) if(flag_editor_enable){ // изменение 1 сегмента lcd.blink(); // выключить мигание set_cursor(cur_col,cur_row); // установить курсор change_one_segment_on_lcd(); // Опрос боковых кнопок ( для изменения) if(ask_button(CENTRE_BUTTON)){ // опрос центральной кнопки flag_editor_enable = false; // выключить редактирование on_off_lcd_for_page_one(ON_TEXT); // отобразить на дисплее включение Timer1.setFrequency(get_freq()); // установить частоту и запустить таймер Timer1.phaseShift( CHANNEL_B,get_dem_freq()); // установить сдвиг фазы } } else{ //перемещение курсора lcd.noBlink(); // включить мигание set_cursor(cur_col,cur_row); // установить курсор move_cursor(); // Опрос боковых кнопок ( для перемещения) if(ask_button(CENTRE_BUTTON)){ // опрос центральной кнопки flag_editor_enable = true; // включить редактирование on_off_lcd_for_page_one(OFF_TEXT);// отобразить на дисплее выключение //Timer1.pause(); // приостановить Timer1.stop(); // приостановить и сбросить } } Work = micros(); // этот цикл завершен } } /****************************************************/
Признаю, что все можно сделать лучше, но я себе поставил задачу сделать как можно «more universal» — по минимум изменений. Надеюсь на 3+ выполнил.=)
ссылка на оригинал статьи https://habr.com/ru/articles/753508/
Добавить комментарий