Шаг 4.5. Пишем длинные строчки в LCD.

Кажется, что в нашей программке все отлично - команды пишутся, строчки тоже... Но вот беда - если мы попробуем отослать строку длиннее, то она обрежется на восьмом символе.


Рисунок 1. Проблемы с отображением длинных строк.

Обращаемся к документации: "индикатор содержит ОЗУ размером 80 байт по адресам 0h - 27h и 40h - 67h для хранения данных (DDRAM), выводимых на индикатор. Адреса отображаемых на индикаторе символов распределены следующим образом:

Знакоместо 1 2 3 4 5 6 7 8 9 10
Адрес 0h 1h 2h 3h 4h 5h 6h 7h 40h 41h

Таким образом, первые восемь символов записываются подряд, начиная с адреса "0h", а вот для записи оставшихся двух необходимо ручками устанавливать нужный адрес для записи. Справедливости и злорадства ради заметим, что встроенная библиотека точно так же теряет девятый и десятый символы.

Что ж, проблема есть - так будем её решать!

Функция для отправки как отдельных, так и для последовательностей команд у нас уже есть, самое время изучить, а какие основные команды для МТ-10S1 вообще есть.

Таблица 1. Некоторые команды для дисплея MT-10S1.

Команда DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 Описание
Clear Display 0 0 0 0 0 0 0 1 Очищает индикатор и перемещает курсор в самую левую позицию
Return Home 0 0 0 0 0 0 1 x Перемещает курсор в левую позицию
Entry Mode Set 0 0 0 0 0 1 ID SH Установка направления сдвига курсора (ID = 0/1 - влево/вправо) и разрешение сдвига дисплея (SH = 1) при записи в DDRAM
Display ON/OFF control 0 0 0 0 1 D C B Включает индикатор (D = 1) и выбирает тип курсора (C, B)
Cursor or Display Shift 0 0 0 1 SC RL x x Выполняет сдвиг дисплея или курсора (SC = 0/1 - курсор/дисплей, RL = 0/1 - влево/вправо)
Set DDRAM Address 1 ADD Установка адреса для последующих операций и выбор области DDRAM

Для того, чтобы писать длинные строки, нам потребуется единственная команда - Set DDRAM Address. Так, чтобы установить курсор для самую левую позицию, нужно будет послать команду 0b10000000, где крайняя левая единица относится к идентификатору самой команды, а "0000000" - адрес области DDRAM. Чтобы установить курсор в конец строки для записи 9 и 10 символов, используем команду 0b11000000, где адрес - "1000000", то есть 40h.

В принципе, этого уже достаточно для работы, однако нужно заметить, что дисплей не стирает написанное при переходе в начало строки, поэтому если сначала мы пошлём длинную строку, а затем короткую, то у нас получится "наложение" строк друг на друга - не очень-то красиво!


Рисунок 2. Проблема наложения строк на дисплее.

Поэтому перед тем, как писать строку, используем команду Clear Display - 0b00000001.

Смотрим, что у нас получается (рассмотрим только функцию передачи строки и main, остальное без изменений).

На Си:

const int DL_STAY_TEXT_ON_DISPLAY_MS = 2000;                                    //задержка после передачи текста дисплею для возможности прочитать его, в мс
...
//функция отправки дисплею строки, заканчивающейся нулём ('\0')
void LCD_SendStr(byte* Str)
{
  //команды дисплею
  const byte LCD_CMD_SET_BEGIN_OF_STR[] = {0b10000000, 0};                      //переход в начало строки дисплея
  const byte LCD_CMD_SET_END_OF_STR[] = {0b11000000, 0};                        //переход в конец строки дисплея (9 и 10 символы)
  const byte LCD_CMD_CLEAR_DISPLAY[] = {0x01, 0};                               //очистка дисплея и переход в начало строки
  const byte LCD_MAX_SYMBOL_COUNT = 10;                                         //максимальное количество символов - для того, чтобы не писать лишнего в память дисплея
  byte i;
  LCD_SendCommands(LCD_CMD_CLEAR_DISPLAY);
  i = 0;
  while ((Str[i] != 0) && (i < LCD_MAX_SYMBOL_COUNT))
  {
    if (i == 8)                                                                 //если пора "переходить" в конец строки (нужно записать 9 и 10 символы) - то изменяем адрес записи в дисплее
      LCD_SendCommands(LCD_CMD_SET_END_OF_STR);
    LCD_SendByte(Str[i], 1);
    i++;
  }
}

void main()
{
  const byte LCD_CMD_INIT[] = {0b00110011,                                      //команды для инициализации модуля
                               0b00110010,
                               0b00101000,
                               0b00001111,
                               0b00000001,
                               0b00000110,
                               0};
   //строка на вывод
  char sText[] = "Hello,wrld";
  char sMeow[] = "Meow";

  //инициализация портов ввода-вывода
  DDRA = 0;
  PORTA = 0;
  DDRB = 0;
  PORTB = 0;
  DDRD = 0;
  PORTD = 0;
  //инициализация выводов для дисплея - на выход и на 0
  ddrLCD |= (1 << LCD_A0_OUT_NUMBER) | (1 << LCD_E_OUT_NUMBER) | LCD_DATA_MASK;
  
  Delay_ms(DL_LCD_SWITCH_ON_MS);                                                //задержка для дисплея после включения
  LCD_SendCommands(LCD_CMD_INIT);                                               //инициализация дисплея набором константных команд
  LCD_SendStr(sText);                                                           //отправка текста
  Delay_ms(DL_STAY_TEXT_ON_DISPLAY_MS);                                         //задержка для прочтения текста
  LCD_SendStr(sMeow);
  while(1)
  {
   Delay_ms(1);
  }
}

На Паскале:

const DL_STAY_TEXT_ON_DISPLAY_MS = 2000;                                            //задержка после передачи текста дисплею для возможности прочитать его, в мс
...
//функция отправки дисплею строки, заканчивающейся нулём ('\0')
procedure LCD_SendStr(var Str: String);
const
  LCD_CMD_SET_BEGIN_OF_STR : array[2] of Byte = (%10000000, 0);                 //переход в начало строки дисплея
  LCD_CMD_SET_END_OF_STR : array[2] of Byte = (%11000000, 0);                   //переход в конец строки дисплея (9 и 10 символы)
  LCD_CMD_CLEAR_DISPLAY : array[2] of Byte = (0x01, 0);                         //очистка дисплея и переход в начало строки
  LCD_MAX_SYMBOL_COUNT = 10;                                                    //максимальное количество символов - для того, чтобы не писать лишнего в память дисплея
var
  i: Integer;
begin
  LCD_SendCommands(@LCD_CMD_CLEAR_DISPLAY[0]);
  i := 0;
  while ((Str[i] <> 0) and (i < LCD_MAX_SYMBOL_COUNT)) do
  begin
    if (i = 8) then                                                             //если пора "переходить" в конец строки (нужно записать 9 и 10 символы) - то изменяем адрес записи в дисплее
      LCD_SendCommands(@LCD_CMD_SET_END_OF_STR[0]);
    LCD_SendByte(Str[i], 1);
    inc(i);
  end;
end;

const
  Init: array[0.. 6] of Byte  = (%00110011,                                     //команды для инициализации модуля
                                 %00110010,
                                 %00101000,
                                 %00001111,
                                 %00000001,
                                 %00000110,
                                 0);
var
  sText, sMeow: string[10];
begin
  sText := 'Hello,wrld';
  sMeow := 'Meow';

  //инициализация портов ввода-вывода
  DDRA := 0;
  PORTA := 0;
  DDRB := 0;
  PORTB := 0;
  DDRD := 0;
  PORTD := 0;
  //инициализация выводов для дисплея - на выход и на 0
  ddrLCD := ddrLCD or (1 shl LCD_A0_OUT_NUMBER) or (1 shl LCD_E_OUT_NUMBER) or LCD_DATA_MASK;

  Delay_ms(DL_LCD_SWITCH_ON_MS);                                                //задержка для дисплея после включения
  LCD_SendCommands(@Init[0]);                                                   //инициализация дисплея
  LCD_SendStr(sText);                                                           //отправка текста
  Delay_ms(DL_STAY_TEXT_ON_DISPLAY_MS);                                         //задержка для прочтения текста
  LCD_SendStr(sMeow);
  while(1) do
    Delay_ms(1);
end.

Прошивка улучшенной версии программы тут.

В результате получаем следующее:

Автор - Moriam
Обсудить на форуме