Servo styrning med mikroBasic

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Kalf
Inlägg: 249
Blev medlem: 5 november 2005, 09:59:45

Servo styrning med mikroBasic

Inlägg av Kalf »

Hej jag är ganska ny på PIC'ar Jag har ganska nyligen fåt några LEDs att blinka osv, jag har gjort en nightrider och liknande. Nu så hade jag tänkt att testa med att styra servon med min PIC. Då vet inte jag riktigt hur man skall göra för att få fram en frekvens som styr servot. Det jag testade med var att skriva så här:

Kod: Markera allt

program servo

main:
       TRISB = 0
       PORTB = %11111111
       delay_us(100)
       PORTB = %00000000
       Delay_ms(20)
       goto main

end.
Men ni behöver inte äns säga att det inte är så här man "skall" göra, men då kommer min fråga: Hur skall man skriva för att få fram en frekvens så servot funkar som det skall?

Compier: mikrobasic
PIC: 16F628A
OSC: Internal 4mhtz
Användarvisningsbild
Icecap
Inlägg: 26736
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Man skickar ingen "frekvens" man skickar pulser. De ska vara mellan 1 och 2 ms, 1,5ms är mittpunkt.

Pulsernas kan upprepas med upp till 50Hz.
sodjan
EF Sponsor
Inlägg: 43266
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Helt oberoende av vilket språk eller processor man använder (det
är ganska ointressant i sammanhanget), så använder amn en timer
som man sätter till lämpligt intervall.

Jag antar att du talar om ett standard RC servo med
1.5 +/- 0.5 ms puls med 20 ms intervall ?

1. Sätt timern till 20 ms, vänta på interrupt.
2. Sätt utgången hög, och sätt timern till 1.0 - 2.0 ms, vänta på interrupt...
3. Sätt utgången låg, tillbaka till 1...

Sen är det bara att implementera i det språk och på
den processor man råkar föredra...

Den lösning du har provat kanske fungerar *OM* processorn
inte ska göra något annat, vilket är väldigt ovanligt.
Användarvisningsbild
Kalf
Inlägg: 249
Blev medlem: 5 november 2005, 09:59:45

Inlägg av Kalf »

ok, då hade jag missupfattat det, men hur skall man göra då? Hur skickar man pulser med mikrobasic? För det är inte meningen att man skall göra det så som jag gjort det va?


EDIT: tack sodjan, då blir det till att läsa lite om timer funktioner då.
sodjan
EF Sponsor
Inlägg: 43266
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Hur skickar man pulser med mikrobasic?

Först sätter du en pinne "1", sedan vid ett senare tillfälle
sätter du den "0", voilá, du har fått en puls ! :-)

> För det är inte meningen att man skall göra det så som jag gjort det va?

Du behöver ju inte sätt *hela* PORTB, det räcker med den pinne
där servot är ansluten. Men det stora problemet med din kod är
delay_us(x). Det blir stökigt om du vill att processorn ska göra
något mer en att enbart generera en servopuls...
Användarvisningsbild
Icecap
Inlägg: 26736
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Ett sätt är att använda timer.

Detta kan igen göras på 2 sätt:
Antingen styrs timern till att lämna 1 puls av lagom längd och då används den puls till att styra med, detta kan dock bli ganska mycket pyssel.

Ett annat sätt är att sätta timern att ge täta interrupt och i ISR räkna ner en variabel. Vid att kolla värden och sätta/resetta pinnar kan man göra en timer puls som kan styras vid att variera en variabel Om du kör interrupten med 127,5KHz kommer 255 att motsvara 2ms.
sodjan
EF Sponsor
Inlägg: 43266
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Fördelen med Icecap's variant nr 2, är att man får en
generell "tidbas" i applikationen som kan användas till
annat, inte bara servostyrningen. Man kommer inte heller
att helt låsa upp en timer till servostyrningen.

Det hela är lite beroende på vilken övergripande struktur
man vill ha på applikationen (och vad som är möjligt att
åstakomma med Basic).
Användarvisningsbild
Kalf
Inlägg: 249
Blev medlem: 5 november 2005, 09:59:45

Inlägg av Kalf »

Jag hittade i exempel mappen till den compilern jag använder följande kod. Kan jag få hjälp med att förstå vad allting gör. Jag har testat den vad vad den gör då är att blinka en LED.

Kod: Markera allt

program tmr0

dim cnt as byte
dim a as byte
dim b  as byte

sub procedure interupt
   cnt = cnt + 1                ' increment value of cnt on every interupt
   TMR0 = 96
   INTCON = $20              ' set TOIE, clear TOIF
end sub

main:
   a = 0
   b = 1
   OPTION_REG = $84     'assign prescaler to TMR0
   trisb = 0                     'Desegnate portb as outport
   portb = $FF                 ' initialize portb
   cnt = 0                       ' initialize cnt
   TMR0 = 96
   INTCON = $A0            ' enable TMR0 interupt

   do
            if cnt = 200 then ' if cnt is 200, then toggle portb leds and reset cnt
              portb = not(portb)
              cnt = 0
            end if
   loop until 0 = 1
end.
Det ända jag egentligen inte fattar är var man bestämmer tiderna, vad INTCON är, Vad 1 och b gör.

Hoppas någon kan hjälpa mig med detta.
sodjan
EF Sponsor
Inlägg: 43266
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Kan jag få hjälp med att förstå vad allting gör.

Det räcker väl med det som är oklart.
Det mesta förstår du nog.

> vad INTCON är,

Tala om exakt *VAD* som är oklart i databladet angående INTCON !

> Vad 1...

??? 1 är väll 1...

> och b gör

Varken "a" eller "b" verkar ha någon direkt funktion.
Prova med att plocka bort raderan med
dim a as byte
dim b as byte
a = 0
b = 1

och se om det inte fungerar ändå...

> Hoppas någon kan hjälpa mig med detta.

Visst, men börja med databladet, så tar vi det som är
oklart sedan.
Användarvisningsbild
Kalf
Inlägg: 249
Blev medlem: 5 november 2005, 09:59:45

Inlägg av Kalf »

Okay, jag tittade i databladen för de som jag inte fattade och nu har jag skrivit egna kommentarer i koden, kan någon läsa igenom dem och se ifall de är rätt?

Kod: Markera allt

 program tmr0

dim cnt as byte

sub procedure interupt
   cnt = cnt + 1                ' lägger till 1 till cnt
   TMR0 = 96
   INTCON = $20              ' bestämmer att "enable all un-masked peripheral interupts" vad det nu betyder?
end sub

main:
   OPTION_REG = $84     '"portb pull-ups are disbled och prescaler rate är 1:32
   trisb = 0                     'port b är utgångar
   portb = $FF                 'sätter alla port b till 1or
   cnt = 0                       ' här börjar man räkna cnt.
   TMR0 = 96                 ' Detta fattar jag inte
   INTCON = $A0            ' eneble all un-masked interupts & enebelsTMR0 interupt

   do
            if cnt = 200 then ' är det här man bestämmer tiden?
              portb = not(portb) ' Här ändrar man väll 1or till 0or?
              cnt = 0  ' Återställer cnt till 0
            end if
   loop until 0 = 1   ' Detta fattar jag inte heller
end.
Som du kan läsa så är det vissa saker jag fortfarande inte fattar.

Var bestämmer jag tiden? Kan jag skriva in den i ms eller us någonstans eller måste jag räkna ut hur många cykler det kommer att ta? Hur gör jag då jag vill att den skall vara 1a en viss tid och 0a en annan, då kan jag väll inte använda mig av denna koden eller? Jag fattar förtfarande inte vad TMR0 = 96 gör?
sodjan
EF Sponsor
Inlägg: 43266
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

OK, då ska vi se...

> INTCON = $20
> ' bestämmer att "enable all un-masked peripheral interupts"...

Nej, du har läst fel. Kolla igen.
$20 = '00100000', inte '01000000'...

I sådana här fall är det tydligare att skriva "INTCON = b'0010000'.
(Eller hur man nu skriver binära konstanter i ditt verktyg...)

> TMR0 = 96

Sätter TMR0 = 96 (decimalt).
D.v.s det värde som TMR0 kommer att "räkna från".
Hur snabbt TMR0 räknar styra av OPTION_REG, alltså
i ditt fall med 32 us. TMR0 räknar uppåt, så det tar
256-92 = 164 "tics" innan ett interrupt, eller 164x32 =
(ca) 5.2 ms.

> if cnt = 200 then ' är det här man bestämmer tiden?

Det beror på vad du menar med "tiden".
Ovanstående betyder att det kommer att behövas 200
interrupts innan PORTB inverteras. Alltså i ditt fall
5.2x200 = ca 1 sek. Så varje sekund kommer PORTB
att ändra värde. En LED kommer att blinka med frekvensen
0.5 Hz.

> portb = not(portb) ' Här ändrar man väll 1or till 0or?

Ja.

> loop until 0 = 1 ' Detta fattar jag inte heller

När kommer 0 att vara = 1 ??
Svar : aldrig.
Alltså kommer do..loop aldrig att avslutas.
Helt OK och ett vanligt sätt att skriva det.

Notera att att program till en microcontroller aldrig får avslutas !
sodjan
EF Sponsor
Inlägg: 43266
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Det var visst ett par extra frågor...

> Kan jag skriva in den i ms eller us någonstans

Nja...
En microcontroller har inte en aning om vad "sekunder" är.
Det enda den känner till är "cykler". Sedan är det programmerarens
(d.v.s din) rol att översätta "sekunder" till "cykler". Det hela beror
naturligtsivs helt på vilken hastighet processorn körs i.

Generellt sätt...

Sedan kan en del verktyg ah hjälpmedel som tex "delay_us()", men
de bygger på att man även talar om för verktyget vilken
hastighet man tänker köra med. Om man sedan väljer att köra
processorn i en annan hastighet så blir det "fel", så klart...

Här är en av nackdelarna med att *inte* börja med assembler, det
tar lite längre tid att förstå hur en microcontroller fungerar.

> då kan jag väll inte använda mig av denna koden eller?

Tja, exakt just den där koden gör naturligtsvis bara det den gör
just nu. Men delar av det kan sannolikt återanvändas. Du måste
räkna lite på tider och cykler och justera där det behövs. Sannolikt
ett snabbara TMR0 interrupt och olika "if cnt = xxx" beroende
på om utgången är 0 eller 1. Just blinkar det ju ganska långsamt
jämfört med pulserna till ett servo.

> Jag fattar förtfarande inte vad TMR0 = 96 gör?

Den sätter bara register TMR0 till värdet 96 (decimalt).
Vad detta sedan har för konsekvenser beror på hur TMR0
är konfigurerad och framgår av kapitlet om TMR0 i databladet.
Användarvisningsbild
Kalf
Inlägg: 249
Blev medlem: 5 november 2005, 09:59:45

Inlägg av Kalf »

Nu fattar jag allt förutom hur man räknar.

>Hur snabbt TMR0 räknar styra av OPTION_REG, alltså
i ditt fall med 32 us. TMR0 räknar uppåt, så det tar
256-92 = 164 "tics" innan ett interrupt, eller 164x32 =
(ca) 5.2 ms.

menar du 96 istället för 92? annars var fårdu 92 ifrån?
"tics" är det cykler?
Hur får man 164 tics till ca 5.2ms? Som du sa så beror det på vilken klocka man använder. Jag använder den interna på 4mhtz.
Användarvisningsbild
JimmyAndersson
Inlägg: 26651
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Inlägg av JimmyAndersson »

Ska se om jag förstått rätt:

TMR0 är en 8bitars räknare och kan alltså räkna till 256 (255 om man inte tar med nollan.)

92 ska nog vara 96 som är värdet varifrån timern ska börja räkna. (Ställs in med TMR0 = 96)

Alltså räknar räknaren 256-96 = 160 "tics" innan det kommer ett interrupt.
Antal tics x prescalern = tiden i millisekunder. Dvs 160x32 = ca 5.2ms. (32 är alltså prescaler-raten.)
sodjan
EF Sponsor
Inlägg: 43266
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

92 ska vara 96, korrekt, fel av mig.

> "tics" är det cykler?

Jag hittade inget bra begrepp, en "tic" är ett "steg" på TMR0.
Tänk "tick-tack-tick-tack" på en klocka... :-)

Med prescaler 1:1 är 1 tic = 1 cykel.
Med prescaler 1:32 är 1 tic = 32 cykler.
O.s.v.

Alltså "högre" prescaler -> långsammare TMR0 (längre mellan varje "tic")...
Eller hur fasen man nu ska beskriva det... :-)

> Antal tics x prescalern = tiden i millisekunder.

Nja, Antal tics x prescalern = tiden i **cykler** !!

(Och en cykel är alltså *alltid* = oscillator frekvensen/4, eller utryckt
på annat sätt, Fcyc = Fosc/4, eller Tcyc = Tosc*4.

Just i det aktuella fallet (med en 4 Mhz klocka) är det så att 1 cykel =
1 microsekund (inte millisekund). Med en annan hastighet blir
det något annat, så klart.

För övrigt är ser det OK ut...
Skriv svar