Sistem P5 Embedded Pc de Comanda Si Control al Unui Brat Robotic

Introducere

Odată cu dezvoltarea uimitoare a tehnologiei și a științei din ultimele decenii au apărut si posibilități aproape nelimitate de utilizare a acestora pentru a face viața oamenilor mai ușoară si mai productivă. Un alt avantaj major al inovațiilor tehnologice și științifice este capacitatea acestora de a-și auto susține evoluția . Aceasta înseamnă că anumite inovații științifice vor fi folosite la rândul lor de către cercetători și oameni de știință pentru a produce alte descoperiri în domeniu într-un timp mult mai redus. Aceasta a dus ( cel puțin în ultimul secol ) la o caracteristică a evoluției tehnologice de tip exponențial.

În continuare vom sublinia două domenii a științei și tehnologiei care sunt relevante pentru lucrarea de față și câteva din principalele avantaje ale lor. Aceste două domenii sunt robotica și interfațarea om-mașină.

O ramură a tehnologiei care a adus și va continua să aducă beneficii substanțiale în îmbunătățirea vieții oamenilor, a activităților comerciale ( sporind productivitatea și diminuând costurile ) , a medicinii precum și în multe alte domenii este robotica. Aceasta se ocupă în principal cu designul, construcția, operarea și dezvoltarea roboților. Prin termenul de robot se va înțelege un sistem automatizat care acționează pe baza unui program de lucru stabilit sau reacționează la anumite influente exterioare, dând impresia executării unor acțiuni omenești. În acest secol domeniul roboticii a evoluat foarte mult (si pare că rata evoluției va continua în viitor) contribuind cu avantaje în aproape fiecare domeniu existent. Rezultatul rapid , cu un grad de siguranță și încredere ridicat obținut prin aplicații robotice particularizate este unul dintre cele mai importante avantaje ale acestui domeniu. Calitatea muncii efectuate este crescută pe când numărul de greșeli este mult mai mic decât în cazul unui om. În domeniul medical, aplicațiile robotice ajută la efectuarea intervențiilor chirurgicale foarte complicate. Ținând cont de viteza și precizia crescută a roboților, de capacitatea acestora de a funcționa încontinuu aceștia vor vedea o creștere foarte mare în domeniul industrial. Unii roboți pot efectua munca echivalentă executată de o sută de muncitori.

Un al doilea domeniu de interes pentru dezvoltarea lucrării de față este interfațarea om mașină ( human-machine interaction ) . Acesta reprezintă studiul, planificarea și design-ul interacțiunii dintre om și mașini. De obicei, acesta este privit că intersecția dintre mai multe domenii, dintre care informatica , știința comportamentului și design . Interacțiunea dintre oameni și mașini se întâmpla la nivelul interfeței utilizator, care include componente hardware și software. Scopul interacțiunii dintre om și mașina la nivelul interfeței este acela de a controla și utiliza eficient mașina precum și de a primi informații de la aceasta , informații care îl vor ajuta pe operatorul mașinii să ia decizii operaționale pentru înfăptuirea scopului propus; de exemplu, caracterele sau obiectele obținute că rezultat al rulării unei aplicații software sunt afișate pe monitor, datele de intrare necesare pentru funcționarea programului sunt introduse de utilizator prin intermediul componentelor hardware precum tastatura și mouse. Unul din scopurile principale și pe termen lung al domeniului este acela de a minimiza bariera dintre modelul cognitiv uman folosit pentru obținerea unui rezultat și modalitatea calculatoarelor/mașinilor de a înțelege ceea ce utilizatorul vrea să facă.

O interfață utilizator este un sistem prin care oamenii interacționează cu mașinile. Interfața utilizator include componente fizice ( hardware ) și logice ( software ). Aceste interfețe există pentru o foarte mare varietate de sisteme și oferă un mijloc de:

Intrare ( de a introduce date ), oferind utilizatorului posibilitatea de a manipula sistemul sau/și

Ieșire , oferind sistemului posibilitatea de a indica utilizatorului efectele manipulării indicate

În general, scopul interacțiunii om-mașina este acela de a produce o interfață care face că utilizarea mașinii să fie eficientă, ușoară și plăcută într-un mod care produce rezultatele dorite. Într-un mod puțin mai generalizat aceasta înseamnă că utilizatorul trebuie să ofere mașinii informații minimale pentru obținerea rezultatului urmărit și că mașina trebuie să diminueze numărul rezultatelor nefolositoare returnate utilizatorului.

În lucrarea de față se va prezenta o modalitate de facilitare a interacțiunii om-mașină, prezentarea implicațiilor teoretice necesare , dezvoltarea unui prototip, precizarea avantajelor utilizării acestui model precum și posibilitățile de dezvoltare ulterioară a acestuia.

Aplicația dezvoltată în cadrul acestei lucrări implică mai multe componente:

o mănușă de date prin care se vor putea prelua informații direct de la utilizator pentru determinarea: poziției mâinii acestuia în spațiu, a unghiului la care mana este inclinata precum și a poziției degetelor (daca acestea sunt strânse sau nu ).

o aplicație pe calculator folosită pentru facilitarea preluării datelor precizate mai sus și pentru trimiterea acestor informații către un microcontroler

un microcontroler cu ajutorul căruia se va putea controla mișcarea unui braț robotic

un braț robotic care va putea fi folosit pentru imitarea gesturilor unui braț uman dar care va putea efectua și o serie de gesturi pe care utilizatorul îl va învăța să le facă

Ideea de bază este următoarea : utilizatorul să-și poată pune mănușa și prin rularea unei aplicații pe calculator, brațul robotic să poată să-i imite gesturile.

În continuare, foarte pe scurt va fi descris termenul de braț robotic și posibilitățile de utilizare a acestuia. Brațele robotice sunt dispozitive controlate mecanic care au că scop reproducerea mișcărilor unui braț uman. Aceste dispozitive sunt folosite în ziua de azi pentru ridicarea greutăților și îndeplinirea sarcinilor care necesită concentrare și acuratețe extraordinară. Cel mai des, brațul robotic este folosit în scopuri industriale însă utilizarea nefiind limitată la acestea. În acest moment sunt disponibile o mare varietate de brațe robotice. Fiecare tip are propriile specificații. De exemplu, numărul de încheieturi a brațului variază, precum și direcția în care aceste încheieturi se pot deplasa. Câteva din cele mai des întâlnite brațe robotice includ articulații verticale, asamblare selectivă conformă cerințelor impuse, articulație polară, carteziană, cilindrică și paralelă.

Brațele robotice sunt folosite peste tot în industrie. Aceste dispozitive sunt cel mai des folosite pentru manipularea obiectelor grele, de exemplu pe șantiere. Ele sunt folosite și în situații în care pot exista vapori toxici potențial periculoși pentru oameni precum anumite vopsitorii. Brațele robotice sunt utilizate și în fabrici unde este necesară asamblarea unor echipamente periculoase. Ca utilizări non industriale vom preciza folosirea acestora pentru colectarea specimenelor în arii contaminate. Agenția NASA este cunoscută pentru utilizarea brațelor robotice în scopul menținerii poziției unui astronaut în spațiu când acesta efectuează operațiuni de asamblare/dezasamblare a unui echipament. În viitor acestea ar putea fi folosite pentru înlocuirea unor membre a persoanelor cu dizabilități. DARPA ( Defense Advancement Research Agency ) lucrează la un proiect pentru replicarea unei mâini umane care să răspundă la mesaje primite de la sistemul nervos central.

Brațul robotic din această lucrare va mai trebui să poată efectua repetitiv o serie de mișcări pe care utilizatorul îl va învăța să le facă. Acest proiect poate avea aplicabilitate intr-o scara larga de domenii printre care:

industrial unde se pot efectua munci care sunt prea periculoase sau prea greu de îndeplinit pentru oameni și acestea constă într-un set de gesturi repetitive sau nu.

medical unde : se pot efectua intervenții chirurgicale cu o precizie mult mai mare, și gesturile nu se pot realiza automatizat (evident va exista posibilitatea scalării mișcării mâinii utilizatorului cu mișcarea brațului, prin setarea unui parametru precum sensibilitatea) și se poate chiar considera realizarea unor proteze pentru oamenii cu dizabilități.

spațial , în locuri unde se desfășoară proiecte inaccesibile oamenilor și care necesita un control al brațului efectuat de un utilizator uman

Acest proiect își propune simplificarea folosirii mașinilor prin controlarea acestora direct prin gesturi ale utilizatorului ceea ce face că totul să fie mai natural față de controlul prin intermediul manetelor sau a aplicațiilor soft. Aplicația software va fi folosită doar pentru a face setări inițiale și pentru a citi anumite date .

Fundamentare teoretică

În cadrul acestui capitol vor fi descrise toate conceptele teoretice și instrumentele utilizate pentru dezvoltarea proiectului. Vom începe prin a face o enumerare a acestora:

mănușa de date

servomecanisme și servomotoare

modularea impulsurilor în durata ( PWM )

comunicația prin portul serial

microcontrolerul MCF5213EC de la firma Freescale

Mănușa de date

Despre mănușile de date

O mănușă de date ( Date Glove ) este un dispozitiv folosit pentru introducerea datelor în cadrul domeniului interacțiune om-calculator. Cel mai folosit subdomeniu în care sunt folosite mănușile de date este realitatea virtuala. De-a lungul procesului de fabricare a mănușilor sunt folosite diferite tehnologii pentru crearea senzorilor de pe mănușă care vor putea capta informații precum gradul de flexare a degetelor mâinii. De obicei mai este atașat și un detector de mișcare pentru a detecta poziția în spațiu și unghiul la care mănușa este inclinată. Toate aceste informații sunt interpretate de un instrument software care însoțește mănușa, astfel încât orice mișcare poate însemna un număr foarte mare de lucruri. Anumite gesturi pot fi categorisite ca informații utile, cum ar fi un program de interpretare a limbajului semnelor. Mănușile de date ( data gloves ) mai sunt numite și cyber-mănuși ( cybergloves).

Mănușile de ultimă generație ( la un preț foarte ridicat ) pot de asemenea să ofere utilizatorului un răspuns de tip haptic, care de fapt este o simulare a simțului tactil. Acest mod de a folosi o mănușă de date permite că aceasta să fie folosită și ca un dispozitiv de ieșire, nu doar ca unul de intrare. În mod uzual, mănușile de date sunt disponibile pe piață la prețuri foarte mari, și cu posibilitatea de achiziționare a senzorilor pentru detectarea poziției degetelor și a dispozitivului de urmărire a poziției mâinii în spațiu, separat. O altă alternativă pentru mănușile de date este utilizarea unei camere video conectată la un calculator și a unui program de recunoaștere a poziției 3D din informația video procesată.

Prima mănușă de date care a intrat pe piață a fost creata de Electronic Visualization Laboratory în 1977. În 1982, Thomas G. Zimmermann a completat un patent de invenție ( US Patent 4542291 ) pentru un senzor optic montat intr-o mănușa care poate detecta gradul de flexare a degetelor. Zimmermann a colaborat cu Jaron Lanier pentru a incorpora în sistem un senzor cu ultrasunete și unul magnetic pentru detectarea poziției mâinii în spațiu. Apoi au apărut mănușile de date Power Glove și Data Glove. Senzorul optic de detecție a gradului de flexare a degetelor care a fost folosit în Data Glove a fost inventat de Young L. Harvill ( US Patent 5097252, completat în 1989) care a zgâriat fibra la nivelul încheieturii degetelor pentru a o face mai receptivă pe plan local la flexare.

Prima mănușă care a fost disponibila utilizatorilor în 1987 a fost Power Glove de la Nintendo. Aceasta a fost fabricată că o mănușa pentru consola de jocuri Nintendo Entertainment System. Mănușa era echipata cu senzori rudimentari pentru detectarea poziției 3D și a gradului de flexare a degetelor precum și cu un buton plasat pe spatele mănușii. Senzorii folosiți în mănușa Power Glove au fost de asemenea folosiți și de către oamenii pasionați pentru a-și crea propriile mănuși de date.

În anul 2002 este lansată mănușa de date P5 Glove . În aplicațiile uzuale ea funcționa că un dispozitiv pentru controlul cursorului și câteva jocuri pe calculator au fost adaptate pentru a oferi suport 3D pentru aceasta.

Specificațiile tehnice ale mănușii de date P5 Glove sunt:

Senzorii de pe degete:

5 măsurători independente posibile

o rezoluție de 0.5 grade (o raza de la 0 pana la 90 de grade fiind disponibila)

Specificații ale sistemului de urmărire a poziției în spațiu:

rata de reîmprospătare de 60 Hz

raza maxima de detecție a ledurilor este intre 1 și 1.5m

6 grade de libertate (x/ y/ z / yawn/ pitch / roll)

Specificații X, Y, Z

O rezoluție de 3.17 mm

O acuratețe de 1.27 cm

In paginile următoare vom descrie modul în care este detectată poziția mănușii ( unghiulară și spațială )

Interpretarea datelor primite de la mănușa

Figura 1

În interiorul turnului de recepție a semnalelor există doua panouri de senzori. Unul dintre panouri este plasat în partea de sus a turnului iar cel de-al doilea în partea de jos. În spațiul dintre cele doua panouri sunt doar elemente de circuit.

În primul panou ( head 1 ) se pot observa 4 fotodiode. Acestea măsoară intensitatea luminii la care fiecare dintre ele este expusa. Primele două fotodiode de sus formează detectorul pentru poziția pe axa Y, iar cele două fotodiode de sub cele pentru Y alcătuiesc detectorul pentru poziția pe axa X. Se observă că împrejurul fiecărei perechi de fotodiode este plasat un anumit obstacol din material opac. Atunci când mănușa este plasată în partea din stânga a turnului, lumina nu va cădea pe fotodioda din stânga a receptorului pentru axa X, din cauza materialului care împrejmuiește senzorul, pe când fotodioda din partea dreapta a senzorului va fi total luminată. Când mănușa este în partea din dreaptă a turnului, fotodioda din dreapta va fi parțial mascată, pe când cea din stânga va fi total luminată. Acest procedeu permite ca mișcarea pe axa X să fie măsurată. Detectorul de poziție pentru axa Y funcționează în mod analog cu scopul măsurării mișcării pe verticală.

Deci, luminozitatea totală a led-urilor nu va schimba nimic, iar valoarea tensiunii de la fotodioda total luminată poate fi folosită pentru a scala informațiile primite în intervalul corect de valori. Acest procedeu, poate fi de asemenea utilizat pentru a detecta luminozitatea led-urilor stabilind daca acestea sunt vizibile sau nu receptorilor.

Cantitatea de umbră care cade pe fotodiode este proporțională cu poziția pe orizontală și verticală într-un mod liniar. Dacă luăm valoarea preluată de la fotodioda din dreapta a detectorului X corespunzătoare intensității luminoase aplicate și scădem valoarea preluată de la fotodioda din stânga corespunzătoare intensității luminoase, vom obține o valoare intre -1 și 1. Aceasta ne va spune cât de departe sunt led-urile înspre stânga. ( valorile cu “-“ indică o poziție a mănușii spre dreapta turnului) .

Dacă luăm valoarea preluată de la fotodioda din partea de jos a detectorului Y și scădem din ea valoarea preluată de la fotodioda din partea de sus, vom obține tot o valoare între -1 și 1. Această valoare ne spune cât de departe sunt led-urile înspre partea de sus ( valorile cu “ – ” indica o poziție a mănușii înspre partea de jos a turnului ).

Coordonatele X și Y reale sunt proporționale cu distanța la care este plasată mănușa. De exemplu, în cazul în care distanța dintre led și turn este de 30 cm pe axa Z, atunci vom înmulți valorile orizontale și verticale obținute cu 30 cm pentru a obține coordonatele X și Y.

Avem câte o pereche de valori pentru poziția pe orizontală și verticală pentru fiecare panou de senzori. Aceste valori sunt calculate pentru toate cele 8 led-uri de pe mănușa de date, dar doar valorile de la cele mai luminoase patru led-uri sunt trimise la calculator pentru procesare.

Fiecare cadru trimis la calculator prin intermediul interfeței USB/HID este un raport de tipul Report 1 de lungime 23 de octeți.

Octetul 0 reprezintă numărul raportului ( Report Number) care va fi 1 pentru a arăta că trimitem date de tipul Report 1.

Primii șase biți ( biții 7-2 ai octetului 1) reprezintă informațiile de la senzorul de flexare a degetului arătător. Intervalul de valori în care această informație este cuprinsă este 0 și 63 (valoarea efectivă depinzând de modul în care mănușa a fost calibrată ). Următorii șase biți ( biții 1 și 0 ai octetului 1 și apoi biții 7 pana la 4 aparținând octetului 2) corespund informației primite despre gradul de flexare a degetului mijlociu. În mod analog sunt primite informațiile corespunzătoare pentru restul degetelor în ordinea inelar, degetul mic și degetul mare.

Următorii doi biți sunt ignorați.

Următorii patru biți (biții de la 7 la 4 din octetul 5 ) corespund informațiilor de la cele patru butoane de pe mănușa.

După aceste date , în continuare se trimit patru biți ( biții de la 3 la 0 din octetul 5) care precizează care led prezintă intensitatea luminoasă cea mai mare. Aceasta va fi o valoare cuprinsă intre 0 și 7 sau daca led-urile nu sunt vizibile ea va fi 15 ( -1) .

În următorii patru biți este precizat care led este al doilea ca nivel al intensității luminoase. După aceea în mod analog vor fi trimise și informațiile despre al treilea și al patrulea led ca intensitate luminoasa.

Led-urile sunt numerotate la nivelul mănușii ca în imagine:

Figura 2

După aceea urmează valorile corespunzătoare poziției pe orizontală și verticală a celor mai luminoase patru led-uri.

Acestea sunt numere întregi reprezentate în complement față de doi ( valorile negative sunt reprezentate standard în complement fata de 2) .Intervalul în care sunt cuprinse valorile este -512 și 511. Pentru a converti aceste valori în numere zecimale cuprinse intre -1 și 1 ele vor fi fiecare împărțite cu 512.

Primele trei valori corespund celui mai luminos led, următoarele trei corespund celui de-al doilea led ca intensitate luminoasă, după aceea trei valori pentru al treilea și al patrulea led, în ordinea luminozității.

Prima din cele 3 valori corespunzătoare fiecărui led este valoarea pe verticală de la primul panou de senzori ( din partea de sus a turnului) . Numere pozitive înseamnă că mănușa e în partea de sus a panoului iar numere negative că aceasta este în partea de jos.

A doua din cele trei valori pentru fiecare led este valoarea pe verticală de la cel de-al doilea panou de senzori ( din partea de jos a turnului ).

Cea de-a treia valoare din serie este valoarea pe orizontală ( de la primul panou de senzori ). Valoarea pe orizontală a celui de-al doilea panou, ar trebui să fie identică cu cea de la panoul 1 , deci nu mai este inclusă.

Aceasta este toată informația pe care turnul de recepție o trimite la calculator.

Decodarea informațiilor

Acum, că avem toate trei valorile corespunzătoare fiecărui LED, putem calcula coordonatele x, y, z ale acestora. Din păcate, nu putem folosi valorile pe care le primim deoarece panourile de senzori nu sunt de fapt poziționate vertical.

Panourile de senzori aflate în partea de sus și jos a turnului de recepție sunt rotite precum în figură, pentru a da turnului un aspect curbat. Aceasta înseamnă că valorile verticale pe care le avem vor trebui corectate în jos cu 10 o pentru panoul 1 de senzori ( partea de sus ) și în sus cu 17 o pentru panoul de senzori din partea de jos.

Pentru a putea face asta, este necesar să convertim toate valorile pe care le avem în valori unghiulare corespunzătoare.

Folosindu-ne de câteva formule trigonometrice fundamentale rezultă:

Observăm că valoarea maximă pe care o putem obține pentru unghiul θ este de 45 o .

În continuare, ținând seama de faptul că valori pozitive înseamnă în sus, vom adăuga 17 o la unghiul pe verticală pentru panoul de senzori doi (partea de jos ) și vom scădea 10 o din valoarea verticală a unghiului de la panoul 1 ( din partea de sus ).

În acest moment mai avem doar o singură problemă. Mai există o mică distorsiune în valorile primite de la turnul de recepție introdusa de filtrele și lentilele care sunt deasupra fotodiodelor. Acest unghi este foarte mic și îl adăugam la valoarea unghiurilor noastre. Aceste valori de compensare se pot citi de la turnul de recepție prin utilizarea funcției GetFeature usage 7 . Aceasta va returna un raport semnat Report 6.

32 de unități din octeții în care sunt precizate unghiurile reprezintă echivalentul a 1o.

Biții sunt configurați în modul următor:

Primul octet al raportului 6, octetul 0 este 6 ( numărul raportului ).

Ceilalți octeți ai raportului conțin valori întregi cuprinse intre -128 și 127

Octetul 1 conține valoarea unghiului vertical de la panoul 1 reprezentata în 1o/32.

Octetul 2 conține valoarea unghiului orizontal de la panoul 1 reprezentata în 1o/32.

Octetul 3 conține valoarea unghiului vertical de la panoul 2 reprezentata în 1o/32.

Octetul 4 conține valoarea unghiului orizontal de la panoul 2 reprezentata în 1o/32 (această valoare nu este folosita)

Octetul 5 conține distanța dintre panoul 1 și 2 reprezentata în zecimi de centimetru (inch).

Valorile de compensare citite de la receptorul (turnul de recepție) utilizat de noi sunt:

Se adaugă 2.1875 o la unghiul vertical de la panoul 1

Se scad 1.5 o din valoarea unghiului orizontal de la panoul 1

Se adaugă 0 o la valoarea unghiului vertical de la panoul 2

Distanta dintre cele doua panouri de senzori (1 și 2) este de 206.6 zecimi de centimetru, adică 20,66 centimetri

Deci, odată ce adăugam aceste unghiuri la unghiurile obținute de noi, vom avea valorile corecte a unghiurilor pe orizontală și verticală a ambelor panouri de senzori.

După cum probabil a fost observat până acum, deocamdată ne lipsesc valorile corespunzătoare axei Z care ne spun cât de departe este led-ul de receptor. Acesta este motivul principal pentru care avem două panouri de senzori la o distanța așa mare unul de altul. Cu alte câteva formule trigonometrice foarte simple putem afla valoarea corespunzătoare axei Z și, folosind aceasta putem găsi coordonatele X și Y.

Figura 4

Din ecuațiile scrise mai sus putem observa că distanța Z= distanța dintre Panouri (- tan (unghiul vertical al panoului 1) + tan (unghiul vertical al panoului 2) )

Distanta Panouri este distanța pe verticală dintre cele doua panouri de senzori care se găsesc în turnul de recepție. Valoarea variabilei Distanța Panouri o putem obține din informațiile trimise de la turn în raportul ce conține valorile tan de compensare (Report 6) sau se poate folosi distanța standard din specificații, adică 7.9 inch . 7.9 inch înseamnă 20.0660 cm sau 200.66 mm sau 0.658333333 feet (picioare) sau 404.479980 unități P5. 51.2 unități P5 reprezintă un inch (2.54 cm). Valorile recepționate sunt reprezentate în unități P5.

Motivul pentru care un inch este reprezentat că 51.2 unități P5 este că receptorul are o rezoluție maxima la o distanța de 25.4 cm (10 inch ). Ne vom aminti că valorile erau inițial cuprinse în intervalul -512 și 511. Deci, la o distanța de 10 inch aceasta reprezintă intervalul de la -10 inch la +10 inch. Deci, la o distanță de 10 inch de receptor, acuratețea maxima pe care o putem avea este a 51.2-a parte dintr-un inch.

Există două posibilități pentru distanța Y. Una este posibilitatea de a obține distanța de la panoul 2 (cel din partea de jos) sau distanța de la panoul 1 (din partea de sus). În continuare, vom folosi distanța obținuta de la panoul 2.

Distanta Y (de la panoul 2) =Distanta Z *

sau

Distanta Y (de la panoul 1)= distanța Z*

Aparent, distanța X este:

Distanta X= distanța Z * tan (unghiul orizontal)

Problema care apare în presupunerea anterioară este că noi măsuram distanța pe orizontală din planul orizontal XZ, dar unghiul orizontal care este format la nivelul panoului 1 (care măsoară unghiul orizontal ) nu este în planul XZ, ci este în planul care este incident panoului 1 la un unghi egal cu valoarea unghiului vertical al panoului 1. Acesta înseamnă că distanța Z și valorile unghiurilor nu corespund fidel cu ceea ce ne dorim.

Soluția la aceasta problemă este:

Acum avem distanța X, distanța Y, și distanța Z relative la panoul 2. Dar o valoare pozitivă pentru X înseamnă tot în stânga turnului, și o valoare pozitivă pentru Z este înspre utilizator. Driverul inițial returna coordonatele mănușii în spațiul pentru mana stângă, adică valorile pozitive corespunzătoare axei X erau în partea dreaptă iar valorile pozitive corespunzătoare axei Z erau înspre turn (exact că în mediul Direct 3D). Deci pentru a obține valorile în același sistem de coordonate că și driver-ul inițial, trebuie să negăm valorile X și Z (si să ne asigurăm că ele sunt reprezentate în a 51.2-a parte dintr-un inch).

Motoare servo

Motoare și mecanisme servo

Termenul de servo face referire la o metodă de control printr-un mecanism de reacție folosit pentru detecția erorilor folosită pentru corectarea performanței unui sistem. Motoarele servo sau servo RC (Radio Controlled) sunt un anumit tip de motoare echipate cu un mecanism servo pentru un control precis al poziției unghiulare. Motoarele servo au de obicei o limită de rotație de până la 90 ° la 180 °. Unele motoare servo au limita de rotație stabilită la 360 ° sau chiar mai mult. Motoarele servo nu se rotesc continuu, rotația fiind limitată între unghiuri fixe.

Utilizarea servo motoarelor

Motoarele servo sunt folosite azi într-o larga varietate de roboti, mașini și aplicații robotice generalizate, incluzând: brațe și picioare robotice, mașini, elicoptere și avioane controlate prin unde radio, mașini industriale și multe alte aplicații. Există un număr mare de motive care fac ca motoarele servo să fie atât de răspândite. Printre acestea se numără : ușurința controlului acestora, puterea necesară mică (eficiență mare) , cuplu dezvoltat mare , controlul se face cu tensiuni TTL, și chiar anumite avantaje fizice precum dimensiunea și greutatea redusă a acestora.

Moduri de conectare a servo motoarelor

Servo motoarele existente pe piață au ca și modalitate de conectare trei cabluri . Două dintre acestea sunt folosite pentru alimentarea motorului DC (direct curent, curent continuu). Cel de-al treilea fir este folosit pentru transmiterea semnalului de comandă la motor. Cablurile conectate la servomotor sunt codate cu diferite culori. Firul roșu este folosit pentru alimentarea motorului DC și trebuie să fie conectat la borna pozitivă a unei surse de tensiune de curent continuu . Valoarea tensiunii de alimentare trebuie să fie între 4.8 și 6 volți ( în funcție de producător ). Prin firul negru al motorului se face conexiunea la masă. Culoarea celui de-al treilea cablu (cu rolul de a furniza semnalul de control ) variază în funcție de producător. El poate fi de culoare galbenă (in cazul Hitec) albă ( în cazul Futaba ) , maro , etc.

Spre deosebire de motoarele în curent continuu, prin inversarea conexiunilor de alimentare ( pozitivă și masă ) nu se va schimba direcția de rotație a servo motorului. Aceasta poate duce chiar la distrugerea acestuia.

Controlul servo motoarelor

Unghiul la care este orientat axul unui servo motor poate fi modificat prin trimiterea unui puls modulat în lățime ( PWM ) pe canalul folosit pentru trimiterea semnalului de control. Un puls cu lățime variabila între 1 și 2 milisecunde este trimis în mod repetat la servo motor cu o frecventa minimă de 50 Hz. Valoarea lățimii pulsului determină poziția unghiulară a axului.

De exemplu, un puls cu o lățime de 1 milisecundă va determina deplasarea axului la un unghi de  0 ° , în timp ce o creștere a lățimii pulsului la o valoare de 2 milisecunde va determina o deplasare la 180 °. Pentru valori intermediare a poziției unghiulare a axului se vor trimite impulsuri de lățime corespunzătoare. Astfel, un puls de lățime 1.5 milisecunde va determina o rotire a axului pană la un unghi de 90 °.

Trebuie ținut cont că aceste valori sunt aproximative. Comportamentul real al servo motorului diferă în funcție de producător și este descris în fișa tehnică.

Este necesară trimiterea unei secvențe de impulsuri la servo motor cu o perioada de 50 Hz pentru menținerea unei anumite poziții unghiulare. Atunci când motorul servo primește un impuls, el este capabil să mențină poziția unghiulară (specificata de lățimea pulsului ) pentru următoarele 20 de milisecunde. Deci un puls trebuie trimis la servo motor cel puțin o dată la fiecare 20 de milisecunde.

Figura 5 Forma unui impuls și efectele lui

Un servo motor este în principal alcătuit dintr-un motor de curent continuu, un sistem de transmisie format din roti dințate, un senzor de poziție care este materializat printr-un potențiometru și partea electronică de control care de obicei consta într-un circuit integrat.

In figura de mai jos observam toate componentele unui servo motor:

Figura 6 Componentele unui servo motor

După cum se observă din figura de mai sus, motorul de curent continuu este direct conectat cu un mecanism de transmisie care oferă informații de poziție unui senzor. Acest senzor este de cele mai multe ori un potențiometru. Din sistemul de transmisie , ieșirea din servo motor se face prin axul acestuia pe brațul servo. Potențiometrul își modifică poziția corespunzător cu poziția curentă a motorului. Așadar, o schimbare în valoarea curentă a rezistenței potențiometrului va produce o schimbare echivalentă a tensiunii de pe potențiometru. Lățimea pulsului este convertită intr-o tensiune echivalentă care va fi comparată cu tensiunea semnalului de pe potențiometru într-un amplificator. Semnalul rezultat la ieșirea amplificatorului va reprezenta diferența dintre cele doua semnale (eroarea).
Acest rezultat va fi amplificat și va fi oferit motorului de curent continuu. Deci, tensiunea aplicată pe motorul de curent continuu va reprezenta o tensiune de compensare între poziția dorită a motorului și poziția actuală a acestuia. Aceasta tensiune va fi cu atât mai mică (ideal 0 ) cu cât poziția actuală a motorului se apropie cât mai mult de poziția dorită.

Diagrama unui sistem servo motor poate fi observata în figura următoare:

Figura 7 Diagrama sistem servo motor

În cazul în care diferența dintre poziția dorită ( indicată în trenul de impulsuri ) și poziția actuală este mare, motorul se mișcă repede. Atunci când aceasta este mică, motorul se va mișca mai lent. Trenul de impulsuri necesar pentru controlul servo motorului poate fi generat de un circuit integrat de tip temporizator sau un microcontroler poate fi programat pentru generarea formei de undă dorite.

Surse de alimentare pentru servo motoare

Servo motoarele necesită o tensiune de alimentare cuprinsă între 4.8 și 6 volți. Pentru un anumit tip de servo motor, tensiunea de alimentare este dată ca o specificație în foaia de catalog. Alimentarea în curent continuu poate fi obținută prin utilizarea bateriilor sau a unui regulator de tensiune. Tensiunea bateriei trebuie să fie apropiată de tensiunea de funcționare a servo motorului, acest fapt având ca efect reducerea pierderii de energie prin radiație termică.

Alegerea unui servo motor

De obicei, selectarea unui anumit servo motor depinde de parametri precum: cuplu, viteză, greutate, dimensiuni, tipul motorului și tipul sistemului de transmisie. Tipul motorului poate fi de 3 poli sau de 5 poli. Numărul de poli face referire la numărul de magneți permanenți care sunt atașați electromagneților. Servo motoarele cu 5 poli sunt mai bune decât motoarele cu 3 poli deoarece oferă un cuplu mai bun.

Servo motoarele sunt fabricate cu diferite valori ale cuplului și a vitezei de rotație. Cuplul reprezintă forța aplicată de motor pentru a deplasa brațul servo. Viteza este măsura care dă o estimare despre cât de repede un servo motor atinge poziția dorită. Un anumit fabricant poate face un compromis între cuplu și viteză, putând să aleagă pentru un anumit tip de servo motor un cuplu mai mare-viteză mai mică ( sau invers) în funcție de circumstanțe. Sunt de preferat motoarele cu un cuplu cât mai mare.

Greutatea și dimensiunea sunt direct proporționale cu cuplul dezvoltat. Evident, un servo motor cu un cuplu mai mare va avea, la rândul lui o dimensiune și greutate mai mare. Alegerea unui servo motor se poate face în funcție de cuplu și viteza corespunzătoare cerințelor aplicației în care acesta va fi folosit. Greutatea și dimensiunea pot de asemenea fi argumente decisive în aplicații care constă în construirea unui elicopter sau a unui avion controlat prin radio în care se vor folosi servo motoare. Pentru obținerea mai multor detalii pentru anumite modele se pot consulta paginile web ale producătorilor. Unii dintre aceștia, precum Futaba, oferă utilizatorului posibilitatea folosirii unui calculator online pentru alegerea tipului de servo motor specific aplicației.

Interferențe și semnal de zgomot

Semnalul PWM este livrat servo motorului prin firul de semnal. Semnalele de zgomot și de interferență provenite de la componentele electronice înconjurătoare pot cauza erori de poziție. Pentru a elimina această problemă, semnalele de control sunt furnizate după amplificarea acestora. Acest lucru va suprima zgomotul și semnalele de interferență.

Modificarea servo motoarelor pentru obținerea unei rotații complete

Un utilizator poate dori să folosească un servo motor pentru aplicații robotice în care este necesară rotirea completă a axului acestuia. Acest avantaj poate fi obținut printr-o mică modificare. Sistemul de transmisie al servo motorului are un element mecanic ( element de stopare ) care împiedică acesta să efectueze o rotație completă. Dacă acest mecanism de stopare se înlătura, sistemul de transmisie va fi liber să efectueze o rotație completa a axului.

Situația este exemplificată în figura de mai jos:

Figura 8 Mecanism de stopare în sistemul de transmisie

Înlăturarea acestui mecanism de stopare nu este însă suficientă pentru efectuarea unei rotații complete. Servo motorul funcționează pe baza unui mecanism cu reacție. Deci este nevoie ca inițial potențiometrul să fie poziționat în centru. Aceasta poziționare poate fi obținută prin transmiterea unui anumit tren de impulsuri de la microcontroler. După efectuarea acestui pas, trebuie fixate rotițele de pe potențiometru cu lipici și astfel, servo motorul va funcționa în funcție de poziția de mijloc și nu în funcție de poziția actuală.

Modulația pulsurilor în durată – PWM

Modulația pulsurilor în durată (pulse-width modulation , PWM ) este o tehnică folosită frecvent pentru controlul dispozitivelor electrice inerțiale . Acest fenomen este obținut practic prin utilizarea comutatoarelor electronice de putere.

Valoarea medie a tensiunii (si curentului ) debitată pe sarcină este controlată prin închiderea și deschiderea comutatoarelor între aceasta și alimentare cu o frecvență foarte mare. Cu cât timpul în care comutatorul este în starea on e mai mare decât cel în care este off, cu atât va fi mai mare puterea debitată pe sarcină.

Frecvența de comutare a PWM-ului trebuie să fie mult mai mare decât una care ar putea afecta sarcina respectivă ( aici prin sarcina se înțelege dispozitivul în sine care consumă putere). Uzual, frecvența comutărilor trebuie să fie de câteva ori pe minut pentru o plita electrică, de 120 Hz într-un dispozitiv pentru reglarea intensității luminoase, de la câțiva kilohertzi (kHz) pana la zeci de kHz pentru controlul unui motor și până la sute de kilohertzi în amplificatoarele audio și sursele de alimentare a calculatoarelor.

Termenul de “ciclu activ” (duty cycle) descrie raportul dintre timpul în care un comutator este on (conectat la sursă) și acesta este off dintr-o perioadă. Un ciclu activ mic corespunde unei puteri mici, deoarece timpul în care comutatorul este on, este mai mic decât cel în care este off . Acesta este reprezentat în procente, 100% însemnând că acel comutator este tot timpul dintr-o perioadă în starea on.

Principalul avantaj al utilizării PWM-ului este că puterea pierdută pe comutatoare la schimbarea stărilor acestora este foarte mică. Atunci când un comutator este off, practic curentul prin aceste poate fi considerat zero iar când acesta este on, tensiunea care apare pe comutator este atât de mică încât este neglijabilă. Puterea disipată pe comutator, fiind produsul dintre tensiune și curent , tinde la zero.

Principiu de funcționare

Modulația pulsurilor în durată folosește un semnal dreptunghiular a cărui lățime este modulată ca rezultat al variației valorii medii a undei. Considerând o forma de unda  f(t) cu o valoare minima  ymin, , o valoare maximă  ymax și un ciclu activ D, valoarea medie a undei este data de formula:

Forma de undă a semnalului va arăta că în figura de mai jos:

Figura 9

Cum f(t) este un semnal dreptunghiular periodic, valoarea lui este  ymax pentru intervalul și respectiv  ymin pentru intervalul  . Ținând cont și de aceste considerente, formula de mai sus va deveni :

În cele mai multe cazuri, expresia de mai sus poate fi simplificată foarte mult deoarece, în majoritatea cazurilor  ymin = 0  și deci . Din acestea putem observa că valoarea medie a semnalului  ()  este direct proporțională cu cilul activ ( duty cycle ) D.

Cel mai ușor mod de generare a semnalelor PWM este prin metoda intersecției unde este nevoie doar de un comparator și de un semnal de tip triunghiular sau dinte de fierăstrău (ușor de generat folosind doar un oscilator ) . Atunci când valoarea semnalului de referință ( semnalul sinus din figura de mai jos) este mai mare decât valoarea semnalului modulator ( reprezentat în figură cu albastru ), semnalul PWM este în starea high (nivel logic 1) , pe când atunci când valoarea semnalului de referință este mai mică decât cea a semnalului modulator, semnalul PWM va fi în starea low ( nivel logic 0).

Figura 10 O simplă metodă pentru generarea unui tren de pulsuri PWM corespunzătoare unui semnal dat constând în metoda intersecției: semnalul (sinusoidal cu roșu) este comparat cu un semnal dinte de fierăstrău (cu albastru).Când semnalul dinte de fierăstrău e mai mic decât cel sinusoidal, nivelul semnalului PWM va fi 1 , altfel nivelul acestuia e 0.

Delta

În utilizarea modulației delta pentru controlul PWM , semnalul de ieșire este integrat și rezultatul este comparat cu limite prestabilite, care corespund cu un semnal de referință defazat cu o constantă. De fiecare dată când valoarea integralei semnalului de ieșire atinge una din aceste limite, semnalul PWM își va complementa starea. Situația este prezentată în figura de mai jos :

Delta Sigma

În modulația delta-sigma ca o metodă de control a semnalului PWM, semnalul de ieșire este scăzut dintr-un semnal referință pentru a forma un semnal eroare. Când acest semnal este integrat și când valoarea integralei semnalului eroare depășește limitele, starea ieșirii se complementează. Situația este prezentată în figura de mai jos:

Figura 11

Transferul puterii

Modulația duratei pulsurilor poate fi utilizată pentru a regla cantitatea totala a energiei livrate unei sarcini fără a avea în vedere pierderile survenite în mod normal la transferul de energie prin mijloace rezistive. Dezavantajele sunt definirea duratei pulsațiilor prin reglarea ciclului activ (duty cycle ), frecvența de comutație și proprietățile sarcinii. Prin utilizarea unei frecvențe de comutație suficient de mari și atunci când este necesar , a filtrelor pasive, trenul de impulsuri poate fi distorsionat pentru recuperarea în medie a formei de undă analogice.

Sistemele PWM de control de înaltă frecvență sunt ușor realizabile cu comutatoare din semiconductori. Așa cum s-a arătat anterior, puterea disipată pe comutatoare în starea on și off este neglijabilă. Cu toate acestea, în timpul tranzițiilor între stările complet on și complet off, atât tensiunea cât și curentul sunt diferite de zero, acest fapt ducând la apariția unor pierderi considerabile pe comutatoare. Din fericire, aceste tranziții au durata (uzual mai mică de 100 nanosecunde ) mult mai mică decât durata în care comutatoarele sunt on sau off, deci puterea medie disipată este mult mai mică în comparație cu puterea livrata sarcinii chiar și atunci când sunt folosite circuite de comutație de înalta frecventa.

Comutatoarele moderne din semiconductoare precum MOSFET sau tranzistoare bipolare cu poarta izolată ( Insulated-gate bipolar transistors IGBT ) sunt componente care se apropie foarte mult de comutatoare ideale. Astfel pot fi construite dispozitive de control cu randament ridicat. De obicei, convertoarele de frecvență utilizate pentru controlul motoarelor AC, au un randament mai mare de 98%. Sursele de alimentare comutate au o eficiență mai scăzută datorată nivelului mic al tensiunii din ieșire ( de multe ori este necesară o tensiune mai mică de 2 volți pentru microprocesoare ), dar și acestea pot atinge o eficiență mai mare de 70 -80 %.

Dispozitivele de control folosite pentru a varia viteza de rotație a ventilatorului de la calculatoare utilizează de obicei PWM, deoarece acesta este mult mai eficient fata de un potențiometru sau un reostat. ( Nu este practic controlul electronic al nici unuia dintre cele două, ar mai fi necesar un mic motor pentru acestea ).

Variatoarele de lumină pentru uz casnic folosesc un anumit tip de control PWM. Acestea de obicei includ un circuit electronic pentru suprimarea curentului de-a lungul unor porțiuni definite pe fiecare ciclu al tensiunii alternative. Ajustarea intensității luminoase emise de o sursă este o chestiune care se reduce la setarea unei anumite tensiuni în jumătatea de ciclu a curentului alternativ la care variatorul începe să debiteze curent electric sursei de lumină. În acest caz, ciclul activ PWM este raportul dintre timpul de conducție și durata unei jumătăți de perioadă a curentului alternativ definită de frecvența de funcționare a rețelei de tensiune alternativă ( 50 sau 60 Hz depinzând de țară).

Microcontrolerul MFC5213 ColdFire

Caracteristici MFC5213 ColdFire

Familia de microcontrolere MFC5213 este membră a familiei ColdFire de microprocesoare RISC ( Reduced Instruction Set Computing ).

Acest dispozitiv care funcționează pe 32 de biți este bazat pe versiunea a 2- a nucleului ColdFire care operează la o frecvență maximă de 80 MHz, oferind performanțe ridicate și consum mic de energie. Memoria disponibilă include pana la 256 Kocteti de memorie flash și 32 Kocteti de memorie SRAM ( Static Random Access Memory ). Modulele prezente pe chip includ:

Versiunea 2-a a nucleului ColdFire care executa 76 Milioane de Instrucțiuni Pe Secunda ( MIPS ) la o frecventa de 80 MHz rulând din memoria Flash cu unitatea MAC ( Multiply Accumulate ) și divizor hardware

Modul FlexCAN controller area network ( CAN )

Trei porturi UART ( Universal asynchronous/synchronous receiver/ transmitters)

Modul interfață magistrală circuit Inter-integrat (I2C)

Modul queued serial peripheral interface (QSPI)

Convertor Analog Digital cu 8 canale de 12 biți

Controler pentru acces direct la memorie ( DMA ) cu 4 canale

4 temporizatoare cu posibilitatea comparării valorilor pe 32 biți care pot fi folosite pentru captura intrării /ieșire ( DTIM)

Temporizator pentru uz general ( GPT General Purpose Timer ) cu patru canale utilizabil pentru comparări captura intrării / ieșire, modularea pulsurilor în durată ( PWM ) , și acumularea pulsurilor

Temporizator pentru modularea lățimii pulsurilor cu 8 canale/ 4 canale, pe 8 biți/ 16 biți

Două temporizatoare pe 16 biți pentru întreruperi periodice ( PIT )

Modul temporizator de tip watchdog programabil software

Controler de întreruperi capabil să se descurce cu 52 de surse

Port pentru acces în modul test/ depanare ( JTAG, BDM)

În figura următoare este prezentată diagrama bloc a dispozitivului :

Figura 12

Deși microcontrolerul MFC5213 dispune de multe opțiuni și avantaje, vom discuta doar despre acelea care ne interesează în mod direct pentru proiectul de față. Acestea sunt porturile UART, temporizatorul pentru utilizări generale ( GPT ) și modulele temporizator pentru modularea lățimii pulsurilor.

Porturile UART

Dispozitivul are trei porturi UART full-duplex care funcționează independent. Cele trei porturi pot fi cronometrate cu semnalul de ceas al magistralei, eliminând astfel necesitatea utilizării unui semnal de ceas extern. Pentru pachete de dimensiune mai mică, cel de-al treilea port UART este multiplexat cu funcții de intrare/ieșire.

Temporizatorul pentru uz general ( GPT )

Temporizatorul pentru uz general constă dintr-un modul temporizator programabil de 16 biți cu patru canale dirijat de un scalor programabil de 7 etape .Fiecare din cele patru canale poate fi folosit pentru captarea datelor de intrare sau compararea ieșirii. În plus, canalul trei poate fi configurat să funcționeze că un acumulator de puls. O funcție pentru depășirea temporizatorului permite software-ului extinderea capacității de cronometrare a sistemului dincolo de intervalul de 16 biți ai contorului.

Funcțiile pentru captura intrării și compararea ieșirii permit măsurarea unor forme de undă de intrare și simultan generarea unor forme de undă la ieșire. Funcția de captură a intrării poate face captura în momentul unei tranziții. Funcția de comparare a ieșirii poate genera forme de undă la ieșire și întârzierile software pentru temporizator. Acumulatorul pe 16 biți poate funcționa ca un contor de evenimente sau că un acumulator dependent de timp.

Descrierea funcțională a regiștrilor pentru PWM

Regiștrii folosiți pentru modularea lățimii pulsurilor sunt descriși în tabelul de mai jos:

PWM Clock Select

Există patru semnale de ceas disponibile – ceas A, B, să ( A Scalat ), și SB ( B Scalat ) – toate fiind bazate pe ceasul principal al sistemului.

Semnalele de ceas A și B pot fi programate să ruleze la 1, 1/ 2, … ,1/128 din perioada ceasului principal al sistemului. Semnalele de ceas SA și SB folosesc ceasul A și B ca intrări și le divizează pe acestea mai departe cu ajutorul unui contor reîncărcabil. Frecvențele la care ceasurile SA și SB funcționează sunt frecvențele ceasurilor A/ B, împărțite la 2, 4, …, 512. Fiecare canal PWM are capabilitatea de a selecta unul din cele două grupuri de ceasuri, prescalat ( A sau B ) sau scalat ( SA sau SB ). Diagrama bloc din figură prezintă cele patru ceasuri diferite și modul în care semnalele scalate de ceas sunt obținute.

Semnalele de ceas prescalate ( A sau B )

Semnalul de ceas principal al sistemului este considerat intrare pentru prescalorul PWM care poate fi dezactivat atunci când dispozitivul este în modul de depanare prin setarea bitului corespunzător al registrului PWMCTL[PFRZ]. Acest procedeu este util pentru reducerea puterii consumate și pentru a putea ̋ îngheța ̋ semnalul PWM din ieșire intr-o anumită stare în modul de depanare. Semnalul de ceas principal al sistemului nu mai este considerat intrare atunci când toate canalele PWM sunt oprite ( PWMEn=0 )

Semnalele de ceas A sau B sunt de fapt semnale obținute din semnalul de ceas principal, divizat cu o anumită valoare. Valoarea cu care dorim să divizăm semnalul principal de ceas va fi 1, 1/ 2, … , 1/128. Valoarea selectată pentru semnalele de ceas A și B este determinată de biții PWMPRCLK [ PCKAn ] și respectiv PWMPRCLKB [PCKBn].

Semnalele de ceas scalate ( SA sau SB )

Semnalul de ceas scalat SA sau SB folosesc ca intrări semnalele de ceas A respectiv B, și le divid în continuare cu o valoare programabilă de utilizator, și după aceea împart totul la 2. Frecvența disponibilă pentru semnalul de ceas SA poate fi selectată ca fiind frecvența de funcționare a semnalului de ceas A împărțită la 2, 4, … , 512. Analog se desfășoară pentru semnalul de ceas scalat SB.

Semnalul de ceas scalat SA este egal cu semnalul de ceas A împărțit la doi ori valoarea din registrul PWMSCLA:

În mod similar, semnalul de ceas SB este generat după următoarea ecuație:

Ca exemplu, să considerăm cazul în care utilizatorul scrie valoarea 0xFF în registrul PWMSCLA. În aceste condiții, semnal de ceas A este selectat că fiind semnalul de ceas principal divizat cu 4. Un puls este generat cu o frecvență de o dată la 255*4 perioade ale semnalului principal. Trecând acest semnal prin două circuite divizoare, avem un semnal de ceas obținut din semnalul principal de ceas divizat cu 2040. În mod similar, o valoare de 0x01 în registrul PWMSCLA înseamnă selectarea semnalului de ceas A ca semnal intern de ceas, acesta fiind obținut din semnalul intern principal de ceas, divizat de 8 ori.

Scrierea unor valori în regiștrii PWMSCLA sau PWNSCLB va avea că și consecința reîncărcarea contoarelor pe 8 biți asociate .

Scrierea valorilor în regiștrii de scalare cât timp canalele sunt operaționale, poate cauza neregularități în semnalele PWM din ieșire.

Alegerea semnalului de ceas

Fiecare canal PWM prezintă posibilitatea selectării unuia din cele două semnale de ceas disponibile. Pentru canalele 0, 1, 4 și 5 alegerea se poate face dintre semnalele de ceas A și SA. Pentru canalele 2, 3, 6, și 7 se poate alege unul din semnalele de ceas B sau SB. Selecția semnalului de ceas se face prin setarea biților de control ai registrului PWMCLK [ PCLKx ].

Modificarea valorilor biților de control când canalele sunt operaționale poate produce neregularități în forma de undă de la ieșire.

Temporizatoare pentru canalele PWM

Partea cea mai esențiala a modulului PWM sunt temporizatoarele în sine. Fiecare canal al temporizatorului are un numărător, un registru perioadă ( registru pentru stabilirea valorii perioadei ) și un registru ciclu activ ( pentru stabilirea valorii ciclului activ ( duty cycle)). Acești regiștrii au dimensiunea de opt biți. Perioada formei de undă din ieșire este controlată de momentul în care valoarea din numărător este egală cu cea din registru perioadă. Ciclul activ este controlat prin momentul în care valoarea din numărător este egală cu cea din registru ciclu activ, ceea ce permite ca starea formei de undă din ieșire să se modifice de-a lungul unei perioade. Polaritatea de început a semnalului de ieșire este programabilă pentru fiecare canal în parte.

În figură este prezentată o diagrama bloc pentru un temporizator PWM:

Figura 13

Autorizarea PWM

Fiecare canal PWM are un bit de autorizare ( PWMEn) folosit pentru afișarea formei de undă la ieșire. Când oricare din biții PWMEn sunt setați, semnalul asociat undei PWM din ieșire este autorizat instantaneu dar el nu va apărea la ieșire decât la următoare perioadă a semnalului de ceas; aceasta se întâmplă din cauza procesului de sincronizare dintre semnalul PWM și semnalul de ceas. O excepție apare atunci când canalele sunt concatenate.

În prima perioada de după autorizarea canalului, forma semnalului de ieșire poate fi neregulată. Atunci când un canal nu este autorizat, numărătorul acelui canal nu numără.

Polaritatea semnalului PWM

Fiecare canal are cate un bit polaritate care stabilește în prima perioada a semnalului de ieșire daca acesta începe din starea “high” sau “low”. Acest lucru este implementat în diagrama bloc printr-un multiplexor. Atunci când unul din biții registrului PWMPOL este setat, forma de undă corespunzătoare canalului respectiv pleacă din starea inițiala high, iar după ce se ajunge la valoarea stabilita în registrul ciclu activ, aceasta își va schimba starea în “low”. În mod analog, atunci când bitul de polaritate este “0” forma de undă va începe din starea “low“ și ea va trece în starea “high” atunci când se va ajunge la valoarea salvată în registrul ciclu activ.

Perioada și ciclul activ a semnalului PWM

Exista regiștrii pentru stabilirea perioadei și regiștrii pentru setarea ciclului activ separat pentru fiecare canal. Valoarea acestor regiștrii este trecută printr-un registru tampon de lungime doua cicluri de ceas, astfel încât dacă aceste valori se modifică atunci când canalul respectiv este autorizat, schimbarea nu se observă până când apare unul din următoarele cazuri:

Perioada respectivă se termină

Este scrisă o valoare în registrul PWMCNT ( numărătorul se resetează la 0x00)

Canalul nu este autorizat, PWMEn=0

In acest mod, forma de undă a semnalului PWM de la ieșire este fosta formă de undă sau o formă de undă nouă, nu o combinație intermediară dintre cele două. Dacă canalul nu este autorizat, valorile scrise în regiștrii perioadă și ciclu activ sunt transferate direct în latch-uri și în registru tampon.

O schimbare instantanee a formei de undă poate fi forțată prin setarea valorilor regiștrilor perioada și ciclu dar și scrierea unei valori în numărător. Acest fapt va duce la resetarea numărătorului și scrierea în latch-uri a noii valori a ciclului activ si/sau a perioadei. În plus, din cauză că valoarea numărătorului poate fi citită, este posibil să aflăm unde este numărătorul față de ciclul activ și este posibilă ajustarea valorilor.

Depinzând de valoarea bitului de polaritate, registrul ciclu activ conține intervalul de timp corespunzător stărilor “high” sau “low”.

Numărătoarele temporizatoarelor PWM

Fiecare canal are cate un numărător dedicat pe 8 biți bidirecțional (acesta poate număra crescător sau descrescător ) care funcționează la frecvența semnalului de ceas selectat. Valoarea acestui numărător este comparată cu valorile din regiștrii perioadă și ciclu activ. Atunci când valoarea din numărătorul PWM este egală cu cea din registru ciclu activ, starea bistabilului din ieșire se schimbă, ducând și la schimbarea stării formei de undă PWM de la ieșire. O egalitate între valoarea numărătorului și valoarea din registrul perioadă duce la o comportare diferită, aceasta depinzând de modul de ieșire selectat ( Ieșiri Aliniate la Stânga sau Ieșiri Aliniate Central ).

Valoarea oricărui numărător al unui canal poate fi citită la orice moment de timp fără a afecta numărarea sau modul de funcționare al canalului PWM.

Scrierea oricărei valori în numărător va duce la resetarea acestuia la valoarea 0x00, modul de numărare va fi setat crescător, la încărcarea regiștrilor perioadă și ciclu activ cu valorile din registru tampon , și modificarea stării semnalului de ieșire în funcție de valoarea bitului de polaritate. Atunci când canalul nu este autorizat ( PWMEn=0), numărătorul se oprește . Când un canal devine autorizat, ( PWMEn=1), numărătorul asociat va continua să numere de la valoarea înscrisă în registrul PWMCNTn. Acest fapt permite formei de undă de la ieșire să continue de unde a rămas atunci când canalul este autorizat din nou. Atunci când canalul nu este autorizat, scrierea valorii 0 în registrul perioadă va cauza resetarea numărătorului la următorul front activ de ceas.

Daca utilizatorul dorește să înceapă o nouă formă de unda PWM, neafectată de valorile precedente, acesta trebuie mai întâi să scrie o valoare în registrul corespunzător numărătorului asociat canalului respectiv ( PWMCNTn ) înainte de a autoriza canalul respectiv (PWMEn=1)

Uzual, procedurile de setare a valorii numărătorului sunt făcute înainte de autorizarea canalului respectiv. Totuși scrierea unei valori într-un numărător poate fi făcută și când respectivul canal PWM este autorizat (numărătorul va număra). Efectul acestui fapt este similar cu scrierea unei valori în numărător atunci când canalul nu este autorizat, exceptând doar faptul că o nouă perioadă a semnalului va fi începe imediat, cu semnalul în starea inițială corespunzătoare valorii din bitul polaritate. Inscripționarea unei valori în numărător în timp ce canalul este autorizat, poate duce la apariția unei perioade a formei de unda PWM neregulată la ieșire.

Numărătorul este resetat la sfârșitul fiecărei perioade efective. În tabelul de mai jos este precizat când numărătorul este în una din cele trei stări posibile, adică:

Ieșiri aliniate la stânga

Temporizatoarele PWM oferă posibilitatea alegerii între doua modalități de reprezentare a datelor la ieșire: aliniate la stânga sau aliniate în centru. Selectarea uneia din cele două se face prin setarea corespunzătoare a valorilor biților registrului PWMCAE [CAEn]. Dacă bitul CAEn este setat “0”, ieșirea va fi una de tip aliniată la stânga.

În modul ieșire aliniată la stânga, numărătorul pe 8 biți este configurat să numere doar crescător (în sus). Valoarea din numărător va fi comparată cu valoarea din cei doi regiștrii ( registrul perioadă și registrul ciclu activ ). Atunci când valoarea numărătorului PWM este egală cu valoarea inscripționată în registrul ciclu activ, bistabilul din ieșire își schimbă starea, aceasta ducând la o schimbare a stării semnalului din ieșire. O egalitate dintre valoarea corespunzătoare numărătorului și valoarea inscripționată în registrul perioadă, resetează numărătorul , făcând ca bistabilul din ieșire sa-și schimbe starea dar și efectuează o încărcare a valorilor din registrul tampon (de dimensiune doi) perioadă și ciclu activ în regiștrii corespunzători. Numărătorul PWM va număra de la 0 până la valoarea înscrisa în registrul perioadă minus 1.

Modificarea tipului ieșirii de la aliniată la stânga la aliniată la centru ( sau invers ) poate cauza neregularități în forma de undă PWM de la ieșire. Este recomandat să setam tipul ieșirii înainte să autorizam canalul respectiv.

Figura 14

Pentru a calcula frecventa de ieșire a semnalului cu tipul ieșirii pentru un anumit canal aliniată la stânga, vom lua frecvența semnalului de ceas sursă ( A, B, SA, SB ) și o vom împărți cu valoarea din registrul perioadă a canalului respectiv:

Ciclul activ ( procentul de timp dintre cât este semnalul în starea “high “ și durata unei perioade) este exprimat ca:

Exemplu pentru ieșirea aliniată la stânga

Pentru a lua un scurt exemplu vom considera cazul următor:

Semnalul de ceas sursă = semnalul de ceas principal cu frecvența 40 MHz (o perioadă de 25 ns)

PPOLn= 0, PWMPERn = 4, PWMDTYn = 1

Frecvența PWMn = 40 MHz/4 = 10MHz

Perioada PWMn= 100ns

Forma de undă generată la ieșire este:

Figura 15

Ieșiri aliniate în centru

Pentru selecția tipului ieșirilor că fiind aliniate în centru, trebuie setat bitul corespunzător din registrul PWMCAE [CAEn]si ieșirea respectivă va fi aliniată în centru.

În acest caz numărătorul pe 8 biți funcționează ca numărător în sens crescător/descrescător și el este setat ca numărător crescător ( în sus) atunci când valoarea acestuia va fi 0x00. Valoarea numărătorului va fi comparată cu valoarea din cei doi regiștrii. Atunci când valoarea numărătorului va fi egală cu valoarea din registrul ciclu activ,bistabilul din ieșire își va schimba starea, cauzând astfel și schimbarea stării semnalului PWM din ieșire. O egalitate între valoarea numărătorului și a registrului perioadă va schimba modul de numărare al acestuia de la numărare în sus la numărare în jos .

Comunicația serială – standardul RS232

Transmisia serială – date, semnale si temporizări

Transmisia digitală de date a evoluat de la conexiunea între un calculator cu echipamentele periferice, la calculatoare care comunică în rețele internaționale complexe. Mai sunt însă multe de învățat de la simpla legătură punct la punct, sau RS232 după standardul EIA. Cu toate că transferul paralel este mai rapid, majoritatea transmisiilor de date între calculatoare sunt făcute pe cale seriala pentru a reduce costul cablului si conectorilor. Există si limitări fizice de distanță, care nu pot fi depășite de magistrale paralele. În comunicația serială, datele sunt transmise bit cu bit. Toate comunicațiile sunt caracterizate de trei elemente principale:

Date – înțelegerea lor, scheme de codificare, cantitate

Temporizări – sincronizarea intre receptor si emițător, frecvența si faza

Semnale – tratarea erorilor, controlul fluxului si rutare

Sincronizare – frecvența si faza

Este necesar un mecanism care să permită receptorului să citească corect bitul curent de intrare la jumătatea duratei lui. Receptorul trebuie să știe durata unui bit si de unde începe bitul respectiv, adică trebuie să cunoască frecvența si faza secvenței de date. Dacă emițătorul și receptorul au același semnal de tact, sincronizarea este perfectă; emițătorul scrie bitul pe frontul crescător al tactului, iar receptorul citește bitul pe frontul coborâtor al tactului. Problemele apar când receptorul si emițătorul nu au un semnal de tact comun. Dacă duratele celor două semnale de tact, pentru emițător si receptor, nu sunt egale, apare o decalare, care după un anumit număr de biți rezultă intr-o eroare. Pentru a evita aceasta, receptorul trebuie resincronizat regulat la nivel de bit. Din alte motive, trebuie resincronizate și începutul unui caracter, pachet sau mesaj. În figura de mai jos, în primul caz, fiecare bit este citit la mijlocul duratei lui, iar in cazul al doilea, bitul 4 se pierde deoarece tactul receptorului este prea încet.

Figura 16

Dacă emițătorul și receptorul au același semnal de tact atunci se spune ca lucrează in mod Sincron. Altfel, daca au semnale de tact separate, atunci lucrează în mod Asincron.

În modul Asincron, emițătorul nu trimite un tact deodată cu datele, ci inserează un pseudo-impuls de tact, cunoscut ca Bit de Start, în fața fiecărui octet transmis. Astfel, pentru fiecare caracter ASCII avem o transmisie independentă, cu adăugarea biților de Start, Stop și Paritate. Viteza de lucru se stabilește manual la începutul transmisiei. Pentru informația de fază, receptorul trebuie sa detecteze începutul bitului de Start. Pentru ca această metodă să funcționeze, trebuie să existe, o perioadă de pauză intre caractere, realizată cu bitul de Stop.

În modul Sincron, caracterele sunt transmise rapid, unul după altul, fără biți de Start și de Stop. Pentru sincronizare, mesajul transmis este precedat de caractere speciale de sincronizare, detectabile de circuitul receptorului. Acestea sunt transmise încontinuu și când nu sunt date de transmis. Transmisiile in mod sincron pot folosi scheme inteligente de modulare, care se bazează pe circuite suplimentare, iar semnalele de date și tact folosesc aceeași pereche de fire. Această metoda, cunoscută sub numele de codificare Manchester, este folosita în rețelele Ethernet.

O metodă sincronă alternativă este folosită pentru transmisii seriale rapide non-caracter, orientate pe bit. Protocoale care folosesc această metodă permit transferul de date la viteze mari. Un astfel de protocol este și protocolul HLDC.

Codificarea datelor si controlul erorilor

Erorile pot apărea când circuitele folosite pentru conexiune sunt afectate de zgomot (interferențe electrice) cum ar fi: lămpi fluorescente, comutarea unor motoare mari, etc. Aceste vârfuri sunt induse in firele de comunicație care se comportă ca niște antene. Deoarece tensiunile cu care se lucrează in calculatoare sunt mici, efectul generat de acest zgomot este important. Circuitele respective trebuie să fie imune la aceste zgomote.

Canalele moderne de comunicație sunt din ce in ce mai fiabile. Metodele de detecție și corecție a erorilor se îndreaptă spre domeniile CD-ROM-urilor si DVD-urilor. Toate aceste metode implică introducerea de informație neesențială, pe lângă datele utile, în transmisia datelor. Există mai multe metode care merită să fie studiate:

Biți de paritate – simplu de aplicat , nu oferă siguranță mare

Sume de control la nivel de bloc – simplu de aplicat , nu ajută prea mult

Împărțire polinomială – mai complicat de calculat, oferă securitate

De exemplu, receptorul trimite înapoi o copie a datei primite. Acest mecanism înjumătățește lățimea de banda folosită. O alternativa ar fi ca emițătorul să trimită data urmată de o copie a acesteia.

Toate metodele de tratare a erorilor folosesc informație redundantă. De cele mai multe ori, aceste informații sunt codificate înainte de transmisie.

Paritatea este cea mai discutata metoda de detecție a erorilor pentru protecția transmisiilor seriale de caractere ASCII. La oricare din metode, emițătorul prelucrează o parte din date și generează un fel de semnătura pe care apoi o transmite împreună cu date utile. Când mesajul ajunge la receptor, acesta prelucrează datele primite si generează o semnătură pe care o compară cu cea primită. Dacă cele doua semnături nu coincid, atunci s-a produs o eroare. Metoda bitului de paritate se poate aplica pentru date binare de orice lungime. Pentru fiecare cuvânt este adăugat un bit de paritate (semnătura). Paritatea poate fi pară (cuvântul conține un număr par de 1) sau impară (cuvântul conține un număr impar de 1). Calcularea parității se poate face cu operatorul XOR (SAU Exclusiv) între biții cuvântului. Prin această metodă este posibila doar detecția erorii singulare, când sunt afectați un număr impar de biți. O eroare dublă (afectează un număr par de biți) nu poate fi detectată prin acest mecanism. Prin urmare, această metodă nu oferă prea multă securitate. Un singur bit de paritate nu oferă informații despre poziția erorii.

Codurile Hamming reprezintă o alta metodă care permite și localizarea erorii prin adăugarea a mai mult de un bit de paritate după biții utili. Este astfel posibilă detecția si corecția erorii. Problema este "unde sunt poziționați biții de paritate intre biții utili ?" Răspuns: pentru biții de paritate se alocă pozițiile care sunt puteri ale lui 2 in cuvântul dat. De exemplu, pentru 4 biți utili avem următoarea așezare:

Figura 7

Unde D1, D2, D3, D4 sunt biți utili și P1, P2, P3, și P4 sunt biți de paritate. Cu formula se poate calcula numărul de biți utili (d ) acoperiți de un număr ( p) de biți de paritate.

Suma de control la nivel de bloc este alt mecanism de detecție a erorilor de transmisie. Prima dată este necesar ca datele să fie împărțite in blocuri, care apoi se însumează și se obține o sumă care va fi trunchiată, inversată și adăugată la sfârșit. La recepție, blocurile primite, care includ si suma de la sfârșit, se adună pe măsură ce sosesc, și dacă suma obținuta nu este 0 atunci înseamnă ca datele sunt eronate și secvența trebuie retransmisă. Nu este posibilă corecția erorii. Pentru identificarea erorilor multiple si corecția lor, s-a dezvoltat mecanismul BCH, dar acesta nu va fi prezentat.

O altă metodă de detecție a erorilor este CRC (Cyclic Redundant Check). Și în acest caz se calculează o sumă de control, dar prin împărțire aritmetică. Secvența de biți este împărțita cu un număr special ales. Împărțirea se face in modulo 2, adică folosind operatorul XOR. Restul împărțirii reprezintă semnătura care va fi adăugată la sfârșit, după biții utili. Divizorul se obține cu algoritmul folosit la codurile Hamming. La recepție, se recalculează restul împărțirii și dacă nu coincide cu cel primit, atunci secvența este eronată.

Performanțele acestei metode sunt impresionante. Un CRC care generează un rest de 16 biți poate detecta:

toate erorile in rafală de maxim 16 biți

toate numerele impare de biți din eroare

99.998 % din toate erorile de orice lungime

CRC-ul se poate calcula mai ușor prin metode hardware, folosind registre cu deplasare și porți logice XOR.

Controlul fluxului de date – metode hardware si software

Controlul fluxului de date este necesar pentru a preveni erorile de depășire, când receptorul nu poate prelucra datele care vin cu viteză prea mare. La început, datele sunt recepționate corect, dar nu pot fi prelucrate cu viteza cu care alte date sosesc și astfel se umple buffer-ul de recepție, rezultând o eroare de depășire. În acest caz, datele vechi din buffer vor fi înlocuite de date noi, înainte de a fi prelucrate. Pentru a evita această eroare, receptorul trebuie să poată să-i ceară emițătorului să oprească transmisia până când sunt prelucrate toate datele din buffer-ul de recepție (golire). Această facilitate se numește controlul fluxului de date si este un element esențial pentru toate legăturile de comunicație.

Mecanismul poate fi implementat în mai multe moduri:

Cu ecou – receptorul trimite înapoi la emițător fiecare caracter primit

Hardware – folosind liniile de control RTS/CTS (RequestToSend/ClearToSend)

Software – prin coduri de control ^S/^Q (Stop/Continuare)

La nivel de cadru – prin codurile ACK/NAK (Acknowledge/Negativeacknowledge)

Circuitul UART 16550 – Standardul RS232

La nivelul hardware cel mai de jos al unei comunicații seriale, cel mai folosit este standardul RS232 sau V24. Acesta presupune un conector D cu 25 sau 9 pini, la care de cele mai multe ori sunt legate doar 3 fire. Un bit de 1 logic este transmis ca aproximativ -9 volți, iar un bit de 0 logic ca +9V.

În standardul RS232 datele se transmit in următorul format:

Figura 8

Modul in care un octet furnizat de către utilizator este serializat și trimis pe linia TxD, respectiv recepționat pe linia RxD este invizibil utilizatorului. Aceste operațiuni sunt executate la nivel hardware de portul serial implementat cu circuitul UART 16550 (Recepție si Transmisie Universala în mod Asincron). Toate calculatoarele oferă porturi seriale pentru conectare de modem sau alte echipamente UART. Acest circuit face legătura intre magistrala de date paralela, din interiorul calculatorului, si linia seriala din exterior. Pe lângă liniile de date și control, circuitul oferă și linii de semnalizare a erorilor de cadrare, depășire, paritate și semnale de întrerupere pentru a anunța procesorul de sosirea unei date. Porturile seriale pot fi configurate la nivel utilizator prin interfețe grafice oferite de sistemul de operare, care permit stabilirea parametrilor unei comunicații seriale: viteza de lucru, numărul de biți de date, tipul de paritate, modul de control al fluxului de date.

Cupla seriala cu 9 pini (partea de la calculator) are următoarea configurație:

Acest tip de comunicație este folosit pentru a transmite un semnal digital de la un calculator la un modem, care folosind mai departe alte standarde de comunicație, transmite semnalul sub forma analogica, pe linia de telefon, spre un alt modem legat la alt calculator. Computerul se numește DTE (Data Terminal Equipment), iar modem-ul DCE (Data Communications Equipment). În figura următoare este ilustrat rolul pe care-l are legătura RS232 in comunicațiile de date.

Având astfel toate aceste informații la dispoziție ne mai rămâne doar să facem legătura intre ele. Pașii urmați pentru aceasta sunt precizați în următorul capitol.

Partea practică

În această parte a lucrării , după prezentarea cunoștințelor teoretice necesare, vom dezvolta o aplicație pe calculator care sa poată prelua coordonatele in spațiu a mănușii și gradul de flexare a degetelor si cu aceste informații să controleze mișcările unui braț robotic.

Pentru a obține rezultatul dorit , adică aplicația în sine care funcționează cum ne-am propus, va fi necesară parcurgerea unor etape prestabilite. Vom preciza acele etape, exact in ordinea parcursă și după aceea vom indica la ce este fiecare din aceste etape folositoare.

În ordine, etapele parcurse sunt:

Preluarea și afișarea datelor de la mănușa de date

Analizarea și interpretarea datelor pe calculator

Alegerea motoarelor pentru brațul robotic

Modalitatea de transmitere a datelor de la calculator la motoare

Construcția brațului robotic

Înainte de a putea să parcurgem oricare dintre etapele prezentate anterior trebuie luată o hotărâre referitoare la instrumentul software folosit pentru dezvoltarea aplicației pe calculator.

După analizare mai multor posibilități, soluția la care s-a recurs a fost programul LabWindows/CVI de la firma National Instruments. Acesta a fost ales după anumite criterii , care vor rezulta din scurta descriere făcută mai jos.

Mediul LabWindows/CVI este o platformă pentru dezvoltare de software bazat pe o interfață grafică orientat spre aplicațiile de instrumentație. A fost creat în sprijinul programatorilor specializați în C. Ne pune la dispoziție un mediu interactiv de dezvoltare a unor aplicații care au toate elementele clasice Windows, îmbinând deci avantajele programării de tip vizual cu avantajele simplității și flexibilității limbajului C. Deși a fost creat special pentru aplicații de instrumentație și control, multiplele lui facilități îl fac capabil să suporte și dezvoltarea unor aplicații care să fie de altă factură. Limbajul acceptat este ANSI C cu extensii specifice. Fiecare funcție de bibliotecă din cadrul acestui mediu are o interfață specializată numită panoul funcției (function panel) care permite:

Ajutor on-line;

Execuție interactivă;

Generare automată de cod.

Mediul este extrem de flexibil, permițând interfațarea bidirecțională cu alte compilatoare de C sau C++. De asemenea acceptă sau generează DLL-uri.

Caracteristicile principale sunt date de bibliotecile puse la dispoziție de către mediu, acestea conținând tot ce este necesar implementării diverselor faze care conduc la o aplicație din domeniul achiziției de date sau a controlului automat.

Biblioteci specializate pentru achiziții și schimburi de date:

pentru instrumentele ce sunt interfațate prin GPIB/GPIB 488.2

pentru achiziție de date

pentru operații I/O în cazul achiziției de date

pentru portul serial RS232

pentru plăcile ISA și sistemul VXI

Biblioteci specializate pentru analiza datelor reprezentate sub formă vectorială sau nu, complexă sau reală, matricială, funcții de analiză statistică, procesări numerice de semnal, interpolări etc. De fapt, aceste biblioteci împreună cu modul simplu și intuitiv de generare a unei aplicații constituie forța și puterea acestui mediu de dezvoltare.

Deci, programul a fost ales in special pentru acceptarea DLL-urilor și pentru bibliotecile specializate disponibile pentru operații cu portul serial RS232. Ajungem astfel la prima etapă, adică preluarea datelor de la mănușa de date.

1.Preluarea și afișarea datelor de la mănușa de date

La finalul acestei etape vom avea informațiile corespunzătoare poziției în spațiu a mănușii și a nivelului de flexare a degetelor. A fost precizat modul în care mănușa trimite date la calculator. Este trimisă poziția celor mai luminoase patru led-uri vizibile și cu aceasta se calculează poziția în spațiu a mănușii. Pentru interfațarea mănușii cu sistemul de operare Microsoft Windows a fost creat de producător un „driver”. Acesta este un program software care asigură compatibilitatea dintre instrumentul hardware ( în cazul nostru turnul cu mănușa) și sistemul de operare ( în acest caz, Microsoft Windows ). Un fișier cu extensia .DLL este de fapt o librărie de date sau de funcții executabile ce pot fi folosite de o aplicație Windows sau de un alt DLL. Avantajul major este ca un fișier DLL poate fi folosit de mai multe aplicații în același timp.

Legarea funcțiilor și a datelor la cele corespunzătoare din bibliotecile dinamice ( DLL) se rezolvă prin legarea acestora la o librărie de import în momentul construcției (building ) sau legării ( linking ) care apar în procesul creării unui fișier executabil (.EXE). Astfel, fișierul executabil creat va conține o tabelă de adrese pentru import (import address table , IAT ) prin care sunt referite toate funcțiile din DLL existente ( fiecărei funcții din DLL referită îi corespunde o intrare proprie in IAT). În momentul rulării, IAT este umplută cu adresele corespunzătoare care indică înspre funcții din DLL (care a fost încărcat separat ). Ca și bibliotecile statice, bibliotecile de import pentru DLL-uri sunt deosebite prin expresia .LIB. De exemplu, DLL-ul pentru funcțiile de bază ale sistemului de operare Microsoft Windows ( precum crearea fișierelor și gestionarea memoriei ), este legat prin kernel32.lib

Având aceste informații, trebuie să aflăm ce funcții sunt disponibile in fișierul DLL pentru mănușă și să ne hotărâm cum le vom folosi. Primul lucru car îl vom face va fi să punem fișierul DLL disponibil pentru mănușă în directorul system32 din Windows. Așa vom ști că acesta va fi încărcat odată ce este încărcat și sistemul de operare. După cum am spus și în paragrafele anterioare vom mai avea nevoie și de fișierul .LIB pe care va trebui să-l includem în proiectul nostru. Observăm că pe lângă fișierul .LIB mai avem și un fișier .H. Acesta este un fișier header și aici sunt definite prototipurile funcțiilor astfel încât din informațiile oferite de acest fișier vom ști cum să apelăm funcțiile care ne interesează. Conținutul acestui fișier header este prezentat în anexa 1.A . Primul lucru pe care trebuie să îl facem este să inițializăm mănușa din aplicația creată de noi. Codul sursă corespunzător aplicației dezvoltate în mediul LabWindowsCVI poate fi găsit în anexa 1.B. În prima parte, odată cu încărcarea programului , vom inițializa mănușa cu funcția P5_Init().

initValue=P5_Init();

p5ID=gloveInfo.DeviceID;

P5_SetFilterMode(P5_FILTER_AVERAGE);

P5_SetFilterAmount(10, 0);

P5_SetRequiredAccuracy(10);

p5State=P5_GetStatePointer(p5ID);

gloveInfo este o instanță a structurii de date P5Info. Aceasta este prezentată în anexa 1.A. În această structură avem câmpuri precum numărul de identificare al producătorului, a mănușii, al dispozitivului, poziția fizică a led-urilor , valorile de compensare ( prezentate în partea teoretică ), și multe altele.

După inițializarea mănușii, vom obține și reține in același timp codul cu care vom identifica mănușa în continuare, numărul de identificare al dispozitivului. Acesta este necesar deoarece fișierul DLL a fost creat pentru a putea funcționa cu două mănuși (pentru ambele mâini ) și astfel majoritatea funcțiilor iau ca unul din parametrii această valoare. Deci, vom stoca această valoare în variabila p5ID :

p5ID=gloveInfo.DeviceID;

Următoarele linii de cod vor specifica:

a)modul de filtrare a informațiilor ( să fie mediu ) P5_SetFilterMode(P5_FILTER_AVERAGE);

b)cantitatea de cadre care să fie filtrate P5_SetFilterAmount(10, 0);

c) precizia informațiilor P5_SetRequiredAccuracy(10);

Funcția P5_SetFilterAmount() ia ca unul din parametrii numărul de cadre . Astfel valorile calculate ( pentru rotație ) vor fi de fapt media valorilor din numărul de cadre specificat ca parametru.

Funcția P5_SetRequiredAccuracy() ia ca parametru precizia dorită pentru a seta cât de exactă trebuie să fie poziția led-urilor pentru a se calcula o nouă valoare a rotației. Valori mai mici ale parametrului luat de funcție indică actualizări ale valorii de rotație mai rare, și se vor utiliza doar valori exacte, pe când valori mai mari înseamnă că valoarea de rotație va fi actualizată mai des ( ceea ce elimină problema ̎ înghețării ̎ datelor ) dar va oferi și valori ale rotației care vor fi greșite.

După cum reiese din fișierul P5DLLc.h din anexa 1.A, avem două structuri de date disponibile in care sunt păstrate informații despre mănușă. Acestea sunt P5Info si P5State. Din P5Info an obținut numărul de identificare al dispozitivului. Structura P5State conține mai multe câmpuri, dar cele care ne interesează pe noi sunt FilterPos(3) , Finger(5), FilterYaw, FilterPitch, FilterRoll .

FilterPos(3) este un vector de trei elemente de tip float. De aici vom lua poziția în spațiu.

Finger(5) este un vector de trei elemente în care se găsesc informații despre gradul de flexare a fiecărui deget de la mână.

FilterYaw, FilterPitch, FilterRoll sunt câmpuri a căror valori indică rotația filtrată în jurul celor trei axe de coordonate în spațiu (X, Y, Z). O figură care exemplifică semnificația diferitelor valori este următoarea:

Figura 9 Valorile pytch, yaw, roll

Interfața cu utilizatorul este formată din două panouri. Pe primul panou vor fi afișate poziția în spațiu a mănușii ( valori X, Y, Z), precum și butoane de control. O captură a primului panou este prezentată în imaginea următoare:

Figura 10

În continuare va fi prezentat exact procedeul prin care au fost preluate datele și afișate în panou.

După cum am precizat în pagina anterioară, informațiile corespunzătoare poziției în spațiu a mănușii sunt stocate în structura P5State. Astfel, ce trebuie sa facem mai întâi este să obținem un pointer la acea structură pentru a putea obține informații din ea. Strategia de a avea un pointer la structură a fost aleasă deoarece deși informațiile din structură ( precum poziția în spațiu) se vor modifica frecvent, noi vom avea acces la cele mai actuale deoarece vom accesa prin intermediul pointer-ului locațiile de memorie corespunzătoare la care acestea sunt stocate. Deci, prima dată obținem un pointer la structura respectivă:

static P5State * p5State;

p5State=P5_GetStatePointer (p5ID);

Funcția P5_GetStatePointer(p5ID) va lua ca parametru numărul de identificare al dispozitivului ( pe care l-am obținut mai devreme ) și va returna un pointer la acea structură.

După ce variabila p5State este setată, vom putea avea acces la informațiile din structura respectivă. Mai jos arătat cum au fost extrase informațiile despre poziția în spațiu:

xPos= (*p5State).FilterPos[0];

yPos= (*p5State).FilterPos[1];

zPos= (*p5State).FilterPos[2];

Până în acest moment, avem informațiile despre poziția în spațiu a mănușii. Mai observăm pe panoul prezentat cele două temporizatoare. Unul se numește Serial Send Timer ( Temporizator 1) iar celălalt Record Timer ( Temporizator 2). Valoarea setată pentru Temporizator 1 și 2 este de 100 ms.

Aceasta înseamnă ca atunci când primul și/ sau cel de-al doilea sunt active, la trecerea a 100 de milisecunde va fi generată o întrerupere cu elementul de identificare EVENT_TIMER_TICK și se va executa codul din rutina de tratare a acelui tip de întrerupere, după care temporizatorul se va reseta și va reîncepe încă odată să numere.

După cum am spus înainte, avem două temporizatoare. Prima oara vom prezenta rolul și modalitatea de implementare a temporizatorului Serial Send Timer.

Serial Send Timer

Rolul principal al acestui temporizator este acela de a stabili rata de reîmprospătare pentru informațiile obținute de la mănușă. După cum se observă în liniile de cod următoare, la fiecare 100 de milisecunde , valorile corespunzătoare gradului de flexare a degetelor vor fi transferate în variabilele : thumb, index, middle, ring, pinky . După aceea se face o verificare care va indica care panou este cel activ ( cel cu care utilizatorul interacționează ). Dacă acest panou este panoul principal (1, prezentat în această secțiune ) atunci vom extrage coordonatele corespunzătoare poziției în spațiu în variabilele xPos , yPos, zPos. Aceste valori le vom afișa pe panou, utilizatorului. Valorile xPos, yPos, zPos vor mai trebui prelucrate înainte de a fi trimise mai departe ( despre prelucrarea acestora se va discuta în secțiunea următoare ) .

De asemenea, tot în continuare, vom verifica dacă utilizatorul a apăsat butonul de înregistrare (record ). Dacă da, atunci odată cu trimiterea datelor mai departe prin portul serial le vom și stoca în trei vectori corespunzători (xPosVect, yPosVect, zPosVect). Dimensiunea maximă aleasă pentru cei trei vectori este de 1000. Aceasta corespunde unui timp de înregistrare de 1 minut si 40 de secunde, suficient pentru a demonstra funcționalitatea prototipului. De asemenea, utilizatorului i-ar putea fi oferită opțiunea de a introduce el un timp maxim de înregistrare.

case EVENT_TIMER_TICK:

thumb=(float)(*p5State).finger[0];

index=(float)(*p5State).finger[1];

middle=(float)(*p5State).finger[2];

ring=(float)(*p5State).finger[3];

pinky=(float)(*p5State).finger[4];

if(panelHandle==PANEL){

xPos=(*p5State).FilterPos[0];

yPos=(*p5State).FilterPos[1];

zPos=(*p5State).FilterPos[2];

fingerAverage=(thumb+index+middle+ring+pinky)/5;

SetCtrlVal(panelHandle,PANEL_COLORNUM,xPos);

SetCtrlVal(panelHandle,PANEL_COLORNUM_2,yPos);

SetCtrlVal(panelHandle,PANEL_COLORNUM_3,zPos);

if(recStatus==1)

{

xPosVect[vectSize]=xRs;

yPosVect[vectSize]=yRs;

zPosVect[vectSize]=zRs;

vectSize+=1;

}

}

else if(panelHandle==PANEL_2)

{

În această porțiune de cod vom afișa valorile corespunzătoare gradului de flexare a degetelor , rotației mâinii precum și valorile de compensare. Codul este prezentat în anexa 1.B

}

Al doilea temporizator se numește Record Timer.

Record Timer

Rolul principal al acestui temporizator este de a trimite informațiile ( gesturile ) înregistrate de către utilizator la mănușă. Pentru acesta a fost ales un interval de timp tot de 100 milisecunde , similar cu intervalul de timp al primului temporizator, pentru imitarea exactă în timp a mișcărilor. Dacă, de exemplu, am dori ca brațul să execute aceleași gesturi ca cele înregistrate , dar efectuate mai rapid, vom seta o valoare mai mica pentru cel de-al doilea temporizator.

În continuare vom prezenta rolul diferitelor butoane de pe panou și modalitatea de implementare a acestora. În ordinea importanței ca funcționalitate, butoanele sunt:

Configurare Port Serial

Rol:

de a configura portul serial. După cum a fost prezentat in partea teoretică ( Comunicația serială) , parametrii principali ai portului serial care trebuie setați pentru transferul de date sunt: viteza de modulație ( este rata cu care se modifică stările electrice ale modemului într-o secundă exprimată in baud) , paritatea (0 – fără paritate, 1-paritate impară, 2-paritate pară, etc.) , numărul de biți de date pentru portul respectiv ( 5, 6, 7, 8), numărul de biți de stop ( 1 sau 2), dimensiunea cozii de intrare pentru portul selectat , dimensiunea cozii de ieșire.

Acest buton va fi singurul activ atunci când programul va fi rulat, împiedicând astfel utilizatorul să apese alte butoane. A fost adoptată această abordare deoarece parametrii comunicației prin portul serial trebuie setați înainte de a face orice altceva.

Modalitatea de implementare:

if ( OpenComConfig (1, "", 2400, 0, 8, 1, 512, 512)>=0)

EnablePanelControls(0);

În funcția de mai sus, testăm succesul funcției care deschide portul serial cu parametrii stabiliți de noi și dacă aceasta a reușit, apelăm funcția EnablePanelControls. Rolul acesteia este de a face accesibile utilizatorului și celelalte butoane.

Start Captură Date

Rol:

Acest buton are rolul de a activa temporizatorul 1. Ideea pe care a fost creat acest buton este că atunci când utilizatorul deschide programul să nu fie trimise dintr-o dată informații la brațul robotic, ci acesta să poată fi controlat la un moment de timp ulterior rulării programului, ales de utilizator prin apăsarea butonului.

Modalitatea de implementare:

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,1);

Cu funcția de mai sus, setăm atributul componentei PANEL_TIMER (respectiv timer-ul 1) , aparținând panoului panelHandle ca fiind 1 ( adică îl activăm).

Start Înregistrare

Rol:

Butonul Start Înregistrare are rolul de a oferi utilizatorului posibilitatea înregistrării informațiilor primite de la mănușă (a gesturilor) . După cum am spus și mai devreme, înregistrarea informațiilor este făcută în rutina de tratare a întreruperii generată de temporizatorul 1, adică cu o perioadă de 100 de milisecunde. Astfel, atunci când utilizatorul va apăsa acest buton, va fi setată valoarea unei variabile ( recStatus ) la 1, iar când va fi generată o întrerupere de temporizator, valoarea variabilei recStatus va fi 1 și coordonatele calculate se vor adăuga și în vectorii xPosVect , yPosVect .

Implementare:

Pentru parcurgerea vectorului și pentru adăugarea noilor poziții după cele vechi ( în vector ), avem nevoie de o variabilă care să ne ofere informații despre unde ne aflăm în vector cu prelucrarea informațiilor (scriere, dimensiunea actuală a vectorului). Această variabilă în programul nostru se va numi vectSize. Atunci când butonul Start Înregistrare va fi apăsat, pe lângă setarea valorii variabilei recStatus la 1, vom stabili și valoarea variabilei vectSize ca fiind 0 . Aceasta ne va permite ca la prima întrerupere generată de temporizatorul 1 să adăugăm coordonatele la începutul vectorilor corespunzători, după care, la fiecare întrerupere, variabila vectSize va fi incrementată,actualizându-se dimensiunea celor trei vectori, oferindu-ne posibilitatea de a adăuga coordonate în continuarea celor vechi .

Codul din interiorul funcției care se va executa la apăsarea butonului Start Înregistrare este:

recStatus=1;

vectSize=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

Primele două linii au fost discutate. În cea de-a treia linie de cod, setăm temporizatorul 2 să fie oprit. După cum s-a explicat în paragraful Record Timer , temporizatorul 2 este responsabil cu trimitea datelor din vectorii care conțin înregistrările coordonatelor. Acesta este oprit deoarece dacă înregistrăm, înseamnă că nu avem coordonate în cei trei vectori pe care să le trimitem, și oricum , temporizatorul 1 fiind pornit și variabila recStatus având valoarea 1, coordonatele curente de la mănușă sunt și trimise mai departe prin portul serial și stocate în vectori.

Stop Înregistrare

Rol:

Butonul Stop Înregistrare are rolul de a opri procesul de înregistrare declanșat de utilizator prin apăsarea butonului Start Înregistrare. De fapt, apăsând acest buton, utilizatorul oprește funcționarea ambelor temporizatoare.

Implementare:

Ținând cont că transmiterea informațiilor mai departe prin portul serial ( și a celor înregistrate, și a celor actuale ) se face cu ajutorul temporizatoarelor, în rutina de tratare a întreruperilor generate de acestea, tot ce trebuie să facem atunci când utilizatorul apasă butonul este să oprim funcționarea celor doua temporizatoare, și să setăm valoarea variabilei recStatus la 0 (deși aceasta nu este necesar deoarece, oprind temporizatoarele, nu se vor mai executa liniile de cod din rutina de tratare a întreruperilor, unde se verifică valoarea variabilei recStatus ). Atribuim valoarea 0 variabilei recStatus deoarece în cod, dacă vom avea nevoie undeva de valoarea acestei variabile, ea să fie 0 dacă utilizatorul a apăsat butonul Stop Înregistrare.

În continuare sunt prezentate liniile de cod care au ca și consecință: atribuirea valorii 0 variabilei recStatus, oprirea funcționării temporizatorului 1 și respectiv oprirea funcționării temporizatorului 2.

recStatus=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,0);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

Redare Miscare

Rol:

Butonul Redare Miscare are rolul de a opri captura datelor de la mănușă și de a trimite mai departe prin portul serial datele stocate în cei trei vectori.

Implementare:

După cum am spus, dorim să trimitem datele înregistrate mai departe prin portul serial. Știm că în variabila vectSize ( din paragraful Start Înregistrare ) vom avea dimensiunea actuală a vectorilor care conțin coordonatele înregistrate .Știind astfel dimensiunea vectorilor, mai avem nevoie doar de o variabilă cu care să îi parcurgem. Pentru aceasta vom folosi variabila vectIterator, căreia îi vom atribui valoarea 0 în momentul apăsării butonului. Deci, acum avem o variabilă inițializată cu care vom parcurge vectorii de coordonate, mai avem nevoie să și trimitem acele coordonate periodic, și în consecință, vom activa temporizatorul 2, acela responsabil cu trimiterea coordonatelor, și vom opri funcționarea temporizatorului 1. După cum a fost explicat în paragraful Record Timer, temporizatorul 2 este responsabil cu trimiterea datelor înregistrate din vectorii de coordonate. Ca și temporizatorul 1, la trecerea a 100 de milisecunde, dacă temporizatorul 2 este activ, acesta va genera o întrerupere și se vor executa liniile de cod plasate în rutina de tratare a acelei întreruperi. Deci, la fiecare 100 de milisecunde, vom trimite un set de coordonate din vectori și vom incrementa variabila vectIterator care este folosită pe post de contor.

Următoarele linii de cod se execută la apăsare butonului Redare Miscare și în ele este prezentată inițializarea variabilei vectIterator, oprirea temporizatorului 1, și pornirea temporizatorului 2 :

vectIterator=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,0);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,1);

P5 Mouse State

Rol:

Butonul P5 Mouse State oferă utilizatorului posibilitatea de a folosi mănușa pentru a controla cursorul de pe ecran. Aceasta prezintă avantajul de a naviga printre programele deschise și ferestrele existente prin intermediul gesturilor.

Implementare:

Pentru a ne atinge scopul, constând în implementarea ideii prezentate mai sus, va trebui să folosim două funcții pe care producătorii le-au pus la dispoziție în fișierul DLL. Una este P5_GetMouseState(p5ID), iar cealaltă este P5_SetMouseState(p5ID, mouseState). Prima funcție ( P5_GetMouseState(p5ID) ), ia ca parametru numărul de identificare al dispozitivului și verifică dacă dispozitivul respectiv e folosit pentru a controla cursorul de pe ecran. Aceasta va returna o valoare 0 ( dacă mănușa nu e folosită pentru a controla cursorul ) sau 1 ( în caz contrar ).

Funcția P5_SetMouseState(p5ID,mouseState), ia ca primul parametru numărul de identificare al dispozitivului și ca al doilea o valoare care va selecta dacă mănușa va fi folosită pentru controlul cursorului ( o valoare de 0 pentru parametrul mouseState va însemna că mănușa nu va fi folosită pentru controlul cursorului, pe când o valoare de 1 va însemna că aceasta va fi folosită pentru controlul cursorului) .

Știind acestea, pentru implementarea ideii va trebui să procedăm în felul următor la apăsarea butonului ( acesta însemnând să complementăm starea curentă):

să obținem starea curentă a utilizării mănușii în variabila mouseState ( 0 sau 1)

să verificăm dacă aceasta e 0 sau 1

dacă mouseState e 0, înseamnă că mănușa nu este folosită pentru controlul cursorului (deși utilizatorul ar dori asta ) și astfel apelăm funcția P5_SetMouseState cu parametrii p5ID și 1. Dacă, în schimb, valoarea variabilei mouseState este 1, aceasta înseamnă că mănușa este folosită pentru controlul cursorului iar apăsare butonului ne indică faptul că utilizatorul nu ar dori asta. Astfel, vom apela funcția P5_SetMouseState cu parametrii p5ID și 0;

Funcțiile SetCtrlVal ( PANEL, PANEL_P5MOUSESTATE, 0 ) și SetCtrlVal ( PANEL, PANEL_P5MOUSESTATE, 1 ) sunt folosite pentru a seta aspectul butonului. Apelând funcția SetCtrlVal cu ultimul parametru 0, vom afișa utilizatorului butonul în starea OFF, iar apelând aceeași funcție cu parametrul 1, vom afișa utilizatorului butonul în starea ON. Deci, vom apela această funcție cu valoarea ultimului parametru corespunzătoare cu modul selectat de utilizare a mănușii (pentru controlul cursorului sau nu ).

Liniile de cod corespunzătoare situației de mai sus sunt prezentate mai jos:

mouseState=P5_GetMouseState(p5ID);

if(mouseState==1)

{

P5_SetMouseState(p5ID,0);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,0);

}

else if(mouseState==0)

{

P5_SetMouseState(p5ID,1);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,1);

}

Mai multe informații

Rol:

Butonul Mai multe Informatii are rolul de a afișa utilizatorului o nouă fereastră (panoul 2), aceasta conținând mai multe informații despre mănușă precum valori corespunzătoare: gradului de flexare a degetelor, rotației celor trei axe de coordonate ( prezentate în paragraful FilterYaw, FilterPitch, FilterRoll de la începutul subcapitolului ). În panoul 2 mai sunt prezentate și valorile de compensare despre care s-a discutat în partea teoretică în porțiunea despre Interpretarea coordonatelor primite de la mănușă.

Mai jos este prezentată o imagine a panoului 2:

Figura 11

Valorile pentru nivelul de flexare a degetelor sunt cuprinse între 0 și 63 (0 însemnând că degetul este întins și 63 că degetul este complet îndoit). Observăm din figură că la momentul capturii imaginii, mănușa era conectată la calculator și degetele arătător, inelar și mijlociu sunt îndoite la anumite nivele.

Implementare:

Pentru a putea verifica dacă utilizatorul interacționează cu panoul 1 sau 2, vom introduce o nouă variabilă care se va numi panelHandle. De fiecare dată când utilizatorul va selecta o anumită fereastră ( cea pe care va vizualiza valorile și/ sau asupra căreia va exercita acțiuni), valoarea variabilei panelHandle se va modifica adecvat. Acest proces de modificare a valorii variabilei panelHandle la selectarea uneia dintre ferestre este posibil datorită unei eveniment numit EVENT_GOT_FOCUS. De fiecare dată când utilizatorul va selecta o fereastră, se va genera evenimentul precizat, in cadrul funcției corespunzătoare ferestrei selectate.

Deci, în porțiunea corespunzătoare funcției panoului 1, vom face ca valoarea variabilei panelHandle să ia valoarea PANEL, care de fapt este o constantă egală cu 1 definită în fișierul header.

case EVENT_GOT_FOCUS:

panelHandle=PANEL;

Procesul se desfășoară în mod analog pentru panoul 2. Deci, care panou dintre cele 2 este cel activ, acela va stabili valoare variabilei panelHandle. Această valoare va fi verificată în cadrul rutinei de tratare a întreruperii generată de temporizatorul 1, o dată la 100 de milisecunde. Situația este prezentată în anexa 1.B unde, dacă valoarea variabilei panelHandle este PANEL (adică 1), atunci vom actualiza informațiile din primul panou, iar dacă valoarea variabilei este PANEL_2 (adică 2, constantă definită în fișierul .h ), vom actualiza valorile prezente în cel de-al doilea panou. Situația din urmă este prezentată în următoarele linii de cod:

SetCtrlVal(panelHandle,PANEL_2_THUMB,thumb);

SetCtrlVal(panelHandle,PANEL_2_INDEX ,index);

SetCtrlVal(panelHandle,PANEL_2_MIDDLE,middle);

SetCtrlVal(panelHandle,PANEL_2_RING,ring);

SetCtrlVal(panelHandle,PANEL_2_PINKY,pinky);

SetCtrlVal(panelHandle,PANEL_2_YAWANGLE,(*p5State).FilterYaw);

SetCtrlVal(panelHandle,PANEL_2_PITCHANGLE,(*p5State).FilterPitch);

SetCtrlVal(panelHandle,PANEL_2_ROLLANGLE,(*p5State).FilterRoll);

SetCtrlVal(panelHandle,PANEL_2_VERTHEAD1,(*P5_GetInfoPointer(p5ID)).Head1_VAngle);

SetCtrlVal(panelHandle,PANEL_2_HORHEAD1,(*P5_GetInfoPointer(p5ID)).Head_HAngle);

SetCtrlVal(panelHandle,PANEL_2_VERTHEAD2,(*P5_GetInfoPointer(p5ID)).Head2_VAngle);

SetCtrlVal(panelHandle,PANEL_2_HORHEAD2,(*P5_GetInfoPointer(p5ID)).Head2_HAngle);

Variabila p5State este un pointer la structura de date care conține diverse informații de la mănușă. În liniile următoare se arată cum s-au colectat valorile gradului de flexare a degetelor. La fiecare 100 de milisecunde, atunci când temporizatorul 1 este activ, se vor executa următoarele linii de cod:

thumb=(float)(*p5State).finger[0];

index=(float)(*p5State).finger[1];

middle=(float)(*p5State).finger[2];

ring=(float)(*p5State).finger[3];

pinky=(float)(*p5State).finger[4];

fingerAverage=(thumb+index+middle+ring+pinky)/5;

A mai fost precizat că vectorul finger(5) din structura P5State conține gradul de flexare a degetelor. Noi vom prelua aceste valori (individual pentru fiecare deget ), le vom converti în numere reale și, cum dispozitivul mecanic de prindere de pe brațul robotic este controlat de un singur motor, este mai convenabil să facem o medie a valorilor și cu aceasta să controlăm acel motor.

2.Analizarea și interpretarea datelor pe calculator

Analizarea gradului de flexare a degetelor

Valorile corespunzătoare nivelelor de flexare a degetelor sunt cuprinse în intervalul 0 și 63. O valoare de 0 corespunde unui deget complet întins iar o valoare de 63 corespunde unui deget complet flexat. După cum am spus și mai devreme, avem disponibil doar un singur servo motor pentru controlul mecanismului de prindere, deci este mai convenabil să facem o medie a valorilor și aceea să o trimitem mai departe. Acest fapt va permite o utilizare mai naturală a mănușii și un calcul al gradului total de flexare a degetelor mai exact și dependent de toate degetele.

Analizarea coordonatelor primite

Pentru analizarea coordonatelor primite trebuie mai întâi să vedem în ce unități de măsură sunt acestea reprezentate. După cum a fost prezentat în partea teoretică în partea despre interpretarea coordonatelor, valorile acestora trimise de la mănușă la calculator sunt reprezentate în unități P5.

Dintr-un anumit motiv ( explicat in partea despre controlul motoarelor ), valorile pe care le vom trimite prin portul serial la microcontroler pentru a controla motoarele trebuie să fie cuprinse între 61 și 193. Apare astfel o problemă din cauză că valorile coordonatelor pe care noi le avem disponibile sunt cuprinse între -1000 și 1000 pentru axele X și Y și 0 și 1000 pentru axa Z.

De fapt, prolema apărută ne forțează să transformăm valorile din intervalul (-1000, 1000) și (0, 1000) în valori din intervalul ( 61, 193). Cea mai elegantă rezolvare pe care o putem găsi la problema respectivă este una care ne permite să ignorăm unitățile în care sunt reprezentate coordonatele, păstrând în același timp un raport de proporționalitate între valori, și anume vom utiliza ecuația dreptei determinată de două puncte.

Mai jos este prezentat cazul coordonatei X.

Valorile primite, după cum am spus sunt cuprinse între (-1000, 1000) și dorim să le proporționăm în cu valori din intervalul (61, 193). Din alt motiv, tot precizat în partea despre controlul motoarelor, o valoare de -1000 va corespunde unei valori de 193 și o valoare de 1000 va corespunde uneia de 61.

Vom considera că valoarea primită corespunde unei valori situată pe axa X, iar valoarea care ne interesează să o determinăm corespunde unei valori de pe axa Y. Ținând cont și de faptul că -1000 va trece în 193 și 1000 va trece în 61, putem să stabilim limitele prin puncte, ca în figură:

Figura 12

Știind că ecuația dreptei determinate de două puncte, A(x1,y1) și B(x2,y2) este:

, rezultă că valoarea y care ne interesează pe noi va fi

ceea ce în cazul de față înseamnă

În această ultimă relație, y este valoarea care ne interesează să o trimitem prin portul serial pentru controlul servo motoarelor, iar x reprezintă valoarea coordonatei x primită de la mănușă.

Am afirmat înainte că nu ne interesează unitățile de măsură deoarece din momentul în care avem stabilită o relație de proporționalitate între cele două intervale, este foarte intuitiv pentru utilizator ( dacă brațul robotic este în câmpul lui vizual ) să facă o corelație între mișcările mâinii lui și cele ale brațului robotic.

Vom proceda în mod analog și pentru calculul valorilor de control a motoarelor corespunzătoare coordonatelor axelor Y și Z.

După efectuarea calculelor, vom ajunge la următoarele rezultate :

În ecuațiile de mai sus, xRs, yRs, zRs sunt valorile care vor fi trimise la microcontroler iar xPos, yPos, zPos sunt valorile primite de la mănușă.

Aplicând același procedeu pentru nivelul de flexare degetelor, obținem:

În ecuația de mai sus, fingerRS este valoarea pe care o vom trimite servo motorului care comandă mecanismul de prindere, iar valoarea fingerAverage este media aritmetică a valorilor primite de la mănușă care indică nivelul de flexare a degetelor.

Liniile de cod care efectuează aceste scalări prin implementarea ecuațiilor de mai sus sunt prezentate mai jos:

xRs=(int)RoundRealToNearestInteger (0.066*(-xPos)+127);

yRs=(int)RoundRealToNearestInteger (0.066*(-yPos)+127);

zRs=(int)RoundRealToNearestInteger (0.127*(-zPos)+193);

fingerRs=(int)RoundRealToNearestInteger (-2.2*fingerAverage+193) ;

Este necesară după cum se poate observa utilizarea funcției RoundRealToNearestInteger (value) care ia ca parametru un număr real (value ) și returnează numărul întreg cel mai apropiat ca valoare de acel număr real. Am spus că este necesară funcția deoarece numerele noastre sunt reale, dar pentru controlul motoarelor avem nevoie de numere întregi pozitive.

3.Alegerea motoarelor pentru brațul robotic

După cum a fost observat din subsecțiunea Servo motoare aparținând capitolului Fundamentare teoretică , motoarele alese pentru construcția brațului robotic sunt servo motoare, aceste fiind de fapt motoare de curent continuu ( DC), echipate cu un mecanism servo. Principalele motive pentru alegerea acestor tipuri de motoare sunt : posibilitatea stabilirii unei poziții unghiulare exacte a axului motorului în funcție de lățimea pulsului primit, dimensiunea și greutatea acestora și cuplul dezvoltat.

Controlul acestor servo motoare se va face cu un tren de pulsuri modulate în durată puse la dispoziție de către microcontroler.

Pentru fiecare servo motor din cadrul brațului robotic, va trebui stabilită o poziție neutră (inițială ) prin trimiterea unui tren de pulsuri cu lățimea acestora predefinită.

După cum a fost precizat și în secțiunea Servo Motoare , pentru a putea controla servo motoarele disponibile avem la dispoziție pentru fiecare din ele trei fire. Pe unul dintre ele se va aplica semnalul de control, iar celelalte două vor fi folosite pentru alimentarea servomotorului. Problema care apare aici va fi aceea că la ieșirea microcontrolerului vom avea semnale de control sub forma unor pulsuri dreptunghiulare modulate în durată de amplitudine vârf la vârf 3.3 volți, iar pentru controlul servo motoarelor avem nevoie de un semnal de control de amplitudine 5-5.5 volți.

Soluția adoptată pentru această problemă este utilizarea unui amplificator format din două etaje de amplificare elementare cu tranzistor bipolar în conexiune emitor comun. Este nevoie de două etaje deoarece etajul emitor comun este inversor. Observăm în figura de mai jos schema utilizată pentru crearea plăcuțelor. Generatorul de semnal dreptunghiular SemnalControl reprezintă semnalul dreptunghiular care provine de la microcontroler de amplitudine 3.3 volți. Tot circuitul este alimentat de la o tensiune de 5- 5.5 volți.

Tranzistoarele din cele două etaje trec doar prin două stări : blocare și saturație .

Atunci când semnalul de control va avea valoarea de 3.3 volți, aceasta va fi aplicată pe baza tranzistorului Q0 , ducând-ul pe acesta în regiunea de saturație, tensiunea din colectorul Q0 fiind Vcesat , aproximativ 0.2 volți, neglijabilă. Această tensiune va fi aplicată pe baza tranzistorului Q1 , fapt care îl va conduce pe acesta în regiunea de blocare, fenomen care la rândul lui va determina ca tensiunea din colectorul lui Q1 , adică tensiunea cu care va fi controlat servo motorul, să fie aproximativ egală ( neglijând căderea de tensiune pe rezistorul R3) cu VDD, care este tensiunea cu care este alimentat circuitul, adică o valoare între 5 și 5.5 volți.

Figura 13

În mod analog, atunci când valoarea semnalului de control va fi de 0 volți și acesta va fi aplicat pe baza tranzistorului Q0 , acesta va fi blocat, fapt care va determina o tensiune în colector aproximativ egală ( neglijând căderea de tensiune pe rezistorul R1) cu tensiunea de alimentare. Această tensiune va fi la rândul ei aplicată pe baza tranzistorului Q1, conducând-ul pe acesta în saturație, și determinând la ieșirea circuitului ( aplicată pe servo motor ) o tensiune egală cu Vcesat ,pe care o putem aproxima cu 0 ținând cont de faptul că valorile tensiunii de la ieșire sunt cuprinsre între 5 și 5.5 volți.

Pentru brațul robotic din lucrarea de față am folosit patru astfel de plăcuțe.

Modalitatea de control a servo motoarelor

În această secțiune vom explica cum cu datele obținute de la mănușa, după prelucrarea acestora, vom controla efectiv motoarele care vor fi montate pe încheieturile brațului robotic.

Discuția din rândurile următoare va urmări două idei principale:

trimiterea datelor prelucrate de la calculator la microcontroler

primirea datelor de către microcontroler și efectuarea controlului motoarelor

Vom începe cu prima idee și adică trimiterea datelor disponibile în aplicație la microcontroler.

Datele pe care , după prelucrarea informațiilor de la mănușă, le avem la dispoziție sunt valorile corespunzătoare celor trei axe de coordonate, X, Y și Z , și o valoare care indică nivelul de flexare a degetelor. Pe brațul robotic vor fi montate patru servomotoare.

O primă problemă care apare în atingerea scopului propus ( adică controlul motoarelor ) este reprezentată prin necesitatea posibilității de diferențiere a motoarelor, astfel încât fiecare din servo motoare să primească valorile care îi erau destinate și nu valorile menite altui servo motor.

O soluție folosită în această lucrare pentru a face diferența între cele patru servo motoare este implementată în codul aplicației pe calculator și constă în scrierea la portul serial a unui caracter înainte de scrierea informației pentru motorul respectiv. Acel caracter are rolul de a indica aplicației care rulează pe microcontroler căruia dintre cele patru motoare îi este destinată informația care va fi scrisă după trimiterea caracterului. Un lucru trebuie luat însă în considerare. Acela este că valorile celor patru caractere scrise la portul serial să nu poată fi egale cu valorile care controlează efectiv servo motorul ( care sunt cuprinse în intervalul ( 61, 193) ) pentru a nu crea confuzie . De aceea , valorile de control alese pentru motoare sunt: 10, 20, 30, 40. Se poate observa că aceste valori nu sunt cuprinse în intervalul (61, 193), deci nu poate exista nici o confuzie în legătură cu ce înseamnă fiecare valoare. Mai exact, dacă aplicația care rulează pe microcontroler primește o valoare egală cu 10, 20, 30, 40 , va ști că este o valoare de control, va alege motorul corespunzător și următoarea valoare primită ( care va fi una cuprinsă între 61 și 193 ) o va folosi efectiv pentru controlul acelui motor.

Codul corespunzător afirmațiilor făcute mai sus este prezentat în liniile următoare:

success=ComWrtByte (1,10);

success=ComWrtByte (1, xRs);

success=ComWrtByte (1,20);

success=ComWrtByte (1, yRs);

success=ComWrtByte (1,30);

success=ComWrtByte (1, zRs);

success=ComWrtByte (1,40);

success=ComWrtByte (1, fingerRs);

În continuarea ideii inițiale, ajungem la cea de-a doua parte, adică primirea și interpretarea informațiilor de către aplicația care rulează pe microcontroler.

Pentru partea de aplicație pe microcontroler vom folosi ca dispozitiv hardware microcontrolerul MCF5213 ColdFire produs de firma Freescale. Plăcuța de dezvoltare care conține microcontrolerul respectiv este M5213EVB. De pe plăcuța de dezvoltare vom utiliza unul din cele trei porturi seriale disponibile ( UART0), iar de pe microcontroler vom utiliza modulul PWM și întreruperile. Caracteristicile principale utilizate a acestor componente sunt descrise în secțiunea Microcontrolerul MFC5213 ColdFire.

Mediul de dezvoltare în care a fost creată aplicația care rulează pe microcontroler se numește Freescale CodeWarior. Pentru dezvoltarea aplicației s-au folosit avantajele instrumentului ProcessorExpert și scrierea codului sursă in C.

ProcessorExpert este un instrument rapid folosit pentru design-ul aplicațiilor integrate în CodeWarior. Caracteristicile acestuia sunt :

O interfață grafică care permite ca aplicația să fie definită prin funcționalitatea acesteia

Aplicația este creată cu condiții specificate pentru inițializarea componentelor de bază ale microcontrolerului

Generarea automată de cod C optimizat pentru necesitățile aplicației prin selectarea dispozitivului hardware folosit, in cazul nostru microcontrolerul MFC5213 Coldfire.

În această parte a proiectului trebuie să dezvoltăm o aplicație care poate primi informații prin intermediul portului serial si poate utiliza aceste informații pentru controlul celor patru motoare disponibile. După cum se observă, din nou prolema se structurează în două părți:

Obținerea informațiilor prin intermediul portului serial

Controlul motoarelor cu ajutorul informațiilor primite

1. Obținerea informațiilor prin intermediul portului serial

Pentru a putea citi informațiile care sunt trimise microcontrolerului prin intermediul portului serial, este necesară generarea unei întreruperi atunci când sunt disponibile date la portul serial.

Pentru configurarea aplicației ca aceasta să utilizeze portul serial UART0 va trebui să specificăm în ProcessorExpert următoarele informații:

După configurarea setărilor pentru utilizarea portului serial, mai trebuie să scriem codul care se va executa la generarea întreruperii. Acesta va fi plasat în interiorul acoladelor corespunzătoare metodei SerialComMot. Mai jos este prezentată implementarea în program:

Mai jos avem rutina de tratare a întreruperii cu codul corespunzător:

__decspec( interrupt ) void SerialComMot ( void )

{

receivedChar= uart_getchar(0);

if ( receivedChar == 10 )

receivedChar=uart_getchar (0);

if ( ( 61 <= receivedChar ) && ( receivedChar <= 193)

setReg8(PWMDTY1, receivedChar);

… // vom proceda în mod similar pentru celelate 3 motoare verificând dacă receivedChar este egală cu 20, 30, 40 și setând corespunzător PWMDTY3, PWMDTY5, PWMDTY7.

}

În acest moment avem informațiile trimise prin portul serial. Tot în rutina de tratare a întreruperii facem controlăm și motoarele . Ajungem astfel la partea a doua a prolemei.

2. Controlul motoarelor cu ajutorul informațiilor primite

Mai întâi va trebui să setăm modulul PWM cu valorile inițiale.

Pentru configurarea aplicației ca aceasta să utilizeze modulul PWM cu setările dorite de noi va trebui să specificăm în ProcessorExpert următoarele informații ( vom arăta setările făcute pentru un singur port, pentru celelalte acestea sunt setate în mod analog):

După ce am efectuat aceste setări , am stabilit frecvența impulsurilor PWM ca fiind de 306 Hz care este mai mare decât 50 Hz necesară pentru controlul motoarelor ( problemă tratată în subsecțiunea Motoare Servo), tot ce ne mai rămâne de făcut este să variem valoarea ciclului activ , adică lățimea pulsului proporțional cu valorile primite. Vom face aceasta prin setarea valorii registrului PWMDTY corespunzător (1,3,5 sau 7 ) prin funcția:

setReg8(PWMDTYX, receivedChar);

În funcția de mai sus, X va lua una din valorile 1, 3 5 sau 7, în funcție de care motor va trebui controlat.

Codul complet corespunzător aplicației care rulează pe microcontroler se găsește în Anexa 1.C

Rezumat

Scopul lucrării de față a fost dezvoltarea unei aplicații care, utilizând conceptele interfațării om – mașină, să ușureze utilizarea mașinilor făcând în acest timp acest proces mai intuitiv pentru utilizator. Aplicația este formată din componente software precum aplicația care rulează pe calculator și cea care rulează pe microcontroler dar și din componenta hardware reprezentată de brațul robotic și subcomponentele acestuia.

Ideea în jurul căreia s-a dezvoltat lucrarea de față a fost ca utilizatorul să-și poată pune mănușa de date și cu ajutorul acesteia să poată controla brațul robotic. O altă idee adusă în completarea primei a fost ca utilizatorul să poată învăța brațul să execute o serie de gesturi pe care acesta le va putea face singur după aceea, fără a fi controlat.

În cadrul acestei lucrări au fost parcurse toate etapele indispensabile implementării aplicației. Astfel, în prima parte au fost prezentate fundamentele teoretice necesare începând de la prezentarea mănușilor de date, informații despre servo motoare și utilizarea acestora, microcontrolerul MFC5213 Coldfire împreună cu modulul PWM și comunicația serială. După sublinierea importanței și detalierea acestora s-a trecut la utilizarea cunoștințelor dobândite prin efectuarea părții practice.

Partea practică a fost împărțită la rândul ei în mai multe părți funcționale. Acestea sunt în ordine: preluarea și afișarea datelor de la mănușa de date, analizarea și interpretarea datelor pe calculator, alegerea motoarelor pentru brațul robotic și modalitatea de control a motoarelor prin aplicația care rulează pe microcontroler. Această subîmpărțire a părții practice a fost posibilă din cauză că pentru implementarea diferitelor secțiuni au fost folosite concepte și resurse diferite, fiecare dintre acestea oferind anumite servicii celorlalte blocuri.

Fiecare secțiune din cadrul părții practice a fost tratată după un tipar prestabilit. În prima parte din fiecare secțiune sunt precizate obiectivele propuse după realizarea acesteia, în a doua sunt prezentate modalitățile convenabile de a ne atinge scopul precum și problemele care apar și rezolvarea propusă a acestora.

Pe scurt, realizarea părții practice s-a desfășurat prin înlănțuirea mai multor obiective. Primul dintre acestea a fost obținerea și interpretarea informațiilor primite de la mănușa conținând poziția în spațiu a acestora și nivelul de flexare a degetelor. După ce acest scop a fost atins am trecut la analizarea acestor informații pe calculator și găsirea unei modalități de a le converti astfel încât ele să poată fi folosite pentru controlul servo motoarelor. În continuare am dezvoltat o aplicație care rulează pe microcontroler și cu care vom realiza efectiv semnalele utilizate pentru controlul motoarelor. A mai fost realizată și comunicația dintre aplicația care rulează pe calculator și aplicația care rulează pe microcontroler prin utilizarea comunicației seriale.

Obținerea rezultatelor pozitive în cadrul tuturor pașilor parcurși a dus la materializarea unei aplicații funcționale formată atât din componente software cât și hardware.

Concluzii

Scopul principal al proiectului descris în această lucrare a fost că prin utilizarea anumitor tehnologii disponibile se poate ajunge la înlesnirea utilizării mașinilor și a integrării acestora ca parte din viața noastră. Proiectul a demonstrat într-o anumită măsură că prin aplicarea eficientă a tehnologiei și separarea părții de interfață de partea funcțională putem răspândi utilizarea acesteia, oferind anumite facilități în multe domenii și în același timp îmbunătățind nivelul de trai.

http://ocw.mit.edu/courses/mechanical-engineering/2-12-introduction-to-robotics-fall-2005/lecture-notes/chapter3.pdfhttp://ocw.mit.edu/courses/mechanical-engineering/2-12-introduction-to-robotics-fall-2005/lecture-notes/chapter4.pdf

http://ocw.mit.edu/courses/mechanical-engineering/2-12-introduction-to-robotics-fall-2005/lecture-notes/chapter2.pdf

MFC5213 ColdFire reference manual

Anexă

Anexa 1.A Fișierul P5DLLc.h

/******************************************************************************

File: p5dllc.h

Use: wrapper that is exposed to the world.

Authors: Av Utukuri, Igor Borysov, Carl Kenner

Updates

Aug 24\01 – Av Utukuri – Finished V1.0, made all variables follow hungarian notation, cleaned up dead code

Jul 15\04 – Carl Kenner – Added non-C++ exported functions, extra features, new data structures and more error codes

******************************************************************************/

#ifndef _P5DLL_H_

#define _P5DLL_H_

/*

#define TEST_DLL

#define P5DLL_DEBUG

*/

#ifdef P5DLL_EXPORTS

#define P5DLL_API __declspec(dllexport)

#else

#define P5DLL_API __declspec(dllimport)

#endif

/*

Contrary to its name, this is NOT the head separation value

Actually this is the fraction of an inch that all the coordinates are returned in.

1 inch = 51.2 units. The real head separation value is 7.9 inches.

*/

#define P5HEAD_SEPARATION 51.2f

/*

true or false values are declared as type P5BOOL

*/

#define P5BOOL unsigned int

/*

fingers definitions

*/

#define P5_THUMB 0x0

#define P5_INDEX 0x1

#define P5_MIDDLE 0x2

#define P5_RING 0x3

#define P5_PINKY 0x4

/*

filter modes definitions

*/

#define P5_FILTER_NONE 0x0

#define P5_FILTER_DEADBAND 0x1

#define P5_FILTER_AVERAGE 0x2

/*

[sic]

*/

#define P5_FILTER_DEADBEND 0x1

/*

Error Codes

*/

#define P5ERROR_NONE 0x00

#define P5ERROR_NOP5DETECTED 0x01

#define P5ERROR_P5DISCONNECTED 0x02

#define P5ERROR_P5CONNECTED 0x03

#define P5ERROR_INVALID_P5ID 0x04

#define P5ERROR_INVALID_P5GLOVETYPE 0x05

#define P5ERROR_WRONGFIRMWAREVER 0x06

#define P5ERROR_EXCEPTION 0x07

#define P5ERROR_GETTIME 8

#define P5ERROR_PARSEBEND 9

#define P5ERROR_PARSEBUTTONS 10

#define P5ERROR_PARSELEDNUM 11

#define P5ERROR_PARSELED 12

#define P5ERROR_UNWARP 13

#define P5ERROR_PROCESS 14

#define P5ERROR_PROCESSROTATION 15

#define P5ERROR_PROCESSRELATIVEPOSITION 16

#define P5ERROR_PROCESSRELATIVEAVGPOSITION 17

#define P5ERROR_PROCESSROTATIONREAL 18

#define P5ERROR_PROCESSABSOLUTEPOSITION 19

#define P5ERROR_PROCESSLEDPOSITIONS 20

#define P5ERROR_INTERNAL 21

/*

Glove types

*/

#define P5GLOVETYPE_RIGHTHAND 0x00

#define P5GLOVETYPE_LEFTHAND 0x01

/*

Distance units

*/

#define P5_UNITS (1.0f)

#define P5_CM (2.54f/51.2f)

#define P5_MM (P5_CM*10)

#define P5_INCHES (1.0f/51.2f)

#define P5_FEET (1.0f/51.2f/12.0f)

#define P5_M (P5_CM/100)

#define P5_KM (P5_M/1000)

/*

Time Units

*/

#define P5_SECONDS (1.0f)

#define P5_MINUTES (P5_SECONDS/60)

#define P5_HOURS (P5_MINUTES/60)

#define P5_FRAME (1.0f*P5_SECONDS/41.7f)

/*

Velocity Units

*/

#define P5_KPH (P5_KM/P5_HOURS)

#define P5_MPS (P5_M/P5_SECONDS)

/*

Acceleration Units

*/

#define P5_MPSPS (P5_M/P5_SECONDS/P5_SECONDS)

/*

Rotation Units

*/

#define P5_DEGREES (1.0f)

#define P5_RADIANS (3.14159/(180*P5_DEGREES))

#define P5_REVOLUTIONS (P5_DEGREES/360)

/*

What position to return via the old interface

*/

#define P5MODE_RELATIVE 0

#define P5MODE_ABSOLUTE 1

#define P5MODE_RELATIVEAVG 2

#define P5MODE_FILTERED 3

#define P5MODE_LED0 4

#define P5MODE_LED1 5

#define P5MODE_LED2 6

#define P5MODE_LED3 7

#define P5MODE_LED4 8

#define P5MODE_LED5 9

#define P5MODE_LED6 10

#define P5MODE_LED7 11

#define P5MODE_LED8 12

#define P5MODE_LED9 13

#define P5MODE_LEDBRIGHT 14

#define P5MODE_LEDBEST 15

/*

What rotation to return via the old interface

*/

#define P5MODE_ORIGINAL_PYR (256*0)

#define P5MODE_BETTER_PYR (256*10)

#define P5MODE_BETTER_YPR (256*11)

#define P5MODE_BETTER_RYP (256*12)

#define P5MODE_BETTER_RPY (256*13)

#define P5MODE_BETTER_PRY (256*14)

#define P5MODE_BETTER_YRP (256*15)

#define P5MODE_BETTER_MIX (256*16)

#define P5MODE_FILTERED_PYR (256*20)

#define P5MODE_FILTERED_YPR (256*21)

#define P5MODE_FILTERED_RYP (256*22)

#define P5MODE_FILTERED_RPY (256*23)

#define P5MODE_FILTERED_PRY (256*24)

#define P5MODE_FILTERED_YRP (256*25)

#define P5MODE_FILTERED_MIX (256*26)

/*////////////////////////////////////////////////////////////////////////////////////

P5Data

This is the original data structure that all the glove's information was stored in.

It is missing a lot of information, so two new data structures were created also.

The new data structures should be used instead of this one most of the time.

This one is mostly for backwards compatibility

////////////////////////////////////////////////////////////////////////////////////*/

typedef struct

{

/*

Actual P5 variables

*/

char VendorID[50], ProductID[50], Version[50];

char ProductString[255], ManufacturerString[255], SerialNumString[255];

int m_nDeviceID;

int m_nMajorRevisionNumber, m_nMinorRevisionNumber;

int m_nGloveType;

float m_fx, m_fy, m_fz; /* x,y,z of the hand */

float m_fyaw, m_fpitch, m_froll; /* yaw\pitch\roll of the hand */

unsigned char m_byBendSensor_Data[5]; /* All the bend sensor data */

unsigned char m_byButtons[4]; /* P5 Button data */

float m_fRotMat[3][3]; /* matrix for inverse kinematics */

/*

The fields below only exist in special debug versions of the DLL file.

But don't worry, the same information is available in the new P5State structure.

for distribution delete these lines

*/

#ifdef P5DLL_DEBUG

float VisLedPos[4][3]; /* X\Y\Z of visible LEDs */

float m_fV1Angle[4], m_fV2Angle[4], m_fHAngle[4]; /* Angle of entry of visible LEDs */

unsigned char m_byLEDNum[4]; /* Visible LEDs */

int m_nlastBrightLED[4];

unsigned char m_RawDistalSensor_Data[5];

unsigned int LED0[3], LED1[3], LED2[3], LED3[3];

float ActualLED_X[10];

float ActualLED_Y[10];

float ActualLED_Z[10];

#endif

}P5Data;

/*///////////////////////////////////////////////////////////////////////////////////

CP5DLL

This is the original C++ class that represents all the P5 Gloves on your system.

You just create one of these objects at the start of your C++ program,

then call the P5_Init() method. The object will then automatically update its

fields and structure arrays until you call the P5_Close() method.

This class is missing a lot of important functionality, and is limited to Visual C++

You are better off using the C style functions.

This is mostly just for backwards compatibility.

///////////////////////////////////////////////////////////////////////////////////*/

#include <pshpack8.h>

typedef struct {

/* Location */

float x, y, z; /* x,y,z of the hand */

float RelativeX, RelativeY, RelativeZ;

float RelativeAvgX, RelativeAvgY, RelativeAvgZ;

float FilterPos[3];

float Velocity[3];

float Acceleration[3];

/* Rotation */

float RotMat[3][3]; /* rotation matrix [row][column] like Direct3D, premultiply by row vector */

float pitch, yaw, roll;

float FilterRotMat[3][3];

float FilterPitch, FilterYaw, FilterRoll;

float AngularVelocity[3]; /* around the x, y, and z axes */

float AngularAcceleration[3];

/* Visibility */

int Visible;

/* Fingers */

unsigned char finger[5]; /* All the bend sensor data */

short FingerAbsolute[5];

float FingerVelocity[5];

float FingerAcceleration[5];

/* Buttons */

unsigned char button[4]; /*P5 Button data*/

/* Leds */

float LedPos[10][3]; /* Position of 10 LEDs */

float LedVelocity[10][3]; /* Velocity of 10 LEDs */

float LedGeometricAccuracy[10];/* Dot product of vectors to other LEDs */

unsigned char LedVisible[10]; /* 1 if visible, 0 if not */

int TrackedLed;

/* Visible Leds */

int VisibleLedCount;

signed char VisibleLedNumber[4]; /* Which of the 10 LEDs is each visible one */

int VisibleLedRaw[4][3]; /* Raw Led values -512 to +511 */

float VisibleLedV1Angle[4], VisibleLedV2Angle[4], VisibleLedHAngle[4]; /*Angle of entry of visible LEDs*/

float VisibleLedPos[4][3]; /* debug */

/* Time */

double TimeStamp;

float FrameRate;

long long Frame;

} P5State;

typedef struct {

char VendorID[52], ProductID[52], Version[52];

char ProductString[256], ManufacturerString[256], SerialNumString[256];

int DeviceID;

int MajorRevisionNumber, MinorRevisionNumber;

int GloveType;

/* Physical Led Layout */

float ActualLedPos[10][3];

float LEDDistances[10][10];

/* Global tan compensation data */

float Head1_VAngle, Head_HAngle, Head2_VAngle, Head2_HAngle;

float HeadSeparation;

/* AC Adaptrer Status */

P5BOOL ACAdapterStatus;

/* Finger Calibration */

int FingerStraight[5];

int FingerBent[5];

/* Handles */

void *DeviceHandle; /* PHID_DEVICE */

unsigned int ThreadId;

unsigned int ThreadHandle;

P5BOOL ThreadStatus;

} P5Info;

#include <poppack.h>

/* Init function – required on start of program */

P5DLL_API P5BOOL __stdcall P5_Init(void);

P5DLL_API void __stdcall P5_Close(void);

P5DLL_API void * __stdcall P5_GetCP5DLL();

P5DLL_API void * __stdcall P5_GetCP5();

P5DLL_API int __stdcall P5_GetCount();

P5DLL_API P5Data * __stdcall P5_GetDataPointer(int P5Id);

P5DLL_API P5State * __stdcall P5_GetStatePointer(int P5Id);

P5DLL_API P5Info * __stdcall P5_GetInfoPointer(int P5Id);

P5DLL_API void * __stdcall P5_GetPrivatePointer(int P5Id);

P5DLL_API void __stdcall P5_SaveBendSensors(int P5Id);

P5DLL_API void __stdcall P5_CalibrateBendSensors(int P5Id);

P5DLL_API void __stdcall P5_CalibratePositionData(int P5Id);

P5DLL_API void __stdcall P5_SetClickSensitivity(int P5Id, unsigned char leftvalue, unsigned char rightvalue, unsigned char middlevalue);

P5DLL_API void __stdcall P5_GetClickSensitivity(int P5Id, int *leftclick, int *rightclick, int *middleclick);

P5DLL_API P5BOOL __stdcall P5_GetMouseState(int P5Id);

P5DLL_API void __stdcall P5_SetMouseState(int P5Id, P5BOOL state);

P5DLL_API void __stdcall P5_SetMouseStickTime(int P5Id, unsigned char time);

P5DLL_API int __stdcall P5_GetMouseStickTime(int P5Id);

P5DLL_API void __stdcall P5_GetMouseButtonAllocation(int P5Id, int *leftclick, int *rightclick, int *middleclick);

P5DLL_API void __stdcall P5_SetMouseButtonAllocation(int P5Id, int leftclickfinger, int rightclickfinger, int middleclickfinger);

P5DLL_API P5BOOL __stdcall P5_GetLastError(int *P5Id, int *ErrorCode);

/* AC Adaptor */

P5DLL_API P5BOOL __stdcall P5_GetACAdaptorStatus(int P5Id);

/* Bend Sensors */

P5DLL_API void __stdcall P5_GetBendCalibration(int P5Id, int *ThumbStraight, int *ThumbBent,

int *IndexStraight, int *IndexBent,

int *MiddleStraight, int *MiddleBent,

int *RingStraight, int *RingBent,

int *PinkyStraight, int *PinkyBent);

P5DLL_API void __stdcall P5_GetRawFingerBends(int P5Id, int *thumb, int *index, int *middle, int *ring, int *pinky);

P5DLL_API void __stdcall P5_GetCalibrationData(int P5Id, unsigned char data[]);

/* Space Warp */

P5DLL_API void __stdcall P5_SetUnits(float units);

P5DLL_API void __stdcall P5_SetForwardZ(int z);

P5DLL_API void __stdcall P5_SetTowerPos(int P5Id, float x, float y, float z);

P5DLL_API void __stdcall P5_SetTowerRotMat(int P5Id, float RotMat[][3]);

P5DLL_API void __stdcall P5_TiltTowerForward(int P5Id, float degrees);

P5DLL_API void __stdcall P5_SpinTowerRight(int P5Id, float degrees);

P5DLL_API void __stdcall P5_TiltTowerRight(int P5Id, float degrees);

P5DLL_API void __stdcall P5_SetSensitivity(int P5Id, float multiplier);

P5DLL_API void __stdcall P5_SetTrackerUnwarp(int P5Id, float UnwarpX, float UnwarpY, float UnwarpZ, float UnwarpScaleX, float UnwarpScaleY, int NewUnwarp);

P5DLL_API void __stdcall P5_GetTrackerUnwarp(int P5Id, float *UnwarpX, float *UnwarpY, float *UnwarpZ, float *UnwarpScaleX, float *UnwarpScaleY, int *NewUnwarp);

/* Glove configuration */

P5DLL_API void __stdcall P5_SetLedPos(int P5Id, int led, float x, float y, float z);

P5DLL_API void __stdcall P5_SetGlovePos(int P5Id, float x, float y, float z);

P5DLL_API void __stdcall P5_SetGloveRotMat(int P5Id, float RotMat[][3]);

P5DLL_API void __stdcall P5_PitchGlove(int P5Id, float theta);

P5DLL_API void __stdcall P5_YawGlove(int P5Id, float theta);

P5DLL_API void __stdcall P5_RollGlove(int P5Id, float theta);

/* Filters */

P5DLL_API void __stdcall P5_SetInterfaceMode(int mode);

P5DLL_API void __stdcall P5_SetFilterMode(int filters);

P5DLL_API void __stdcall P5_SetFilterAmount(int ExtraFrames, float DeadDistance);

P5DLL_API void __stdcall P5_ClearArchive(int P5Id);

P5DLL_API void __stdcall P5_SetRequiredAccuracy(float accuracy);

P5DLL_API void __stdcall P5_SetPrediction(int prediction);

/* Mouse */

P5DLL_API void __stdcall P5_RestoreMouse(int P5Id);

P5DLL_API void __stdcall P5_UnRestoreMouse(int P5Id);

/* Low level hardware control */

P5DLL_API void __stdcall P5_SetFeature(int P5Id, unsigned char usage, unsigned char numvalues, unsigned char *reportdata);

P5DLL_API void __stdcall P5_SetFeatureReport(int P5Id, unsigned char report, unsigned char numvalues, unsigned char *reportdata);

P5DLL_API int __stdcall P5_GetFeature(int P5Id, unsigned char usage, unsigned char numvalues, unsigned char *reportdata);

P5DLL_API int __stdcall P5_GetFeatureReport(int P5Id, unsigned char report, unsigned char numvalues, unsigned char *reportdata);

/* Copy structures */

P5DLL_API void __stdcall P5_GetData(int P5Id, P5Data *data);

P5DLL_API void __stdcall P5_GetState(int P5Id, int frame, P5State *state);

P5DLL_API void __stdcall P5_GetInfo(int P5Id, P5Info *info);

/* Get values stored in structures */

P5DLL_API void __stdcall P5_GetAbsolutePos(int P5Id, float *x, float *y, float *z);

P5DLL_API void __stdcall P5_GetRelativePos(int P5Id, float *x, float *y, float *z);

P5DLL_API void __stdcall P5_GetRelativeAvgPos(int P5Id, float *x, float *y, float *z);

P5DLL_API void __stdcall P5_GetRotation(int P5Id, float *yaw, float *pitch, float *roll);

P5DLL_API void __stdcall P5_GetFingerBends(int P5Id, float *thumb, float *index, float *middle, float *ring, float *pinky);

P5DLL_API void __stdcall P5_GetButtons(int P5Id, float *A, float *B, float *C, float *D);

P5DLL_API P5BOOL __stdcall P5_GetThreadStatus(int P5Id);

#endif

Anexa 1.B Codul sursă al aplicației din LabWindowsCVI

#include <utility.h>

#include <cvirte.h>

#include <userint.h>

#include "P52.h"

#include <P5DLLc.h>

#include <rs232.h>

static int panelHandle;

static P5BOOL

initValue,

mouseState;

static float

alpha,

alphaP,

notAlphaP,

xPos,

xPosPrev=0,

yPos,

yPosPrev=0,

zPos,

thumb,

index,

middle,

ring,

pinky,

yaw,

pitch,

roll,

rollPrev=0,

fingerAverage,

head1_VAngle,

head1_HAngle,

head2_VAngle,

head2_HAngle;

int

xRs,

yRs,

zRs,

fingerAverageRs,

success;

static P5Info gloveInfo;

static P5State * p5State;

static int

p5ID,

xPosVect[1000],

yPosVect[1000],

zPosVect[1000],

fingerAvgVect[1000],

vectSize,

recStatus,

vectIterator;

/*–––––––––––––––––––––––––*/

/* Internal function prototypes */

/*–––––––––––––––––––––––––*/

void EnablePanelControls (int);

void EnablePanelTimers (int);

float filter (float,float);

int main (int argc, char *argv[])

{

initValue=P5_Init();

p5ID=gloveInfo.DeviceID;

P5_SetFilterMode(P5_FILTER_AVERAGE);

P5_SetFilterAmount(10, 0);

P5_SetRequiredAccuracy(10);

p5State=P5_GetStatePointer(p5ID);

if (InitCVIRTE (0, argv, 0) == 0)

return -1; /* out of memory */

if ((panelHandle = LoadPanel (0, "P52.uir", PANEL)) < 0)

return -1;

EnablePanelControls(1);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,P5_GetMouseState(p5ID));

DisplayPanel (panelHandle);

RunUserInterface ();

DiscardPanel (panelHandle);

return 0;

}

int CVICALLBACK fPanel (int panel, int event, void *callbackData,

int eventData1, int eventData2)

{

switch (event)

{

case EVENT_GOT_FOCUS:

panelHandle=PANEL;

break;

case EVENT_LOST_FOCUS:

break;

case EVENT_CLOSE:

P5_Close();

QuitUserInterface (0);

break;

}

return 0;

}

int CVICALLBACK fButton (int panel, int control, int event,

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,1);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

break;

}

return 0;

}

int CVICALLBACK fTimer (int panel, int control, int event, // O data la 100 de milisecunde voi actualiza informatiile si le voi trimite mai departe

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_TIMER_TICK:

thumb=(float)(*p5State).finger[0];

index=(float)(*p5State).finger[1];

middle=(float)(*p5State).finger[2];

ring=(float)(*p5State).finger[3];

pinky=(float)(*p5State).finger[4];

if(panelHandle==PANEL){

xPos=(*p5State).FilterPos[0];

xPos=filter(xPos,xPosPrev);

xPosPrev=xPos;

yPos=(*p5State).FilterPos[1];

yPos=filter(yPos,yPosPrev);

yPosPrev=yPos;

zPos=(*p5State).FilterPos[2];

fingerAverage=(thumb+index+middle+ring+pinky)/5;

SetCtrlVal(panelHandle,PANEL_COLORNUM,xPos);

SetCtrlVal(panelHandle,PANEL_COLORNUM_2,yPos);

SetCtrlVal(panelHandle,PANEL_COLORNUM_3,zPos);

xRs=(int)RoundRealToNearestInteger (0.066*(xPos)+127);

success=ComWrtByte (6,10);

success=ComWrtByte (6, xRs);

yRs= (int)RoundRealToNearestInteger (0.066*(-yPos)+127);

success=ComWrtByte (6,20);

success=ComWrtByte (6, yRs);

roll=(*p5State).FilterRoll;

roll=filter(roll,rollPrev);

rollPrev=roll;

zRs=(int)RoundRealToNearestInteger (0.55*roll+127) ;

success=ComWrtByte (6,30);

success=ComWrtByte (6, zRs);

fingerAverageRs=(int)RoundRealToNearestInteger (-2.2*fingerAverage+193) ;

success=ComWrtByte (6,40);

success=ComWrtByte (6, fingerAverageRs);

if(recStatus==1)

{

xPosVect[vectSize]=xRs;

yPosVect[vectSize]=yRs;

zPosVect[vectSize]=zRs;

fingerAvgVect[vectSize]=fingerAverageRs;

vectSize+=1;

}

}

else if(panelHandle==PANEL_2)

{

//P5_GetFingerBends(p5ID,&thumb,&index,&middle,&ring,&pinky);

//P5_GetRotation(p5ID, &yaw, &pitch,&roll);

SetCtrlVal(panelHandle,PANEL_2_THUMB,thumb);

SetCtrlVal(panelHandle,PANEL_2_INDEX ,index);

SetCtrlVal(panelHandle,PANEL_2_MIDDLE,middle);

SetCtrlVal(panelHandle,PANEL_2_RING,ring);

SetCtrlVal(panelHandle,PANEL_2_PINKY,pinky);

SetCtrlVal(panelHandle,PANEL_2_YAWANGLE,(*p5State).FilterYaw);

SetCtrlVal(panelHandle,PANEL_2_PITCHANGLE,(*p5State).FilterPitch);

SetCtrlVal(panelHandle,PANEL_2_ROLLANGLE,(*p5State).FilterRoll);

SetCtrlVal(panelHandle,PANEL_2_VERTHEAD1,(*P5_GetInfoPointer(p5ID)).Head1_VAngle);

SetCtrlVal(panelHandle,PANEL_2_HORHEAD1,(*P5_GetInfoPointer(p5ID)).Head_HAngle);

SetCtrlVal(panelHandle,PANEL_2_VERTHEAD2,(*P5_GetInfoPointer(p5ID)).Head2_VAngle);

SetCtrlVal(panelHandle,PANEL_2_HORHEAD2,(*P5_GetInfoPointer(p5ID)).Head2_HAngle);

}

break;

}

return 0;

}

int CVICALLBACK ConfigCallBack (int panel, int control, int event, //voi configura portul serial

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

if ( OpenComConfig (6, "", 2400, 0, 8, 1, 512, 512)>=0)

EnablePanelControls(0);

break;

}

return 0;

}

int CVICALLBACK fRecord (int panel, int control, int event, //Daca apas pe butonul record voi schimba valoarea variabilei recStatus in 1

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

recStatus=1;

vectSize=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

break;

}

return 0;

}

int CVICALLBACK fTimerRecord (int panel, int control, int event, //Aici va fi timerul 2 prin care voi trimite ce am inregistrat

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_TIMER_TICK:

if(vectIterator < vectSize)

{

success=ComWrtByte (6,10);

success=ComWrtByte (6, xPosVect[vectIterator]);

success=ComWrtByte (6,20);

success=ComWrtByte (6,yPosVect[vectIterator]);

success=ComWrtByte (6,30);

success=ComWrtByte (6, zPosVect[vectIterator]);

success=ComWrtByte (6,40);

success=ComWrtByte (6, fingerAvgVect[vectIterator]);

vectIterator+=1;

}

break;

}

return 0;

}

int CVICALLBACK fPlayRecord (int panel, int control, int event, //Pentru a trimite ce am inregistrat, resetez vectIterator, opresc timer 1 si pornesc timer 2

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

vectIterator=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,0);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,1);

break;

}

return 0;

}

int CVICALLBACK fStopRecording (int panel, int control, int event, //pentru a opri inregistrarea opresc ambele timere

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

recStatus=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,0);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

break;

}

return 0;

}

int CVICALLBACK fPanelInfo (int panel, int event, void *callbackData, //in aceasta functie stabilesc care este panoul activ (cu care interactioneaza utilizatorul)

int eventData1, int eventData2)

{

switch (event)

{

case EVENT_GOT_FOCUS:

panelHandle=PANEL_2;

break;

case EVENT_LOST_FOCUS:

break;

case EVENT_CLOSE:

DiscardPanel (panelHandle);

panelHandle=PANEL;

break;

}

return 0;

}

void EnablePanelControls (int enable) //functie pentru activarea/ dezactivarea butoanelor

{

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON, ATTR_DIMMED, enable);

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_3 , ATTR_DIMMED, enable);

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_4, ATTR_DIMMED, enable);

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_5, ATTR_DIMMED, enable);

}

float filter (float actVal, float prevVal) // inegrator pentru netezirea miscarilor

{

float retVal=0;

retVal=alpha*actVal+(1-alpha)*prevVal;

return retVal ;

}

int CVICALLBACK fMoreInfo (int panel, int control, int event,

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

panelHandle=LoadPanel (0, "P52.uir", PANEL_2);

DisplayPanel (panelHandle);

break;

}

return 0;

}

int CVICALLBACK fMouseState (int panel, int control, int event, //functie folosita pentru a seta utilizarea manusii pentru controlul cursorului

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

mouseState=P5_GetMouseState(p5ID);

if(mouseState==1)

{

P5_SetMouseState(p5ID,0);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,0);

}

else if(mouseState==0)

{

P5_SetMouseState(p5ID,1);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,1);

}

break;

}

return 0;

}

int CVICALLBACK fFilter (int panel, int control, int event,

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

GetCtrlVal (panel, PANEL_FILTER_VALUE, &alpha);

alphaP=100*alpha;

notAlphaP=100-alphaP;

if(alphaP==0 || alphaP==100)

SetCtrlVal (PANEL, PANEL_NUMERIC_CURRENT, (int)alphaP);

else

SetCtrlVal (PANEL, PANEL_NUMERIC_CURRENT, (int)alphaP+1);

SetCtrlVal (PANEL, PANEL_NUMERIC_PREVIOUS, (int)notAlphaP);

break;

}

return 0;

}

Anexa 1.C Codul sursă al aplicației care rulează pe microcontroler

#include "MCF5213.h"

#include "MCF5213_GPIO.h"

typedef volatile uint8 vuint8; /* 8 bits */

typedef volatile uint16 vuint16; /* 16 bits */

typedef volatile uint32 vuint32; /* 32 bits */

/*Including used modules for compiling procedure*/

#include "Cpu.h"

#include "Events.h"

#include "UART0.h"

#include "Pwm1.h"

/*Include shared modules, which are used for whole project*/

#include "PE_Types.h"

#include "PE_Error.h"

#include "PE_Const.h"

#include "IO_Map.h"

/* System Clock Info */

#define SYSTEM_CLOCK_KHZ (80000)

/****** COMANDA LED-uri ******/

void initLEDs (void);

void LED1 (char stareLED); //stareLED = 1 – aprinde LED-ul corespunzator pozitie in binar

void LED2 (char stareLED); //stareLED = 0 – stinge

void LED3 (char stareLED);

void LED4 (char stareLED);

void leds_display(uint8 number); //primii 4 biti LSB reprez. starea unui LED

void main(void)

{

/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/

PE_low_level_init();

/*** End of Processor Expert internal initialization. ***/

for(;;){}

/*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/

} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

MySerial.C

//lucru pe intreruperi

#include "MCF5213.h"

#include "MCF5213_UART.h"

#include "Pwm1.h"

unsigned char receivedChar;

char uart_getchar (int channel);

__declspec(interrupt) void SerialComMot(void)

{

receivedChar = uart_getchar (0);

if (receivedChar == 10)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193) )

/* if (receivedChar<xLast-3|| receivedChar>xLast+3)

{ */

setReg8(PWMDTY1, receivedChar); //motor dreapta

/* xLast=receivedChar;

}

else

{

setReg8(PWMDTY1,xLast);

}*/

}

if (receivedChar == 20)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193))

setReg8(PWMDTY3, receivedChar); //motor dreapta

}

if (receivedChar == 30)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193))

setReg8(PWMDTY5, receivedChar); //motor dreapta

}

if (receivedChar == 40)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193))

setReg8(PWMDTY7, receivedChar); //motor dreapta

}

}

//––––––––––––––––––––

//

// Wait for a character to be received on the specified UART

//

// Return Values: the received character

//

//––––––––––––––––––––

char uart_getchar (int channel)

{

/* Wait until character has been received */

while (!(MCF_UART_USR(channel) & MCF_UART_USR_RXRDY))

;

return MCF_UART_URB(channel);

}

http://ocw.mit.edu/courses/mechanical-engineering/2-12-introduction-to-robotics-fall-2005/lecture-notes/chapter3.pdfhttp://ocw.mit.edu/courses/mechanical-engineering/2-12-introduction-to-robotics-fall-2005/lecture-notes/chapter4.pdf

http://ocw.mit.edu/courses/mechanical-engineering/2-12-introduction-to-robotics-fall-2005/lecture-notes/chapter2.pdf

MFC5213 ColdFire reference manual

Anexă

Anexa 1.A Fișierul P5DLLc.h

/******************************************************************************

File: p5dllc.h

Use: wrapper that is exposed to the world.

Authors: Av Utukuri, Igor Borysov, Carl Kenner

Updates

Aug 24\01 – Av Utukuri – Finished V1.0, made all variables follow hungarian notation, cleaned up dead code

Jul 15\04 – Carl Kenner – Added non-C++ exported functions, extra features, new data structures and more error codes

******************************************************************************/

#ifndef _P5DLL_H_

#define _P5DLL_H_

/*

#define TEST_DLL

#define P5DLL_DEBUG

*/

#ifdef P5DLL_EXPORTS

#define P5DLL_API __declspec(dllexport)

#else

#define P5DLL_API __declspec(dllimport)

#endif

/*

Contrary to its name, this is NOT the head separation value

Actually this is the fraction of an inch that all the coordinates are returned in.

1 inch = 51.2 units. The real head separation value is 7.9 inches.

*/

#define P5HEAD_SEPARATION 51.2f

/*

true or false values are declared as type P5BOOL

*/

#define P5BOOL unsigned int

/*

fingers definitions

*/

#define P5_THUMB 0x0

#define P5_INDEX 0x1

#define P5_MIDDLE 0x2

#define P5_RING 0x3

#define P5_PINKY 0x4

/*

filter modes definitions

*/

#define P5_FILTER_NONE 0x0

#define P5_FILTER_DEADBAND 0x1

#define P5_FILTER_AVERAGE 0x2

/*

[sic]

*/

#define P5_FILTER_DEADBEND 0x1

/*

Error Codes

*/

#define P5ERROR_NONE 0x00

#define P5ERROR_NOP5DETECTED 0x01

#define P5ERROR_P5DISCONNECTED 0x02

#define P5ERROR_P5CONNECTED 0x03

#define P5ERROR_INVALID_P5ID 0x04

#define P5ERROR_INVALID_P5GLOVETYPE 0x05

#define P5ERROR_WRONGFIRMWAREVER 0x06

#define P5ERROR_EXCEPTION 0x07

#define P5ERROR_GETTIME 8

#define P5ERROR_PARSEBEND 9

#define P5ERROR_PARSEBUTTONS 10

#define P5ERROR_PARSELEDNUM 11

#define P5ERROR_PARSELED 12

#define P5ERROR_UNWARP 13

#define P5ERROR_PROCESS 14

#define P5ERROR_PROCESSROTATION 15

#define P5ERROR_PROCESSRELATIVEPOSITION 16

#define P5ERROR_PROCESSRELATIVEAVGPOSITION 17

#define P5ERROR_PROCESSROTATIONREAL 18

#define P5ERROR_PROCESSABSOLUTEPOSITION 19

#define P5ERROR_PROCESSLEDPOSITIONS 20

#define P5ERROR_INTERNAL 21

/*

Glove types

*/

#define P5GLOVETYPE_RIGHTHAND 0x00

#define P5GLOVETYPE_LEFTHAND 0x01

/*

Distance units

*/

#define P5_UNITS (1.0f)

#define P5_CM (2.54f/51.2f)

#define P5_MM (P5_CM*10)

#define P5_INCHES (1.0f/51.2f)

#define P5_FEET (1.0f/51.2f/12.0f)

#define P5_M (P5_CM/100)

#define P5_KM (P5_M/1000)

/*

Time Units

*/

#define P5_SECONDS (1.0f)

#define P5_MINUTES (P5_SECONDS/60)

#define P5_HOURS (P5_MINUTES/60)

#define P5_FRAME (1.0f*P5_SECONDS/41.7f)

/*

Velocity Units

*/

#define P5_KPH (P5_KM/P5_HOURS)

#define P5_MPS (P5_M/P5_SECONDS)

/*

Acceleration Units

*/

#define P5_MPSPS (P5_M/P5_SECONDS/P5_SECONDS)

/*

Rotation Units

*/

#define P5_DEGREES (1.0f)

#define P5_RADIANS (3.14159/(180*P5_DEGREES))

#define P5_REVOLUTIONS (P5_DEGREES/360)

/*

What position to return via the old interface

*/

#define P5MODE_RELATIVE 0

#define P5MODE_ABSOLUTE 1

#define P5MODE_RELATIVEAVG 2

#define P5MODE_FILTERED 3

#define P5MODE_LED0 4

#define P5MODE_LED1 5

#define P5MODE_LED2 6

#define P5MODE_LED3 7

#define P5MODE_LED4 8

#define P5MODE_LED5 9

#define P5MODE_LED6 10

#define P5MODE_LED7 11

#define P5MODE_LED8 12

#define P5MODE_LED9 13

#define P5MODE_LEDBRIGHT 14

#define P5MODE_LEDBEST 15

/*

What rotation to return via the old interface

*/

#define P5MODE_ORIGINAL_PYR (256*0)

#define P5MODE_BETTER_PYR (256*10)

#define P5MODE_BETTER_YPR (256*11)

#define P5MODE_BETTER_RYP (256*12)

#define P5MODE_BETTER_RPY (256*13)

#define P5MODE_BETTER_PRY (256*14)

#define P5MODE_BETTER_YRP (256*15)

#define P5MODE_BETTER_MIX (256*16)

#define P5MODE_FILTERED_PYR (256*20)

#define P5MODE_FILTERED_YPR (256*21)

#define P5MODE_FILTERED_RYP (256*22)

#define P5MODE_FILTERED_RPY (256*23)

#define P5MODE_FILTERED_PRY (256*24)

#define P5MODE_FILTERED_YRP (256*25)

#define P5MODE_FILTERED_MIX (256*26)

/*////////////////////////////////////////////////////////////////////////////////////

P5Data

This is the original data structure that all the glove's information was stored in.

It is missing a lot of information, so two new data structures were created also.

The new data structures should be used instead of this one most of the time.

This one is mostly for backwards compatibility

////////////////////////////////////////////////////////////////////////////////////*/

typedef struct

{

/*

Actual P5 variables

*/

char VendorID[50], ProductID[50], Version[50];

char ProductString[255], ManufacturerString[255], SerialNumString[255];

int m_nDeviceID;

int m_nMajorRevisionNumber, m_nMinorRevisionNumber;

int m_nGloveType;

float m_fx, m_fy, m_fz; /* x,y,z of the hand */

float m_fyaw, m_fpitch, m_froll; /* yaw\pitch\roll of the hand */

unsigned char m_byBendSensor_Data[5]; /* All the bend sensor data */

unsigned char m_byButtons[4]; /* P5 Button data */

float m_fRotMat[3][3]; /* matrix for inverse kinematics */

/*

The fields below only exist in special debug versions of the DLL file.

But don't worry, the same information is available in the new P5State structure.

for distribution delete these lines

*/

#ifdef P5DLL_DEBUG

float VisLedPos[4][3]; /* X\Y\Z of visible LEDs */

float m_fV1Angle[4], m_fV2Angle[4], m_fHAngle[4]; /* Angle of entry of visible LEDs */

unsigned char m_byLEDNum[4]; /* Visible LEDs */

int m_nlastBrightLED[4];

unsigned char m_RawDistalSensor_Data[5];

unsigned int LED0[3], LED1[3], LED2[3], LED3[3];

float ActualLED_X[10];

float ActualLED_Y[10];

float ActualLED_Z[10];

#endif

}P5Data;

/*///////////////////////////////////////////////////////////////////////////////////

CP5DLL

This is the original C++ class that represents all the P5 Gloves on your system.

You just create one of these objects at the start of your C++ program,

then call the P5_Init() method. The object will then automatically update its

fields and structure arrays until you call the P5_Close() method.

This class is missing a lot of important functionality, and is limited to Visual C++

You are better off using the C style functions.

This is mostly just for backwards compatibility.

///////////////////////////////////////////////////////////////////////////////////*/

#include <pshpack8.h>

typedef struct {

/* Location */

float x, y, z; /* x,y,z of the hand */

float RelativeX, RelativeY, RelativeZ;

float RelativeAvgX, RelativeAvgY, RelativeAvgZ;

float FilterPos[3];

float Velocity[3];

float Acceleration[3];

/* Rotation */

float RotMat[3][3]; /* rotation matrix [row][column] like Direct3D, premultiply by row vector */

float pitch, yaw, roll;

float FilterRotMat[3][3];

float FilterPitch, FilterYaw, FilterRoll;

float AngularVelocity[3]; /* around the x, y, and z axes */

float AngularAcceleration[3];

/* Visibility */

int Visible;

/* Fingers */

unsigned char finger[5]; /* All the bend sensor data */

short FingerAbsolute[5];

float FingerVelocity[5];

float FingerAcceleration[5];

/* Buttons */

unsigned char button[4]; /*P5 Button data*/

/* Leds */

float LedPos[10][3]; /* Position of 10 LEDs */

float LedVelocity[10][3]; /* Velocity of 10 LEDs */

float LedGeometricAccuracy[10];/* Dot product of vectors to other LEDs */

unsigned char LedVisible[10]; /* 1 if visible, 0 if not */

int TrackedLed;

/* Visible Leds */

int VisibleLedCount;

signed char VisibleLedNumber[4]; /* Which of the 10 LEDs is each visible one */

int VisibleLedRaw[4][3]; /* Raw Led values -512 to +511 */

float VisibleLedV1Angle[4], VisibleLedV2Angle[4], VisibleLedHAngle[4]; /*Angle of entry of visible LEDs*/

float VisibleLedPos[4][3]; /* debug */

/* Time */

double TimeStamp;

float FrameRate;

long long Frame;

} P5State;

typedef struct {

char VendorID[52], ProductID[52], Version[52];

char ProductString[256], ManufacturerString[256], SerialNumString[256];

int DeviceID;

int MajorRevisionNumber, MinorRevisionNumber;

int GloveType;

/* Physical Led Layout */

float ActualLedPos[10][3];

float LEDDistances[10][10];

/* Global tan compensation data */

float Head1_VAngle, Head_HAngle, Head2_VAngle, Head2_HAngle;

float HeadSeparation;

/* AC Adaptrer Status */

P5BOOL ACAdapterStatus;

/* Finger Calibration */

int FingerStraight[5];

int FingerBent[5];

/* Handles */

void *DeviceHandle; /* PHID_DEVICE */

unsigned int ThreadId;

unsigned int ThreadHandle;

P5BOOL ThreadStatus;

} P5Info;

#include <poppack.h>

/* Init function – required on start of program */

P5DLL_API P5BOOL __stdcall P5_Init(void);

P5DLL_API void __stdcall P5_Close(void);

P5DLL_API void * __stdcall P5_GetCP5DLL();

P5DLL_API void * __stdcall P5_GetCP5();

P5DLL_API int __stdcall P5_GetCount();

P5DLL_API P5Data * __stdcall P5_GetDataPointer(int P5Id);

P5DLL_API P5State * __stdcall P5_GetStatePointer(int P5Id);

P5DLL_API P5Info * __stdcall P5_GetInfoPointer(int P5Id);

P5DLL_API void * __stdcall P5_GetPrivatePointer(int P5Id);

P5DLL_API void __stdcall P5_SaveBendSensors(int P5Id);

P5DLL_API void __stdcall P5_CalibrateBendSensors(int P5Id);

P5DLL_API void __stdcall P5_CalibratePositionData(int P5Id);

P5DLL_API void __stdcall P5_SetClickSensitivity(int P5Id, unsigned char leftvalue, unsigned char rightvalue, unsigned char middlevalue);

P5DLL_API void __stdcall P5_GetClickSensitivity(int P5Id, int *leftclick, int *rightclick, int *middleclick);

P5DLL_API P5BOOL __stdcall P5_GetMouseState(int P5Id);

P5DLL_API void __stdcall P5_SetMouseState(int P5Id, P5BOOL state);

P5DLL_API void __stdcall P5_SetMouseStickTime(int P5Id, unsigned char time);

P5DLL_API int __stdcall P5_GetMouseStickTime(int P5Id);

P5DLL_API void __stdcall P5_GetMouseButtonAllocation(int P5Id, int *leftclick, int *rightclick, int *middleclick);

P5DLL_API void __stdcall P5_SetMouseButtonAllocation(int P5Id, int leftclickfinger, int rightclickfinger, int middleclickfinger);

P5DLL_API P5BOOL __stdcall P5_GetLastError(int *P5Id, int *ErrorCode);

/* AC Adaptor */

P5DLL_API P5BOOL __stdcall P5_GetACAdaptorStatus(int P5Id);

/* Bend Sensors */

P5DLL_API void __stdcall P5_GetBendCalibration(int P5Id, int *ThumbStraight, int *ThumbBent,

int *IndexStraight, int *IndexBent,

int *MiddleStraight, int *MiddleBent,

int *RingStraight, int *RingBent,

int *PinkyStraight, int *PinkyBent);

P5DLL_API void __stdcall P5_GetRawFingerBends(int P5Id, int *thumb, int *index, int *middle, int *ring, int *pinky);

P5DLL_API void __stdcall P5_GetCalibrationData(int P5Id, unsigned char data[]);

/* Space Warp */

P5DLL_API void __stdcall P5_SetUnits(float units);

P5DLL_API void __stdcall P5_SetForwardZ(int z);

P5DLL_API void __stdcall P5_SetTowerPos(int P5Id, float x, float y, float z);

P5DLL_API void __stdcall P5_SetTowerRotMat(int P5Id, float RotMat[][3]);

P5DLL_API void __stdcall P5_TiltTowerForward(int P5Id, float degrees);

P5DLL_API void __stdcall P5_SpinTowerRight(int P5Id, float degrees);

P5DLL_API void __stdcall P5_TiltTowerRight(int P5Id, float degrees);

P5DLL_API void __stdcall P5_SetSensitivity(int P5Id, float multiplier);

P5DLL_API void __stdcall P5_SetTrackerUnwarp(int P5Id, float UnwarpX, float UnwarpY, float UnwarpZ, float UnwarpScaleX, float UnwarpScaleY, int NewUnwarp);

P5DLL_API void __stdcall P5_GetTrackerUnwarp(int P5Id, float *UnwarpX, float *UnwarpY, float *UnwarpZ, float *UnwarpScaleX, float *UnwarpScaleY, int *NewUnwarp);

/* Glove configuration */

P5DLL_API void __stdcall P5_SetLedPos(int P5Id, int led, float x, float y, float z);

P5DLL_API void __stdcall P5_SetGlovePos(int P5Id, float x, float y, float z);

P5DLL_API void __stdcall P5_SetGloveRotMat(int P5Id, float RotMat[][3]);

P5DLL_API void __stdcall P5_PitchGlove(int P5Id, float theta);

P5DLL_API void __stdcall P5_YawGlove(int P5Id, float theta);

P5DLL_API void __stdcall P5_RollGlove(int P5Id, float theta);

/* Filters */

P5DLL_API void __stdcall P5_SetInterfaceMode(int mode);

P5DLL_API void __stdcall P5_SetFilterMode(int filters);

P5DLL_API void __stdcall P5_SetFilterAmount(int ExtraFrames, float DeadDistance);

P5DLL_API void __stdcall P5_ClearArchive(int P5Id);

P5DLL_API void __stdcall P5_SetRequiredAccuracy(float accuracy);

P5DLL_API void __stdcall P5_SetPrediction(int prediction);

/* Mouse */

P5DLL_API void __stdcall P5_RestoreMouse(int P5Id);

P5DLL_API void __stdcall P5_UnRestoreMouse(int P5Id);

/* Low level hardware control */

P5DLL_API void __stdcall P5_SetFeature(int P5Id, unsigned char usage, unsigned char numvalues, unsigned char *reportdata);

P5DLL_API void __stdcall P5_SetFeatureReport(int P5Id, unsigned char report, unsigned char numvalues, unsigned char *reportdata);

P5DLL_API int __stdcall P5_GetFeature(int P5Id, unsigned char usage, unsigned char numvalues, unsigned char *reportdata);

P5DLL_API int __stdcall P5_GetFeatureReport(int P5Id, unsigned char report, unsigned char numvalues, unsigned char *reportdata);

/* Copy structures */

P5DLL_API void __stdcall P5_GetData(int P5Id, P5Data *data);

P5DLL_API void __stdcall P5_GetState(int P5Id, int frame, P5State *state);

P5DLL_API void __stdcall P5_GetInfo(int P5Id, P5Info *info);

/* Get values stored in structures */

P5DLL_API void __stdcall P5_GetAbsolutePos(int P5Id, float *x, float *y, float *z);

P5DLL_API void __stdcall P5_GetRelativePos(int P5Id, float *x, float *y, float *z);

P5DLL_API void __stdcall P5_GetRelativeAvgPos(int P5Id, float *x, float *y, float *z);

P5DLL_API void __stdcall P5_GetRotation(int P5Id, float *yaw, float *pitch, float *roll);

P5DLL_API void __stdcall P5_GetFingerBends(int P5Id, float *thumb, float *index, float *middle, float *ring, float *pinky);

P5DLL_API void __stdcall P5_GetButtons(int P5Id, float *A, float *B, float *C, float *D);

P5DLL_API P5BOOL __stdcall P5_GetThreadStatus(int P5Id);

#endif

Anexa 1.B Codul sursă al aplicației din LabWindowsCVI

#include <utility.h>

#include <cvirte.h>

#include <userint.h>

#include "P52.h"

#include <P5DLLc.h>

#include <rs232.h>

static int panelHandle;

static P5BOOL

initValue,

mouseState;

static float

alpha,

alphaP,

notAlphaP,

xPos,

xPosPrev=0,

yPos,

yPosPrev=0,

zPos,

thumb,

index,

middle,

ring,

pinky,

yaw,

pitch,

roll,

rollPrev=0,

fingerAverage,

head1_VAngle,

head1_HAngle,

head2_VAngle,

head2_HAngle;

int

xRs,

yRs,

zRs,

fingerAverageRs,

success;

static P5Info gloveInfo;

static P5State * p5State;

static int

p5ID,

xPosVect[1000],

yPosVect[1000],

zPosVect[1000],

fingerAvgVect[1000],

vectSize,

recStatus,

vectIterator;

/*–––––––––––––––––––––––––*/

/* Internal function prototypes */

/*–––––––––––––––––––––––––*/

void EnablePanelControls (int);

void EnablePanelTimers (int);

float filter (float,float);

int main (int argc, char *argv[])

{

initValue=P5_Init();

p5ID=gloveInfo.DeviceID;

P5_SetFilterMode(P5_FILTER_AVERAGE);

P5_SetFilterAmount(10, 0);

P5_SetRequiredAccuracy(10);

p5State=P5_GetStatePointer(p5ID);

if (InitCVIRTE (0, argv, 0) == 0)

return -1; /* out of memory */

if ((panelHandle = LoadPanel (0, "P52.uir", PANEL)) < 0)

return -1;

EnablePanelControls(1);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,P5_GetMouseState(p5ID));

DisplayPanel (panelHandle);

RunUserInterface ();

DiscardPanel (panelHandle);

return 0;

}

int CVICALLBACK fPanel (int panel, int event, void *callbackData,

int eventData1, int eventData2)

{

switch (event)

{

case EVENT_GOT_FOCUS:

panelHandle=PANEL;

break;

case EVENT_LOST_FOCUS:

break;

case EVENT_CLOSE:

P5_Close();

QuitUserInterface (0);

break;

}

return 0;

}

int CVICALLBACK fButton (int panel, int control, int event,

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,1);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

break;

}

return 0;

}

int CVICALLBACK fTimer (int panel, int control, int event, // O data la 100 de milisecunde voi actualiza informatiile si le voi trimite mai departe

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_TIMER_TICK:

thumb=(float)(*p5State).finger[0];

index=(float)(*p5State).finger[1];

middle=(float)(*p5State).finger[2];

ring=(float)(*p5State).finger[3];

pinky=(float)(*p5State).finger[4];

if(panelHandle==PANEL){

xPos=(*p5State).FilterPos[0];

xPos=filter(xPos,xPosPrev);

xPosPrev=xPos;

yPos=(*p5State).FilterPos[1];

yPos=filter(yPos,yPosPrev);

yPosPrev=yPos;

zPos=(*p5State).FilterPos[2];

fingerAverage=(thumb+index+middle+ring+pinky)/5;

SetCtrlVal(panelHandle,PANEL_COLORNUM,xPos);

SetCtrlVal(panelHandle,PANEL_COLORNUM_2,yPos);

SetCtrlVal(panelHandle,PANEL_COLORNUM_3,zPos);

xRs=(int)RoundRealToNearestInteger (0.066*(xPos)+127);

success=ComWrtByte (6,10);

success=ComWrtByte (6, xRs);

yRs= (int)RoundRealToNearestInteger (0.066*(-yPos)+127);

success=ComWrtByte (6,20);

success=ComWrtByte (6, yRs);

roll=(*p5State).FilterRoll;

roll=filter(roll,rollPrev);

rollPrev=roll;

zRs=(int)RoundRealToNearestInteger (0.55*roll+127) ;

success=ComWrtByte (6,30);

success=ComWrtByte (6, zRs);

fingerAverageRs=(int)RoundRealToNearestInteger (-2.2*fingerAverage+193) ;

success=ComWrtByte (6,40);

success=ComWrtByte (6, fingerAverageRs);

if(recStatus==1)

{

xPosVect[vectSize]=xRs;

yPosVect[vectSize]=yRs;

zPosVect[vectSize]=zRs;

fingerAvgVect[vectSize]=fingerAverageRs;

vectSize+=1;

}

}

else if(panelHandle==PANEL_2)

{

//P5_GetFingerBends(p5ID,&thumb,&index,&middle,&ring,&pinky);

//P5_GetRotation(p5ID, &yaw, &pitch,&roll);

SetCtrlVal(panelHandle,PANEL_2_THUMB,thumb);

SetCtrlVal(panelHandle,PANEL_2_INDEX ,index);

SetCtrlVal(panelHandle,PANEL_2_MIDDLE,middle);

SetCtrlVal(panelHandle,PANEL_2_RING,ring);

SetCtrlVal(panelHandle,PANEL_2_PINKY,pinky);

SetCtrlVal(panelHandle,PANEL_2_YAWANGLE,(*p5State).FilterYaw);

SetCtrlVal(panelHandle,PANEL_2_PITCHANGLE,(*p5State).FilterPitch);

SetCtrlVal(panelHandle,PANEL_2_ROLLANGLE,(*p5State).FilterRoll);

SetCtrlVal(panelHandle,PANEL_2_VERTHEAD1,(*P5_GetInfoPointer(p5ID)).Head1_VAngle);

SetCtrlVal(panelHandle,PANEL_2_HORHEAD1,(*P5_GetInfoPointer(p5ID)).Head_HAngle);

SetCtrlVal(panelHandle,PANEL_2_VERTHEAD2,(*P5_GetInfoPointer(p5ID)).Head2_VAngle);

SetCtrlVal(panelHandle,PANEL_2_HORHEAD2,(*P5_GetInfoPointer(p5ID)).Head2_HAngle);

}

break;

}

return 0;

}

int CVICALLBACK ConfigCallBack (int panel, int control, int event, //voi configura portul serial

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

if ( OpenComConfig (6, "", 2400, 0, 8, 1, 512, 512)>=0)

EnablePanelControls(0);

break;

}

return 0;

}

int CVICALLBACK fRecord (int panel, int control, int event, //Daca apas pe butonul record voi schimba valoarea variabilei recStatus in 1

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

recStatus=1;

vectSize=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

break;

}

return 0;

}

int CVICALLBACK fTimerRecord (int panel, int control, int event, //Aici va fi timerul 2 prin care voi trimite ce am inregistrat

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_TIMER_TICK:

if(vectIterator < vectSize)

{

success=ComWrtByte (6,10);

success=ComWrtByte (6, xPosVect[vectIterator]);

success=ComWrtByte (6,20);

success=ComWrtByte (6,yPosVect[vectIterator]);

success=ComWrtByte (6,30);

success=ComWrtByte (6, zPosVect[vectIterator]);

success=ComWrtByte (6,40);

success=ComWrtByte (6, fingerAvgVect[vectIterator]);

vectIterator+=1;

}

break;

}

return 0;

}

int CVICALLBACK fPlayRecord (int panel, int control, int event, //Pentru a trimite ce am inregistrat, resetez vectIterator, opresc timer 1 si pornesc timer 2

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

vectIterator=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,0);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,1);

break;

}

return 0;

}

int CVICALLBACK fStopRecording (int panel, int control, int event, //pentru a opri inregistrarea opresc ambele timere

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

recStatus=0;

SetCtrlAttribute(panelHandle,PANEL_TIMER,ATTR_ENABLED,0);

SetCtrlAttribute(panelHandle,PANEL_TIMER_2,ATTR_ENABLED,0);

break;

}

return 0;

}

int CVICALLBACK fPanelInfo (int panel, int event, void *callbackData, //in aceasta functie stabilesc care este panoul activ (cu care interactioneaza utilizatorul)

int eventData1, int eventData2)

{

switch (event)

{

case EVENT_GOT_FOCUS:

panelHandle=PANEL_2;

break;

case EVENT_LOST_FOCUS:

break;

case EVENT_CLOSE:

DiscardPanel (panelHandle);

panelHandle=PANEL;

break;

}

return 0;

}

void EnablePanelControls (int enable) //functie pentru activarea/ dezactivarea butoanelor

{

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON, ATTR_DIMMED, enable);

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_3 , ATTR_DIMMED, enable);

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_4, ATTR_DIMMED, enable);

SetCtrlAttribute (panelHandle, PANEL_COMMANDBUTTON_5, ATTR_DIMMED, enable);

}

float filter (float actVal, float prevVal) // inegrator pentru netezirea miscarilor

{

float retVal=0;

retVal=alpha*actVal+(1-alpha)*prevVal;

return retVal ;

}

int CVICALLBACK fMoreInfo (int panel, int control, int event,

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

panelHandle=LoadPanel (0, "P52.uir", PANEL_2);

DisplayPanel (panelHandle);

break;

}

return 0;

}

int CVICALLBACK fMouseState (int panel, int control, int event, //functie folosita pentru a seta utilizarea manusii pentru controlul cursorului

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

mouseState=P5_GetMouseState(p5ID);

if(mouseState==1)

{

P5_SetMouseState(p5ID,0);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,0);

}

else if(mouseState==0)

{

P5_SetMouseState(p5ID,1);

SetCtrlVal (PANEL,PANEL_P5MOUSESTATE,1);

}

break;

}

return 0;

}

int CVICALLBACK fFilter (int panel, int control, int event,

void *callbackData, int eventData1, int eventData2)

{

switch (event)

{

case EVENT_COMMIT:

GetCtrlVal (panel, PANEL_FILTER_VALUE, &alpha);

alphaP=100*alpha;

notAlphaP=100-alphaP;

if(alphaP==0 || alphaP==100)

SetCtrlVal (PANEL, PANEL_NUMERIC_CURRENT, (int)alphaP);

else

SetCtrlVal (PANEL, PANEL_NUMERIC_CURRENT, (int)alphaP+1);

SetCtrlVal (PANEL, PANEL_NUMERIC_PREVIOUS, (int)notAlphaP);

break;

}

return 0;

}

Anexa 1.C Codul sursă al aplicației care rulează pe microcontroler

#include "MCF5213.h"

#include "MCF5213_GPIO.h"

typedef volatile uint8 vuint8; /* 8 bits */

typedef volatile uint16 vuint16; /* 16 bits */

typedef volatile uint32 vuint32; /* 32 bits */

/*Including used modules for compiling procedure*/

#include "Cpu.h"

#include "Events.h"

#include "UART0.h"

#include "Pwm1.h"

/*Include shared modules, which are used for whole project*/

#include "PE_Types.h"

#include "PE_Error.h"

#include "PE_Const.h"

#include "IO_Map.h"

/* System Clock Info */

#define SYSTEM_CLOCK_KHZ (80000)

/****** COMANDA LED-uri ******/

void initLEDs (void);

void LED1 (char stareLED); //stareLED = 1 – aprinde LED-ul corespunzator pozitie in binar

void LED2 (char stareLED); //stareLED = 0 – stinge

void LED3 (char stareLED);

void LED4 (char stareLED);

void leds_display(uint8 number); //primii 4 biti LSB reprez. starea unui LED

void main(void)

{

/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/

PE_low_level_init();

/*** End of Processor Expert internal initialization. ***/

for(;;){}

/*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/

} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

MySerial.C

//lucru pe intreruperi

#include "MCF5213.h"

#include "MCF5213_UART.h"

#include "Pwm1.h"

unsigned char receivedChar;

char uart_getchar (int channel);

__declspec(interrupt) void SerialComMot(void)

{

receivedChar = uart_getchar (0);

if (receivedChar == 10)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193) )

/* if (receivedChar<xLast-3|| receivedChar>xLast+3)

{ */

setReg8(PWMDTY1, receivedChar); //motor dreapta

/* xLast=receivedChar;

}

else

{

setReg8(PWMDTY1,xLast);

}*/

}

if (receivedChar == 20)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193))

setReg8(PWMDTY3, receivedChar); //motor dreapta

}

if (receivedChar == 30)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193))

setReg8(PWMDTY5, receivedChar); //motor dreapta

}

if (receivedChar == 40)

{

receivedChar = uart_getchar (0);

if ( (61 <= receivedChar) && (receivedChar <= 193))

setReg8(PWMDTY7, receivedChar); //motor dreapta

}

}

//––––––––––––––––––––

//

// Wait for a character to be received on the specified UART

//

// Return Values: the received character

//

//––––––––––––––––––––

char uart_getchar (int channel)

{

/* Wait until character has been received */

while (!(MCF_UART_USR(channel) & MCF_UART_USR_RXRDY))

;

return MCF_UART_URB(channel);

}

Similar Posts