- PVSM.RU - https://www.pvsm.ru -
Продолжение поста Алгоритм Брезенхэма в приложениях реального времени [1]
Напомним, что речь идет написании программы вывода для лазерных сканаторов
В моем случае лазерный сканатор используется на установке так называемой лазерной стереолитографии [2], где рисование идет лучом ультрафиолетового лазера по поверхности жидкого фотополимера. Глазом ультрафиолет увидеть нельзя, но можно увидеть гармоники (излучение относительно малой мощности в видимом диапазоне, которое генерируется в лазере на неосновной для него длине волны) а так же флюоресценцию от фотополимера или от простой белой мелованной бумаги.
Тут на снимке — мелованная бумага, слегка пропитавшаяся фотополимером, на покровном стекле нанесен слой фотополимера толщиной примерно 1 мм, на котором рисуется, как можно заметить, любимое на Хабре слово. Слева — «точка отстоя», куда луч уходит после рисования. След между рисунком и точкой отстоя — это холостой переброс луча, который мы видим только за счет значительного послесвечения флюоресценции бумаги. В терминах обсуждавшегося ранее алгоритма этот переброс можно представить как последовательное выполнение
putXY(x,y, wait); // например, x=32000, y=32000
putXY(x0,y0, wait); // yнапример, x0=100, y0=100 - точка отстоя
Никаких «зигзагов» или «петель» у хорошего сканатора мы не увидим. По крайней мере — глазом.
Хотя если рядом будет источник электро-магнитной помехи типа мобильника может получится вот такое
Вернемся теперь к нашему алгоритму. В комментах к первой части никто не догадался, что же мы увидим в результате реализации предложенной реализации алгоритма Брезенхэма.
У видим мы вот такую штуку
Слева — то, что мы увидим, справа — то, что ожидали увидеть.
Короткие яркие чёрточки говорят о том, что скорость сканирования иногда уменьшается. Как же она может уменьшаться, если у нас в контроллере нет никакой многозадачности и вообще операционной системы? Очень просто — у нас есть прерывания! Даже если контроллер не нагружен никаким другими функциями, кроме выдачи на ЦАПы, у нас всё равно остаются прерывания, связанные с коммуникацией с управляющим компьютером. Например, через 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(), вне этой функции эффект влияния прерываний на длительность выполнения может по-прежнему проявляться. Надеюсь, что читателю не составит труда догадаться, как следует поступить в общем случае.
Ну и напоследок — если вы нахомутали с программой, то можете увидеть самые неожиданные загогулины вместо ожидаемых тестовых круга и квадрата.
В этом случае «всё почти работало», только появлялись непонятные зубчики. Как оказалось, одна строка 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/
Нажмите здесь для печати.