Найти тему
Евгений Бывшев

Программирование микроконтроллеров. Часть 4-1

Делаем вольтметр

   В третьей части этого курса я показал, как можно использовать встроенный в микроконтроллер АЦП. Сегодня я покажу как подключить LCD 16-и символьный 2-х строчный индикатор и используя материал из предыдущей части отобразить на индикаторе измеренное напряжение на входе АЦП, т.е. сделаем простейший вольтметр.

  В Proteus я зашел в библиотеку компонентов, нашел в ней дисплей LM016L и подключил его к схеме, взятой из предыдущей части, следующим образом:

И сохранил проект.

Если сейчас запустить симуляцию, то LCD дисплей будет просто светиться и ничего не отображать. Так и должно быть, ведь для него не написано еще ни одной строчки в программе и микроконтроллер не знает, что с ним делать. Для того чтобы дисплей заработал нужно запустить AtmelStudio, создать новый проект и скопировать весь код из предыдущего урока.

В функции port_ini() нужно добавить порт к которому будет подключен дисплей. Так как режим у нас 4-битный, то нам вполне хватит ножек, даже останутся. В данном случае – это PORTB. Делается это следующим образом:

DDRB=0xFF;
PORTB=0x00;

Для инициализации дисплея необходимо через определенные промежутки три раза передать двоичное число 11, и тогда контроллер дисплея "поймёт", что от него хотят именно того, чтобы он общался с нами в 4-х битном режиме, и чтобы он в данный режим как-то переключился. Напишем функцию инициализации дисплея. Код данной функции нужно разместим перед функцией main()

void LCD_ini(void)
{
}

И эту функцию нужно вызвать в функции main().

port_ini(); //Инициализируем порты
ADC_ini(); //Инициализируем АЦП
LCD_ini(); //Инициализируем АЦП

В теле данной функции, судя по диаграмме инициализации из технической документации необходимо подождать сначала не менее 15 миллисекунд

void LCD_ini(void)
{
_delay_ms(15); //Ждем 15 мс

Дальше необходимо как-то передавать контроллеру дисплея байты. Но так как байты сразу передать не получиться, ибо у нас даже ножки данных половина не подключены, то передавать нужно полубайты. Так как это не совсем простой процесс, предлагаю завести под это дело ещё одну функцию и назвать её send_half_byte. В качестве входного параметра ей нужно передавать unsigned char, так как у нас 4-битных переменных не бывает, мы просто не будем использовать в данной переменной первые 4 бита.

void send_half_byte(unsigned char c)
{
}

Сначала необходимо сдвинуть наш входной аргумент влево на 4 бита, так как приходиться работать со старшими разрядами шины (4-7). И не просто сдвинуть, а этому же аргументу и присвоить, написав после операции сдвига знак равно.

void send_half_byte(unsigned char c)
{
c<<=4;

Сначала я напишу несколько макроподстановок для удобства работы с линиями E и RS, то есть их включение и выключение.

#include <util/delay.h>
#define e1 PORTB|=0b00001000 //установка линии E в 1
#define e0 PORTB&=0b11110111 //установка линии E в 0
#define rs1 PORTB|=0b00000100 //установка линии RS в 1 (данные)
#define rs0 PORTB&=0b11111011 //установка линии RS в 0 (команда)

В функции передачи полубайта необходимо включить линию E, для того чтобы сказать дисплею, что мы будем передавать команду. Затем подождать 50 микросекунд, стереть информацию на ножках 4-7 порта B, остальные ножки трогать не будем, потом установить нужные биты на шине данных из переменной c, в которой находятся данные биты. Потом отключить линию E и подождать ещё 50 микросекунд.

Теперь весь код этой функции будет выглядеть так:

void send_hal_fbyte(unsigned char c)
{
c<<=4;
e1; //включаем линию Е
_delay_us(50);
PORTB&=0b00001111; //стираем информацию на входах 4-7, остальное не трогаем
PORTB|=c;
e0; //выключаем линию Е
_delay_us(50);
}

Теперь необходимо в контроллер дисплея передать двоичное число 11 три раза в функции LCD_ini, также применяя соответствующие задержки, взяв их из даташита дисплея. В данном примере это дисплей модели LCD-1602.

_delay_ms(15); //Ждем 15 мс
send_half_byte(0b00000011);
_delay_ms(4);
send_half_byte(0b00000011);
_delay_us(100);
send_half_byte(0b00000011);
_delay_ms(1);

Дальше передаём двоичное число 10 таким же образом

_delay_ms(1);
send_half_byte(0b00000010);
_delay_ms(1);


Контроллер дисплея должен "догадаться", что мы его переключаем в 4 битный режим, и следующая команда будет же с полноправным байтом, переданным поочередно и причем в этой команде уже будет конкретная команда перевода в 4-битный режим. Но для полноправных команд необходимо написать другую функцию send_byte, расположив ее код после функции send_half_byte.

void send_byte(unsigned char c, unsigned char mode)
{
}

В эту функцию необходимо передавать уже два аргумента, один — это данные, а другой - это режим, то есть мы здесь будем говорить, данные мы будем передавать или команду.
C помощью условия необходимо узнать, команда в нашу функцию пришла или данные, и среагировать на это установкой в соответствующее состояние шины RS

void send_byte(unsigned char c, unsigned char mode)
{
if (mode==0) rs0;
else rs1;

Добавим ещё одну переменную

else rs1;
unsigned char hc=0;

Сдвинув вправо на 4 пункта наш байт отправим результат в данную переменную. Тем самым в младшую тетраду байта поместим старшую

unsigned char hc=0;
hc=c>>4;

Передадим сначала её в функцию send_half_byte, а затем и саму нетронутую переменную c. Не важно, что будет в её старшей части, так как функция send_half_byte работает только с младшей тетрадой.

hc=c>>4;
send_half_byte(hc); send_half_byte(c);
}

Теперь применяя вышенаписанную функцию необходимо передать следующий байт, взятый из даташита в функции LCD_ini

_delay_ms(1);
send_byte(0b00101000, 0); //4бит-режим и 2 линии
_delay_ms(1);


Передадем следующий байт

_delay_ms(1);
send_byte(0b00001100, 0); //включаем изображение на дисплее, курсоры никакие не включаем
_delay_ms(1);

Передаём последнюю команду в функции инициализации дисплея

_delay_ms(1);
send_byte(0b00000110, 0); //курсор (хоть он у нас и невидимый) будет двигаться влево
_delay_ms(1);
}

Весь код функции будет выглядеть так:

void LCD_ini(void)
{
_delay_ms(15); //Ждем 15 мс
send_half_byte(0b00000011);
_delay_ms(4);
send_half_byte(0b00000011);
_delay_us(100);
send_half_byte(0b00000011);
_delay_ms(1);
send_half_byte(0b00000010);
_delay_ms(1);
send_byte(0b00101000, 0); //4бит-режим и 2 линии
_delay_ms(1);
send_byte(0b00001100, 0); //включаем изображение на дисплее, курсоры никакие не включаем
_delay_ms(1);
send_byte(0b00000110, 0); //курсор (хоть он у нас и невидимый) будет двигаться влево
_delay_ms(1);
}

На этом инициализация дисплея закончена.
В следующей части урока попробуем поработать с выводом данных на экран дисплея.