Здесь показан код доработанной библиотеки [2], которая мне очень нравится, где решена проблема вывода на однострочный алфавитно-цифровой индикатор BC1601A1 (16x1).
[lcd.h]
#pragma once
#include < avr/io.h >
//Добавьте тут другие типы индикаторов, если нужна будет
//какая-то особая поддержка вывода.
#define GENERIC 1
#define BC1601A1 2
//Одно из определений ниже должно быть раскомментировано:
//#define LCDTYPE GENERIC
#define LCDTYPE BC1601A1
// Отредактируйте эти определения для подключения индикатора
// к определенным выводам AVR:
#define LCD_DDR DDRA
#define LCD_PORT PORTA
#define LCD_RS 5
#define LCD_RW 6
#define LCD_EN 4
#define LCD_D0 0
#define LCD_D1 1
#define LCD_D2 2
#define LCD_D3 3
//Количество символов в строке:
#define LCD_COL_COUNT 8
//Количество строк:
#define LCD_ROW_COUNT 2
// Остальное следует оставить без изменений:
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00
void lcd_init(void);
void lcd_command(uint8_t command);
void lcd_write(uint8_t value);
void lcd_on(void);
void lcd_off(void);
void lcd_clear(void);
void lcd_return_home(void);
void lcd_enable_blinking(void);
void lcd_disable_blinking(void);
void lcd_enable_cursor(void);
void lcd_disable_cursor(void);
void lcd_scroll_left(void);
void lcd_scroll_right(void);
void lcd_set_left_to_right(void);
void lcd_set_right_to_left(void);
void lcd_enable_autoscroll(void);
void lcd_disable_autoscroll(void);
void lcd_create_char(uint8_t location, uint8_t *charmap);
void lcd_set_cursor(uint8_t col, uint8_t row);
void lcd_puts(char *string);
void lcd_printf(char *format, ...);
[lcd.c]
#include "lcd.h"
#include < stdarg.h >
#include < stdio.h >
#include < util/delay.h >
#include "ANSI1251-C0-FF.h"
static uint8_t lcd_displayparams;
#if (LCDTYPE==GENERIC)
static char lcd_buffer[LCD_COL_COUNT + 1];
#elif (LCDTYPE==BC1601A1)
static uint8_t charcnt = 0;
static char lcd_buffer[LCD_COL_COUNT*LCD_ROW_COUNT + 1];
#else
#error "Не определен тип индикатора"
#endif
static void lcd_write_nibble(uint8_t nibble)
{
LCD_PORT = (LCD_PORT & 0xff & ~(0x0f << LCD_D0)) | ((nibble & 0x0f) << LCD_D0);
LCD_PORT = LCD_PORT & ~(1 << LCD_EN);
LCD_PORT = LCD_PORT | (1 << LCD_EN);
LCD_PORT = LCD_PORT & ~(1 << LCD_EN);
_delay_ms(0.04);
}
static void lcd_send(uint8_t value, uint8_t mode)
{
if (mode)
{
//Передача данных
LCD_PORT = LCD_PORT | (1 << LCD_RS);
}
else
{
//Передача инструкции (команды)
LCD_PORT = LCD_PORT & ~(1 << LCD_RS);
}
LCD_PORT = LCD_PORT & ~(1 << LCD_RW);
lcd_write_nibble(value >> 4);
lcd_write_nibble(value);
}
void lcd_command(uint8_t command)
{
lcd_send(command, 0);
}
void lcd_write(uint8_t value)
{
lcd_send(value, 1);
}
void lcd_init(void)
{
// Конфигурация выводов как выходов
LCD_DDR = LCD_DDR
| (1 << LCD_RS)
| (1 << LCD_RW)
| (1 << LCD_EN)
| (1 << LCD_D0)
| (1 << LCD_D1)
| (1 << LCD_D2)
| (1 << LCD_D3);
// Ожидание готовности LCD (документация говорит 15 мс +)
_delay_ms(15);
LCD_PORT = LCD_PORT
& ~(1 << LCD_EN)
& ~(1 << LCD_RS)
& ~(1 << LCD_RW);
_delay_ms(4.1);
lcd_write_nibble(0x03); // переключение в 4-битный режим
_delay_ms(4.1);
lcd_write_nibble(0x03); // 2-й раз
_delay_ms(4.1);
lcd_write_nibble(0x03); // 3-й раз
_delay_ms(4.1);
lcd_write_nibble(0x02); // установка 8-битного режима (?)
//lcd_command(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS);
lcd_command(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x10DOTS);
lcd_displayparams = LCD_CURSOROFF | LCD_BLINKOFF;
lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams);
#if (LCDTYPE==BC1601A1)
charcnt = 0;
#endif
}
void lcd_on(void)
{
lcd_displayparams |= LCD_DISPLAYON;
lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams);
}
void lcd_off(void)
{
lcd_displayparams &= ~LCD_DISPLAYON;
lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams);
}
void lcd_clear(void)
{
lcd_command(LCD_CLEARDISPLAY);
_delay_ms(2);
#if (LCDTYPE==BC1601A1)
charcnt = 0;
#endif
}
void lcd_return_home(void)
{
lcd_command(LCD_RETURNHOME);
_delay_ms(2);
}
void lcd_enable_blinking(void)
{
lcd_displayparams |= LCD_BLINKON;
lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams);
}
void lcd_disable_blinking(void)
{
lcd_displayparams &= ~LCD_BLINKON;
lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams);
}
void lcd_enable_cursor(void)
{
lcd_displayparams |= LCD_CURSORON;
lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams);
}
void lcd_disable_cursor(void)
{
lcd_displayparams &= ~LCD_CURSORON;
lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams);
}
void lcd_scroll_left(void)
{
lcd_command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
#if (LCDTYPE==BC1601A1)
if (charcnt)
charcnt--;
#endif
}
void lcd_scroll_right(void)
{
lcd_command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
#if (LCDTYPE==BC1601A1)
if (charcnt < 16)
charcnt++;
#endif
}
void lcd_set_left_to_right(void)
{
lcd_displayparams |= LCD_ENTRYLEFT;
lcd_command(LCD_ENTRYMODESET | lcd_displayparams);
}
void lcd_set_right_to_left(void)
{
lcd_displayparams &= ~LCD_ENTRYLEFT;
lcd_command(LCD_ENTRYMODESET | lcd_displayparams);
}
void lcd_enable_autoscroll(void)
{
lcd_displayparams |= LCD_ENTRYSHIFTINCREMENT;
lcd_command(LCD_ENTRYMODESET | lcd_displayparams);
}
void lcd_disable_autoscroll(void)
{
lcd_displayparams &= ~LCD_ENTRYSHIFTINCREMENT;
lcd_command(LCD_ENTRYMODESET | lcd_displayparams);
}
void lcd_create_char(uint8_t location, uint8_t *charmap)
{
lcd_command(LCD_SETCGRAMADDR | ((location & 0x7) << 3));
for (int i = 0; i < 8; i++)
{
lcd_write(charmap[i]);
}
}
void lcd_set_cursor(uint8_t col, uint8_t row)
{
static uint8_t offsets[] = { 0x00, 0x40, 0x14, 0x54 };
if (row > 1)
row = 1;
lcd_command(LCD_SETDDRAMADDR | (col + offsets[row]));
}
void lcd_puts(char *string)
{
for (char *it = string; *it; it++)
{
#if (LCDTYPE==BC1601A1)
//Поддержка дисплея BC1601A1 (16x1):
if (charcnt==0)
lcd_set_cursor(0, 0);
else if (charcnt==8)
lcd_set_cursor(0, 1);
else if (charcnt>15)
return;
#endif
if (*it < 0xC0)
{
//Вывод без перекодировки:
lcd_write(*it);
}
else
{
//Вывод с перекодировкой:
lcd_write(ANSI1251_CO_FF[(unsigned char)(*it)-0xC0]);
}
charcnt++;
}
}
void lcd_printf(char *format, ...)
{
va_list args;
va_start(args, format);
vsnprintf(lcd_buffer, sizeof(lcd_buffer), format, args);
va_end(args);
lcd_puts(lcd_buffer);
}