Servojen ohjaus Atmellin timerillä

Olen tässä miettinyt, että miten atmellillä saisi ohjattua useampaa, vaikkapa jopa 8 servoa saman aikaisesti, toisistaan riippumattomasti. Keksin omasta mielestäni tähän erittäin hyvän ajatuksen, mutta en tiedä toimiiko se. Jos liitetään vaikka yksi täysi portillinen servoja niin että PA0:ssa on ensimmäinen, PA1:ssä seuraava, jne.

Servollehan siis annetaan pulssi jonka kesto on 1-2ms (riippuen halutusta asennosta) ja tämä pulssi annetaan 20ms:n välein.

Jos Atmellille virittäisi timerin niin, että se laskisi vaikkapa 0-10 000 20ms aikana. Tämän jälkeen määritellään servoille halutut asennot väliltä 500-1000 vaikkapa seuraavalla tavalla:

1 = 852, 2 = 554, 3 = 682, 4 = 700, 5 = 980, 6 = 888, 7 = 799, 8 = 689

Sitten timer laitetaan k√§yntiin, kaikki servojen I/O nastat nostetaan loogiseen ykk√∂s ‚Äď tilaan, eli servon ohjauspulssi nousee ykk√∂seksi. T√§m√§n j√§lkeen timerin compare match arvoon laitetaan sen servon arvo joka on pienin, eli 554 (servo 2). Kun timer on lasekut 554, tulee compare match keskeytys, jossa tutkitaan, mink√§ servon arvo oli 554. T√§m√§n j√§lkeen sen servon I/O nasta painetaan loogiseen nolla ‚Äď tilaan. T√§ss√§ tapauksessa se oli servo2. Seuraavaksi asetetaan compare match arvoksi seuraavaksi suurin arvo, eli t√§ss√§ tapauksessa 682, eli siis servon 3 arvo. Sitten jatketaan timerin laskemista, kunnes tullaan arvoon 682, saadaan j√§lleen compare match keskeytys, jolloin painetaan servon 3 ohjaus pinni alas. T√§t√§ jatkuu kunnes jokainen servo on k√§yty l√§pi, jonka j√§lkeen lasketaan timeri√§ niin pitk√§√§n, ett√§ saavutetaan 10 000 jonka j√§lkeen timer nollataan. Nollauksen yhteydess√§ sitten I/O:t nostetaan yl√∂s seuraavaa pulssia varten.

Sitten timerin maksimi arvo sekä servojen ohjaus alue voidaan määritellä sen mukaan millaisen resoluution servojen ohjaukseeen tarvitsee…

Voiko tämä toimia, vai olenko vain nähnyt hyviä unia?

Toki se noinkin onnistuu, mutta voit myös main loopissa pollailla timerin arvoa ja sen mukaan ohjailla i/o:ita, jolloin sinulla jää vielä timeri muuhun käyttöön.

No itseasiassa noin sitä olen yhdessä paikassa tehnytkin, (tosin sovelluksessa vain yksi servo) mutta tuossa voi olla vaarana se, että jos main looppiin alkaa kertymään paljon koodia, esimerkiksi näytön päivittäminen, nappien pollaus tms. niin jos sattuu, että sielä sitten tuleekin aliohjelmakutsuja ja muita vastaavia, main loopin kiertoaika pitenee. Kun sitten kiertoaika pitenee, myös servojen ohjauspulssin pituus voi muuttua ja näin servojen asentokin muuttuu.

Eikö noi servot voi päivittää peräjälkeen about 2ms välein?

Eli yksi timeri, mikä juoksee 2sm aikaa. Komparelta saadulla keskeytyksellä nollataan kyseisen servon ohjausnasta. Kun 2 ms tulee täyteen niin siirrytään ohjaamaan seuraavaa servoa ja päivitetään haluttu kompare arvo.

Toivottavasti ei tullu pahoja ajattelumokia, kun meinaa painaa jo väsymys päälle.

No tuohan toimii. Mikäänhän ei estä servoja käskemästä peräkkäin. Ainoa mikä tuosta nyt puuttuu, on sitten tuo 20ms sekvenssi? Eli millä voidaan varmistaa, että ensimmäinen servo saa seuraavan pulssin sitten 20ms päästä?

Sen 20 ms sekvenssin saa aikaan käyttämällä 2 ms ajastinta. Keskeytys aliohjelmassa lasketaan kuinka monta kertaa 2 ms ajatimen keskeytys on tapahtunut.

Nyt käsitit väärin. Servojen asento ei muutu, jos vertailet timerin arvoon ajastuksia. Et siis kellota main-loopin kiertoajalla. Niin kauan kuin main looppi on nopeampi kuin lyhyin servolle annettava pulssi, homma toimii. Tietenkään loopissa ei saa olla mitään mikä blockaa, mutta eihän kukaan sellaista tekisikään. :wink:

Periaatteessa tässä tulee servon resoluutioksi kuinka nopeaa tuo pääluuppi pyörähtää. Ratkaisu tähän olisi pollata aluksi kaikki servon arvot läpi ja toivoa että minkään pulssit ei ole liian lähellä toisiaan (epätarkkuus). Tässä menis aikaa max 2.x ms sitten on aikaa pääluupille 17.x ms enneku pitää olla alussa.

Toinen ratkasu olisi käyttää keskeytyksillä. Ei ne niin pahoja ole, ellei sitte jotain arduinoa käytä :slight_smile:
Vaikka samallaista siinäkin on, mutta taitaa osa resursseista olla varattunä käyttikselle.

Niin, tuo minun alkuperäinen ehdotushan on täysin keskeytyspohjainen, eli jos globaaleita keskeytyksiä ei ole kielletty, niin kello keskeyttää aina kun servoille tarvitsee pulssin reuna nostaa tai laskea.

Näin main loopissa voi sitten päivittää LCD:tä tai periaatteessa tehtailla mitä vain koko loppuajan ja keskeytykset pitää huolen siitä, että servot pysyy aina paikoillaan.

Tietenkin riippuu sovelluksesta, jos vaikka joutuu kuuntelemaan sarjaportteja tai muuta vastaavaa, niin silloin on kellot pysäytettävä, että saadaan keskeytys aikaiseksi sarjaliikenteestä. Tarkoitan siis, että keskeytykset on tietenkin sitten priorisoitava niin, että kaikille tominnoille saadaan järjestettyä tarvittava määrä prosessoriaikaa. Mutta tämä on sitten toinen juttu.

Tai t√§ss√§kin on varmaan ylip√§√§t√§√§n sellainen tilanne, ett√§ jos kelloja tarvii kriittisemp√§√§n paikkaan kun servojen ohjaamiseen, kannattaa kello j√§tt√§√§ k√§ytt√§m√§tt√§ servojen ohjauksessa, mutta taas jos servojen ohjaus on kriittinen, ei minusta ole suuri menetys, jos yhdell√§ kellolla voidaan ohjata teoriassa ‚Äúm√§√§r√§tt√∂m√§sti‚ÄĚ servoja tarkasti.

Koska ‚Äúrajattoman‚ÄĚ servom√§√§r√§n ohjaus nyt on poissa kyseest√§ jo pinnien loppumisen takia, itse l√§htisin varmaan sille linjalle, ett√§ valitsisin kontrollerin tarvittavan servom√§√§r√§n mukaan. Hardiksella ohjaus kun s√§√§st√§√§ noita CPU-jaksoja aika tuhottomasti ja helpottaa muutenkin hommaa, etenkin jos on paljon l√§hell√§ toisiaan olevia asentoja.

Elikkä esim. ATtiny261/461/861 on kohtuu halpa ja sisältää deadtimella ja invertoiduilla lähdöillä varustetun timerin kolmella eri output compare linjalla eli ts. kuudella lähdöllä jo ainoastaan tuosta yhdestä timerista. Sitten on nuo AT90PWM-sarjan kontrollerit. Elikkä tällöin vain timerin prescalerit (ja mahdollisesti TOP-arvo) siten, että tulee ylivuotoajaksi tuo haluttu 20ms luuppiaika, ja sitten servojen asennot suoraan output compare rekistereihin 0…TOP-välille skaalattuna, ja kaikki hoituu raudalla (esim. PWM-asetuksilla set on BOTTOM, clear on compare match).

Jos tarvitaan enemmän kuin saa suoraan raudalla timereilla AVR:stä, niin itse katsoisin joko rinnakkaisia kontrollereita, eri kontrolleriperheitä (esim Cortex-M3) tai CPLD tai FPGA-piirejä ja sitten joku dataväylä jonka läpi pääkontrolleri kertoo mitä halutaan. Nuo CPLD:t ja/tai FPGA:t on kyllä aika passeleita juuri tällaisiin modulaattoreihin.

Itse tein jotain ton tapaista. Amforth pyöriin. Vaaditaan 16+kilon mikrokoira. yhteen taskiin aikakriittinen tavara ja loppuihin kaikki muu höttö. Amforth generoi käytännössä yhden keskeytyksen. Keskeytykset ovat poissa sen aikaa, että kirjoitetaan SREGin T bitti ylös. Sovellus oli ATMega168 päälle tehty, jossa oli 57600 sarjanaru bitbangillä ja siinä protokolla. Lisäksi oli DMX kirjoitus. Muuna mukavana oli 6 ADC:tä ja 5 karvaa PWM:llä softana. Tuo peijooni jaksaa

Tästä seurasi, että päätason ohjelmat oli määritetty lyhyiksi ja nukkumaan mentäessä, yksi pinni laskettiin alas ja värkki nukkui 60% ajasta.

FORTH on mun perversio. C/C++ itkemisen jälkeen tuntuu niin helpottavalta vain määrittää pieniä sanoja, jotka tekevät pieniä asioita ja aina oikein. Sitten ne pinotaan peräkkäin. (sanaleikki tarkoitettu) Lopuksi saadaan jotain tän näköistä:
: pesukone pese huuhtele linkoa ;
Tämä ei tosiaankaan sitten sovi kaikille ja yhdellekään kaupalliselle ei ollenkaan. On vain tautisen nopeaa kirjoittaa testipenkkejä pöytäkoneen C softille esimerkiksi ficl nimisellä värkillä.