Sprawozdanie z wykonania projektu Dalmierz optyczny
Transkrypt
Sprawozdanie z wykonania projektu Dalmierz optyczny
Sprawozdanie z wykonania projektu Dalmierz optyczny Anna Sadowska 133152 19 czerwca 2007 ∗ Wizualizacja c danych sensorycznych Anna Sadowska 1 ∗ 1 Zadanie do wykonania Jako zadanie projektowe miałam wykonać dalmierz optyczny z wykorzystaniem czujnika typu PSD. Miał on się komunikować z komputerem PC poprzez złącze RS232 oraz zapewniać wizualizację paskiem diód LED. Przewidywane było kilka trybów pracy: pomiar ciągły wyzwalany przyciskiem na obudowie bez komunikacji, pomiar pojedynczy wyzwalany przyciskiem na obudowie, pomiar na żądanie z komputera PC. Dodatkowo możliwe miało być wyłączenie wizualizacji realizowanej za pomocą diód LED. Do obsługi dalmierza od strony PC miałam stworzyć prostą aplikację w Qt. 2 Wykonany sprzęt 2.1 Mikrokontroler Do wykonania mojego zadania projektowego wybrałam mikrokontroler firmy Freescale - 16-bitowy MC9S12A64CFUE. Oto jego skrócona specyfikacja techniczna: • zgodność z listą rozkazów M68HC11, • model programowy analogiczny jak w M68HC11, • kolejka instrukcji, • rozbudowane tryby adresowania indeksowego, • 512K bajtów Flash EEPROM, • 4.0K bajtów EEPROM, • 14.0K bajtów RAM, • dwa 10-kanałowe przetworniki analogowo-cyfrowe, • interfejsy szeregowe, – dwa asynchroniczne interfejsy szeregowe SCI, – synchroniczny interfejs szeregowy urządzeń zewnętrznych SPI, – interfejs zgodny z magistralą I 2 C, • 61 uniwersalnych wejść-wyjść binarnych: – 51 dwukierunkowych: – 10 wyłaącznie wejściowych, • interfejs uruchomieniowy (wbudowany emulator) BDM (Background Debug Mode) z pułapkami sprzętowymi, • wbudowane instrukcje Fuzzy Logic, • obudowa QFP80. 2 Rysunek 1: Uklad pinów na 80-pinowej QFP MC9S12A64. 3 Rysunek 2: Struktura4i zasoby MC9S12A64. Po przylutowaniu wszystkich elementów do modułu z mikrokontrolerem, wygląda on następująco: Rysunek 3: Wygląd zmontowanego modułu. 2.2 Płytka drukowana Moduł z mikrokontrolerem został poźniej wmontowany wraz z innymi elementami do zaprojektowanej przeze mnie płytki drukowanej, która stanowi podstawę budowy dalmierza. Do konstrukcji dalmierza optycznego zaprojektowałam dwie płytki: jedna stanowi bazę całej konstrukcji, na drugiej zaś umieścilam elementy, które powinny wystawać ponad obudowę dalmierza (przyciski, diody). Poniżej przedtawiam schematy oraz układ ścieżek zaprojektowanych przeze mnie płytek. Rysunek 4: Schemat połączeń na płytce z diodami i przyciskami. 5 6 Rysunek 6: Schemat do wydruku płytki głównej układu. Rysunek 7: Schemat do wydruku płytki z przyciskami i diodami. 7 Rysunek 8: Schemat wyprowadzeń czujnika. Rysunek 9: Zależność napięcia na wyjściu czujnika od mierzonej odległości. Rysunek 10: Wewnętrzna struktura czujnika. 2.3 2.3.1 Pozostałe element czujnik GP2Y0A02YK firmy Sharp Czujnik ten działa na zasadzie trialangulacyjnej, to znaczy mierzy odległość między punktem, gdzie wiązka światła została wyslana, a punktem odbioru. Odległość od mierzonego elementu jest proporcjonalna do wzajemnej odległości tych dwóch punktów. Na wyjściu czujnik daje napięcie analogowe, które jest zależne od zmierzonej odległości. Rysunki 7, 8 i 9 przedstawiają charakterystykę wyjściową czujnika, jego strukturę i schemat. 8 2.3.2 Stabilizator napięcia ST7805 Do stabilizacji napięcia wejściowego do układu użyłam popularnego stabilizatora 7805 produkcji STMicroelectronics. Poniżej przedstawiam układ nóżek stabilizatora i jego budowę. Rysunek 11: Schemat do wydruku płytki głownej (rysunek po lewej) i płytki z przyciskami (po prawej). 2.3.3 ST232 Do komunikacji z kompuerem PC poprzez złącze RS232 użyłam ST232 firmy STMicroelectronics. Jego specyfikację przedstawiam na rysunkach 12 i 13. 2.3.4 linijka diodowa Do wizualizacji pomiarów użyłam linijki diodowej. Jej funkcja polega na że im dalej znajduje się przedmiot, którego odległość mierzymy, tym więcej diód się pali. 2.3.5 mostek Gretza W układzie zastosowałam mostek Gretza przy zasilaniu. Dzięki temu, możliwe jest stosowanie zasilacza z plusem i minusem w środku. 2.3.6 zasilacz Układ, ktory zmontowałam testowałam z zasilaczem 9V, 500mA firmy Tatarek. Jest to zasilacz z minusem w środku, ale dzięki zastosowaniu mostku Gretza, można użyć także zasilacza z plusem w środku. 2.3.7 Dodatkowo do montażu użyłam następujących elementów: • diody led, • rezystory ograniczające prąd, • kondensatory 9 Rysunek 12: Układ pinów w ST232. Rysunek 13: Opis pinów. 10 Rysunek 14: Schemat użytej linijki diodowej. 11 Rysunek 15: Mostek Gretza. 12 3 Programowanie mikrokontrolera Aby zmontowane urządzenie mogło poprawnie działać, musiałam napisać program pod mikrokontroler w języku C. Jego działanie jest bardzo proste. Napięcie wjściowe czujnika ciągle idzie na odpowiednią nóżkę mikrokontrolera. Gdy zostanie naciśniety przycisk oznaczający żądanie pomiaru, program odczytuje wartość z odpowiedniego portu i po obsłudze tej wartości (między innymi przekonwertowanie wartości [0...140] na konkretna wartość odległości zgodnie z charakterystyką czujnika oraz włączenie linijki diodowej). Z poziomu PC można także wywołać pomiar poprzez wysłanie do mikroprocesora małej literki “p”. Mikrokontroler w odpowiedzi na ten sygnał prześle bieżącą wartość odległości. Transmisja danych odbywa się z następującymi parametrami: prędkość - 9600, brak bitu parzystości, 1 bit stopu i 8 bitów danych. Pełny kod źródłowy tego programu znajduje się poniżej. #include #include #include #include "main.h" "sci.h" "init.h" <ctype.h> #define INP_NBR 4 #define IloscPomiarow 15 //zakres dziele //na tyle rownych odcinkow, ile wskazuje IloscPomiarow int ZaleznoscOdleglWskazaniaDiody [4][IloscPomiarow] = {{140,130,102,79,63,56,46,41,36,31,26,23,20}, //progi {15,20,30,40,50,60,70,80,90,100,110,120,130,140,150}, //zmierz.odl. {0xFE,0xFE,0xFC,0xFC,0xF8,0xF0,0xE0,0xC0,0xC0,0x80, 0x80,0x00,0x00,0x00,0x00}, //co ma byc wpisane do porta {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFE,0xFE,0xFC,0xFC}}; //co ma byc wpisane do portb typedef struct { //struktura ma pola, do ktorych wpiszemy odpowiednia liczbe, jaka ma byc wpisana //do porta aby odpowiednia ilosc diod sie zapalila; dla b-analogicznie int ile_a; int ile_b; }Ile; void WlaczDiody(Ile IleZapalic) { //funkcja wlacza tyle diod, ile wskazuje pomiar // wpis danych zapalajacych diody Regs.porta.byte = IleZapalic.ile_a; Regs.portb.byte = IleZapalic.ile_b; } Ile SkalowanieMierzeniaDlaDiod(int WartoscPomiaru) { //WartPom-wartosc zmierzona z czujnika to liczba [0,155] 13 //trzeba to przeskalowac zeby diody odpowiednio swiecily Ile wynik; int i; for (i=0;i<IloscPomiarow;i++) if (WartoscPomiaru>=ZaleznoscOdleglWskazaniaDiody[0][i]) { wynik.ile_a = ZaleznoscOdleglWskazaniaDiody[2][i]; wynik.ile_b= ZaleznoscOdleglWskazaniaDiody[3][i]; break; } return wynik; } int PodajOdleglosc(int WartoscPomiaru) { //funkcja na podstawie zmierzonej wartosci podaje, jaka jest //odleglosc zmierzona przez czujnik //WartoscPomiaru to bezposrednio zmierzona liczba do zamiany na odl. int wynik=0,i; for (i=0;i<IloscPomiarow;i++) if (WartoscPomiaru >= ZaleznoscOdleglWskazaniaDiody[0][i]) { wynik = ZaleznoscOdleglWskazaniaDiody[1][i]; break; } return wynik; } //do obslugi portu szeregowego void InitSCI1(void) { Sci1.scibd.byte.lsb.byte =(unsigned char)(((8000000UL*3+4800)600+8)6 ); Sci1.scicr2.byte = TE | RE; } void PutCSCI1(const char c){ while (!Sci1.scisr1.bit.tdre); // wait for output buffer empty Sci1.scidrl.byte = c; } void PutSSCI1(const char *text){ while (*text != ’\0’){ PutCSCI1(*text++); } } int GetCSCI1(void){ char s, c; if (0 == (s = Sci1.scisr1.byte & (PF | FE | NF | ORF | RDRF))) return (0x8000); c = Sci1.scidrl.byte; if(s & (PF | FE | NF | ORF)) return (0x8000 + s); else return ((int) c ); 14 } //koniec funkcji do obslugi portu szeregowego void main () { int TrybPomiaruSprz; //tryb wyzwalany sprzetowo przez przycisk char TrybPomiaruApl; //tryb pomiaru przekazywany z aplikacji, =’p’ //gdy ma byc wykonany pomiar unsigned int ZmierzonaOdl; int tmp,i=0; Ile IleDiodZapalic; int WynikPomiaruCzujnika; //to co odczytamy z czujnika EnableInterrupts; // initialize CPU clock (3x8MHz) Crg.clksel.bit.pllsel = 0; //disengage PLL to system Crg.pllctl.bit.pllon = 1; //turn on PLL Crg.synr.byte = initSYNR; //set PLL multiplier Crg.refdv.byte = initREFDV; //set PLL divider asm { nop; // a short delay nop; } while (!(Crg.crgflg.bit.lock==1)){} // wait for PLL lock Crg.clksel.bit.pllsel = 1; //engage PLL to system InitSCI1(); //inicjalizacja polaczenia przez port szeregowy Atd0.atdctl2.byte = ADPU | AFFC; Atd0.atdctl5.byte = MULT | SCAN; Atd0.atdctl3.bit.slc = INP_NBR; ustawienie kierunku * Regs.ddrb.byte = 0xFF; Pim.ddrt.byte = 0x00; //atd enable and fast flag clear all //atd multichannel continuous scan //conversion sequence length Regs.ddra.byte = 0xFF; //piny wejsciowe // wylaczenie diod Regs.porta.byte = 0xFF; Regs.portb.byte = 0xFF; 15 //piny wyjsciowe for(;;) { TrybPomiaruSprz = Pim.ptit.byte; if(0 < (tmp = GetCSCI1())) //ustalanie trybu pomiaru z aplikacji TrybPomiaruApl = (char) (tmp & 0xFF); if ((TrybPomiaruSprz == 0x08)|| //pomiar pojed (TrybPomiaruSprz == 0x20)|| // pomiar ciagly (TrybPomiaruApl == ’p’)) { //pomiar z apl. WynikPomiaruCzujnika = Atd0.atddr[3].d8.datah; //pomiar z 4.kanalu //zapal diody IleDiodZapalic = SkalowanieMierzeniaDlaDiod(WynikPomiaruCzujnika); WlaczDiody(IleDiodZapalic); //zamien odczyt na odleglosc ZmierzonaOdl = PodajOdleglosc(WynikPomiaruCzujnika); //wyslij wartosc odleglosci przez port szeregowy PutCSCI1((char)(ZmierzonaOdl&0xFF)); } TrybPomiaruApl = 0; //zerujemy przed kolejnym pomiarem } } 4 Aplikacja w Qt W mikrokontrolerze zaimplementowałam funkcję komunikacji z komputerem PC poprzez złącze RS232. Aby móc obserwować wyniki otrzymane z dalmierza, napisałam aplikację w Qt pod Linuksem. Jest to bardzo prosta aplikacja. Poniżej przedstawam okno aplikacji. Rysunek 16: Widok aplikacji do wizualizacji pomiarów z dalmierza 16 Do działania aplikacji potrzebne jest zaledwie kilka guzików: • Pomiar pojedynczy - wywolłanie pomiaru pojedynczego, • Pomiar ciągły - wywołanie pomiaru ciągłego, • Koniec pomiaru ciągłego - zakończenie pomiaru ciągłego, • Zamknij - zamnknięcie okna aplikacji. Kod źródłowy aplikacji przedstawiam poniżej. • Plik dalmierzoptyczny.hpp #ifndef DALMIERZ_OPTYCZNY_HPP #define DALMIERZ_OPTYCZNY_HPP #ifdef __GNUG__ #pragma implementation #pragma interface #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <QApplication> <QWidget> <QGridLayout> <QMainWindow> <QPushButton> <QSpinBox> <QStatusBar> <QString> <QLabel> <QLCDNumber> <QProgressBar> <QStyle> <QMessageBox> <QCloseEvent> <QBrush> <QTimer> <QMenuBar> //klasa zawiera wszyskie guziki aplikacji i QLCDNumbed i QProgressBar class Kanwa: public QWidget { //-----------------------------------------Q_OBJECT public: Kanwa(QWidget *wRodzic = 0L); signals: void ZglosZamkniecie(); void ZglosPomiarPojed(); void ZglosPomiarCiagly(); 17 void KoniecCiaglego(); void ZglosNapis(); void UstawLiczbeNaWyswietlaczu(int Liczba); private: QGridLayout *wOrganizer; QPushButton *wPrzyciskZamknij; QProgressBar *wBelkaPostepu; QPushButton *wPrzyciskPomiarPojed; QPushButton *wPrzyciskPomiarCiagl; QPushButton *wPrzyciskKoniecCiagl; QLCDNumber *wLCD1; }; //---------------------------------------------------------------------//okno glowne aplikacji class OknoGlowne: public QMainWindow { //---------------------------------Q_OBJECT public: OknoGlowne(QWidget *wRodzic = 0L); QTimer *timer; virtual void closeEvent( QCloseEvent * event ); bool CzyMoznaZamknac(); void WypiszKomunikat(); signals: void WyslijLiczbeNaWyswietlacz(int Liczba); void BladPomiaru(); public void void void void void void slots: GdyZamkniecie(); WykonajPomiarPojed(); WykonajPomiarCiagly(); ZakonczPomiarCiagly(); UstawNapisStatusu(); UsunNapisStatusu(); }; //---------------------------------------------------------------------#endif • Plik dalmierzoptyczny.cpp 18 #ifdef __GNUG__ #pragma implementation #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include <QApplication> <QWidget> <QGridLayout> <QMainWindow> <QPushButton> <QSpinBox> <QStatusBar> <QString> <QLabel> <QLCDNumber> <QStyle> <QMessageBox> <QCloseEvent> <QBrush> <QTimer> <QMenuBar> <QProgressBar> <cstdio> #include "rs232.cpp" #include "dalmierz_optyczny.hpp" int ZmierzonaWartosc=0; static int COM; //========================================================================= //................ Kanwa .................................................. Kanwa::Kanwa(QWidget *wRodzic): QWidget(wRodzic) { wOrganizer = new QGridLayout(this); wPrzyciskPomiarPojed = new QPushButton(tr("Pomiar pojedynczy"),this); wOrganizer->addWidget(wPrzyciskPomiarPojed,0,3); wPrzyciskPomiarCiagl = new QPushButton(tr("Pomiar ciagly"),this); wOrganizer->addWidget(wPrzyciskPomiarCiagl,1,3); wPrzyciskKoniecCiagl = new QPushButton(tr("Zakoncz pomiar ciagly"),this); wOrganizer->addWidget(wPrzyciskKoniecCiagl,2,3); wLCD1 = new QLCDNumber(3,this); wLCD1->setSegmentStyle(QLCDNumber::Flat); 19 wOrganizer->addWidget(wLCD1,0,1,4,1); wPrzyciskZamknij = new QPushButton(tr("Zamknij"),this); wOrganizer->addWidget(wPrzyciskZamknij,3,3); wBelkaPostepu = new QProgressBar(this); wBelkaPostepu->setRange(0,150); wBelkaPostepu->setFormat("%v"); wOrganizer->addWidget(wBelkaPostepu,4,1,1,4); connect(wPrzyciskPomiarPojed,SIGNAL(clicked()), this,SIGNAL(ZglosPomiarPojed())); connect(wPrzyciskPomiarCiagl,SIGNAL(clicked()), this,SIGNAL(ZglosPomiarCiagly())); connect(wPrzyciskKoniecCiagl,SIGNAL(clicked()), this,SIGNAL(KoniecCiaglego())); connect(this,SIGNAL(UstawLiczbeNaWyswietlaczu(int)), wLCD1,SLOT(display(int))); connect(this,SIGNAL(UstawLiczbeNaWyswietlaczu(int)), wBelkaPostepu,SLOT(setValue(int))); connect(wPrzyciskZamknij,SIGNAL(clicked()), this,SIGNAL(ZglosZamkniecie())); connect(wPrzyciskPomiarCiagl,SIGNAL(clicked()), this,SIGNAL(ZglosNapis())); wOrganizer->setColumnStretch(1,4); wOrganizer->setColumnStretch(3,7); wOrganizer->setRowStretch(0,3); wOrganizer->setRowStretch(1,7); setLayout(wOrganizer); } //................ Kanwa .................................................. //========================================================================= //========================================================================= //................ OknoGlowne ............................................. OknoGlowne::OknoGlowne(QWidget *wRodzic): QMainWindow(wRodzic) 20 { timer = NULL; Kanwa *wOkno = new Kanwa(this); setCentralWidget(wOkno); setStatusBar(new QStatusBar()); resize(300,230); const QString Napis = tr("Trwa pomiar ciagly"); connect(wOkno,SIGNAL(ZglosZamkniecie()), this,SLOT(GdyZamkniecie())); connect(wOkno,SIGNAL(ZglosPomiarPojed()), this,SLOT(WykonajPomiarPojed())); connect(wOkno,SIGNAL(ZglosPomiarCiagly()), this,SLOT(WykonajPomiarCiagly())); connect(wOkno,SIGNAL(KoniecCiaglego()), this,SLOT(ZakonczPomiarCiagly())); connect(this,SIGNAL(WyslijLiczbeNaWyswietlacz(int)), wOkno,SIGNAL(UstawLiczbeNaWyswietlaczu(int))); connect(this,SIGNAL(BladPomiaru()), qApp,SLOT(quit())); connect(wOkno,SIGNAL(ZglosPomiarCiagly()), this,SLOT(UstawNapisStatusu())); connect(wOkno,SIGNAL(KoniecCiaglego()), this,SLOT(UsunNapisStatusu())); setPalette(QPalette(Qt::cyan)); setAutoFillBackground(true); } void OknoGlowne::UstawNapisStatusu() { const QString Napis= "trwa pomiar ciagly"; statusBar()->showMessage(Napis); } void OknoGlowne::UsunNapisStatusu() { statusBar()->clearMessage(); } void OknoGlowne::WykonajPomiarPojed() 21 { //nastapi wyslanie przez port szeregowy prosby o pomiar pojedynczy //potem odczyt wyslanej przez mikrokontroler wartosci //trzeba to wpisac do LCD unsigned int tmp; char z[2]; COM=OpenCom(1); SetCom(COM); SetBaudRate(COM,9600); SetParity(COM,"NONE"); SetXONXOFF(COM,"DISABLE"); SetStopBits(COM,1); SetDataBits(COM,8); Send(COM,"p"); Receive(COM,z,1,0 ); tmp = *z; ZmierzonaWartosc = tmp; emit WyslijLiczbeNaWyswietlacz(ZmierzonaWartosc); } void OknoGlowne::WykonajPomiarCiagly() { //wysylaj co 0.5sek prosbe o pomiar ciagly timer = new QTimer(this); //do OknoGlowne timer ->start(500); //timer odpala co 0.5s connect(timer,SIGNAL(timeout()),this,SLOT(WykonajPomiarPojed())); } void OknoGlowne::ZakonczPomiarCiagly() { //rozlaczenie timera i tym samym wylaczenie dialogu z mikroprocesorem if (timer) disconnect(timer, SIGNAL(timeout()), 0, 0); } bool OknoGlowne::CzyMoznaZamknac() { return QMessageBox::question(this,tr("Pytanie"), tr("Czy chcesz zakonczyc?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes; } 22 void OknoGlowne::closeEvent( QCloseEvent * event ) { if (CzyMoznaZamknac()) event->accept(); else event->ignore(); } void OknoGlowne::GdyZamkniecie() { if (CzyMoznaZamknac()) qApp->quit(); } //................ OknoGlowne ............................................. //========================================================================= //========================================================================= //========================================================================= int main( int argc, char * argv[] ) { QApplication App(argc,argv); OknoGlowne Okno; App.setStyle("plastique"); // Okno.resize(220,180); Okno.show(); return App.exec(); } 23