Sida 1 av 2
Servo styrning med mikroBasic
Postat: 2 juni 2006, 14:27:13
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
Postat: 2 juni 2006, 14:37:19
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.
Postat: 2 juni 2006, 14:39:36
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.
Postat: 2 juni 2006, 14:40:28
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å.
Postat: 2 juni 2006, 15:00:23
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...
Postat: 2 juni 2006, 15:10:39
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.
Postat: 2 juni 2006, 15:22:36
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).
Postat: 2 juni 2006, 15:22:42
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.
Postat: 2 juni 2006, 15:29:09
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.
Postat: 2 juni 2006, 15:51:07
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?
Postat: 2 juni 2006, 16:45:50
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 !
Postat: 2 juni 2006, 16:57:51
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.
Postat: 2 juni 2006, 17:04:20
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.
Postat: 2 juni 2006, 21:03:24
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.)
Postat: 2 juni 2006, 21:44:01
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...