Урок 25. Подключение знакосинтезирующих LCD дисплеев к STM32. Библиотека LCD780.

LCD и STM32

В уроке расскажу об использовании совместно с контроллерами STM32 знакосинтезирующих жидкокристаллических (LCD) дисплеев. Представлю свою библиотеку для управления ими.

Предыдущий урок     Список уроков     Следующий урок

Речь идет о LCD дисплеях с управляющими контроллерами HD44780 или совместимыми с ними (SPLC780, KS0066 и подобными).

 

На рынке существует громадное число разнообразных дисплеев этого класса. Они имеют разную организацию отображаемой информации. Мне известны дисплеи с форматами от 1 строка 8 символов до 4 строки 40 символов. Но при этом у всех LCD дисплеев практически одинаковые параметры, одни и те же сигналы интерфейса и команды.

Достаточно подробно об этом я писал в уроке 23 Ардуино. Там есть справочная информация, описаны сигналы подключения к микроконтроллеру. Не буду повторяться.

 

Аппаратное подключение LCD дисплея к плате STM32.

Я буду использовать в примерах урока индикатор WH2004A (4 строки по 20 символов) в четырех битном режиме обмена.

Схема подключения к плате STM32 выглядит так.

Схема подключения LCD к STM32

Я выбрал первые попавшиеся выводы из разных портов, пропуская те, что уже заняты кнопкой, светодиодом и UART1. Дисплей питается от 5 В, значит выводы должны быть толерантными к 5 В.

О назначении резисторов можно почитать в уроке 23 Ардуино.

Дисплей я припаял к отладочной плате из предыдущих уроков.

LCD и STM32

Теперь есть все, чтобы использовать его в программе.

 

Библиотека LCD780.

Я разработал библиотеку для управления LCD дисплеями. Как мне кажется, работать с ней очень удобно.

Библиотека LCD780 предназначена для управления знакосинтезирующими LCD дисплеями с контролерами HD44780 и совместимыми с ними.

Интерфейс подключения дисплея к STM32 – параллельный в 4х битном режиме. Я посчитал, что необходимости в 8ми битном подключении нет. Зачем тратить для связи с индикатором 10 выводов, когда можно обойтись шестью.

Нет никаких ограничений для выбора выводов подключения дисплея. Они могут быть из разных портов. Для каждого вывода при инициализации библиотеки задается свой порт и номер вывода.

Не требуются предварительные установки режимов выводов для подключения дисплея. Конфигурация выводов происходит в функции инициализации. Вы вызываете функцию с заданными в параметрах номерами выводов и можно работать с дисплеем.

Временные задержки, необходимые для сигналов связи с дисплеем, рассчитываются с учетом тактовой частоты STM32. Используется библиотека DelayDWT.

Поддерживаются разные форматы дисплеев (число строк, число символов).

Может быть включен режим поддержки кириллических символов.

Используются следующие команды-функции:

  • Инициализация дисплея.
  • Вывод символа с заданным кодом.
  • Вывод массива символов.
  • Вывод на экран числа uint32_t с выбранной системой исчисления.
  • Вывод на экран числа int32_t с выбранной системой исчисления.
  • Вывод текстовой строки.
  • Вывод числа float с выбранным количеством разрядов после запятой.
  • Очистка экрана.
  • Перемещение курсора в начало экрана.
  • Установка курсора в заданную позицию.
  • Выключение и включение дисплея.
  • Выключение и включение отображения курсора.
  • Выключение и включение режима мигания курсора.
  • Установка направления сдвига при печати символов.
  • Выключение и включение автопрокрутки.
  • Создание пользовательского символа.
  • Запись байта в контроллер дисплея.

Несмотря на обилие функций и широкие функциональные возможности библиотека компактная. Она занимает менее 2 кбайт программной памяти.

Библиотеку можно загрузить по ссылке LCD780.

 

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!
 

Я буду рассказывать о функциях библиотеки и одновременно демонстрировать ее работу на примерах.

 

Я создал проект Lesson25_1, в котором конфигурировал только систему тактирования на частоту 72 мГц. Все остальные установки сделает библиотека при инициализации.

Еще необходимо создать в проекте папку Libraries и копировать в нее папки с библиотеками LCD780 и DelayDWT.

В Atollic TrueStudio:

Подключаем библиотеки.

/* USER CODE BEGIN Includes */
#include "LCD780.h"
#include "DelayDWT.h"
/* USER CODE END Includes */

  • Правой кнопкой мыши нажимаем на папку LCD780 в проекте, Add/Remove Include Path -> OK
  • Ту же операцию повторяем с папкой DelayDWT.
  • Правой кнопкой по Libraries, Properties -> C/C++ General -> Paths and Symbols -> Source Location -> Add Folder -> Apply

Теперь можно работать с функциями библиотеки.

 

Функции библиотеки LCD780.

void init_LCD( GPIO_TypeDef *GPIOx_RS, uint16_t GPIO_Pin_RS, GPIO_TypeDef *GPIOx_EN, uint16_t GPIO_Pin_EN, GPIO_TypeDef *GPIOx_D4, uint16_t GPIO_Pin_D4, GPIO_TypeDef *GPIOx_D5, uint16_t GPIO_Pin_D5, GPIO_TypeDef *GPIOx_D6, uint16_t GPIO_Pin_D6, GPIO_TypeDef *GPIOx_D7, uint16_t GPIO_Pin_D7, uint8_t col, uint8_t row, uint8_t cirill)

Инициализация дисплея. Параметры:

  • GPIO_TypeDef *GPIOx_RS, uint16_t GPIO_Pin_RS – порт и вывод сигнала RS;
  • GPIO_TypeDef *GPIOx_EN, uint16_t GPIO_Pin_EN – порт и вывод сигнала EN;
  • GPIO_TypeDef *GPIOx_D4, uint16_t GPIO_Pin_D4 – порт и вывод сигнала D4;
  • GPIO_TypeDef *GPIOx_D5, uint16_t GPIO_Pin_D5 – порт и вывод сигнала D5;
  • GPIO_TypeDef *GPIOx_D6, uint16_t GPIO_Pin_D6 – порт и вывод сигнала D6;
  • GPIO_TypeDef *GPIOx_D7, uint16_t GPIO_Pin_D7 – порт и вывод сигнала D7;
  • uint8_t col – число символов в строке;
  • uint8_t row – число строк;
  • uint8_t cirill – признак поддержки кириллических символов.

Функция должна вызываться до работы с дисплеем. До функции инициализации необходимо разрешить тактирование используемых портов и инициализировать библиотеку DelayDWT.

Перед циклом while добавляем в проект

// инициализация дисплея
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPAEN ;
initDelayDwt();
init_LCD(GPIOA, 1<<12, GPIOA, 1<<15, GPIOB, 1<<14, GPIOB, 1<<15, GPIOA, 1<<8, GPIOA, 1<<11, 20, 4, 1);

Выводы я определил в соответствии со схемой урока. Дисплей у меня 4 строки по 20 символов. Включил поддержку кириллических символов.

void write_LCD(uint8_t valChar)

Выводит на экран символ с кодом, заданным в параметре uint8_t valChar.

Следующий код напечатает ”Проверка дисплея!”.

write_LCD('П');
write_LCD('р');
write_LCD('о');
write_LCD('в');
write_LCD('е');
write_LCD('р');
write_LCD('к');
write_LCD('а');
write_LCD(' ');
write_LCD('д');
write_LCD('и');
write_LCD('с');
write_LCD('п');
write_LCD('л');
write_LCD('е');
write_LCD('я');
write_LCD('!');

Напечатал.

LCD и STM32

void writeBuf_LCD(uint8_t * buf, uint16_t length)

Вывод массива символов на экран.

  • uint8_t * buf – указатель на массив.
  • uint16_t length – количество символов.

Следующий код печатает надпись из предыдущего примера ”Проверка дисплея!”, только другим способом.

uint8_t str[] = "Проверка дисплея!";
writeBuf_LCD(str, 17);

void print_uint_LCD(uint32_t n, uint8_t base)

Выводит текстовое представление беззнакового целочисленного числа в заданной системе исчисления.

  • uint32_t n – число.
  • uint8_t base – основание системы исчисления.

Следующий код печатает в цикле числа от 0 в десятичной, двоичной и шестнадцатеричной системах исчисления.

for( uint16_t i=0; ; i++) {
  clear_LCD();
  print_uint_LCD(i, 10);
  write_LCD(' ');
  print_uint_LCD(i, 2);
  write_LCD(' ');
  print_uint_LCD(i, 16);
  HAL_Delay(1000);
}

LCD и STM32

void print_int_LCD(int32_t n, uint8_t base)

Выводит текстовое представление знакового целочисленного числа в заданной системе исчисления. Аналог предыдущей функции, только с учетом знака числа.

  • int32_t n – число.
  • uint8_t base – основание системы исчисления.

for( int16_t i=-10; ; i++) {
  clear_LCD();
  print_int_LCD(i, 10);
  HAL_Delay(1000);
}

void print_float_LCD(float number, uint8_t digits)

Вывод числа float в текстовом виде.

  • float number – число.
  • uint8_t digits – количество знаков после запятой.

Программа выводит числа с плавающей запятой.

print_float_LCD(3.1415926, 5);
write_LCD(' ');
print_float_LCD(3.1415926, 1);
write_LCD(' ');
print_float_LCD(-3256.123, 2);

LCD и STM32

void print_str_LCD(const uint8_t str[])

Печатает текстовую строку. Конец строки определяется байтом 0.

uint8_t str[] = "Проверка print_str\0";
print_str_LCD(str);

LCD и STM32

Можно так.

print_str_LCD("Проверка print");

void clear_LCD(void)

Очищает экран, курсор устанавливает в верхнюю левую позицию.

void home_LCD(void);

Курсор устанавливает в верхнюю левую позицию.

void setCursor_LCD(uint8_t col, uint8_t row)

Устанавливает курсор в заданную позицию.

  • uint8_t col – позиция в строке, начиная с 1.
  • uint8_t row – номер строки, начиная с 1.

Следующий код выводит число в заданной позиции, увеличивая его  в цикле.

print_str_LCD("Число в строке 4");
setCursor_LCD(1, 2);
print_str_LCD("в позиции 10");
for( uint16_t i=0; ; i++) {
  setCursor_LCD(10, 4);
  print_uint_LCD(i, 10);
  HAL_Delay(1000);
}

LCD и STM32

void noDisplay_LCD()

Выключение дисплея.

void display_LCD()

Включение дисплея.

При выключении данные экрана не теряются. Следующая программа выводит на экране сообщение ”Проверка выключения”, через секунду выключает дисплей. Когда еще через секунду дисплей включается, то надпись появляется на экране, хотя была напечатана до выключения.

print_str_LCD("Проверка выключения");
HAL_Delay(1000);
noDisplay_LCD();
HAL_Delay(1000);
display_LCD();

void cursor_LCD()

Включение отображения курсора.

void noCursor_LCD()

Выключение отображения курсора.

После инициализации библиотеки курсор не отображается на экране. Этими 2мя функциями можно включить и отключить его.

cursor_LCD();
print_str_LCD("Проверка курсора");

LCD и STM32

void blink_LCD()

Включение мигания курсора.

void noBlink_LCD()

Выключение мигания курсора.

Можно включить и отключить мигание знакоместа курсора.

cursor_LCD();
blink_LCD();
print_str_LCD("Проверка курсора");

void scrollDisplayLeft_LCD()

Сдвиг информации на экране влево.

void scrollDisplayRight_LCD()

Сдвиг информации на экране вправо.

Можно сдвигать текст на экране влево и вправо. Следующая программа 2 раза сдвигает текст вправо, а затем возвращает его назад.

print_str_LCD("Проверка сдвига");
HAL_Delay(1000);
scrollDisplayRight_LCD();
HAL_Delay(1000);
scrollDisplayRight_LCD();
HAL_Delay(1000);
scrollDisplayLeft_LCD();
HAL_Delay(1000);
scrollDisplayLeft_LCD();

void leftToRight_LCD()

Вывод символов слева направо.

void rightToLeft_LCD()

Вывод символов справа налево.

По умолчанию символы печатаются слева направо. Так мы пишем. Но можно изменить на противоположное направление.  Даже не могу предположить, кому это может потребоваться. Следующий код выводит символы с 10 позиции справа налево.

rightToLeft_LCD();
setCursor_LCD(10, 1);
write_LCD('1');
HAL_Delay(1000);
write_LCD('2');
HAL_Delay(1000);
write_LCD('3');
HAL_Delay(1000);
write_LCD('4');

LCD и STM32

void autoscroll_LCD()

Включение автопрокрутки.

void noAutoscroll_LCD()

Выключение автопрокрутки.

Если автоматическая прокрутка отключена, то при переполнении экрана информация выводится с начала экрана. Если включить автопрокрутку, то при поступлении нового символа вся текстовая информация сдвигается влево. Я сомневаюсь в необходимости этих функций, но они реализованы на аппаратном уровне дисплея. Поэтому библиотека их поддерживает.

void createChar_LCD(uint8_t location, uint8_t charmap[])

Функция позволяет создать пользовательский символ.

  • uint8_t location – код символа (0…7).
  • uint8_t charmap[] – массив пикселей символа (5x8).

Следующий код задает и выводит новый символ – ”рожицу”.

uint8_t smile[8] = {
  0B00000000,
  0B00010001,
  0B00000000,
  0B00000000,
  0B00010001,
  0B00001110,
  0B00000000,
  0B00000000
};

createChar_LCD(1, smile); // создаем символ
clear_LCD();
print_str_LCD("Новый символ: ");
write_LCD(1);

LCD и STM32

void writeByte_LCD(uint8_t dt)

Запись байта в контроллер дисплея. Функция может быть использована для реализации пользовательских  команд.

Все примеры урока есть в этом проекте.

 

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!
 

Библиотека LCD780 не использует объектно-ориентированные возможности программирования. Ее можно применять в обоих вариантах языка: С и C++. Скорее это положительное свойство. Я специально создавал не объектно-ориентированный вариант библиотеки.

 

Но планирую сделать и ООП-вариант библиотеки. Конечно,  если у меня до этого ”дойдут руки” или ”голова”. Постараюсь.

 

Предыдущий урок     Список уроков     Следующий урок

0

Автор публикации

не в сети 2 дня

Эдуард

280
Комментарии: 1936Публикации: 197Регистрация: 13-12-2015

29 комментариев на «Урок 25. Подключение знакосинтезирующих LCD дисплеев к STM32. Библиотека LCD780.»

  1. Вот сколько не смотрел вариантов,а захотелось свой велосипед ,так сказать максимально универсальный,где можно выбрать как с проверкой флага bf и 8 или 4bit и чтоб легко настраивать gpio, не через hal и с любыми ногами : https://mega.nz/#!DfoDDaRa!H8ye_xw6tgihfLWiLQlHswaV6xheoUQYA1uU-Y-51Q0

    0
  2. Эдуард спасибо за урок. Хотелось бы увидеть урок, в котором измеряется аналоговый сигнал и значение АЦП выводится на подключенный индикатор. Я все это делал на Microchipe используя assembler. Теперь изучаю STM32 и планирую собрать стенд, который будет измерять температуру объекта при помощи
    1) MCP9700 (измеряется напряжение на выходе датчика и потом высчитывается темпетатура Uo=500mV+10mV/C*t)
    2) 1- Wire датчика
    3) ИК датчика
    Я всё это делал на ассемблере на PIC. Алгоритмически всё понятно. Теперь буду привыкать к С.

    0
    • Здравствуйте!
      Именно это и будет в следующих уроках про АЦП. Один уже написан, но все время меняю представление материала. Очень много регистров АЦП, много информации.

      1
  3. Добрый день, Эдуард! Огромное Вам спасибо за Ваш огромный труд. Уроки замечательные. У меня вопрос. Скажите, пожалуйста, можно применять библиотеки, написанные для различных датчиков Ардуино в среде Keil для stm32. Не хочется (вернее хочется, но нет времени) изучать Datasheet на все цифровые датчики (температуры, освещенности, тока и т.п.). Я искал в интернете подобную информацию, но пока безрезультатно Заранее спасибо.

    0
  4. Здравствуйте, Эдуард!
    Не могу пробиться через ошибку в команде вывода строки
    print_str_LCD(«COMMAND «)
    Выводится ошибка:
    Description Resource Path Location Type
    invalid conversion from ‘const char*’ to ‘const uint8_t* {aka const unsigned char*}’ [-fpermissive] line 149 C/C++ Problem
    Пример конвертировал в С++.
    Пробовал менять uint8_t на char в библиотеках для этой команды, но ничего не изменилось. Прошу совета.

    0
  5. ‘GPIO_TypeDef’ {aka ‘struct ‘} has no member named ‘CRH’
    ‘GPIO_TypeDef’ {aka ‘struct ‘} has no member named ‘CRL’
    такие ошибки выдает система на все строки с использованием этих самых CRL u CRH, этот кусок кода находится в инициализации. вопрос такой: зачем этот ИФ вообще нужен после ВАЙЛа, в котором прописывается пин вывода?

    0
  6. ..\..\Core\Src\LCD780.c(18): error: #65: expected a «;»
    _displaycontrol &= 0b11111011 ;
    ..\..\Core\Src\LCD780.c(26): error: #65: expected a «;»
    _displaycontrol |= 0b00001100;
    ..\..\Core\Src\LCD780.c(34): error: #65: expected a «;»
    _displaycontrol &= 0b11111101;
    ..\..\Core\Src\LCD780.c(42): error: #65: expected a «;»
    _displaycontrol |= 0b00001010;
    ..\..\Core\Src\LCD780.c(50): error: #65: expected a «;»
    _displaycontrol &= 0b11111110;
    ..\..\Core\Src\LCD780.c(58): error: #65: expected a «;»
    _displaycontrol |= 0b00001001;
    ..\..\Core\Src\LCD780.c(88): error: #65: expected a «;»
    _displaymode &= 0b11111101;
    ..\..\Core\Src\LCD780.c(104): error: #65: expected a «;»
    _displaymode &= 0b11111110;
    ..\..\Core\Src\LCD780.c(283): error: #65: expected a «;»
    _GPIOx_EN->CRL |= 0b0011 <CRL &= ~ (0b1100 <CRH |= 0b0011 <CRH &= ~ (0b1100 <CRL |= 0b0011 <CRL &= ~ (0b1100 <CRH |= 0b0011 <CRH &= ~ (0b1100 <CRL |= 0b0011 <CRL &= ~ (0b1100 <CRH |= 0b0011 <CRH &= ~ (0b1100 <CRL |= 0b0011 <CRL &= ~ (0b1100 <CRH |= 0b0011 <CRH &= ~ (0b1100 <CRL |= 0b0011 <CRL &= ~ (0b1100 <CRH |= 0b0011 <CRH &= ~ (0b1100 <CRL |= 0b0011 <CRL &= ~ (0b1100 << (i * 4));
    ..\..\Core\Src\LCD780.c: 0 warnings, 30 errors
    Такие ошибки выдает Keil при компиляции(((

    0
  7. Здравствуйте Эдуард
    Вопрос
    К чему подключена кнопка и светодиод на схеме?
    Фотография не совпадает со схемой.
    Мучаюсь отыскивая в предыдущих уроках подключения. Зачем?
    Для чего нужна здесь кнопка (кроме кнопок переключения режимов работа/программирование)

    0
    • Здравствуйте!
      Осталась от предыдущих уроков. Не стал отпаивать ее. Не обращайте на кнопку и светодиод внимание.

      0
  8. Очень помогают Ваши уроки. Пожалуй, это самое лучшее, что я нашел!
    Вопрос: Как вывести на экран LCD десятичное число и менять количество разрядов после запятой

    0
    • Здравствуйте!

      Функция print_float_LCD.
      Вот ее описание из урока.
      void print_float_LCD(float number, uint8_t digits)
      Вывод числа float в текстовом виде.
      float number – число.
      uint8_t digits – количество знаков после запятой.

      Например:
      float x=3.1415926;
      print_float_LCD(x, 3); // 3 разряда после запятой

      0
    • Выведите, для начала, просто число. Зачем это обилие бесконечных циклов один в другом. Возьмите пример из урока. Потом добавьте один цикл.

      0
  9. Здравствуйте, Эдуард.
    Следуя Вашему совету начал с вывода просто чисел и получил:
    5 разрядов до запятой:
    float x = 54321.820;
    print_float_LCD(x, 3); // 3 разряда после запятой Выводит !

    Выводит на экран LCD — 54321.820

    6 разрядов до запятой:
    float x = 654321.820;
    print_float_LCD(x, 3); // 3 разряда после запятой Выводит !

    Выводит на экран LCD — 654321.812

    7 разрядов до запятой:
    float x = 7654321.820;
    print_float_LCD(x, 3); // 3 разряда после запятой Выводит !

    Выводит на экран LCD — 7654322.000 //ошибка при выводе!

    8 разрядов до запятой:
    float x = 87654321.820;
    print_float_LCD(x, 3); // 3 разряда после запятой Выводит !

    Выводит на экран LCD — 87654320.000 //ошибка при выводе!

    8 разрядов до запятой:
    float x = 87654321.820;
    print_float_LCD(x, 1); // 1 разряда после запятой Выводит !

    Выводит на экран LCD — 87654320.0 //ошибка при выводе!

    Для измерительных приборов не подходит!
    В чем моя ошибка?

    0
    • Здравствуйте!
      Вам надо разобраться в формате типа float. Точность теряется не при выводе на дисплей, а при загрузке переменной float.
      Тип float использует 32 разряда: 24 разрядов мантисса и 8 разрядов порядок. Число представляется, как мантисса * 2 в степени порядок. Именно мантисса определяет точность числа.
      При мантиссе 24 разряда, максимальное значение кода в ней = 2 в степени 24 и = 16 777 216.
      У вас, при float x = 7654321.820; в мантиссе должно храниться 765 432 182. Естественно, младшие разряды отбрасываются и теряется точность.
      Ответ на фразу “Для измерительных приборов не подходит!” Неужели, при числе с целой частью 7 разрядов имеют значение разряды после запятой!
      Существует другой формат чисел с плавающей запятой double. Он использует не 4, а 8 байтов, точность значительно выше. Но, на микроконтроллерах он, как правило, не используется.

      0
  10. Вывод: 7 разрядов (до и после запятой). При большем количестве разрядов начинаются ошибки. Может проблема в инициализации?
    Библиотека хорошая, но правильный вывод чисел с плавающей запятой необходим.
    Может так:
    Старшие полбайта
    Строб
    Снова старшие полбайта
    Строб
    Младшие полбайта
    Строб

    0
    • Здравствуйте!
      Александр! Причем здесь библиотека?
      Вы не согласны с моими объяснением по поводу точности представления чисел в формате float? В чем я не прав? Как можно число 765432182 упаковать без ошибки в 24 двоичных разряда?
      Тогда у меня вопрос. Какую точность чисел обеспечивает 32х разрядный формат float? Или вы считаете, что все в мире не имеет предела?
      Посмотрите другие источники по поводу точности float. После этого будете обвинять библиотеку в некорректной работе.

      0
  11. Эдуард, я не хотел обидеть Вас или библиотеку. Тем более, что библиотека нравится! Удобная, продуманная.
    Дело в том, что нужно изготовить измерительный прибор. На базе Arduino я сделал несколько приборов, но точности не хватает да и частотный диапазон маловат. Решил делать на STM32. Кстати в скетчах Arduino я использовал именно double для вывода на LCD результатов измерений. Получалось хорошо.
    Можно возразить, что мол хватит тебе 7 разрядов при использовании float. От STM32 я ожидал большего.
    Повторяю, библиотека хорошая и лично мне она нравится.
    Ваши уроки также хороши! Спасибо!

    0
    • Как не хватает точности!
      При float x = 7654321.820; один разряд после запятой это погрешность 0,0000013 %.
      Еще, считается, что в Ардуино формат double аналогичен float, тоже 32 разряда.
      Может мы не понимаем друг друга. Если вы загрузите, например, float x = 1.7654321;, то получите гораздо больше значащих разрядов после запятой. Форматы с плавающей запятой всегда сдвигают мантиссу относительно старшего значащего разряда. И, таким образом, абсолютная погрешность зависит от величины числа, а относительная погрешность стабильна и зависит от разрядности мантиссы.

      0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".