PWM VELOCE

Imposta il timer 2 in modalità Phase Correct PWM ad 8 bit. La frequenza in questa modalità la calcoli così: Fclk/(2*prescaler*max_val_cont) Ora se metti un prescaler di 8 ed usi un valore massimo del contatore di 56 ottieni 160000000/(2*8*56)=17857,qualcosa e ci saresti pienamente.

Per ottenere un valore max del contatore di 56 basta farlo partire da 200, dato che (256-56)=200. Quindi metti una ISR che reimposta il contatore a 200 ad ogni overflow.

leo72: Imposta il timer 2 in modalità Phase Correct PWM ad 8 bit. La frequenza in questa modalità la calcoli così: Fclk/(2*prescaler*max_val_cont) Ora se metti un prescaler di 8 ed usi un valore massimo del contatore di 56 ottieni 160000000/(2*8*56)=17857,qualcosa e ci saresti pienamente.

Per ottenere un valore max del contatore di 56 basta farlo partire da 200, dato che (256-56)=200. Quindi metti una ISR che reimposta il contatore a 200 ad ogni overflow.

ok grazie mille ;) teoricamente ho capito cosa dovrei riuscire a fare, cos' quando vado a fare l'analogWrite posso arrivare solo fino a 56? o devo partire da 200? o posso andare direttamente sui registri OCRnB?

vale lo stesso processo anche per tutti gli altri timer? per il timer 0 che ha una frequenza doppia mi basta raddoppiare il range del pwm da 56 a 112? grazie ancora ;)

No no...il 200 lo devi impostare come base nel timer all'interno della ISR. 56 sono gli aggiornamenti che il contatore farà prima di andare in overflow. All'overflow, rimetti il contatore a 200.

Con l'analogWrite gestisci il duty cicle e basta.

Se non hai bisogno di modificare il duty cicle, puoi semplicemente agganciare o sganciare il segnale mettendo il pin in output o input, rispettivamente.

leo72: No no...il 200 lo devi impostare come base nel timer all'interno della ISR. 56 sono gli aggiornamenti che il contatore farà prima di andare in overflow. All'overflow, rimetti il contatore a 200.

Con l'analogWrite gestisci il duty cicle e basta.

Se non hai bisogno di modificare il duty cicle, puoi semplicemente agganciare o sganciare il segnale mettendo il pin in output o input, rispettivamente.

io devo gestire dei motori, è quindi ho bisogno di poterlo variare da 0 al massimo... nell'analogWrite mi basta mettere valori da 0 a 255 come al solito?

Sì, nell'analogWrite metti valori da 0 a 255. Il mio "no no.." era riferito alla tua precedente domanda in cui chiedevi se dovevi mettere un valore max di 56 nell'analogWrite.

leo72: Sì, nell'analogWrite metti valori da 0 a 255. Il mio "no no.." era riferito alla tua precedente domanda in cui chiedevi se dovevi mettere un valore max di 56 nell'analogWrite.

ok grazie mille ;) domani provo se riesco a fare qualcosa ;)

purtroppo ho appena scoperto che il mio sistema non funziona sopra gli 8khz, adesso devo capire perchè e cercare di risolvere questo problema prima di poter pensare di alzare ulteriormente la frequenza... :(

?R: purtroppo ho appena scoperto che il mio sistema non funziona sopra gli 8khz, adesso devo capire perchè e cercare di risolvere questo problema prima di poter pensare di alzare ulteriormente la frequenza... :(

Beh, la formula l'hai capita. ;) Per ottenere altre frequenze giochi col prescaler e con il valore iniziale del contatore.

leo72:

?R: purtroppo ho appena scoperto che il mio sistema non funziona sopra gli 8khz, adesso devo capire perchè e cercare di risolvere questo problema prima di poter pensare di alzare ulteriormente la frequenza... :(

Beh, la formula l'hai capita. ;) Per ottenere altre frequenze giochi col prescaler e con il valore iniziale del contatore.

si, l'ho capita, anche se non ho proprio le idee chiare sui registri, magari provando riesco a fare qualcosa, grazie mille! ;)

Se mi dici la frequenza che ti serve ed il timer da usare, te la posso passare io la configurazione per i registri. ;)

il timer sarebbe il 3 dell'atmega2560, volevo provare ad una frequenza di 3khz, perchè a 8 sembra troppo alta...

Secondo i miei calcoli, basta mettere un prescaler di /8 e si ottiene una frequenza di 3049 Hz senza dover modificare il contatore, quindi senza ISR aggiuntive.

Fai così:

cli(); //fermi gli interrupt
//prescaler a /8
TCCR3B |= (1<<CS31);
TCCR3B &= ~((1<<CS30) | (1<<CS32));
sei();

Adesso basta fare un analogWrite(pin, 127) per avere un’onda a 3 Khz circa con duty cicle al 50%. Prova e fammi sapere.

leo72:
Secondo i miei calcoli, basta mettere un prescaler di /8 e si ottiene una frequenza di 3049 Hz senza dover modificare il contatore, quindi senza ISR aggiuntive.

Fai così:

cli(); //fermi gli interrupt

//prescaler a /8
TCCR3B |= (1<<CS31);
TCCR3B &= ~((1<<CS30) | (1<<CS32));
sei();



Adesso basta fare un analogWrite(pin, 127) per avere un'onda a 3 Khz circa con duty cicle al 50%. Prova e fammi sapere.

grazie mille! appena ho tempo provo :wink:

finalmente l'ho provato, mi viene una frequenza di 3,92khz una curiosità, il pezzo di programma che hai fatto tu equivale a scrivere TCCR3B = (TCCR3B & 0xF8) | 0x02 ; che è quello che fanno anche qui: http://sobisource.com/?p=195 no? si cambia solo il prescaler?

?R: finalmente l'ho provato, mi viene una frequenza di 3,92khz una curiosità, il pezzo di programma che hai fatto tu equivale a scrivere TCCR3B = (TCCR3B & 0xF8) | 0x02 ; che è quello che fanno anche qui: http://sobisource.com/?p=195 no? si cambia solo il prescaler?

Sì, si cambia solo il prescaler. Solo che io l'ho fatto manipolando i singoli bit, ottenendo un codice più leggibile.

PS: avevo scritto che avresti ottenuto una frequenza di circa 3 KHz perché ho invertito i numeri, avevo letto 3,092 invece di 3,902 Khz. Scusa :sweat_smile: Ti va bene lo stesso oppure vuoi 3 KHz precisi?

per adesso va bene, devo ancora capire cosa non va sul driver motori quindi mi serve solo per provare :stuck_out_tongue_closed_eyes:

adesso sto riprovando, il mio driver non riesce neanche a stare dietro ai 980hz di default del pwm abbinato al TIMER0… se abbasso la frequenza di quel pwm rischio di avere problemi con i delay, millis, pulseIn e tutte le altre funzioni che si basano su quel timer… c’è una soluzione “indolore” per diminuire la frequenza di quel PWM senza andare a dare problemi alle altre funzioni?
grazie

?R: adesso sto riprovando, il mio driver non riesce neanche a stare dietro ai 980hz di default del pwm abbinato al TIMER0... se abbasso la frequenza di quel pwm rischio di avere problemi con i delay, millis, pulseIn e tutte le altre funzioni che si basano su quel timer... c'è una soluzione "indolore" per diminuire la frequenza di quel PWM senza andare a dare problemi alle altre funzioni? grazie

Premesso che il timer 0 non va toccato più di tanto perché è configurato in modo da lavorare con quella frequenza (976 Hz) pena l'alterazione delle funzioni temporali (come hai giustamente evidenziato), ci potrebbe essere una soluzione "rustica". Si potrebbe modificare la ISR dell'overflow del timer 0 ed inserire alla fine la generazione di un segnale PWM su un pin predeterminato con frequenza dimezzata (quindi 488 Hz). Per semplificare molto la cosa affinché la routine non pesi sul resto della ISR così che i tempi rimangano il più possibile esenti da alterazioni si dovrebbe rendere il codice molto snello, quindi niente impostazioni avanzate selezionabili da codice ecc.. giusto le istruzioni per fare ciò che deve su un pin preimpostato.

ogni volta che parli di modificare l'ISR non capisco mai dove è il codice nel quale devo andare a mettere le mani... :sweat_smile:

ero partito con l'idea del pwm veloce e adesso mi trovo a dover fare un pwm lento, se avessi a disposizione un generatore di funzione vorrei proprio fare uno sweep per vedere a che frequenza taglia questo driver motori, sul datasheet non l'ho visto... domani provo a farlo con l'arduino...

ho un'altra domanda: se io vario la frequenza di un pwm di conseguenza vario anche quella di tutti i pwm legati a quel timer? o dipende come lo vario?

?R:
ogni volta che parli di modificare l’ISR non capisco mai dove è il codice nel quale devo andare a mettere le mani… :sweat_smile:

Beh, avendo passato un pò di ore a guardare tutti i file del core di Arduino, poi le cose so dove cercarle :wink:

ho un’altra domanda: se io vario la frequenza di un pwm di conseguenza vario anche quella di tutti i pwm legati a quel timer? o dipende come lo vario?

Partiamo a monte. C’è un timer. Un timer è un circuito che contiene un contatore aggiornato da un circuito collegato ad un prescaler, ossia un divisore di clock posto sull’ingresso. Grazie a questo divisore, posso variare il segnale d’ingresso per cui posso diminuire a mio piacimento la frequenza con la quale viene aggiornato il contatore. Il contatore poi è un registro in RAM per cui posso anche variarne il contenuto. In questo modo posso ottenere overflow molto rapidi oppure molto lenti, a seconda di come imposto il tutto.

Ora vediamo come possiamo usare il timer. Lo possiamo usare essenzialmente in 3 modi: per generare un segnale di interrupt, per cambiare lo stato di un pin oppure per sollevare entrambi gli eventi.
Se lo uso solo per sollevare un interrupt, il timer diventa un semplice contatore. Se invece lo collego materialmente ad un pin esso diventa un generatore di segnale PWM, la cui frequenza e duty cicle varia in base a come lo impostiamo (se in modalità FastPWM, Phase Correct PWM, con controllo sul valore massimo e/o sul valore minimo ecc… ci sono una dozzina di modi differenti per sistemarlo).

E’ ovvio che il timer 0 è “delicato”, nel senso che essendo già stato impostato dal core di Arduino per determinate funzioni, se vai a toccare il prescaler, il valore del contatore, il duty cicle o la modalità di generazione del segnale PWM, alteri tutto quello che c’è a valle.

Fin qui è chiaro, no?

Adesso come possiamo variarlo? Se noi inseriamo una piccola routine alla fine della funzione che intercetta l’overflow del timer 0, che sull’Arduino è usata per la gestione del contatore di millisecondi, possiamo fargli eseguire delle operazioni. Ad esempio, possiamo variare lo stato di un pin a nostro piacimento, alternandolo LOW/HIGH con una frequenza dimezzata rispetto a quella del timer. Per far ciò basta cambiare lo stato di questo pin ogni 2 chiamate della ISR così che se la ISR lavora a 976 Hz il nostro generatore di PWM lavori ad una frequenza di (976/2)=488 MHz.

Fai questo test. Apri il file /arduino-1.0.2/hardware/arduino/cores/arduino/wiring.c
Intorno alla posizione 44 la funzione che intercetta l’overflow del contatore del timer (che usa una parola chiave, SIGNAL, che sui compilatori avr-gcc sta andando in “deprecato”, sostituita appunto dalla parola chiave ISR)

Il codice è questo:

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

Tu modificalo così:

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
	volatile static uint8_t _tempBitStatus = 0; //MOD
	
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
	
	if (_tempBitStatus & 1) { //MOD
		PORTD |= (1<<7); //MOD
	} else { //MOD
		PORTD &= ~(1<<7); //MOD
	} //MOD
	_tempBitStatus++; //MOD
}

Adesso carica questo sketch sul tuo Arduino:

void setup() {
}

void loop() {
    //accende e spenge il PWM sul pin 7 ogni 5 sec
    pinMode(7, OUTPUT);
    delay(5000);
    pinMode(7, INPUT);
    delay(5000);
}

Controlla il segnale sul pin 7: avrai un PWM con frequenza dimezzata rispetto a quella solita generata dal timer 0, che viene accesa e spenta ogni 5 secondi.