Как подключить LСD дисплей на базе HD44780 к ATmega16 или его цифровой аналог LM016L 16×2 в Proteus
При работе с Arduino, Atmega, PIC или с другим микроконтроллером часто возникает необходимость вывести какие-либо текстовые данные на дисплей. С цифрами проще, можно использовать 7 сегментный индикатор, а для вывода текста необходимо использовать LCD-дисплеи (ЖКИ). В данной статьи мы рассмотрим подключение LCD-дисплея на базе контроллера HD44780 к ATmega16.
Для подключения LCD-дисплея на базе HD44780 к ATmega16 нам нужно использовать 12 выводов, можно и все 16, но не на всех контроллерах это удастся сделать, ибо физически невозможно, а программно — да:
- 1 — Vss, земля -> GND
- 2 — Vdd, питание -> +5 В
- 3 — Vo (Vee), управление контрастностью напряжением -> выход потенциометра
- 4 — RS, выбор регистра
- 5 — R/W, чтение/запись -> земля (режим записи)
- 6 — E, он же Enable, cтроб по спаду
- 7-10 — DB0-DB3, младшие биты 8-битного интерфейса; не подключены
- 11-14 — DB4-DB7, старшие биты интерфейса
- 15 — A, питание для подсветки -> +5 В
- 16 — K, земля для подсветки -> GND
Пример программы в Atmel Studio 7
LCD.h
#ifndef LCD_H_ #define LCD_H_ #define LCDDATAPORT PORTB // Порт и пины, #define LCDDATADDR DDRB // к которым подключены #define LCDDATAPIN PINB // сигналы D4-D7. #define LCD_D4 3 #define LCD_D5 4 #define LCD_D6 5 #define LCD_D7 6 #define LCDCONTROLPORT PORTB // Порт и пины, #define LCDCONTROLDDR DDRB // к которым подключены #define LCD_RS 0 // сигналы RS, RW и E. #define LCD_RW 1 #define LCD_E 2 #define LCD_STROBEDELAY_US 5 // Задержка строба #define LCD_COMMAND 0 #define LCD_DATA 1 #define LCD_CURSOR_OFF 0 #define LCD_CURSOR_ON 2 #define LCD_CURSOR_BLINK 3 #define LCD_DISPLAY_OFF 0 #define LCD_DISPLAY_ON 4 #define LCD_SCROLL_LEFT 0 #define LCD_SCROLL_RIGHT 4 #define LCD_STROBDOWN 0 #define LCD_STROBUP 1 #define DELAY 1 void lcdSendNibble(char byte, char state); char lcdGetNibble(char state); char lcdRawGetByte(char state); void lcdRawSendByte(char byte, char state); char lcdIsBusy(void); void lcdInit(void); void lcdSetCursor(char cursor); void lcdSetDisplay(char state); void lcdClear(void); void lcdGotoXY(char str, char col); void lcdDisplayScroll(char pos, char dir); void lcdPuts(char *str); void lcdPutsf(char *str); void lcdPutse(uint8_t *str); void lcdLoadCharacter(char code, char *pattern); void lcdLoadCharacterf(char code, char *pattern); void lcdLoadCharactere(char code, char *pattern); #endif /* LCD_H_ */
LCD.c
// Подключение LCD на базе HD44780 к ATmega16 (LM016L LCD 16x2)
// сайт http://micro-pi.ru
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include "LCD.h"
/*
Отправляет младшую половину байта byte в LCD. Если state == 0,
то передаётся как команда, если нет, то как данные.
*/
void lcdSendNibble(char byte, char state) {
// Пины управления - на выход
LCDCONTROLDDR |= 1<<LCD_RS | 1<<LCD_RW | 1<<LCD_E;
// Пины данных - на выход
LCDDATADDR |= 1<<LCD_D4 | 1<<LCD_D5 | 1<<LCD_D6 | 1<<LCD_D7;
// Режим записи, RW = 0
LCDCONTROLPORT &= ~(1<<LCD_RW);
// Устанавливаем 1 в RS
if (state) {
// если отдаём данные
LCDCONTROLPORT |= 1<<LCD_RS;
} else {
LCDCONTROLPORT &= ~(1<<LCD_RS);
}
// Взводим строб
LCDCONTROLPORT |= 1<<LCD_E;
// Обнуляем пины данных
LCDDATAPORT &= ~(1<<LCD_D4 | 1<<LCD_D5 | 1<<LCD_D6 | 1<<LCD_D7);
// Записываем младшую
if (byte & (1<<3)) {
// половину байта
LCDDATAPORT |= 1<<LCD_D7;
}
// byte в порт вывода данных
if (byte & (1<<2)) {
LCDDATAPORT |= 1<<LCD_D6;
}
if (byte & (1<<1)) {
LCDDATAPORT |= 1<<LCD_D5;
}
if (byte & (1<<0)) {
LCDDATAPORT |= 1<<LCD_D4;
}
// Пауза
_delay_us(LCD_STROBEDELAY_US);
// Опускаем строб. Полубайт ушёл
LCDCONTROLPORT &= ~(1<<LCD_E);
}
/*
Читает половину байта из LCD. Если state == 0, то читается команда,
если нет, то данные.
*/
char lcdGetNibble(char state) {
char temp = 0;
// Пины управления - на выход
LCDCONTROLDDR |= 1<<LCD_RS | 1<<LCD_RW | 1<<LCD_E;
// Режим чтения
LCDCONTROLPORT |= 1<<LCD_RW;
// Устанавливаем 1 в RS
if (state) {
// если получаем данные
LCDCONTROLPORT |=(1<<LCD_RS);
} else {
LCDCONTROLPORT &= ~(1<<LCD_RS);
}
// Взводим строб
LCDCONTROLPORT |= 1<<LCD_E;
// Пины данных - на вход
LCDDATADDR &= ~(1<<LCD_D4 | 1<<LCD_D5 | 1<<LCD_D6 | 1<<LCD_D7);
// с подтяжкой
LCDDATAPORT |= 1<<LCD_D4 | 1<<LCD_D5 | 1<<LCD_D6 | 1<<LCD_D7;
// Пауза
_delay_us(LCD_STROBEDELAY_US);
// Опускаем строб
LCDCONTROLPORT &= ~(1<<LCD_E);
// Читаем пины
if (LCDDATAPIN & (1<<LCD_D7)) {
// во временную переменную
temp |= 1<<3;
}
if (LCDDATAPIN & (1<<LCD_D6)) {
temp |= 1<<2;
}
if (LCDDATAPIN & (1<<LCD_D5)) {
temp |= 1<<1;
}
if (LCDDATAPIN & (1<<LCD_D4)) {
temp |= 1<<0;
}
// возвращаем прочитанное
return temp;
}
/*
Читает байт из LCD. Если state == 0, то читается команда,
если нет, то данные.
*/
char lcdRawGetByte(char state) {
char temp = 0;
temp |= lcdGetNibble(state);
temp = temp<<4;
temp |= lcdGetNibble(state);
return temp;
}
/*
Отравляет байт в LCD. Если state == 0, то передаётся как команда,
если нет, то как данные.
*/
void lcdRawSendByte(char byte, char state) {
lcdSendNibble((byte>>4), state);
lcdSendNibble(byte,state);
}
/*
Читает состояние LCD, возвращает 0xff, если флаг занятости установлен,
и 0x00, если нет.
*/
char lcdIsBusy(void) {
/* TODO
if (lcdRawGetByte(LCD_COMMAND) & (1<<7))
return 0xff;
else
return 0x00;
*/
_delay_ms(DELAY);
return 0x00;
}
/*
Выполняет начальную инициализацию дисплея. Четырёхбитный режим.
*/
void lcdInit(void) {
while (lcdIsBusy()) ;
lcdSendNibble(0b0010, LCD_COMMAND);
while (lcdIsBusy()) ;
lcdRawSendByte(0b00101000, LCD_COMMAND);
while (lcdIsBusy()) ;
lcdRawSendByte(0b00000001, LCD_COMMAND);
while (lcdIsBusy()) ;
lcdRawSendByte(0b00000110, LCD_COMMAND);
while (lcdIsBusy()) ;
lcdRawSendByte(0b00001100, LCD_COMMAND);
}
/*
Устанавливает режим курсора: 0 - выключен, 2 - включен, 3 - моргает.
Если на момент запуска LCD был выключен (lcdSetDisplay), то он будет включен.
*/
void lcdSetCursor(char cursor) {
while (lcdIsBusy());
lcdRawSendByte((0b00001100 | cursor), LCD_COMMAND);
}
/*
Включает или выключает отображение символов LCD.
При каждом вызове выключает курсор.
*/
void lcdSetDisplay(char state) {
while (lcdIsBusy());
lcdRawSendByte((0b00001000 | state), LCD_COMMAND);
}
/*
Очищает LCD.
*/
void lcdClear(void) {
while (lcdIsBusy()) ;
lcdRawSendByte(0b00000001, LCD_COMMAND);
}
/*
Устанавливает курсор в заданную позицию.
*/
void lcdGotoXY(char str, char col) {
while (lcdIsBusy());
lcdRawSendByte((0b10000000 | ((0x40 * str) + col)), LCD_COMMAND);
}
/*
Сдвигает область отображения на указанное количество символов
вправо или влево.
*/
void lcdDisplayScroll(char pos, char dir) {
while (pos){
while (lcdIsBusy()) ;
lcdRawSendByte((0b00011000 | dir), LCD_COMMAND);
pos--;
}
}
/*
Выводит строку из RAM в позицию курсора.
*/
void lcdPuts(char *str) {
while (*str){
while (lcdIsBusy()) ;
lcdRawSendByte(*str++, LCD_DATA);
}
}
/*
Выводит строку из flash в позицию курсора.
*/
void lcdPutsf(char *str) {
while (pgm_read_byte(str)){
while (lcdIsBusy()) ;
lcdRawSendByte(pgm_read_byte(str++), LCD_DATA);
}
}
/*
Выводит строку из eeprom в позицию курсора.
*/
void lcdPutse(uint8_t *str) {
while (eeprom_read_byte(str)){
while (lcdIsBusy()) ;
lcdRawSendByte((char)(eeprom_read_byte(str++)), LCD_DATA);
}
}
/*
Загружает символ в знакогенератор.
*/
void lcdLoadCharacter(char code, char *pattern) {
while (lcdIsBusy());
lcdRawSendByte((code<<3) | 0b01000000, LCD_COMMAND);
for (char i = 0; i <= 7; i++){
while (lcdIsBusy()) ;
lcdRawSendByte(*pattern++, LCD_DATA);
}
while (lcdIsBusy());
lcdRawSendByte(0b10000000, LCD_COMMAND);
}
/*
Загружает символ из flash в знакогенератор.
*/
void lcdLoadCharacterf(char code, char *pattern) {
while (lcdIsBusy());
lcdRawSendByte((code<<3) | 0b01000000, LCD_COMMAND);
for (char i = 0; i <= 7; i++){
while (lcdIsBusy());
lcdRawSendByte(pgm_read_byte(pattern++), LCD_DATA);
}
while (lcdIsBusy());
lcdRawSendByte(0b10000000, LCD_COMMAND);
}
/*
Загружает символ из eeprom в знакогенератор.
*/
void lcdLoadCharactere(char code, char *pattern) {
while (lcdIsBusy());
lcdRawSendByte((code<<3) | 0b01000000, LCD_COMMAND);
for (char i = 0; i <= 7; i++){
while (lcdIsBusy()) ;
lcdRawSendByte(eeprom_read_byte(pattern++), LCD_DATA);
}
while (lcdIsBusy()) ;
lcdRawSendByte(0b10000000, LCD_COMMAND);
}
main.c
// Подключение LCD на базе HD44780 к ATmega16 (LM016L LCD 16x2)
// сайт http://micro-pi.ru
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include "LCD.h"
int main(void) {
_delay_ms(100);
lcdInit();
lcdClear();
lcdSetDisplay(LCD_DISPLAY_ON);
lcdSetCursor(LCD_CURSOR_OFF);
char text[17];
strcpy(text, " Hello World! ");
lcdGotoXY(0, 0);
lcdPuts(text);
strcpy(text, "site:micro-pi.ru");
lcdGotoXY(1, 0);
lcdPuts(text);
while (1);
}
Схема подключения LCD на базе HD44780 к ATmega16 в ISIS 7 Professional — Proteus. Симуляция.
Вам также потребуется добавить резистор номиналом 100-150 Ом к 15-му контакту, чтобы индикатор подсветки не вышел из строя.
Скачать
проект в Atmel Studio 7 LCD 16×2 ATmega16.7z
проект в Proteus LCD 16×2 ATmega16.DSN.7z

