#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)
#define digitalRead(pin) ((pin & (1 << (pin))) ? HIGH : LOW)
#define ENCODER_CLK 2
#define ENCODER_DT 4
const byte LEDMATRIX[][5]= {
B11100101, B01100101, B11100100, B01100100, B11100011, //riga 6
B01100011, B11100010, B01100010, B11100001, B01100001, //riga 5
B11100000, B01100000, B11010100, B01010100, B11010011, //riga 4
B01010011, B11010010, B01010010, B11010001, B01010001, //riga 3
B11010000, B01010000, B11000011, B01000011, B11000010, //riga 2
B01000010, B11000001, B01000001, B11000000, B01000000, //riga 1
B10110010, B00110010, B10110001, B00110001, B10110000 //riga 0
};
uint8_t diag; //Usato per diagnostica
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 int r, c, pin1, pin2; //indici temporanei per i cicli for
volatile uint8_t led, segno; //variabili temporanee per indicare LED e polarità (HIGH o LOW)
//Variabili encoder
volatile byte dtValue; //valore letto dell'encoder dopo un cambio di stato
//Variabili matrice
volatile uint8_t divisione[5] = {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
};
void setup()
{
Serial.begin(115200);
//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
} // FINE SETUP
//===================== GESTIONE INTERRUPTS TIMER2, ADC ed ENCODER===============================================
// Interrupt service routine encoder
ISR(INT0_vect)
{
//pressione del bottone in alto, aumenta la var. potenza
dtValue = (PIND & (1 << PD4)) ? HIGH : LOW; //lettura pin3 encoderDT (A1)
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);
//spegniMatrice(); //cancella la media visualizzata
i=1500; //ripetuto i volte per rendere persistente all'occhio il numero visualizzato
//visualizza sulla matrice di led il valore numerico di potenza
do{
for(c = 0; c < 5; c++) //da sinistra verso destra
{
for(r = 7; r >= 0; r--) //dall'alto verso il basso
{
if ((comb[potenza][c] & (1 << r)) != 0)
{
accendiLed(7-r, c);
}
}
}
}while(i-- > 0);
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
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) usando la matrice LEDMATRIX[r][c],
il valore recuperato dalla matrice indica i due pin fisici da usare per accendere il led e la loro polarità
*/
void accendiLed(int r, int c) {
/* codifica del valore led e segno
BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0
SEGNO 2^2 2^1 2^0 0 2^2 2^1 2^0
1=HIGH [binario primo pin] zero [binario secondo pin]
0=LOW
*/
led = LEDMATRIX[r][c];
segno = bitRead(led,7);
/*
traduce il valore di r (0..6) e c (0..4) nei corrispondenti pin fisici,
viene aggiunto il valore 6 perche nello schema elettrico utilizzato i pin usati
non vanno da D0 a D6 ma da D6 a D12
*/
pin1 = 6 + ((led &= B01110000) >> 4); //traduce il binario del primo pin in decimale
led = LEDMATRIX[r][c]; //l'operazione di shift corrompe la variabile, la rileggo
pin2 = 6 + ((led &= B00000111)); //traduce il binario del secondoo pin in decimale
pinOut(pin1); //i due pin vengono impostati in modalità OUTPUT
pinOut(pin2);
if (segno == 1) { //accende il led di sinistra della coppia di led accoppiati a polarità invertita
pinHigh(pin1);
pinLow(pin2);
pinLow(pin1);
} else { // se segno = 0, accende il led di destra ...
pinLow(pin1);
pinHigh(pin2);
pinLow(pin2);
}
pinInp(pin1); //rimette i 2 pin usati in modalità ad alta impedenza
pinInp(pin2);
}
//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
}
//spegne tutti i led della matrice
void spegniMatrice() {
for (r=6; r<13; r++) {
pinInp(r);
}
}
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
//Calcolo il led da accendere (8 divisioni da 0 a 7)
//scorre le colonne da 4 a 1 di una posizione verso destra
for(c = 4; c > 0; c--) {divisione[c] = divisione[c - 1];}
//aggiorna la prima colonna con la nuova tacca corrispondente a media/7
divisione[0] = B00000000;
divisione[0] |= 1 << (media/170);
//diagnosi OPZIONALE
/*
diag = digitalRead(13);
if (diag == HIGH) {
Serial.print("Tacca VUMeeter(BIN)=");Serial.println(divisione[0],BIN);
Serial.print("Potenza=");Serial.print(potenza);Serial.print(" = mS:");Serial.println(numMS);
Serial.print("Media =");Serial.println(media);
Serial.print("ultimo adcVal=");Serial.println(adcVal);
Serial.print("adcNum=");Serial.println(adcNum);
Serial.print("numTI. =");Serial.println(numTI);
Serial.println(millis());
Serial.println("===============================");
}
*/
// riparto con nuove letture
attivaMisurazioni();
}
//visualizzo sulla matrice le ultime 5 medie misurate, la più recente a sinistra
for (c=0; c<5; c++) {
for (r=0; r<7; r++) { //essendoci 7 tacche l'ultimo bit (bit 0) non viene considerato
if (bitRead(divisione[c], 6-r) == 1) {accendiLed(r,c);}
}
}
}
/*
tabella di conversione tra posizione dei led e pin di pilotaggio come rilevato dallo schema elettrico
della matrice iniziale Charlieplexing in forma triangolare (rif. figura Matrice-7Pin-CharliePlexing)
I numeri indicano il pin digitale, + indica HIGH, - indica LOW
PIN n. LED01 LED02 LED03 LED04 LED05 LED06 LED07 LED08 LED09 LED10 LED11 LED12
6 +6-5 -6+5 +6-4 -6+4 +6-3 -6+3 +6-2 -6+2 +6-1 -6+1 +6-0 -6+0
LED13 LED14 LED15 LED16 LED17 LED18 LED19 LED20 LED21 LED22
5 +5-4 -5+4 +5-3 -5+3 +5-2 -5+2 +5-1 -5+1 +5-0 -5+0
LED23 LED24 LED25 LED26 LED27 LED28 LED29 LED30
4 +4-3 -4+3 +4-2 -4+2 +4-1 -4+1 +4-0 -4+0
LED31 LED32 LED33 LED34 LED35 LED36
3 +3-2 -3+2 +3-1 -3+1 +3-0 -3+0
LED37 LED38 LED39 LED40
2 +2-1 -2+1 +2-0 -2+0
LED41 LED42
1 +1-0 -1+0
0
TABELLA RIARRANGIATA 7x6 con ROW=0..6 e COL=0..5 NB: la sesta colonna (COL=5)
non viene utilizzata e quindi i led da 36 a 42 non appaiono nella matrice.
NB: i numeri dei pin sono rappresentati in binario da 0 a 7 con 3 bit.
Il bit 7 indica il segno, 1 indica (+) quindi HIGH, 0 indica (-) quindi LOW.
I bit 6:4 indicano il primo pin da attivare, il bit 3 è sempre a 0, non utilizzato,
i bit 2:0 indicano i secondo pin da attivare con polarità contraria a segno,
se uno è HIGH l'altro è LOW e viceversa.
CODIFICA IN MATRICE,
esempio del LED26 acceso, LEDMATRIX[1][0] = B01000010 = vale -4 +2 cioè pin 4 LOW e pin 2 HIGH
BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0
SEGNO 2^2 2^1 2^0 0 2^2 2^1 2^0
0 1 0 0 0 0 1 0
LOW binario 100 = pin 4 HIGH binario 010 = pin 2
LEDMATRIX[R][C]
COL
------------------------------------------------
ROW 0 1 2 3 4
======== ======== ======== ======== ========
6 LED01 LED02 LED03 LED04 LED05
+6-5 -6+5 +6-4 -6+4 +6-3
11100101 01100101 11100100 01100100 11100011
5 LED06 LED07 LED08 LED09 LED10
-6+3 +6-2 -6+2 +6-1 -6+1
01100011 11100010 01100010 11100001 01100001
4 LED11 LED12 LED13 LED14 LED15
+6-0 -6+0 +5-4 -5+4 +5-3
11100000 01100000 11010100 01010100 11010011
3 LED16 LED17 LED18 LED19 LED20
-5+3 +5-2 -5+2 +5-1 -5+1
01010011 11010010 01010010 11010001 01010001
2 LED21 LED22 LED23 LED24 LED25
+5-0 -5+0 +4-3 -4+3 +4-2
11010000 01010000 11000011 01000011 11000010
1 LED26 LED27 LED28 LED29 LED30
-4+2 +4-1 -4+1 +4-0 -4+0
01000010 11000001 01000001 11000000 01000000
0 LED31 LED32 LED33 LED34 LED35
+3-2 -3+2 +3-1 -3+1 +3-0
10110010 00110010 10110001 00110001 10110000
*/