- PVSM.RU - https://www.pvsm.ru -

Алгоритм Брезенхэма в приложениях реального времени — часть вторая

Продолжение поста Алгоритм Брезенхэма в приложениях реального времени [1]

Напомним, что речь идет написании программы вывода для лазерных сканаторов
image

В моем случае лазерный сканатор используется на установке так называемой лазерной стереолитографии [2], где рисование идет лучом ультрафиолетового лазера по поверхности жидкого фотополимера. Глазом ультрафиолет увидеть нельзя, но можно увидеть гармоники (излучение относительно малой мощности в видимом диапазоне, которое генерируется в лазере на неосновной для него длине волны) а так же флюоресценцию от фотополимера или от простой белой мелованной бумаги.
image
Тут на снимке — мелованная бумага, слегка пропитавшаяся фотополимером, на покровном стекле нанесен слой фотополимера толщиной примерно 1 мм, на котором рисуется, как можно заметить, любимое на Хабре слово. Слева — «точка отстоя», куда луч уходит после рисования. След между рисунком и точкой отстоя — это холостой переброс луча, который мы видим только за счет значительного послесвечения флюоресценции бумаги. В терминах обсуждавшегося ранее алгоритма этот переброс можно представить как последовательное выполнение

   putXY(x,y, wait); //  например, x=32000, y=32000
   putXY(x0,y0, wait); // yнапример, x0=100, y0=100 - точка отстоя

Никаких «зигзагов» или «петель» у хорошего сканатора мы не увидим. По крайней мере — глазом.

Хотя если рядом будет источник электро-магнитной помехи типа мобильника может получится вот такое
image

Вернемся теперь к нашему алгоритму. В комментах к первой части никто не догадался, что же мы увидим в результате реализации предложенной реализации алгоритма Брезенхэма.
У видим мы вот такую штуку
image

Слева — то, что мы увидим, справа — то, что ожидали увидеть.
Короткие яркие чёрточки говорят о том, что скорость сканирования иногда уменьшается. Как же она может уменьшаться, если у нас в контроллере нет никакой многозадачности и вообще операционной системы? Очень просто — у нас есть прерывания! Даже если контроллер не нагружен никаким другими функциями, кроме выдачи на ЦАПы, у нас всё равно остаются прерывания, связанные с коммуникацией с управляющим компьютером. Например, через UART (COM-порт) или Ethernet. Вроде бы не так много времени занимают прерывания и вроде бы не так часто мы делаем опрос. Чёрточки могут быть и короче, превращаясь в точки, но полностью от них избавится при такой реализации задержки невозможно

void   putpixel(int x, int y, int wait))
{    outPortX(x);  // установить на ЦАП X значение x
      outPortY(y);  // установить на ЦАП Y значение y 
      delay(wait); // задержка wait  единиц таймера
}  

Часто задержки в программировании микроконтроллеров делают на простом счетчике

void    delay(int wait) // задержка wait  единиц "таймера"
{ int i;
  for(i=0;i<wait; i++);
}

Дёшево и сердито неправильно. В случае прерывания такая реализация задержки никогда не узнает о том, что программа «уходила» в другое место и требуемое время прошло. Идеологически более правильная задержка использует аппаратный счетчик таймера

void    delay(int wait) 
{  unsigned int t0;
   t0 = /LPC_TIM1->TC
//LPC_TIM1->TC  - указатель на адрес аппаратного счетчика таймера й  в контроллерах LPC 17xx
  while(LPC_TIM1->TC - t0 < wait); 
}

В этом случае функция задержки автоматически узнает о произошедшем прерывании, хотя и не гарантирует абсолютную точность «до такта» процессора. Если программа основное время проводит в задержках этим можно было бы и ограничится, однако для случая быстрых перемещений использование аппаратного таймера должно быть сквозным

/* real time Bresenham alg. implementation */
void Line(int x1, int y1, int x2, int y2, int wait)
{  int x1,y1,dx,dy,sx,sy,d,d1,d2;
   unsigned int t0  = LPC_TIM1->TC;
   int i, x,y;

   if( x2 >= x1)
   {  dx = x2 - x1;
      sx = 1;
   } else {
      dx = x1 - x2; 
      sx = -1;
   } 
   if( y2 >= y1)
   {  dy = y2 - y1;
      sy = 1;
   } else {
      dy = y1 - y2; 
      sy = -1;
   } 

/****************************/
   if(dy <= dx) 
   {  d = (dy << 1) - dx;
      d1 = dy << 1;
      d2 = (dy - dx) << 1;
     for(x=x1+sx,y=y1,i=1; i <= dx ; i++,x+=sx)
      {  if(d > 0)
         {  d += d2;
            y += sy;
            putXY(x,y,wait,&t0);
         } else {
            d += d1;
            putX(x, wait, &t0);
         }   
      } 
   } else {
      d  = (dx << 1) - dy;
      d1 = dx << 1;
      d2 = (dx - dy) << 1;

      for(x=x1,y=y1+sy,i=1;i <= dy ; i++,y += sy)
      {  if(d > 0)
         {  d += d2;
            x += sx;
            putXY(x,y,wait, &t0);
         } else {
            d += d1;
            putY(y, wait, &t0);
         }   
      } 
   } /* endif(dy <=dx) */
}

void putXY(int x, int y, int wait, unsigned int *pT0)
{     outPortXY(x,y);  // установить на ЦАПах X и Y значения x и y 
                                  // в простейшем случае   #define outPortXY(x,y)   outPortX(x); outPortY(y); 
/* задержка wait  единиц таймера */
  while(LPC_TIM1->TC - *pT0 < wait); 
  *pT0 += wait;
}  

void putX(int x,  int wait, unsigned int *pT0)
{     outPortX(x);  // установить на ЦАП X значение x
/* задержка wait  единиц таймера */
  while(LPC_TIM1->TC - *pT0 < wait); 
  *pT0 += wait;
}  
void putY(int y,  int wait, unsigned int *pT0)
{     outPortY(y);  // установить на ЦАП Y значение y
/* задержка wait  единиц таймера */
  while(LPC_TIM1->TC - *pT0 < wait); 
  *pT0 += wait;
}  

Заметим, что в этом варианте автоматически учитывается как разные времена для вывода на ЦАП для одного канала или двух каналов, так и разброс времени вывода, если используется вывод до получения готовности.

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

Ну и напоследок — если вы нахомутали с программой, то можете увидеть самые неожиданные загогулины вместо ожидаемых тестовых круга и квадрата.
image
В этом случае «всё почти работало», только появлялись непонятные зубчики. Как оказалось, одна строка y += sy; была заменена на x += sx;. Между прочим, из-за случайного задетого тачпада на ноутбуке.

Автор: buratino

Источник [3]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/algoritmy/51808

Ссылки в тексте:

[1] Алгоритм Брезенхэма в приложениях реального времени: http://habrahabr.ru/post/205654/

[2] лазерной стереолитографии: http://wiki.laser.ru/index.php/%D0%9B%D0%B0%D0%B7%D0%B5%D1%80%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D0%B5%D1%80%D0%B5%D0%BE%D0%BB%D0%B8%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F

[3] Источник: http://habrahabr.ru/post/207740/