shift <<

Ho trovato questo esempio per l’uso di un Timer con Interrupt.
Lo compilo Ok.
Non ho ancora una scheda (ordinata) quindi non ho provato.

Nella routine di setup() esiste : TCCR2A |= ((1<<CS22) | (0<<CS21) | (0<<CS20));

Cosa servono quei 2 Or con lo zero? restituiscono sempre zero.
Provato con un compilatore C.
Non si può assegnare il valore necessario direttamente?

Qui sotto l’esempio completo.

Grazie

Saluti Carlo.
//-------------------------------------------------------------------
/*Timer2.pde
13-11-2011
Interrupt TIMER2
*/
#include <avr/interrupt.h>
#include <avr/io.h>

#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT

int ledPin = 13;
int int_counter = 0;
volatile int second = 0;
int oldSecond = 0;
long starttime = 0;

// Arduino runs at 16 Mhz, so we have 1000 Overflows per second…
// 1\ ((16000000 /64) / 256) = 1 / 1000
ISR(TIMER2_OVF_vect){
RESET_TIMER2;
int_counter += 1;
if (int_counter == 1000) {
second ++;
int_counter = 0;
}
cli(); //Disabilita Interrupt.
}

void setup() {
Serial.begin(9600);
Serial.println(“Initializing timer interrupt)”);
//Timer2 Settings: Timer Prescaler /64
TCCR2A |= ((1<<CS22) | (0<<CS21) | (0<<CS20));
// Use internal clock - external clock not used in Arduino
ASSR |= (0<<AS2);
TIMSK2 |= (1<<TOIE2) | (0<<OCIE2A); //Timer2 Overflow Interrupt Enable.
RESET_TIMER2;
sei(); //Abilita Interrupt.
starttime = millis();
}

void loop() {
if (oldSecond != second) {
Serial.print(second);
Serial.print(". ->");
Serial.print(millis() - starttime);
Serial.println(".");
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
oldSecond = second;
}
}

PREMESSA:
stiamo parlando dei REGISTRI del microcontrollore, un argomento delicato.

I registri modificano come il micro lavora quindi non si possono cambiare così, a vanvera.
Ogni registro è un byte di memoria i cui 8 bit possono assumere valori di 0 e 1, ogni bit modifica qualcosa.

Per modificare questi bit esistono 2 metodi:

  1. usare le funzioni precostituite di Arduino per la manipolazione dei singoli bit
  2. usare le funzioni logiche AND, OR e le costanti che rappresentano i singoli bit.

Il punto 1) è poco usato perché le funzioni per la manipolazione dei singoli bit sono state introdotte da poco quindi il vecchio codice usa il secondo approccio.

Tornando alla premessa, è buona norma NON FARE MAI assegnazione diretta di un valore precalcolato ad un intero registro perché in questo modo, fregandosene dei valori precedentemente presenti, si modificherebbe non solo il funzionamento che ci interessa, ma anche altre cose che potrebbero alterare il micro in fase di esecuzione.
Quindi si usa manipolare solo i bit interessati.
Ad esempio:

TCCR2B &= ~(1<<CS22); 
TCCR2B |= ((1<<CS21) | (1<<CS20));

Nel primo rigo, si pone ad 1 il bit CS22 e poi lo si inverte di valore, e si fa un AND logico con il registro TCCR2B. Questo equivale a porre a zero il medesimo bit. Così facendo si porranno SEMPRE a zero, dato che qualunque numero AND 0 dà sempre zero.
Nel secondo caso invece, si pongono a 1 i bit CS21 e CS20, dopodiché si sommano logicamente (OR) al contenuto del registro RCCR2B. In questo modo siamo certi di settarli ad 1, in ogni caso.

Tornando all’esempio da te citato, mi pare che non sia corretto perché i registri CS22, CS21 e CS20 si riferiscono ai registro TCCR2B e non al registro TCCR2A, che contiene invece i bit WGM21 e WGM20.

@leo72 Mi fido, hai snocciolato la questione meglio di come potessi fare io, non conosco i registri a memoria.

Solo una cosa, quel codice molto probabilmente è pensato per lavorare in codice C/C++ non usando la cores lib di arduino, comunque sia all'interno delle macro ISR non è necessario disabilitare totalmente gli interrupt in modo globale, ancora peggio perchè disabilitandoli millis(), Serial ecc usano gli interrupt per lavorare.

Se si ha necessità di disabilitare globalmente gli interrupt deve essere fatto per il più breve periodo temporale per poi riabilitarli subito dopo. Ciao.

@Mauro: i registri dei micro credo siano definiti dentro ad avr-gcc perché non li mai ho visto in nessun file del core di Arduino (per lo meno non li ho mai trovati).

Tornando al cli, ricordati che, come avevamo analizzato giorni fa, all'interno di un interrupt di default l'impostazione è quella di disabilitare gli interrupt esterni al blocco ISR quindi la disattivazione viene già fatta in automatico. Però la cosa poco chiara è perché il cli() sia messo come ultima istruzione: mi pare inutile, dato che all'uscita del blocco ISR vengono appunto ripristinati tutti gli interrupt (funzione sei()). Boh. Quel codice è veramente misterioso.

Un modo elegante, e molto leggibile, per indirizzare i singoli bit dei registri è questo:

define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

cbi(sfr, bit) sta per "clear bit", ovvero pone a 0 un bit del registro sfr indicato dal valore di bit (da 0 a 7). sbi (sfr, bit) sta per "set bit", ovvero pone a 1 un bit del registro-

Al posto del valore numerico per bit si possono usare i nomi predefiniti dalla avrlibc, p.e. sbi (TCCR2A,CS22) oppure cbi (TCCR2A,CS21).

sbi() e cbi() sono predefiniti solo nelle ultime release delle avrlibc, se non sono già presenti basta aggiungere le due define che ho indicato all'inizio del post.

Nel core di Arduino ci sono bitClear e bitSet, non serve ridefinire altre funzioni. Cmq anch'io uso gli OR e gli AND più per compatibilità con altri codici che per reale voglia di usare quei metodi.

Grazie, adesso ne avrò per un po' ad assimilare i vostri suggerimenti.

Saluti Carlo