AVR Урок 12. LCD индикатор 16×2. Часть 5

 

 

 

 

Урок 12

Часть 5

 

LCD индикатор 16×2

 

 

Сегодня мы продолжим изучение жидкокристаллического индикатора символьного, который способен выводить определённые символы в две строки по 16 символов в каждую.

В прошлой части мы завершили и проверили написание кода функции, которая выводит любой символ на экран дисплея.

Теперь пришло время написать функцию вывода на экран целой строки, так как выводить посимвольно не совсем удобно, хотелось бы работать со строками. Добавим данную функцию прямо перед функцией main() и передавать мы ей будем массив символов неопределённой размерности

 

//—————————————-

void str_lcd (char str1[])

{

 

}

//—————————————-

 

Вызовем данную функцию в main(), удалив перед этим весь код посимвольного вывода на дисплей

 

LCD_ini(); //Инициализируем дисплей

setpos(0,0);

str_lcd("Hello World!");

 

Дальше начнём писать тело функции вывода строки. Объявим в теле функции переменную для символа. Переменная у нас будет несколько иного типа. Как правило с таким типом лучше распознаются коды символов. Вы можете, конечно, поэксперементировать с другими типами

 

void str_lcd (char str1[])

{

  wchar_t n;

 

Далее мы, соответственно, организуем цикл и будем попеременно перебирать все переданные символы в массиве и выводить их на дисплей. Применим также мы вариант представления нулевого симвлова 'n' и именно до него мы и будем перебирать символы

 

  wchar_t n;

  for(n=0;str1[n]!='';n++)

    sendchar(str1[n]);

}

 

Соберём код и проверим в протеусе работу кода

 

image24

 

Теперь можно попробовать вывести строку ещё и в другое место экрана. Напишем код в main()

 

str_lcd("Hello World!");

setpos(2,1);

str_lcd("String 2");

while(1)

 

Соберём код и посмотрим результат

 

image25

 

Всё работает! Отлично!

Ну конечно нужно ещё помотреть, как будет код работать на живом дисплее с живым контроллером. Для этого мы прошьём контроллер

 

image26

 

Оформляем функции в отдельный модуль

 

Дошли мы с кодом до такого состояния, что наш главный и единственный файл с кодом переполнился до того, что в нём теперь тяжело уже вообще что-то найти. Как же мы с этим можем боротья? Бороться с этим мы будем путём оформления кода функций для отдельно взятого устройства или шины или какой-то технологии в отдельный модуль. Грамотный модуль состоит как правило из заголовочного файла и файла реализации функций. Поэтому давайте для нашего LCD дисплея мы так и поступим. Также это всё дело нужно для того, что если мы будем писать новый проект, то мы данные файлы будем просто к нему подключать, если нам потребуется воспользоваться LCD дисплеем. Это будет нашей так называемой библиотекой для дисплея. Конечно, библиотеки обычно пишутся и компилируются в отдельный файл lib, но в этом случае обычно нет исходного кода и данные библиотеки не могут быть подправлены. А наша библиотека будет вполне исправимой и нам ещё ой как пригодится в будущем.

 

 

Но прежде, чем создать данную библиотеку, мы создадим главный заголовочный файл и назовём его main.h, чтобы убрать в данный файл все подключенные библиотеки, различные глобальные переменные и макроподставновки

Для этого мы в дереве проектов щёлкаем правой кнопкой по нашему проекту Test09 и выбираем в контекстном меню субменю Add, а в нём уже выбираем пункт New Item

 

image27

 

И в открывшемся диалоге выбираем тип файла, который мы будем создавать, "Include File" И внизу в имени файла меняем IncFile1 на main, затем жмём кнопку Add.

Соответственно, у нас создастся файл main.c вот с таким содержимым

 

#ifndef MAIN_H_

#define MAIN_H_

 

#endif /* MAIN_H_ */

 

Это очень хорошо. Например. в Keil, когда мы занимается программированием контроллеров STM, мы должны это всё писать в ручную. Здесь данная директива говорит о том, что, если файл уже подключался в исполняемый код, чтобы прероцессор его повторно не включал.

В данный файл мы посместим подключения всех заголовочных файлов библиотек и все макроподстановки, а в файле Test09.c всё это, конечно, мы удалим

 

#ifndef MAIN_H_

#define MAIN_H_

 

#define F_CPU 8000000UL

#include <avr/io.h>

#include <avr/interrupt.h>

#include <util/delay.h>

#include <stdio.h>

#include <stdlib.h>

//—————————————-

#define e1 PORTD|=0b00001000 // установка линии E в 1

#define e0 PORTD&=0b11110111 // установка линии E в 0

#define rs1 PORTD|=0b00000100 // установка линии RS в 1 (данные)

#define rs0 PORTD&=0b11111011 // установка линии RS в 0 (команда)

//—————————————-

 

#endif /* MAIN_H_ */

 

Но недостаточно данный заголовочный файл подключить в Solution Explorer, его также мы должны в файле Test09.c подключить в самом начале в код

 

#include "main.h"

//—————————————-

 

Соберём проект, ещё раз проверим его работоспособность.

Теперь начнём создавать нашу библиотеку для дисплея.

Для этого мы таким же образом, как и main.h, создадим заголовочный файл lcd.h

Содержание получится аналогичным, только вместо MAIN везде будет LCD. Подключим данный файл в файле main.h

 

#include <stdlib.h>

#include "lcd.h"

 

И наоборот, в файл lcd.h мы подключим файл main.h

 

#ifndef LCD_H_

#define LCD_H_

#include "main.h"

 

Насчет того, что получится какое-то перекрёстное зацикливание, можно не беспокоиться — директивы не дадут такому случиться.

Также все макроподстановки из файла main.h мы заберём в файл lcd,h, а в main.h удалим

 

#include "main.h"

//—————————————-

void LCD_ini(void);

void setpos(unsigned char x, unsigned y);

void str_lcd (char str1[]);

void sendchar(unsigned char c);

//—————————————-

 

А, чтобы забрать все функции по работе с дисплеем из файла Test09.c, мы создадим теперь уже другой файл — lcd.с. В нём и будет код реализации всех функций

Создаётся файл точно таким же образом, только вместо "Include File" мы выбираем тип файла "C File".

Файл lcd.c создан. В нём уже не будет никаких директив, единственное, будет авторский комментарий, который мы удалим, чтоб не мешался.

В данном файле мы также подключим заголовочный файл lcd.h

 

#include "lcd.h"

//—————————————-

 

Теперь мы в данный файл перенесём полностью все функции, предназначенные для работы с дисплеем, из файла Test09.c. В нём останутся только две фунции — port_ini и main().

Тем самым мы очень серьёзно разгрузим главный файл приложения, сделав его удобочитаемым.

Но этого нам недостаточно. Ни одна функция теперь не будет "видна" в файле Test09.c. Поэтому те функции, которые мы будем использовать в других файлах, мы обязаны объявить, или, как говорят в народе, создать на них прототипы. Делается это обычно в заголовочном файле. Поэтому мы создадим прототипы в заголовочном файле lcd.h. Прототип делается очень легко. Пишется, или обычно копируется заголовок функции со всеми аргументами (всё кроме тела) и в конце ставится точка с запятой. Нам нужны будут функции инициализации дисплея, позиционирования на дисплее и вывода строки на дисплее. Символы мы отдельно пока выводить не будем, поэтому на соответствующую функцию мы прототип не создаём. Вот наши прототипы

 

#include "main.h"

//—————————————-

void LCD_ini(void);

void setpos(unsigned char x, unsigned y);

void str_lcd (char str1[]);

//—————————————-

#define e1 PORTD|=0b00001000 // установка линии E в 1

 

Теперь соберём файл, запустим его в протеусе, и проверим его работоспособность. Также проверим на практике. Если всё работает, то мы всё сделали правильно. Проект на весь урок приложен внизу и доступен по ссылке "Исходный код".

Таким образом, в сегодняшнем уроке мы много чему научились. Мы научились работать с символьным дисплеем и подключать его к контроллеру AVR. Также мы в рамках данного урока научились грамотному оформлению кода и использованию модульного программирования.

 

Предыдущая часть Программирование МК AVR Следующий урок

 

Исходный код

 

Техническая документация на дисплей

Техническая документация на контроллер дисплея HD44780

 

Программатор и дисплей можно приобрести здесь:

Программатор (продавец надёжный) USBASP USBISP 2.0

Дисплей LCD 16×2

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

AVR LCD индикатор 16×2

27 комментариев на “AVR Урок 12. LCD индикатор 16×2. Часть 5
  1. Роман:

    Добрый день. У вас 

    void str_lcd (char str1[])

    {

      wchar_t n;

    здесь ошибка?

    Мой авр студия ругаеться 

    Я изменил на  char n;

    И  спасибо за уроки.

    • admin:

      Нет, не ошибка. У меня работает нормально.

      Видимо, возможно, у Вас уже другая версия студии и данный тип она не знает.

      Вооще здеь можно использовать, да, возможно и нужно char.

      Просто я. видимо, из какого-то древнего проекта у себя по Visual C взял это, здесь уже не очень актуально.

      Это вообще тип для Unicode.

      Ну пусть остаётся.

      Вам также спасибо за оценку моего труда!

  2. Роман:

    Добрый день.

    Хотел вывести  счетчик на экран.

    Неработает.

    char test = 0;

     while(1)
        {
            test ++;
            setpos(1,0);
            //sendchar('o');
            str_lcd("Hello");
            setpos(1,1);
            //sendchar('o');
            sendchar(test);
            //TODO:: Please write your application code 
            _delay_ms(200);
        }

  3. Aspir:

    Доброго времени суток . хочется выразить вам благодарность за ваши труды -для нас , очень кратко и понятно все что за чем и как .

    и хочется сказать большое спасибо за такие уроки ибо когда учился я эту тему не мог понять , а оказывается тут легко ! 

    нам бы такого преподавателя !))))))))

  4. Николай:

    Всё ввел буква в букву — не работает

    • Попробуйте скачать проект и запустить. Возможно, что-то недоввели.

      Также дисплеи могут различаться в зависимости от производителя. Так что внимательно читайте документацию именно на своё устройство.

  5. Сергей:

    Здравствуйте. В строке for(n=0;str1[n]!='';n++) компилятор ругается на отсутствие костанты ' '.\0 исправляет ситуацию,  хотелось бы пояснений.

    • Здравствуйте!
      Это автоматика текстовая на сайте так срабатывает, она такие вещи убирает. Поэтомоу пользуйтесь лучше скачанным проектом.

  6. Алексей:

    for (n=0; str1[n]!='\0';n++)

    в тексте рассказа надо добавить \0, чтоб работало. В проекте все есть.

    Спасибо на труд. Полезно. Коротко. Эффективно.

  7. Антон:

    Здравствуйте. При компиляции выдает предупреждение: deprecated conversion from string constant to 'char*'.

  8. Melamed:

    Спасибо Вам за ваши уроки.

    Хотелось бы прочитать, как менять символы с заданными кодами для LCD индикаторов. Это необходимо делать для их руссофикации в частности. Знаю, что это возможно. Но информации на эту тему не могу найти.

    • Думал на эту тему, но всё некогда этим загнаться.

      • Дмитрий:

        Добрый день! Хочу поблагодарить за уроки, благодаря Вам перестаю потихоньку смотреть на си как на китайскую грамоту. С Вашего позволения хочу поделиться дополнением к Вашему уроку про вывод своего символа на дисплей. Как известно у этого контроллера дисплея есть память для восьми программируемых пользователем символов. Сваял функцию:
        //Пишем в память свой символ(64-команда 01000000 из даташита)
        void RAM_write( char CGRAM_adress, char d0, char d1, char d2, char d3, char d4, char d5, char d6, char d7)
        {
        sendbyte(64 + (CGRAM_adress << 3), 0);//Выбираем адрес ячейки памяти(от 0 до 7)
        sendbyte(d0,1); //Пишем строки символа
        sendbyte(d1,1);
        sendbyte(d2,1);
        sendbyte(d3,1);
        sendbyte(d4,1);
        sendbyte(d5,1);
        sendbyte(d6,1);
        sendbyte(d7,1);
        }
        //_______________________________________________________
        Теперь пример(человечек):
        RAM_write(0,0x0E,0x04,0x0E,0x15,0x04,0x0A,0x0A,0x00);//Пишем в память коды своего символа
        sendchar(0);//Выводим свой символ из адреса 0

    • Дмитрий:

      Если дисплей руссифицированный, то в даташите в таблице символов есть русские буквы. Выводите функцией segchar код символа. В другом случае только самому рисовать и писать в память дисплея(но не больше восьми).

  9. Саня:

    Привет.Спасибо за уроки.  хотел сказать,1то в видеоуроке ошибка и я не совсем пойму как это у тебя работало но доказательство на Видео 🙂

     

    В Датачите у них распиновка 

    RS R/W DB7 DB6 DB5 DB4

    а по твой распиновки

    RS R/W DB4 DB5 DB6 DB7

    У меня заработало только после того,.как я сделал по датачиту и загнал при инитиализации 3 раза

    00001100

    либо нужно перекинуть ножки

     

     

  10. Глеб:

    lcd.c никуда не подключен ? 

  11. Vitalik:

    Здравствуйте как поменять подключения выводов LCD к МК к примеру DB4-PD5..DB7-PD2

    • Виталий:

      Ну просто переписать проект). У меня не заработал, на атмеге88. Хотя проверял на 8 битном подключении с другим проектом, и лсдишку, и мегу. Инициализацию не проходит, строка первая только. Придется ковыряться в подключении(

      • Виталий:

        Одна мега88 оказалась с битыми портами D6, D7, поэтому взял другую, проверил на проекте с 8 бит подключением для меги328 — работает. Автор, похоже, не читает комменты к старым урокам. Протеус 7 глючит с мегой88, так что проверить только в железе остается. У меня смд переходник, придется купить соик мегу8. Печалька, конечно.

  12. Алексей:

    Как передать значения переменной
    unsigned char menu_tar=1;
    if(menu_tar==1){setpos(0,0); str_lcd(«Menu PWM:»); sendbyte(menu_tar, 1);}
    if(menu_tar==1){setpos(0,0); str_lcd(«Menu PWM:»); str_lcd(menu_tar);} Думал так должно работать. Но тоже не то. Ну очень нужно. Всю голову сломал.

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

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

*