Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

niedziela, 20 marca 2011

DIY: Wykresiarka - tani ploter A4


Autor: mopsiok
Redakcja: Dondu

Cześć!W niniejszym artykule przedstawię Wykresiarkę - prosty ploter powstały z zalegających w domu "śmieci".

Jak wskazuje nazwa, przeznaczony jest głównie do rysowania wykresów do szkoły, ale jego możliwości są o wiele większe - od rysowania prostych rysunków, przez tekst, a kończąc na mozaikach PCB.

Na wstępie zaznaczę, że maszynka cechuje się dość nietypowymi rozwiązaniami: zrezygnowałem z używania "oklepanego" oprogramowania sterującego i dedykowanych sterowników dla silników krokowych. Zamiast tego wykorzystuję własną aplikację okienkową (głównie ze względu na projektowanie wykresów), a całe sterowanie mechaniką zostawiłem dla ATmegi16 i najprostszych końcówek mocy (ULN2803 i L293D).

Zaintrygowany/a?
Zapraszam do obejrzenia filmu i lektury. :-)




Część mechaniczna

Zaczęło się od kolekcjonowania różnych przydasiów - wymontowanych ze starych drukarek i skanerów. Trochę to trwało, ale ostatecznie zebrałem wszystko czego mi trzeba:
  • silnik bipolarny (M42SP-7) dla osi X
  • silnik unipolarny (M35SP-7T) dla osi Y
  • elektromagnes z dźwignią (TDS-F06A-03) dla osi Z
  • zasilacz dwunapięciowy (HP 0957-2094) +16V / +32V

W przypadku silników konieczne było odszukanie takich ze zintegrowaną przekładnią i paskiem napędowym. Musiały też mieć taką samą rozdzielczość - w przypadku starych skanerów jest to zazwyczaj 300dpi.

Mając to wszystko, zaprojektowałem model Wykresiarki (w załączniku, plik Blendera .blend). Na jego podstawie wykonałem rysunek elementów montażowych, który zleciłem do wycięcia laserem w pleksi 3mm (w załączniku, plik .pdf).

Wykorzystałem prowadnice do szuflad (kulkowe) o długości około 310 mm - chodziły dość ciężko, więc usunąłem stary smar i zastąpiłem go teflonowym. Po takim zabiegu trochę się zacinały, ale potraktowałem je odrobiną oleju wazelinowego i od tej pory śmigają jak marzenie.

W roli krańcówek osi X i Y użyłem zwykłych przycisków od myszek komputerowych. Na zdjęciu widać także laminat bez miedzi użyty do przyciskania krańcówek. Przydał się również przy kilku innych rzeczach, takich jak unieruchomienie elektromagnesu czy wyrównywanie kartki. Całość została zamontowana na płycie meblowej, a do przytrzymywania kartki użyłem folii magnetycznej i małych magnesów neodymowych.


Elementy do budowy
Elementy do budowy

Ploter - Oś Y i koszyk na elektromagnes
Oś Y i koszyk na elektromagnes


Ploter - Montaż osi X, silnika i przewodów
Montaż osi X, silnika i przewodów


Ploter - Zbliżenie na koszyk osi Z
Zbliżenie na koszyk osi Z


Nie chcę zanudzać szczegółową relacją z budowy. Wspomnę tylko, że nie potrzebowałem do tego celu specjalnych narzędzi. Prowadnice są przyklejone do podstawy przy pomocy kleju polimerowego, folia magnetyczna na wikolu, a pozostałe elementy - na super glue ;).





Sterownik

Pora przejść do ciekawszych rzeczy. Tak jak wspominałem, wszystkim steruje ATmega16. Zajmuje się ona sterowaniem silnikami krokowymi (poprzez scalaki ULN2803 i L293D) i elektromagnesem osi Z (przez MOSFET z logiką 5V, dokładniej IRLML2502). Poza tym musi też obsługiwać krańcówki, LCD czy komunikację z komputerem.

Ta ostatnia jest o tyle ciekawa, że wykorzystuje mój autorski protokół komunikacyjny (mopsioCODE ;)), pozwalający na zarówno na pracę w trybie komend, jak i przesyłanie "surowych" danych. Komunikacja odbywa się przez UART (FT232RL). Postanowiłem użyć separacji galwanicznej od komputera przez 2 transoptory TCMT1109 dla lepszego bezpieczeństwa układu (raz w trakcie testów spaliłem ATmegę).

Dobrze, koniec gadania - oto schemat i gotowa płytka. W załączniku są pliki Eagle'a dla całego sterownika (razem z kodem źródłowym w C).


Schemat sterownika Wykresiarki (plotera).
Schemat sterownika Wykresiarki


Ploter - Gotowa i zamontowana płytka sterująca.
Gotowa i zamontowana płytka sterująca





Aplikacja na PC

Ten program to mój debiut w Visual C# Express 2010, więc na pewno nie jest idealny. Jego głównym przeznaczeniem jest projektowanie wykresów i wysyłanie odpowiednich komend do maszyny, choć oczywiście ma możliwość sterowania ręcznego (przez pojedyncze komendy). Zajmuje się też parsowaniem plików HPGL, więc można coś narysować w Inkscapie, a potem wysłać to do plotera.

W załączniku znajduje się cały projekt z Visual C#, a także gotowy do odpalenia plik .exe (wymagane .NET Framework 4.0).


Aplikacja na PC wyświetlająca zaprojektowane wykresy.
Aplikacja na PC wyświetlająca zaprojektowane
wykresy.






Ukończony ploter


Ukończony ploter
Ukończony ploter

Narysowany wykres
Narysowany wykres


Test rysowania PCB
Test rysowania PCB


Rysowanie grafiki z tekstem
Rysowanie grafiki z tekstem





Do pobrania

Kompletne materiały obejmujące:
  • kody źródłowe sterownika,
  • kody źródłowe PC,
  • schemat i PCB w Eagle,
  • inne niezbędne do wykonania plotera.

Pobierz: Wykresiarka_pliki.rar (kopia)




Podsumowanie

Na zdjęciach powyżej widać małą aluminiową "skrzyneczkę". Są to 2 rezystory 33Ω/5W (po jednym na każdą cewkę silnika osi X) otoczone radiatorami z profili aluminiowych. Sposób sterowania powoduje, że bez rezystorów przez silnik płynie spory prąd i mocno się grzeje.

Należy również mieć na uwadze, że projekt był dostosowany do posiadanych przeze mnie części i elementów. Może istnieć konieczność niewielkiej zmiany sterownika i/lub części do wycięcia laserowego.
Niemniej jednak ploter sprawuje się całkiem dobrze: spełnia swoje zadanie i jest tani. A jak tani, to już zależy od posiadanych w domu części. Osobiście zmieściłem się w 60 złotych.

Jeśli zainteresował Cię ten projekt, zapraszam na mój kanał YouTube, gdzie można obejrzeć inne moje konstrukcje. Szczegóły dotyczące działania Wykresiarki można znaleźć w temacie na Elektrodzie.

Pozdrawiam i zachęcam do budowy! :-)
mopsiok


main.c
/*
 * Sterownik Wykresiarki
 * autor: mopsiok
 * wersja: 1.0
 * data: 12.2013
 *
 * Program zajmuje się komunikacja z komputerem i sterowaniem czescia mechaniczna Wykresiarki.
 */

#include <avr/io.h>
#include <util/delay.h>
#include "mopsioCODE_2_0/mopsiocode_2_0.h"
#include "LCD_HD44780/lcd.h"
#include "common.h"
#include "mopsiocode_config.h"

//deklaracje funkcji pomocniczych
void config(void);
void timer(void);

//================================================================================================
//          FUNKCJA GLOWNA PROGRAMU
int main(void) {
 config(); //konfiguracja portow IO i timera 100Hz
 mopsiocode_config(); //konfiguracja mopsioCODE
 lcd_init(); //konfiguracja LCD


 lcd_putstr("Wykresiarka v1.0");
 lcd_goto(6,1);
 lcd_putstr("by mopsiok");
 _delay_ms(3000);
 LCD_erase();
 lcd_goto(0,0);
 lcd_putstr("Oczekiwanie...");

 while(1) {
  mopsiocode_event(); //obsluga komunikacji USART
  mopsiocode_rawdata_event(); //obsluga trybu RawData
  timer(); //timer sprzetowy 100Hz
 }
 return 0;
}


//================================================================================================
//          FUNKCJE POMOCNICZE
void config(void) { //konfiguracja portow IO i timera 100Hz
 STEP_DDR = 255; //silniki - wyjscia
 RELAY_DDR |= 1<<RELAY; //elektromagnes - wyjscie
 LIMIT_DDR &= ~(1<<LX_MIN) & ~(1<<LX_MAX) & ~(1<<LY_MIN) & ~(1<<LY_MAX); //krancowki - wejscia
 MENU_DDR &= ~(1<<MENU_DOWN) & ~(1<<MENU_OK) & ~(1<<MENU_UP); //przyciski menu - wejscia

 //ustawianie stanow domyslnych
 STEP_PORT = 0; //silniki
 RELAY_PORT &= ~(1<<RELAY); //elektromagnes
 LIMIT_PORT |= (1<<LX_MIN) | (1<<LX_MAX) | (1<<LY_MIN) | (1<<LY_MAX); //krancowki - pullup
 MENU_PORT |= (1<<MENU_DOWN) | (1<<MENU_OK) | (1<<MENU_UP); //przyciski menu - pullup

 //konfiguracja sprzetowego timera 100Hz
 TCCR0 |= (1<<WGM01)|(1<<CS02)|(1<<CS00); //tryb CTC, preskaler 1024
 OCR0 = 155;
}


void timer(void) { //obsluga timera 100Hz
 if (TIFR & (1<<OCF0)) {
  if (LCD_status_flag == 1) { //obsluga statusow na LCD - tylko po wyraznym uruchomieniu
   if (timerLCD) timerLCD--;
   else { //wyznaczony czas minal - wygas status i wyjdz
    lcd_goto(14,1);
    lcd_putstr("  ");
    LCD_status_flag = 0; }
  }
  TIFR |= (1<<OCF0); //kasowanie flagi
 }
}



common.h
/*
 * common.h
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Zmienne globalne i funkcje pomocnicze
 */

#ifndef COMMON_H_
#define COMMON_H_

//================================================================================================
//         KONFIGURACJA PROGRAMU
//silniki osi X i Y
#define STEP_PORT PORTC
#define STEP_DDR DDRC
#define STEP_XMASK 0b11110000 //wyprowadzenia silnika X
#define STEP_YMASK 0b00001111 //wyprowadzenia silnika Y
#define MAX_FREQ 400 //maksymalna czestotliwosc silnikow [Hz]
#define MAX(a,b) ((a)>(b)?(a):(b)) //zwraca wieksza z 2 wartosci

//elektromagnes
#define RELAY_PORT PORTD
#define RELAY_DDR DDRD
#define RELAY 7
#define RELAY_DELAY 50 //opoznienie po opuszczeniu pisaka [ms]

//krancowki osi X i Y
#define LIMIT_PORT PORTD
#define LIMIT_DDR DDRD
#define LIMIT_PIN PIND
#define LX_MIN 6
#define LX_MAX 5
#define LY_MIN 4
#define LY_MAX 3
#define XMIN_FLAG (!(LIMIT_PIN & (1<<LX_MIN)))
#define XMAX_FLAG (!(LIMIT_PIN & (1<<LX_MAX)))
#define YMIN_FLAG (!(LIMIT_PIN & (1<<LY_MIN)))
#define YMAX_FLAG (!(LIMIT_PIN & (1<<LY_MAX)))
#define FLAGS ((8*XMIN_FLAG) | (16*XMAX_FLAG) | (32*YMIN_FLAG) | (64*YMAX_FLAG))

//przyciski menu
#define MENU_PORT PORTB
#define MENU_DDR DDRB
#define MENU_PIN PINB
#define MENU_DOWN 2
#define MENU_OK 1
#define MENU_UP 0

//konfiguracja LCD znajduje sie w pliku lcd.h (PA2..PA7)
//================================================================================================

//makra pomocnicze
#define SAVEPOWERFLAG power_flag_last = power_flag, power_flag = 1 //zapisywanie poprzedniego stanu i ustawianie trybu dokladnego
#define LOADPOWERFLAG _setPowerMode(power_flag_last) //przywracanie poprzedniego stanu

//deklaracje funkcji - opis w common.c
uint16_t gcd(uint16_t, uint16_t);
uint32_t lcm(uint16_t, uint16_t);
int16_t _sin(uint16_t);
int16_t _cos(uint16_t);
void LCD_erase(void);
void LCD_cmd(char* str);

//deklaracje zmiennych globalnych - opis w common.c
extern const uint8_t stepX[4];
extern const uint8_t stepY[4];
extern uint16_t posX, posY;
extern uint8_t posZ;
extern uint8_t xi, yi;
extern int8_t xdir_last, ydir_last;
extern uint8_t power_flag, power_flag_last;

extern uint16_t LCD_count;
extern uint8_t timerLCD;
extern uint8_t LCD_status_flag;

extern uint16_t rawdata_count, rawdata_counter;
extern volatile uint8_t rawdata_byte, rawdata_flag, rawdata_xy_flag, rawdata_text_flag;
extern uint8_t rawdata_xy[4];
extern uint16_t rawdata_text_x, rawdata_text_y;
extern uint8_t rawdata_text_size, rawdata_text_spacing;


#endif /* COMMON_H_ */



common.c
/*
 * common.c
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Zmienne globalne i funkcje pomocnicze
 */

#include <avr/pgmspace.h>
#include "LCD_HD44780/lcd.h"

//wartosci funkcji 32767*sin(x) dla x od 0 do 359 - do rysowania lukow
int16_t sine[360] PROGMEM = {0,571,1143,1714,2285,2855,3425,3993,4560,5125,5689,6252,6812,7370,7927,8480,9031,9580,10125,10667,11206,11742,12274,12803,13327,13847,14364,14875,15383,15885,16383,16876,17363,17846,18323,18794,19259,19719,20173,20620,21062,21497,21925,22347,22761,23169,23570,23964,24350,24729,25100,25464,25820,26168,26509,26841,27165,27480,27787,28086,28377,28658,28931,29195,29450,29696,29934,30162,30381,30590,30790,30981,31163,31335,31497,31650,31793,31927,32050,32164,32269,32363,32448,32522,32587,32642,32687,32722,32747,32762,32767,32762,32747,32722,32687,32642,32587,32522,32448,32363,32269,32164,32050,31927,31793,31650,31497,31335,31163,30981,30790,30590,30381,30162,29934,29696,29450,29195,28931,28658,28377,28086,27787,27480,27165,26841,26509,26168,25820,25464,25100,24729,24350,23964,23570,23169,22761,22347,21925,21497,21062,20620,20173,19719,19259,18794,18323,17846,17363,16876,16383,15885,15383,14875,14364,13847,13327,12803,12274,11742,11206,10667,10125,9580,9031,8480,7927,7370,6812,6252,5689,5125,4560,3993,3425,2855,2285,1714,1143,571,0,-571,-1143,-1714,-2285,-2855,-3425,-3993,-4560,-5125,-5689,-6252,-6812,-7370,-7927,-8480,-9031,-9580,-10125,-10667,-11206,-11742,-12274,-12803,-13327,-13847,-14364,-14875,-15383,-15885,-16383,-16876,-17363,-17846,-18323,-18794,-19259,-19719,-20173,-20620,-21062,-21497,-21925,-22347,-22761,-23169,-23570,-23964,-24350,-24729,-25100,-25464,-25820,-26168,-26509,-26841,-27165,-27480,-27787,-28086,-28377,-28658,-28931,-29195,-29450,-29696,-29934,-30162,-30381,-30590,-30790,-30981,-31163,-31335,-31497,-31650,-31793,-31927,-32050,-32164,-32269,-32363,-32448,-32522,-32587,-32642,-32687,-32722,-32747,-32762,-32767,-32762,-32747,-32722,-32687,-32642,-32587,-32522,-32448,-32363,-32269,-32164,-32050,-31927,-31793,-31650,-31497,-31335,-31163,-30981,-30790,-30590,-30381,-30162,-29934,-29696,-29450,-29195,-28931,-28658,-28377,-28086,-27787,-27480,-27165,-26841,-26509,-26168,-25820,-25464,-25100,-24729,-24350,-23964,-23570,-23169,-22761,-22347,-21925,-21497,-21062,-20620,-20173,-19719,-19259,-18794,-18323,-17846,-17363,-16876,-16383,-15885,-15383,-14875,-14364,-13847,-13327,-12803,-12274,-11742,-11206,-10667,-10125,-9580,-9031,-8480,-7927,-7370,-6812,-6252,-5689,-5125,-4560,-3993,-3425,-2855,-2285,-1714,-1143,-571};


//================================================================================================
//          ZMIENNE I STALE
//kolejne stany portu dla pelnego cyklu silnika
const uint8_t stepX[4] = {80,144,160,96}; //os X, silnik bipolarny + L293DD
const uint8_t stepY[4] = {8,4,2,1}; //os Y, silnik unipolarny + ULN2803

//informacje o pisaku
uint16_t posX, posY; //pozycja pisaka - ilosc krokow od poczatku ukladu wspolrzednych
uint8_t posZ; //stan pisaka: 0 - podniesiony; 1 - opuszczony
uint8_t xi, yi; //indeksy dla stepX i stepY
int8_t xdir_last=1, ydir_last=1; //kierunek poprzedniego przesuwania [-1/1]
uint8_t power_flag; //jesli 1, to po przesunieciu pisaka silniki dalej beda zasilane (troche sie beda grzac), ale za to znacznie zmniejszy sie blad pozycjonowania
uint8_t power_flag_last; //poprzedni stan power_flag (do funkcji prostokata, luku i tekstu)

//obsluga LCD
uint16_t LCD_count; //ilosc odebranych rozkazow
uint8_t timerLCD; //timer programowy do wygasania informacji o statusach
uint8_t LCD_status_flag; //flaga do wygasania informacji o statusach

//obsluga surowych danych (poza mopsioCODE)
uint16_t rawdata_count; //ilosc bajtow do przeczytania w trybie surowym (mopsioCODE wylaczony)
uint16_t rawdata_counter; //licznik - ile bajtow juz odczytano
volatile uint8_t rawdata_byte; //odebrany bajt
volatile uint8_t rawdata_flag; //flaga ustawiana przy odbiorze bajtu
volatile uint8_t rawdata_xy_flag; //ustawiane na czas wykonywania komendy setRawXY
volatile uint8_t rawdata_text_flag; //ustawiane na czas wykonywania komendy text

uint8_t rawdata_xy[4]; //dane do przesuwania pisaka
uint16_t rawdata_text_x, rawdata_text_y; //pozycja aktualnie rysowanego znaku
uint8_t rawdata_text_size, rawdata_text_spacing; //rozmiar i odstepy miedzy znakami



//================================================================================================
//          FUNKCJE POMOCNICZE

uint16_t gcd(uint16_t a, uint16_t b) { //najwiekszy wspolny dzielnik 2 liczb
 if (b==0)
  return a;
 gcd(b, a%b);
 return 1; //zeby nie wywalalo warninga
}

uint32_t lcm(uint16_t a, uint16_t b) { //najmniejsza wspolna wielokrotnosc 2 liczb
 return (uint32_t)a*b/gcd(a,b);
}

int16_t _sin(uint16_t x) { //zwraca 16-bitowy sinus kata (w stopniach) [-32767 .. 32767]
 return pgm_read_word(&(sine[x % 360]));
}

int16_t _cos(uint16_t x) { //zwraca 16-bitowy cosinus kata (w stopniach) [-32767 .. 32767]
 return pgm_read_word(&(sine[(x+90) % 360])); //przesuniecie o -90 stopni
}

void LCD_erase(void) { //"odswieza" pamiec LCD w celu unikniecia efektu duszkow
 lcd_clear();
 lcd_goto(0,1);
 lcd_putstr("R:");
 lcd_goto(11,1);
 lcd_putstr("S:");
}

void LCD_cmd(char* str) { //wysyla do LCD opis rozkazu
 lcd_goto(0,0);
 lcd_putstr(str);
}



control.h
/*
 * control.h
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Funkcje sterujace
 */

#ifndef CONTROL_H_
#define CONTROL_H_

void _returnXY(void);
void _setZ(uint8_t);
uint8_t _setXY(uint16_t, uint16_t);
void _getZ(void);
void _getXY(void);
void _setPowerMode(uint8_t);
void _setRawXY(uint16_t);
void _selectPen(void);
void _rect(uint16_t, uint16_t, uint16_t, uint16_t);
void _arc(uint16_t, uint16_t, uint16_t, uint16_t, uint16_t, uint8_t);
void _text(uint16_t, uint16_t, uint8_t, uint8_t, uint8_t);
void __drawChar(uint8_t);

#endif /* CONTROL_H_ */



control.c
/*
 * control.c
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Funkcje sterujace
 *
 *  ====================================
 *  WARNING!
 *  This part of software is weird :D.
 *  ====================================
 */

#include <avr/pgmspace.h>
#include <util/delay.h>
#include "mopsioCODE_2_0/mopsiocode_2_0.h"
#include "common.h"
#include "font.h"

void _returnXY(void) { //ustawia pisak w pozycji poczatkowej
 uint16_t dt = 1000000/MAX_FREQ; //czas wykonywania kroku [us]
 uint8_t enX = 1, enY = 1; //flagi odblokowujace dana os
 while (enX || enY) { //dopoki nie wroci na poczatek
  if (enX)
   if (--xi > 3) xi = 3; //po dekrementacji z 0 na 255 wroci z powrotem na 3
  if (enY)
   if (--yi > 3) yi = 3;
  STEP_PORT = (enX * stepX[xi]) | (enY * stepY[yi]); //ustaw odpowiednie bity (wyzeruj dana os jesli wrocila na poczatek)
  enX = !XMIN_FLAG, enY = !YMIN_FLAG;

  for (uint16_t i=0; i<dt-1; i++) { //opoznienie: dt-1, bo powyzsze operacje tez sie troche wykonuja
   asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); } //+6 cykli na for - razem 16 cykli, czyli 1us
 }
 STEP_PORT = 0;
 posX = 0, posY = 0;
}

void _setZ(uint8_t posZ) { //podnosi (1) lub opuszcza (0) pisak
 if (posZ) RELAY_PORT |= (1<<RELAY);
 else {
  RELAY_PORT &= ~(1<<RELAY);
  _delay_ms(RELAY_DELAY); //czekaj na wytlumienie drgan
 }
}

uint8_t _setXY(uint16_t destX, uint16_t destY) { //przesuwa pisak na zadana pozycje
 uint8_t result=0; //kod bledu zwracany przez funkcje
 int16_t dx = destX-posX, dy = destY-posY; //przesuniecie

 if ((dx == 0) && (dy == 0)) result = 4; //brak przesuniecia: kod 4
 else { //ruch w co najmniej jednej osi
  uint16_t absX, absY; //modul krokow
  int8_t dirX, dirY; //kierunek krokow
  uint8_t paX[4], paY[4]; //tablice krokow
  if (dx < 0) { //os X - ruch "ujemny"
   paX[0] = stepX[3], paX[1] = stepX[2], paX[2] = stepX[1], paX[3] = stepX[0];
   dirX = -1;
   absX = -dx; }
  else { //os X - ruch "dodatni"
   paX[0] = stepX[0], paX[1] = stepX[1], paX[2] = stepX[2], paX[3] = stepX[3];
   dirX = 1;
   absX = dx; }
  if (dy < 0) { //os Y
   paY[0] = stepY[3], paY[1] = stepY[2], paY[2] = stepY[1], paY[3] = stepY[0];
   dirY = -1;
   absY = -dy; }
  else { //os Y
   paY[0] = stepY[0], paY[1] = stepY[1], paY[2] = stepY[2], paY[3] = stepY[3];
   dirY = 1;
   absY = dy; }
  if (dirX != xdir_last) //jesli poprzedni ruch mial inny kierunek...
   xi = 3 - xi; //...to trzeba odwrocic takze indeksy
  if (dirY != ydir_last)
   yi = 3 - yi;

  //ruch w jednej osi - nie trzeba dzielic na mikrokroki
  uint16_t dt = 1000000/MAX_FREQ; //czas wykonywania kroku [us]
  uint8_t enX = (dx!=0), enY = (dy!=0); //flagi odblokowujace dana os
  if (!enX) { //ruch osi Y
   while (enY) {
    posY += dirY;
    yi = (yi+1) % 4;
    STEP_PORT = (STEP_PORT & STEP_XMASK) | paY[yi]; //stan silnika X zostaje ten sam
    enY = !((YMIN_FLAG && (dirY == -1)) || (YMAX_FLAG && (dirY == 1)) || (posY == destY)); //wylaczenie osi jesli dotknie krancowki lub zajmie koncowa pozycje
    for (uint16_t i=0; i<dt-1; i++) { //opoznienie: dt-1, bo powyzsze operacje tez sie troche wykonuja
     asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); } //+6 cykli na for - razem 16 cykli, czyli 1us
   }
  }
  else if (!enY) { //ruch osi X
   while (enX) {
    posX += dirX;
    xi = (xi+1) % 4;
    STEP_PORT = (STEP_PORT & STEP_YMASK) | paX[xi];
    enX = !((XMIN_FLAG && (dirX == -1)) || (XMAX_FLAG && (dirX == 1)) || (posX == destX));
    for (uint16_t i=0; i<dt-1; i++) {
     asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); }
   }
  }
  else { //ruch w obu osiach (na podstawie http://hostcode.sourceforge.net/view/1085)
   uint32_t micro_total = lcm(absX, absY); //ilosc mikrokrokow na czas trwania calego ruchu - NWW
   uint16_t microX = micro_total/absX,
      microY = micro_total/absY; //ilosc mikrokrokow na kazdy krok osi
   dt = (uint16_t)(1000000 * (MAX(absX, absY)) / MAX_FREQ / micro_total); //czas trwania mikrokroku [us]

   //wykonywanie mikrokrokow - osobny kod dla dluzszych opoznien (niezbyt eleganckie, ale szkoda czasu na ify wewnatrz petli)
   uint16_t mX=0, mY=0; //do iteracji
   uint8_t changed=0;
   if (dt >= 3) { //jesli mikrokrok trwa co najmniej 3us
    while (enX || enY) { //dopoki ktorakolwiek os jest aktywna
     mX++, mY++;
     if ((enX) && (mX == microX)) { //sygnal na os X
      mX = 0;
      posX += dirX;
      xi = (xi+1) % 4;
      enX = !((XMIN_FLAG && (dirX == -1)) || (XMAX_FLAG && (dirX == 1)) || (posX == destX));
      changed++;
     }

     if ((enY) && (mY == microY)) { //sygnal na os Y
      mY = 0;
      posY += dirY;
      yi = (yi+1) % 4;
      enY = !((YMIN_FLAG && (dirY == -1)) || (YMAX_FLAG && (dirY == 1)) || (posY == destY));
      changed++;
     }

     if (changed) {
      STEP_PORT = paX[xi] | paY[yi];
      changed = 0;
     }

     for (uint16_t i=0; i<dt-2; i++) { //opoznienie: dt-2, bo powyzsze bloki tez sie troche wykonuja
      asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
      asm("nop"); asm("nop"); asm("nop"); //+6 cykli na for: razem 1us
     }
    }
   }
   else { //dla krotszych mikrokrokow brak opoznienia
    while (enX || enY) {
     mX++, mY++;
     if ((enX) && (mX == microX)) {
      mX = 0;
      posX += dirX;
      xi = (xi+1) % 4;
      enX = !((XMIN_FLAG && (dirX == -1)) || (XMAX_FLAG && (dirX == 1)) || (posX == destX));
      changed++;
     }
     if ((enY) && (mY == microY)) {
      mY = 0;
      posY += dirY;
      yi = (yi+1) % 4;
      enY = !((YMIN_FLAG && (dirY == -1)) || (YMAX_FLAG && (dirY == 1)) || (posY == destY));
      changed++;
     }
     if (changed) {
      STEP_PORT = paX[xi] | paY[yi];
      changed = 0;
     }
    }
   }
  }
  xdir_last = dirX, ydir_last = dirY; //zapamietaj kierunek ruchu
  if (FLAGS) result = FLAGS; //zwroc blad jesli wcisnieto krancowke
 }
 if (!power_flag) STEP_PORT = 0; //i ewentualnie wyzeruj po zakonczeniu przesuwania
 return result;
}

void _getZ(void) { //wysyla aktualny stan pisaka (0 - podniesiony; 1 - opuszczony)
 USART_putc(posZ);
}

void _getXY(void) { //wysyla aktualna pozycje pisaka
 USART_putc((uint8_t)(posX>>8));
 USART_putc((uint8_t)posX);
 USART_putc((uint8_t)(posY>>8));
 USART_putc((uint8_t)posY);
}

void _setPowerMode(uint8_t mode) { //ustawia tryb pracy silnikow (0 - male cieplo; 1 - duza dokladnosc)
 power_flag = mode;
 if (mode == 0) STEP_PORT = 0; //zerowanie silnikow
}

void _setRawXY(uint16_t points) { //wylacza mopsioCODE i przesuwa pisak na podstawie surowych danych
 _rxcallback = 1; //wlaczenie przechwytywania danych (mopsioCODE wylaczony!)
 rawdata_xy_flag = 1; //wlaczenie obslugi pisaka
 rawdata_count = 4 * points; //kazdy punkt ma po 4 bajty
 rawdata_counter = 0; //zerowanie poprzedniego wykonania
}

void _selectPen(void) { //podnosi pisak i czeka na wcisniecie przycisku OK
 _setZ(1);
 while (bit_is_set(MENU_PIN, MENU_OK));
}

void _rect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { //rysuje prostokat na podstawie dwoch przeciwleglych wierzcholkow
 SAVEPOWERFLAG; //wlaczanie trybu dokladnego
 _setZ(1);
 _setXY(x1, y1);
 _setZ(0);
 _setXY(x2, y1);
 _setXY(x2, y2);
 _setXY(x1, y2);
 _setXY(x1, y1);
 _setZ(1);
 LOADPOWERFLAG; //przywracanie poprzedniego trybu
}

void _arc(uint16_t x, uint16_t y, uint16_t r, uint16_t a1, uint16_t a2, uint8_t type) {
 //rysuje luk o srodku (X,Y), promieniu R, poczatku w kacie A1 i koncu A2 (od poziomu, w stopniach)
 //w zaleznosci od TYPE, luk moze byc normalny (0) lub zakonczony odcinkami do srodka w punkcie X,Y (1)
 SAVEPOWERFLAG;
 uint16_t xstart = x + (int16_t)((int32_t)r*_cos(a1)/32767),
    ystart = y - (int16_t)((int32_t)r*_sin(a1)/32767); //pozycje poczatku luku
 _setZ(1);
 if (type == 1) { //rysowanie odcinka od srodka do poczatku luku
  _setXY(x, y);
  _setZ(0);
  _setXY(xstart, ystart);
 } else { //normalne przejscie na poczatek luku
  _setXY(xstart, ystart);
  _setZ(0);
 }

 //rysowanie luku
 for (uint16_t a = a1+1; a <= a2; a++) {
  _setXY(x + (int16_t)((int32_t)r*_cos(a)/32767),
      y - (int16_t)((int32_t)r*_sin(a)/32767));
 }

 if (type == 1) _setXY(x, y); //rysowanie drugiego odcinka (od konca luku do srodka)
 _setZ(1);
 LOADPOWERFLAG;
}

void _text(uint16_t x, uint16_t y, uint8_t size, uint8_t spacing, uint8_t count) {
 //wylacza mopsioCODE i wypisuje tekst na podstawie surowych danych
 //poczatek w punkcie (X,Y), wielkosc czcionki SIZE, odleglosc miedzy punktami SPACING, ilosc znakow COUNT
 _rxcallback = 1;
 rawdata_text_flag = 1;
 rawdata_text_x = x, rawdata_text_y = y, rawdata_text_size = size, rawdata_text_spacing = spacing;
 rawdata_count = count;
 rawdata_counter = 0;
 SAVEPOWERFLAG;
}

void __drawChar(uint8_t byte) { //rysuje pojedynczy znak o podanym kodzie (od 0 do 111, kodowane po stronie komputera)
 const TCHAR * charP = &(font1_chars[byte]);
 uint16_t offset = pgm_read_word(&charP->arr_offset); //offset w tablicy font1_arr
 uint8_t width = pgm_read_byte(&charP->width); //szerokosc znaku
 int16_t yshift = pgm_read_word(&charP->yshift); //przesuniecie w pionie

 PGM_bP arrP = (PGM_bP)(font1_arr + offset); //wskaznik do poczatku znaku w tablicy font1_arr
 while (1) {
  _setZ(1);
  uint8_t points = pgm_read_byte(arrP++); //ilosc punktow do odczytania
  if (points == 0) break; //jesli koniec tablicy
  for (uint8_t i=0; i<points; i++) {
   uint8_t charx = pgm_read_byte(arrP++), chary = pgm_read_byte(arrP++);
   uint16_t x = rawdata_text_x + rawdata_text_size*charx/60; //uwzglednienie rozmiaru czcionki (tablice zapisane sa dla rozmiaru 60)
   uint16_t y = rawdata_text_y + rawdata_text_size*(chary + yshift)/60;
   _setXY(x, y);
   if (i==0) _setZ(0); //po przeniesieniu na pozycje pierwszego punktu, opusc pisak
  }
 }
 rawdata_text_x += rawdata_text_size*(width+rawdata_text_spacing)/60;
}



font.h
/*
 * font.h
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Czcionka do pisania tekstu
 */

#ifndef FONT_H_
#define FONT_H_

#include <avr/pgmspace.h>

typedef struct PROGMEM { //struktura opisujaca pojedynczy znak
 uint16_t arr_offset; //offset w tablicy lamanych
 uint8_t width; //szerokosc znaku
 int16_t yshift; //przesuniecie znaku w pionie wzgledem glownej linii
} TCHAR;
#define PGM_bP const prog_uint8_t * //analogicznie jak dla PGM_P w pgmspace.h

extern const uint8_t font1_arr[] PROGMEM;
extern const TCHAR font1_chars[] PROGMEM;

#endif /* FONT_H_ */



font.c
/*
 * font.c
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Czcionka do pisania tekstu
 */

#include "font.h"

/* znaki do rysowania tekstu
 * kazdy znak to odpowiednia ilosc lamanych o zadanej liczbie punktow
 * tablica font1_arr zawiera dane o lamanych wszystkich znakow - kazdy znak zapisany jest w osobnym wierszu
 * pierwszy bajt w wierszu okresla ilosc bajtow 1. lamanej znaku (x1, y1, x2, y2, ...)
 * po okreslonej liczbie bajtow nastepuje kolejny bajt okreslajacy ilosc bajtow 2. lamanej znaku
 * w ten sposob moze byc zapisana dowolna ilosc lamanych odpowiadajacych znakowi, a na koncu wiersza znajduje sie bajt 0 okreslajacy koniec znaku
 */
const uint8_t font1_arr[] PROGMEM = {
 0, //
 2,6,164,6,40,2,0,0,11,0,0, //!
 2,0,52,0,0,2,30,52,30,0,0, //"
 2,11,0,38,177,2,82,174,57,0,2,10,107,94,106,2,0,66,86,66,0, //#
 16,80,153,77,177,59,192,41,195,17,189,3,172,3,127,17,112,62,106,79,89,79,43,62,25,39,18,11,30,0,51,0,69,2,40,219,40,195,2,39,19,39,0,0, //$
 2,2,0,83,175,9,12,173,0,164,0,145,11,133,30,133,42,144,42,162,30,173,12,173,9,58,41,46,32,47,13,58,1,77,1,89,12,89,30,77,41,58,41,0, //%
 11,113,78,102,78,91,43,71,11,53,0,29,0,11,13,0,36,1,63,14,82,40,93,8,79,162,62,170,45,170,27,158,21,139,25,117,95,6,116,6,0, //&
 2,0,51,0,0,0, //'
 6,27,241,8,185,0,130,0,97,8,50,30,0,0, //(
 6,3,241,22,185,30,130,30,97,22,50,0,0,0, //)
 2,1,14,55,47,2,0,46,53,13,2,27,62,27,0,0, //*
 2,0,47,91,47,2,47,100,47,0,0, //+
 4,0,41,15,41,15,14,1,0,0, //,
 2,0,0,87,0,0, //-
 2,0,0,14,0,0, //.
 2,0,0,84,171,0, ///
 13,0,145,10,162,31,174,57,174,76,160,84,144,84,27,75,12,55,0,28,0,11,11,0,27,0,145,0, //0
 3,9,142,44,168,44,0,2,0,0,91,0,0, //1
 11,0,123,0,143,9,160,27,169,48,169,64,158,70,137,70,101,1,46,1,0,73,0,0, //2
 19,0,47,0,26,9,9,27,0,41,0,63,8,73,21,73,74,54,93,25,93,54,93,75,111,75,143,64,164,46,175,28,175,10,164,0,148,0,136,0, //3
 4,67,0,67,171,0,48,86,48,0, //4
 15,61,168,7,168,7,82,20,102,36,108,56,108,70,99,78,80,78,27,66,9,52,0,23,0,8,9,2,24,0,38,0, //5
 18,71,138,68,159,56,171,24,171,10,165,0,150,0,28,13,10,28,0,47,0,63,9,76,26,76,84,67,99,51,110,30,110,12,99,0,80,0, //6
 3,0,171,82,171,27,0,0, //7
 25,32,172,12,166,0,146,0,116,12,101,30,90,52,90,70,80,78,65,78,20,66,7,50,0,31,0,12,7,3,24,3,62,12,80,31,90,52,90,68,99,79,114,79,147,67,166,50,172,32,172,0, //8
 18,2,31,12,11,29,0,53,0,69,11,76,31,76,153,64,169,46,174,28,174,11,165,0,149,0,84,11,68,27,60,47,60,65,71,77,97,0, //9
 2,1,101,16,101,2,0,0,14,0,0, //:
 2,0,145,14,145,4,1,41,15,41,15,14,1,0,0, //;
 3,60,97,0,48,59,0,0, //<
 2,1,40,92,40,2,0,0,91,0,0, //=
 3,0,97,60,48,0,0,0, //>
 12,0,111,0,139,10,156,28,164,55,164,71,154,78,129,78,92,72,72,59,62,38,61,38,33,2,31,0,41,0,0, //?
 20,0,136,11,158,29,172,62,172,88,156,97,127,97,14,86,0,71,0,53,10,53,100,39,112,24,112,9,104,2,88,1,19,12,5,27,0,41,2,53,10,0, //@
 3,0,1,45,171,86,0,2,11,39,76,39,0, //A
 14,0,0,0,168,57,168,71,156,77,141,77,114,69,100,54,91,69,82,78,67,78,26,70,10,53,0,0,0,2,56,91,0,91,0, //B
 14,82,116,82,145,73,163,54,175,28,175,10,165,0,148,0,33,11,13,29,0,57,0,75,14,82,33,82,51,0, //C
 9,0,166,0,0,43,0,64,9,74,24,74,138,63,154,41,165,0,166,0, //D
 4,80,166,0,166,0,0,78,0,2,1,84,70,84,0, //E
 3,0,0,0,173,77,173,2,0,89,69,89,0, //F
 15,79,124,79,147,70,164,54,173,27,173,11,162,0,142,0,32,11,11,29,0,51,0,70,10,80,31,80,87,47,87,0, //G
 2,0,178,0,1,2,69,180,69,0,2,1,89,69,89,0, //H
 2,0,166,90,166,2,46,165,46,0,2,0,1,92,1,0, //I
 7,82,175,82,39,70,12,53,0,30,0,11,12,0,38,0, //J
 2,0,0,0,181,3,78,180,1,94,75,0,0, //K
 3,0,173,0,0,79,0,0, //L
 5,0,0,0,174,35,73,74,175,74,0,0, //M
 4,0,0,0,176,81,2,81,177,0, //N
 13,0,138,0,32,10,10,30,0,53,0,73,10,83,31,83,138,71,160,50,173,32,173,11,160,0,138,0, //O
 9,0,0,0,171,41,171,64,161,77,140,77,100,65,78,45,65,2,65,0, //P
 2,62,11,95,0,13,0,139,0,33,10,12,30,1,53,1,73,12,84,33,84,139,71,162,50,174,32,174,11,162,0,139,0, //Q
 2,38,66,69,0,9,0,1,0,173,41,173,64,163,77,142,77,101,65,80,45,67,2,67,0, //R
 20,76,129,76,145,65,163,48,174,28,174,12,164,2,148,2,113,12,96,29,87,49,87,67,75,78,58,78,28,68,10,51,0,29,0,11,10,0,27,0,47,0, //S
 2,0,170,90,170,2,45,169,45,0,0, //T
 8,0,176,0,32,12,11,31,0,47,0,65,11,76,31,76,179,0, //U
 3,0,173,46,0,93,174,0, //V
 5,0,170,9,0,41,101,74,1,83,172,0, //W
 2,0,1,90,175,2,1,172,91,0,0, //X
 3,0,175,41,96,79,176,2,40,97,40,0,0, //Y
 6,0,166,84,166,84,153,2,12,2,0,88,0,0, //Z
 4,33,228,0,228,0,0,31,0,0, //[
 2,0,175,81,0,0, //\ tu musi byc tekst, zeby nie komentowalo linijki nizej
 4,0,229,32,229,32,0,2,0,0, //]
 3,0,0,36,97,73,0,0, //^
 2,0,0,121,0,0, //_
 2,0,11,29,0,0, //`
 7,5,102,12,116,23,125,54,125,67,117,75,102,75,0,11,75,61,61,74,44,80,20,80,7,72,0,59,0,22,16,4,44,4,61,12,75,31,0, //a
 2,0,180,0,0,12,0,31,13,13,30,4,51,4,67,13,75,32,75,107,65,118,47,126,30,126,14,116,0,101,0, //b
 12,72,91,64,112,47,124,26,124,9,115,0,99,0,28,11,9,24,0,55,0,69,14,72,39,0, //c
 12,75,97,61,119,45,128,26,128,9,118,0,102,0,31,12,13,25,3,51,3,64,17,76,42,2,75,179,75,0,0, //d
 14,72,39,69,14,55,0,25,0,11,9,0,28,0,98,9,114,26,124,47,124,64,112,72,90,72,62,0,62,0, //e
 6,24,0,24,154,36,173,71,173,87,155,87,143,2,0,103,75,103,2,1,2,62,2,0, //f
 12,75,154,60,176,45,185,26,185,9,175,0,159,0,88,11,70,25,61,51,61,64,74,75,99,8,74,183,74,26,65,10,48,0,27,0,12,6,2,24,2,38,0, //g
 2,0,0,0,180,7,1,98,14,119,32,128,50,128,67,117,75,96,75,1,0, //h
 2,0,0,91,0,3,46,0,46,116,12,116,2,40,164,50,164,0, //i
 9,29,181,73,181,73,27,61,9,48,0,27,0,9,10,0,26,0,38,2,67,229,77,229,0, //j
 2,0,178,0,3,3,72,125,1,65,74,0,0, //k
 3,13,167,46,167,46,0,2,0,1,88,1,0, //l
 4,0,116,9,124,21,122,21,0,4,22,120,43,127,62,120,62,0,4,59,121,85,127,102,118,102,1,0, //m
 2,0,0,0,126,7,0,90,7,111,27,126,49,126,67,117,75,99,75,1,0, //n
 13,67,14,55,0,24,0,11,10,0,28,0,99,9,115,26,125,47,125,64,112,74,91,74,38,67,14,0, //o
 12,1,87,14,69,30,59,52,59,67,69,75,88,75,162,65,174,48,182,31,182,15,172,1,157,2,0,181,0,0,0, //p
 12,75,86,62,69,45,59,24,59,8,69,0,88,0,162,10,174,28,182,45,182,61,171,75,157,2,75,180,75,0,0, //q
 6,0,116,24,108,40,118,65,118,78,102,78,87,2,24,107,24,0,2,2,0,50,0,0, //r
 18,70,98,61,116,44,124,27,124,9,118,0,104,0,87,8,74,25,64,52,64,68,54,74,41,74,21,65,8,48,0,24,0,8,12,0,29,0, //s
 7,81,25,72,9,57,0,37,0,23,8,17,27,17,155,2,0,107,75,107,0, //t
 2,75,126,75,0,7,75,36,67,15,47,0,25,0,8,9,0,27,0,125,0, //u
 3,0,116,44,0,90,114,0, //v
 5,0,117,15,0,40,85,64,0,78,118,0, //w
 2,0,2,79,125,2,0,123,78,0,0, //x
 2,26,0,92,182,2,0,179,45,56,0, //y
 4,0,115,84,115,5,0,87,0,0, //z
 11,54,241,38,231,31,214,31,140,19,127,0,121,18,113,33,97,33,25,42,9,55,0,0, //{
 2,0,235,0,0,0, //|
 11,1,241,17,231,24,214,24,140,36,127,55,121,37,113,22,97,22,25,12,9,0,0,0, //}
 10,0,5,5,14,13,20,23,20,32,15,38,7,45,1,55,0,63,5,70,16,0, //~
 7,0,33,44,203,86,32,74,21,72,8,80,0,91,2,2,11,71,76,71,0, //Ą
 14,83,117,83,145,73,163,55,175,28,175,10,165,0,148,0,34,11,13,29,0,58,0,75,15,83,34,83,51,2,44,189,68,209,0, //Ć
 8,80,199,0,199,0,32,78,32,64,20,62,8,69,0,81,1,2,1,116,71,116,0, //Ę
 3,15,173,15,0,94,0,2,0,67,50,108,0, //Ł
 2,36,193,60,213,4,0,0,0,176,80,2,80,177,0, //Ń
 13,0,138,0,32,11,11,30,0,54,0,74,11,84,31,84,138,71,160,50,173,33,173,12,160,0,138,2,41,191,66,211,0, //Ó
 20,76,129,76,145,65,163,48,174,28,174,12,164,2,148,2,113,12,96,29,87,49,87,67,75,79,58,79,28,68,10,51,0,29,0,11,10,0,27,0,47,2,36,190,60,210,0, //Ś
 6,0,165,84,165,84,152,3,12,3,0,88,0,2,42,187,66,207,0, //Ź
 6,0,166,84,166,84,152,3,12,3,0,88,0,2,41,197,46,197,0, //Ż
 11,6,130,13,143,24,153,54,153,68,144,76,130,76,28,65,20,63,8,70,0,80,2,11,75,88,62,101,45,107,21,107,7,100,0,87,0,50,17,31,44,31,61,40,76,58,0, //ą
 12,72,90,64,112,47,124,26,124,9,114,0,98,0,27,11,9,24,0,55,0,69,14,72,38,2,26,140,50,160,0, //ć
 14,72,66,69,41,55,27,24,27,11,36,0,55,0,126,9,142,26,151,47,151,64,139,72,118,72,89,0,89,5,55,28,45,19,44,8,51,0,60,2,0, //ę
 3,13,167,47,167,47,0,2,0,1,89,1,2,23,64,73,105,0, //ł
 2,0,0,0,126,7,0,90,8,111,27,126,50,126,67,117,75,99,75,1,2,26,140,50,160,0, //ń
 13,68,14,55,0,25,0,12,10,0,28,0,99,9,115,26,125,48,125,65,112,75,91,75,38,68,14,2,25,141,50,161,0, //ó
 18,71,98,62,116,45,124,27,124,9,118,1,104,1,87,9,74,25,64,53,64,69,54,75,41,75,21,66,8,49,0,25,0,9,12,0,29,2,25,141,49,161,0, //ś
 4,0,114,84,114,5,0,87,0,2,31,137,55,157,0, //ź
 4,0,114,84,114,5,0,87,0,2,39,150,44,150,0, //ż
};

const TCHAR font1_chars[] PROGMEM = { //tablica uzywana do rysowania znakow
 {0, 80, 0},        //
 {1, 11, 6},        //!
 {12, 30, 30},      //"
 {23, 94, 0},       //#
 {44, 80, -24},     //$
 {88, 89, 0},       //%
 {132, 116, 0},     //&
 {173, 0, 30},      //'
 {179, 30, -64},    //(
 {193, 30, -64},    //)
 {207, 55, 127},    //*
 {223, 91, 23},     //+
 {234, 15, -29},    //,
 {244, 87, 67},     //-
 {250, 14, 6},      //.
 {256, 84, 0},      ///
 {262, 84, 0},      //0
 {290, 91, 0},      //1
 {303, 73, 0},      //2
 {327, 75, 0},      //3
 {367, 86, 0},      //4
 {377, 78, 0},      //5
 {409, 76, 0},      //6
 {447, 82, 0},      //7
 {455, 79, 0},      //8
 {507, 77, 0},      //9
 {545, 16, 6},      //:
 {556, 15, -29},    //;
 {571, 60, 6},      //<
 {579, 92, 53},     //=
 {590, 60, 6},      //>
 {598, 78, 6},      //?
 {629, 97, 0},      //@
 {671, 86, 0},      //A
 {684, 78, 0},      //B
 {719, 82, 0},      //C
 {749, 74, 0},      //D
 {769, 80, 0},      //E
 {784, 77, 0},      //F
 {797, 80, 0},      //G
 {829, 69, 0},      //H
 {845, 92, 0},      //I
 {861, 82, 0},      //J
 {877, 78, 0},      //K
 {890, 79, 0},      //L
 {898, 74, 0},      //M
 {910, 81, 0},      //N
 {920, 83, 0},      //O
 {948, 77, 0},      //P
 {968, 95, 0},      //Q
 {1001, 77, 0},     //R
 {1026, 78, 0},     //S
 {1068, 90, 0},     //T
 {1079, 76, 0},     //U
 {1097, 93, 0},     //V
 {1105, 83, 0},     //W
 {1117, 91, 0},     //X
 {1128, 79, 0},     //Y
 {1141, 88, 0},     //Z
 {1155, 33, -62},   //[
 {1165, 81, 0},     //\ tu musi byc tekst, zeby nie komentowalo linijki nizej
 {1171, 32, -62},   //]
 {1181, 73, 78},    //^
 {1189, 121, -32},  //_
 {1195, 29, 171},   //`
 {1201, 75, 0},     //a
 {1240, 75, 0},     //b
 {1271, 72, 0},     //c
 {1297, 76, 0},     //d
 {1328, 72, 0},     //e
 {1358, 87, 0},     //f
 {1382, 75, -66},   //g
 {1425, 75, 0},     //h
 {1446, 91, 0},     //i
 {1464, 77, -66},   //j
 {1489, 74, 0},     //k
 {1502, 88, 0},     //l
 {1515, 102, 0},    //m
 {1543, 75, 0},     //n
 {1564, 74, 0},     //o
 {1592, 75, -62},   //p
 {1623, 75, -62},   //q
 {1654, 78, 0},     //r
 {1678, 74, 0},     //s
 {1716, 81, 0},     //t
 {1737, 75, 0},     //u
 {1758, 90, 0},     //v
 {1766, 78, 0},     //w
 {1778, 79, 0},     //x
 {1789, 92, -62},   //y
 {1800, 87, 0},     //z
 {1810, 55, -67},   //{
 {1834, 0, -64},    //|
 {1840, 55, -67},   //}
 {1864, 70, -59},   //~
 {1886, 91, -38},   //Ą
 {1907, 83, 0},     //Ć
 {1942, 81, -38},   //Ę
 {1965, 94, 0},     //Ł
 {1978, 80, 0},     //Ń
 {1993, 84, 0},     //Ó
 {2026, 79, 0},     //Ś
 {2073, 88, 0},     //Ź
 {2092, 88, 0},     //Ż
 {2111, 80, -36},   //ą
 {2158, 72, 0},     //ć
 {2189, 72, -34},   //ę
 {2230, 89, 0},     //ł
 {2248, 75, 0},     //ń
 {2274, 75, 0},     //ó
 {2307, 75, 0},     //ś
 {2350, 87, 0},     //ź
 {2365, 87, 0},     //ż
};



mopsiocode_config.h
/*
 * mopsiocode_config.h
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Konfiguracja mopsioCODE i callbacki wywoływane przez protokol po odebraniu odpowiedniej komendy
 */

#ifndef MOPSIOCODE_CONFIG_H_
#define MOPSIOCODE_CONFIG_H_

void mopsiocode_config(void);
void mopsiocode_rawdata_event(void);
void status(uint8_t byte);
void receive(uint8_t byte);
void returnXY(void);
void setZ(void);
void setXY(void);
void getZ(void);
void getXY(void);
void setPowerMode(void);
void setRawXY(void);
void selectPen(void);
void rect(void);
void arc(void);
void text(void);


#endif /* MOPSIOCODE_CONFIG_H_ */



mopsiocode_config.c
/*
 * mopsiocode_config.c
 *    Data:  11-02-2014
 *    Autor: mopsiok
 *  
 *  Konfiguracja mopsioCODE i callbacki wywoływane przez protokol po odebraniu odpowiedniej komendy
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include "mopsioCODE_2_0/mopsiocode_2_0.h"
#include "LCD_HD44780/lcd.h"
#include "control.h"
#include "common.h"
#include "mopsiocode_config.h"

void mopsiocode_config(void) { //konfiguracja protokolu mopsioCODE
 mopsiocode_init(99); //inicjalizacja USARTu dla kwarcu 16MHz i 10000baud
 mopsiocode_callback1(returnXY); //powrot pisaka na pozycje poczatkowa (0,0)
 mopsiocode_callback2(setZ); //podnoszenie/opuszczanie pisaka
 mopsiocode_callback3(setXY); //przesuwanie pisaka - zwracane wartosci: 4 (brak przesuniecia) lub FLAGS (wcisnieta krancowka)
 mopsiocode_callback4(getZ); //zwracanie stanu pisaka
 mopsiocode_callback5(getXY); //zwracanie pozycji pisaka
 mopsiocode_callback6(setPowerMode); //tryb pracy silnikow - wieksza dokladnosc (1) lub mniejsze cieplo (0)
 mopsiocode_callback7(setRawXY); //przesuwanie pisaka - surowe dane (poza mopsioCODE) do szybszej transmisji
 mopsiocode_callback8(selectPen); //wybor pisaka
 mopsiocode_callback9(rect); //rysowanie prostokata
 mopsiocode_callback10(arc); //rysowanie luku
 mopsiocode_callback11(text); //pisanie tekstu
 mopsiocode_callback_status(status); //obsluga statusow komunikacyjnych
 mopsiocode_callback_receive(receive); //obsluga przerwania RX
 sei();
}

void mopsiocode_rawdata_event(void) { //zdarzenie obslugujace surowe dane (wywolywane w petli glownej)
 if (rawdata_flag) { //jesli odebrano nowy bajt
  if (rawdata_xy_flag) { //jesli wykonuje sie komenda setRawXY
   uint8_t i = rawdata_counter % 4; //numer odczytywanego bajtu (0..3)
   rawdata_xy[i] = rawdata_byte; //zapisz odczytany bajt do tablicy

   if (i == 3) //jesli odczytano 4 bajty, to przesun pisak
    _setXY((uint16_t)(rawdata_xy[0]<<8) + rawdata_xy[1], (uint16_t)(rawdata_xy[2]<<8) + rawdata_xy[3]);
  }

  else if (rawdata_text_flag) { //jesli wykonuje sie komenda text
   __drawChar(rawdata_byte); //rysuj pojedynczy znak
  }

  if (++rawdata_counter == rawdata_count) {
   if (rawdata_text_flag)
    LOADPOWERFLAG; //przywracanie poprzedniego trybu
   _rxcallback = 0; //jesli odebrane zostaly wszystkie dane, to przywroc mopsioCODE
   rawdata_text_flag = 0, rawdata_xy_flag = 0; //zerowanie flag
  }

  rawdata_flag = 0;
  USART_putc(255); //potwierdz odbior
 }
}



//================================================================================================
//      CALLBACKI WYWOLYWANE Z POZIOMU MOPSIOCODE
// sa to funkcje posrednie - funkcje odpowiedzialne za faktyczne sterowanie znajduja sie w control.c

void status(uint8_t byte) { //informuje o przychodzacych rozkazach na LCD
 LCD_erase(); //czyszczenie ekranu i wyswietlanie stalych elementow
 lcd_goto(14,1);
 if (byte == 0) lcd_putstr("OK"); //poprawnie odebrany rozkaz
 else if (byte == 1) lcd_putstr("E1"); //blad transmisji - niezgodnosc z oryginalem
 else if (byte == 2) lcd_putstr("E2"); //bledna ilosc parametrow
 else lcd_putstr("E3"); //nieobsluzony rozkaz
 timerLCD = 50; //500ms wyswietlania
 LCD_status_flag = 1; //odliczanie wlaczone

 if (byte == 0) { //+1 odebrany rozkaz jesli status OK
  LCD_count++;
  lcd_goto(3,1);
  lcd_putint(LCD_count);
 }
}

void receive(uint8_t byte) { //funkcja wywolywana w przerwaniu RX gdy mopsioCODE jest wylaczony
 rawdata_flag = 1;
 rawdata_byte = byte;
}

void returnXY(void) {
 LCD_cmd("returnXY"); //wysylanie opisu do LCD
 _returnXY();
}

void setZ(void) {
 LCD_cmd("setZ "); //nazwa rozkazu
 lcd_putint(_args[0]); //parametr
 _setZ(_args[0]);
}

void setXY(void) {
 uint16_t destX = (uint16_t)(_args[0] << 8) + _args[1]; //przeslana pozycja
 uint16_t destY = (uint16_t)(_args[2] << 8) + _args[3];

 LCD_cmd("setXY "); //nazwa rozkazu
 lcd_putint(destX); //parametr X
 lcd_putc(' '); //odstep
 lcd_putint(destY); //parametr Y

 _return_value = _setXY(destX, destY);
}

void getZ(void) {
 LCD_cmd("getZ");
 _getZ();
}

void getXY(void) {
 LCD_cmd("getXY");
 _getXY();
}

void setPowerMode(void) {
 LCD_cmd("setPowerMode ");
 lcd_putint(_args[0]);

 _setPowerMode(_args[0]);
}

void setRawXY(void) {
 uint16_t points = (uint16_t)(_args[0]<<8) + _args[1]; //w argumentach przesylana jest ilosc punktow do narysowania

 LCD_cmd("setRawXY ");
 lcd_putint(points);

 _setRawXY(points);
}

void selectPen(void) {
 LCD_cmd("selectPen");
 _selectPen();
}

void rect(void) {
 uint16_t x1 = (uint16_t)(_args[0] << 8) + _args[1], y1 = (uint16_t)(_args[2] << 8) + _args[3],
    x2 = (uint16_t)(_args[4] << 8) + _args[5], y2 = (uint16_t)(_args[6] << 8) + _args[7];

 LCD_cmd("rect ");
 lcd_putint(x1);
 lcd_putc(' ');
 lcd_putint(y1);
 lcd_putc(' ');
 lcd_putint(x2);
 lcd_putc(' ');
 lcd_putint(y2);

 _rect(x1, y1, x2, y2);
}

void arc(void) {
 uint16_t x = (uint16_t)(_args[0] << 8) + _args[1], y = (uint16_t)(_args[2] << 8) + _args[3],
    r = (uint16_t)(_args[4] << 8) + _args[5],
    a1 = (uint16_t)(_args[6] << 8) + _args[7], a2 = (uint16_t)(_args[8] << 8) + _args[9];
 uint8_t type = _args[10]; //typ luku: wycinek (1: odcinki do srodka luku) lub normalny luk (0: bez odcinkow)

 LCD_cmd("arc ");
 lcd_putint(x);
 lcd_putc(' ');
 lcd_putint(y);
 lcd_putc(' ');
 lcd_putint(r);
 lcd_putc(' ');
 lcd_putint(a1);
 lcd_putc(' ');
 lcd_putint(a2);
 lcd_putc(' ');
 lcd_putint(type);

 if (a1 == a2) { a1 = 0; a2 = 360; type = 0; } //pelny okrag
 if (a1 > a2) a2 += 360; //jesli luk przechodzi przez kat 0 to sie robia takie dziwadla
 _arc(x, y, r, a1, a2, type);
}

void text(void) {
 uint16_t x = (uint16_t)(_args[0] << 8) + _args[1], y = (uint16_t)(_args[2] << 8) + _args[3];
 uint8_t size = _args[4],
   spacing = _args[5],
   count = _args[6];
 LCD_cmd("text ");
 lcd_putint(x);
 lcd_putc(' ');
 lcd_putint(y);
 lcd_putc(' ');
 lcd_putint(y);
 lcd_putc(' ');
 lcd_putint(size);
 lcd_putc(' ');
 lcd_putint(spacing);
 lcd_putc(' ');
 lcd_putint(count);

 _text(x, y, size, spacing, count);
}



mopsiocode_2_0.h
/*
 * mopsioCODE - mopsowy protokół komunikacyjny
 * autor: mopsiok
 * wersja: 2.0
 * data: 12.2013
 *
 * Plik konfiguracyjny, ale tylko do magicznej kreski - poniżej wstęp wzbroniony
 */

#ifndef MOPSIOCODE_H_
#define MOPSIOCODE_H_

//ilosc parametrow dla kolejnych kodow rozkazow - nieuzywane ustawic na 0
#define CMD1 0 //powrot na poczatek ukladu wspolrzednych
#define CMD2 1 //podnoszenie/opuszczanie pisaka
#define CMD3 4 //przesuwanie pisaka
#define CMD4 0 //zwracanie stanu pisaka
#define CMD5 0 //zwracanie pozycji pisaka
#define CMD6 1 //tryb pracy silnikow
#define CMD7 2 //przesuwanie pisaka - tryb surowy (bez mopsioCODE)
#define CMD8 0 //wybor pisaka (czekanie na przycisk)
#define CMD9 8 //rysowanie prostokata: w argumentach pozycje przeciwleglych wierzcholkow
#define CMD10 11 //rysowanie luku: [X srodka] [Y srodka] [promien] [kat poczatkowy] [kat koncowy] [luk/wycinek]
#define CMD11 7 //pisanie tekstu: [X poczatku] [Y poczatku] [rozmiar] [odstep miedzy znakami] [ilosc znakow]
#define CMD12 0
#define CMD13 0
#define CMD14 0
#define CMD15 0

// Magiczna Kreska:
//-----------------------------------------------------

//MAX zwraca wieksza z 2 wartosci, MAX_PARAMS to najwieksza sposrod wszystkich ilosci parametrow
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MAX_PARAMS MAX(MAX(MAX(MAX(MAX(MAX(MAX(MAX(MAX(MAX(MAX(MAX(MAX(MAX(CMD1, CMD2), CMD3), CMD4), CMD5), CMD6), CMD7), CMD8), CMD9), CMD10), CMD11), CMD12), CMD13), CMD14), CMD15)
#define BUFFER_SIZE 6+MAX_PARAMS*2 //wielkosc bufora: [kod] 0x00 ([parametr] 0x00)*n 0x0D 0x0A [CRC] [CRC]

void (*callback1)(void); //callbacki rozkazow
void (*callback2)(void);
void (*callback3)(void);
void (*callback4)(void);
void (*callback5)(void);
void (*callback6)(void);
void (*callback7)(void);
void (*callback8)(void);
void (*callback9)(void);
void (*callback10)(void);
void (*callback11)(void);
void (*callback12)(void);
void (*callback13)(void);
void (*callback14)(void);
void (*callback15)(void);
void (*callback_status)(uint8_t byte); //callback nowego statusu
void (*callback_receive)(uint8_t byte); //callback odebrania nowego bajtu po UART

extern volatile uint8_t _rxcallback; //1 jesli odczytany znak ma byc pominiety i przekazany do callbacka
extern uint8_t _return_value; //wartosc zwracana po wykonaniu callbacka (0 - OK; inne - blad)
extern uint8_t _code; //kod odczytanego rozkazu
extern uint8_t _args_count; //ilosc argumentow
extern volatile uint8_t _args[]; //argumenty


void mopsiocode_callback1( void(*callback)(void)); //funkcje rejestrujace callbacki
void mopsiocode_callback2( void(*callback)(void));
void mopsiocode_callback3( void(*callback)(void));
void mopsiocode_callback4( void(*callback)(void));
void mopsiocode_callback5( void(*callback)(void));
void mopsiocode_callback6( void(*callback)(void));
void mopsiocode_callback7( void(*callback)(void));
void mopsiocode_callback8( void(*callback)(void));
void mopsiocode_callback9( void(*callback)(void));
void mopsiocode_callback10( void(*callback)(void));
void mopsiocode_callback11( void(*callback)(void));
void mopsiocode_callback12( void(*callback)(void));
void mopsiocode_callback13( void(*callback)(void));
void mopsiocode_callback14( void(*callback)(void));
void mopsiocode_callback15( void(*callback)(void));
void mopsiocode_callback_status( void(*callback)(uint8_t byte));
void mopsiocode_callback_receive( void(*callback)(uint8_t byte));

void mopsiocode_init(uint16_t); //inicjalizacja USARTu
void mopsiocode_event(void); //interpretacja rozkazu
void USART_putc(uint8_t); //wysylanie bajtu po USART
void USART_puts(char*); //wysylanie stringa po USART

#endif /* MOPSIOCODE_H_ */



mopsiocode_2_0.c
/*
 * mopsioCODE - mopsowy protokół komunikacyjny
 * autor: mopsiok 
 * wersja: 2.0
 * data: 12.2013
 *
 * Szczegóły dotyczące działania i instrukcje uruchomienia znajdują się w pliku README.html.
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <string.h>
#include <util/crc16.h>
#include "mopsiocode_2_0.h"

volatile uint8_t _rxcallback; //1 jesli odczytany znak ma byc pominiety przez protokol i przekazany do callbacka
uint8_t _return_value; //wartosc zwracana po wykonaniu callbacka: 0 = OK; >3 = blad podczas wykonywania
uint8_t _code; //poprawnie odczytany kod...
uint8_t _args_count; //ilosc argumentow...
volatile uint8_t _args[MAX_PARAMS]; //a takze same argumenty

volatile uint8_t buffer[BUFFER_SIZE]; //bufor przechowujacy odczytane dane
volatile uint8_t count; //ilosc odczytanych bajtow w buforze
volatile uint8_t byte_1, byte_2, byte_3; //trzy ostatnio odebrane bajty - do obslugi buffer_flag
volatile uint8_t buffer_flag = 1; //0 jesli odczytany znak ma byc pominiety i niezapisany w buforze
volatile uint8_t event_flag; //1 jesli odczytano nowy znak i trzeba wywolac event

void mopsiocode_callback1( void(*callback)(void)) { callback1 = callback; }
void mopsiocode_callback2( void(*callback)(void)) { callback2 = callback; }
void mopsiocode_callback3( void(*callback)(void)) { callback3 = callback; }
void mopsiocode_callback4( void(*callback)(void)) { callback4 = callback; }
void mopsiocode_callback5( void(*callback)(void)) { callback5 = callback; }
void mopsiocode_callback6( void(*callback)(void)) { callback6 = callback; }
void mopsiocode_callback7( void(*callback)(void)) { callback7 = callback; }
void mopsiocode_callback8( void(*callback)(void)) { callback8 = callback; }
void mopsiocode_callback9( void(*callback)(void)) { callback9 = callback; }
void mopsiocode_callback10( void(*callback)(void)) { callback10 = callback; }
void mopsiocode_callback11( void(*callback)(void)) { callback11 = callback; }
void mopsiocode_callback12( void(*callback)(void)) { callback12 = callback; }
void mopsiocode_callback13( void(*callback)(void)) { callback13 = callback; }
void mopsiocode_callback14( void(*callback)(void)) { callback14 = callback; }
void mopsiocode_callback15( void(*callback)(void)) { callback15 = callback; }
void mopsiocode_callback_status( void(*callback)(uint8_t byte)) { callback_status = callback; }
void mopsiocode_callback_receive( void(*callback)(uint8_t byte)) { callback_receive = callback; }

void mopsiocode_init(uint16_t ubrr);
void mopsiocode_event(void);
int8_t check_command(void);
void respond(uint8_t arg);
void execute(void);
uint16_t crc16(uint8_t start, uint8_t stop);
void USART_putc(uint8_t data);
void USART_puts(char* str);

//inicjalizacja modulu USART (ubrr = fosc/16/baud - 1) - komunikacja obustronna, format ramki 8;1;N
void mopsiocode_init(uint16_t ubrr) {
 UBRRH = (uint8_t)(ubrr>>8);
 UBRRL = (uint8_t)ubrr;
 UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE); //przesylanie i odbieranie danych oraz wlaczenie obslugi przerwania RXC
 UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //8 bitow danych, 1 bit stopu
}

//przerwanie odebrania danych po USART
ISR (USART_RXC_vect) {
 uint8_t byte = UDR; //odbierz dane
 if (_rxcallback) { //przekazanie bajtu do callbacka
  if (callback_receive) callback_receive(byte);

 } else if (buffer_flag == 1) { //zapis do bufora wlaczony
  buffer[count] = byte;
  count++;
  event_flag = 1; //wywolanie eventu - pora sprawdzic kilka rzeczy

 } else { //zapis wylaczony, wystapil jakis blad - czekaj na sekwencje R N CRC CRC
  if ((byte_3 == 13) && (byte_2 == 10)) { //jesli znalazlo to wroc do poczatku buforu i wyjdz
   byte_3 = 0; byte_2 = 0; byte_1 = 0; //poprzednie bajty z powrotem na 0
   count = 0; //poczatek buforu
   buffer_flag = 1; //odblokowanie zapisu
   respond(1); //wyslij odpowiedz o problemach z komunikacja, tak dla porzadku
  } else { byte_3 = byte_2; byte_2 = byte_1; byte_1 = byte; } //jesli nie znalazlo, to przesun bajty o 1 dalej
 }
}

//interpretacja buforu i podejmowanie odpowiednich akcji w zaleznosci od zawartosci
void mopsiocode_event(void) {
 if (event_flag) {
  uint8_t new = check_command(); //sprawdz czy odczytano nowy rozkaz
  if (new == -1) { //jesli wystapil blad (bledne CRC lub brak RN)
   buffer_flag = 0; //to pomijaj wszystkie odczytane bajty do nastepnej sekwencji R N CRC CRC
   count = 0;
   if (callback_status) callback_status(1); //wywolaj callback z bledem 1
   respond(1); //zwroc kod bledu 1
  } else if (new == 1) { //jesli poprawnie odczytano rozkaz
   execute(); //to wykonaj
   count = 0; } //i wroc na poczatek buforu
  event_flag = 0;
 }
}

//sprawdza czy bufor zawiera poprawnie odebrany rozkaz i jesli tak, to zapisuje istotne dane i zwraca 1
int8_t check_command(void) {
 //jesli ramka ma co najmniej 6 znakow, a 4. i 3. od konca to \r\n
 if ((count >= 6) && (buffer[count-4] == 13) && (buffer[count-3] == 10)) {
  uint16_t crc = crc16(0, count-3); //oblicz CRC dla ramki bez 2 ostatnich bajtow
  if (((uint8_t)(crc>>8) == buffer[count-2]) && ((uint8_t)crc == buffer[count-1])) { //jesli obliczone CRC zgadza sie z dwoma ostatnimi bajtami
   _code = buffer[0]; //zapis kodu rozkazu...
   _args_count = (count - 6)/2; //ilosci argumentow...
   for (uint8_t i=0; i<_args_count; i++) _args[i] = buffer[2+i*2]; //i samych argumentow
   return 1; }
  else return -1; //jesli CRC sie nie zgadza, to blad transmisji
 } else if (count >= BUFFER_SIZE) { //jesli bufor jest pelny, a dalej nie ma \r\n
  return -1; //to zwroc blad
 } else return 0; //w przeciwnym wypadku po prostu nie odczytano calego komunikatu
}

//wysyla odpowiedz (kod 255) z argumentem arg - potwierdzenie wykonania lub kod bledu
void respond(uint8_t arg) {
 uint8_t arr[8] = {255,0,arg,0,13,10,0,0};
 for (uint8_t i=0; i<8; i++) USART_putc(arr[i]);
}

//interpretuje rozkaz i jesli wszystko sie zgadza, to wywoluje odpowiadajacy mu callback
void execute(void) {
 //tablica z ilosciami parametrow dla kazdego z 15 mozliwych rozkazow
 uint8_t counts[] = {CMD1, CMD2, CMD3, CMD4, CMD5, CMD6, CMD7, CMD8, CMD9, CMD10, CMD11, CMD12, CMD13, CMD13, CMD14, CMD15};

 uint8_t c = 0; //domyslnie zwracane ma byc potwierdzenie
 _return_value = 0; //domyslnie funkcja ma sie wykonywac poprawnie
 if (_args_count == counts[_code-1]) { //jesli ilosc przekazanych argumentow zgadza sie z zalozeniami
  //to wywolaj odpowiedni callback (o ile jest zarejestrowany) i callback statusu OK
  if (callback1 && _code == 1) { if (callback_status) callback_status(0); callback1(); }
  else if (callback2 && _code == 2) { if (callback_status) callback_status(0); callback2(); }
  else if (callback3 && _code == 3) { if (callback_status) callback_status(0); callback3(); }
  else if (callback4 && _code == 4) { if (callback_status) callback_status(0); callback4(); }
  else if (callback5 && _code == 5) { if (callback_status) callback_status(0); callback5(); }
  else if (callback6 && _code == 6) { if (callback_status) callback_status(0); callback6(); }
  else if (callback7 && _code == 7) { if (callback_status) callback_status(0); callback7(); }
  else if (callback7 && _code == 8) { if (callback_status) callback_status(0); callback8(); }
  else if (callback7 && _code == 9) { if (callback_status) callback_status(0); callback9(); }
  else if (callback10 && _code == 10) { if (callback_status) callback_status(0); callback10();}
  else if (callback11 && _code == 11) { if (callback_status) callback_status(0); callback11(); }
  else if (callback12 && _code == 12) { if (callback_status) callback_status(0); callback12(); }
  else if (callback13 && _code == 13) { if (callback_status) callback_status(0); callback13(); }
  else if (callback14 && _code == 14) { if (callback_status) callback_status(0); callback14(); }
  else if (callback15 && _code == 15) { if (callback_status) callback_status(0); callback15(); }
  else c = 3; //a jesli nie znaleziono obsluzonego callbacka, to zwroc blad nr 3
 } else c = 2; //ilosc odczytanych parametrow nie zgadza sie z zalozeniami - blad nr 2

 if (c == 0) respond(_return_value); //wyslij kod bledu wewnetrznego (lub OK)
 else {
  respond(c); //lub kod bledu
  if (callback_status) callback_status(c); }
}

//oblicza 16-bitowy CRC dla podanego zakresu bufora: <start .. end>
uint16_t crc16(uint8_t start, uint8_t stop) {
 uint16_t crc = 0;
 for (uint8_t i = start; i <= stop; i++) crc = _crc16_update(crc, buffer[i]);
 return crc;
}

//wysyla pojedynczy bajt po USART
void USART_putc(uint8_t data) {
 while (!(UCSRA & (1<<UDRE))); //czekaj na zakonczenie poprzedniej transmisji
 UDR = data; //wyslij
}

//wysyla string po USART
void USART_puts(char* str) {
 uint8_t k = 0;
 while(k < strlen(str)) {
  USART_putc(str[k]);
  k++; }
}



lcd.h
#ifndef LCD_H_
#define LCD_H_

//==================================================
// konfiguracja portow i wlasciwosci wyswietlacza
//==================================================
#define DDR_LCD DDRA
#define PORT_LCD PORTA
#define RS 2
#define EN 3
#define D4 4
#define D5 5
#define D6 6
#define D7 7
#define INIT_N 1  //2 linijki znakow; 0 - 1 linijka znakow
#define INIT_F 0  //znak 5*8px; 1 - znak 5*10px
#define INIT_ID 1 //inkrementacja pozycji kursora; 0 - dekrementacja
#define INIT_S 0  //bez przesuwania w prawo; 1 - z przesuwaniem
#define INIT_D 1  //wlaczenie wyswietlania; 0 - wylaczenie
#define INIT_C 0  //wlaczenie kursora; 0 - wylaczenie
#define INIT_B 0  //kursor miga; 0 - nie miga



//==================================================
//  makra ulatwiajace wysylanie danych
//==================================================

//maski bitow
#define RS_M 1<<RS
#define EN_M 1<<EN
#define D4_M 1<<D4
#define D5_M 1<<D5
#define D6_M 1<<D6
#define D7_M 1<<D7
#define DATA_M (1<<D4)|(1<<D5)|(1<<D6)|(1<<D7)

//ustawianie bitow
#define RS_H PORT_LCD |= RS_M
#define EN_H PORT_LCD |= EN_M
#define D4_H PORT_LCD |= D4_M
#define D5_H PORT_LCD |= D5_M
#define D6_H PORT_LCD |= D6_M
#define D7_H PORT_LCD |= D7_M

//zerowanie bitow
#define RS_L PORT_LCD &= ~(RS_M)
#define EN_L PORT_LCD &= ~(EN_M)
#define D4_L PORT_LCD &= ~(D4_M)
#define D5_L PORT_LCD &= ~(D5_M)
#define D6_L PORT_LCD &= ~(D6_M)
#define D7_L PORT_LCD &= ~(D7_M)



//==================================================
//  definiowanie zmiennych i funkcji
//==================================================

extern volatile uint8_t pos;

void _set_halfbyte(uint8_t);
void send_byte(uint8_t, uint8_t);
void lcd_init(void);
void lcd_clear(void);
void lcd_goto(uint8_t, uint8_t);
void lcd_putc(uint8_t);
void lcd_putint(int16_t);
void lcd_putstr(char*);


#endif /* LCD_H_ */



lcd.c
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#include <stdlib.h>
#include "lcd.h"

volatile uint8_t pos = 0; //pozycja kursora

//ustawia mlodsza polowe bajtu na szynie danych
void _set_halfbyte(uint8_t byte) {
 PORT_LCD &= ~(DATA_M); //wyzeruj wszystkie bity szyny danych, reszte zostaw
 if (byte & 0b00000001) D4_H; else D4_L; //jesli bit ustawiony, to wyslij 1 na odpowiadajacy mu bit szyny
 if (byte & 0b00000010) D5_H; else D5_L;
 if (byte & 0b00000100) D6_H; else D6_L;
 if (byte & 0b00001000) D7_H; else D7_L;
}

//wysyla bajt danych (type=1) lub komende (type=0)
void send_byte(uint8_t byte, uint8_t type) {
 EN_H; //zacznij transmisje
 if (type==0) RS_L; else RS_H; //komenda lub dana

 _set_halfbyte(byte >> 4); //ustaw na szyne starsza czesc bajtu
 EN_L; //wyslij
 asm volatile("nop");
 EN_H;
 _set_halfbyte(byte); //ustaw na szyne mlodsza czesc bajtu
 EN_L; //wyslij

 _delay_us(40); //dychnij sobie chwilke.
}

//inicjuje wyswietlacz ze sterownikiem HD44780
void lcd_init(void) {
 DDR_LCD |= (RS_M)|(EN_M)|(DATA_M); //ustaw kierunek wyjsciowy na pinach RS i EN oraz szynie danych
 PORT_LCD = 0;
 _delay_ms(45); //czekaj na stabilizacje napiecia

 //wyslij trzy razy sekwencje 0011
 for (uint8_t i=0; i<3; i++) {
  EN_H;
  D4_H; //0001
  D5_H; //0011
  EN_L;
  _delay_ms(5); }

 //ustaw interfejs 4-bitowy: 0010
 EN_H;
 D4_L; //0010
 EN_L;

 //ustaw parametry wyswietlacza (szyna czterobitowa, wielkosc znaku i ilosc linijek
 send_byte((0b00100000) | (INIT_N<<3) | (INIT_F<<2), 0);

 //ustaw tryb pracy wyswietlacza (inkr./dekr. adresu zapisu, wl./wyl. przesuwania w prawo)
 send_byte((0b00000100) | (INIT_ID<<1) | (INIT_S), 0);

 //wlacz wyswietlacz (wyswietlanie, widocznosc kursora, miganie kursora)
 send_byte((0b00001000) | (INIT_D<<2) | (INIT_C<<1) | (INIT_B), 0);

 lcd_clear(); //wyczysc wyswietlacz
}

//czysci wyswietlacz
void lcd_clear(void) {
 send_byte(1, 0);
 _delay_ms(1.64); //dychnij sobie chwilke.
 pos = 0;
}

//przenosi kursor na zadana pozycje (lewy gorny rog: 0,0)
void lcd_goto(uint8_t x, uint8_t y) {
 uint8_t newpos = x + 40*y;
 int8_t d = newpos - pos;
 if (d < 0) { //przesun -d razy w lewo
  for(uint8_t x=1; x<=(-d); x++) send_byte(0b00010000, 0);
 }
 else if (d > 0) { //przesun d razy w prawo
  for(uint8_t x=1; x<=d; x++) send_byte(0b00010100, 0);
 }

 pos = newpos;
}

//wysyla pojedynczy znak na aktualna pozycje kursora
void lcd_putc(uint8_t byte) {
 send_byte(byte, 1);
 pos++;
}

//wysyla liczbe na aktualna pozycje kursora
void lcd_putint(int16_t integer) {
 char buffer[6];
 itoa(integer, buffer, 10);
 lcd_putstr((char*)buffer);
}

//wysyla ciag znakow na aktualna pozycje kursora
void lcd_putstr(char* str) {
 uint8_t k = 0;
 while(k < strlen(str)) {
  send_byte(str[k], 1); //wyslij dane
  pos++; //kursor o jeden dalej
  k++;
 }
}

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

8 komentarzy:

  1. jestem pod wrażeniem :-O

    OdpowiedzUsuń
  2. Wspaniały projekt !

    OdpowiedzUsuń
  3. dla formatu A4 lepiej skorzystać z drukarki ;)

    mimo to jestem pod wrażeniem, miałem coś podobnego w planach do rysowania na laminacie

    pozdrawiam

    OdpowiedzUsuń
  4. Czy jest możliwość sterowania ramieniem w funkcji czasu? Gdyby tak, to byłbym zainteresowany kupnem takiej wykreślarki.

    OdpowiedzUsuń
  5. @Piotr B. My to wiemy, ale nauczyciel sprzed pół wieku twierdzi że wykresy trzeba robić ręcznie :).
    @Anonimowy: W teorii byłaby taka możliwość, ale nie planuję wykonywania nowej maszyny ani sprzedaży tej już wykonanej. Każdy może sobie coś takiego zrobić samemu na podstawie dołączonych materiałów i opisu.

    OdpowiedzUsuń
  6. Świetne, tego właśnie szukałem! Dodać tylko obracany nożyk i ploter jak się patrzy:) Dzięki!

    OdpowiedzUsuń
  7. Super!!! Sam bym sobie taki zrobił do wycinania np: naklejek lub wizytówek jednakże od strony elektronicznej czy programowej raczej nie dał bym rady :)

    OdpowiedzUsuń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.