PWM VELOCE

?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.