Урок 7
HAL. LCD 20×4. 4-битный режим. Выводим информацию
Сегодня мы продолжим работать с дисплеем 20×4, с которым мы начали работать на прошлом занятии. Мы написали инициализацию и проверили работу кода на практике.
Теперь пытаемся что-то вывести на дисплей.
Для этого сначала пишем функцию для записи данных в дисплей, она немного будет отличаться от команд (только лишь лапкой rs)
void LCD_Data(uint8_t dt)
{
rs1;
LCD_WriteData(dt>>4);
e1;
delay();
e0;
LCD_WriteData(dt);
e1;
delay();
e0;
}
Еще напишем функцию очистки дисплея
void LCD_Clear(void)
{
LCD_SendCommand4(0x01);
HAL_Delay(2);
}
Также, собственно, напишем функцию вывода символа на дисплей
void LCD_SendChar(char ch)
{
LCD_Data((uint8_t) ch);
delay();
}
Сделаем ее видимой, написав прототип в lcd.h.
И попытаемся наконец вывести какой то символ в главной функции программы
LCD_ini();
LCD_SendChar('s');
Прошьем и посмотрим
Попробуем еще что то вывести на экран
LCD_SendChar('s');
LCD_SendChar('t');
LCD_SendChar('m');
Прошиваем, смотрим
Все работает, но, согласитесь, не совсем удобно писать программу, используя для каждого символа отдельную команду. Давайте поэтому напишем функцию для вывода целой строки
void LCD_String(char* st)
{
uint8_t i=0;
while(st[i]!=0)
{
LCD_Data(st[i]);
delay();
i++;
}
}
Напишем прототип для данной функции и вызовем ее из главного модуля, пытаясь вывести какую-то строку на дисплей.
Сначала объявим массив и проинициализируем
int main(void)
{
/* USER CODE BEGIN 1 */
char str[] = «Stm32F407VG»;
/* USER CODE END 1 */
Затем вызовем функцию, а посимвольный вывод пока закомментируем
LCD_ini();
LCD_String(str);
// LCD_SendChar('s');
// LCD_SendChar('t');
// LCD_SendChar('m');
Соберём код, прошьём контроллер и посмотрим результат
Вся наша строка на дисплее видна.
Также нам не интересно писать подряд, поэтому необходимо создать функцию для вывода на дисплей в желаемое место.
Для этого заглянем в даташит (открыв специализированный для LCD2004)
Мы видим там (на стр. 8), что строки имеют свои адреса в памяти DDRAM и все символы также:
1 строка – 0x00
2 строка – 0x40
3 строка – 0x14
4 строка – 0x54
А каждый символ будет смещением относительно начала строки.
Поняв это, пишем функцию позиционирования по столбцу и строке
void LCD_SetPos(uint8_t x, uint8_t y)
{
switch(y)
{
case 0:
LCD_Command(x|0x80);
HAL_Delay(1);
break;
case 1:
LCD_Command((0x40+x)|0x80);
HAL_Delay(1);
break;
case 2:
LCD_Command((0x14+x)|0x80);
HAL_Delay(1);
break;
case 3:
LCD_Command((0x54+x)|0x80);
HAL_Delay(1);
break;
}
}
Не забываем про прототип.
Попробуем что-нибудь написать в другую позицию.
Изменим код в main.c следующим образом. Сначала объявим по другому (без инициализации) строковый массив
/* USER CODE BEGIN 1 */
char str[100];
/* USER CODE END 1 */
Затем изменим код вывода
LCD_ini();
sprintf(str, «Stm32F407VG»);
LCD_String(str);
LCD_SetPos(10, 2);
sprintf(str, «ARM mc»);
LCD_String(str);
Также наш дисплей должен уметь полностью очищаться. Для этого пишем функцию очистки.
void LCD_Clear(void)
{
LCD_Command(0x01);
HAL_Delay(2);
}
Для нее также нужен будет прототип.
Попробуем после некоторой задержки очистить наш экран
sprintf(str, «ARM mc»);
LCD_String(str);
HAL_Delay(2000);
LCD_Clear();
Прошьем и посмотрим. В видеоверсии, прикреплённой внизу урока, это будет нагляднее, поэтому здесь не показываю.
Добавим переменную в main() и напоследок немного поприкалываемся
/* USER CODE BEGIN 1 */
uint32_t i=0;
char str[100];
/* USER CODE END 1 */
Добавим код
sprintf(str, «ARM mc»);
LCD_String(str);
HAL_Delay(2000);
LCD_Clear();
LCD_SetPos(4,0);
LCD_SendChar('s');
LCD_SetPos(8,1);
LCD_SendChar('t');
LCD_SetPos(12,2);
LCD_SendChar('m');
LCD_SetPos(16,3);
LCD_SendChar('3');
LCD_SendChar('2');
HAL_Delay(2000);
/* USER CODE END 2 */
Также добавим код в бесконечный цикл
/* USER CODE BEGIN WHILE */
while (1)
{
LCD_SetPos(0,0);
LCD_SendChar((char) ((i/100)%10) + 0x30);
LCD_SendChar((char) ((i/10)%10) + 0x30);
LCD_SendChar((char) (i%10) + 0x30);
LCD_SetPos(16,1);
LCD_SendChar((char) (((i+250)/100)%10) + 0x30);
LCD_SendChar((char) (((i+250)/10)%10) + 0x30);
LCD_SendChar((char) ((i+250)%10) + 0x30);
LCD_SetPos(6,2);
LCD_SendChar((char) (((i+500)/100)%10) + 0x30);
LCD_SendChar((char) (((i+500)/10)%10) + 0x30);
LCD_SendChar((char) ((i+500)%10) + 0x30);
LCD_SetPos(9,3);
LCD_SendChar((char) (((i+750)/100)%10) + 0x30);
LCD_SendChar((char) (((i+750)/10)%10) + 0x30);
LCD_SendChar((char) ((i+750)%10) + 0x30);
HAL_Delay(100);
i++;
/* USER CODE END WHILE */
Ну и уж совсем на посошок уберем надоедливую черную мигалку (или курсор). Для этого меняем только одну букву в инициализации
LCD_Command(0x28);//режим 4 бит, 2 линии (для нашего большого дисплея это 4 линии), шрифт 5х8
HAL_Delay(1);
LCD_Command(0x28);//еще раз для верности
HAL_Delay(1);
LCD_Command(0x0C);//дисплей включаем (D=1), курсоры никакие не нужны
HAL_Delay(1);
LCD_Command(0x01);//уберем мусор
HAL_Delay(2);
LCD_Command(0x06);//пишем влево.
HAL_Delay(1);
LCD_Command(0x02);//возврат курсора в нулевое положение
Вот что у нас получилось (соответственно, в видеоверсии смотреть лучше)
Предыдущий урок Программирование МК STM32 Следующий урок
Техническая документация:
Отладочную плату можно приобрести здесь STM32F4-DISCOVERY
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Большое спасибо за урок! Мне, как начинающему, все понятно. Пошел "зашивать".
Значит не зря стараюсь.
Конечно не зря! Даже у меня получилось, хотя я и не программист, а электронщик и Ваш LCD — вторая программа после мигания светодиодами и очень нужна для серьезной работы, а времени и опыта что-то самому писать нет. Дальше буду осваивать ADC по вашим урокам. Заранее огромное спасибо!
С выводом 15 промахнулись, когда рисовали схему, и подключили к RS вместо VDD. И 330 ом многовато. Лучше 10 ом.
Уважаемый — Ваши уроки это супер! Спасибо большое за труд и старания!
Хотелось бы спросить:
Строка: LCD_WriteData(dt>>4);
В прошлом уроке ее небыло. Или я что-то не правильно понял?
Спасибо заранее.
Спасибо за оценку!
Да вроде должна быть, иначе как мы старшую тетраду передадим.
Добрый день. Спасибо за ответ. Я пока к практике не перешел (Сижу проводочки паяю ))))
На сколько я понял мы вызываем эту процедуру:
void LCD_WriteData(uint8_t dt)
{
if(((dt >> 3)&0x01)==1) {d7_set();} else {d7_reset();}
if(((dt >> 2)&0x01)==1) {d6_set();} else {d6_reset();}
if(((dt >> 1)&0x01)==1) {d5_set();} else {d5_reset();}
if((dt&0x01)==1) {d4_set();} else {d4_reset();}
}
поидее строка: LCD_WriteData(dt>>4); будет равно вот этому условию: if((dt&0x01)==1) {d4_set();} else {d4_reset();}? Если так — то почему не в таком виде: if(((dt >> 4)&0x01)==1) {d4_set();} else {d4_reset();}? Я еще не силен в СИ, в основном BASIC и ASSEMBLER(совсем чуть чуть). Спасибо заранее.
И еще хотелось бы спросить — а будет урок посвященный работе со встроенными RTC? Спасибо еще раз за громадную проделанную работу!!!!!!!!
Добрый день!!! РАЗОБРАЛСЯ )))))))))) Почитал еще основные математические действия ) и все понял ). Я изначально не так понял эти строки:
if(((dt >> 3)&0x01)==1) {d7_set();} else {d7_reset();}
if(((dt >> 2)&0x01)==1) {d6_set();} else {d6_reset();}
if(((dt >> 1)&0x01)==1) {d5_set();} else {d5_reset();}
if((dt&0x01)==1) {d4_set();} else {d4_reset();}
Теперь стало все понятно )))) Еще раз спасибо за подробные уроки!
Здравствуйте! Для начала хочю сказать спасибо за Вашы уроки! Очень подробно все описываете, такого больше нигде не встречал!
Нужна Ваша помощь по одному вопросу.
Заказал я как-то из Китая дисплей 1602. Он пришел, я подпаял разъем для подключения контактов, подал питание и засветилась первая строка, все вроде как должно быть. Только вот после инициализации вместо того, чтобы символы показывать светится две строки заполненные прямоугольниками. Единственное что их немного хуже видно чем одну строку до инициализации. Пробовал подключать и от 5В, и от 3,3В, брал и Mega16 и STM32F030C8T6, результат тот же — две строки с прямоугольниками. Пробовал и инициализировать по разному, смотрел код инициализации на разных сайтах, в разных даташитах, ничего не получается. Похоже что бракованный пришел.
После этого заказал у другого продавца дисплей с подпаянным модулем I2C, подключил, все работает, инициализация проходит успешно.
Так вопрос вот в чем: как считаете, это реально брак, или можно с ним еще что-то сделать?
Здравствуйте!
Спасибо за оценку ресурса!
Скорее всего, всё-таки брак. Глюки программной части проявляются немного не так.
У меня был подобный дисплей, на котором я нечаянно перепутал полюса питания и он после этого вёл себя приблизительно так же. Только правда он половину своего экрана заполнял символами, а половину — закрашенными прямоугольниками. А вообще, чтобы точно вынести вердикт, что это не глюк прошивки, нужно подключить такой же, только заведомо годный, в смысле не по другому интерфейсу, а именно по параллельному.
Спасибо вам огромное за проделанный труд!Еще никто так подробно не разжевывал библиотеку НАL…хочется ещё каких нибудь уроков например по энкодеру,разным режимам ШИМ.Еще раз спасибо!
Спасибо за оценку ресурса!
Добрый день!
Вопрос по коду
void LCD_SendChar(char ch)
{
LCD_Data((uint8_t) ch);
delay();
}
Неужели все так просто? Как будто экран синхронизирован с контроллером. Я смотрел спецификацию на этот экран и там написано что нужно отправить число типо LLHHLHHH чтобы вывести s. Мы же отправляем s напрямую как? Никаких lcd библиотек мы не подключали..
А мы её не подключали, а сами написали.
Не могли бы вы пожалуйста подсказать эту библиотеку можно найти в этом уроке?
Приветствую!
Подключил lcd , написал на нем надписи . Всё по уроку. Дошел до указания позиции курсора и тут затык . Keil выдаёт такую ошибку при компиляции :
../Inc/lcd.h(26): error: #20: identifier "uint8_t" is undefined
void LCD_SetPos(uint8_t x, uint8_t y);
Подскажите пожалуйста почему ему не нравится тип uint8_t
#include «stdint.h» в хедер
Здравствуйте!
Спасибо большое за материалы.
Хотел бы оставить свой ИМХО.
void LCD_String(const char* st) — такая сигнатура должна позволить писать вызов как LCD_String("stm32").
А что это за "LCD_SendCommand4(0x01);" в void LCD_Clear(void) ?
Я сейчас уже не помню. Возможно, я объяснял в уроке к AVR. В документации нет такой?
там ошибка на видео правильно написано, сам столкнулся, но быстро понял ))
void LCD_Clear(void) // Функция отчистки дисплея
{
LCD_Command(0x01); // отчиска дисплея
HAL_Delay(2);
}
что за функция sprintf
юрий, sprinf — функция стандартной библиотеки по работе со строками. Данная функция подготавливает строку, собирая её из различных элементов с использованием специальных символов форматирования.
Она должна быть подключена отдельно, включением в хедер stdio.h? В коде этого не видно (
Если можно, расскажите немного подробнее об использовании sprintf()
Интересует, также, как форматировать и выводить числа на экран, полученные с АЦП
Здравствуйте! Очень долго провозился с уроком №6, но в итоге получилось вывести мигающий курсор. Пришлось сделать следующие поправки:
1. Изменил максимальное значение для i. Я в Cube выставлял частоту 100 Мгц.
void delay(void)
{
uint16_t i;
for(i=0;i<50;i++)
{
}
}
2. Увеличил первую задержку с 1 до 5 для инициализации, т.к. в даташите указано, что задержка должна быть более 4,1 мс.
void LCD_ini(void)
{
HAL_Delay(40);
rs0;
LCD_WriteData(3);
e1;
delay();
e0;
HAL_Delay(5);
…
В итоге получилось вывести символ, но и тут проблема:
Первый символ выводится без проблем, курсор переходит вправо, но когда добавляю еще один символ, то ничего не изменяется. Также один символ и курсор во второй клетке. С чем это может быть связано?
P.S. Спасибо за ваши уроки, это очень полезно!
с таймингами играйтесь.
Большое спасибо за Ваш труд и за очень доходчивое объяснение!
Единственно, пришлось бодаться с выводом строки. Выводило только первый символ пока не заменил на:
void LCD_String(char* st)
{
uint8_t i=0;
while(st[i]!=0)
{
LCD_SendChar(st[i]);
delay();
i++;
}
}
Здравствуйте.Перевел проект на STM32f103c8 в средеCUBEIDE.Уже неделю его обрабатываю ,ничего не выводится.
Завел проект в STM32CUBEIDE. Кому нужно сброшу.
Скинь мне пожалуйста, мучаюсь с STM32F103 CUBE IDE
У меня все работает, но одно предупреждение: warning: #223-D: function «sprintf» declared implicitly sprintf(str, «Stm32F407VG»).
Подключите библиотеку «stdio.h» в main.c
Уроки очень хорошо всё объясняют, но остается ещё больше вопросов. Я как начинающий почитал серию уроков про LCD и DS18B20. Как то получилось собрать всё это и запустить. Команда sprintf не позволяет мне преобразовывать float и как вывести теперь температуру с дробной частью, ни как не получается. На данный момент (LCD 16×2) в первой строке выводится 1 датчик и температура целым числом, в второй другой датчик так же целым числом. Может на LCD нужно какие то ещё или просто другие библиотеки? stdio.h не помогло. Или может она у меня не та?
Нашёл ответ на свой вопрос, если кто столкнулся с такой проблемой то всё очень просто в среде программирования нужно изменить параметры билдера на стандартные вместо укороченных. Для Атоллика это:
Go to:
Project Properties -> C/C++ Build —> Tool Settings —> C Linker —> General
Now you see the configuration for «Runtime Library» and there you can select Newlib standard.
Добрый день. Спасибо за уроки. Начал смотреть не давно. вперед пока не забегаю. стараюсь вникнуть. Есть пара вопросов один в тему другой общий. Надеюсь вы ответите.
1 При инициализации дисплея судя по даташиту не обязательно каждый раз посылать данные в шину. достаточно дергать ножкой Е по таймеру. // LCD_WriteData(3); закомментировал, все работает.
void LCD_ini(void)
{
HAL_Delay(40);
rs0;
LCD_WriteData(3);
e1;
delay();
e0;
HAL_Delay(5);
// LCD_WriteData(3);
e1;
delay();
e0;
HAL_Delay(1);
// LCD_WriteData(3);
e1;
delay();
e0;
HAL_Delay(1);
и Второй вопрос самый главный. Сразу после КУБА собираю проект. и там появляется хедер файл main.h но работать с ним не получается при попытке прописать в нем #include «LCD.h» не может его найти, а в созданном нами хедер файле работает. ПОЧЕМУ?
Прописывайте теперь в main.c в соответствующей секции.