PWM signal på en 16f628a i Pascal

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
brange
Inlägg: 66
Blev medlem: 28 november 2005, 15:01:20

PWM signal på en 16f628a i Pascal

Inlägg av brange »

Hejsan..

Har precis börjat med PIC och programmerat lite i Pascal.
Har nu ett gammalt RC servo som jag skulle vilja styra, och då behöver jag en PWM "puls"

Problemet är då självklart att jag inte får det att fungera.
Såhär står det i hjälpfilen till mikroPascal(min kompilator)

Kod: Markera allt

Note: These routines support module on RC2, and wont work with modules on other ports. You can find examples for PICmicros with module on other ports in mikroPascal installation folder, subfolder Examples. Also, mikroPascal does not support enhanced PWM modules.
Nu har ju 16f628a ingen PORTC, så där har vi första problemet, men det står att man kan hitta exempel med moduler på andra portar i mikropascals mapp, men där finns inga.
Alla exempel som följde med använder sig av PORTC.

Såhär ser koden ut (från exemplena)

Kod: Markera allt

program PWM_lib;

var j : byte;

begin
  TRISB := 0;             // designate portb pins as output
  PORTB := 0;             // set PORTB to 0
  j     := 180;           // initial value for j
  TRISC := 0;             // designate portc pins as output
  PORTC := $FF;           // set portc to $FF
  PWM_Init(5000);         // initialize PWM module
  PWM_start;              // start PWM
  while true do           // endless loop
    begin
      delay_ms(10);       // wait 10ms
      j := j+1;           // increment j
      PWM_Change_Duty(j); // set new duty ratio
      PORTB :=  CCPR1L;   // send value of CCPR1L to PORTB
    end;
end.
*EDIT*
Felet jag får(första felet) är:
11:17 3 Identifier 'Pwm_Init' was not declared test3_pwm_servo.ppas
den klagar alltså på denna rad:
PWM_Init(5000); // initialize PWM module

Någon som har idéer eller kanske har kört PWM i Pascal(eller basic eller annat språk) på en PIC som inte har PORTC?

Tacksam för hjälp.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Vi talar alltså om en vanlig servo signal med (ca) 20 ms intervall och 1-2 ms puls ?
PIC'arnas PWM modul är inte konstruerad för så låga frekvenser.
Enklast är att ha en timer som ger de två intervallen (1-2 resp 20 ms) och
sedan i ISR'en "flippa" en pinne som styr servot.

Om man vill styra mer än ett servo så går det lika bra. För t.ex 4 servon
så låter man en timer i tur och ordning ta fyra pulstider, var och en 1-2 ms,
och sedan lägga till en 16 ms paus för att få det hela 20 ms intervallet.

> Någon som har idéer eller kanske har kört PWM i Pascal(eller basic eller annat språk) på en PIC som inte har PORTC?

Ja.
brange
Inlägg: 66
Blev medlem: 28 november 2005, 15:01:20

Inlägg av brange »

Okej, tack så mycket.
Tror jag förstår vad du menar, får testa det sen när jag har lite tid över.

*edit*
har testat lite nu.
Med denna koden:
while Button(PORTA,1,1,1) do
begin
//knapp tryckt.
PORTB.1 :=1;
//skicka puls
PORTB.2 :=1;
Delay_ms(1);
PORTB.2 :=0;
Delay_ms(18);
PORTB.2 :=1;
Delay_ms(1);
PORTB.2 :=0;
end;
if Button(PORTA,1,1,0) then
PORTB.1 :=0;
Det gör så när jag trycker in min knapp på PORTA.1 så vrider sig servo lite.
Men hur ska jag göra för att kunna få det i olika lägen och vrida det ännu mer?
Någon som vet någon bra sida om RC Servo?
Användarvisningsbild
lgrfbs
Inlägg: 7311
Blev medlem: 28 januari 2005, 15:48:53
Ort: X-län
Kontakt:

Inlägg av lgrfbs »

Prova variabel delay ska ha kommit med i nyaste versionen av mikroPascal.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Jag vet inte hur du har tänkt, men som det är nu så
skickar du väll bara samma pulslängd hela tiden ?
Det ser också ut som om du skickar två pulser
snabbt efter varandra !? Hur har du tänkt ?

1-2 ms ger olika lägen på servot. Så pulsen skall
vara mellan 1 och 2 ms beroende på vilket "läge"
du vill att servot skall ha.

Det fungerar, som du säkert förstår, INTE med en
delay som bara kan ta hela ms som parameter !

Det finns fler saker som du måste reda ut innan
du skriver någon kod. T.ex :
- Vilken upplöning (antal steg) vill du ha ?
- Vad skall applikationen gör för övrigt ?

Sen är det bäst att ha en bakgrunds process (sannolikt
triggat av ett timer imterrupt) som sköter pulserna.
Din förgrundsprocess kan sedan sköta knapparna
och inställning av pulslängd (d.v.s servo position).
brange
Inlägg: 66
Blev medlem: 28 november 2005, 15:01:20

Inlägg av brange »

fick det att funka nu.
skapade en loop som pulsar såhär:
hög 1ms
låg 25ms
hög 1ms
låg 25ms
osv.
det gör så det snurrar åt ena hållet,
sen andra hållet så låter jag den vara hög i 2ms.
Funkar, men går säkert att göra på smartare sätt än vad jag har gjort, men men :)
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Var det bara två olika lägen du ville ha ?
Ja, då fungerar det så. Jag trodde att du ville
kunna posotionera servot lite var som helst...
brange
Inlägg: 66
Blev medlem: 28 november 2005, 15:01:20

Inlägg av brange »

Två lägen?
nja, men jag kan få mer än två lägen med min kod, den ser ut såhär:

Kod: Markera allt

while Button(PORTA,0,1,1) do
   //knapp ett tryckt.(ccw)
   begin
   //skicka puls
   inc(j);
   PORTB.2 :=1;
   Delay_ms(2);
   PORTB.2 :=0;
   PORTB.3 :=1;
   Delay_ms(25);
   PORTB.3 :=0;
   Delay_ms(50);
   end;
När jag håller ner knappen som är kopplad till PORTA.0 så roterar den moturs.(småsteg åt gången)

>Ja, då fungerar det så. Jag trodde att du ville
>kunna posotionera servot lite var som helst...
Menar du att man kan typ pulsa en puls så lägger sig servot i den "positionen"?
typ:

Kod: Markera allt

PORTB.2 :=1;
delauy_ms(1);
PORTB.2 :=0;
delay_ms(25);
Så roterar servot helt tillbaka.
Kanske inte just den pulsen då utan någon liknande?
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Ett normalt RC servo ställer sig i ett läge mellan två ytterlägen beroende
på en pulsens längd 1-2 ms.

1 ms = ena ändläget.
2 ms = andra ändläget.
1.5 ms = mittenläget.
1.25 ms = 25% från första ändläget.
1.75 ms = 25% mot andra ändläget.

o.s.v...

Om du t.ex vill ha 100 olika möjliga positioner,
behöver du en upplösning på 10 us på pulsen.
Alltså 1.000, 1.010, 1.020,.... 1.980, 1.990, 2.000 ms.

Man "duttar" inte fram servot.
Däremot om servot tappar signalen, så lär det stanna där det är just då.

Sen finns det specialservon (from Parallax t.ex) där man har kopplat bort
återkopplingen i servot, så det snurrar kontinuerligt åt ena eller andra
hållet beroende på om pulsen är > eller < 1.5 ms.

Servot skall ha ett *kontinuerligt* pulståg med 1-2 ms pulser med (ca)
20 ms intervall. Detta skall normalt aldrig avbrytas under drift.

> När jag håller ner knappen som är kopplad till PORTA.0 så roterar den moturs.(småsteg åt gången)

Det ska det inte göra. Det skall snabbt inta rätt position beroende på
pulsens längd. Jag har inte sätt sätt att du varierar pulsens längd.
brange
Inlägg: 66
Blev medlem: 28 november 2005, 15:01:20

Inlägg av brange »

ah, okej. Nu börjar det klarna upp för mig=)

Har lyckats göra ett bra program som fungerar rätt så bra, men då med pulserna i "main-loopen". Men jag skulle gärna vilja så den skickar pulser hela tiden i någon backgrunds process.
Om jag har förstått det rätt så kan man göra det med "procedure interrupt;" i mikroPascal.
Denna koden jag har skrivit fungerar dock inte(fungerar om jag skickar pulserna från mainloopen).

När jag försöker kompilera det så får jag detta felmedelandet:
0:0 0 Linker error: CALL from interrupt test6_pwm3.ppas
Om jag tyder det rätt så betyder det att jag inte kan "kalla" på någon anna procedure från interrupt procedure.
Men utan att göra det så vet jag inte hur jag ska kunna fixa detta.

Någon som har några tips?

Kod: Markera allt

//j är en variabel som jag ändrar m.h.a. två knappar.(j är alltså pulslängden i µs)
procedure pause(var i: word);
begin
if i=1000 then
delay_us(1000);
//alla här imellan också....
if i=1900 then
delay_us(1900);
if i=2000 then
delay_us(2000);
end;

procedure interrupt;
begin
j:=1500;
while true do
begin
 PORTB.3 :=1;
 pause(j);
 PORTB.3 :=0;
 delay_ms(20);
 end;
end;
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Välj en timer som har ett område som passar (med hänsyn till post/pre-scaler o.s.v).
628A har väll tre timers, och jag kan inte säga på rak arm vilken
som är lämpligast, TMR1 kanske...

Exakt hur timern skall sättas upp beror bl.a på vilken "upplösning"
("resolution") du vill att 1-2 ms intervallet skall ha. Eller med andra
ord, hur många distinkta "lägen" servot skall kunna inta.

Trigga igång timern från din init rutin och se samtidigt till att sätta upp
interrupt miljön så att timern ger interrupt.

I din "procedure interrupt;" får du sedan se till att ge timern lämpliga nya
startvärden innan du avslutar procen, samt samtidigt se till att pinnen
pulsas på rätt sätt. Inga delay_xx() eller pause() här !!

I princip är det bara två värden du behöver hantera, ett fixed värde som ger
20 ms (ca, inte kritiskt) intervallet, och ett variabelt som ger 1-2 ms
intervallet.

Du ska *inte* använda pause() eller delay_xx() överhuvud taget för
servo hanteringen. Varken i main eller någon annanstans.

På detta sätt kommer servopuls genereringen att bli helt transparant
för din main loop, som nu enbart behöver kolla knappar och beräkna
nya värden för 1-2 ms pulsen (alltså det värde som interrupt procen
sedan använder för att sätta timern).

Detta är "rätt" sätt att bygga det på. Fråga bara på om något var
oklart !
brange
Inlägg: 66
Blev medlem: 28 november 2005, 15:01:20

Inlägg av brange »

Okej, tack så mycket.

Får ta och läsa på lite om timers och så. återkommer senare när jag har problem eller lösning :)

*återkommen* :)
har läst lite nu, och testat lite.
TMR1 verkar vara bra till detta, står i databladet att den kan köras i två lägen(modes):
som en timer
eller som en counter.

För att sätta prescalen så använder jag denna kodsnutt:
OPTION_REG := 0; // assign prescaler to TMR1
Då borde den blir 1:1, eller?
Och om den är 1:1 och jag har en 20Mhz kristall, så borde väll denna interrupt koden räkna count :=count+1 20miljoner gånger/sekund?

Kod: Markera allt

//j är pulslängden
//k är 20ms. Om jag tänkt rätt med 20M/sek, så borde den vara 40 000 för att det ska bli 20ms.
//PORTB.3 är kopplad till servots PWM sladd.
procedure interrupt;
begin
   count :=count+1;
   if count=j then
   begin
      PORTB.3 :=0;
   end;
   if count=k then
   begin
      PORTB.3 :=1;
      count :=0;
   end;
end;
här är resten av koden till själva timern, har tagit och försök förstå på vad det gör från exempel som följde med mikropascal

Kod: Markera allt

  OPTION_REG := 0;       // assign prescaler to TMR1
  T1CON := 1;
  clearbit(pir1,TMR1IF);    // clear TMR1IF
  PIE1  :=   1;             // enable interrupts
  portb := $F0;
  count :=   0;             // initialize count,count2
  INTCON:= $C0;
Jag vet inte vad som är fel, bara att något är fel.
Servo rör sig bara lite då och då, och det blir inte heller någon skillnad(vad jag skan se med ögonen) när jag ändrar på variablen j(pulslängden).

Några tips?=)
Skriv svar