Sari la conținut
ELFORUM - Forumul electronistilor

BASCOM


yo9hnf

Postări Recomandate

Va salut baieti,

Vreau sa dau si eu un sfat aici, din perspectiva celui care a folosit BASCOM-ul in toate proiectele si a întâmpinat aceleasi greutati la codul pentru Encoder.

1. Ca sa implementezi o functie encoder este aproape imperios necesar sa folosesti o intrerupere de system, sau in cel mai rau caz un TIMER; de preferinta TIMER0.

Asta ca sa beneficiezi de functia "debounce" pe care o castigi in acest mod si nu-numai. Se citesc terminalele encoderuli numai cand se produce takt-ul respectiv (intrerupere) sau numai intr-un anumit interval de timp dat (Timer). Totodata combinând doua timere (TIMER0 si TIMER1) se poate face usor si functia FAST-ENCODER.

2. Citirea celor doua contacte ale encoderului se face cu functia "SAU-EXCLUSIV" pentru a pre-întâmpina citiri eronate exact ceea ce nu se vrea.  

Daca se respecata aceste doua puncte, se va putea citi excellent orice encoder oricât de prost ar fi ( bine-nteles pana la o anumita limita!).

Va spun astea din proprie experienta si am sa caut niste bucati de cod-sursa ca sa exemplific cele expuse anterior coduri implementate deja de mine in diferite proiecte care merg fara cusur. 

Pana atunci, success la compilat programe!

Link spre comentariu

Iata o rutina de citire a encoderului folosita de mine care a dat rezultate foarte bune si stabile cu diferite encodere mecanice. Partea slaba a acestui cod este ca foloseste trei pini pentru a comanda encoderul doi dintre ei (INT0-INT1) conectati impreuna; partea buna este simplitatea si eficienta:

'(Rutina de citire Encoder cu intreruperi de sistem INT0-INT1Se utilizeaza ambele intrari de INT ale unui mcu Attiny3213conectate impreuna si legate la un pin al encoderului EncACelalalt pin al Encoderului este conectat la un port oarecare al mcu.')$regfile = "attiny2313.dat"$crystal = 8000000'FUSEBITS ON: [SUT0,CKSEL3,CKSEL1,CKSEL0] restul...OFF$hwstack = 40$swstack = 16$framesize = 32'===================================================================Mcucr = &B00001011                                          'set interrupt edges on mcuConfig Int0 = Rising                                        'configurare INT0 front crescatorConfig Int1 = Falling                                       'config.INT1 pe front descrescatorOn Int0 Phase_a_edge                                        'executa rutina la INT0On Int1 Phase_a_edge                                        'executa rutina la INT1Enable Int0                                                 'activeaza INT0Enable Int1                                                 'activeaza INT1Config Portd.3 = Input                                      'input encoderA INT1Config Portd.2 = Input                                      'input encoderA INT0Phase_a Alias Pind.2                                        'pinul A al encoderuluiConfig Portd.4 = Input                                      'input EncoderBPhase_b Alias Pind.4                                        'pinul B al encoderuluiSet Portd.4                                                 'pull-up EncoderBSet Portd.2                                                 'pull-up EncoderAdim valoare_raw as byte                                     'Valoarea care se modifica in functie de encoderenable interrupts                                           'activeaza intreruperile de sistem'======================================================================do'aici se executa altceva in bucla principala de program loop'=======================================================================' Rutina pentru citire Encoder A-BPhase_a_edge:If Phase_b <> Phase_a Then                                  'daca encoderul s-a miscat...   Decr Valoare_raw                                         'decrementeaza valoareaElse    Incr Valoare_raw                                        'incrementeaza valoareaEnd IfReturn'=========================================================================end
Link spre comentariu

Am revenit cu un cod de comanda pentru encodere conectate la mcu care au functia "pin-change-interrupt"; din aceasta categorie sunt procesoarele Atmega; deasemenea se foloseste in acest caz o functie suplimentara de multiplicare a numarului de impulsuri citite la rotirea rapida a butonului encoder, adica functia FAST-ENCODER.

Aceasta metoda o utilizez actualmente la ultimul meu transceiver.

'(Rutina de citire encoder cu intreruperi de systemSe utilizeaza encoder pe pini cu intrerupere activati hardwareTIMER0 se foloseste pentru functia FAST-ENCODER (optional)Se foloseste procesor M324P dar se poate adapta la orice procesor care suporta"Pin-change-interrupt" function')$regfile = "m324pdef.dat"$crystal = 20000000$hwstack = 64$swstack = 48$framesize = 48'=====[ Enable Pin change interrupt pt.pini encoder]============================Pcmsk3 = &B00000011                                         ' Set mask pentru pinii PORTD.0 si PORTD.1Pcicr.3 = 1                                                 ' PCICR register (Enable Pin Change Interrupt Enable 3)On Pcint3 Int_isr                                           ' Set interrupt vector for PCMSK3'------------------------------- FAST ENCODER SETTINGS -------------------------'Configurare Timer0 pentru citire FAST ENCODER'Timer0 basculeaza odata la (20000000/1024)*256 = 13,1 msConfig Timer0 = Timer , Prescale = 1024On Ovf0 , Tim0_isrEnable Ovf0Start Timer0                                                'start TIMER0dim b as byte                                               'variabila de multiplicare fast-encoderdim Fast_encoder as byte                                    'Variabila Fast-Encoderdim coder as byte                                           'variabila de integrare stare encoderdim coder_old as byte                                       'variabila de memorare stare encoderdim rotary_encoder as byte                                  'variabila encoderdim Mx as byte                                              'variabila de multiplicareEnable Interrupts                                           'activare all interrupts.do'se executa altceva aici...loopend'==============================================================================='Rutina Encoder ISRInt_isr:   Incr Fast_encoder                                        'incrementeaza variabila F-E (optional)  Ddrd.0 = 0                                                'set PortD.0=input  Ddrd.1 = 0                                                'set PortD.1=input  Coder = Pind                                              'encoderul este conectat la PortD(0-1)  Coder = Coder And &B00000011                              'citeste doar PIND.0 SI PIND.1If Coder.1 < Coder_old.1 Then                               'daca se roteste encoderul...   If Coder.0 = 1 Then                                      'rotire inainte        'rutina ENCODER inainte:         Rotary_encoder = Rotary_encoder + 1                'incrementeaza variabila encoder        '------------rutina fast-encoder (optionala) ---------------------------        If Fast_encoder > 10 Then Rotary_encoder = Rotary_encoder + Mx       'la 10 impulsuri numarate F-E        '----------------------------   End If   If Coder.0 = 0 Then                                      'rotire inapoi         Rotary_encoder = Rotary_encoder - 1                'decrementeaza var.encoder        '------------rutina fast-encoder (optionala) ---------------------------        If Fast_encoder > 10 Then Rotary_encoder = Rotary_encoder - Mx       'la 10 impulsuri numarate FE -        '-----------------------------------------------------------------------   End IfEnd IfCoder_old = Coder                                           'memoreaza starea actuala a encoderului  Pcifr = 8                                                 're-activeaza intreruperileReturn'===============[ Rutina de Intrerupere pentru fast-encoder ]===================Tim0_isr:Incr B                                                      'INCR variabila de inmultire TMR0If B > 4 Then                                               'TMR0 x 4 = 13,1 x 4 = 52msec   Fast_encoder = 0                                         'reseteaza fast-encoder odata la 52ms   B = 0                                                    'reset variabila de inmultire Tmr0End IfReturn

Pentru cei care nu detin un mcu cu functie "pin-change -interrupt" se poate utiliza o versiune simplificata folosind doar intreruperea generata de TIMER0. Pentru un cristal de 4MHz se seteaza rutina de intrerupere Timer0 astfel:

'Timer0 basculeaza odata la (4000000/64)/256 => 4 msConfig Timer0 = Timer , Prescale = 64On Ovf0 , Tim0_isrEnable Ovf0Start Timer0

Codul corespunzator citirii encoderului ramane neschimbat si se plaseaza in interiorul rutinei de intrerupere TIMER0 (Tim0_isr).

Atentie la setarea perioadei de intrerupere pentru TIMER0 ca sa nu fie prea lenesa la citirea variatiilor rapide ale axului encoder!

Editat de yo6pir
Link spre comentariu

Am incercat in jumatae de ora urmatorul cod deocamdata fara rezultat:

'(    Rutina de citire encoder cu intreruperi de system    Se utilizeaza encoder pe pini cu intrerupere activati hardware    TIMER0 se foloseste pentru functia FAST-ENCODER (optional)    Se foloseste procesor M324P dar se poate adapta la orice procesor care suporta    "Pin-change-interrupt" function    ')     $regfile = "m8def.dat"    Config Lcd = 16 * 2    Config Lcdpin = Pin , Db7 = Portb.7 , Db6 = Portb.6 , Db5 = Portb.5 , Db4 = Portb.4 , E = Portb.3 , Rs = Portb.0    Cursor Off    Cls    '=====[ Enable Pin change interrupt pt.pini encoder]============================    'Pcmsk3 = &B00000011       ' Set mask pentru pinii PORTD.0 si PORTD.1    'Pcicr.3 = 1       ' PCICR register (Enable Pin Change Interrupt Enable 3)    'On Pcint3 Int_isr       ' Set interrupt vector for PCMSK3    '------------------------------- FAST ENCODER SETTINGS -------------------------    'Configurare Timer0 pentru citire FAST ENCODER      'Timer0 basculeaza odata la (4000000/64)/256 => 4 ms    Config Timer0 = Timer , Prescale = 64    On Ovf0 , Int_isr:       'Tim0_isr    Enable Ovf0    Start Timer0    dim b as byte                                               'variabila de multiplicare fast-encoder    dim Fast_encoder as byte                                    'Variabila Fast-Encoder    dim coder as byte                                           'variabila de integrare stare encoder    dim coder_old as byte                                       'variabila de memorare stare encoder    dim rotary_encoder as byte                                  'variabila encoder    dim Mx as byte                                              'variabila de multiplicare    'Ddrd.6 = 0       'set PortD.6=input    'Ddrd.7 = 0       'set PortD.7=input    'Coder = Pind       'encoderul este conectat la PortD(0-1)    'Coder = Coder And &B11000000       'citeste doar PIND.6 SI PIND.7Config Portd.6 = InputConfig Portd.7 = InputPortd.6 = 1Portd.7 = 1Coder = Pind       'encoderul este conectat la PortD(6-7)Coder = Coder And &B11000000       'citeste doar PIND.6 SI PIND.7    Enable Interrupts                                           'activare all interrupts.    do    Locate 2 , 1    Lcd "Test  "    loop    end    '===============================================================================    Int_isr:    If Coder < Coder_old Then       'daca se roteste encoderul...       If Coder.6 = 1 Then       'rotire inainte            'rutina ENCODER inainte:             Rotary_encoder = Rotary_encoder + 1                'incrementeaza variabila encoder       End If       If Coder.6 = 0 Then       'rotire inapoi             Rotary_encoder = Rotary_encoder - 1       'decrementeaza var.encoder       End If    End If    Coder_old = Coder                                           'memoreaza starea actuala a encoderului    Gosub Afis    Return    Afis:    Locate 1 , 1    Lcd Rotary_encoder ; "   "    Return

Mc-ul este Atmega8 setat intern la 4Mhz cu pinii D6,7 activati .

Intreruperea functioneaza, am testat separat, insa nu reusesc sa "citesc" (cred) rotirea encoderului, pe LCD am afisat doar "0"

Link spre comentariu

Eu asa as fi scris aceasta bucata de cod:

     '(    Rutina de citire encoder cu intreruperi de system    Se utilizeaza encoder pe pini cu intrerupere activati hardware    TIMER0 se foloseste pentru functia FAST-ENCODER (optional)    Se foloseste procesor M324P dar se poate adapta la orice procesor care suporta    "Pin-change-interrupt" function    ')$Regfile = "m8adef.dat"$Crystal = 4000000$hwstack = 40$swstack = 16$framesize = 32    Config Lcd = 16 * 2    Config Lcdpin = Pin , Db7 = Portb.7 , Db6 = Portb.6 , Db5 = Portb.5 , Db4 = Portb.4 , E = Portb.3 , Rs = Portb.0    Cursor Off    Cls      'Timer0 basculeaza odata la (4000000/64)/256 => 4 ms    Config Timer0 = Timer , Prescale = 64    On Ovf0 , Int_isr       'Tim0_isr    Enable Ovf0    Start Timer0    dim b as byte                                               'variabila de multiplicare fast-encoder    dim Fast_encoder as byte                                    'Variabila Fast-Encoder    dim coder as byte                                           'variabila de integrare stare encoder    dim coder_old as byte                                       'variabila de memorare stare encoder    dim rotary_encoder as byte                                  'variabila encoder    dim Mx as byte                                              'variabila de multiplicareConfig Portd.6 = InputConfig Portd.7 = InputPortd.6 = 1Portd.7 = 1    Enable Interrupts                                           'activare all interrupts.Rotary_encoder = 0                                          'initiere variabila encoder    do    Locate 2 , 1    Lcd "Test  " ; Rotary_encoder    loop    end    '===============================================================================    Int_isr:    '================================================    'Aici trebuie sa fie aceste doua linii de cod!    Coder = Pind       'encoderul este conectat la PortD(6-7)    Coder = Coder And &B11000000                            'citeste doar PIND.6 SI PIND.7    '=================================================     'Atentie la conditia de mai jos! se refera la unul din pinii encoderuli[pin 7]    If Coder.7 < Coder_old.7 Then                           'daca se roteste encoderul...       If Coder.6 = 1 Then       'rotire inainte            'rutina ENCODER inainte:             Rotary_encoder = Rotary_encoder + 1                'incrementeaza variabila encoder       End If       If Coder.6 = 0 Then       'rotire inapoi             Rotary_encoder = Rotary_encoder - 1       'decrementeaza var.encoder       End If    End If    Coder_old = Coder                                           'memoreaza starea actuala a encoderului  'Aici nu mai trebuie adaugata instructiunea gosub ca sa nu intarzie degeaba bucla de temporizare TIMER0 '   Gosub Afis    Return '(    Afis:    Locate 1 , 1    Lcd Rotary_encoder ; "   "    Return')
Editat de yo6pir
Link spre comentariu

Diseara o sa incerc negresit, ce am postat mai sus era doar ca idee si doar sa-mi afiseze ceva pe LCD dupa care il "slefuiam", am incercat (vorbesc din memorie) si conditia "If Coder.7 < Coder_old.7 Then " insa i-mi dadea eroare la compilare (probabil am scris-o incorect) oricum multumesc pentru implicare si o sa vad precis diseara, sint tare curios de cum o sa raspunda astfel programul la comenzile encoderului, daca sare pasi, daca e destul de rapid etc etc

Editat de nickrvl
Link spre comentariu

"yo6pir" stima, nu degeaba se spune ca o imagine face cit o mie de cuvinte, exemplul dat este perfect functional, fara rateuri sau bilbiieli din partea encoderului, deabia acum am priceput cum poate fi implement un encoder cu ajutorul functiei "Timer", mai departe repostez varianta usor slefuita, testata si perfect functionala:

$regfile = "m8def.dat"   $crystal = 4000000   $hwstack = 40   $swstack = 16   $framesize = 32        Config Lcd = 16 * 2        Config Lcdpin = Pin , Db7 = Portb.7 , Db6 = Portb.6 , Db5 = Portb.5 , Db4 = Portb.4 , E = Portb.3 , Rs = Portb.0        Cursor Off        Cls        'Timer0 basculeaza odata la (4000000/64)/256 => 4 ms        Config Timer0 = Timer , Prescale = 64        On Ovf0 , Int_isr        Enable Ovf0        Start Timer0        Dim Coder As Byte       'variabila de integrare stare encoder        Dim Coder_old As Byte       'variabila de memorare stare encoder        Dim Rotary_encoder As Byte       'variabila encoder        Config Portd.6 = Input        Config Portd.7 = Input        Portd.6 = 1        Portd.7 = 1     Enable Interrupts       'activare all interrupts.     Rotary_encoder = 0       'initiere variabila encoder        do        Locate 2 , 1        Lcd "Test  " ; Rotary_encoder ; "  "        loop     End        '===============================================================================        Int_isr:        '================================================        'Aici trebuie sa fie aceste doua linii de cod!        Coder = Pind       'encoderul este conectat la PortD(6-7)        Coder = Coder And &B11000000                            'citeste doar PIND.6 SI PIND.7        '=================================================        'Atentie la conditia de mai jos! se refera la unul din pinii encoderuli[pin 7]        If Coder.7 < Coder_old.7 Then                           'daca se roteste encoderul...           If Coder.6 = 0 Then       'rotire inainte            Incr Rotary_encoder       'incrementeaza variabila encoder           End If           If Coder.6 = 1 Then       'rotire inapoi            Decr Rotary_encoder       'decrementeaza var.encoder           End If        End If        Coder_old = Coder       'memoreaza starea actuala a encoderului     Return

L.E. Atmega8 are functia "pin-change-interrupt"? intreb deoarece codul pentru asta postat mai sus nu il pot compila, nu-mi recunoaste functiile "Pcmsk" , "Pcicr", tind sa cred ca pentru "fast encoder"  trebuie folosit inca un Timer....

Editat de nickrvl
Link spre comentariu

Ma bucur ca ti-a reusit, felicitari pentru perseverenta!

Pentru Atmega8 cu intrerupere se poate folosi prima varianta de cod cea cu trei pini folositi pentru encoder. Si in completare se poate folosi TIMER0 pentru FAST-ENCODER.

Dez-avantajul este ca se utilizeaza 3 pini pentru encoder. dar nu deranjeaza daca ai destui liberi ca sa-i folosesti la altceva.

SUCCES!

Link spre comentariu

Aceasta varianta de citire a encoderului este aplicata de catre mine atat pe AVR cat si pe PIC cu folosirea TIMER0 ca baza de intrerupere:

'(Varianta De Citire Encoder Cu Timer0 Pe Doi Pini Ai Unui PortEncoderul conectat la PortD oricare doi pini alaturati.Se Fac Setari Pentru Timer0 Ca Sa Basculeze Odata La 4msFrecventa de ceas 4MHz, prescaler=64(4000000/64)/256 => 4ms')Config Timer0 = Timer , Prescale = 64Start Timer0On Ofv0 , Tim0_isrEnable Ovf0Config Portd = Input                                        'seteaza portD intrariPortd = 255                                                 'Pull-Up portDDim Old As Byte                                             'variabila encoder memorata temporarDim New As Byte                                             'variabila encoder actualaDim Encoder_raw As Byte                                     'variabila de integrare encoderDim Direction As Bit                                        'BIT care arata directia de rotire a encoderuluiEnable Interrupts'===============================================================================DoLoopEnd'===============================================================================Tim0_isr:Old = New                                                   'vechea valoare este memorata'se citeste noua stare a encoderuluiNew = Pind And &B00000011                                   'encoderul este conectat la PinD(0-1)If New <> Old Then                                          'daca se misca encoderul..'decodifica directia in care se roteste cu functia "SAU-EXCLUSIV"   Direction = New.1 Xor Old.0   If Direction = 1 Then                                    'rotire inainte           Encoder_raw = Encoder_raw + 1                    'incrementeaza valoarea   Else                                                     'rotire inapoi           Encoder_raw = Encoder_raw - 1                    'decrementeaza valoarea   End IfEnd IfReturn
Link spre comentariu

Creează un cont sau autentifică-te pentru a adăuga comentariu

Trebuie să fi un membru pentru a putea lăsa un comentariu.

Creează un cont

Înregistrează-te pentru un nou cont în comunitatea nostră. Este simplu!

Înregistrează un nou cont

Autentificare

Ai deja un cont? Autentifică-te aici.

Autentifică-te acum
×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări