#define pinOut(x) ((x) < 8 ? (DDRD |= (1 << (x))) : (DDRB |= (1 << ((x) - 8))))
#define pinInp(x) ((x) < 8 ? (DDRD &= ~(1 << (x))) : (DDRB &= ~(1 << ((x) - 8))))
#define pinHigh(x) ((x) < 8 ? (PORTD |= (1 << (x))) : (PORTB |= (1 << ((x) - 8))))
#define pinLow(x) ((x) < 8 ? (PORTD &= ~(1 << (x))) : (PORTB &= ~(1 << ((x) - 8))))
#define bitRead(value, x) (((value) >> (x)) & 0x01) // value: byte da leggere, x: posizione del bit
#define digitalRead(pin) ((pin & (1 << (pin))) ? HIGH : LOW)
#define ENCODER_CLK 2
#define ENCODER_DT 3
#define ENCODER_SW 4
#define ACCENDI 6
const int pinDecoderA1 = 13;
const int pinDecoderA2 = 12;
const int pinDecoderA3 = 11;
const int pinDecoderB1 = 10;
const int pinDecoderB2 = 9;
const int pinDecoderB3 = 8;
uint8_t xByte;
volatile boolean flagMedia = true; // FLAG, se vale 1 stiamo misurando l'input, se vale 0 stiamo presentando i risultati
volatile uint32_t adcSum = 0; // variabile che contiene la somma delle letture ADC del segnale analogico in input
volatile uint16_t adcNum = 0; // variabile che contiene il numero delle letture analogiche
volatile uint16_t numTI = 0; // numero di Timer Interrupt eseguiti
volatile uint16_t media = 0; // valore medio delle letture
volatile uint16_t adcVal = 0; // combina i due registri H e L della conversione ADC
volatile uint8_t r, c; //indici temporanei per i cicli for
//Variabili encoder
volatile byte dtValue; //valore letto dell'encoder dopo un cambio di stato
//Variabili matrice
volatile uint8_t divisione[8] = {0,0,0,0,0,0,0,0}; //tiene in memoria le ultime 5 medie delle letture ADC
volatile uint16_t i;
//Impostazione della base dei tempi iniziale
volatile uint8_t potenza = 6; //il circuito parte con la base dei tempi a 2^9 mS
volatile uint16_t numMS = 64; //numero di volte che viene attivato il Timer2 da 1 mS
//Tabella contenente le combinazioni di led da accendere per visualizzare i vari numeri (da 0 a 10) sulla matrice
const byte comb[][5] = {
B11111110, B10000010, B10000010, B10000010, B11111110, //0
B00100000, B01000010, B11111110, B00000010, B00000000, //1
B10011110, B10010010, B10010010, B10010010, B11110010, //2
B10010010, B10010010, B10010010, B10010010, B11111110, //3
B11110000, B00010000, B00010000, B00010000, B11111110, //4
B11110010, B10010010, B10010010, B10010010, B10011110, //5
B11111110, B10010010, B10010010, B10010010, B10011110, //6
B10000000, B10000000, B10000000, B10000000, B11111110, //7
B11111110, B10010010, B10010010, B10010010, B11111110, //8
B11110010, B10010010, B10010010, B10010010, B11111110, //9
B11111110, B00000000, B11111110, B10000010, B11111110 //10
};
/*
COMBINAZIONI DECODER
Combinazione --> colonna/riga
000 --> 1
001 --> 2
010 --> 3
011 --> 4
100 --> 5
101 --> 6
110 --> 7
111 --> 8
*/
//==================================== SETUP ===============================================
void setup(){
//Serial.begin(115200);
pinLow(ACCENDI);
pinOut(pinDecoderA1);
pinOut(pinDecoderA2);
pinOut(pinDecoderA3);
pinOut(pinDecoderB1);
pinOut(pinDecoderB2);
pinOut(pinDecoderB3);
//INTERRUPT ENCODER ROTATIVO In caso di interrupt viene chiamata la funzione letturaPotenza()
//Imposta il pin come input (questo passaggio è opzionale se il pin è già configurato correttamente)
//DDRD &= ~(1 << PD3); // Imposta il bit 3 del registro DDRD (PD3) a 0 per impostare il pin 3 come input
//DDRD &= ~(1 << PD2); // Imposta il bit 3 del registro DDRD (PD2) a 0 per impostare il pin 2 come input
//pinMode(ENCODER_CLK, INPUT);
// ↑ I PIN SONO GIA' IMPOSTATI IN INPUT DI DEFAULT ↑
//attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), letturaPotenza, FALLING); //Rileva interazione utente
EICRA = B00000010; // Imposta ISC11 a 1, ISC10 a 0 per impostare l'interrupt a falling
EIMSK = B00000001; // EIMSK – External Interrupt Mask Register, abilito External Interrupt Request INT0
//REGISTRI ADC
ADMUX = B00000001; //Registro per impostazioni ADC
//Bit 7:6 Reference selection Bits (voltaggio ADC) - selezionato 00 AREF
//Bit 4 Reserved
//Bit 3:0 MUX Analog channel selection Bits (selezione pin per conversioni) - selezionato ADC1 (A1 = 0001)
ADCSRA = B10001111; //ADC control and status register A
//Bit 7 - ADEN: ADC enable - selected 1 (enabled)
//Bit 6 - ADSC: ADC start conversion - selected 0 (wait to start a new conversion)
//Bit 5 ADATE: ADC auto trigger enable - selected 0 (auto trigger disabled) - Nel simulatore wokwi
//... l'auto trigger non funziona, perciò è disabilitato e l'avvio delle conversioni è "manuale"
//Bit 4 ADIF: ADC interrupt flag - selected 0 (conversion in progress) - lascio a 0 in quanto
//... ponendo ADSC a 1 viene automaticamente impostato a 0 (1 = conversione completata)
//Bit 3 ADIE: ADC interrupt enable - selected 1 (interrupt enabled)
//Bit 2:0 ADPS2:0: ADC prescaler selected Bits - selected 111 = 128 (prescaler a 128 per
//... una maggiore precisione)
//REGISTRI TIMER2
//ASSR = 0; // Asynchronous Status Register Par:17.11.8 (pag.133)
//CTC CON VALORE DI COMPARAZIONE 125, COMBINATO AD UN PRESCALER A 128, fa si che venga lanciato
//un interrupt ogni millisecondo (la combinazione è stata scelta a fini di precisione di calcolo)
TCCR2A |= (1 << WGM20); // CTC mode (17.11.1 – Timer/Counter Control Register A)
//Bit1:0 WGM21:0: waveform generation mode - selected "1" 01 (CTC) - Imposto CTC per lanciare un
//... interrupt ogni volta che il timer raggiunge 125 tick (impostato nel registro OCR2A)
TCCR2B |= (1 << CS22);
TCCR2B |= (1 << CS20);
//Bit 2:0 CS22:0: clock select (prescaler) - selected 101 (clk/128)
TCNT2 = 0; // restart counter (17.11.3 TCNT2 – Timer/Counter Register)
//Registro contenente il numero di tick
OCR2A = 125; // compare register = 125, will be reached after 1ms
//Registro contenente il valore da raggiungere
//AVVIO TIMER E ADC
TIMSK2 |= (1 << OCIE2A); //Timer/Counter2 Output Compare Match A Interrupt Enable
ADCSRA |= B01000000; // Set ADSC in ADCSRA (0x7A) to start the ADC conversion
}
//===================== GESTIONE INTERRUPTS TIMER2, ADC ed ENCODER===============================================
// Interrupt service routine encoder (SELEZIONE POTENZE DEL 2)
ISR(INT0_vect)
{
//pressione del bottone in alto, aumenta la var. potenza
dtValue = (PIND & (1 << PD3)) ? HIGH : LOW; //lettura pin3 encoderDT (D3)
if (dtValue == HIGH && potenza < 10) {
potenza++;
}
if (dtValue == LOW && potenza > 0) { //pressione del bottone in basso, diminuisce la var. potenza
potenza--;
}
flagMedia=false; // stoppa le misurazioni
numMS = 1 << potenza; //imposta la durata del tempo di misura in 2^potenza millisecondi
Serial.print("Nuova Potenza del 2: "); Serial.println(potenza);
for(int a = 0; a < 10000; a++)
{stampaPotenza(potenza);}
attivaMisurazioni(); //riattiva le misurazioni
}
// Interrupt service routine for the 1ms tick (TIMER)
ISR(TIMER2_COMPA_vect)
{
if (flagMedia == true) { //se è attiva la misurazione
numTI++; //incremento il numero di mS passati
}
if (numTI >= numMS) { //se sono passati più di numTI interrupt di Timer2 equivalenti a numTI mS
flagMedia = false; //e fermo la misurazione del segnale analogico
}
}
// Interrupt service routine for ADC conversion complete (LETTURE ADC)
ISR(ADC_vect)
{
if (flagMedia == true) {
adcNum ++; // incrementa il contatore di letture
//Vedi par.23.9.3 pag:219 del Datasheet, ADCL and ADCH – The ADC Data Register
adcVal = ADCL | (ADCH << 8); // leggo in sequenza i 2 semiregistri
adcSum += adcVal; // aggiungo il valore attuale alla somma
}
ADCSRA |=B01000000; // riabilita interrupt
}
//======================================= FUNZIONI ========================================================
//Accende e poi spegne il led indicato da r (riga) e c (colonna)
void accendiLed(uint8_t r, uint8_t c){
pinLow(ACCENDI); // Spengo finchè non ho impostato la combinazione corretta
//Imposto a Low tutti i pin
PORTB = B11000000;
//Imposto la combinazione corretta 7 6 5 4 3 2 1 0
// r r r c c c
//PORTB = (r << 3);
//PORTB |= (c);
xByte = (r << 3);
xByte |= (c);
PORTB = xByte;
pinHigh(ACCENDI);
}
void stampaPotenza(int num) //num = numero da stampare sulla matrice
{
// Lista di byte da accendere contenuti nell'array bidimensionale "comb"
// Numero da visualizzare "num"
for(int colonna = 0; colonna < 5; colonna++)
{
for(int riga = 7; riga > 0; riga--)
{
if(bitRead(comb[num][colonna], riga) == 1)
{
accendiLed(7-riga, colonna);
}
}
}
}
//Azzera tutte le variabili e fa ripartire le misurazioni e il calcolo della media
void attivaMisurazioni() {
numTI = 0;
adcSum = 0;
adcNum = 0;
adcVal = 0;
flagMedia = true; // riparto con le misurazioni
}
void loop(){
if (flagMedia == false) { // ho finito le misurazioni, presento i risultati
// calcolo la media
media = adcSum / adcNum; //ritorna un numero tra 0 e 1023
//Serial.println(media);
//Calcolo il led da accendere (8 divisioni da 0 a 7)
//scorre tutte le colonne di una posizione verso destra
for(c = 7; c > 0; c--) {divisione[c] = divisione[c - 1];}
//aggiorna la prima colonna con la nuova tacca corrispondente a media/8
divisione[0] = B00000000;
divisione[0] |= 1 << (media/128);
// riparto con nuove letture
attivaMisurazioni();
}
//visualizzo sulla matrice le 5 medie misurate, la più recente a sinistra
for (c=0; c<8; c++) {
//Serial.println(divisione[c], BIN);
for (r=0; r<8; r++) {
if (bitRead(divisione[c], 7-r) == 1) {
accendiLed(r,c);
}
}
}
/*
pinHigh(ACCENDI);
PORTB = 11011110;
delay(800);
PORTB = 11101110;
delay(800);
//pinLow(ACCENDI);
//PORTB = (3 << 3);
//PORTB |= (3);
//PORTB = ((3 << 3) | 3);
//pinHigh(ACCENDI);
*/
}