Decodifica segnale IR temporizzazioni

Salve ho scritto un codice per la decodifica di un generico segnale di un telecomando a IR (ho visto che ci sono vari argomenti a riguardo sul forum ...) l'ho testato prima su MATLAB con dei segnali registrati e funziona perfettamente , l'ho quindi riadattato per l'esecuzione su arduino pero mi trovo in difficoltà nella misura dei tempi fra le operazioni , dato che ho la necessita di eseguire tutte le operazioni in tempi stretti in modo da decodificare tutti i bit.
ho misurato la durata dei vari bit del segnale IR:
il bit 0 è composto da LV basso 1/3ms LV alto 1/3ms
il bit 1 è composto da LV basso 1/3ms LV alto 2/3ms

questo è il codice

byte b0=1;
byte b1=1;
byte a1=1;
byte start=0;
int n=1;
byte BitCode[32];
byte bitn=1;


byte in1=7;
byte ou1=2;



void setup() {
  pinMode(in1,INPUT);
  pinMode(ou1,OUTPUT);
  Serial.begin(9600);

}

void loop() {

    while(start==0){
      
      a1=digitalRead(in1);
      
      b1=digitalRead(in1);
    
      if(a1==0 && b1==1 || a1==1 && b0==0){
        start=1;
      }
      b0=b1;

    }
    
    while(bitn<=32){
      
          a1=digitalRead(in1);
      
          b1=digitalRead(in1);
       if(a1==0 && b1==1 || a1==1 && b0==0){
       
          while(b1==1){
          n=n+1;
          b1=digitalRead(in1);
         
          }

       if (n<=??){//misurare (quanti campioni prende in 2/3 ms??)
           BitCode[bitn-1]=0;
       }else{
           BitCode[bitn-1]=1;       
           }
       n=1;
       bitn=bitn+1;
       
       }
       b0=b1;
      
     }
      
      
      b0=1;
      b1=1;
      a1=1;
      start=0;
      n=1;
      bitn=1;
     
      digitalWrite(ou1,HIGH);
      delay(2000);
      digitalWrite(ou1,LOW);
      Serial.println(BitCode[0]);
      Serial.println(BitCode[1]);
       
      delay(2000);
}

dove ci sono i ''??'' significa che non so che valore mettere
il b0 serve per evitare il caso in cui a1 e b1 sorpassino il fronte di salita insieme a vadano alti.
(utilizzando come ing un pulsante funziona)

come faccio a capire il tempo fra ogni operazione ? ho provato ad usare micros ma diventa troppo ingarbugliata la cosa ... esiste un metodo alternativo? un debug ?

in allegato un segnale tipico...

mi sono fatto un piccolo diagramma di flusso per capire dove utilizzare micros() per calcolare i vari tempi tuttavia riscontro un problema...
per esempio se uso micros() cosi (bypassanto la condizione while)...

 while(start==0){
      T1=micros();
      a1=digitalRead(in1);
      //pausa qua
      b1=digitalRead(in1);
    
      if(a1==0 && b1==1 || a1==1 && b0==0){
        start=1;
      }
      b0=b1;
Serial.println(micros()-T1);
    }

ottengo questo

......
20
16
16
16
16
16
16
16
16
16
16
16
16
16
65552
65552
65552
65552
65552
65552
65.....
per le prime iterazioni è ok ma dopo un certo numero di iterazioni inizia ad aumentare tendendo all'infinito... come mai? T1 è un int ci sono problemi di overflow ? ma non penso dato che micros() va in overflow dopo circa 70 minuti?
per il tempo posso prendere i primi valori? 16us ?

Scusate ho risolto T1 deve essere long e non int
resta ancora il dubbio ... cosa accadeva?

micros() come millis() ritorna una uint32_t (senza segno) ... cioè 4 byte o 32 BIT con valori da 0 a 4294967295....da come dici T1 è int16_t (con segno) ma da dati che riporti è uint16_t (senza segno) cioè 2 byte o 16 BIT e che può avere valori da 0 a 65535.

come spesso detto se usi micros() o millis() le variabili legate alle loro operazioni devono essere tutte uint32_t...che puoi anche chiamare "unsigned long".

Non capisco perché Ti fai questo lavoro visto che esiste una aposita libreria.
Inoltre esistono piú codici e tipi di decodifica che non seguono lo schema
il bit 0 è composto da LV basso 1/3ms LV alto 1/3ms
il bit 1 è composto da LV basso 1/3ms LV alto 2/3ms

Ciao Uwe

Lo faccio perché se riesco a capire come funziona qualcosa e a scrivere il codice poi per riadattarlo o modificarlo ad altre situazioni è molto più semplice , mi sembra sbagliato fare il classico copia e schiaffa senza nemmeno sapere come funziona una cosa ( a parte casi particolari), poi è più divertente cosi ! da più soddisfazione.

uwefed:
il bit 0 è composto da LV basso 1/3ms LV alto 1/3ms
il bit 1 è composto da LV basso 1/3ms LV alto 2/3ms

Lo so , ma come detto prima basta modificare un po il codice.

ora l'ho testato e sembrerebbe funzionare
ho calcolato tutti i tempi e ho impostato il valore di n per lo 0 a 40 dato che il loop di conteggio dura circa 12us quindi in 333 us ci stanno circa 30 conteggi e in 666us 60

e in 999 90 :smiley:

irolokirt:
Lo faccio perché se riesco a capire come funziona qualcosa e a scrivere il codice poi per riadattarlo o modificarlo ad altre situazioni è molto più semplice , mi sembra sbagliato fare il classico copia e schiaffa senza nemmeno sapere come funziona una cosa ( a parte casi particolari), poi è più divertente cosi ! da più soddisfazione.

Normalmente questi sono compiti perfetti per l'assembly, ma il C è comunque molto veloce, a patto di: disabilitare gli interrupt che normalmente "girano sotto" e leggere direttamente le porte senza passare dalle digitalRead (i tempi di accesso passano da 5µs a meno di 200ns). Le misure con 'micros' non vanno bene per tempi molto piccoli perché hanno un jitter minimo di di 4µs (8µs con clock 8MHz) e comunque è gestito tramite interrupt.

Piuttosto ci sono delle cose che non capisco. Perché la doppia lettura in 'a1' e 'b1'? È chiaro che 'b0' rappresenta la lettura precedente, per cui per riconoscere una variazione sarebbe sufficiente confrontare 'b1' con 'b0'.
Cosa vuol dire la condizione seguente?

if ( a1==0 && b1==1 || a1==1 && b0==0 ) {

La seconda parte riconosce un fronte di salita, ma la prima parte? Riconoscerebbe un fronte di salita solo nel caso in cui avvenisse tra la prima e la seconda digitalRead, mi sembra un doppione inutile o mi sfugge qualcosa?
Secondo me per attendere un fronte di salita (con un ATMEGA328) è sufficiente questo:

#define IN1PIN  7
//--------------------------------------------------
void setup()
{
  pinMode(IN1PIN, INPUT);
}
//--------------------------------------------------
byte b0 = 1;

void loop()
{
  noInterrupts();
  while(true) //attesa fronte di salita
  {
    byte b1 = (PORTD >> 7) & 1;
    if (b1 & !b0) { b0 = b1;  break; }
    b0 = b1;
  }
  ......
  interrupts();
  ......
}
//--------------------------------------------------

Poi non ho capito come fai a sincronizzarti con l'inizio della trama.

Claudio_FF:
È chiaro che 'b0' rappresenta la lettura precedente, per cui per riconoscere una variazione sarebbe sufficiente confrontare 'b1' con 'b0'.
.......
Poi non ho capito come fai a sincronizzarti con l'inizio della trama.

che intendi con l'ultima frase?

Si hai ragione , è sufficiente b1 per il fronte di salita

b0=1;
b1=digitalRead(in1);
      
      if(b1==1 && b0==0){
        start=1;
      }
      b0=b1;

    }

una cosa del genere potrebbe funzionare
sinceramente sono agli inizi della programmazione C con arduino quindi ho scelto un un metodo più diretto possibile , per quello che hai suggerito te ancora non sono molto pratico con gli interrupts e non l'ho capito bene.

Tuttavia

ho optato per un approccio differente (dato che con questo codice avevo dei problemi) , ovvero invece di decodificare in 'real time' ho pensato di effettuare una decodifica registrando prima i valori in ingresso, dopo di che nel tempo che trascorre fra un segnale e l'altro (sui 50ms, ma non sono sicurissimo) decodificare la registrazione.
con questo metodo funziona perfettamente!

Premessa
Ho utilizzato come trasmettitore un telecomando per elicotterini quindi non un telecomando da TV , il segnale che emette è composto da sequenze di 32bit inviate ogni tot ms, ogni sequenza contiene le coordinate di avanti/indietro destra/sinistra su/giu e rotellina di regolazione in 4 sequenze da 8 bit
questo è il codice che modificherò per il rilevamento del fronte di salita...per ora mi sono concentrato sulla decodifica del segnale su/giu.

byte BitSequence[2100];
byte BitCode[32];
byte Num=0;
byte bitn=0;
int n=0;
int k=1;
byte a1=1;
byte b1=1;
byte b0=1;
byte start=0;
byte in1=7;



void setup() {
 pinMode(in1,INPUT);
Serial.begin(9600);
}

void loop() {

    while(start==0){
     
      a1=digitalRead(in1);
      delayMicroseconds(8);
      b1=digitalRead(in1);
      
      if(a1==0 && b1==1 || a1==1 && b0==0){
        start=1;
      }
      b0=b1;

    }
    delay(2);
  while (n<2100){
  BitSequence[n]=digitalRead(in1);
  delayMicroseconds(8);
  n=n+1;
  }

//qui abbiamo memorizzato i primi 32 bit
  n=1;
  while (bitn<32){
    if (BitSequence[k]==1 && BitSequence[k-1]==0) {
        
        while (BitSequence[k]==1){
            n=n+1;
            k=k+1;
        }
        
        if (n<=30){
            BitCode[bitn]=0;
        }else{
            BitCode[bitn]=1;
        }
        n=1;
        bitn=bitn+1;
    }
    k=k+1;
  }
  
Num=BitCode[23]+4*BitCode[22]+8*BitCode[21]+16*BitCode[20]+32*BitCode[19]+64*BitCode[18]+128*BitCode[17]+256*BitCode[16];
Serial.println(Num);

n=0;
bitn=0;
k=1;
start=0;



  
}

magari anche qua si possono fare delle correzioni...

A differenza del codice in real time funziona perfettamente , tuttavia utilizza il 28% 2329byte della memoria dinamica mentre il precedente algoritmo solo il 2% (del MEGA)

in allegato un grafico del risultato

irolokirt:
che intendi con l'ultima frase?

Pensavo lavorassi in real time. Dal grafico del segnale vedo che inizia con dei tempi 0 e 1 molto più lunghi che precedono i 32 bit dati. Questi tempi immagino che servono per riconoscere (sincronizzarsi con) l'inizio della trama. Non mi pare che il tuo programma li controlli.

non sono molto pratico con gli interrupts e non l'ho capito bene

Le funzioni 'noInterrupts' e 'interrupts' semplicemente li disabilitano e riabilitano. Altrimenti i tempi dei campionamenti software possono essere falsati dalla gestione degli interrupt che è normalmente attiva.

Claudio_FF:
Dal grafico del segnale vedo che inizia con dei tempi 0 e 1 molto più lunghi che precedono i 32 bit dati. Questi tempi immagino che servono per riconoscere (sincronizzarsi con) l'inizio della trama. Non mi pare che il tuo programma li controlli.

no non li controlla direttamente , nel caso del non real time la registrazione parte dopo 2ms dal primo fronte di salita ovvero il fronte di salita del primo 1 che dura appunto 2ms dopodiché parte la registrazione , questo per risparmiare spazio...
diciamo che uso solo il fronte di salita presente tra lo zero di 2ms e l'uno di 2ms per sincronizzare il tutto

qua...

while(start==0){
     
      a1=digitalRead(in1);
      delayMicroseconds(8);
      b1=digitalRead(in1);
      
      if(a1==0 && b1==1 || a1==1 && b0==0){
        start=1;
      }
      b0=b1;

    }

Claudio_FF:
Pensavo lavorassi in real time

il primo codice è in real time, ma mi da dei problemi ; non decodifica correttamente ed è abbastanza complicato fare un debug

Ok.

Ma per decodificare qualcosa con sicurezza in tempo reale io misurerei la durata di tutti i valori, sia 0 che 1.

Se so che si parte con 2ms->0 e 2ms->1 controllerei sia fronti di discesa che di salita, e solo quando misuro circa 2ms a zero seguiti da circa 2ms a uno prendo per buono uno start.

A quel punto devo misurare degli zeri lunghi circa 333µs seguiti da "uni" di circa 333 o 666µs.

Ogni valore misurato fuori range riporta all'attesa dello start.

Claudio_FF:
Ok.

Ma per decodificare qualcosa con sicurezza in tempo reale

giustamente
dato che si possono avere disturbi di ogni genere e rimanere nel loop che assegna i 32 bit

una volta misurati lo zero e l'uno di un bit devo verificare e assegnare il bit ,questo va fatto ''rubando'' tempo ad uno zero quindi se ne dovrebbe tenere conto.

comunque spero sia chiaro che con real time intendo decodificare bit a bit durante il segnale ,non in real time intendo decodificare tutti i bit tra un segnale e l'altro quindi alla fine non ci sarebbe alcuna differenza nel risultato , l'unica differenza come gia detto è che nel caso real time occupo meno memoria e ho piu tempo per eseguire operazioni fra un segnale e l'altro

Lasciando attivi gli interrupt, e quindi avendo 'micros' funzionante, si può comunque misurare il tempo con micros accettando un ±20% rispetto ai tempi ideali (con clock 16MHz micros ha un jitter di 4µs).

Sui fronti si compiono le operazioni (compresa la ricarica della variabile tempo), sui livelli si controllano i timeout.

Secondo me basta un sistema a sette stati, uno di riposo, due per riconoscere lo start, due per leggere i bit, due per riconoscere lo stop (l'ultima discesa a zero seguita dl livello di riposo lungo).

Ogni errore/timeout riporta allo stato zero.

Un timeout di 2ms sull'ultimo stato indica ricezione terminata correttamente.

Quindi la lettura sarebbe lunga quanto la trama più due millisecondi.

Qualcosa del genere da completare:

//legge ingresso riconoscendo fronti
onLow = false;
onHigh = false;
in = digitalRead(...);
if (in != prec_in) {
    prec_in = in;
    if (in == LOW) onLow = true; else onHigh = true;
}

//tempo trascorso da ultima carica variabile tempo
elapsed = micros() - t;

//processo ricezione
switch(s) {
    case 0:  //riposo
        if (onLow) { t = micros();  s = 1; }
        break;    

    case 1:  //misura start 0
        if (elapsed > 2400) s = 0; //low troppo lungo
        else if (onHigh) {
            if (elapsed > 1600) { t = micros();  s = 2; }
            else s = 0;            //low troppo corto
        }
        break;    

    case 2:  //misura start high
        if (elapsed > 2400) s = 0; //high troppo lungo
        else if (onLow) {
            if (elapsed > 1600) { t = micros();  s = 3; }
            else s = 0;            //high troppo corto
        }
        break;    

    case 3:
        ....
        break;    
    case 4:
        ....
        break;    
    case 5:
        ....
        break;    
    case 6:
        ....
}