Barriera Infrarossi

NOTA: ci troviamo spesso a consigliare la lettura di questo Topic, le ragioni valide sono diverse:
1 - trovate il codice per generare 38KHz direttamente con i timer dei micro, sia ATmega328 che ATiny85, in modo indipendente dal resto del software, nel caso del 328 addirittura può continuare a lavorare perfino mettendo il micro in stand-by (parziale ovviamente)
2 - trovate info interessanti sul dialogo IR in generale
3 - trovate il codice per mandare in sleep i due micro e per svegliarli mediante interrupt
4 - trovate un sacco di fesserie scritte dal sottoscritto, per farvi due risate, la forte inesperienza di allora, non che ora vada meglio.....

Devo realizzare un controllo tipo barriera infrarossi. Uso un LED IR per la trasmissione ed un TSOP per la ricezione, con questa coppia sono piuttosto immune da disturbi esterni, anche se devo ancora fare prove serie. Il LED devo farlo lavorare, come noto, a 38KHz. Con questo semplice CODE:

/*
  Controllo trasmissione IR
 */

void setup() {                

  pinMode(3, OUTPUT);     
}

void loop() {
    
   digitalWrite(3, HIGH);   // set the LED on
   delayMicroseconds(8);              // wait for a second
   digitalWrite(3, LOW);    // set the LED off
   delayMicroseconds(9);              // wait for a second
  
}

ottengo una frequenza di 37,78KHz, più che sufficiente, visto che il range di lavoro del TSOP va da 36 a 42KHz circa (sperimentati). Vorrei però riuscire ad ottenere la frequenza esatta, non tanto per questo circuito ma per altre esigenze in generale, ma mi sono reso conto che il delayMicroseconds ignora i valori decimali. Allora mi sono "rivolto" al pwm perché ricordavo di aver visto una sintassi particolare a tre valori, proprio col comando PWM, che però non trovo, e con analogWrite la frequenza è di circa 500Hz e la variazione del duty cicle ovviamente non serve a nulla nel mio caso.
Mi rassegno?

Riciclare il codice d'esempio che ti avevo dato per generare i 40kHz per le capsule ultrasoniche ?

/* 
 Generatore di impulsi 40 kHz
 */

byte i,j,stato=0;

void setup()
{
  pinMode(13, OUTPUT);     
  pinMode(2,  OUTPUT);    
  digitalWrite(13, HIGH);  
}

void loop()
{
 for(j=0;j<255;j++)
 {
  stato ^= 1; 
  digitalWrite(2,stato);
  for(i=0;i<9;i++)
   {
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop"); 
    asm("nop");
    asm("nop");
    asm("nop");
    asm("nop"); 
   }
  }
 
 //delay(25);
}

Agendo sul numero delle nop e dei cicli for puoi ottenere una frequenza di 38 kHz esatti.

:blush:, l'unica scusa che riesco ad inventarmi è quella (vera) che non mi sono ancora messo a lavorare con gli ultrasuoni e questo codice era ben conservato nella specifica cartellina.
Provo subito, dò per scontato che risolvo (ovviamente!) e quindi sollevo il problema successivo:
Dopo x tempo di NON interruzione del fascio devo mandare in sleep il micro, e fin qui va tutto bene, ma così spengo il LED ed il TSOP va HIGH e mi attiva l'interrupt e riaccende tutto :astonished:; non t'inczz, chiarisco subito:
Il micro è attivo e la barriera funziona (TSOPout a LOW)
Quando viene interrotta (TSOPout a HIGH) il micro svolge la sua azione di controllo (p.es. un led, un relé, ecc.).
Cosa vorrei fare ora:
Se non viene interrotta per x tempo il micro si spegne, ma dovrebbe restare attiva la barriera, in modo da riaccendere il micro. Usando un tiny85 non dispongo p.es. della separazione di alimentazione dell'ADC altrimenti pensavo di lasciare accesa questa sezione sperando di usarla.
Allora ho notato questa cosa interessante:
Se alimento il LED a tensione fissa a 5V il TSOP ovviamente resta in HIGH, però se interrompo il fascio, al ripristino il TSOP mi dà un impulso LOW e poi torna su HIGH, come se per un'istante ricevesse la frequenza giusta; potrei invertirmi la logica di questo comportamento, ma il problema è che l'impulso ce l'ho non all'interruzione del fascio ma alla fine, quando ormai tutto è ritornato normale.
Che dici metto un 555 e non se ne parla più? Anche se questo va contro l'idea del risparmio energetico, visto che il 555 in azione mi consuma più del micro, credo.

Innanzitutto saluto Menniti, Astrobeed e tutti i partecipanti del forum,
sto cercando di capire cosa svolge la routine asm, ma cercando su "san google" sono riuscito a capire che serve per implementare uno straccio di codice in assembly, non riuscendo a spiegarmi cosa centri con la possibilità di avere 40kHz esatte mi chiedevo:

  • asm serve veramente a ad implementare del codice assemly?
  • è una questione di "calcoli" sugli intervalli di tempo?
    asm viene ripetuto 2560 volte in un loop
    vuol dire che asm impiega 0,015625 hz?
  • perchè proprio da 25?
  • la stringa "nop" è un parametro costante o potevo scriverci anche "abc" ? e "pippo"?

Non dovrebbe interrompersi sul passaggio? (quindi LOW) e se "si vedono" dare un HIGH?
Sto facendo troppe domande :)?

Marbi

[/list]

Ciao Marbi, mi pare che NOP stia per "not operation", comunque leggi qui
Comunque è un parametro, non puoi scrivere abc.
Tutto quello schetch serve per "tarare" la frequenza sul pin 2 a circa 40KHz
Il TSOP funziona come ho detto: LOW se si vedono e HIGH se non si vedono.

@ astrobeed: secondo le mie misure lo sketch fornisce una frequenza di 39857 Hz, ho fatto un po' di prove e non riesco comunque ad ottenere 40KHz o 38KHz, a questo punto va benissimo qualsiasi valore, siamo vicinissimi alle frequenze richieste ed i sensori hanno buone tolleranze.

ciao a tutti
@menniti
Non capisco perché vuoi spegnere l' arduino ma tener acceso il led IR.
Se vuoi risparmiare corrente risparmi di piú spegnendo il led.
Non so che barriera IR vuoi fare e cosa vuoi trigherare ma basta un controllo 2 a 10 al secondo per rilevare una persona.
A quel punto:

  • fari dei brevi treni di impulsi da 38kHz e rilievi in quel momento il segnale del TSOP
  • se vuoi risparmiare ancora di piú metti il ATmega in sleep vedi libreria Narcoleptic.h Google Code Archive - Long-term storage for Google Code Project Hosting. oppure programmando direttamente i registri e il timer.
  • fare la frequenza con un timer (ma non chiedermi a me come si fa, so che si puó fare ma non so come)
  • generare i 38Khz con un oscillatore esterno che puó essere spento.

@Marbi
NOP é un istruzione macchina come scrive menniti e non fa niente. Dura un ciclo di clock che sono a 16Mhz 0,0625 µsecondi.
Si deve fare anche i conti con le altre funzioni tradotte in linguaggio macchina hanno bisogno di tempo per essere interpretati. Alla fine il for insieme ai NOP non é nient altro che un ritardo di 12,5µsecondi.
Un riassunto delle istruzioni li trovi a pagina 427 del http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf

Ciao Uwe

ciao a tutti
@menniti
Non capisco perché vuoi spegnere l' arduino ma tener acceso il led IR.
Se vuoi risparmiare corrente risparmi di piú spegnendo il led.

Ciao Uwe, lo scopo è didattico, quindi non devo risparmiare corrente in assoluto ma solo far vedere che il micro si può spegnere e riattivare all’occorrenza; il circuito sarà alimentato tramite alimentatore.

Non so che barriera IR vuoi fare e cosa vuoi trigherare ma basta un controllo 2 a 10 al secondo per rilevare una persona.
A quel punto:

  • fari dei brevi treni di impulsi da 38kHz e rilievi in quel momento il segnale del TSOP

Questa è un’ottima idea, 5-6 controlli al secondo mi stanno bene, oltretutto così implemento facilmente il controllo dello stato del TSOP

Io uso la sleep.h con ottimi risultati, cos’ha questa libreria di particolare? E’ migliore?

  • fare la frequenza con un timer (ma non chiedermi a me come si fa, so che si puó fare ma non so come)
  • generare i 38Khz con un oscillatore esterno che puó essere spento.

Alla prima soluzione avevo pensato ma nemmeno io so come si fa; ne parlarono Leo e Astrobeed ed altri in un Topic sull'RTC ma uscì fuori solo che non era precisa (ma nel mio caso andrebbe bene lo stesso).
La seconda è quella che dicevo col 555, ripeto lo scopo è didattico, quindi il problema del consumo reale non si pone, vorrei però fare tutto col micro (un tiny85 in stand-alone)
Ciò che devo fare in pratica è spegnere il micro e le utenze che controlla, per poi riattivarlo quando la barriera viene interrotta; se la barriera la controllo col micro mi sa che non posso fare quello che vorrei, perché si spegne anche l’uscita a 38KHz e il TSOP si comporta come se fosse stata interrotta.
Grazie.

Intanto che tornate da fuori porta vado avanti :slight_smile:

/*
  Controllo barriera IR
 */

#define TX 6
//#define RX 2
#define LV 8
#define LR 9

void setup() 
{                
  pinMode(TX, OUTPUT);     
  //pinMode(RX, INPUT);
  pinMode(LV, OUTPUT);
  pinMode(LR, OUTPUT);
  //digitalWrite(RX, HIGH);
  attachInterrupt(0, Passaggio, RISING);  
}

void loop() 
{
  /*Le seguenti quattro righe generano sul pin un'onda quadra 
    di circa 38KHz */
   digitalWrite(TX, HIGH);  // setta il pin su HIGH
   delayMicroseconds(9);    // pausa di 8 µsec
   digitalWrite(TX, LOW);   // setta il pin su LOW
   delayMicroseconds(8);    // pausa di 9 µsec
   
}
void Passaggio()        // interruzione barriera
{
  digitalWrite(LR, HIGH); // accende il LED per 1 secondo
  delay(1000);
  digitalWrite(LR,LOW);
  detachInterrupt(0);      //disattiva l'interrupt
  
}

Nelle mie misere intenzioni volevo attivare l'interrupt al passaggio del pin 2 (interrupt 0) da LOW ad HIGH (RISING). Quando lancio lo sketch tutto funziona apparentemente, se interrompo in modo stabile la barriera, la sola prima volta il led fa un rapido lampeggio, poi più niente.
L'altra cosa strana è che in altri schetch avevo definito il pin 2 (INT0) con una costante (p.es. RX) e poi avevo usato la costante per l'interrupt, in questo caso la ignora, devo proprio usare "0" altrimenti non fa nemmeno il lampeggio. Idee?

EDIT: specifico che per ora sto lavorando col 328 di Arduino, il passaggio al tiny85 verrà solo quando funziona tutto.

Il tuo codice è un PWM a tutti gli effetti :slight_smile:
Cmq per fare temporizzazioni precise devi impostare un timer interno collegato ad un interrut. Imposti il timer, poi al raggiungimento del valore massimo il contatore viene resettato: questa operazione genera un interrupt. Intercetti l'interrupt e spedisci il tuo segnale:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1167000525

Non credo tu possa usare un RTC per farti generare un segnale di clock da usare perché un DS1307 o un PCF8563 generano un clock massimo di 32768 Hz (lo stesso del quarzo), troppo basso per i tuoi 38 kHz.

Non ho un problema di temporizzazioni precise, riguardo la frequenza esterna ho detto che eventualmente userei un 555 e con quello non ho problemi di sicuro; il riferimento all'RTC era perché volevo capire come far uscire da un pin del micro una frequenza di riferimento di 38KHz, usando quindi il timer interno, ma non ho idea di come si possa fare. Comunque, nel periodo non collegato ho capito perché il code non andava: il Reference spiega :sweat_smile: che lanciando una routine tramite interrupt i delay & Co & timer non funzionano, quindi ho aggirato il problema.

/*
  Controllo barriera IR
 */

#define TX 6
#define LV 8
#define LR 9
volatile int stato = LOW;

void setup() 
{                
  pinMode(TX, OUTPUT);     
  pinMode(LV, OUTPUT);
  pinMode(LR, OUTPUT);
  attachInterrupt(0, Passaggio, RISING);  // se il pin INT0 (PD2) passa da LOW ad HIGH esegue la routine Passaggio()
  digitalWrite(LR, LOW);
  digitalWrite(LV, HIGH);
}

void loop() 
{
  /*Le seguenti quattro righe generano sul pin un'onda quadra 
    di circa 38KHz */
   digitalWrite(TX, HIGH);  // setta il pin su HIGH
   delayMicroseconds(8);    // pausa di 8 µsec
   digitalWrite(TX, LOW);   // setta il pin su LOW
   delayMicroseconds(8);    // pausa di 8 µsec
   
   if(stato==HIGH)
     {
       digitalWrite(LR, HIGH);   // accende il LED R per 10 secondi
       digitalWrite(LV, LOW);
       delay(10000);
       stato = !stato;
       digitalWrite(LR, LOW);
       digitalWrite(LV, HIGH);  // accende il LED V fino a nuovo interrupt
     }  
}
void Passaggio()            // interruzione barriera
{
    stato = !stato;
}

Ora mi succede questo: quando si interrompe la barriera per dieci secondi si inverte lo stato dei led, ma se la barriera resta interrotta oltre i 10 secondi c'è comunque un istante in cui i led cambiano di nuovo stato; questa cosa mi crea notevoli problemi: un eventuale meccanismo di sicurezza per un istante non funzionerebbe. Che logica potrei usare per risolvere?

Due domande OT:
1 - c'è una combinazione di tasti per far uscire le due parentesi graffe nell'editazione?
2 - come si fa a far apparire lo sketch con i colori originali dell'IDE?

menniti:
Io uso la sleep.h con ottimi risultati, cos’ha questa libreria di particolare? E’ migliore?

Non so perché non conosco la sleep.h .

Ciao Uwe

menniti:
1 - c'è una combinazione di tasti per far uscire le due parentesi graffe nell'editazione?

Shift + alt graph + [ per la {
Shift + alt graph + ] per la }

@ Uwe: nessun problema XD

@ astrobeed: ottimo, mi ero scocciato di fare ogni volta copincolla delle graffe XD
hai letto i vari post dopo il tuo ultimo intervento? Qualche idea in proposito?

1 - c'è una combinazione di tasti per far uscire le due parentesi graffe nell'editazione?

Usa la tastiera tedesca; li si arriva con
alt graph + 7 per {
alt graph + 0 per } :wink: :wink: :wink:

La differnza tra sleep.h e Narcoleptic.h dovrebbe essere che Narcoleptic.h ha piú funzionalitá nel senso piú trigger di risveglio rispetto la sleep.h. Non avendo usata ne l' una ne l' altra non sono sicuro in questo.

Ciao Uwe

uwefed:

1 - c'è una combinazione di tasti per far uscire le due parentesi graffe nell'editazione?

Usa la tastiera tedesca; li si arriva con
alt graph + 7 per {
alt graph + 0 per } :wink: :wink: :wink:

La differnza tra sleep.h e Narcoleptic.h dovrebbe essere che Narcoleptic.h ha piú funzionalitá nel senso piú trigger di risveglio rispetto la sleep.h. Non avendo usata ne l' una ne l' altra non sono sicuro in questo.

Ciao Uwe

La tastiera tedesca? :sweat_smile:
Ho visto che la narcoleptic usa un comando che dovrebbe racchiudere un paio di comendi della sleep, comunque alla fine il risultato è quello, non ho grandi esigenze. Ora sto cercando di farlo addormentare il bimbo, ma non ne vuole sapere, eppure non è la prima volta, i miei nanetti a quest'ora dormono beati e non consumano nulla :wink:

Allora con Linux basta fare "Alt-Gr" + "[", su qualunque tastiera :stuck_out_tongue:

@Mike:

  1. con il 555 immagino non avrai problemi.
  2. sì, in un interrupt nessuna funzione temporale di Arduino va perché delay, millis & co. è tutta agganciata al timer 0, a sua volta gestito da un interrupt. E dentro ad un interrupt, gli altri interrupt sono inibiti.
  3. Tu scrivi:

il riferimento all'RTC era perché volevo capire come far uscire da un pin del micro una frequenza di riferimento di 38KHz, usando quindi il timer interno, ma non ho idea di come si possa fare.

Allora non ho capito io. Il segnale di clock lo vuoi spedire dal micro all'RTC o lo vuoi ricevere dall'RTC sul micro?
Nel secondo caso basta abilitare il segnale di clock esterno (variando i registri dell'RTC) però, come ti ho detto, max arrivi ad avere 32 kHz (che è la frequenza del clock usato dall'RTC). Peccato perché il PCF8563 ha anche un pin INT su cui esce un segnale di interrupt ogni qual volta un contatore interno si resetta. Molto utile per pilotare altri chip.

Non, sono io che mi spiego malissimo:
Io ho bisogno di 38KHz per pilotare il LED infrarosso; volevo solo capire se invece del code HIGH/LOW che sto usando, posso farli generare al micro a partire dal timer interno; RTC esterni non ne sto usando e non mi servono.
Il vero problema è che se (ammesso che :cold_sweat:) riesco a mandare a riposo il micro, automaticamente la frequenza generata, a prescindere dal metodo usato, si spegne e quindi si paralizza tutto.

Dati di fatto, datasheet alla mano:
1)
in tutti e 3 gli sleep mode supportati il clock della CPU viene spento per cui il tuo codice non prosegue. Se non prosegue, non puoi generare il segnale usando il metodo che hai usato (ossia ciclo loop()).

in nessuno dei 3 modi sleep supportati il modulo INT0 & PIN CHANGE va in standby.

Secondo me ne esci solo usando un generatore esterno per il segnale PWM e svegliando il micro tramite l'INT0 quando qualcosa succede sul sensore.

EDIT:
Non avevo letto questo:

7.1.1 Idle Mode
When the SM[1:0] bits are written to 00, the SLEEP instruction makes the MCU enter Idle mode,
stopping the CPU but allowing Analog Comparator, ADC, USI, Timer/Counter, Watchdog, and
the interrupt system to continue operating. This sleep mode basically halts clkCPU and clkFLASH,
while allowing the other clocks to run

Quindi in teoria un timer continuerebbe ad andare. E se va il timer, puoi usare quello agganciato all'INT0, seguendo l'esempio presente nel link che ti ho postato (ovviamente adattando il codice all'Attiny).

EDIT2:
uhm... però si ripresenterebbe il problema che in un interrupt non puoi usare la delay per generare i ritardi. Potresti forse inserisci una cascata di asm volatile ("NOP") per rallentare il programma: ogni NOP è 1 ciclo di clock. Però poi rallenteresti anche il tuo codice, quando il micro si risveglia...
Sicuro che non vuoi usare un 555? :smiley:

leo72:
Dati di fatto, datasheet alla mano:
1)
in tutti e 3 gli sleep mode supportati il clock della CPU viene spento per cui il tuo codice non prosegue. Se non prosegue, non puoi generare il segnale usando il metodo che hai usato (ossia ciclo loop()).

in nessuno dei 3 modi sleep supportati il modulo INT0 & PIN CHANGE va in standby.

Secondo me ne esci solo usando un generatore esterno per il segnale PWM e svegliando il micro tramite l'INT0 quando qualcosa succede sul sensore.

Già, è quello che stavo provando. Ho messo il generatore di frequenze a 38KHz per pilotare il led, tutto il resto su Arduino e funziona, ora devo tarare lo sketch perché chiaramente ragionava in modo diverso.
Intanto lo faccio funzionare poi decido cosa fare realmente, l'idea iniziale era un'altra, non vorrei uscire "fuori tema".