Generazione treno impulsi variabile

Devo generare un treno di impulsi (stile pwm) il periodo deve poter essere impostato (tramite interfaccia grafica) e il cui dutycycle vari ad ogni impulso (scalandolo ad esempio del 20% ogni volta) per un massimo di circa 5-6 impulsi (tipo immagine allegata) . L’idea è quindi quella di implementarmi da zero la funzione che probabilmente non sarà molto semplice. Conoscente altri modi per fare questo?
Avevo pensato al PWM di arduino ma risulta essere un problema ogni volta settare il prescaler per variare il periodo del segnale e sapere quando è stato trasmesso un impulso e ridurne il dutycycle del successivo.
Grazie.

ma non è più comodo un NE55 per queste cose? parlo da profano ovviamente :sweat_smile:

Il problema è però come fare a variare il duty cycle durante l'esecuzione del ciclo con un ne555. Dovrei cioè utilizzare la modulazione PFM http://en.wikipedia.org/wiki/Pulse-frequency_modulation

non si basava sul valore di 2 resistenze? non puoi mettere 2 potenziometri? ripeto che parlo da profano, NE555 mai usati.

per fare tutto con arduino dipende dalla lunghezza del segnale, di che range stiamo parlando? lunghezza di ogni impulso?

Si tratta di impulsi con periodo di circa 100us quindi impossibili da gestire manualmente

non ricordo la risoluzione di arduino sui microsecondi, ma se ricordo bene non è pari ad 1, aspetta qualcuno che usi segnali più brevi di quelli che solitamente uso io ;)

ypkdani: Si tratta di impulsi con periodo di circa 100us quindi impossibili da gestire manualmente

Non so cosa intendi con manualmente, comunque per un ATmega 328 non è certo un problema generare una sequenza composta da 5-6 impulsi con durata a scalare. Basta che usi un timer su cui carichi il periodo che ti serve di volta in volta per poi ripetere il tutto all'infinito o per quante volte è necessario. Esempio pratico il tuo treno di impulsi è composto da 5 impulsi, il primo è di 100 us, il secondo è di 80 us, il terzo è di 60us, il quarto di 40 us, il quinto è di 20 us, se non ti serve una precisione elevata puoi pure usare la delayMicroseconds() invece di programmare direttamente un timer. Come prima cosa metti a 1 logico il pin di uscita, aspetti i 100 us e lo rimetti a 0 e ce lo tieni per il tempo necessario, lo rimetti a 1 per 80us per poi riportarlo a 0, così via fino a terminare la sequenza che poi andrai a ripetere.

Ok, quindi intendi utilizzando gli interrupt dei timer per verificare l'arrivo al valore impostato nel registro del timer? Grazie

Dipende dalla precisione che ti serve, se usi un timer e l’interrupt, ma basta anche il polling sul relativo flag di stato, ottieni la massima precisione con una risoluzione di 62 ns (la durata del ciclo macchina).
Se usi la delayMicroseconds() hai un errore medio di circa 2us su tutti gli impulsi, volendo si può compensare riducendo la durata della delay, e lo sketch è semplicissimo.
Ti allego un esempio che ho scritto al volo dove genero un treno di impulsi di 100-80-60-40-20 us spaziati di 50 us con un intervallo di 100us prima della ripetizione, puoi cambiare questi tempi a piacere modificando il parametro della delay.
L’immagine allegata è la schermata del DSO di verifica del treno di impulsi, i cursori evidenziano che l’impulso di 100 us in realtà dura circa 102 us, l’errore è presente anche sugli altri impulsi.

void setup()
{
  // set the digital pin as output:
  pinMode(13, OUTPUT);      
}

void loop()
{
   digitalWrite(13,HIGH);
   delayMicroseconds(100);
   digitalWrite(13,LOW);
   delayMicroseconds(50);
   
   digitalWrite(13,HIGH);
   delayMicroseconds(80);
   digitalWrite(13,LOW);
   delayMicroseconds(50);
   
   digitalWrite(13,HIGH);
   delayMicroseconds(60);
   digitalWrite(13,LOW);
   delayMicroseconds(50);
      
   digitalWrite(13,HIGH);
   delayMicroseconds(40);
   digitalWrite(13,LOW);
   delayMicroseconds(50);
      
   digitalWrite(13,HIGH);
   delayMicroseconds(20);
   digitalWrite(13,LOW);
   delayMicroseconds(100);
}

Grazie astrobeed ora vedo un po come fare in base alle esigenze sulla precisione. :D

@astrobeed: una domanda molto da profano, se non da "gnurante". L'Atmega dell'Arduino usa l'oscillatore interno che, come sappiamo, sbarella non di poco rispetto ad un quarzo. Se i tempi sono calcolati dal timer interno, collegato al segnale dell'oscillatore, non dovrebbero risultare alterati anch'essi? Eppure tu hai letto 102 us, un valore molto prossimo. Dici che i 2 us sono introdotti dal tempo di esecuzione della stessa funzione delayMicroseconds però comunque il tuo strumento ha letto 102.

leo72: L'Atmega dell'Arduino usa l'oscillatore interno che, come sappiamo, sbarella non di poco rispetto ad un quarzo.

Credo stai facendo un pochino di confusione tra i vari modi di funzionamento del clock, se parliamo di Arduino standard la base dei tempi è il quarzo da 16 MHz il che implica che il ciclo macchina dura 1/16000000 = 62.5 ns, che è anche il periodo base utilizzato per i vari timer al netto degli eventuali prescaler. Quanto sopra significa che la precisione di computo del tempo è legata alla precisione del quarzo utilizzato, sulla UNO viene usato un risonatore, costa meno, che ha un errore maggiore e questo si riflette con un maggiore scarto nel computo dei tempi, ma parliamo sempre di errori molto piccoli e praticamente ininfluenti. Il discorso cambia radicalmente quando si usa come clock l'oscillatore RC interno, questo si che ha un errore sensibile, non a caso c'è un apposito parametro di calibrazione, ed è fortemente influenzato dalla temperatura-

Se i tempi sono calcolati dal timer interno, collegato al segnale dell'oscillatore, non dovrebbero risultare alterati anch'essi? Eppure tu hai letto 102 us, un valore molto prossimo. Dici che i 2 us sono introdotti dal tempo di esecuzione della stessa funzione delayMicroseconds però comunque il tuo strumento ha letto 102.

Il valore di 102 us deriva dal controllo strumentale, l'errore di circa 2 us è praticamente costante su tutti i vari impulsi generati ed è dovuto al tempo reale di esecuzione delle varie istruzioni, oltretutto la stessa delaymicroseconds non è certo superprecisa perché è un delay software. Dato che il tempo di esecuzione è quasi una costante, in realtà possono esserci delle minime differenze tra i vari cicli, diventa semplice compensarlo sottraendo la sua durata alla DelayMicroseconds(), p.e. al posto di 100 si mette 98 e così via. Va da se che il sistema del delay software è semplice e veloce da mettere in pratica, per contro non è molto preciso e tiene bloccata la cpu per tutto il tempo, cioè non è possibile fare altro, ma se lo scopo è solo generare in continuazione una determinata sequenza di impulsi va bene pure così. Il sistema migliore è l'utilizzo di un timer e del relativo interrupt per ricaricarlo quando arriva a 0, precisione e risoluzione ottenibili sono quelle massime, però anche in questo caso tocca tenere conto della latenza introdotta dall'interrupt e decurtare tale tempo dal valore che si carica nel timer.

Sì, scusa. Ho scritto male. L'Arduino ha un risonatore esterno. Abituato come sono ad usare micro in standalone, negli ultimi tempi, ormai ragiono solo sulla questione "quarzo sì - quarzo no" dimenticandomi quel piccolo risonatore sopra all'Atmega :sweat_smile:

Il resto del mio discorso non teneva conto del risonatore e del piccolo margine di errore che ha rispetto all'oscillatore interno.

Scusate ma non capisco cosa sbaglio in questi conti. Devo creare un impulso di 100us alto e 100us basso. Ho preso quindi la formula allegata dal datasheet dell’atmel e ho ottenuto (rigirandola) che OCR1A= Fck/2 * Tperiodo - 1. quindi per avere il segnale mi risulta OCR1A=1600 ma il segnale risultante è 5us basso e 6 alto, qui il codice

    digitalWrite(pwm,LOW); 
    OCR1A=1600;
    while(!OCF1A){}
    TIFR1 = (1 << OCF1A);   
    digitalWrite(pwm,HIGH);
    OCR1A=1600;
    while(!OCF1A){}
    TIFR1 = (1 << OCF1A);

Grazie!!

PS: ho visto che il problema sta nel fatto che non entra nei while, il timer l’ho impostato settando:
TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
manca qualche altro parametro??

Capture.JPG

Aggiornamento, ho caricato questo programma:

Flashing LED at 1Hz (using CTC) 
int LEDPin = 13;  // Built-in LED pin 
// Arduino-ized version 
void setup () { 
pinMode(LEDpin, OUTPUT);  // Make sure it’s an output 
bitSet (TCCR1B, WGM12); // Configure timer 1 for CTC mode 
bitSet (TCCR1B, CS12);     // Timer1 prescale of 1024
bitSet (TCCR1B, CS10); 
OCR1A = 15624;  //Set CTC compare value in OCR1A register 
}  
void loop (){ 
if (bit_is_set(TIFR1, OCF1A)) {   // reached 15624 – CTC sets the flag 
   digitalWrite(LEDPin, !digitalRead(LEDPin)); // toggle LEDPin
   bitSet (TIFR1, OCF1A);         // reset flag by writing a 1 to it 
                  // Strange but true…

preso da qui:http://www.eng.utah.edu/~cs5968/2010/slides/Interruptsx6.pdf ma anche questo non funziona, forse ho un problema con i registri o con il timer.....

ho modificato i registri ed ora funziona ora il problema è che il segnale viene generato da questa procedura:

double tmp1=0;
for(int k=0;k<number_pulse;k++){            
    tmp1= pulse_duration*16;
    tmp1=tmp1/100;
    tmp1=tmp1*duty_cycle-1;
    TIFR1 = (1 << OCF1A);
    OCR1A=(unsigned int)tmp1;
    digitalWrite(pwm,LOW);
    while(!(TIFR1 & (1 << OCF1A))){}
    tmp1= pulse_duration*16;
    tmp1=tmp1/100;
    tmp1=tmp1*duty_cycle-1;
    TIFR1 = (1 << OCF1A);   
    OCR1A=(unsigned int)tmp1;
    digitalWrite(pwm,HIGH);
    while(!(TIFR1 & (1 << OCF1A))){}
    TIFR1 = (1 << OCF1A);  
  }

il problema è che se vado sotto i 200us di impulso con duty cycle 50% il tutto non funziona più. Può essere che quei conti impieghino così tanto tempo per essere eseguite? Per andare a più altre frequenze devo cambiare processore??