Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

Chiarimenti debouncing con Timer0

Raccolta di codici sorgenti

Moderatore: Foto UtentePaolino

0
voti

[1] Chiarimenti debouncing con Timer0

Messaggioda Foto UtenteDavide90 » 23 dic 2015, 11:02

Salve a tutti, vi scrivo perché vorrei sapere come è possibile implementare un antirimbalzo software in maniera efficiente usando un timer del PIC ( timer0).

Ho scritto un po di codice ma non ne sono convinto, il codice funziona ve lo spiego brevemente:

L' idea è quella di usare il timer0 come un system timer, cioè un timer che scorre sempre, viene avviato con il PIC e terminerà con lo shutdown del PIC.
E' stato configurato per ottenere un interrupt ogni 10ms, e incrementare un contatore ogni volta che accade.

Il software in se , non fa altro che pilotare due motori attraverso un driver LM297 , c' è un pulsante che premuto la prima volta, fa girare i motori in un senso, premendo per la seconda volta girano nell' altro senso( basta cambiare la polarità sui pin dell' integrato LM297), fin qui tutto benone.

Quando scatta l' interrupt sulla PORTAB( il pulsante) viene avviata la routine di antirimbalzo, la quale, la prima volta che è chiamata non fa altro che memorizzare il valore corrente del contatore usato nel timer0, la seconda volta che è chiamata(secondo interrupt), esegue la differenza tra il valore attuale di quel contatore e il vecchio valore, se superiore ad una certa soglia(indica che sono passati "x" ms ) allora avvia la routine di controllo del motore.

Il mio problema è che , prima o poi, ci sarà una differenza negativa( quando il contatore vecchio sarà maggiore rispetto a quello attuale( dai conti che ho fatto dopo un' ora di funzionamento).
Sapete dirmi come ovviare a questo problema e/o come scrivere un debouncing efficiente?
Probabilmente mi sto complicando la vita inultimente, ed il problema è di facile soluzione, ma al momento è la strada più semplice che vedo.
Non posso usare le istruzioni "__delay(ms)" in quanto tali istruzioni bloccano l' esecuzione del software fino al loro completamento, ed è una cosa che voglio evitare.

ecco il codice in C:

Codice: Seleziona tutto
/*
* File:   newmain.c
* Author: Davide
*
* Created on 30 novembre 2015, 18:30
*/
// importo le librerie
#include<stdio.h>
#include <htc.h>
#include <xc.h>
//motore 1 comandato da IN1 e IN3
//motore 2 comandato da IN2 e IN4
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG
#pragma config FOSC = INTOSCCLK // Oscillator Selection bits (INTOSC oscillator: CLKOUT function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)
//prototipi delle funzioni
void rotazione(int PressioniPulsate);
void antirimbalzo(void);
#define _XTAL_FREQ 4000000
#define Enable_Motori RA0
char pulsate=0;
char timer=0;
int differenza=0;
int valoretimerold=0;
char isFirstTime=0;
void main(void){
TRISA=0b00000;
TRISB=0b00010000;
PORTA=0b00000;
//PORTA=0b01010;//test mode
PORTB=0;
CMCON=0x07;// spengo i comparatori
OPTION_REG=0b10000101;
TMR0 = 99;             // preset for timer register // interrupt ogni circa 10ms
INTCON=0b10101100;// accendo interrupt sulla porta B e del timer0


while(1){
 
}
}

void antirimbalzo(void){
    if(!isFirstTime){
        //memorizzo il valore della prima pressione
      valoretimerold=timer;
      isFirstTime=1; // non è più la prima volta
    }else{
    differenza=timer-valoretimerold;
    if (PORTBbits.RB4 && ((differenza)>1)){// antirimbalzo
          pulsate++;
          rotazione(pulsate);

          if (pulsate>1)
           pulsate=0;
    }
    }
}

void rotazione(int PressioniPulsate){
   //abilito i motori, sono sempre abilitati
Enable_Motori=1;
if (PressioniPulsate==1){
     //indicatore led

   PORTA=0b01011;
   PORTAbits.RA7=1;
   //porto i motori in posizione
   __delay_ms(5000);//delay necessario, valutare di rimuoverlo usando un interrupt
   //fermo i motori
   PORTA=0b00000;
}else{
   // attendo che i motori siano fermi
     PORTA=0b00000;
   PORTAbits.RA7=1;
   __delay_ms(500);//delay necessario, valutare di rimuoverlo usando un interrupt
   //indicatore led
   PORTAbits.RA7=0;
   //Effettuo l' inversione
   PORTA=0b10101;
   //porto i motori in posizione
   __delay_ms(5000);//delay necessario, valutare di rimuoverlo usando un interrupt
   //fermo i motori
   PORTA=0b00000;
   }
}

void interrupt ISR(void){

   if (TMR0IF){
      TMR0IF=0;
      TMR0 = 99;
   // generato interrupt timer0 ogni 10 ms
      timer++;

      if (timer>3600)//resetto il timer dopo un ora
       timer=0;

      }

   // è stato premuto un pulsante?
   if (RBIF){
     
       //genera interrupt sia sul fronte di salita che di discesa
       //quindi chiamo la funzione antirimbalzo due volte
            // antirimbalzo
// Verificare sul progetto finale
      antirimbalzo();

       RBIF=0;




}
}


Il PIC che sto usando è un PIC16F628A, e purtroppo genera un'interrupt unico , sia sul fronte di salita che di discesa.
Avatar utente
Foto UtenteDavide90
29 6
Frequentatore
Frequentatore
 
Messaggi: 130
Iscritto il: 5 lug 2012, 11:34

1
voti

[2] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto Utentegrandegiove » 23 dic 2015, 11:24

Ciao Davide,

l'approccio è sicuramente positivo: sfruttare le potenzialità degli interrupt fin dai primi progetti è sicuramente una cosa positiva.

Diciamo che il problema che ti rimane è sicuramente relativo.

Per evitare l'overflow della variabile incrementata nella ISR del timer0 potresti semplicemente azzerarla ad ogni occorrenza dell'interrupt relativo all'ingresso del pulsante. Così semplicemente la variabile ti da sempre la misura temporale della distanza degli eventi.

Nella ISR dell'external interrupt aggiungerei un controllo dello stato del pin relativo per poter distinguere fronte di salita e fronte di discesa (se alto l'interrupt è stato scatenato da un fronte di salita e viceversa).

:ok:
Avatar utente
Foto Utentegrandegiove
1.072 1 4 8
Expert
Expert
 
Messaggi: 513
Iscritto il: 18 ott 2010, 9:59

-1
voti

[3] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto Utentelelerelele » 24 dic 2015, 18:53

Davide90 ha scritto:Non posso usare le istruzioni "__delay(ms)" in quanto tali istruzioni bloccano l' esecuzione del software fino al loro completamento, ed è una cosa che voglio evitare.

Non è necessario, implementi una routine che deve essere ritardata, la richiami dal main (o chi per esso),
una volta ogni N passaggi, hai gia implementato un ritardo senza per questo interrompere lo scorrimento del programma principale.

in più occasioni l'ho realizzato caricando una variabile in + ogni volta che il contatto è alto, in - quando leggo il contatto in basso, determinando se è sopra o sotto sai se è premuto oppure no, devi solo impostare i limiti del conteggio, diciamo +-100..


saluti.
Avatar utente
Foto Utentelelerelele
2.146 2 7 8
Expert EY
Expert EY
 
Messaggi: 2418
Iscritto il: 8 giu 2011, 8:57
Località: Reggio Emilia

0
voti

[4] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto UtenteIlGuru » 24 dic 2015, 19:56

Ma scusa, per l'antirimbalzo a te interessa solo che timer sia diverso da valoretimerold quindi visto che il valore di differenza poi non lo usi più:
Codice: Seleziona tutto
    if ( PORTBbits.RB4 && timer!=valoretimerold ){// antirimbalzo
\Gamma\nu\tilde{\omega}\theta\i\ \sigma\epsilon\alpha\upsilon\tau\acute{o}\nu
Avatar utente
Foto UtenteIlGuru
4.124 1 10 13
Master
Master
 
Messaggi: 1361
Iscritto il: 31 lug 2015, 23:32

1
voti

[5] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto UtenteDavide90 » 25 dic 2015, 20:14

Ragazzi, grazie a tutti per le risposte!

Foto Utentegrandegiove, ho messo a frutto i tuoi consigli ! La tua soluzione è la più semplice della mia, ho riscritto la routine debouncing e funziona alla perfezione, simulando il tutto, bisognerà poi vedere nella realtà!

Foto UtenteIlGuru, è vero che a me interessa solamente che timer sia diverso da timerold, ma usando il metodo che mi suggerisci tu (di cui ti ringrazio!) non ho un criterio di selettività, l' unica discriminante è il tempo di interrupt del timer0, dovendo usare il timer0 come timer di sistema(vorrei usare un solo timer per fare più di una routine di attesa, come ad esempio quella per il controllo della rotazione dei motori), non posso permettermi di usare tempi di interrupt troppo lunghi, o comunque calcolati sul tempo di debouncing.

Foto Utentelelerelele
lelerelele ha scritto:in più occasioni l'ho realizzato caricando una variabile in + ogni volta che il contatto è alto, in - quando leggo il contatto in basso, determinando se è sopra o sotto sai se è premuto oppure no, devi solo impostare i limiti del conteggio, diciamo +-100..

Questo sicuramente mi tornerà utile in progetti futuri, purtroppo però ogni volta che si crea/usa una nuova variabile, si aggiunge codice, e se non si ha un hardware adeguato(sia per velocità che per quantità di memoria disponibile, può diventare un problema), ricordo un vecchio progetto in cui era diventato vitale stringere il codice al minimo indispensabile, usando meno variabili possibili. Dopo un po, ho cambiato hardware, era decisamente sottodimensionato per lo scopo!

lelerelele ha scritto:Non è necessario, implementi una routine che deve essere ritardata, la richiami dal main (o chi per esso),
una volta ogni N passaggi, hai gia implementato un ritardo senza per questo interrompere lo scorrimento del programma principale.

intendi usando comunque le istruzioni "__delay"?

Posto il codice funzionante:

Codice: Seleziona tutto
/*
* File:   newmain.c
* Author: Davide
*
* Created on 30 novembre 2015, 18:30
*/
// importo le librerie
#include<stdio.h>
#include <htc.h>
#include <xc.h>
//motore 1 comandato da IN1 e IN3
//motore 2 comandato da IN2 e IN4
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG
#pragma config FOSC = INTOSCCLK // Oscillator Selection bits (INTOSC oscillator: CLKOUT function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)
//prototipi delle funzioni
void rotazione(char PressioniPulsate);
void antirimbalzo(void);
#define _XTAL_FREQ 4000000
#define Enable_Motori RA0
char pulsate=0;
char timer=0;
int differenza=0;
int valoretimerold=0;
char isFirstEntry=0;
void main(void){
TRISA=0b00000;
TRISB=0b00010000;
PORTA=0b00000;
//PORTA=0b01010;//test mode
PORTB=0;
CMCON=0x07;// spengo i comparatori
OPTION_REG=0b10000101;
TMR0 = 99;             // preset for timer register // interrupt ogni circa 10ms
INTCON=0b10101100;// accendo interrupt sulla porta B e del timer0


while(1){
   
}
}

void antirimbalzo(void){
// la prima pressione mi ha fatto entrare qui dentro.
   if (isFirstEntry){
      isFirstEntry=0;
      timer=0;
   }else{
      isFirstEntry=1;
  if (PORTBbits.RB4 && (timer>5)){
     // routine controllo
     pulsate++;
       rotazione(pulsate);
     
       if (pulsate>1)
          pulsate=0;
       isFirstEntry=0;
   }
  // aspetto pochi millisecondi e ricontrollo
   
}
    RBIF=0; // per evitare che quando si tiene premuto il pulsante rimanga nella routine dell' interrupt(simulando accade, bisogna verificare nella realtà)
    //altrimenti rispostarlo nella ISR
}

void rotazione(char PressioniPulsate){
   //abilito i motori, sono sempre abilitati
Enable_Motori=1;
if (PressioniPulsate==1){
     //indicatore led

   PORTA=0b01011;
   PORTAbits.RA7=1;
   //porto i motori in posizione
   __delay_ms(5000);
   //fermo i motori
   PORTA=0b00000;
}else{
   // attendo che i motori siano fermi
     PORTA=0b00000;
   __delay_ms(500);
   //indicatore led
   PORTAbits.RA7=0;
   //Effettuo l' inversione
   PORTA=0b10101;
   //porto i motori in posizione
   __delay_ms(5000);
   //fermo i motori
   PORTA=0b00000;
   }
}

void interrupt ISR(void){

   if (TMR0IF){
      TMR0IF=0;
      TMR0 = 99;
   // generato interrupt timer0 ogni 10 ms
      timer++;

      if (timer>3600)//resetto il timer dopo un ora
    timer=0;

      }

   // è stato premuto un pulsante?
   if (RBIF){
       //genera interrupt sia sul fronte di salita che di discesa, e simulando con mplab sembra che l'interrupt permanga quando il tasto è premuto.
       //quindi chiamo la funzione antirimbalzo due volte
            // antirimbalzo
// generato dalla pressione del tasto
      if (PORTBbits.RB4){
   
      antirimbalzo();

   

      }


}
}
Avatar utente
Foto UtenteDavide90
29 6
Frequentatore
Frequentatore
 
Messaggi: 130
Iscritto il: 5 lug 2012, 11:34

1
voti

[6] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto Utentelelerelele » 27 dic 2015, 13:34

Davide90 ha scritto:intendi usando comunque le istruzioni "__delay"?

no.

realizzi la funzione che devi riitardare, diciamo

Codice: Seleziona tutto
UINT8 Ritardo_sub(void){
..
..
}


la chiami dal main

Codice: Seleziona tutto
void Main (void){
UINT8 r=1000;

while(1){
//.......
//cicli che vengono eseguiti a tutta velocità
//........

if (r==0){
Ritardo_sub();//ciclo che viene eseguito una volta ogni 1000 passaggi per il main
r=1000;}
else r--;

}
}


senza _delay() ritardi la function "Ritardo_sub()".
Avatar utente
Foto Utentelelerelele
2.146 2 7 8
Expert EY
Expert EY
 
Messaggi: 2418
Iscritto il: 8 giu 2011, 8:57
Località: Reggio Emilia

0
voti

[7] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto UtenteDavide90 » 28 dic 2015, 10:50

In effetti in questo modo avrei solo il blocco della funzione, ma il PIC sarebbe ancora in grado di rilevare eventuali interrupt!

Mi tornerà utile in futuro!

Grazie
Avatar utente
Foto UtenteDavide90
29 6
Frequentatore
Frequentatore
 
Messaggi: 130
Iscritto il: 5 lug 2012, 11:34

0
voti

[8] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto Utentelelerelele » 28 dic 2015, 12:04

Davide90 ha scritto:In effetti in questo modo avrei solo il blocco della funzione, ma il PIC sarebbe ancora in grado di rilevare eventuali interrupt!

non vorrei dire fesserie, visto che non sono così esperto, ma penso che i delay software siano appunto routine chiuse che generano ritardi, quindi dovrebbero essere interrotte pure loro dall'interrupt, specialmente in questo caso in cui l'interrupt è hardware.

potresti fare un po di prove per verificare questo.....altrimenti attendiamo conferma da chi ne sa.

io questo metodo l'ho usato parecchie volte, ed ho sempre ottenuto buoni risultati.

saluti
Avatar utente
Foto Utentelelerelele
2.146 2 7 8
Expert EY
Expert EY
 
Messaggi: 2418
Iscritto il: 8 giu 2011, 8:57
Località: Reggio Emilia

0
voti

[9] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto Utenteobiuan » 28 dic 2015, 14:52

Ciao

Una routine efficiente di antirimbalzo deve evitare che un evento logico raggiunga "l'intelligenza centrale" fintanto che non è un evento certo. Alla pressione del pulsante ricevi un treno di impulsi che devi filtrare (meglio se lo fai anche in analogico): la routine di antirimbalzo deve "accettare" il cambio di stato solo se permane per un certo tempo. L'approccio comune è mettere l'antirimbalzo nella ISR associata all'interrupt del pulsante stesso (metodo + e - suggerito da Foto Utentelelerelele :ok: ), tu lo vuoi fare con un di tick di sistema che campiona lo stato della porta ogni 10ms. Questo comporta un problema:

...che succede col tuo codice se l'utente preme e rilascia entro i 10ms? o se il rumore triggera una commutazione?
_______________________________________________________
Gli oscillatori non oscillano mai, gli amplificatori invece sempre

Io HO i poteri della supermucca, e ne vado fiero!
Avatar utente
Foto Utenteobiuan
5.824 2 10 12
Master
Master
 
Messaggi: 930
Iscritto il: 23 set 2013, 23:45

0
voti

[10] Re: Chiarimenti debouncing con Timer0

Messaggioda Foto UtenteEcoTan » 28 dic 2015, 15:06

Davide90 ha scritto:un pulsante che premuto la prima volta, fa girare i motori in un senso, premendo per la seconda volta girano nell' altro senso

Ritengo che il pulsante, finché nessuno lo tocca, non possa dare falsi interventi. Il problema è giustamente quello di evitare rimbalzi, cioè che una singola pressione venga confusa con una doppia pressione e quindi faccia girare il motore all'incontrario. E' un compromesso, perché può darsi anche il caso che l'operatore volontariamente dia un breve impulso e poi corregga nell'altro senso.
Avatar utente
Foto UtenteEcoTan
5.340 4 10 13
Expert EY
Expert EY
 
Messaggi: 3191
Iscritto il: 29 gen 2014, 8:54

Prossimo

Torna a Firmware e programmazione

Chi c’è in linea

Visitano il forum: Nessuno e 0 ospiti