// circa 1 ms //per determinare l'inizio di un frame
const uint16_t time_start = 0b0000000000111111;
//circa 0.5 secondi //per determinare che non ci sono più dati in arrivo
const uint8_t time_stop = 0b11111000;
//grandezza dell'array 200 x 2 byte = 400byte (su 512 disponibili di SRAM)
const uint8_t slot_time = 0b11001000;
//16.777.215 //tempo prima di entrare in sleep mode
const uint32_t time_sleep = 0xFFFFFF;
uint8_t index_time, time_phase, index_tr, cd_transm;
uint16_t time[slot_time]; //array per memorizzare i tempi H e L
uint32_t countdown ;
//ogni volta che il timer0 va in overflow e genera un interrupt
//il timer 1 viene incrementato di 1
ISR(TIM0_OVF) {
TCNT1++;
}
//ogni volta che il timer0 genera un interrupt
//nella fase di trasmissione,inverte il valore del pin0
ISR(TIM0_COMPA) {
PORTB ^= (1 << PB0);
}
//ogni volta che il timer 1 genera un interrupt
//incrementa la variabile cd_transm di 1
//utilizzato nella trasmissione dei dati
ISR(TIM1_OVF) {
cd_transm++;
}
void setup() {
DDRB &= ~(1 << PB1); // Impostare la pin 1 come input
PORTB |= (1 << PB1); // Impostare il pull-up sulla pin 1
DDRB |= (1 << PB0); // Impostare la pin 0 come uscita
time_phase = index_time = index_tr = 0;
//imposta prescaler a 64 su timer0, ciò vuol dire che il tempo
//di overflow del timer0 è di 2.048 millisecondi
TCCR0B |= (1 << CS01) | (1 << CS00);
OCR0A = 255;
//abilita interrupt
TIMSK = (1 << OCIE0A);
//il clock del timer 1 è a 0 (timer fermo)
TCCR1 &= ~((1 << CS02) | (1 << CS01) | (1 << CS00));
//abilita interrupt globali
sei();
}
void loop() {
countdown = time_sleep;
//abilita interrupt pin change su pin 1
PCMSK = (1 << PCINT1);
GIMSK |= (1 << PCIE);
//finchè il pin PB1 è H incrementa il timer0 di uno
while (PINB & (1 << PB1)) {
//la variabile vale 16+M vuol dire che con una frequenza di clock
//di 8MHz e 1MISP per MHz, l'MCU impiegherà circa 3 secondi ad azzerare la viariabile
if (countdown-- == 0) {
//diabilita interrupt
cli();
//imposta sleep mode power down
MCUCR |= (1 << SE);
MCUCR |= (1 << SM1);
MCUCR &= ~(1 << SM0);
//riabilita gli interrupt
sei();
}
}
//il segnale va LOW e l'interrupt pin change sul pin 1 accende l'MCU
//ora l'MCU è accesa
TCNT0 = 0; //azzera il timer0
TCNT1 = 0; //azzera il timer1
//disabilita interrupt pin change
GIMSK &= ~(1 << PCIE);
//finchè il segnale è LOW rimane nel cilco while
while (!(PINB & (1 << PB1))) {/*non fa nulla*/}
//memorizza in time1 il valore dei due registri
//il primo byte contiene il valore del registro timer1
//il secondo byte contiene il valore del registro timer0
time_phase = TCNT1 << 8 | TCNT0;
//subito dopo aver salvato i valori dei timer
//vengono azzerati per avere una lettura più precisa della fase H
TCNT0 = 0; //azzera il timer0
TCNT1 = 0; //azzera il timer1
//se la durata della fase L del segnale è sufficientemente grande
//salva la durata nell'array e inizia a leggere il segnale
if (time_phase > time_start) {
//salva il valore nell'array
time[index_time++] = time_phase;
//inizia a memorizzare le durate di segnale
readSignal();
//la funzione readSignal ritorna e invia il segnale memorizzato
transmitSignal();
}
}
void readSignal() {
while (true) {
//finchè il segnale è HIGH resta nel ciclo while
while (PINB & (1 << PB1)) {
//se il valore del timer 1 è maggiore della soglia la
//funzione ritorna al loop
if (TCNT1 > time_stop) {
return;
}
}
//ora il segnale è LOW
//salva il tempo attuale nella variabile
time_phase = TCNT1 << 8 | TCNT0;
//azzera i timer per avere una lettura più precisa
TCNT0 = 0; //azzera il timer0
TCNT1 = 0; //azzera il timer1
//salva il valore sull'array
time[index_time++] = time_phase;
//controlla che il vettore non sia pieno
if (index_time == slot_time) {
return;
}
//entra nel ciclo while
while (!(PINB & (1 << PB1))) {/*non fa niente*/}
//salva il tempo attuale nella variabile
time_phase = TCNT1 << 8 | TCNT0;
//azzera i timer per avere una lettura più precisa
TCNT0 = 0; //azzera il timer0
TCNT1 = 0; //azzera il timer1
//salva il valore sull'array
time[index_time++] = time_phase;
//controlla che il vettore non sia pieno
if (index_time == slot_time) {
return;
}
}
}
void transmitSignal() {
//la variabile cd_transm aumenta di 1 ogni volta che il timer1
//genera un interrupt, il prescaler del timer1 è impostato a 64
//generando un interrupt ogni 2.048 ms
// Configurazione del timer/counter 0 come modalità Fast PWM
TCCR0A |= (1 << WGM01) | (1 << WGM00);
TCCR0B |= (1 << WGM02);
// Impostare il prescaler del timer0 a 1
TCCR0B &= ~(1 << CS01);
// Impostare il valore di confronto per il timer
// per generare frequenza 38KHz
OCR0A = 210;
//imposta prescaler a 64 su timer/counter1 a 64
TCCR1 |= (1 << CS12) | (1 << CS11) | (1 << CS10);
//il ciclo while itera finchè
//intex_tr è minore della posizione dell'ultimo dato
//memorizzato nell'array
while (index_tr < index_time) {
//azzera il timer e la variabile
TCNT1 = 0;
cd_transm = 0;
//abilita la modalità fast PWM sul pin output
TCCR0A |= (1 << COM0A1);
//finchè la variabile cd_transm è minore dei primi 8 bit memorizzati
//nella posizione index_tr dell'array(che corrisponde al valore di TCNT1 al momento
//della lettura del segnale in input) && il valore attuale di TCNT1 è minore
//degli ultimi 8 bit memorizzati nella posizine index_tr dell'array(che corrispondono
//al valore di TCNT0 al momento della lettura del segnale in input)
while (cd_transm < (time[index_tr++] >> 8) && TCNT1 <= (time[index_tr++] & 0xFF)) {/*itera a vuoto*/}
//disabilita la modalità fast PWM sul pin output
TCCR0A &= ~(1 << COM0A1);
//azzera il timer
TCNT1 = 0;
cd_transm = 0;
//controlla che ci sia ancora un valore all'interno del vettore
//in caso negativo esce dal ciclo while
if (index_tr == index_time) break;
//stessa cosa di sopra ma con il valore memorizzato nella posizione successiva
while (cd_transm < (time[index_tr++] >> 8) && TCNT1 <= time[index_tr++] & 0xFF) {}
}
//disabilita la modalità fast PWM sul pin output
TCCR0A &= ~(1 << COM0A1);
//azzera gli indici
index_tr = index_time = 0;
//reimposta il prescaler del timer1 a 0 (timer fermo)
TCCR1 &= ~((1 << CS02) | (1 << CS01) | (1 << CS00));
//rimuove modalità PWM timer0
TCCR0A &= ~((1 << WGM01) | (1 << WGM00));
TCCR0B &= ~(1 << WGM02);
//reimposta il prescaler del timer0 a 64
TCCR0B |= (1 << CS01) | (1 << CS00);
OCR0A = 255;
return;
}