Как подключить 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
В схеме нет токоограничивающего резистора подсветки (≈100 Ом). Без него подсветке индикатора быстро придёт конец.
Огромное спасибо за код lcd. Нужно было проверить дисплей и на атмеге8 запустился с 1го раза.
Аккуратный, понятный код. Всё сразу заработало как надо. Спасибо вам!
Спасибо большое из 2021, всё заработало тут же!
маркетплейс аккаунтов соцсетей продать аккаунт
заработок на аккаунтах магазин аккаунтов
купить аккаунт гарантия при продаже аккаунтов
Accounts market Buy and Sell Accounts
Purchase Ready-Made Accounts Accounts for Sale
Account Acquisition Account Acquisition
Account market Secure Account Purchasing Platform
Database of Accounts for Sale Accounts marketplace
accounts marketplace buy account
accounts for sale secure account purchasing platform