Простое однострочное LCD меню для проектов с использованием Atmega8 и WH1602A
Для более наглядного отображения режима работы устройства на микроконтроллерах используют символьный ЖКИ типа WH1602A, вместо обычных светодиодов. В данной статье я расскажу как сделать не большое однострочное меню, названия пунктов которого будут выводиться на экран ЖКИ. Меню будет представлять из себя массив пунктов, которые при нажатии на кнопки "+" и "-" будут прокручиваться, тем самым запуская на выполнение необходимые программы.
В данном примере будет находиться всего 3 пункта: Menu1, Menu2 и Menu3. Так как это простенькое меню и его можно считать отправной точкой для дальнейших экспериментов, то сами названия меню буду не в переменных а задаваться именно при выведении на экран ЖКИ. В дальнейшем будем пробовать сделать массив из пунктов меню и чтобы не занимать лишнее место в ОЗУ микроконтроллера Atmega8 запишем эти переменные в память программ.
Так же советую установить параллельно кнопкам конденсаторы на 0?1vrA? для уменьшения влияния дребезга контактов на переключение пунктов меню.
Алгоритм программы довольно простой и я думаю интуитивно понятный, тем более программа содержит комментарии, по которым очень легко воссоздать алгоритм работы. Программа написана для микроконтроллера Atmega8, возможна переделка кода без потери функционала на микроконтроллер ATtiny2313. Схема подключения ЖКИ к микроконтроллеру взята из статьи о работе микроконтроллера и ЖКИ.
Первоначально кнопки планировалось подключать к выводам внешних прерываний INT0 и INT1, но в дальнейшем решили все таки оставить эти выводы как резерв и использовать выводы порта C для подключения кнопок. А саму программу обработки пунктов меню поместить в основной(вечный) цикл главной программы.
Собственно код программы однострочного меню:
#include <avr/io.h>//Стандартная библиотека ввода/вывода #include <avr/interrupt.h>//Библиотека прерываний unsigned char mode=0;//Переменная пункта меню int flag=0;//Флаг вывода на экран пункта меню #define RS 1 //Вывод RS ЖКИ на вывод PD1 МК #define E 3 //Вывод E ЖКИ на вывод PD3 МК #define TIME 10 //Прототипы функций void lcd_init (void);//Инициализация ЖКИ void lcd_dat (unsigned char lcd);//Программа записи данных в ЖКИ void lcd_com (unsigned char lcd);//Программа записи комманд в ЖКИ void pause (unsigned int a);//Переменная для программы задержки(паузы) //Обработчик прерываний по таймеру 0 ISR(TIMER0_OVF_vect) { if ((PINC&0x00)==0x01)//Если низкий уровень на PC0 есть { pause(1000); //Ждем 0.01с if ((PINC&0x00)==0x01)//Если низкий уровень на PC0 есть { while ((PINC&0x01)==0x00);//Ждем отжатия клавиши mode=(mode+1)%3;//Меняем переменнюу пункта меню в +1 flag=0;//Меняем флаг на 0 } TCNT0=0x00;//Очищаем счетчик таймера0 TIFR=0x00; //и флаг переполнения } else //Если на PC0 нет низкого уровня if ((PINC&0x01)==0x00)//проверяем низкий уровень на PC1 { pause(1000); //Если есть Ждем 0.01с if ((PINC&0x01)==0x00) //проверяем низкий уровень на PC1 еще раз { while ((PINC&0x01)==0x00); //Ждем отжатия клавиши mode=(mode-1)%3;////Меняем переменнюу пункта меню в -1 flag=0;//Меняем флаг на 0 } TCNT0=0x00;//Очищаем счетчик таймера0 TIFR=0x00; //И флаг переполнения } return; } //Программа вывода строки на ЖКИ void lcd_string( char *s ) { while( *s ) //цикл пока указатель существует lcd_dat( *s++ ); //выводить строку } //Функция задержки(паузы) void pause (unsigned int a) { unsigned int i; //переменная для задержки for (i=a;i>0;i--); //цикл задержки } //Программа передачи комманды в ЖКИ void lcd_com (unsigned char lcd) { unsigned char temp; temp=(lcd&~(1<<RS))|(1<<E); //RS=0 – это команда PORTD=temp; //Выводим на portD старшую тетраду команды, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи команды temp=((lcd*16)&~(1<<RS))|(1<<E); //RS=0 – это команда PORTD=temp; //Выводим на portD старшую тетраду команды, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи команды pause (20*TIME); //Пауза для выполнения команды } //Программа передачм данных в ЖКИ void lcd_dat (unsigned char lcd) { unsigned char temp; temp=(lcd|(1<<RS))|(1<<E); //RS=1 – это данные PORTD=temp; //Выводим на portD старшую тетраду данных, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи данных temp=((lcd*16)|(1<<RS))|(1<<E); //RS=1 – это данные PORTD=temp; //Выводим на portD старшую тетраду данных, сигналы RS, E asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации PORTD=temp&~(1<<E); //Сигнал записи данных pause(10*TIME); //Пауза для вывода данных } ////Программа инициализации ЖКИ void lcd_init (void) { lcd_com(0x2C); //4-проводный интерфейс, 5x8 размер символа pause(200*TIME);//пауза на выполненеи комманды lcd_com(0x0C); //Показать изображение, курсор не показывать pause(200*TIME);//пауза на выполненеи комманды lcd_com(0x01); //Очистить DDRAM и установить курсор на 0x00 pause (200*TIME);//пауза на выполненеи комманды } //Программа инициализации таймера 0 void init_timer (void) { TIMSK=(1<<TOIE0); //Разрешить прерывания по переполнению таймера 0 TCCR0=(1<<CS00)|(1<<CS01)|(0<<CS02); //Делитель =/64 } //Программа обработки переменной меню void menu(void) { if (mode==0)//Если переменная меню=0 { if (flag==0)//Если флаг=0 { lcd_com(0x80);//Чистим экран ЖКИ lcd_string("<Menu1>");//Выводим название первого пункта меню 1 flag=1;//Переключаем флаг=1 } } else //иначе if (mode==1)//Если переменная меню=1 { if (flag==0)//Если флаг=0 { lcd_com(0x80);//Чистим экран lcd_string("<Menu2>");//Выводим название первого пункта меню 2 flag=1;//Переключаем флаг=1 } } else //В любом другом случаи { if (flag==0)//Если флаг=0 { lcd_com(0x80);//Чистим экран ЖКИ lcd_string("<Menu3>");//Выводим название первого пункта меню 3 flag=1;//Переключаем флаг=1 } } } //Основная программа int main(void) { /////////////Конфигурируем порты МК//////////////// DDRD=0xFA; //PD0,PD2 как входа, остальные - выхода PORTD=0x05;//Выводим на порт D = 000000101 DDRC=0x00;//Весь порт C как вход PORTC=0xFF;//Включаем подтягивающие резисторы /////////////////////////////////////////////////// pause(3000); //Задержка для включения ЖКИ lcd_init(); //Инициализация ЖКИ pause(3000); //Задержка для включения ЖКИ init_timer();//Инициализация таймера 0 sei();//Глобально разрешаем прерывания while(1)//Вечный цикл { menu();//Запуск обработки пунктов меню } }
Названия пунктов меню можно без труда изменить под свой конкретный проект, более того, разместив в коде вызов необходимой программы, можно легко включать нужный режим:
if (mode==0)//Если переменная меню=0 { if (flag==0)//Если флаг=0 { lcd_com(0x80);//Чистим экран ЖКИ lcd_string("<Menu1>");//Выводим название первого пункта меню 1 flag=1;//Переключаем флаг=1 ///////////////////////////////////// Programm_1();//Программа которая выполняется в пункте меню 1 ///////////////////////////////////// } }
Таким образом можно легко организовать выбор выполнения трех различных программ, как пример это могут быть три режима яркости свечения фонаря, или три режима работы новогодней гирлянды и т.д. При желании количество меню можно увеличить до необходимого количества.
Программа рассчитана на работу от внешнего кварцевого генератора на частоте 4МГц, в принципе по желанию можно свободно перевести МК на работу от внутреннего генератора.
На кнопки обязательно ставить параллельно замыкающим контактам конденсатор 0,1 мкФ, очень помогает дребезг контактов уменьшить.
Видео работы программы:
На момент написания статьи Я не знал о конструкции switch по сему придумывал велосипед.
На данный момент наилучший результат показало меню на конструкции switch, про него расскажу в одной из статей немного позже.
не читает АЦП в подразделе меню.
Вот такая вот проблема. Переделал немного твой код - переключение меню по прерыванию от кнопки на INT2. Из кода видно что меню с вложенной программой находится в цикле while (1), но обновляться почему-то совсем не желает, однако если "прогнать" меню по кругу и вернуться к п. 1 то показания меняются.. Код выкладываю собсно...
Прошил программу, собрал
Прошил программу, собрал схему, при включении запускается тест-программа ЖКИ (верхняя строка горит, нижняя нет) и все. Может подключил чего не так?
PC0 - кнопка +
PC1 - кнопка -
PD1 - RS
PD3 - E
PD4 - DB4
PD5 - DB5
PD6 - DB6
PD7 - DB7
Питание и контрастность ЖКИ подключены правильно, W/R - на землю.
А какой кварц стоит? Или
А какой кварц стоит? Или какая частота внутреннего если от внутреннего генератора тактируешь?
Внутренний генератор,
Внутренний генератор, прошивал как при 2,4 и 8 МГц.
ЖКИ у меня RC1602A.
Дело в том что программа
Дело в том что программа написана под ЖКИ WH1602 я не знаю что там с твоим ЖКИ но возможной проблемой работы устрйоства может быть разное время инициализации и выполнения команд индикатором. Попробуй поиграться с задержками в библиотеке работы с ЖКИ, а именно с теми, что касаются инициализации и паузы после отправки команды в ЖКИ. Попробуй увеличить их!
Как вывести кириллицу?
Как вывести кириллицу?
Вместо 'a','б','ф','ц' пиши
Вместо 'a','б','ф','ц' пиши коды кириллических букв:
Таблица кодов
вопрос покажеться глупым ,но
вопрос покажеться глупым ,но где это нужно писать?
Поробуй модифицировать
Поробуй модифицировать следующим образом: