AVR340: прямое управление сегментами ЖКИ с помощью портов GPIO
Добавил(а) microsin
Приведен перевод даташита Atmel [1], рассказывающий о принципах прямого управления LCD с помощью портов GPIO микроконтроллера AVR (семейств ATtiny и ATmega). Т. е. специальный контроллер LCD не используется, в качестве контроллера LCD выступает сам микроконтроллер AVR со специальным программным драйвером.
Прим. переводчика: даташит AVR340 [1] довольно небрежный, пестрит опечатками и ошибками. Что заметил, исправил, но возможно не все. Несмотря на эти огрехи, основной принцип прямого управления индикатором даташит раскрывает достаточно понятно.
[Введение]
Многие радиоэлектронные устройства требуют подключения ЖКИ (Liquid Crystal Display, LCD) к микроконтроллеру (MCU). В этом апноуте описана работа с мультиплексированным LCD. Также обсуждается форма электрических сигналов и соединения, которые требуются для LCD, и программа на языке C, которая нужна для работы LCD. В результате получается отличное, недорогое решение, которое можно использовать как стартовую позицию для разработки многих продуктов.
Хотя мультиплексированные LCD сложнее для применения, они наиболее дешевы, и требуют наименьшего количества портов I/O по сравнению со всеми стеклянными LCD (под "стеклянными" имеются в виду LCD, которые не имеют на борту встроенного контроллера для управления сегментами).
[Как устроен мультиплексированный стеклянный LCD]
Рис. 2-1. LCD индикатор PD332.
На рисунке показан пример LCD без встроенного контроллера, у которого есть 4 общих входа (COM) и 8 входов сегментов. Такой LCD потребует 4+8=12 портов GPIO микроконтроллера ATmega48. При этом останутся свободными от 11 до 16 GPIO, в зависимости от выбранного корпуса микроконтроллера.
LCD для своей работы требует подачи на сегменты переменных сигналов прямоугольной формы. Если не устранить постоянную составляющую из сигналов управления сегментами, то LCD будет поврежден, потому что произойдет необратимая металлизация сегментов.
Сегменты LCD становятся видимыми, если разность напряжения между входом COM (общие входы) и входом сегмента составит обычно 3.5 VAC. В этом примере 4 входа COM получают каждый смещение, равное 1/2 напряжения питания микроконтроллера VCC. Это сделано для того, чтобы выбранные сегменты стали видимыми. На рис. 2-2 показаны формы сигнала на 3 из 4 входах COM. Все 4 COM-а разрешаются последовательно, чтобы было сгенерировано по 1 циклу переменного напряжения для каждого COM. Общее время для формирования циклов всех 4 COM составляет приблизительно 16 миллисекунд, так что скорость обновления изображения на LCD получается около 60 Гц. Это достаточно быстро, чтобы не было заметно мерцания, и достаточно медленно для того, чтобы не было двоения выключенных сегментов (чтобы они успели выключиться).
На осциллограммах показано, как каждый сигнал COM переходит от 1/2 VCC до VCC на 2 мс, и затем к GND на 2 мс, и затем обратно к 1/2 VCC. Этот уровень называется "половина возбуждающего напряжения VCC". Более сложные LCD часто используют "возбуждение по одной трети", однако этот метод в этом апноуте не рассматривается. Дополнительную информацию по возбуждению LCD см. [2].
Каждый COM-цикл активен 4 мс, затем возвращается к 1/2 VCC, в неактивное состояние. Общее время для всех 4 COM составит 16 мс.
Рис. 2-2. Осциллограммы сигналов возбуждения LCD.
Чтобы активировать (отобразить) сегмент, его фаза должна быть на 180 градусов отличаться от фазы его сигнала COM. Таким образом, разница напряжения между сигналами сегмента и COM составит 5V AC, что приводит к тому, что сегмент станет видимым после 300..400 мс циклов обновления.
Чтобы деактивировать (выключить) сегмент, сигналы COM и сегмента должны быть в фазе друг с другом в тот момент, когда COM этого сегмента является активным, т. е. на равен 2.5V. Таким образом, все сегменты LCD будут выключены, когда входы сегментов всегда будут В ФАЗЕ с каждым из его входом COM (на рис. 2-2 сигнал COM3 опущен, так как были доступны только 4 канала).
[Описание схемы]
На рисунке 3-1 показан простой интерфейс между ATmega48 и PD-332 LCD. Обратите внимание на pullup резисторы на сигналах COM: эти резисторы предоставляют напряжение 1/2 VCC, которое нужно, когда сигналы COM переходят в неактивное состояние. Для неактивного состояния ATmega48 просто устанавливает нужный вывод в режим входа, после чего резисторы переводят уровень COM к 1/2 VCC.
У LCD 8 соединений сегментов переименованы как 1A, 1B, 2A, 2B, и так далее, чтобы соответствовать данным в таблице сегментов программы на языке C.
Рис. 3-1. Схема подключения LCD к микроконтроллеру.
[Как работает табличный алгоритм]
Таблица для генерации сигналов (Look Up Table, LUT) является 10-байтовым списком (см. для примера таблицу 4-1), который описывает, какие сегменты нужно активировать, чтобы отобразить цифры от 0 до 9. Дополнительные символы можно определить, добавляя в таблицу дополнительные байты - по 1 байту на 1 новый символ.
• В основной программе 4 ячейки RAM содержат 4 символа для отображения. Эти ячейки RAM содержат двоичные числа от 0 до 9 (диапазон кодов может быть увеличен позже, если нужно добавить еще другие символы ASCII). • Для каждой позиции символа есть входы COMA и COMB. Когда каждый из COM входов активен, входы COMA и COMB являются результатом инструкций языка C, где каждое значение LUT соединяется с другим операцией OR. • Например, чтобы отобразить цифру 5 в крайней правой позиции, просмотрите последнюю строку таблицы LUT.
1. Каждые 2 мс происходит прерывание, которое переключает ветвление кода по оператору switch. 2. Когда активен COM1 (ветвь case 0), программа берет из LUT строку "5", и маскирует все биты кроме двух крайних правых. Они добавляются к переменной segs_out, находящейся в памяти RAM микроконтроллера AVR®. 3. Похожим образом, когда активен COM1, программа делает еще 3 выборки из LUT, чтобы выполнить операцию OR в переменную segs_out для первых 2 бит оставшихся для отображения 3 символов. 4. Все 2*4 = 8 бита складываются операцией ИЛИ (OR) вместе в переменной segs_out, и затем записываются в выходной порт B (регистр PORTB). 5. После истечения 2 мс получит управление ветка case 1 оператора switch. В этот момент данные segs_out будут проинвертированы операцией XOR с константой 0xFF, что даст требуемую форму переменного напряжения, требуемого для LCD. Эти результаты операции XOR отправляются в регистр PORTB.
• После истечения следующих 2 мс вышеуказанная последовательность повторится, но для активного COM2. Оператор switch передаст управление на ветвь case 2. В этот момент данные для каждого отображаемого символа будут выбираться из LUT, и собираться в переменной segs_out, затем будут отправляться в регистр PORTB, в то время как активность сигнала COM2 будет управляться данными регистра PORTD. • Последовательность повторится для сигналов COM3 и COM4. Все 4 сигнала 4 требуют для формирования 16 мс, по 2 мс на каждую из 8 ветвей оператора switch.
[Осциллограммы сигналов COM1-COM4]
На рис. 5-1 показано, как формы переменных формируемых сигналов генерируются для двух сегментов LCD. Сигналы COM генерируются программой на языке C, показанной в следующей секции. Чтобы активировать сегмент LCD, к нему должен быть приложен импульс двухполярного напряжения к нужным входам A и B индикатора LCD. Для каждой позиции символа есть входы A и B.
// Файл: LCX_App_Note_5_3_05.c демонстрационного C-кода для запуска LCD индикатора// PPD-332 фирмы Pacific Display. Код компилировался компилятором CodeVision AVR // версии 1.24.4a Evaluation, доступным на сайте http://www.hpinfotech.ro.// Эта программа отображает 4 инкрементируемые десятичные цифры на LCD. У LCD есть// 4 входа COM и 8 входов сегмента (всего 12 подключений).// COM1-COM4 выводятся на PORTD.// Сегменты выводятся на PORTB, и называются 1A, 1B, 2A, 2B, 3A, 3B - чтобы// соответствовать значениям A & B из массива segment_table.// PD-332 имеет идентичную разводку LCD для каждой из 3 цифр; 2 вывода сегмента// на цифру, помеченные как A и B.#include < mega48.h > unsignedchar segs_out =0;
unsignedchar state_counter =8;
unsignedchar output_change =0;
unsignedchar LCD_d_1 =0; // отображаемый счетчик стартует от "000"unsignedchar LCD_d_2 =0;
unsignedchar LCD_d_3 =0;
unsignedchar LCD_d_4 =0;
unsignedchar Pt_1_sec =0; // счетчик единиц времени 0.1 сек voidinitialization(void);
#define debug 0#define LCD_Driver 1 // Look Up Table (LUT) для 3-х 1/2 цифрового 4 COM LCD компании Pacific Displays, #PD-332// Следующая таблица имеет 10 записей для отображения символов 0-9, HEX-значения COM1-COM4// для входов LCD A & B.constunsignedchar segment_table[] = {0x77,0x22,0xDB,0x97,0x2E,0x6D,0x7C,0x23,0xFF,0x2F}; //********************* Обработчик прерывания по переполнению Timer 0 *******************
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Перезагрузка значения для Timer 0// Период Timer0 = 0.125 мкс = 8 МГц / 64.// 2 мс = 8 * 250 мкс// 5 = 255-250
TCNT0=5;
state_counter++;
output_change =1; // Это флаг для цикла mainif (state_counter >7)
state_counter =0;
} //******************** Здесь находится главный цикл программы ***************************void main(void)
{
// Вызов функции инициализации (описание см. ниже):
initialization();
// Разрешить все прерывания:
#asm("sei")
// Дальше идет бесконечный цикл с оператором switch, который генерирует// постоянное обновление LCD.#if LCD_Driverwhile (1)
{
if(output_change)
{
output_change =0;
// state_counter генерирует сигналы 4 выходов COM через PORTD, // каждый имеет состояния HIGH и LOW.switch (state_counter)
{
case0:
//получение бит A & B цифр:
segs_out = (segment_table[LCD_d_1]&0x03); // единиц
segs_out = segs_out | ((segment_table[LCD_d_2]&0x03)*4); // десятков
segs_out = segs_out | ((segment_table[LCD_d_3]&0x03)*16); // сотен
segs_out = segs_out | ((segment_table[LCD_d_4]&0x03)*64); // тысяч
DDRD =0;
PORTD =0x00;
PORTB = segs_out;
DDRB =0xFF; // всегда включено
DDRD =0x01; //COM1 выставляется в состояние LOWbreak;
case1:
PORTD =0x01;
PORTB = segs_out ^0xFF; // инверсия выходов сегментов
DDRB =0xFF; // всегда включено
DDRD =0x01; //COM1 выставляется в состояние HIGHbreak;
case2:
//получение бит A & B цифр:
segs_out = (segment_table[LCD_d_1]&0x0C)/4; // единиц
segs_out = segs_out | (segment_table[LCD_d_2]&0x0C); // десятков
segs_out = segs_out | ((segment_table[LCD_d_3]&0x0C)*4); // сотен
segs_out = segs_out | ((segment_table[LCD_d_4]&0x0C)*16); // тысяч
DDRD =0;
PORTD =0x00;
PORTB = segs_out;
DDRB =0xFF; // всегда включено
DDRD =0x02; //COM2 выставляется в состояние LOWbreak;
case3:
PORTD =0x02;
PORTB = segs_out ^0xFF; // инверсия выходов сегментов
DDRB =0xFF;
DDRD =0x02; //COM2 выставляется в состояние HIGHbreak;
case4:
//получение бит A & B цифр:
segs_out = (segment_table[LCD_d_1]&0x30)/16; // единиц
segs_out = segs_out | ((segment_table[LCD_d_2]&0x30)/4); // десятков
segs_out = segs_out | (segment_table[LCD_d_3]&0x30); // сотен
segs_out = segs_out | ((segment_table[LCD_d_4]&0x30)*4); // тысяч
DDRD =0;
PORTD =0x00;
PORTB = segs_out;
DDRB =0xFF;
DDRD =0x04; //COM3 выставляется в состояние LOWbreak;
case5:
PORTD =0x04;
PORTB = segs_out ^0xFF; // инверсия выходов сегментов
DDRB =0xFF;
DDRD =0x04; //COM3 выставляется в состояние HIGHbreak;
case6:
//получение бит A & B цифр:
segs_out = (segment_table[LCD_d_1]&0xC0)/64; // единиц
segs_out = segs_out | ((segment_table[LCD_d_2]&0xC0)/16); // десятков
segs_out = segs_out | ((segment_table[LCD_d_3]&0xC0)/4); // сотен
segs_out = segs_out | (segment_table[LCD_d_4]&0xC0); // тысяч
DDRD =0;
PORTD =0x00;
PORTB = segs_out;
DDRB =0xFF;
DDRD =0x08; //COM4 выставляется в состояние LOWbreak;
case7:
PORTD =0x08;
PORTB = segs_out ^0xFF; // инверсия выходов сегментов
DDRB =0xFF;
DDRD =0x08; //COM4 выставляется в состояние HIGHbreak;
default: DDRB =0;
DDRD =0; // COM1-COM4 отключены (входы)
}//switch (state_counter)// Инкремент счетчика для отсчета единиц 0.1 сек
Pt_1_sec++;
//Реализация BCD счетчика на цифровом индикаторе LCD 3 1/2if (Pt_1_sec >=50)
{
//.1 сек
Pt_1_sec =0;
LCD_d_1++;
if (LCD_d_1 >=10)
{
LCD_d_1 =0;
LCD_d_2++;
}
if (LCD_d_2 >=10)
{
LCD_d_2 =0;
LCD_d_3++;
}
if (LCD_d_3 >=10)
{
LCD_d_3 =0;
LCD_d_4++;
}
}
}//if(output_change)
}//while(1)#endif
} //************************ Функция инициализации ***********************void initialization(void)
{
// Коэффициент деления частоты кварцевого тактового генератора: 1
CLKPR=0x80;
CLKPR=0x00;
//DDRB=0x7F; //7 выходов сегмента// Инициализация Timer/Counter 0
TCCR0A=0x00;
TCCR0B=0x03; // = 8МГц/64 3/22/05
TCNT0=0xC1;
// Инициализация внешних прерываний (все выключено)// INT0: выключено// INT1: выключено// Прерывание на любом изменении выводов PCINT0-7: отключено// Прерывание на любом изменении выводов PCINT8-14: отключено// Прерывание на любом изменении выводов PCINT16-23: отключено
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;
// Инициализация прерываний Timer/Counter 0:
TIMSK0=0x01;
// Инициализация прерываний Timer/Counter 1:
TIMSK1=0x00;
// Инициализация прерываний Timer/Counter 2:
TIMSK2=0x00;
}
[Ссылки]
1. AVR340: Direct Driving of LCD Using General Purpose IO using tinyAVR and megaAVR devices site:atmel.com. 2. AVR065: LCD Driver for the STK502 site:atmel.com. 3. 141223AVR340.zip - код драйвера, документация.