Redakcja i uwagi: Dondu
Jakiś czas temu zacząłem się interesować tematem wyświetlaczy dotykowych. W połączeniu z mikrokontrolerem daje to całkiem sporo możliwości.
Przedstawię i opiszę projekt prostego sterownika gniazdka sieciowego 230VAC (max 4A) sterowanego za pomocą mikrokontrolera ATmega32 wraz z wyświetlaczem graficznym 128x64 (opartego na popularnym sterowniku KS0108) z dołączonym rezystancyjnym panelem dotykowym.
Sterownik prosty, gdyż w czasie przed-świątecznej zawieruchy nie miałem czasu projektu rozwinąć (większa część opisu powstała w pociągu). Jeśli mnie odpowiednio zmotywujecie komentarzami :-) mogę projekt rozwinąć dodając np. układ RTC oraz obsługę wielu gniazdek (jak wspomniałem - możliwości jest sporo).
Zacznijmy od początku ...
Wyświetlacz
Jak wspomniałem w projekcie użyłem wyświetlacza graficznego 128x64 piksele opartego na KS0108 (tak właściwie to na dwóch sterownikach, bo jeden obsługuje tylko 64x64 piksele, ale o tym można poczytać m.in. w książce Tomasza Francuza).
Wyświetlacz graficzny daje nam większe możliwości niż popularne wyświetlacze alfanumeryczne, sterujemy nim "zapalając" odpowiednie piksele. Zadowalająco można na nich przedstawić wykresy, w sieci są również projekty pozwalające wyświetlić grafiki z plików .pgm.
Niestety w sterowniku KS0108 nie mamy standardowo zaimplementowanej obsługi tekstu, więc chcąc takowy wyświetlić, musimy sami zdefiniować funkcję, która nam "włączy" odpowiednie piksele. Oczywiście jak do większości popularnych urządzeń, ktoś już napisał biblioteki upraszczające nam to zadanie.
W sieci krąży sporo bibliotek do sterownika KS0108, ja wykorzystałem bibliotekę napisaną przez Radosława Kwietnia (biblioteka w załączniku projektu). Niestety biblioteki te swoje ważą - stąd mikrokontroler musi dysponować odpowiednią ilością pamięci. Biblioteka pozwala zarówno na wyświetlanie tekstu w łatwy sposób (poprzez tablicę char-ów) jak i rysowanie lini, prostokątów, czy też okręgów.
Wyświetlenie poszczególnych elementów wygląda analogicznie jak w wyświetlaczach alfanumerycznych (m.in. tutaj jest to opisane), oczywiście trzeba pamiętać o "clear-owaniu" całego wyświetlacza (dzięki funkcji GLCD_ClearScreen()) lub danego obszaru, jeśli chcemy np. update-ować dane. Przykładowy program i jego efekt poniżej.
// TPT //Mega32 8MHz + LCD AG-128064A-FHW #include <avr/io.h> #include "KS0108.h" #include "KS0108-AVR.c" #include "graphic.d" int main(void) { // czekamy na inicjalizacje sprzetu for(volatile uint16_t i=0; i<15000; i++); // Imicjujemy LCD GLCD_Initalize(0); // ustawiamy pozycje GLCD_GoTo(15,10); // Wyswietlamy napis GLCD_WriteString("Hello World!!!"); // rysujemy ramke GLCD_Rectangle(5, 5, 100, 20); GLCD_Line(0,60,15,30); GLCD_Line(15,30,30,60); GLCD_Line(30,60,45,30); GLCD_Line(45,30,60,60); GLCD_Line(60,60,75,30); GLCD_Line(75,30,90,60); GLCD_Line(90,60,105,30); GLCD_Line(105,30,120,60); while(1) { } }
Efekt działania programu:
Wyświetlacz graficzny z KS108 |
Warto również dodać, że w docelowych aplikacjach dobrze jest zastosować kwarc o większej niż 8MHz wartości (aby wyświetlacz "rysował" grafiki szybciej), ja w docelowej aplikacji wykorzystałem kwarc 16MHz co dało zadowalający rezultat,
TOUCH PANEL
Technik detekcji dotyku oraz położenia punktu przyłożenia jest sporo. Ze względu na cenę oraz dostępność dla "zwykłego hobbysty" ja używam rezystancyjnego panela dotykowego. Produkowane są one w przeróżnych rozmiarach (nawet do 15"), w popularnych sklepach elektronicznych spokojnie znajdziemy panel pasujący do wyświetlaczy 128x64 (ceny od 15zł - stan na grudzień 2013).
Panele rezystancyjne składają się z kilku warstw, z których najważniejsze są dwie przewodzące oraz jedna (o strukturze siatki) pełniąca rolę dystansu pomiędzy nimi. Ścieżki przewodzące naniesione na obu warstwach krzyżują się, ale nie są ze sobą zwarte, ponieważ pomiędzy nimi jest umieszczona warstwa dystansująca. Napięcie zasilające jest przykładane do jednej z warstw i mierzone na drugiej.
Na skutek docisku warstwy zwierają się i w ten sposób powstaje para rezystancyjnych dzielników napięcia o napięciu wyjściowym charakterystycznym dla danego miejsca o współrzędnych X, Y na panelu. Obsługa takiego panelu sprowadza się do pomiaru dwóch napięć tego dzielnika za pomocą ADC i określaniu na tej podstawie położenia punktu nacisku.
Ze względu na wspomnianą budowę panela rezystancyjnego nie da się obsługiwać lekkimi naciśnięciami, jak np. w nowoczesnych telefonach komórkowych (ale przynajmniej mamy tani panel ;)). Nie są one również wybitnie trwałe (osobiście udało mi się zniszczyć jeden taki panel przez zbyt mocne naciśnięcie), siłę dotyku trzeba "wyczuć", szczególnie należy uważać w rogach panela.
Najczęściej spotykane są panele z czterema wyprowadzeniami.
Schemat budowy touch panela rezystancyjnego |
W praktyce oczywiście musimy również odczytać kiedy tak właściwie dotykamy panela, stąd funkcja check touch w poniższym kodzie.
Bardzo fajny artykuł na temat paneli dotykowych znajdziesz W Elektronice Praktycznej 10/2009:
"Obsługa rezystancyjnych paneli dotykowych w systemach mikroprocesorowych" (kopia)
Do wyliczenia aktualnej pozycji wskaźnika (palca lub innego obiektu), musimy oczywiście nasz układ odpowiednio skalibrować, tj. dając mu stałe (Xmin, Xmax, Ymin, Ymax) niezbędne do określenia, że przykładając wskaźnik w lewym górnym rogu wyświetlacza mamy pozycję X=0 oraz Y=0.
Istnieją specjalne układy (AR1020 lub STMPE811), które wspierają obsługę paneli, można też ustawić na sztywno wartości tych stałych. Jednak jako, że lubię sobie utrudniać zadanie, sam stworzyłem funkcję, dzięki której użytkownik sam dokonuje kalibracji i jednocześnie zapisuje stałe do EEPROMu. Funkcja uwzględnia również inne przyłożenie panela (np. do góry nogami - odpowiadają za to zmienne sideH i sideV):
// TPT 2013 // Mega32 16MHz + LCD AG-128064A-FHW + LCD-AG-TP-128064M-CF // // www: mech4fun2me.blogspot.com // //#define F_CPU 16000000UL <--- Zdefiniuj w opcjach projektu #include <stdio.h> #include <inttypes.h> #include <avr/io.h> #include <avr/pgmspace.h> #include <avr/interrupt.h> #include <avr/eeprom.h> #include "graphic.h" #include "font5x8.h" #include "KS0108.h" #include "KS108-AVR.c" #include "menu.h" //########### ADC Touch Panela #define ADC0 PA0 #define ADC1 PA1 #define ADC2 PA2 #define ADC3 PA3 //############ Wyjscie #define POWER PA4 #define DISPLAY PA6 #define RESETEEPROM PA7 volatile unsigned int touch=0, settings=0,display=1; volatile unsigned int ADC_value=0, ADC_in=0; volatile unsigned int ADC0_value=0, ADC1_value=0, ADC2_value=0, ADC3_value=1044; volatile unsigned int cnt=0, cnt2=0, cnt3=0, cnt4=0, display_cnt=0; volatile unsigned int m=0, n=1; // flaga menu volatile unsigned int i=1, j=0, k=0,flagP;//flagi volatile unsigned int X=0, Y=0, Xmax=0, Xmin=0, Ymax=0, Ymin=0; //dla wyswietlacza serii M:Xmax=920, Xmin=120, Ymax=847, Ymin=255; volatile unsigned int Xtmp, Ytmp, Xadc, Yadc; //dla wyswietlacza serii M: sideV=1, sideH=0; volatile unsigned int sideV=0, sideH=0; EEMEM uint16_t eXmax, eYmax, eXmin, eYmin, esideV, esideH, use=0; volatile unsigned int Power=0; EEMEM uint16_t ePower; //struktura flag menu struct MenuFlag mflag; //------------------------------------- // I/O start settings //------------------------------------- void io_init(void) { DDRA = 0b01010000; PORTA |= (1 << RESETEEPROM);//PULL-up dla resetu eepromu } //------------------------------------- // Get ADC conversion value //------------------------------------- int getADC(unsigned char channel) { unsigned int W = 0; ADMUX = (ADMUX & 0xE0) + channel; ADCSRA |= (1 << ADSC); while(ADCSRA & (1 << ADSC)); ADCSRA |= (1 << ADSC); W = ADCL; W |= (ADCH << 8); return W; } //------------------------------------- // ADC Initialization //------------------------------------- void ADC_Init(void) { ADMUX = (1 << REFS0); ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); } //------------------------------------- // ADC Disable //------------------------------------- void ADC_Disable(void) { ADCSRA &= ~((1 << ADEN) | (1 << ADIE)); } //------------------------------------- // TIMER Init //------------------------------------- void timer_init(void) { //########### Wlaczenie timerow 0 i 2 TIMSK |= (1<<TOIE0); //########### TIMER 0 TCCR0 |= (1<<CS01);//preskaler 8 } //------------------------------------- // Show position //------------------------------------- void show_position(int x, int y) { if (touch == 1) { GLCD_DrawCircle(Xtmp,Ytmp,10,Bi); GLCD_DrawCircle(x,y,10,Cz); flagP; } if (touch == 0 && flagP == 1 && (Xtmp == x || Ytmp == y)) { flagP=0; GLCD_DrawCircle(x,y,10,Bi); } Xtmp=x; Ytmp=y; } //------------------------------------- // Check touch //------------------------------------- void check_touch(void) { int tmp; DDRA = 0b00000110; PORTA |= (1 << ADC1); PORTA &= ~(1 << ADC0); PORTA &= ~(1 << ADC2); PORTA &= ~(1 << ADC3); tmp=ADC3_value/(ADC0_value+1); if (tmp < 10) // Rtmax=5 -> wyznaczone empirycznie touch = 1; else touch = 0; } //------------------------------------- // Get position X //------------------------------------- void get_positionX(void) { int tmp, resolution; DDRA = 0b00000101; //########## wyznaczanie X PORTA &= ~(1 << ADC2); PORTA &= ~(1 << ADC3); PORTA &= ~(1 << ADC1); PORTA |= (1 << ADC0); resolution=(Xmax-Xmin); resolution=resolution/128; Xadc = ADC1_value;//potrzebne przy kalibracji X=(ADC1_value-Xmin)/resolution; if (sideH == 1)//w zaleznosci od zamocowania touch panela X=127-X; if (X > 190)//zabezpieczenie przed niedopasowaniem panela X=0; if(X > 128 && X < 190) X=127; } //------------------------------------- // Get position Y //------------------------------------- void get_positionY(void) { int tmp, resolution; DDRA = 0b00001010; //######## wyznaczanie Y PORTA &= ~(1 << ADC0); PORTA &= ~(1 << ADC2); PORTA &= ~(1 << ADC3); PORTA |= (1 << ADC1); resolution=(Ymax-Ymin)/64; resolution=resolution+1;//korekcja - panel jest wyrzszy niz szerszy stad problem Yadc = ADC0_value;//potrzebne przy kalibracji Y=(ADC0_value-Ymin)/resolution; if (sideV == 1)//w zależności od zamocowania touch panela Y=63-Y; if(Y < 0 )//zabezpieczenie przed niedopasowaniem panela Y=0; if(Y > 63) Y=64; } //------------------------------------- // Read settings //------------------------------------- int read_settings(void) { Xmax=eeprom_read_word(&eXmax); Xmin=eeprom_read_word(&eXmin); Ymax=eeprom_read_word(&eYmax); Ymin=eeprom_read_word(&eYmin); sideV=eeprom_read_word(&esideV); sideH=eeprom_read_word(&esideH); Power=eeprom_read_word(&ePower); if (((Xmax < 0) && (Xmin < 0))||((Ymax < 0) && (Ymin < 0))||((Xmax > 1024) && (Xmin > 1024))||((Ymax > 1024) && (Ymin > 1024))) return 0; else return 1; } //------------------------------------- // Reset eeprom //------------------------------------- void reset_eeprom(void) { int xmin=3000,xmax=3000,ymin=3000,ymax=3000,sidev=0,sideh=0; eeprom_write_word(&eXmax, xmax); eeprom_write_word(&eXmin, xmin); eeprom_write_word(&eYmax, ymax); eeprom_write_word(&eYmin, ymin); eeprom_write_word(&esideV, sidev); eeprom_write_word(&esideH, sidev); GLCD_ClearScreen(); GLCD_GoTo(10,25); GLCD_WriteString("RESET"); settings = 0; } //------------------------------------- // Calibration //------------------------------------- void calibration(void) { int xmin[2],xmax[2],ymin[2],ymax[2],sidev,sideh, u; int fSettings; m=7; GLCD_ClearScreen(); GLCD_GoTo(10,25); GLCD_WriteString("Wskaz punkt"); //############## Lewy gorny for( int l =0; l<20;l++) { for(int m=0;m<20;m++) lcdSetPixel(l,m); } while ( touch == 0); xmin[0] = Xadc; ymin[0]= Yadc; for( int l =0; l<20;l++) { for(int m=0;m<20;m++) lcdClrPixel(l,m); } //############## Prawy gorny for( int l =107; l<127;l++) { for(int m=0;m<20;m++) lcdSetPixel(l,m); } while ( touch == 0); xmax[0] = Xadc; ymin[1]= Yadc; for( int l =107; l<127;l++) { for(int m=0;m<20;m++) lcdClrPixel(l,m); } //############ Lewy dol for( int l =0; l<20;l++) { for(int m=45;m<64;m++) lcdSetPixel(l,m); } while ( touch == 0); xmin[1] = Xadc; ymax[0]= Yadc; for( int l =0; l<20;l++) { for(int m=45;m<64;m++) lcdClrPixel(l,m); } //############# Prawy dol for( int l =107; l<127;l++) { for(int m=45;m<64;m++) lcdSetPixel(l,m); } while ( touch == 0); xmax[1] = Xadc; ymax[1]= Yadc; for( int l =107; l<127;l++) { for(int m=45;m<64;m++) lcdClrPixel(l,m); } xmax[2]=(xmax[1]+xmax[0])/2; xmin[2]=(xmin[0]+xmin[1])/2; ymax[2]=(ymax[0]+ymax[1])/2; ymin[2]=(ymin[0]+ymin[1])/2; if(xmax[2] < xmin[2]) { sideH = 1; Xmax = xmin[2]; Xmin = xmax[2]; } else { sideH = 0; Xmax = xmax[2]; Xmin = xmin[2]; } if(ymax[2] < ymin[2]) { sideV = 1; Ymax = ymin[2]; Ymin = ymax[2]; } else { sideV = 0; Ymax = ymax[2]; Ymin = ymin[2]; } eeprom_write_word(&eXmax, Xmax); eeprom_write_word(&eXmin, Xmin); eeprom_write_word(&eYmax, Ymax); eeprom_write_word(&eYmin, Ymin); eeprom_write_word(&esideV, sideV); eeprom_write_word(&esideH, sideH); u = eeprom_read_word(&use); if ( u < 0 ) u=0; u++; eeprom_write_word(&use, u); GLCD_ClearScreen(); fSettings = read_settings(); if (fSettings == 0) { GLCD_GoTo(10,25); GLCD_WriteString("Blad!!!"); reset_eeprom(); } if(fSettings == 1) { GLCD_GoTo(10,25); GLCD_WriteString("Dane zapisane"); GLCD_GoTo(50,45); GLCD_WriteString("OK"); while ( touch == 0); } GLCD_ClearScreen(); settings = read_settings(); m=0; } //------------------------------------- // MAIN //------------------------------------- int main(void) { for(volatile uint16_t h=0; h<15000; h++); GLCD_Initalize(); GLCD_ClearScreen(); timer_init(); io_init(); sei(); settings = read_settings(); if (settings == 0) calibration(); while(1) { //############### MENU if ( settings == 1 ) { //------------------------------------- // Main Menu //------------------------------------- if (m == 0) { if ( n != m) { GLCD_ClearScreen(); n=0; } MENU_MainMenu(Power, &mflag); if (touch == 1 && display == 1 && (X >= 0 && X <= 80) && (Y >= 0 && Y <= 35)) m=3; } //------------------------------------- // Confirm //------------------------------------- if (m == 3) { if ( n != m ) { GLCD_ClearScreen(); n=3; } MENU_Confirm(); if (touch == 1 && (X >= 0 && X <= 60) && (Y >= 35 && Y <= 63)) { if ( Power == 0) { Power = 1; PORTA |= (1 << POWER); } else { Power = 0; PORTA &= ~(1 << POWER); } eeprom_write_word(&ePower, Power); m = 0; } if (touch == 1 && (X >= 70 && X <= 127) && (Y >= 35 && Y <= 63)) { m = 0; } } } if( settings == 0) calibration(); } } //------------------------------------- // Timer0 overflow- przerwanie timera0 //------------------------------------- ISR(TIMER0_OVF_vect)//przerwanie po przepelnieniu timera { //------------------------------------- // Odczyt pozycji i dotyku //------------------------------------- cnt2++; if(cnt2 == 1) { DDRA = 0b00000110; PORTA |= (1 << ADC1); PORTA &= ~(1 << ADC0); PORTA &= ~(1 << ADC2); PORTA &= ~(1 << ADC3); } if(cnt2 == 2) { ADC_Init(); ADC0_value = getADC(0); ADC_Disable(); } if (cnt2 == 3) { ADC_Init(); ADC3_value = getADC(3); ADC_Disable(); } if (cnt2 == 4) check_touch(); if (cnt2 == 5) { DDRA = 0b00000101; PORTA &= ~(1 << ADC2); PORTA &= ~(1 << ADC3); PORTA &= ~(1 << ADC1); PORTA |= (1 << ADC0); ADC_Init(); ADC1_value=getADC(1); ADC_Disable(); } if (cnt2 == 6) { get_positionX(); } if (cnt2 == 7) { DDRA = 0b00001010; PORTA &= ~(1 << ADC0); PORTA &= ~(1 << ADC2); PORTA &= ~(1 << ADC3); PORTA |= (1 << ADC1); ADC_Init(); ADC0_value = getADC(0); ADC_Disable(); } if(cnt2 == 8) get_positionY(); if(cnt2 == 9) { cnt2=0; } //------------------------------------- // Komunikat o wcisnieciu- dziala w tescie ADC lub kalibracji //------------------------------------- if (settings == 0) { if (touch == 1) { if(i==0) { clear_area(10,120,0,20); GLCD_GotoXY(20,0); GLCD_Puts_P(PSTR("OK!!!")); i=1; } } if (touch == 0) { if (i == 1) { clear_area(20,120,0,20); GLCD_GoTo(20,0); GLCD_WriteString("Touch me!!!"); i=0; } } } //------------------------------------- // Opcje podswietlenia //------------------------------------- if (m == 7) { PORTA |= (1 << DISPLAY); display = 0; display_cnt = 0; } if (touch == 1 && display == 0) { PORTA |= (1 << DISPLAY); display = 1; } if (touch == 1 && display == 1) display_cnt=0; if (display == 1 && m != 7) { PORTA |= (1 << DISPLAY); display_cnt++; if (display_cnt == 64000) //czas podświetlenia { display = 0; display_cnt = 0; PORTA &= ~(1 << DISPLAY); } } //######### RESET EEPROM int oldEepromReset = 1; if((PINA & (1 << RESETEEPROM))^oldEepromReset &&(PINA & (1 << RESETEEPROM)) == 0) { reset_eeprom(); settings = 0; calibration(); } }
po połączeniu:
Sterowanie gniazdkiem 230V
Na wstępie pragnę zalecić maksymalną ostrożność:
Pomimo licznych zabezpieczeń stosowanych w sieciach domowych radzę zachować pełne skupienie. Nie radzę układu do sterowania napięciem 230VAC budować na płytkach stykowych (wprawdzie nie jest to dla nich specjalnych problemem jak wynika z doświadczeń użytkowników elektroda.pl).
Dodane przez Dondu:
Projekt przedstawiony przez autora w niniejszym artykule dotyczy napięć, które przekraczają wartość napięcia bezpiecznego. Aby projekt realizować powinieneś mieć niezbędną wiedzę i uprawnienia.
Dodane przez Dondu:
Projekt przedstawiony przez autora w niniejszym artykule dotyczy napięć, które przekraczają wartość napięcia bezpiecznego. Aby projekt realizować powinieneś mieć niezbędną wiedzę i uprawnienia.
Do testów proponuję zbudować układ ze stabilnym gniazdkiem jaki np. zrobiłem ja:
Sterownik 230VAC |
Uczuliłem Was na zachowanie bezpieczeństwa, więc teraz mogę opowiedzieć jak sterować zasilaniem sieciowym: ja do tego celu wykorzystałem triak BT136-600E (max 4A) oraz optotriak MOC3041 :
230VAC driver |
230VAC driver - płytka PCB |
Do pobrania schemat i płytka Eagle: Sterownik-230V.zip (kopia)
Ponieważ maksymalne obciążenie pinu mikrokontrolera wynosi 20mA, zasilenie diody optotriaka zrealizowałem przez tranzystor NPN.
Do końcowego projektu dorzuciłem zapamiętywanie ostatniego stanu, jak i sterowanie podświetleniem wyświetlacza (gaśnie po określonym czasie od ostatniego dotyku). Dzięki synergicznemu połączeniu wyżej wymienionych elementów otrzymałem taki efekt:
Schemat projektu |
Do pobrania
Program, makefile i biblioteki: sterownik-gniazdka-230VAC.rar (kopia)
Na wszelkie pytania odpowiem w komentarzach.
Pozdrawiam i życzę wszystkiego dobrego w Nowym 2014 Roku!!! ;)
Tomek
Uwagi redakcji
Projekt, który nieco odbiega od pozostałych projektów konkursowych. Jednakże zawiera dość sporo istotnych elementów, przez co w naszej ocenie sklasyfikowany został nieco wyżej od opinii głosujących czytelników.
Przede wszystkim na uwagę zasługuje obsługa panelu dotykowego przy pomocy wbudowanego w mikrokontroler przetwornika ADC. Mamy jednak uwagi - dioda optotriaka sterującego triakiem nie ma rezystora, na schemacie też go nie widać. Tu znowu warto zwrócić uwagę, że LED nigdy nie należy łączyć bez rezystora, nawet jeśli jego wartość byłaby mała.
Drugi problem związany jest z triakiem. Różnego typu obciążenia powodują przesunięcie w fazy napięcia względem prądu, w efekcie pojawia się problem z wyzwalaniem triaka. Szczególnym problemem są obciążenia indukcyjne. Stąd też producenci optotriaków proponują różne schematy łączenia ich z triakami zapewniającymi poprawne wyzwalanie dla różnego charakteru obciążeń. Zazwyczaj bezpośrednie połączenie optotriaka z triakiem nie jest zalecane.
zupełnie fajne przeniesienie istniejących (lub podobnych) układów na ATMEL'a :) gratuluję zaangażowania :) - ode mnie 10 pkt za zaangażowanie
OdpowiedzUsuńEkran dotykowy do sterowania gniazdkiem!? Zdecydowanie zbędna zabawka.
OdpowiedzUsuńTak myśląc można także twierdzić, że zbędne są wielkie ekrany LCD w telefonach ... artykuł należy traktować jako przykład, który można dowolnie modyfikować.
UsuńAle można by było zrobić jakąś prostą grę np. kółko i krzyżyk czy pong. Pracy niewiele więcej a efekt zdecydowanie lepszy.
UsuńTo trzeba było zgłosić taki projekt, ale pewnie znalazłby się wtedy jakiś maruda, który twierdziłby, że marnujesz wyświetlacz, bo trzeba było zrobić dotykowy sterownik 230V, albo przyrząd do pomiaru ciśnienia krwi.
UsuńKorzystaj z artykułu lub nie ... albo wnieś coś konstruktywnego i przedstaw swój projekt :-)
Odpowiedź na tę wątpliwość udzieliłem na samym początku artykułu:
Usuń"Sterownik prosty, gdyż w czasie przed-świątecznej zawieruchy nie miałem czasu projektu rozwinąć (większa część opisu powstała w pociągu). Jeśli mnie odpowiednio zmotywujecie komentarzami :-) mogę projekt rozwinąć dodając np. układ RTC oraz obsługę wielu gniazdek (jak wspomniałem - możliwości jest sporo)."
Zresztą nawet w tytule jest zawarte słowo "prosty" ;) projekt jest bardziej bazą pod coś konkretniejszego- co już tam komu się wymyśli.
Pozdrawiam
Program musi być nieźle skopany, skoro tak mułowato wszysko jest odswieżane. Płynnie powinno dzialać już na 1MHz.
OdpowiedzUsuń