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