Учебный курс. Подключение lcd к микроконтроллеру. Разбираемся с флагом занятости.

13/11/2009 - 23:22

Чтение флага занятости

   Для выполнения команд или вывода данных контроллеру символьного lcd требуется определенное время. Поэтому в конце функций записи мы использовали программные задержки в 40 us. Это вполне работоспособное решение, однако не единственно возможное.   
   Контроллер символьного lcd имеет флаг занятости BF. Он устанавливается в 1,  когда контроллер выполняет внутреннюю работу и сбрасывается, когда контроллер освобождается. Вместо программной задержки можно опрашивать состояние этого флага и запускать цикл записи команд/данных только когда флаг сброшен.

   Для 8-ми разрядной шины процедура чтения флага занятости и адреса выглядит следующим образом:

1.    Настроить порт микроконтроллера на вход
2.    Установить RW = 1
3.    Установить RS = 0
4.    Установить E = 1
5.    Считать значение байта данных с шины DB7…DB0
6.    Установить E = 0
7.    Установить RW = 0
8.    Настроить порт на выход

7 бит считанного байта – будет флагом занятости BF. Биты с 6 по 0 –  значение адресного счетчика. Сейчас нас интересует только флаг занятости.
   Оформим эту последовательность в виде функции, а шаги с 4 по 6 засунем в цикл. Условием выхода из цикла будет сброс флага.

#define FLAG_BF 7

#pragma inline = forced
void LCD_Wait(void)
{
  unsigned char data;
  DDRX_DATA = 0;             //конфигурируем порт на вход
  PORT_DATA = 0xff;          //включаем pull-up резисторы
  SetBit(PORT_SIG, RW);      //RW в 1 чтение из lcd
  ClearBit(PORT_SIG, RS);     //RS в 0
  do{
    SetBit(PORT_SIG, EN);    
    _delay_us(2);
    data = PIN_DATA;               //чтение данных с порта
    ClearBit(PORT_SIG, EN);    
  }while((data & (1<<FLAG_BF))!= 0 );
  ClearBit(PORT_SIG, RW);
  DDRX_DATA = 0xff;
}

   Ничего нового, только качестве цикла мы использовали do…while. Он сначала выполняет блок кода, а затем проверяет условие повтора цикла. Таким образом цикл всегда выполняется хотя бы раз.

Исправление кода

   Теперь состыкуем функцию ожидания готовности дисплея LCD_Wait() c функциями записи команд/данных.

//функция записи команды
void LCD_WriteCom(unsigned char data)
{
  LCD_Wait();                    //ожидание готовности дисплея
  ClearBit(PORT_SIG, RS);    //установка RS в 0 - команды
  LCD_CommonFunc(data);
}

//функция записи данных
void LCD_WriteData(unsigned char data)
{
  LCD_Wait();                    //ожидание готовности дисплея
  SetBit(PORT_SIG, RS);      //установка RS в 1 - данные
  LCD_CommonFunc(data);
}

Надеюсь вы не забыли, что в предыдущей статье я вынес общие куски кода в отдельную встраиваемую функцию. Ее тоже нужно подправить - удалить программную задержку  _delay_us(40);

#pragma inline = forced
void LCD_CommonFunc(unsigned char data)
{
  PORT_DATA = data;        //вывод данных на шину индикатора
  SetBit(PORT_SIG, EN);     //установка E в 1
  _delay_us(2);
  ClearBit(PORT_SIG, EN);    //установка E в 0 - записывающий фронт
}

Исправим функции вывода строк следующим образом.

//функция вывода строки из флэш памяти
void LCD_SendStringFlash(unsigned char __flash *str)
{
  unsigned char data;            
  while (*str)
  {
    data = *str++;
    LCD_WriteData(data);
  }
}

//функция вывда строки из ОЗУ
void LCD_SendString(unsigned char *str)
{
  unsigned char data;
  while (*str)
  {
    data = *str++;
    LCD_WriteData(data);
  }
}

Все готово. Жмем F7. У меня все прошло без ошибок, но это не значит что программа работает. Прошиваем микроконтроллер ... и?
Уффф... работает.

Директивы условной компиляции

   Алгоритм чтения флага занятости потяжелее программной задержки в плане объема кода, но дает преимущество в быстродействии. Однако с помощью директив условной компиляции можно совместить два куска кода в одной функции, и предоставить пользователю (ну или себе) возможность выбора подходящего варианта. Делается это так.

В хедере:

#define CHECK_FLAG_BF

В сишном файле:

void LCD_Wait(void)
{
#ifdef CHECK_FLAG_BF

   //код реализующий опрос флага занятости

#else

   //программная задержка

#endif
}

   Буквально это означает, что если определена константа CHECK_FLAG_BF, то компилироваться будет код опроса флага занятости, в противном случае – программная задержка. Так как в хедере мы определили нужную константу, функция ожидания готовности дисплея будет опрашивать флаг. Но если константу закомментировать, то функция будет состоять из программной задержки.

#pragma inline = forced
void LCD_Wait(void)
{
#ifdef CHECK_FLAG_BF
  unsigned char data;
  DDRX_DATA = 0;                //конфигурируем порт на вход
  PORT_DATA = 0xff;            //включаем pull-up резисторы
  SetBit(PORT_SIG, RW);        //RW в 1 чтение из lcd
  ClearBit(PORT_SIG, RS);       //RS в 0 команды
  do{
    SetBit(PORT_SIG, EN);       //EN = 1
    _delay_us(2);
    data = PIN_DATA;            //чтение данных с порта
    ClearBit(PORT_SIG, EN);     //EN = 0
  }while((data & (1<<FLAG_BF))!= 0 ); //пока флаг не сброшен, крутимся в цикле
  ClearBit(PORT_SIG, RW);
  DDRX_DATA = 0xff;
#else
  _delay_us(40);
#endif  
}

Проверим не обманул ли я вас.
В настройках проекта устанавливаем разрешение выдачи файла листинга.
Project > Options…    
С/С++ Compiler >List  галочка Output List File


Включаем оптимизацию по размеру на максимум
С/С++ Compiler > Optimizations

 
Сохраняем.
Сейчас строчка #define CHECK_FLAG_BF не закомментирована.
Жмем F7.
Открываем файл листинга - 170 байтов.


 
Закомментируем #define CHECK_FLAG_BF и снова запустим компиляцию. Теперь - 144 байта.
Значит не обманул.

Файлы

Проект для IARa. Проект для WINAVR.

Comments   

# alexandershahbazov 2009-12-08 20:38
а для 4-х разрядной шины
# Pashgan 2009-12-08 22:03
Скоро будет
# Guest 2010-02-06 18:50
После "Исправим функции вывода строк следующим образом." функция вывода строки из флэш памяти не правильно написана, насколько я понимаю.

должно быть както так:
void LCD_SendStringF lash(prog_uint8 _t *str)
{
uint8_t data = pgm_read_byte(s tr);
while (data)
{
LCD_WriteData(d ata);
str++;
data = pgm_read_byte(s tr);
}
}

Ну и пользуясь случаем - спасибо за статью.
# Guest 2010-02-06 19:09
или для iar это правильно. в общем, я запутался в этих компиляторах.
# Pashgan 2010-02-08 08:04
Для iar`a это правильно.
# Electronik 2010-10-06 09:19
У меня такая проблема: есть функция, которая считывает флаг занятости ЖКИ BF, когда я её вставляю в функии записи данных или команд, на индикатор ничего не выводится (горит тоько верхняя строка чёрных квадратов), проверил напряжение на DB7 там около 5 вольт, когда её комменчу - всё работает отлично. Не могу понять, что я не так делаю при опросе флага.
static inline void LCD_Wait(void)
{
unsigned char d;
DDR_DATA = 0;
PORT_DATA = 0xff;
ClearBit(PORT_SIG, RS);
SetBit(PORT_SIG, RW);
do
{
SetBit(PORT_SIG, E);
_delay_ms(2);
ClearBit(PORT_SIG, E);
d = PIN_DATA;
}
while(d & (1
# Pashgan 2010-10-06 17:23
Какой контроллер у дисплея?
# Brigadir 2011-02-17 13:57
Слишком большая пауза при стробировании линии Enable LCD. Достаточно нескольких циклов, а у тебя аж 2 милисекунды - это очень долго.
# Brigadir 2011-02-18 19:40
Или зацикливается на проверке флага. Что-то последняя строка в приведенном коде непонятная, надо бы исправить, и тогда все станет ясно.
# Electronik 2010-10-07 16:33
ЖКИ WH1602B-YYH-CTK , контроллер AVR ATMega16
# Electronik 2010-10-08 19:43
ЖКИ WH1602B-YYH-CTK , контроллер Ks0066
# Илья 2011-02-15 09:51
Как с помощью вашей библиотеки вывести значение переменной на LCD.
# Pashgan 2011-02-16 22:12
С помощью вот этой библиотеки http://www.chipenable.ru/index.php/library-for-iar/12-library-for-iar/26-bcdrar.html
Скачай какой нибудь из проектов, где она применяется, если не разберешься как ее использовать. Например вот этот - часы на микроконтроллер е http://www.chipenable.ru/index.php/files-for-lessons-iar-avr/13-files-for-lessons/127-chasy-na-mikrokontrollere-iar-avr.html
# megannnn98 2011-10-26 10:33
спасибо за интересные и подробные статьи

У вас недостаточно прав для комментирования.