Проблема вывода на однострочный LCD BC1601A1 (16x1) Печать
Добавил(а) microsin   

Я спас из мусора старенький символьный LCD, у него формат экрана 16x1. По апноуту MicroChip AN587 попробовал его подключить, и обнаружил, что он работает только наполовину. При попытке вывести 16 символов вывелось только 8, в первые позиции экрана. После попытки изучить даташит Hitachi 44780 (скорее всего этот индикатор основан на этом контроллере или совместим с ним) мне удалось отобразить последние 8 символов из 16-символьной строки, либо от начала экрана, либо начиная с середины строки. Также получилось включить мигающий курсор, но с вывести полную строку 16x1 все еще не получалось.

BC1601A1GPLCWs

Оказалось, что экран у индикатора 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_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);
}

[Ссылки]

1Русификация индикатора LCD на чипе Hitachi 44780.
2. aostanin/avr-hd44780 site:github.com.