Go Down

Topic: generare una sequenza di bit a frequenza variabile (Read 6922 times) previous topic - next topic

Michele Menniti

@ astrobed:
sei un pozzo di scienza! mi sono riguardato quest'argomento su un testo di calcolatori, giusto per "fissarlo" con qualche esempio, spieghi meglio tu di Bucci!
@ lesto e leo72:
grazie per la soluzione ed i ragguagli: oggi la provo, riguardo la precisione, non devo realizzare uno strumento di lettura, bensì convertire il periodo in frequenza, in realtà mi basterebbero addirittura i soli interi (11364 (Hz), p.es., se poi ottengo un valore con 3 cifre decimali, e mi dite che sono abbastanza significative, ho fatto molto più di quanto mi serve. Vi ricordo che alla fine devo solo capire con che frequenza "indicativa" si ripete la sequenza che sto generando.
Certo questa disquisizione tornerà utilissima quando sarà necessaria, in altri casi, una lettura precisa, ma un buon frequenzimetro di rado mostra oltre le 5 cifre decimali, quindi hai voglia ad accontentarsi!
Infine mi sono reso conto di un errore a monte: per ottenere la frequenza in Hz devo prima rapportare i µs in s, quindi devo dividere seq_time per 1000000, poiché stiamo parlando di KHz, in realtà mi basta rapportarli a ms, dividendo per 1000. Pensavo quindi di fare:
Serial.println((float)1)/seq_time/1000;
il dubbio che ho: il solo float iniziale mi rende tutta l'espressione in virgola mobile? Inoltre vorrei far uscire tutto sulla stessa riga con l'indicazione della parola "frequenza" e l'unità di misura, penso mi basti "giocare" con , e ;
Proverò oggi e vi faccio sapere come va a finire. Intanto inizio a fare qualche prova con uno shift da 8 bit ed un dip, in attesa dell'uovo di pasqua di uwe :)
Grazie a tutti.
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

astrobeed

Premesso che l'unico modo semplice per ottenere quello che desideri è usare degli shift register con ingresso seriale e uscita sia seriale che parallela (SIPO shift register) per ottenere un ring shift register della desiderata dimensione, però c'è un punto oscuro da risolvere prima di disegnare lo schema.
La frequenza di 10 MHz è riferita al singolo bit del pacchetto che devi trasmettere oppure è la frequenza di ripetizione di tutto il pacchetto ?

Michele Menniti

In effetti c'era confusione su questa cosa, poi mi sono chiarito le idee, e da lì è nata la discussione sulla misura della frequenza. I 10MHz riguardano la velocità alla quale devono essere "sparati" i singoli bit in seriale, considerando che sono una trentina (circa, come ho già spiegato) credo che l'intera sequenza si ripeta ogni 300-350KHz, quindi il clock sarà di 10MHz. Poiché carichiamo in seriale ad una velocità decisamente inferiore, bisognerebbe prima caricare lo shift e poi renderlo circolare (serve a questo la sezione parallela?), l'eventuale cambio sequenza per me rappresenta una prova ex novo.
Aspetto aiuti. Grazie.
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

lesto


Serial.println((float)1)/seq_time/1000;

a parte che è sbagliata per via della parentesi del println che si chiude prima delle divisioni (quindi in pratica stampi 1 in float e poi provi a dividere una funzione, credo che il compilatore si incavoli un pò :)

al solito... se non erro il compilatore, in caso di pari priorità degli operatori, parte da DESTRA.
quindi prima fa seq_time/1000
e poi 1/quelchel'è
come vedi però hai perso precisione per la storia degli interi nella /1000, quindi o usi i cast o fai 1000.0

per evitare mille parentesi e cast inutili (che vengono fatti a runtime, se non erro, facendo perdere qualche ciclo CPU) fai:
Serial.println( 1.0/seq_time/1000.0 );
e vai sicuro
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

Michele Menniti

Ho risolto così:
Serial.print("Tempo della sequenza: ");
  Serial.print(seq_time);
  Serial.print("Frequenza (KHz): ");
  Serial.println(1.0/(seq_time/1000.0));
quindi l'operazione viene fatta regolarmente da sx verso dx (se non metto le () alla 2a divisione mi dà 0.
Ma il risultato è con 2 decimali, ne volevo almeno 3, probabilmente non ho capito qualcosa nella discussione precedente.  :~
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

astrobeed

I due decimali sono un limite della serial.println, in realtà il numero ha tutti i decimali del caso, per vederne di più  moltiplica il valore per 10-100-1000 e poi fai la serial.println, ovviamente dovrai riposizionare mentalmente la virgola.
Esempio pratico se faccio 7/3 ottengo 2.33 se moltiplico per 1000 la variabile che contiene il risultato ottengo 2333.33


astrobeed

Come non detto, mi sono appena ricordato che basta specificare dopo la virgola il numero dei decimali desiderati, se scrivo Serial.println(a,4) e a contiene 32.12345 mi viene inviato 32.1234.

Michele Menniti

Perfetto, ora funziona bene; speriamo che troviate anche il tempo di aiutarmi col circuito esterno.
Grazie
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

astrobeed

#38
Mar 06, 2011, 04:57 pm Last Edit: Mar 06, 2011, 04:59 pm by astrobeed Reason: 1
Per il momento prendilo come schema di principio, è una cosa che ho buttato giù al volo, ma dovrebbe andare bene.
Le tre linee in ingresso se lasciate a 1 logico fanno funzionare lo ring shift register come serve a te, portando Enable a 0 logico viene bloccato sia il clock a 10 MHz che il ricircolo dei dati, ogni volta che mandi un impulso negativo sulla linea clock il valore presente su DATA viene trasferito all'interno dello shift register, attenzione che vengono negati quindi o lo prevedi da software oppure aggiungi un inverter.
Fornendo 30 impulsi sul clock carichi completamente lo shift register, è possibile variare la lunghezza del pacchetto modificando l'out per il ricircolo dati.
Gli shift register sono dei 54HC4094 (oppure 74HC4094) da 8 bit l'uno, le porte logiche sono contenute in un singolo 54HC00, l'oscillatore è del tipo quarzato ibrido, ma puoi metterci quello che ti pare.


Michele Menniti

WOW! alla faccia del "volo"!
Allora alcuni chiarimenti: per caricare lo shift con i miei 30 impulsi da Arduino, visto che devo farne passare uno per volta, mi conviene abilitare un altro pin che porto a 0 prima di ogni bit della sequenza, e che collego all'ENABLE, dopo lo porto a 1, e così via; dopo il 30mo impulso devo però bloccare il loop, non potrei mettere il code sotto SETUP invece che sotto loop? In questo modo potrei tenere ENABLE a 0 per l'intera durata del ciclo oppure funziona su fronte e quindi serve proprio l'impulso?
Con questa tipologia di schema posso decidere di variare i bit della sequenza (avendo necessità di un range 25-32) semplicemente scegliendo una delle uscite Q1 (4) ÷ Q8 (11)? Potrei usare un dip commutando un solo pin per volta verso l'OUT.
Infine, per fare alcune prove oggi, dispongo di due comunissimi cd4094, di un cd4011 (nand, anche se con pinatura delle porte leggermente diversa), di un generatore di funzioni a 10MHz (ma penso che i CMOS non reggano questa frequenza, potrei provare a 1MHz); il dubbio che ho è se questi componenti possono andar bene per una simulazione con sequenza a 16 bit (o meno) e se i livelli logici dell'Arduino vengono riconosciuti dalle porte CMOS.
Che dici?
Grazie di tutto, mi hai fatto davvero un grande regalo!
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

astrobeed


dopo il 30mo impulso devo però bloccare il loop, non potrei mettere il code sotto SETUP invece che sotto loop? In questo modo potrei tenere ENABLE a 0 per l'intera durata del ciclo oppure funziona su fronte e quindi serve proprio l'impulso?


Sarà che oggi ho la testa un pochino sconfusionata da un bel raffreddore, ma non ti seguo :)
Il software su Arduino deve seguire una logica di questo tipo:
Nel setup inizializzi tutti i vari pin come serve.
All'interno del loop dovrai attendere un evento che causa il cambio sequenza, dato che non hai specificato come questo avviene posso solo ipotizzare che lo fai a intervalli fissi di tempo oppure tramite uno specifico comando premendo un pulsante o un carattere che arriva dalla seriale.
Comunque sia non devi fare altro che aspettare l'evento, cioè inserisci un controllo, e quando questo avviene chiami la funzione che genera l'eventuale sequenza random, oppure specifichi un array precaricato in memoria che contiene la sequenza, oppure la ricevi dalla porta seriale, dopo di che chiami la funzione che la carica nello shift register.
La funzione che carica la sequenza non deve fare altro che eseguire queste operazioni partendo dal presupposto che le tre linee di controllo, ENABLE, DATA, CLOCK, si trovano tutte quante 1 logico durante il normale funzionamento dello shift register:

1) portare ENABLE a 0 logico
2) mettere su DATA il valore caricare prelevandolo dall'array che contiene la sequenza.
2) Portare CLOCK da 1 a 0
3) Attendere 10us
4) Portare CLOCK da 0 a 1
5) Ripetere dal punto 2 per tante volte quanti sono i bit da caricare.
6) Riportare DATA, CLOCK, ENABLE a 1 logico per riavviare lo shift register.

In pratica nella funzione che carica la sequenza usi una for(i=0;i<n;i++) ove n è il numero dei bit da caricare.

Quote

Con questa tipologia di schema posso decidere di variare i bit della sequenza (avendo necessità di un range 25-32) semplicemente scegliendo una delle uscite Q1 (4) ÷ Q8 (11)? Potrei usare un dip commutando un solo pin per volta verso l'OUT.


Si, in teoria puoi avere da 2 a 32 bit, basta che cambi il bit usato per il ricircolo e per l'out.

Quote

Infine, per fare alcune prove oggi, dispongo di due comunissimi cd4094, di un cd4011 (nand, anche se con pinatura delle porte leggermente diversa), di un generatore di funzioni a 10MHz (ma penso che i CMOS non reggano questa frequenza, potrei provare a 1MHz)


Direi che hai tutto quello che serve, la massima frequenza di lavoro dipende dalla serie, se sono i vecchi CD4xxx sarà difficile che arrivi oltre i 5 MHz, però per provare va bene lo stesso.

Quote

il dubbio che ho è se questi componenti possono andar bene per una simulazione con sequenza a 16 bit (o meno) e se i livelli logici dell'Arduino vengono riconosciuti dalle porte CMOS.


Anche se le soglie di tensione per 1 e 0 sono leggermente diverse tra l'ATmega e i CMOS della serie CD4xxx non dovrebbero esserci problemi.

Michele Menniti

Non è il raffreddore, sono io...
Dunque la parte elettronica ora mi è chiara. I problemi sono sulla parte Arduino (ancora non ci ho capito molto, come ben vedi!)
Il mio sistema deve funzionare in modo semplice e manuale:
Carico il code su Arduino, lo invio al tuo circuito, stop, a quel punto il tuo circuito dovrebbe vivere di vita propria e l'altro che ho realizzato (il riconoscitore di sequenze) legge i dati provenienti da esso e mi avvisa quando riconosce una delle sequenze che sto cercando; quindi non ho comandi automatici da mandare o controllare; la sequenza la imposto su Arduino e mi vale per tutta la sperimentazione; se decido di cambiarla semplicemente rimando il code ad Arduino e tutto ricomincia daccapo. Spero di essere stato più chiaro così. :smiley-eek:
Ho provato a tradurre le tue indicazioni
Code: [Select]
#define DATA 12
#define ENABLE 13
#define CLOCK 7
byte sequenza[] = {
  1, 1, 1, 0, 1, 1, 0, 0};

void setup() {               
  pinMode(DATA, OUTPUT);  // attivo le uscite
  pinMode(ENABLE, OUTPUT);
  pinMode (CLOCK, OUTPUT);
  digitalWrite(ENABLE, LOW); //blocco tutto
  digitalWrite(DATA, LOW);
  digitalWrite(CLOCK, LOW);
}

void loop()
{
  for(int i=0; i<8; i++)  //ciclo per 8 bit
  {
    digitalWrite(ENABLE, LOW);
    digitalWrite(DATA, sequenza[i]);
    digitalWrite(CLOCK, LOW);
    delay (10);
    digitalWrite(CLOCK, HIGH);
  }
  digitalWrite(ENABLE, HIGH); //riavvio tutto
  digitalWrite(DATA, HIGH);
  digitalWrite(CLOCK, HIGH);
}


Il mio dubbio è che in questo stesso thread mi hano spiegato che ciò che è nella sezione loop si ripete all'infinito, ma questo mi creerebbe problemi; io vorrei eseguire tutto una sola volta in modo da non "disturbare" più il tuo circuito. Mi spieghi come fare e soprattutto se quando ho postato è corretto?
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

astrobeed

Meglio così  :)

Code: [Select]

#define DATA 12
#define ENABLE 13
#define CLOCK 7
byte sequenza[] = {
 1, 1, 1, 0, 1, 1, 0, 0};

void setup() {                
 pinMode(DATA, OUTPUT);  // attivo le uscite
 pinMode(ENABLE, OUTPUT);
 pinMode (CLOCK, OUTPUT);
 digitalWrite(ENABLE, LOW); //blocco tutto
 digitalWrite(DATA, HIGH);
 digitalWrite(CLOCK, HIGH);
}

void loop()
{
 digitalWrite(ENABLE, LOW); // fermi ciclo shift

for(int i=0; i<8; i++)  //ciclo per 8 bit
 {
   digitalWrite(DATA, sequenza[i]);
   digitalWrite(CLOCK, LOW);
   delay (10);
   digitalWrite(CLOCK, HIGH);
 }

 digitalWrite(ENABLE, HIGH); //riavvio tutto
 digitalWrite(DATA, HIGH);
 digitalWrite(CLOCK, HIGH);

 while(1);  // loop infinito e blocca il ciclo.

}

Michele Menniti

Naturalmente....
Grazie di tutto, ormai devo lasciare il mio angolo di pace (ho un piccolo lab fuori casa :D) e tornare alla cruda realtà (moglie, mutuo, ecc  =(), posso continuare a gironzolare sul forum, ma per le prove ormai se ne parla nei prox giorni; naturalmente appena ho finito il mini-circuito posto i risultati, poi realizzerò quello completo con i componenti che mi hai indicato. Siete stati tutti davvero molto disponibili, a te un ringraziamento particolare per non avermi mandato un virus della rabbia invece del circuito...mi armerò della tua pazienza prima di rientrare  ;)
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

Michele Menniti

Carissimo Astrobeed,
funziona alla grande! Per il momento ho testato con due soli cd4094 e un cd4011, in configurazione "full", cioè sfruttando tutti i 16 bit, in quanto prelevo dal pin 9 del secondo Shift; in effetti la frequenza max è di 3MHz, ma dipende dai cmos, come prevedibile.
Confermando quanto dicevi lo stato dei bit è invertito, poiché non ho porte disponibili mi "secca" mettere un inverter nel circuito finale, poiché mi viene scomodo "ragionare" al contrario per inserire la sequenza, ho pensato di invertirla via soft; mi sembrava una cosa facile  :smiley-eek: col seguente codice:

for(int k=0; k<16; k++)
  { if (sequenza [k] == 0)
      (sequenza [k] = 1);
    else
      (sequenza [k] = 0);
  }

invece non funziona per niente :. mi trasforma la sequenza in un'altra che non c'entra niente, ho fatto decine di prove (ho provato a metterla sia in setup che in loop ma non cambia nulla); comunque se non riesco metto l'inverter, però mi pare davvero una cosa banale, dove sbaglio?
In ogni caso moltissime grazie, già solo questo problema risolto mi vale l'acquisto dell'Arduino.
Guida alla programmazione ISP e seriale dei micro ATMEL (Caricare bootloader e sketch):
http://www.michelemenniti.it/Arduino_burn_bootloader.php
Guida alla Programmazione ATmega328 noP:
http://www.michelemenniti.it/atmega328nop.html
Articoli su Elettronica In:
http://www.michelemenniti.it/elettronica_in.html

Go Up