Generatore Onda Quadra 0-1 kHz a Due Canali

dove posso leggere qualcosa per capire meglio tutto quello che c'è sotto (ad esempio bit_is_set e qualt'altro)?? =)

purtroppo io finora mi ero fermato all'apparenza, bisogna capire il profondo!!

stevepizz:
dove posso leggere qualcosa per capire meglio tutto quello che c'è sotto (ad esempio bit_is_set e qualt'altro)?? =)

... la cosa migliore è cominciare con lo studiarsi il "core" di Arduino. Tutto quello che trovi dentro arduino\hardware\arduino\avr\cores\arduino

... per poi passare alla AVR libc

Guglielmo

ciao a tutti,
ho cambiato le specifiche di progetto che avevo in mente.

ora quello che mi serve è generare 2 segnali ad onda quadra con stessa frequenza variabile, stesso duty variabile e sfasamento tra i due segnali variabili.
il range di frequenze è da 0 a 1 kHz (o fino a dove si riesce ad arrivare)

PS. ora ho un Arduino Due quindi in teoria più potenza !!

grazie

Avrei l'idea di una tabella DDS sinusoidale (basterebbero 256 punti e semplificherebbe i calcoli) da leggere a 10kHz costanti (o altra freq.simile ma costante) con step variabile a seconda della frequenza desiderata + applicazione soglia a seconda del duty desiderato + applicazione di un delta ad uno dei due indici per ottenere lo sfasamento costante...

Credo che basterebbe uno scrauso ArduinoUNO (a patto di non usare digitalWrite ma scrittura diretta sulle porte).

Il rumore di fase si potrebbe ridurre disabilitando gli interrupt, quello di ampiezza è inesistente vista l'uscita on/off.

Io, ragionando in modo "hardware" (traduzione: "de coccio" :stuck_out_tongue: :D) farei una cosa leggermente diversa ... dato che i segnali li vuole con frequenza e duty identici, anche se variabili, e solo lo sfasamento fra i due variabile, userei un solo oscillatore (o timer, chiamatelo come volete) per generare un canale con frequenza e duty variabile ... e poi, una seconda uscita pilotata dalla prima attraverso un secondo timer che "sfasa" le due forme d'onda ... mi spiego meglio, se rispetto al primo canale, che ad esempio, sta generando un segnale ad onda quadra da 100Hz, derivo un secondo canale tramite un ritardo variabile, ottengo un secondo canale con la stessa frequenza e duty-cycle del primo, ma sfasato di una quantita' pari al ritardo, e se lo vario, cambio lo sfasamento ... ovviamente il massimo ritardo dovra' corrispondere alla frequenza che sto generando, mentre il minimo, zero, mi dara' due segnali in fase ...

mi potreste mandare un piccolo esempio? sono abbastanza ignorante in materia!!!

Per un'onda quadra ottenuta da una sinusoide a sua volta ottenuta tramite DDS credo di aver ampiamente sottovalutato il rumore di fase :frowning:

L'idea iniziale, basata sulle "prime specifiche", era quella di calcolare una tabella con una sinusoide campione:

#include <math.h>
#define POINTS 64

byte dds_table[POINTS];

void create_dds_table()
{
    for (int i=0; i<POINTS; i++)
    {
        float x = i * 2.0 * M_PI / POINTS;
        float y = sin(x);
        dds_table[i] = (byte)(y*127.0 + 127.0);
    }
}

I punti ottenuti sarebbero anche accettabili:

Poi creare un tempo base per il loop principale, ad esempio 10kHz (usando micros mi aspetto un jitter di circa 5µs, ma usando direttamente il timer si dovrebbe poter fare meglio):

uint32_t t0 = 0;

void loop()
{
    if (micros() - t0 >= 100)
    {
        t0 += 100;
    }
}

E quindi "scorrere" la tabella indirizzandola con i bit alti di un contatore a N bit, ottenendo la frequenza desiderata, che è sempre un multiplo esatto della minima frequenza ottenibile 10000/(2^N).

Ad esempio con un contatore a 16 bit la minima frequenza ottenibile sarebbe 10000/65536=0.153Hz, mentre facendolo avanzare con step 6554 si ottengono 6554*10000/65536=1000.061Hz.

Per ottenere un'onda quadra basta applicare una soglia ai valori ottenuti dalla tabella e comandare di conseguenza i pin in uscita (in questo esempio si comanda il pin D13 di Arduino modificando direttamente il bit 5 della PORTB). Il valore della soglia determina il duty (ma non è lineare, soprattutto per alti e bassi valori, per avere una variazione del duty lineare con la soglia si dovrebbe usare un dente di sega o un triangolo al posto della sinusoide):

#include <math.h>
#define POINTS 64

byte dds_table[POINTS];
uint16_t dds_step = 6554;
uint16_t dds_idx = 0;
uint32_t t0 = 0;
byte sol = 127;

void create_dds_table()
{
    for (int i=0; i<POINTS; i++)
    {
        float x = i * 2.0 * M_PI / POINTS;
        float y = sin(x);
        dds_table[i] = (byte)(y*127.0 + 127.0);
    }
}


void setup()
{
    pinMode(13, OUTPUT);
    create_dds_table();
}


void loop()
{
    if (micros() - t0 >= 100)
    {
        t0 += 100;
        byte v = dds_table[dds_idx >> 10];
        if (v > sol) PORTB|=B00100000; else PORTB&=B11011111;
        dds_idx += dds_step;
    }
}

Con lo stesso sistema si possono generare due, tre, quattro o N frequenze contemporanee con risoluzione 0.153Hz da diversi pin (ciascuna deve avere la sua variabile step, indice, soglia).

Ma qui nasce il problema, i fronti dell'onda visti all'oscilloscopio sono affetti da un ulteriore jitter (oltre ai pochi µs previsti) lungo quanto il tempo base (che compare come "battimento" tra la frequenza generata e la lettura della tabella), in questo caso 0.1ms che è inaccettabile. Quindi idea cassata per questa specifica applicazione.

son riuscito a fare quello che volevo con questo codice:

void paramMode1()
{
      dtyValue_new = (map(analogRead(pinDuty), 0, 1023, minDty, maxDty)) * k / 100;        //valore duty-cycle
      frqLowValue_new = map(analogRead(pinFrqLow), 0, 1023, minFrq, maxFrq);               //valore della frequenza bassa
      dly_new =  map(analogRead(pinDelay), 0, 1023, 0, 99) * k / 100;                      //valore del delay di sfasamento
      count = map(frqLowValue, minFrq, maxFrq, 1, 50);
      
      //lettura dei valori della pulsantiera
      letKeypad();
      //
      if (dtyValue_new != dtyValue)
      {
            dtyValue = dtyValue_new;
            dtyStamp = dtyValue;
            stampaMode1();
      }
    
      if (dly_new != dly)                                           // aggiorna solo se i parametri sono cambiati
      {
            dly = dly_new;
            cnt_1 = 0;                                                // azzera il contatore 1
            cnt_2 = 100 - dly;                                        // setta il contatore 2 allo sfasamento corretto
            // digitalWrite(out_2, LOW);                                 // porta il secondo segnale basso
            stampaMode1();
    
      }
    
      if ((frqLowValue_new < frqLowValue-(5*frqLowValue/100))|| (frqLowValue_new > frqLowValue + (5*frqLowValue/100)))                           // aggiorna solo se i parametri sono cambiati e variano di più del 5% della misura
      {
            frqLowValue = frqLowValue_new;
            frqLowStamp = map(frqLowValue, minFrq, maxFrq, minFrqStamp, maxFrqStamp);
            frqLCD = frqLowStamp;
            period = (float)(1000000 / frqLowValue);                  // periodo in microsecondi
            passo = (float)period / k;                                // passo in microsecondi
            //calcolo dei giri al minuto
            giriMin = (frqLCD * 60.0) / nDenti;
            stampaMode1();
      }


}


void paramMode2()
{
      //lettura dei valori della pulsantiera
      letKeypad();
      //
      dtyValue1_new = map(analogRead(pinDuty1), 0, 1023, 150, 230);   //valore duty-cycle del primo segnale
      dtyValue2_new = map(analogRead(pinDuty2), 0, 1023, 150, 230);     //valore duty-cycle del secondo segnale
      frqValue1_new = (float)map(analogRead(pinFrq1), 0, 1023, minFrq, maxFrq);  //valore della frequenza del primo segnale
      frqValue2_new = (float)map(analogRead(pinFrq2), 0, 1023, minFrq, maxFrq);  //valore della frequenza del secondo segnale
 
      if (dtyValue1_new != dtyValue1)
      {
            dtyValue1 = dtyValue1_new;
            stampaMode2();
      }
    
      if (dtyValue2_new != dtyValue2)
      {
            dtyValue2 = dtyValue2_new;
            stampaMode2();
      }
  
      if (frqValue1_new != frqValue1)                           // aggiorna solo se i parametri sono cambiati
      {
            frqValue1 = frqValue1_new;
            stampaMode2();
      }
    
      if (frqValue2_new != frqValue2)                           // aggiorna solo se i parametri sono cambiati
      {
            frqValue2 = frqValue2_new;
            stampaMode2();
      }
}

void loop()
{
    mode = digitalReadFast(modePin);
   // Serial.println(mode);
    if (mode == mode1)
    {
          if (flag == 0)
          { 
               noInterrupts();
               interrupts();
                paramMode1();
                stampaMode1();
                flag = 1;
          }
          if (cnt_1 == 0) {
                aggCount_2++;                                     //aggCount: contatore per rallentare l'acquisizione dei parametri
          }
          if (aggCount_2 == count)                                //ogni "count" periodi ricalcola il parametro
          {
                paramMode1();
                aggCount_2 = 0;
          }
          if (cnt_1 < (99 - dtyValue))                             // se contatore 1 è minore del dutycycle allora tiene alto il segnale 1
          {
                digitalWriteFast(out_1, HIGH);    
                Serial.println("passoQui");            
          }
          if (cnt_1 >= (99 - dtyValue))                            // se contatore 1 è maggiore del dutycycle allora abbassa il segnale 1
          {
                digitalWriteFast(out_1, LOW);                
          }
          if (cnt_2 < (99 - dtyValue))                             // se contatore 1 è minore del dutycycle allora tiene alto il segnale 2
          {
                digitalWriteFast(out_2, HIGH);                
          }
          if (cnt_2 >= (99 - dtyValue))                           // se contatore 1 è maggiore del dutycycle allora abbassa il segnale 2
          {
                digitalWriteFast(out_2, LOW);
          }
      
          cnt_1 = (cnt_1 + 1) % k;                             // incrementa il contatore 1 e ne calcola il resto della disivione per k
          cnt_2 = (cnt_2 + 1) % k;                             // incrementa il contatore 2 e ne calcola il resto della disivione per k
          delayMicroseconds(passo);
    }
    
    else if (mode == mode2)
    {
          if (flag == 1)
          {
              stampaMode2();
              flag = 0;
          }
          aggCount_1++;                                           // aggCount: contatore per rallentare l'acquisizione dei parametri
          if (aggCount_1 == 500)                                  // ogni 500 periodi di clock ricalcola il parametro
          {
              paramMode2();
              aggCount_1 = 0;
          }
          frqSingle1 = SetPinFrequencySafe(out_1, frqValue1);          //  frequenza 1 impostata secondo lettura trimmer
          pwmWrite(out_1 , ~dtyValue1);                                 //  imposto il dutycycle per la frquenza 1
          frqSingle2 = SetPinFrequencySafe(out_2, frqValue2);         //  frequenza 2 impostata secondo lettura trimmer
          pwmWrite(out_2 , ~dtyValue2);
    }
}

ho un ultimo problema.. quando passo dal mode1 al mode2 va tutto bene. viceversa nell' LCD vedo che cambiano i parametri mentre a oscilloscopio non cambiano. è come se il SetPinFruency fosse sempre attivo e che sovrasta l'attività di scrittura delle l'altro case dell'IF.
come risolvere?