Воскресенье, 22.07.2018
Интеллектуальные системы управления
Меню сайта
Категории раздела
Эксперименты [3]
Программирование микроконтроллеров PIC [7]
Программирование микроконтроллеров STM32 [0]
Программирование микроконтроллеров STM8 [0]
Программирование микроконтроллеров AVR [0]
Программирование микроконтроллеров MSP430 [0]
Программирование ПЛИС ALTERA [0]
Облако тегов
Главная » Статьи » Программирование микроконтроллеров » Программирование микроконтроллеров PIC

Осваиваем вывод информации на ЖК-экран

 С таймерами и прерываниями мы понемногу разобрались, сопряжение с компьютером тоже освоили, теперь не мешало бы научить наш микроконтроллер как-то обозначать свои действия при отладке и нормальной работе. Можно конечно повесить на порт светодиоды и моргать ими в соответствии с алгоритмом, но это ни всегда удобно, особенно если действий много. Напишешь программу, а потом сидишь и втыкаешь как в песне "и кто его знает чего он моргает..." Так что дисплей, каким бы он ни был всегда дисплей, поэтому завязываем с предисловием и приступаем.

Разновидностей дисплеев очень много, они отличаются друг от друга: фирмой изготовителем, принципом работы, предназначением, количеством строк, символов в строке и т.д. и т.п..  В основном дисплеи делятся на графические и знакосинтезирующие. Есть еще сегментные  ЖК индикаторы и цветные ЖК дисплеи, но принцип у них уже немного другой, поэтому о них позже. Нас в первую очередь интересует знакосинтезирующий дисплей. Как уже понятно из названия он знаковый - показывает символы и знаки и предназначен только для этого. Дисплей у нас будет простенький, две строки в каждой по 16 символов, если не делать читалку для книг или КПК то двух строчек всегда хватает за глаза. Есть несколько фирм, изготавливающих такие дисплеи, в принципе, команды у них похожи, если не сказать, одинаковые, поэтому все написанное ниже будет справедливо для многих других дисплеев. Мы с вами будем пробовать индикатор самой известной фирмы WINSTAR WH1602A. WH1602A это знакосинтезирующий 2-х строчный жк-дисплей, с 16-ю символами в каждой строке.

Подключается монитор через 16-ть контактов (уф... минут 5 убил на формулировку). Из которых 2-а контакта отводится под питание, 3-и под управление, 8-мь для параллельной 8-ми битной шины данных, по которой передаются данные и команды, и наконец еще 3-и вывода используются для регулировки контраста и питания подсветки дисплея. В таблице ниже подробно расписано что куда.

Номер

Название

Описание

1GND
Общий вывод источника питания или проще "Земля"
2VCCНапряжение питания +5V
3VOВывод регулировки контраста
4RSВыбор регистра контроллера (1 - регистр данных, 0 - регистр управления)
5R/WВыбор режима обмена (0 - запись, 1 - чтение)
6ENСтробирующий импульс
7D0Шина данных
8D1
9D2
10D3
11D4
12D5
13D6
14D7
15LED+Питание подсветки +5V
16LED-Общий вывод питания подсветки

Схема подключения проще не бывает:

Питание дисплея 3,3В или 5В, как правило это указывается в характеристиках. Вывод контрастности подключается как правило через переменный резистор для изменения яркости и четкости изображения от "вообще ничего не видно" (при 0В) и "глаза, мои глаза" (при VCC). Иногда чтобы не заморачиваться вывод контрастности вообще соединяют с питанием. Выводы данных вместе с выводами управления соединяются напрямую с выводами микроконтроллера. Если нет нужды что-то считывать с дисплея (например флаг состояния и выполнения команд) то вывод R/W часто сажают на землю и используется только запись на дисплей.

Изучив все мелкие детали и особенности работы с дисплеями приступим к коду. Последовательность передачи информации на дисплей следующая:

1. Устанавливаем бит RS в 1 или сбрасываем в 0 в зависимости от того что у нас будет данные или команда (0 - команда, 1 - данные);
2. Выводим символ на шину D7…D0;
3. Устанавливаем бит строба EN в 1;
4. Делаем задержку в программе на 2 млС; 
5. Сбрасываем бит строба EN в 0;
6. Делаем задежку на 40млС.

Последовательсть действий всегда одинакова вне зависимости от того, что мы передаем данные или команду. Если у вас пошло что-то не так и дисплей никак не реагирует то скорее всего у вас не совсем правильно реализована задержка. Для себя я в программе написал несколько функций:

функция передачи команды или данных на дисплей

Code
void lcd_printf(unsigned char cBuffer, cInit)
{
    Display_RS=cInit;
    Display_Data=cBuffer;
    Display_EN=1;
    time_delay(2);
    Display_EN=0;
    time_delay(40);
}

и функция временной задержки, реализованной через таймер TMR0

Code
void time_delay(unsigned char cDelay)
{
    cDelayCount=cDelay;
    TMR0ON=1;
    while(cDelayCount);
    TMR0ON=0;
}

Чтобы было удобнее работать и программа стала более читабельной я заменил стандартные определения выводов микроконтроллера своими:

Code
#define Display_Data    PORTD
#define Display_EN       RB0
#define Display_RW      RB1
#define Display_RS       RB2

И сразу стало понятно куда что идет.

В самом начале, перед использованием дисплея, его необходимо инициализировать как и собственно любой контроллер.

Инициализация выполняется следующим образом:

1. Делаем задежку на 40млС. Чтобы дисплей успел включиться (контроллер внутри дисплея жутко тормозной);
2. Передаем команду настройки дисплея - Function set: 0b00111000;
3. Передаем команду включения дисплея - Display ON/OFF: 0b00001111;
4. Передаем команду очистки экрана - Clear Display: 0b00000001; 
5. Делаем задежку на 2млС ;
6. Передаем команду направления перемещения курсора - Entry Mode Set: 0b00000110.

Говоря "передаем команду" я имею ввиду всю ту последовательность действий при передаче данных указанную выше.

Теперь дисплей окончательно готов к работе и можно попробовать вывести что нибудь.

Code
lcd_printf('L', 1);
lcd_printf('C', 1);
lcd_printf('D', 1);
lcd_printf(' ', 1);
lcd_printf('t', 1);
lcd_printf('e', 1);
lcd_printf('s', 1);
lcd_printf('t', 1);
lcd_printf('!', 1);

Теперь все это надо добавить к нашей заготовке программы, подключить все необходимые библиотеки и настроить порт D и порт B на цифровой выход. Собираем простенькую схемку в PROTEUS-е, можно даже переделать старые проекты.

Схема:

Текст программы:

Я его несколько переделал, вместо одной функции передачи данных я сделал две фиксированные, для передачи текста и для передачи команды. Кроме этого я задействовал USART для последующей реализации передачи данных с компьютера на наш дисплей.


Code
#include <pic18.h>

#define Display_Data PORTD
#define Display_EN RB0
#define Display_RW RB1
#define Display_RS RB2

// Доп.настройки ядра микроконтроллера
__CONFIG(1, HS & FCMEN & IESOEN);
__CONFIG(2, PWRTDIS & BORDIS & WDTDIS);
__CONFIG(3, CCP2RC1 & PBDIGITAL & LPT1DIS & MCLREN);
__CONFIG(4, XINSTEN & STVRDIS & LVPDIS & DEBUGDIS);
__CONFIG(5, UNPROTECT);
__CONFIG(6, UNPROTECT);
__CONFIG(7, UNPROTECT);

bit bBufferFlag;
bit bIncomFlag;
char cCount_i=0;
char cCount_j=0;
unsigned char cIndexSymbol=0;
unsigned char cTempBuffer=0;
unsigned char cBuffer[8]={0,0,0,0,0,0,0,0};
unsigned char cDelayCount=0;
unsigned char cDelaySet=0;

void time_delay(unsigned char cDelay);
void scanf_buffer(void);
void printf_buffer(const char cTempOut);
void lcd_command(unsigned char cCommand);
void lcd_data(unsigned char cData);
void lcd_initialisation(void);
void start_initialisation(void);

void interrupt high_main_int(void)
{
if(TMR0IF)
{
cDelayCount--;
TMR0L=0xEC;
TMR0IF=0;
}
}

void time_delay(unsigned char cDelay)
{
cDelayCount=cDelay;
TMR0ON=1;
while(cDelayCount);
TMR0ON=0;
}

void scanf_buffer(void)
{
switch(cBuffer[0])
{
case 0x01: lcd_command(0b00000001);
lcd_data(cBuffer[1]);
break;
case 0x02: lcd_command(0b00000001);
break;
default: break;
}
bBufferFlag=0;
}

void printf_buffer(const char cTempOut)
{
TXREG=0x00;
time_delay(5);
TXREG=cTempOut;
time_delay(5);
TXREG=cTempOut;
time_delay(5);
TXREG=0xFF;
}

void lcd_command(unsigned char cCommand)
{
Display_RS=0;
Display_Data=cCommand;
Display_EN=1;
time_delay(2);
Display_EN=0;
time_delay(40);
}

void lcd_data(unsigned char cData)
{
Display_RS=1;
Display_Data=cData;
Display_EN=1;
time_delay(2);
Display_EN=0;
time_delay(40);
}

void lcd_initialisation(void)
{
time_delay(40);
lcd_command(0b00111000);
lcd_command(0b00001111);
lcd_command(0b00000001);
time_delay(2);
lcd_command(0b00000110);
}

void start_initialisation(void)
{
// Настройка регистров порта A
LATA=0b00000000; // Настройка защелки порта A
TRISA=0b00000000; // Настройка порта A на цифровой выход
PORTA=0b00000000; // Логический уровень порта A

// Настройка регистров порта В
LATB=0b00000000; // Настройка защелки порта В
TRISB=0b00000000; // Настройка порта В на цифровой выход и вход
PORTB=0b00000000; // Логический уровень порта В

// Настройка регистров порта C
LATC=0b00000000; // Настройка защелки порта C
TRISC=0b11000000; // Настройка порта C на цифровой выход и USART
PORTC=0b00000000; // Логический уровень порта C

// Настройка регистров порта D
LATD=0b00000000; // Настройка защелки порта D
TRISD=0b00000000; // Настройка порта D на цифровой выход
PORTD=0b00000000; // Логический уровень порта D

// Настройка регистров порта E
LATE=0b00000000; // Настройка защелки порта E
TRISE=0b00000000; // Настройка порта E на цифровой выход
PORTE=0b00000000; // Логический уровень порта E

// Настройка таймера TMR0
T0CON=0b00000000; // Регистр управления таймером
T08BIT=1; // Режим 8-бит таймер/счетчик
T0PS2=1; // Прескалер 256
T0PS1=1; //
T0PS0=1; //
TMR0L=0xEC; // Временная задержка 1млС при частоте 20МГц

// Настройка регистров приемника и передатчика USART
TXSTA=0b00000000; // Регистр передатчика USART
TXEN=1; // Разрешение передачи USART
BRGH=1; // Выбор высокоскоростного режима
RCSTA=0b00000000; // Регистр приемника USART
SPEN=1; // Разрешение работы последовательного порта
CREN=1; // Разрешение приема USART
BAUDCON=0b00000000; // Регистр автоопределения скорости приема
SPBRG=129; // Скорость работы порта передачи (20МГц, 9600 бод)

// Настройка регистров прерываний и приоритетов
RCON=0b00000000; // Регистр управления прерываниями 0
IPEN=1; // Разрешение приоритетных прерываний
INTCON=0b00000000; // Регистр управления прерываниями 1
GIEH=1; // Разрешение глобальных прерываний с высоким приоритетом
GIEL=1; // Разрешение глобальных прерываний с низким приоритетом
TMR0IE=1; // Разрешение прерываний от таймера TMR0
INTCON2=0b00000000; // Регистр управления прерываниями 2
TMR0IP=1; // Высокий приоритет прерываний от таймера TMR0
INTCON3=0b00000000; // Регистр управления прерываниями 3
PIR1=0b00000000; // Регистр флагов периферийных прерываний 1
PIR2=0b00000000; // Регистр флагов периферийных прерываний 2
PIE1=0b00000000; // Регистр разрешения периферийных прерываний 1
RCIE=1; // Разрешение прерываний от приемника USART
PIE2=0b00000000; // Регистр разрешения периферийных прерываний 2
IPR1=0b00000000; // Регистр приоритета периферийных прерываний 1
IPR2=0b00000000; // Регистр приоритета периферийных прерываний 2
}

// Основная функция программы
void main(void)
{
start_initialisation();
lcd_initialisation();
lcd_command(0b00000001);
lcd_data('W');
lcd_data('e');
lcd_data('l');
lcd_data('l');
lcd_data('c');
lcd_data('o');
lcd_data('m');
lcd_data('e');
lcd_data('!');
lcd_command(0b11000000);
lcd_data('T');
lcd_data('e');
lcd_data('s');
lcd_data('t');
lcd_data('-');
lcd_data('m');
lcd_data('o');
lcd_data('d');
lcd_data('e');
lcd_data('.');
while(1)
{
if(bBufferFlag)
{
scanf_buffer();
}
}
}

После включения симуляции на дисплее должно появиться "LCD test!".

Собственно для начала очень даже не плохо, но дисплей то у нас двухстрочный, а мы пока писали на одной строке и если продолжить печатать дальше то курсор уйдет за пределы поля и ничего не будет видно пока позиция курсора не станет равной адресу первого символа второй строки. Вообще позицию курсора можно задавать и писать слева, справа и даже по середине строки, а так же переходить на другую сторку (команда 0b11000000), но обо всем об этом мы поговорим в следующей статье, а заодно в ней мы будем использовать вывод R/W, чтобы читать с дисплея его флаги и состояние.

Категория: Программирование микроконтроллеров PIC | Добавил: Anubis (22.04.2012)
Просмотров: 8050 | Комментарии: 1 | Рейтинг: 5.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Copyright MyCorp © 2018