Sida 1 av 1

PIC18LF2320: Encoder-rutin. *Löst*

Postat: 16 augusti 2006, 22:44:06
av JimmyAndersson
PIC18LF2320 i 8MHz - MikroBasic - Rotationsensor (encoder)

Gokväll! :)
Börjar få lite fart på min encoder-rutin, men jag har några problem:
1) Vrider man fort så hänger den inte med.
2) Vrider man medsols (skrivet som "Ena hållet" i koden) så räknar den 0, 2, 4, 6, osv, istället för 1, 2, 3, 4.. Vrider man motsols så räknar den en siffra itaget.
3) Ibland (ca 1 gång av 30) så händer det att variabeln data1 hoppar fram flera siffror på en gång, trots att man vrider motsols.


Liten förklaring av koden:
LED1 är en 'symbol' och är ansluten till PORTB.2
ENCODER1a är ena pinnen på encodern. Ansluten till PORTB.4 som ger "interrupt on change".
ENCODER1b är andra pinnen på encodern. Ansluten till PORTC.0 som inte ger något interrupt alls.

Variabeln data1 visas på en LCD-display som en siffra mellan 0 - 9.
Variabeln min1 = 0. Variabeln max1 = 10.

Kod: Markera allt

sub procedure interrupt
  'Encoders
  if TestBit(INTCON, RBIF) = 1 then
     ClearBit(INTCON, RBIF)
     tmp1 = PORTB

     'Tänd LED1 för att visa att vi fått interrupt
     LED1 = 1
     
     'Läs av encoders
      nyEnc1a = ENCODER1a
      nyEnc1b = ENCODER1b
      if (nyEnc1a <> oldEnc1a) OR (nyEnc1b <> oldEnc1b) then
      
         'Ena hållet
         if (ENCODER1a = 1) AND (ENCODER1b = 0) then
            if data1 < max1 then data1 = data1 + 1 end if
            LED3 = 1
         end if
         if (ENCODER1a = 0) AND (ENCODER1b = 1) then
            if data1 < max1 then data1 = data1 + 1 end if
         end if

         'Andra hållet
         if (ENCODER1a = 0) AND (ENCODER1b = 0) then
            if data1 > min1 then data1 = data1 - 1 end if
            LED2 = 1
         end if
         if (ENCODER1a = 1) AND (ENCODER1b = 1) then
            if data1 > min1 then data1 = data1 - 1 end if
         end if

      end if
      
     oldEnc1a = nyEnc1a
     oldEnc1B = nyEnc1a

  end if
end sub

*Tyvärr* stödjer inte MikroBasic lookup-tables, så if-raderna är min lösning på problemet..


Alla tips är av intresse. Vill optimera rutinen så mycket som möjligt eftersom encodern ska styra spänningen på ett digitalt nätagg.


edit: Skrev helt fel ang. ENCODER1b. Den går inte alls till en IOC-pinne. Har ändrat det. Koden stämmer däremot.

Postat: 16 augusti 2006, 23:03:09
av sodjan
> 1) Vrider man fort så hänger den inte med.

Det kommer *alltid* att finnas en gräns där den inte hänger med... :-)
Lösningen är att inte vrida "för fort"...

> 2) 0.2.4.6...

Kan ha med att du har IOC på *båda* pinnarna. En räcker.
Det kan också ha med med hur du har satt flankerna...

> 3) hoppar fram flera siffror...

Även när du vrider sakta ?
Sannolikt kontaktstudsar, så du får flera interrupt.

> Vill optimera rutinen så mycket som möjligt eftersom encodern ska styra spänningen på ett digitalt nätagg.

Men inte i bara 10 steg, hoppas jag.
Normalt har man så många (och små) steg så att de fenomen du ser
inte märks.

Postat: 17 augusti 2006, 02:28:48
av JimmyAndersson
OBS:
Såg att jag skrev fel i första inlägget. :oops: Andra pinnen på encodern (B i tabellerna) är inte alls ansluten till en ingång som ger IOC. Såhär ska det vara:

ENCODER1a är ena pinnen på encodern. Ansluten till PORTB.4 som ger "interrupt on change".
ENCODER1b är andra pinnen på encodern. Ansluten till PORTC.0 som *inte* ger något interrupt alls.

Bättre att man upptäcker sånt sent än aldrig. :)



>> 3) hoppar fram flera siffror...

> Även när du vrider sakta ?

Jepp. Även om jag vrider så sakta det bara går.

> Sannolikt kontaktstudsar, så du får flera interrupt.

Testade att sätta kondensatorer vid encodern, som brukar avhjälpa kontaktstudsar, men det hjälpte tyvärr inte.

Om variabeln data1 är noll och jag vrider ett varv motsols, (som ska minska värdet om det är större än noll), så kan data1 hoppa upp till ett värde på 4 till 6, (lite olika från gång till gång.) Detta händer om encodern vrids ett varv på kortare än tre sekunder. Är det normalt?


>> Vill optimera rutinen så mycket som möjligt eftersom encodern ska
>> styra spänningen på ett digitalt nätagg.

>Men inte i bara 10 steg, hoppas jag.

Nädå. :) Jag ska ha ca 60 steg: 0.5V-30V där varje steg ger en förändring på 0.5V. (Eventuellt mindre steg, men det är en senare fråga.)


Lite bakgrund till if-raderna:

Vrider encodern åt ena hållet: (Medsols)
A B
0 0 <-- Ett hack (eller "Dentent stability point" om man föredrar det.)
1 0 <-- Ger interrupt
1 1 <-- Nästa hack
0 1 <-- Ger interrupt
och så vidare med 0 0 igen..

Vrider encodern åt andra hållet: (Motsols)
A B
0 0 <-- Hack. Ger interrupt
0 1
1 1 <-- Nästa hack. Ger interrupt
1 0
och så vidare med 0 0 igen

Postat: 17 augusti 2006, 11:02:32
av sodjan
Ett allvarligt fel är att du clearar RBIF *INNAN* du clearar missmatch condition.
Det fungerar inte, RBIF kommer att sättas direkt igen, och interruptet
bara loopar hela tiden. Det står tydligt i kapitlet om PORTRB att du måste
cleara missmatch condition *först*.

Flytta "ClearBit(INTCON, RBIF)" till slutet av ISR'en.

Postat: 17 augusti 2006, 13:04:18
av JimmyAndersson
Ah, där satt problemet! Nu fungerar det mycket bättre! :)

Postat: 17 augusti 2006, 13:40:24
av sodjan
Kul att det funkar.
Här är en variant (ej testad !) med lite förenklade tester.
Kanske att den ger lite effektivare asm-kod...

Kod: Markera allt

sub procedure interrupt
  'Encoders
  if TestBit(INTCON, RBIF) = 1 then

     'Tänd LED1 för att visa att vi fått interrupt
     LED1 = 1
     
     'Läs av encoders
      nyEnc = 0
      nyEnc.0 = ENCODER1a   'bit 0 = pinne-A
      nyEnc.1 = ENCODER1b   'bit 1 = pinne-B

      if (nyEnc <> oldEnc) then    ' något har ändrats !
     
         select case nyEnc
         case 1, 2            'Ena hållet, BA = '01' eller '10'
            if data1 < max1 then data1 = data1 + 1 end if
            LED3 = 1

         case 0, 3            'Andra hållet, BA = '00' eller '11'
            if data1 > min1 then data1 = data1 - 1 end if
            LED2 = 1
         end select

         oldEnc = nyEnc    'spara bara värdet om det har ändrats...
      end if
  end if
  ClearBit(INTCON, RBIF)
end sub

Postat: 17 augusti 2006, 14:41:13
av JimmyAndersson
Smidig kod. Lärde mig även ett nytt kommando: case. :)


Tyvärr har MikroBasic problem med dessa rader:

nyEnc.0 = ENCODER1a 'bit 0 = pinne-A
nyEnc.1 = ENCODER1b 'bit 1 = pinne-B

Jag får felmeddelandet:
"Argument is out of range"

Det verkar som det inte fungerar att komma åt enstaka bitar i en variabel.

Hm, finns det fler sätt att göra samma sak? Har letat i manualen men inte hittat något.


nyEnc och oldEnc är dimensionerade såhär:
dim nyEnc as byte ' Nya värdet för Encoder 1
dim oldEnc as byte ' Gamla värdet för Encoder 1

Postat: 17 augusti 2006, 14:48:44
av sodjan
Hur är ENCODER1a och ENCODER1b definierade ?

Postat: 17 augusti 2006, 15:06:06
av JimmyAndersson
såhär:

symbol ENCODER1a = PORTB.4
symbol ENCODER1b = PORTC.0


Provade även att skriva såhär istället:

nyEnc.0 = PORTB.4 'bit 0 = pinne-A
nyEnc.1 = PORTC.0 'bit 1 = pinne-B

men det gick tyvärr inte heller.

Postat: 17 augusti 2006, 15:14:11
av sodjan
OK. Som jag gissade...

För att ta syntaxen direkt från manualen (sid 30) :

Kod: Markera allt

nyEnc = 0
if (ENCODER1a = 1) then
    nyEnc.0 = 1
end if
if (ENCODER1b = 1) then
    nyEnc.1 = 1
end if
eller

Kod: Markera allt

nyEnc = 0
if (PORTB.4 = 1) then
    nyEnc.0 = 1
end if
if (PORTC.0 = 1) then
    nyEnc.1 = 1
end if
Fungerar inte det andra alternativet (som ser ut nästan exakt som
i manueln) så vet i f-n...

Postat: 17 augusti 2006, 15:23:45
av JimmyAndersson
Aha, det är *så* man ska göra. Jodå, det fungerar utmärkt! :) (Använder den översta varianten.)

Jahopp. Mission completed för den här gången. Tackar och bockar :)