Print this page

Учебный курс. Подключение 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.

Related items