Sprawozdanie z wykonania projektu Dalmierz optyczny AnnaSadowska133152 19 czerwca 2007 Wizualizacjadanychsensorycznych c AnnaSadowska 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 Wykonanysprzę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, interfejszgodnyzmagistralą 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: Struktura i zasoby MC9S12A64. 4
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łytkadrukowana 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 Pozostałeelement 2.3.1 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 linijkadiodowa 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: diodyled, 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 Programowaniemikrokontrolera 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, brakbituparzystości,1bitstopui8bitówdanych.pełnykodźródłowytego programu znajduje się poniżej. #include"main.h" #include"sci.h" #include"init.h" #include <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; inti; 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){ chars,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); elsereturn((int)c); 14
//koniec funkcji do obslugi portu szeregowego voidmain() { 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; //ashortdelay 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; //atd enable and fast flag clear all Atd0.atdctl5.byte = MULT SCAN; //atd multichannel continuous scan Atd0.atdctl3.bit.slc = INP_NBR; //conversion sequence length ustawienie kierunku* Regs.ddra.byte = 0xFF; //piny wyjsciowe Regs.ddrb.byte = 0xFF; Pim.ddrt.byte = 0x00; //piny wejsciowe // wylaczenie diod Regs.porta.byte = 0xFF; Regs.portb.byte = 0xFF; 15
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 AplikacjawQt 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 <QApplication> #include <QWidget> #include <QGridLayout> #include <QMainWindow> #include <QPushButton> #include <QSpinBox> #include <QStatusBar> #include <QString> #include <QLabel> #include <QLCDNumber> #include <QProgressBar> #include <QStyle> #include <QMessageBox> #include <QCloseEvent> #include <QBrush> #include <QTimer> #include <QMenuBar> //klasa zawiera wszyskie guziki aplikacji i QLCDNumbed i QProgressBar classkanwa:publicqwidget{//------------------------------------------ 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 classoknoglowne:publicqmainwindow{//---------------------------------- 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 slots: void GdyZamkniecie(); void WykonajPomiarPojed(); void WykonajPomiarCiagly(); void ZakonczPomiarCiagly(); void UstawNapisStatusu(); void UsunNapisStatusu(); ;//---------------------------------------------------------------------- #endif Plik dalmierzoptyczny.cpp 18
#ifdef GNUG #pragma implementation #endif #include <QApplication> #include <QWidget> #include <QGridLayout> #include <QMainWindow> #include <QPushButton> #include <QSpinBox> #include <QStatusBar> #include <QString> #include <QLabel> #include <QLCDNumber> #include <QStyle> #include <QMessageBox> #include <QCloseEvent> #include <QBrush> #include <QTimer> #include <QMenuBar> #include <QProgressBar> #include <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; charz[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... //========================================================================= //========================================================================= //========================================================================= intmain(intargc,char*argv[]) { QApplication App(argc,argv); OknoGlowne Okno; App.setStyle("plastique"); // Okno.resize(220,180); Okno.show(); return App.exec(); 23