Например TDA7294

РадиоКот > Статьи

Фоновый вывод на знакосинтезирующий ЖКИ.

Автор - Михаил Поликарпов.
Опубликовано 30.10.2008.

Здравствуйте, дорогие мои. Хочу поведать вам грустную, но познавательную историю о том, как человек боролся с мегагерцами и миллисекундами.
Началось всё радостно и красиво. Разжился я микроконтроллером от Analog Devices ADuC841 и ЖКИ от Data Vision DV-16210. И всё, казалось бы, хорошо и замечательно, но нет, мало мне ядра, работающего на 20 МГц, и много тех миллисекунд, что требуются для вывода на ЖКИ. Загрустил было добрый молодец, закурил печально в сторонке: Однако принадлежим мы к роду Homo Sapiens, что с англицкого есть "человек разумный", а стало быть будем этот самый разум прикладывать к сложившейся ситуации.
Что нам известно? А известно нам, что для записи одного байта информации в подобный ЖКИ требуется сформировать сигналы на управляющих входах, на входах данных, послать сигнал разрешения и, что самое ужасное, ждать пока контроллер ЖКИ всё это переварит. Ждать обычно приходится пустым циклом, нещадно сжирающим такты ядра, которые, вообще говоря, было бы неплохо пустить на что-нибудь пополезней. Неэффективность такого подхода очевидна и растёт прямо пропорционально частоте ядра. Контроллер само собой о многозадачности ничего не знает и фоновые процессы с низким приоритетом для него пустой звук. Ну что ж, будем изобретать велосипед.
Зададимся некоторыми начальными данными. Известно, что любому ЖКИ пары - другой миллисекунд хватит за глаза, чтобы переварить полученную информацию. Вообще говоря, меньше, но, во-первых, мы не жадные, а, что, во-вторых, будет видно чуть позже. Далее, выбранный контроллер, как построенный на базе ядра 8052, имеет 3 таймера/счётчика, перезагружаться и вызывать прерывание которым с периодом несколько миллисекунд как раз плюнуть. Памяти оперативной тоже хватает, как ни как 2 килобайта с хвостом.
Ну-с, думаю, для начала хватит. Разъясняю идею.
Тем, кто работал в ДОСе, а особенно программировал под него, должны знать, что вывод на дисплей происходит не напрямую, а через буфер, называемый видеопамятью. Сейчас, в принципе то же самое, но намного запутанней, так что продолжу рассказ на примере ДОСа. Так вот, программе, которая хочет что-то вывести на экран, нужно записать требуемое именно в тот буфер (прямо или косвенно через функции ОС, неважно), из которого потом, независимо от этой программы, происходит уже непосредственный вывод на дисплей. Примитивно, но примерно так.
Ну, так идея состоит как раз в вышеизложенном. Выделить память - буфер, запустить таймер, по прерыванию от которого будет происходить вывод одного символа из буфера. Задержка, необходимая контроллеру ЖКИ, формируется из интервалов между переполнениями таймера. В принципе, всё. Скажу сразу, что результаты лично меня воодушевили. До применения этой методики, основной цикл тестовой программы (расчёт логарифма и вывод результатов на ЖКИ) исполнялся 30-40 раз в секунду. Неплохо, конечно, но: После применения, это значение возросло до нескольких сотен!
Кто-то скажет, что такая производительность редко где требуется, и не стоило даже огород городить. Возможно. Но читатель наверняка заметил, что и контроллер используется не самый распространённый, и тестовая программа явно не стандартный "Hello, world!!!". Причина в том, что на контроллер будет возложено достаточно много вычислений, также требующих немало времени, а помимо этого надо ещё АЦП опросить, ЦАП подрегулировать, да и отчитаться перед вышестоящей системой, что всё нормально, вроде не взрываемся =). Задача, конечно, специфичная, но, возможно, кому-то потребуется подобный трюк.
Подключение ЖКИ ни чем не отличается от стандартного. А теперь позволю привести фрагменты программы, реализующий этот принцип. Программа была написана в среде Keil uVision 3, на языке C с ассемблерными вставками.

...
sfr P2 = 0xa0;
sbit Ei = 0xB7;
sbit RW = 0xB5;
sbit RS = 0xB6;
...
unsigned char xdata scr[2][16];
unsigned char x,y;
bit need_scr;
...
void to_scr(void){
  P3 &= 0x1F;
  if (x&0xF0){    //перевод строки
    x=0;
    y=y^1;
    P2=0x80;
    if(y)P2=P2|0x40;
    impulse_e();
  }else{    //вывод символа
    P2 = scr[y][x];
    RS=1;
    Impulse_e();
    x++;
  }
  if (x==0 && y==0){
    need_scr=0;
  }else{
    need_scr=1;
  }
}
...
void timer0(void) interrupt 1{
  ...
if (need_scr)
    to_scr();
  TL0=0x00;
  TH0=0x17;
  TR0=1;
}
...
void init_display(bit z){
  unsigned char i,j;
  need_scr=0;
  x=0;y=0;
  if (z){
    for (i=0;i<2;i++)
    for (j=0;j<16;j++)
    scr[i][j]=' ';
  }
  ...    //стандартная инициализация дисплея
  Tmod = 0x01;
  IE |= 0x82;
  TL0=0x00;
  TH0=0x17;
  TR0=1;
}
...
void main(void){
  ...
  init_display();
  ...
  While(1){
    ...
    scr[0][1]='x'; //просто для примера
    need_scr=1;
    ...
  }
}

Ну, примерно так. Само собой, посимвольно писать в буфер, как в примере выше, бессмысленно. Имеются процедуры вывода чисел, строк и т.д. Здесь каждый пишет, что ему нужно. Помимо этого имеется несколько интересных моментов. В прерывании можно помимо вывода символа ещё, например, опрашивать клавиатуру, а если точно выставить частоту, с которой вызывается прерывание, можно и время отмерять (хотя в этом контроллере имеется счётчик временных интервалов, служащий этой цели, пользоваться им невозможно, точность никакая, RC-генератор +10%, как-никак). Так, например, было в авторском варианте.
Кстати, чуть не забыл о "во-вторых". Как раз чуть больше может потребоваться, для точной установки частоты, ну и опять же чтобы ещё чуть-чуть сэкономить времени. Меньше раз в единицу времени прерываемся, значит, больше работаем.

Вопросы, как всегда в Форум.


ID: 29

Как вам эта статья?

 Нравится
 Так себе
 Не нравится

Заработало ли это устройство у вас?

 Заработало сразу
 Заработало после плясок с бубном
 Не заработало совсем

15 1 5
1 1
Подробно