Autor: mopsiok
Redakcja: Dondu
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.
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 ;).
Elementy do budowy |
Oś Y i koszyk na elektromagnes |
Montaż osi X, silnika i przewodów |
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ę).
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 |
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. |
Ukończony ploter
Ukończony ploter |
Narysowany wykres |
Test rysowania PCB |
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++; } }
jestem pod wrażeniem :-O
OdpowiedzUsuńWspaniały projekt !
OdpowiedzUsuńBomba
OdpowiedzUsuńdla formatu A4 lepiej skorzystać z drukarki ;)
OdpowiedzUsuńmimo to jestem pod wrażeniem, miałem coś podobnego w planach do rysowania na laminacie
pozdrawiam
Czy jest możliwość sterowania ramieniem w funkcji czasu? Gdyby tak, to byłbym zainteresowany kupnem takiej wykreślarki.
OdpowiedzUsuń@Piotr B. My to wiemy, ale nauczyciel sprzed pół wieku twierdzi że wykresy trzeba robić ręcznie :).
OdpowiedzUsuń@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.
Świetne, tego właśnie szukałem! Dodać tylko obracany nożyk i ploter jak się patrzy:) Dzięki!
OdpowiedzUsuń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ń