rs232_linux-win32_cz8.pdf

(628 KB) Pobierz
rs232_linux-win32_cz8.indd
KURS
Programowanie portu szeregowego
w systemach operacyjnych Linux
i Windows, część 8
Umiejętność programowej obsługi interfejsu RS232 od strony
komputera PC jest dziś istotnym elementem elektronicznego
rzemiosła. W niniejszym kursie piszemy jak w praktyce
oprogramować port szeregowy w środowiskach Linux i Windows.
Wiele miejsca poświęcamy pisaniu przenośnych aplikacji GUI,
które korzystają z interfejsu szeregowego i zachowują się tak samo
w systemach Windows jak i Linux. Wszystkie omawiane zagadnienia
poparte są szczegółowo opisanymi praktycznymi przykładami.
sum kontrolnych (jak choćby CRC,
czy kod Reeda–Solomona), jednak
w niniejszym, prostym przykładzie
suma arytmetyczna jest wystarcza-
jąca.
Odpowiedzią na ramkę zapyta-
nia jest ramka odpowiedzi, której
format jest następujący:
0x56 ByteH ByteL CKS
Pierwszy bajt to kod ASCII zna-
ku ‘V’, symbolizującego napięcie
elektryczne ( Volt ). Jest to nagłówek
ramki. Następne dwa bajty są wy-
nikiem konwersji analogowo–cyfro-
wej i, ze względu na rozdzielczość
przetwornika mikrokontrolera ATme-
ga8, istotnych jest 10 ich najmniej
znaczących bitów. Bajt CKS jest
oczywiście sumą kontrolną, oblicza-
ną tak samo jak w przypadku ramki
zapytania. Pozwala on odbiornikowi
(PC) stwierdzić, czy ramka jest po-
prawna, czy nie.
W poprzednich częściach kursu
poznaliśmy, jak stworzyć przenośną
aplikację wykorzystującą interfejs
RS232 z użyciem pakietu Qt firmy
Trolltech. Poniżej zostanie zapre-
zentowany nieco bardziej złożony
przykład. Jest nim woltomierz od-
czytywany przez łącze RS232.
ASCII znaku zapytania ‚?’ – łatwo
odgadnąć, że symbolizuje on py-
tanie. Ostatni bajt jest sumą kon-
trolną i pozwala odbiornikowi do-
konać kontroli poprawności ramki.
Jego wartość jest dopełnieniem do
dwóch sumy arytmetycznej pozosta-
łych bajtów ramki zapytania, dzięki
czemu najmniej znaczący bajt sumy
arytmetycznej wszystkich bajtów po-
prawnej ramki jest równy zero:
41+3F+80=100 (HEX)
Jak widać, za-
proponowano for-
mat ramki nieco na
wyrost w stosunku
do funkcji jaką peł-
ni. Przesyłany jest
jeden rodzaj infor-
macji, co sprawia,
że suma kontrolna
ma zawsze tę samą
wartość. Pokazuje
on jednak podsta-
wowe cechy, jakie
powinna mieć po-
prawna ramka zapy-
tania: nagłówek słu-
żący synchronizacji,
dane, suma kontro-
lna, a w przypadku
ramek o zmiennych
długościach – prze-
syłana powinna być
długość ramki lub
liczba bajtów da-
nych, ewentualnie
bajt terminujący
(określający koniec
pakietu danych).
W profesjonalnych
systemach stosuje
się znacznie lepsze
sposoby obliczania
Woltomierz – część sprzętowa
Do budowy woltomierza wyko-
rzystano ten sam zestaw testowy,
który posłużył do testów poprzednio
opisanych aplikacji – został on opi-
sany w części 2. kursu. Oprogramo-
wanie części sprzętowej woltomierza
stworzono wykorzystując kompilator
AVR–GCC, ten sam, który wykorzy-
stano przy implementacji opisanej
wcześniej aplikacji testowej. Działanie
oprogramowania mikrokontrolera pole-
ga na skonfigurowaniu modułu UART
i przetwornika A/C, a następnie na
oczekiwaniu na odpowiednie ramki
zapytania. W odpowiedzi na poprawną
ramkę przesyłana jest informacja o na-
pięciu panującym na wejściu ADC0
przetwornika. Jak widać, układ PC
– mikrokontroler jest typowym ukła-
dem Master – Slave . W celu wymia-
ny danych pomiędzy mikrokontrole-
rem a komputerem PC ustalono prosty
protokół komunikacyjny. Format ramki
zapytania ( Query Frame ) wysyłanej
przez komputer jest następujący:
0x41 0x3F 0x80
Pierwszy bajt jest umownie wy-
branym bajtem nagłówkowym ramki
( QF_HEADER ). Określa on początek
ramki i służy do synchronizacji od-
biornika (w tym przypadku mikro-
kontrolera) z nadajnikiem (w tym
przypadku komputerem PC). Dru-
gi bajt ( QF_COMMAND ) to kod
Rys. 15. Test części sprzętowej woltomierza
List. 25. Konfiguracja przetwornika A/C mikrokontrolera
//*******************************
// ADC Init
//*******************************
void Init_ADC(void)
{
//ADC channel 0, internal vref=2.56V
ADMUX=0xC0;
//single conversion
ADCSR=0x97;
//start first dummy conversion -
//- here ADC is adjusting himself
ADCSR|=0x40;
while((ADCSR & 0x40)!=0);
Waitms(10);
}
Elektronika Praktyczna 7/2007
89
152706849.004.png
KURS
List. 26. Konfiguracja USART i funkcje
obsługi łącza RS232
//*******************************
// RS232 Init
//*******************************
void Init_UART(void)
{
//enable transmitter
UCSRB|=(1<<TXEN);
//enable receiver
UCSRB|=(1<<RXEN);
//normal speed
UCSRA&=~(1<<U2X);
//19200 baud, err=0.0%
UBRRH=0;
UBRRL=5;
UCSRB&=~(1<<UCSZ2);
//8bit, no parity, one stop bit
UCSRC=(1<<URSEL)|(3<<UCSZ0);
}
//*******************************
// RS232 Send one byte
//*******************************
void SendByte(unsigned char byte)
{
UCSRA|=(1<<TXC);
UDR=byte;
while((UCSRA&(1<<TXC))==0);
}
//*******************************
// RS232 Send string
//*******************************
void SendString(char *pString)
{
char a;
unsigned char i=0;
while(a=PRG_RDB(&pString[i++]))
SendByte(a);
}
List. 27. Pętla główna programu mikrokontrolera
while(1)
{
//Waiting for query frame header - QF_HEADER
UCSRA&=~(1<<RXC);
while((UCSRA&(1<<RXC))==0);
QFrame[0]=UDR;
if(QFrame[0]==QF_HEADER)
{
//Waiting for rest 2 bytes of query frame with timeout
unsigned char byte_counter;
unsigned int timeout_counter;
timeout_counter=0;
for(byte_counter=1;byte_counter<3;byte_counter++)
{
//Waiting for 1 byte
UCSRA&=~(1<<RXC);
while((UCSRA&(1<<RXC))==0)
if(++timeout_counter>=TIMEOUT)
break;
QFrame[byte_counter]=UDR;
//If timeout occured - break for loop
if(timeout_counter>=TIMEOUT)
break;
}
//If timeout did not occured - check checksum and
//if OK then check query and send response
if(timeout_counter<TIMEOUT)
{
//Checking checksum
unsigned char summa=0;
for(byte_counter=0;byte_counter<3;byte_counter++)
summa+=QFrame[byte_counter];
if((summa & 0xFF)==0)
{
unsigned char adc_valL,adc_valH;
//Checking query command
if(QFrame[1]==QF_COMMAND)
{
//Measuring voltage
RED_ON;
ADCSR|=0x40; //start conversion
while((ADCSR & 0x40)!=0);
adc_valL=ADCL; //read LSB first
adc_valH=(ADCH & 0x03); //read MSB (2 bits)
//Sending response
SendByte(‘V’);
SendByte(adc_valH);
SendByte(adc_valL);
SendByte(0x100-’V’-adc_valH-adc_valL);
Waitms(10);
RED_OFF;
}
}
}
}
}
Przykładowy test części sprzę-
towej z użyciem terminala RS232
Docklight przedstawiono na rys. 15 .
Widoczne są trzy testy. Pierwszy
został dokonany w chwili, gdy na-
pięcie na wejściu ADC0 wynosiło
zero, drugi – gdy było ono maksy-
malne. Trzeci test przeprowadzono
dla dowolnej wartości pośredniej
(w tym przypadku równej 1,48 V,
przy napięciu referencyjnym, odpo-
wiadającym pełnej skali, równym
2,56 V). We wszystkich przypad-
kach najmniej znaczący bajt sumy
arytmetycznej wszystkich bajtów
ramki jest równy 0 (np. 56+02+4E-
+5A=100).
Na list. 25 przedstawiono kon-
figurację przetwornika analogowo-
–cyfrowego mikrokontrolera. Został
on skonfigurowany do pracy z we-
wnętrznym napięciem odniesienia
o wartości nominalnej 2,56 V i do
pracy w trybie pojedynczych kon-
wersji. Pierwsza konwersja, jaka jest
wywoływana w funkcji z list. 25,
ma na celu ustawienie przetwor-
nika – konwersja ta trwa znacznie
dłużej niż późniejsze przetwarzanie.
Na list. 26 przedstawiono konfigu-
rację oraz funkcje obsługi interfej-
su RS232 (wysyłanie jednego baj-
tu i wysyłanie łańcucha znaków).
USART pracuje z ramką 8N1 przy
szybkości 19200 b/s.
Pętlę główną oprogramowania
mikrokontrolera przedstawiono na
list. 27 . Na początku procesor ocze-
kuje na bajt nagłówkowy ramki
zapytania. Oczekiwanie to nie jest
objęte żadnym czasem przetermi-
nowania. Odebranie bajtu innego
niż QF_HEADER (o wartości 0x41)
powoduje jego zignorowanie i kon-
tynuowanie oczekiwania. Jeśli ode-
brano właściwy bajt nagłówkowy,
mikrokontroler przechodzi do pętli,
w której oczekuje na pozostałe dwa
bajty ramki zapytania. Oczekiwanie
to jest objęte odpowiednim czasem
przeterminowania ( timeout ), wyno-
szącym około 4–krotnemu czasowi
transmisji dwóch bajtów. Jeśli w tym
czasie nie odebrano 2 bajtów, ram-
ka jest ignorowana i program prze-
chodzi do oczekiwania na bajt na-
główkowy. Jeśli odebrano, to spraw-
dzana jest wartość drugiego bajtu
ramki ( QF_COMMAND ). Jeśli jego
wartość jest poprawna, to następ-
nie jest obliczana suma arytmetycz-
na wszystkich bajtów ramki i, jeśli
jest ona poprawna, mikrokontroler
dokonuje konwersji analogowo–cy-
frowej. Odczytana z przetwornika
wartość napięcia (w formie dwóch
bajtów) jest wysyłana do kompute-
ra PC w formie ramki odpowiedzi
opisanej wyżej. Na czas konwersji
i transmisji tej ramki oraz przez ok.
10 następnych milisekund, zapalana
90
Elektronika Praktyczna 7/2007
152706849.005.png
KURS
Rys. 16. Aplikacja woltomierza działa-
jąca w systemie Windows
nej (2,56 V) wynosi
0,36 V, co oczywi-
ście znacznie prze-
kracza rozdzielczość
niniejszego wolto-
mierza.
Kod aplikacji
woltomierza składa
się z trzech klas:
– CCommInterface
– SamplingThread
– VoltmeterWFrm
Pierwsza z nich
jest nam dobrze
znana z poprzednich
części kursu. Klasa
VoltmeterWFrm jest
klasą głównej (i je-
dynej) formy apli-
kacji. Została ona
stworzona podob-
nie do klasy formy
aplikacji testowej
Example2W , opisanej
w poprzednich czę-
ściach kursu. Podobnie jak wtedy,
tak i w tym przypadku, do budowy
interfejsu graficznego wykorzystano
program Qt Designer . Deklarację kla-
sy VoltmeterWFrm przedstawiono na
list. 28 . Jak widać, jednym z jej pól
jest obiekt stSampling klasy Sam-
plingThread . Klasa ta implementuje
wątek odpytujący część sprzętową.
Zastosowanie w tym celu osobnego
wątku, zamiast np. licznika klasy
QTimer , ma ogromne zalety. Two-
rząc nowy wątek otrzymujemy moż-
liwość przeprowadzania przeróżnych
operacji, które wykonają się w tle
głównego wątku, dzięki czemu nie
będą one niepotrzebnie zabierać
jego czasu. Wątek SamplingThread
zajmuje się odpytywaniem sprzętu
(działa jako typowy Worker Thread )
i analizowaniem odpowiedzi, a do
wątku głównego przesyła gotowe
efekty tej analizy, które są w tym
wątku jedynie wizualizowane.
Plik nagłówkowy kla-
sy wątku przedstawiono
na list. 29 . Obiekt interfej-
su szeregowego jest polem
tej właśnie klasy. Port jest
otwierany w konstruktorze
klasy VoltmeterWFrm , w któ-
rym wątek jest uruchamiany
( list. 30 ). Do komunikacji
pomiędzy wątkiem próbku-
jącym a wątkiem głównym
wykorzystano mechanizm sy-
gnałów i slotów. Klasa Volt-
meterWFrm posiada następu-
jący slot:
List. 29. Deklaracja klasy wątku SamplingThread
#ifndef SamplingThreadH
#define SamplingThreadH
#include „CommInterface.h”
#include <QtCore/QMutex>
#include <QtCore/QThread>
#include <QtCore/QWaitCondition>
class SamplingThread : public QThread
{
Q_OBJECT
public:
QMutex mutex;
CCommInterface Comm;
SamplingThread(QObject *parent = 0);
~SamplingThread();
void Suspend();
void Resume();
signals:
void NewDataReceived(ulong data, QString status);
protected:
void run();
private:
bool abort;
bool suspend;
QWaitCondition condition;
void CheckResponse(void);
};
Rys. 17. Aplikacja woltomierza działa-
jąca w systemie Linux
jest dioda czerwona D1 sygnalizu-
jąc aktywność woltomierza.
#endif
Woltomierz – program na
komputer PC
Woltomierz działający w systemie
Windows przedstawiono na rys. 16 ,
zaś na rys. 17 pokazano widok
okna programu pracującego w sys-
temie Linux. Aplikacja VoltmeterW
posiada pole wyniku (pokazujące
napięcie wejściowe z rozdzielczością
0,01 V), pole stanu (zawierające
informację o poprawności otrzyma-
nej odpowiedzi lub jej braku), pole
pozwalające zatrzymać i wznowić
odświeżanie wyniku oraz pole tek-
stowe, służące do kalibracji wolto-
mierza. Kalibracja może okazać się
konieczna, ze względu na spory
rozrzut napięć wewnętrznego źródła
napięcia odniesienia mikrokontrolera
ATmega8. Zgodnie z dokumentacja
mikrokontrolera, wewnętrzne napię-
cie odniesienie może zawierać się
w przedziale 2,3...2,7 V. Maksymal-
na odchyłka od wartości nominal-
void UpdateResult(ulong data,
QString status);
Funkcji tej przekazywana jest
wartość napięcia (10 najmniej zna-
czących bitów argumentu data ) oraz
napis, jaki pojawić się ma w polu
stanu. Slot jest wywoływany wtedy,
gdy wątek próbkujący wyemituje
następujący sygnał:
void NewDataReceived(ulong
data, QString status);
Sygnał ten jest zadeklarowany
w klasie wątku (list. 29). Oczywiście
slot jest wywoływany z tymi samy-
mi argumentami, co sygnał z nim
połączony. Połączenie wymienionych
slotów i sygnałów jest dokonywane
w konstruktorze klasy VoltmeterW-
Frm (list. 30). Implementację slotu
UpdateResult() przedstawiono na
list. 31 . Jak widać, jego działanie
polega na pobraniu wartości napię-
cia kalibrującego (odpowiadającego
napięciu pełnej skali) i przetworze-
niu 10–bitowej liczby odczytanej
z przetwornika A/C na liczbę ułam-
kową oraz jej wyświetlenie w po-
lu wyniku. Informacja z argumentu
status trafia do pola stanu. Wartość
napięcia jest zakodowana w 10–bito-
wej zmiennej data (zakres wartości
0...1023).
Ważnymi polami klasy Sampling-
Thread są pola abort i suspend , oba
typu bool (list. 29). Nadanie war-
tości true polu abort powoduje za-
kończenie działania wątku, zaś polu
suspend – zawieszenie jego działa-
nia. Do realizacji zawieszania służy
List. 28. Deklaracja klasy VoltmeterWFrm
#include „ui_VoltmeterWFrm.h”
#include „SamplingThread.h”
class VoltmeterWFrm : public QDialog
{
Q_OBJECT
public:
VoltmeterWFrm(QDialog *parent = 0);
~VoltmeterWFrm();
private slots:
void UpdateResult(ulong data, QString
status);
void ClickedCheckBoxSampling(int state);
private:
SamplingThread stSampling;
Ui::frmVoltmeterW ui;
};
Elektronika Praktyczna 7/2007
91
152706849.006.png 152706849.007.png 152706849.001.png
KURS
List. 30. Konstruktor klasy VoltmeterWFrm
VoltmeterWFrm::VoltmeterWFrm(QDialog *parent)
: QDialog(parent)
{
ui.setupUi(this);
//Set palette (text color) for result display and status label
QPalette pal=ui.lbResult->palette();
pal.setColor(QPalette::Foreground,QColor(tr(„lightgreen”)));
ui.lbResult->setPalette(pal);
pal=ui.lbStatus->palette();
pal.setColor(QPalette::Foreground,QColor(tr(„white”)));
ui.lbStatus->setPalette(pal);
//Set text codec for tr() function
QTextCodec::setCodecForTr(QTextCodec::codecForName(„ISO8859-2”));
//Connect signals and slots
connect(&stSampling, SIGNAL(NewDataReceived(ulong, QString)),
this, SLOT(UpdateResult(ulong, QString)));
connect(ui.cbSampling, SIGNAL(stateChanged(int)),
this, SLOT(ClickedCheckBoxSampling(int)));
//Initial information
UpdateResult(0,”No response”);
//Open port
string port=”COM1”;
if(stSampling.Comm.Open(port,19200)==false)
{
QString portname=port.c_str();
QMessageBox::critical(this,tr(„Error”),tr(„Port „)+portname+
tr(„ opening error. Application will terminate.”));
this->reject();
}
//Start the thread
if (!stSampling.isRunning())
stSampling.start(QThread::TimeCriticalPriority);
}
fragmentu aplikacji testowej, opisa-
nej w poprzednich częściach kursu.
Funkcja sprawdza czy nie wystąpi-
ły błędy i czy odpowiedź w ogóle
nadeszła. W przypadku braku od-
powiedzi lub błędu emituje ona
sygnał NewDataReceived() , podając
jako argument data wartość 0, a ja-
ko status stosowną informację. Jeśli
nadeszła poprawna odpowiedź, emi-
towany jest sygnał zawierający 10-
–bitową informację o napięciu oraz
stan „OK”.
Wróćmy na chwilę do funkcji
run() . Niektóre jej fragmenty są uję-
te w następującą klamrę:
mutex.lock();
...
mutex.unlock();
Pole klasy SamplingThread o na-
zwie mutex (obiekt klasy QMutex )
to semafor binarny wzajemnego wy-
kluczania ( MUTual EXclusion ). Jego
działanie polega na tym, że unie-
możliwia on dwóm (lub więcej)
wątkom na jednoczesne operowanie
na współdzielonych zasobach. Gdy-
by nie został zastosowany, to je-
den wątek mógłby zmienić warun-
ki pracy drugiego wątku w sposób
dla niego zupełnie nieprzewidywal-
ny. Byłoby tak dlatego, że system
operacyjny może przełączyć wątki
na przykład w chwili, gdy jeden
z nich ( wątek A ) jest w trakcie wy-
konywania pewnego ciągu instruk-
cji na pewnych danych. Dostęp do
tych danych ma także drugi wątek
( wątek B ). Wątek B może zmienić
wartości zmiennych, które są wy-
nikami operacji wykonanych przed
przełączeniem przez wątek A . Po
ponownym przekazaniu sterowania
wątkowi A próbuje on skorzystać
z tych zmiennych, ale ich warto-
ści są już inne niż te, których się
spodziewa. Powoduje to oczywiście
nieprzewidywalne zachowanie się
programu. Gdy mutex jest opusz-
czony ( lock ), to wątek próbujący
dostać się do chronionej przez
niego sekcji krytycznej zostaje za-
wieszony. Gdy wątek, który opu-
ścił semafor, podniesie go ( unlock ),
wątek zawieszony wznawia swoje
działanie i wchodzi do sekcji kry-
tycznej opuszczając jednocześnie
semafor. Operacje sprawdzenia sta-
nu semafora i jego opuszczenia (je-
śli to możliwe) są operacjami ato-
mowymi.
W przypadku aplikacji wolto-
mierza, konkurencyjnymi wątkami
są: główny wątek aplikacji i wątek
List. 31. Odświeżanie wartości napięcia i stanu
void VoltmeterWFrm::UpdateResult(ulong data, QString status)
{
//Format result
double fUmax=ui.leCalibration->text().toDouble();
if((fUmax<0) || (fUmax>5))
fUmax=0;
double fResult=(fUmax*data)/1024;
//Dislpay result and status
ui.lbResult->setText(QString::number(fResult,’f’,2)+” V”);
ui.lbStatus->setText(status);
}
List. 32. Funkcja run() wątku SamplingThread
void SamplingThread::run()
{
forever
{
//Repeat sampling every 200ms (150+50)
msleep(150);
//Send query frame
mutex.lock();
unsigned char QueryFrame[3];
QueryFrame[0]=0x41; //’A’
QueryFrame[1]=0x3F; //’?’
QueryFrame[2]=0x80; //checksum
Comm.Write(QueryFrame,3);
mutex.unlock();
//Break Time
msleep(50);
//Check response
mutex.lock();
this->CheckResponse();
mutex.unlock();
if(abort)
return;
mutex.lock();
if(suspend)
condition.wait(&mutex);
mutex.unlock();
}
}
pole condition klasy QWa-
itCondition (szczegóły opi-
sane zostaną dalej).
Na list. 32 przedsta-
wiono funkcję run() wątku
próbkującego. Do tej wła-
śnie funkcji przekazywane
jest sterowanie po urucho-
mieniu wątku za pomocą
metody start() . Działanie
tej funkcji polega na cy-
klicznym wysyłaniu za-
pytania, odczekaniu czasu
break time (ok. 50 ms),
analizie odpowiedzi za
pomocą funkcji CheckRe-
sponse() oraz sprawdzeniu
warunków zakończenia
i zawieszenia wątku. Im-
plementację funkcji Chec-
kResponse() przedstawiono
na list. 33 . Jej działanie
jest podobne do działania
92
Elektronika Praktyczna 7/2007
152706849.002.png
KURS
List. 33. Funkcja analizująca odpowiedź części sprzętowej
void SamplingThread::CheckResponse(void)
{
unsigned long Errors,InQue,BytesRead;
unsigned char *Input;
Comm.CheckCommInput(&Errors,&InQue);
if(Errors!=0)
{
emit NewDataReceived(0,”UART error”);
}
else
{
if(InQue==0)
{
emit NewDataReceived(0,”No response”);
}
else
{
Input=new unsigned char[InQue];
if(Comm.Read(Input,&BytesRead,InQue)==false)
{
emit NewDataReceived(0,”Read error”);
delete [] Input;
return;
}
//Checking response
if((Input[0]!=’V’) || (BytesRead!=4))
{
emit NewDataReceived(0,”Response error”);
delete [] Input;
return;
}
//Checking checksum
unsigned char summa=0;
for(unsigned long j=0;j<BytesRead;j++)
summa+=Input[j];
//Checksum OK
if((summa & 0xFF)==0)
{
//Show result
emit NewDataReceived(256*Input[1]+Input[2],”OK”);
}
else
emit NewDataReceived(0,”Checksum error”);
delete [] Input;
}
}
}
List. 34. Funkcje zawieszenia i wzno-
wienia wątku próbkującego
void SamplingThread::Suspend()
{
suspend=true;
}
void SamplingThread::Resume()
{
mutex.lock();
suspend=false;
condition.wakeOne();
mutex.unlock();
}
nam na ciągłości wykonywanych
operacji. Zasada ta dotyczy zarów-
no implementacji klasy Sampling-
Thread , jak i klasy VoltmeterWFrm ,
znajdującej się w przestrzeni wątku
głównego.
Jeśli pole abort ma wartość true ,
funkcja run() kończy swoje działa-
nie, a co za tym idzie, kończy je
także wątek próbkujący. Jeśli pole
suspend ma wartość true , to wątek
jest zawieszany na zmiennej warun-
kowej condition . Wywołanie funkcji
condition.wait(&mutex);
powoduje nie tylko zawieszenie
działania wątku, ale także niejaw-
ne podniesienie ( unlock ) semafora
mutex . Dzięki temu wątek główny
ma dostęp do zmiennej condition
i może wznowić działanie wątku
próbkującego, gdy tylko „zechce”.
Jednocześnie zapewniona jest spój-
ność operacji sprawdzenia stanu
zmiennej suspend i zawieszenia
wątku. Na list. 34 pokazano funk-
cje zapewniające dostęp do pola
suspend . Funkcje te wykorzystano
w slocie obsługi zdarzenia polega-
jącego na zmianie stanu pola typu
QCheckBox , pozwalającego zatrzy-
mać i wznowić odświeżanie wyni-
ku ( list. 35 ).
Na list. 36 przedstawiono kon-
struktor i destruktor klasy wątku
SamplingThread . Destruktor powo-
duje ustawienie znacznika abort
oraz obudzenie wątku, który może
być zawieszony na warunku con-
dition . Dzięki wywołaniu funkcji
wait() , istnieje pewność, że de-
struktor zakończy swoje działa-
nie dopiero po zakończeniu pracy
funkcji run() . Port szeregowy jest
zamykany w destruktorze klasy
VoltmeterWFrm ( list. 37 ). Aby nie
odbyło się to w trakcie korzysta-
nia z portu przez wątek próbkują-
cy, operacja zamykania portu ujęta
jest w klamrę mutex.lock()...mutex.
unlock() .
Arkadiusz Antoniak, EP
arkadiusz.antoniak@ep.com.pl
www.antoniak.ep.com.pl
List. 35. Slot ClickedCheckBoxSampling()
void VoltmeterWFrm::ClickedCheckBoxSampling(int state)
{
if(0==state)
stSampling.Suspend();
else
stSampling.Resume();
}
SamplingTh-
read . Główny
wątek aplika-
cji ma bezpo-
średni dostęp
do publicznych
pól i metod
wątku Sam-
plingThread ,
a co za tym
idzie – po-
średni dostęp
do składowych
prywatnych.
Należy pamię-
tać, że wywo-
łanie np. kon-
struktora czy
destruktora kla-
sy SamplingTh-
read odbywa
się w przestrzeni wątku głównego
(bo to właśnie on wywołuje kon-
struktor i destruktor). Z tego po-
wodu, klamrą mutex.lock()...mutex.
unlock() należy objąć wszystkie te
odwołania do składowych klasy
SamplingThread , w których zależy
List. 36. Konstruktor i destruktor klasy wątku SamplingThre-
ad
SamplingThread::SamplingThread(QObject *parent)
: QThread(parent)
{
abort = false;
suspend=false;
}
SamplingThread::~SamplingThread()
{
mutex.lock();
abort=true;
suspend=false;
condition.wakeOne();
mutex.unlock();
//Wait until thread ends run()
wait();
}
List. 37. Destruktor klasy Voltme-
terWFr
VoltmeterWFrm::~VoltmeterWFrm()
{
stSampling.mutex.lock();
stSampling.Comm.Close();
stSampling.mutex.unlock();
}
Elektronika Praktyczna 7/2007
93
152706849.003.png
Zgłoś jeśli naruszono regulamin