Одноплатник на 155-й серии — ЧПУ

от автора

А задумывались ли Вы, что можно сделать на ЭВМ «Берёста-4» с объемом памяти ПЗУ 256 байт? Многие из Вас скажут, что можно всего лишь поморгать светодиодом, а я скажу Вам, что можно сделать ЧПУ станок. Лично я давно хотел себе ЧПУ «рисовалку» вот её и задумал сделать.

Начнем с самого простого: подергаем шаговым двигателем. В роли шагового двигателя — шаговый двигатель 17HS08-1004S. Управление «шаговиком» доверим драйверу на микросхеме tb6600. Здесь все просто: выбрал направление кручения и подал импульс. Шаговый двигатель сделал один микрошаг. Главное — настроить драйвер по току да по количеству микрошагов в одном шаге. Настраивается это все выключателями на самом драйвере.

Ну, с этой задачей «одноплатник» справился. В принципе, я и не сомневался, потому что задача из разряда легких. А вот дальше начинается ужас- он же кошмар.) Так как памяти у «Берёсты» очень мало (256 байт), а поставленная задача не из разряда «Hello world», то нужно как-то выкручиваться. На «Берёсте» нет аппаратного UART. При реализации программного, возможно, не хватит памяти. Что же делать?

диаграмма UART

диаграмма UART

Открываю перед собой диаграмму UART и, гипнотизируя её взглядом, начинаю сворачивать извилину мозга в ленту Мебиуса: «Плюс. Все время плюс. Началась передача — это минус. Дальше пошли данные, бит четности и все сначала.  Данные… Вся сложность в данных. А если я их упрощу? Но как? Компьютер выдает стандарт, и станок должен этот стандарт принять. Ставить посредника в виде микроконтроллера не хочу и не буду. В комплексе с этой проблемой идет еще одна. «Берёсте» не хватит памяти, чтобы отработать G-код. Значит, нужно придумать упрощённый стандарт: команда выбора направления вперед/назад, движение по Х шаг (согласно выбранного направления). По Y — то же самое. По Z — поднять/опустить перо.  Итого 6 команд. Если первая команда будет число 1 переданный по UART, вторая команда будет число 3, третья — 7, четвертая — 15, пятая – 31. А так как это все в двоичной системе, то данные будут всегда плюс, который зажат с двух сторон минусами, а различаться будут только длительностью этого плюса. Такой своеобразный «UARTовый ШИМ». А это может сработать!»

 И тут из телевизора раздается голос Пина (Смешарики): «Компрессия!!!»

Я мысленно согласился и начал писать код «ШИМового UARTA».

Диаграмма «ШИМового UARTа»)

Диаграмма «ШИМового UARTа»)

И так, у меня есть программа, которая передает команды. Станок их принимает и даже распознает. Шаговые двигатели в движении. Вроде бы все хорошо, но ставить шаговый двигатель с громоздким драйвером на ось Z это как стрелять из пушки по воробью. От оси Z требуется поднять перо или опустить. Более рационально установить на эту ось сервопривод.

В ходе непринуждённого диалога сервопривод сообщил мне, что он любит ШИМ, но у меня нет аппаратного ШИМ. Придется писать программный. Но у меня нет таймера и прерываний. Можно, конечно, и без этой роскоши (таймер/прерывание), но возникает другая проблема: большую часть времени «Берёста» занята обработкой UART. От того очень сложно рассчитать интервалы. Как же мне быть? Решение возникло, само собой. Оказывается, моему сервоприводу SG90-9G (за другие модели ничего не скажу) не обязательно в определенное время выдавать сигнал угла поворота. То есть достаточно выдать импульс продолжительностью 500-24000 мкс. , что соответствует углу поворота 0-180 градусов, а потом безгранично долго можно ничего не подавать, до возникновения необходимости в этом.

код для «Берёсты»:
//начинаем опрашивать UART start: mov buf,0 mov r2,buf mov buf,8 mov adr,buf mov buf,1 mov ram,buf  mov buf,10 mov r1,buf  mov a,p2 mov roma1, rx mov roma2, rx jbz0//если - то переход на прием // если + mov roma1, start mov roma2, start jmp  rx: mov a,p2 mov roma1, rx mov roma2, rx jbz0//если - то ждем // если +  start2: mov a,p2 mov roma1, rx2 mov roma2, rx2 jbz0//если - то переход на закончить // если +  mov a,r1 add r1 mov roma1, up mov roma2, up jc//если переполнение  mov roma1, start2 mov roma2, start2 jmp  up: mov a,r2 add r2 mov r1,buf mov roma1, start2 mov roma2, start2 jmp  rx2: mov a,p2 mov roma1, rx2 mov roma2, rx2 jbz0//если - то ждем  // если + mov buf,1 mov ram,buf mov a,r2  mov ROMa1, Xclk mov ROMa2, Xclk je//если равны то переход  mov buf,3 mov ram,buf mov a,r2  mov ROMa1, Yclk mov ROMa2, Yclk je//если равны то переход  mov buf,2 mov ram,buf mov a,r2  mov ROMa1, DIRup mov ROMa2, DIRup je//если равны то переход  mov buf,4 mov ram,buf mov a,r2  mov ROMa1, DIRdown mov ROMa2, DIRdown je//если равны то переход  mov buf,5 mov ram,buf mov a,r2  mov ROMa1, Zdown mov ROMa2, Zdown je//если равны то переход  mov buf,6 mov ram,buf mov a,r2  mov ROMa1, Zup mov ROMa2, Zup je//если равны то переход  //иначе ждем следующий байт mov roma1, loop//указываем координаты для переходов на метку start mov roma2, loop//указываем координаты для переходов на метку start jmp//повторим  Zup: mov buf,8//1 в буфер mov p1, buf//отправить в порт 1  mov buf,0 mov r1,buf  delay270:  mov buf,8 mov adr,buf mov buf,1 mov ram,buf mov buf,1  mov a,buf  delay27: add a mov roma1, vse27 mov roma2, vse27 jc//если регистр а переполнился то закончить задержку mov roma1, delay27 mov roma2, delay27 jmp   vse27: mov a,r1 add r1 mov roma1, vse270 mov roma2, vse270 jc//если регистр а переполнился то закончить задержку mov roma1, delay270 mov roma2, delay270 jmp   vse270: mov buf,0//0 в буфер mov p1, buf//отправить в порт 0  mov roma1, loop//указываем координаты для переходов на метку start mov roma2, loop//указываем координаты для переходов на метку start jmp//повторим  Zdown: mov buf,8//1 в буфер mov p1, buf//отправить в порт 1  mov buf,1 mov r1,buf  delay2701: mov buf,8 mov adr,buf mov buf,1 mov ram,buf mov buf,1  mov a,buf  delay271: add a mov roma1, vse271 mov roma2, vse271 jc//если регистр а переполнился то закончить задержку mov roma1, delay271 mov roma2, delay271 jmp   vse271: mov a,r1 add r1 mov roma1, vse2701 mov roma2, vse2701 jc//если регистр а переполнился то закончить задержку mov roma1, delay2701 mov roma2, delay2701 jmp   vse2701: mov buf,0//0 в буфер mov p1, buf//отправить в порт 0 mov roma1, loop//указываем координаты для переходов на метку start mov roma2, loop//указываем координаты для переходов на метку start jmp//повторим  DIRup: mov buf,2 //выбор направления вращения DIR mov p1,buf mov buf,10 //сохраняем выбор направления вращения DIR mov adr,buf mov buf,1 mov ram,buf  mov roma1, loop//указываем координаты для переходов на метку start mov roma2, loop//указываем координаты для переходов на метку start jmp//повторим  DIRdown: mov buf,10 //сохраняем выбор направления вращения DIR mov adr,buf mov buf,0 //выбор направления вращения DIR mov p1,buf mov ram,buf  mov roma1, loop//указываем координаты для переходов на метку start mov roma2, loop//указываем координаты для переходов на метку start jmp//повторим  Yclk: mov buf,10 mov adr,buf mov a,ram mov roma1, lab2 mov roma2, lab2 jbz0//если 0 то переход  mov buf,6 mov p1,buf  mov roma1, delay mov roma2, delay jmp  lab2: mov a,p2 mov roma1, stepY//указываем координаты для переходов на метку  mov roma2, stepY//указываем координаты для переходов на метку  jbz2//если - то переход (концевик не сработал, Y>0) // теперь Y=0 mov roma1, delay mov roma2, delay jmp stepY:  mov buf,4 mov p1,buf mov roma1, delay mov roma2, delay jmp   Xclk: mov buf,10 mov adr,buf mov a,ram mov roma1, lab1 mov roma2, lab1 jbz0//если 0 то переход mov buf,3 mov p1,buf  mov roma1, delay mov roma2, delay jmp  lab1: mov a,p2 mov roma1, stepx//указываем координаты для переходов на метку  mov roma2, stepx//указываем координаты для переходов на метку  jbz1//если - то переход (концевик не сработал, Х>0) // если X=0 mov roma1, delay mov roma2, delay jmp stepx: mov buf,1 mov p1,buf  //вместо delay------------ delay: //- mov buf,8//- mov adr,buf//- mov buf,0//- mov r1,buf//- delay_lab: //- mov a,r1//- add r1//- //- mov roma1, delay_out//- mov roma2, delay_out//- jc//если переполнение mov roma1, delay_lab//- mov roma2, delay_lab//- jmp//- //------------------------ delay_out: mov buf,10 mov adr,buf mov a,ram mov roma1, label3 mov roma2, label3 jbz0//если 0 то переход  mov buf,2 mov p1,buf mov ROMa1, loop mov ROMa2, loop jmp  label3: mov buf,0 mov p1,buf  loop: mov roma1, start//указываем координаты для переходов на метку start mov roma2, start//указываем координаты для переходов на метку start jmp//повторим

Готовые решения управления «рисовалкой» мне не подойдут, так как они отправляют G-коды. Значит, надо писать свою.  Она должна уметь: устанавливать соединение с ЧПУ и преобразовывать G-код в нужную мне систему команд. Если с установкой соединения все просто, то с преобразованием G-кода чуть-чуть интересней.

Описание команд ЧПУ «рисовалки»:

мнемоника

описание команды

U

Выбор направления вперед для осей Х, Y

D

Выбор направления назад для осей Х, Y

X

Двинуть Х на 1 микрошаг

Y

Двинуть Y на 1 микрошаг

+

поднять перо

опустить перо

Пример G-кода (предположим, что исполнительное устройство находится в координатах X=0, Y=0):

G0 X2 Y2

В данном примере ЧПУ-станок построит вектор с координатами (X=0, Y=0)-(X=2, Y=2) и начнет двигать исполнительное устройство по этому вектору до координаты X=2, Y=2

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

Мой код (предположим, что исполнительное устройство находится в координатах X=0, Y=0):

UXYXY

После выполнения этой последовательности исполнительное устройство будет находиться по координатам X=2, Y=2

Ну и пару слов о компьютерной программе. Подключаемся к ЧПУ на скорости 9600 бод. Открываем файл G-кода. Программа преобразует его в упрощённый формат, используя алгоритм Брезенхема. Жмем «отправить» и ЧПУ начинает рисовать. Также есть возможность управлять ЧПУ по всем осям в ручную.

программа управления ЧПУ

программа управления ЧПУ

Максимальный размер рисуемой картинки — 75х85 мм. В роли пера используется карандаш с диаметром графита 0.7 мм. В роли посредника между компьютером и ЧПУ выступает любой преобразователь USB-UART. Я пробовал как основанный на микросхеме CP2102, так и на микросхеме CH240.

художник собственной персоной)

художник собственной персоной)
/это шедевр!)

/это шедевр!)

Подведем итог: программа драйвера для «Берёсты» получилась объёмом 228 байт, что прекрасно вписывается в максимальный объем 256 байт. На её плечи ложится контроль двух концевых выключателей по оси Х и Y, два шаговых двигателя, один сервопривод, прием и обработку 6 команд по UART. И остается еще аж целых 28 байт на развитие.)

Вот такая «Берёста» у нас получилась: поморгает, порисует, а может и еще какие скрытые таланты имеет. Ну, это уже совсем другая история. 😉

исходные файлы проекта: Яндекс Диск

немного видео:


ссылка на оригинал статьи https://habr.com/ru/articles/866556/