Алгоритм брезенхема формула для управления нагревателем. Два микроконтроллерных регулятора мощности

Мы научились выводить на TFT дисплей символы и строки, в этой будем учиться рисовать геометрические фигуры. Геометрических фигур, которые могут пригодиться при создании графического интерфейса, не так уж много, основные из них, это прямоугольник и круг именно их мы и будем учиться рисовать, в двух вариантах закрашенные и не закрашенные. Скажу сразу, что в статье подробно будут описаны алгоритмы рисования лишь некоторых геометрических фигур, чего должно хватить для понимания общих принципов построения растрового изображения. Начнём с самой простой фигуры - закрашенного прямоугольника.
Из прошлой статьи мы помним, что если задать координаты точки, затем просто отправлять цвет, то SSD1289 сам по выбранному, при инициализации алгоритму, будет закрашивать точки. Но в этом случае есть одна особенность, контроллер переходит на следующую строку, только когда достигает конца текущей строки.
Нарисовать закрашенный прямоугольник, нам помогут следующие регистры.

С помощью этих регистров мы можем задать начало и конец области, в которую будем писать, затем в цикле, нужное количество раз, отправим цвет, а контроллер все сделает сам, по заданному при инициализации алгоритму. Но теперь он будет переходить на следующую строку, когда достигнет конца, указанной нами области.
Для записи границ области по Х, предназначен один регистр R44, а для записи границ по Y - два регистра R45 и R46. Давайте описанное выше оформим в виде функции, для удобства, код, который отвечает за выделение рабочей области, вынесем в отдельную функцию Set_Work_Area().
void Set_Work_Area(uint16_t y1, uint16_t x1, uint16_t x2, uint16_t y2) { Lcd_Write_Reg(0x0044,((x2 << 8) | x1)); Lcd_Write_Reg(0x0045,y1); Lcd_Write_Reg(0x0046,y2); Set_Cursor(x1, y1); } /////////////////////////////////////// void Draw_Area(uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint16_t color) { register uint16_t x,y; Set_Work_Area(left, top, right, bottom); for(y=top; y<=bottom; y++) { for(x=left; x<=right; x++) { Lcd_Write_Data(color); } } Set_Work_Area(0, 0, 319, 239); }


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

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



На картинке выше жёлтым цветом показана линия до растеризации, зелёным и красным - расстояние до центров ближайших ячеек.
А вот и код для рисования линии.
void Draw_Line (uint8_t size,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint16_t color) { int deltaX = abs(x2 - x1); int deltaY = abs(y2 - y1); int signX = x1 < x2 ? 1: -1; int signY = y1 < y2 ? 1: -1; int error = deltaX - deltaY; for (;;) { Draw_Point(size,x1,y1,color); if(x1 == x2 && y1 == y2) break; int error2 = error * 2; if(error2 > -deltaY) { error -= deltaY; x1 += signX; } if(error2 < deltaX) { error += deltaX; y1 += signY; } } }
Для того чтобы нарисовать прямоугольник нам понадобится 4 линии, для удобства вынесем в отдельные функции рисование горизонтальных и вертикальных линий,
void Draw_Horizont_Line(uint8_t size,uint16_t x1,uint16_t y1,uint16_t y2,uint16_t color) { Draw_Line(size, x1, y1, x1, y2, color); } /////////////////////////////// void Draw_Vertical_Line(uint8_t size,uint16_t x1,uint16_t x2,uint16_t y1,uint16_t color) { Draw_Line(size, x1, y1, x2, y1, color); } /////////////////////////////// void Draw_Reactangle(uint8_t size,uint16_t left,uint16_t top,uint16_t right,uint16_t bottom,uint16_t color) { Draw_Horizont_Line(size, top, left, right, color); Draw_Horizont_Line(size, bottom, left, right, color); Draw_Vertical_Line(size, top, bottom, left, color); Draw_Vertical_Line(size, top, bottom, right, color); }


А для того чтобы нарисовать треугольник потребуется всего 3 линии)))
void Draw_Triangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint8_t size, uint16_t color) { Draw_Line(size, x1, y1, x2, y2, color); Draw_Line(size, x2, y2, x3, y3, color); Draw_Line(size, x3, y3, x1, y1, color); }


По алгоритму Брезенхема также можно построить окружность. На каждом шаге алгоритма, рассматриваются три точки и более подходящая находится путём сравнения расстояния от центра окружности до выбранной точки с радиусом окружности.
void Draw_Circle (uint8_t size,uint16_t x0,uint16_t y0,uint16_t radius,uint16_t color) { int x = 0; int y = radius; int delta = 2 - 2 * radius; int error = 0; while(y >= 0) { Draw_Point(size,x0 + x, y0 + y,color); Draw_Point(size,x0 + x, y0 - y,color); Draw_Point(size,x0 - x, y0 + y,color); Draw_Point(size,x0 - x, y0 - y,color); error = 2 * (delta + y) - 1; if(delta < 0 && error <= 0) { ++x; delta += 2 * x + 1; continue; } error = 2 * (delta - x) - 1; if(delta > 0 && error > 0) { --y; delta += 1 - 2 * y; continue; } ++x; delta += 2 * (x - y); --y; } }


Под конец написания статьи нашел функцию, которая рисует закрашенные и не закрашенные кружки, отвечает за это аргумент fill.
void Draw_Circle1(unsigned int x,unsigned int y,char radius,char fill, char size, unsigned int color) { int a_,b_,P; a_ = 0; b_ = radius; P = 1 - radius; while (a_ <= b_) { if(fill == 1) { Draw_Area(y-a_,x-b_,y+a_,x+b_,color); Draw_Area(y-b_,x-a_,y+b_,x+a_,color); } else { Draw_Point(size, a_+x, b_+y, color); Draw_Point(size, b_+x, a_+y, color); Draw_Point(size, x-a_, b_+y, color); Draw_Point(size, x-b_, a_+y, color); Draw_Point(size, b_+x, y-a_, color); Draw_Point(size, a_+x, y-b_, color); Draw_Point(size, x-a_, y-b_, color); Draw_Point(size, x-b_, y-a_, color); } if (P < 0) { P = (P + 3) + (2* a_); a_ ++; } else { P = (P + 5) + (2* (a_ - b_)); a_ ++; b_ --; } } } ////////////////////////////////////


Пожалуй это все, что хотелось рассказать про рисование геометрических фигур, а в мы узнаем как работает резистивный сенсорный экран.
Ссылка на код, написанный к статьям про SSD1289, .
Проект для Atmega16 в Atmel Studio 6.2 в архиве (cкачиваний: 167)

Алгоритм Брезенхема является одним из старейших алгоритмов в машинной графике. Казалось бы, как можно применить алгоритм построения растровых прямых при создании домашней паяльной печи? Оказывается, можно, причем с очень достойным результатом. Забегая вперед, скажу, что данный алгоритм очень хорошо скармливается маломощному 8-битному микроконтроллеру. Но обо всем по порядку.

Алгоритм Брезенхе́ма - это алгоритм, определяющий, какие точки двумерного растра нужно закрасить, чтобы получить близкое приближение прямой линии между двумя заданными точками. Суть алгоритма заключается в том, чтобы для каждого столбца X (см. рисунок) определить какая строка Y ближе всего к линии, и нарисовать точку.

Теперь посмотрим, как подобный алгоритм поможет нам при управлении ТЭНами в электропечи.

ТЭН питается от сетевого напряжения 220В/50Hz. Взглянем на график.


При подаче такого напряжения в чистом его виде на вход электронагревателя мы получим на выходе 100% мощность нагрева. Все просто.



Что будет если подать на вход ТЭНа только положительную полуволну сетевого напряжения? Правильно, мы получим 50% выходной мощности нагрева.



Если подать каждую третью полуволну, мы получим 33% мощности.

В качестве примера возьмем 10% градацию выходной мощности и временной отрезок в 100мс, что равносильно 10 полуволнам сетевого напряжения. Нарисуем сетку 10х10 и представим, что ось Y это ось значений выходной мощности. Проведем прямую от 0 до необходимого значения мощности.

Прослеживаете зависимость?
Увеличив временной отрезок до 1 сек, можно получить градацию выходной мощности в 1%. Получится сетка 100х100 со всеми вытекающими.

А теперь о приятном:
Алгоритм Брезенхема можно построить в цикле таким образом, чтобы на каждом шаге по оси X просто отслеживать значение ошибки, которое означает - вертикальное расстояние между текущим значением y и точным значением y для текущего x . Всякий раз, когда мы увеличиваем x , мы увеличиваем значение ошибки на величину наклона. Если ошибка превысила 0.5, линия стала ближе к следующему y , поэтому мы увеличиваем y на единицу (читай - пропускаем одну полуволну напряжения), одновременно уменьшая значение ошибки на 1.

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

Я намеренно не стал грузить вас формулами. Алгоритм элементарный, легко гуглится. Я лишь хочу показать его возможность применения в схемотехнике. Для управления нагрузкой будет использоваться типовая схема подключения симисторной оптопары MOC3063 с детектором нуля.

При таком подходе есть ряд преимуществ.

  • Минимальные помехи в сети из-за частых коммутаций большой нагрузки, включение/выключение будет происходить в моменты перехода напряжения через ноль.
  • Очень простой алгоритм - все вычисления сводятся к работе с целыми числами, что хорошо для микроконтроллера.
  • Нет необходимости городить детектор перехода напряжения через ноль (привет MOC3063). Даже если МК будет просто дергать ногой по таймеру, открывая оптопару, ошибка будет не критичной.

Продолжение следует.

Введение На производстве (в системах автоматизированного управления), в быту зачастую необходимо использовать регулировку мощности, подаваемую на нагрузку. Как правило, нагрузка работает от сети переменного тока. Поэтому задача несколько усложняется, в сравнении с регулировкой мощности нагрузки, работающей на постоянном напряжении. В случае работы нагрузки на постоянном напряжении применяют широтноимпульсную модуляцию (ШИМ), и изменяя скважность соответственно изменяется и мощность, подаваемая на нагрузку. Если использовать управление с помощью ШИМ для регулировки мощности в сети переменного тока, ключ через который регулируем сигнал (например, симистор) будет открываться и пропускать в нагрузку части синусоиды, имеющие разную мощность. Элементная база и сборка регулятора Рис.1. Электрическая принципиальная схема регулятора Для реализации данного проекта были использованы: Pinboard на AVR микроконтроллере ATmega16, симистор Philips BT138 12А, диодный мост DB105, оптосимистор MOC3022, оптопара PC817, сопротивления 220 Ом - 10 кОм, потенциометр 5 кОм. Подключение элементов показано на рис.1. Принцип работы устройства Данный регулятор предназначен для работы с активной нагрузкой, подключаемой к сети напряжением 220 В. Для определения начало каждой полуволны используется оптопара. Таким образом, на выходе детектора нуля получаем короткие положительные импульсы в момент, когда напряжение в сети проходит через 0. Сигнал с детектора нуля подключен на вход внешнего прерывания МК, чтобы определять начало новой полуволны и открывать симистор на необходимое время или на определенное количество полупериодов. Для отпирания симистора на его управляющий электрод подаётся напряжение через оптосимистор относительно условного катода. Фазовый метод При фазовом методе, изменяя значение задержки таймера посредством АЦП микроконтроллера (в нашем случае потенциометром), соответственно изменяем задержку открытия симистора после начала полуволны. Чем больше задержка, тем меньшая часть полуволны будет пропущена на нагрузку и соответственно получаем меньшую мощность, и наоборот. Зная частоту тактирования микроконтроллера, рассчитана задержка. При частоте сетевого напряжения 50 Гц время полупериода составит 0,01 секунды. То есть, если симистор открыт через 0,003 сек, будет пропущено приблизительно 2/3 полуволны, и мощность составит 70%. Если симистор будет открыт без задержки, то пропущена вся полуволна, и выходная мощность составит 100%. Была реализована программа с применением фазового метода управления нагрузкой. Программирование осуществлялось на языке С++ в среде CodeVisionAVR. Показания с осциллографа на нагрузке приведены на рисунке 2. Рис.2. Регулировка мощности фазовым методом Расчет задержки на открытие симистора Так как функция напряжения не линейная, то есть площадь под синусоидой при одном и том же интервале времени будет разной, соответственно и мощность будет разная. Поэтому задержка была рассчитана с учетом нелинейности напряжения. На рисунке 3 показана синусоида сети и интервалы задержки, рассчитанные в таблице 1. Показаны первые пять из ста (в процентах) значений задержки. Рис.3. Регулировка фазовым методом Таблица 1 Расчет задержек на открытие симистора Номер точки полуволны Время в микросекундах Синус точки 0 0 0 1 638 0,199 2 903 0,279 3 1108 0,341 4 1282 0,391 5 1436 0,435 Метод Брезенхема Существует также метод регулировки мощности, основанный на принципе подачи на нагрузку нескольких полупериодов сетевого напряжения с последующей паузой (Рис.4). Моменты коммутации симистора совпадают с моментами перехода сетевого напряжения через ноль, поэтому уровень радиопомех резко снижен. Применение микроконтроллера позволило использовать для равномерного распределения импульсов алгоритм Брезенхема. Однако наблюдается пониженная частота коммутации тока в нагрузке в сравнении с фазовым управлением. Предпочтителен для управления нагрузкой большой мощности (от 1 кВт). Была реализована программа, и также как и в фазовом методе по АЦП изменялось количество пропущенных полупериодов. Был выбран диапазон пропускание от каждой полуволны до пропускания одной полуволны к десяти. На рисунке 4 показаны изображения с осциллографа реализации регулятора методом Брезенхема. Рис.4. Регулировка мощности методом Брезенхема Заключение Регулятор универсален, что дает возможность применить его как в быту, так и в промышленности. Наличие микроконтроллерного управления позволяет быстро перенастроить систему, что обуславливает гибкость устройства. Два алгоритма управления позволят применять регулятор в широких диапазонах мощностей.

Алгоритм Брезенхема является одним из старейших алгоритмов в машинной графике. Казалось бы, как можно применить алгоритм построения растровых прямых при создании домашней паяльной печи? Оказывается можно, причем с очень достойным результатом. Забегая вперед, скажу, что данный алгоритм очень хорошо скармливается маломощному 8-битному микроконтроллеру. Но обо всем по порядку.

Алгоритм Брезенхема - это алгоритм, определяющий, какие точки двумерного растра нужно закрасить, чтобы получить близкое приближение прямой линии между двумя заданными точками. Суть алгоритма заключается в том, чтобы для каждого столбца X (см. рисунок) определить какая строка Y ближе всего к линии, и нарисовать точку.

Теперь посмотрим как подобный алгоритм поможет нам при управлении ТЭНами в электропечи.

ТЭН питается от сетевого напряжения 220В/50Hz. Взглянем на график.


При подаче такого напряжения в чистом его виде на вход электронагревателя мы получим на выходе 100% мощность нагрева. Все просто.




Что будет если подать на вход ТЭНа только положительную полуволну сетевого напряжения? Правильно, мы получим 50% выходной мощности нагрева.




Если подать каждую третью полуволну, мы получим 33% мощности.


В качестве примера возьмем 10% градацию выходной мощности и временной отрезок в 100мс, что равносильно 10 полуволнам сетевого напряжения. Нарисуем сетку 10х10 и представим, что ось Y это ось значений выходной мощности. Проведем прямую от 0 до необходимого значения мощности.

Прослеживаете зависимость?
Увеличив временной отрезок до 1 сек, можно получить градацию выходной мощности в 1%. Получится сетка 100х100 со всеми вытекающими.

А теперь о приятном:
Алгоритм Брезенхема можно построить в цикле таким образом, чтобы на каждом шаге по оси X просто отслеживать значение ошибки, которое означает - вертикальное расстояние между текущим значением y и точным значением y для текущего x . Всякий раз, когда мы увеличиваем x , мы увеличиваем значение ошибки на величину наклона. Если ошибка превысила 0.5, линия стала ближе к следующему y , поэтому мы увеличиваем y на единицу (читай - пропускаем одну полуволну напряжения), одновременно уменьшая значение ошибки на 1.

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

Я намеренно не стал грузить вас формулами. Алгоритм элементарный, легко гуглится. Я лишь хочу показать его возможность применения в схемотехнике. Для управления нагрузкой будет использоваться типовая схема подключения симисторной оптопары MOC3063 с детектором нуля.

При таком подходе есть ряд преимуществ.


  • Минимальные помехи в сети из-за частых коммутаций большой нагрузки, включение/выключение будет происходить в моменты перехода напряжения через ноль.

  • Очень простой алгоритм - все вычисления сводятся к работе с целыми числами, что хорошо для микроконтроллера.

  • Нет необходимости городить детектор перехода напряжения через ноль (привет MOC3063). Даже если МК будет просто дергать ногой по таймеру, открывая оптопару, ошибка будет не критичной.

Продолжение следует.

Поскольку экран растрового дисплея с электронно-лучевой трубкой (ЭЛТ) можно рассматривать как матрицу дискретных элементов (пикселов), каждый из которых может быть подсвечен, нельзя непосредственно провести отрезок из одной точки в другую. Процесс определения пикселов, наилучшим образом аппроксимирующих заданный отрезок, называется разложением в растр. В сочетании с процессом построчной визуализации изображения он известен как преобразование растровой развертки. Для горизонтальных, вертикальных и наклоненных под углом 45°. отрезков выбор растровых элементов очевиден. При любой другой ориентации выбрать нужные пикселы труднее, что показано на рис.1.

Рис.1.1. Разложение в растр отрезков прямых.

Общие требования к алгоритмам вычерчивания отрезков следующие: Отрезки должны выглядеть прямыми, начинаться и заканчиваться в заданных точках, яркость вдоль отрезка должна быть постоянной и не зависеть от длины и наклона, рисовать нужно быстро.

Постоянная вдоль всего отрезка яркость достигается лишь при проведении горизонтальных, вертикальных и наклоненных под углом 45° прямых. Для всех других ориентаций разложение в растр приведет к неравномерности яркости, как это показано на рис. 1.

В большинстве алгоритмов вычерчивания отрезков для упрощения вычислений используется пошаговый алгоритм. Приведем пример подобного алгоритма:

Простой пошаговый алгоритм

позиция = начало

шаг = приращение

1. if позиция - конец < точность then 4

if позици > конец then 2

if позиция < конец then 3

2. позиция = позиция - шаг

3. позиция = позиция + шаг

4. finish

Алгоритм Брезенхема.

Хотя алгоритм Брезенхема был первоначально разработан для цифровых графопостроителей, однако он в равной степени подходит для использования растровыми устройствами с ЭЛТ. Алгоритм выбирает оптимальные растровые координаты для представления отрезка. В процессе работы одна из координат - либо x, либо y (в зависиимости от углового коэффициента) - изменяется на единицу. Изменение другой координаты (на 0 или 1) зависит от расстояния между действительным положением отрезка и ближайшими координатами сетки. Такое расстояние мы назовем ошибкой.

Алгоритм построен так, что требуется проверить лишь знак этой ошибки. На рис.3.1 это иллюстрируется для отрезка в первом октанте, т.е. для отрезка с угловым коэффициентом, лежащим в диапазоне от 0 до 1. Из рисунка можно заметить, что если угловой коэффициент отрезка из точки (0,0) больше, чем 1/2, то пересечение с прямой x = 1 будет расположено ближе к прямой y = 1, чем к прямой y = 0. Следовательно, точка растра (1,1) лучше аппроксимирует ход отрезка, чем точка (1,0). Если угловой коэффициент меньше 1/2, то верно обратное. для углового кэффициента, равного 1/2, нет какого либо предпочтительного выбора. В данном случае алгоритм выбирает точку (1,1).

Рис.3.2. График ошибки в алгоритме Брезенхема.

Так как желательно проверять только знак ошибки, то она первоначально устанавливается равной -1/2. Таким образом, если угловой коэффициент отрезка больше или равен 1/2, то величина ошибки в следующей точке растра с координатами (1,0) может быть вычислена как

e = e + m

где m - угловой коэффициент. В нашем случае при начальном значении ошибки -1/2

e = 1/2 + 3/8 = -1/8

Так как е отрицательно, отрезок пройдет ниже середины пиксела. Следовательно, пиксел на том же самом горизонтальном уровне лучше аппроксимирует положение отрезка, поэтому у не увеличивается. Аналогично вычисляем ошибку

e = -1/8 + 3/8 = 1/4

в следующей точке растра (2,0). Теперь е положительно, значит отрезок пройдет выше средней точки. Растровый элемент (2,1) со следующей по величине координатой у лучше аппроксимирует положение отрезка. Следовательно у увеличивается на 1. Прежде чем рассматривать следующий пиксел, необходимо откорректировать ошибку вычитанием из нее 1. Имеем

e = 1/4 - 1 = -3/4

Заметим, что пересечение вертикальной прямой x = 2 с заданным отрезком лежит на 1/4 ниже прямой у = 1. Еслиже перенести отрезок 1/2 вниз, мы получим как раз величину -3/4. Продолжение вычислений для следующего пиксела дает

e = -3/4 + 3/8 = -3/8

Так как е отрицательно, то у не увеличивается. Из всего сказанного следует, что ошибка - это интервал, отсекаемый по оси у рассматриваемым отрезком в каждом растровом элементе (относительно -1/2).

Приведем алгоритм Брезенхема для первого октанта, т.е. для случая 0 =< y =< x.

Алгоритм Брезенхема разложения в растр отрезка для первого октанта

предполагается, что концы отрезка (x1,y1) и (x2,y2) не совпадают

Integer - функция преобразования в целое

x, y, x, y - целые

е - вещественное

инициализация переменных

Инициализация с поправкой на половину пиксела

е = y/x - 1/2

начало основного цикла

for i = 1 to x

while (e => 0)

e = e + y/x

Блок-схема алгоритма приводится на рис.3.3. Пример приведен ниже.

Рис. 3.3. Блок-схема алгоритма Брезенхема.

Пример 3.1. Алгоритм Брезенхема.

Рассмотрим отрезок проведенный из точки (0,0) в точку (5,5). Разложение отрезка в растр по алгоритму Брезенхема приводит к такому результату:

начальные установки

е = 1 - 1/2 = 1/2

результаты работы пошагового цикла

Результат показан на рис.3.4 и совпадает с ожидаемым. Заметим, что точка растра с координатами (5,5) не активирована. Эту точку можно активировать путем изменения цикла for-next на 0 to x. Активацию точки (0,0) можно устранить, если поставить оператор Plot непосредственно перед строкой next i.

Рис. 3.4. Результат работы алгоритма Брезенхема в первом октанте.

В следующем разделе описан общий алгоритм Брезенхема.