Проблема вывода на однострочный LCD BC1601A1 (16x1) |
![]() |
Добавил(а) microsin |
Я спас из мусора старенький символьный LCD, у него формат экрана 16x1. По апноуту MicroChip AN587 попробовал его подключить, и обнаружил, что он работает только наполовину. При попытке вывести 16 символов вывелось только 8, в первые позиции экрана. После попытки изучить даташит Hitachi 44780 (скорее всего этот индикатор основан на этом контроллере или совместим с ним) мне удалось отобразить последние 8 символов из 16-символьной строки, либо от начала экрана, либо начиная с середины строки. Также получилось включить мигающий курсор, но с вывести полную строку 16x1 все еще не получалось. Оказалось, что экран у индикатора 16x1 может быть организован так, что поделен на 2 части, по 8 символов в каждой. Попробуйте вывести 8 символов по адресам 00h..07h, а для остальной части строки попробуйте вывод начиная с адреса 0Fh или с других адресов. Также можно попробовать получить доступ к своему индикатору 16x1 как к индикатору 8x1. У меня получилось решить проблему следующим образом: //Количество символов в строке:
//#define LCD_COL_COUNT 16
#define LCD_COL_COUNT 8
//Количество строк:
//#define LCD_ROW_COUNT 1
#define LCD_ROW_COUNT 2
void lcd_init(void) { ... //lcd_command(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS); lcd_command(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS); lcd_displayparams = LCD_CURSOROFF | LCD_BLINKOFF; lcd_command(LCD_DISPLAYCONTROL | lcd_displayparams); } char sym = 'Я'; lcd_clear(); lcd_set_cursor(0, 0); //Вывод в позиции 0..7 строки lcd_printf("%02X %i", sym, sym); lcd_set_cursor(0, 1); //Вывод в позиции 8..15 строки lcd_printf("%c", sym); Здесь показан код доработанной библиотеки [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_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); } [Ссылки] 1. Русификация индикатора LCD на чипе Hitachi 44780. |