/*
* LAB Name: Sistema di temperatura controllato
* Author: Alberto Terenzi
* Matricola: 126149
* Board type: Arduino Nano
*/
// Definizioni dei pin usati nel sistema corrispondenti alla locazione dei pulsanti min e max
#define tastoMax 16
#define tastoMin 32
// inizializzazione di alcune variabili
volatile uint8_t lupTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; // Tabella contenente i numeri da visualizzare nei display 7-segmenti
volatile uint8_t digitOne; // Display unità
volatile uint8_t digitTen; // Display decine
volatile bool enDig = false; // Abilita/Disabilita visualizzazione dei display
volatile uint8_t tMin; // Soglia temperatura minima
volatile uint8_t tMax; // Soglia temperatura massima
volatile uint8_t blink_timer; // Durata lampeggio display
volatile bool blink = false; // Abilita/Disabilita lampeggio display
volatile bool cambiaMin = false; // Abilita/Disabilita stato modifica temperatura minima
volatile bool cambiaMax = false; // Abilita/Disabilita stato modifica temperatura massima
volatile uint16_t timeout; // Contatore generale per tutte le temporizzazioni (rappresenta 10ms)
volatile uint16_t lupTemp[] = {235,318,408,501,590,670,739,796,843,880,909}; // Tabella contenente i valori approssimati di temperatura (ogni 8 gradi)
volatile uint8_t ADC_RES_L = 0;
volatile uint8_t ADC_RES_H = 0;
volatile float celsius; // Valore temperatura letta dal sensore in gradi Celsius
const uint8_t tMin_addr = 0; // Indirizzo eeprom dove memorizzare valore temperatura minima
const uint8_t tMax_addr = 1; // Indirizzo eeprom dove memorizzare valore temperatura massima
/*============================================================================================================*/
void setup() { // Impostazioni iniziali.
DDRD = B11111111; // Porta D tutti i pin output
DDRB |= _BV(1) | // Porta B pin 1 output
_BV(2) | // Porta B pin 2 output
_BV(3); // Porta B pin 3 output
DDRB &= ~(_BV(4) | // Porta B pin 4 input
_BV(5)); // Porta B pin 5 input
PORTB |= _BV(1) | // Porta B pin 1 HIGH (spengo digit decine - catodo comune)
_BV(2); // Porta B pin 2 HIGH (spengo digit unità - catodo comune)
eepromInit();
timerInit(); // inizializza timer per Interrupt
adcInit(); // inizializza ADC
pwmInit(); // inizializza PWM
}
/*============================================================================================================*/
void loop() { // loop principale.
adcRead(); // leggo la temperatura della sonda NTC
// tramite l'ADC connesso al PIN AN0.
// il dato viene memorizzato nella variabile
// globale celsius.
pwmSet(celsius); // imposto il segnale PWM in funzione della
// temperatura letta e delle regole assegnate.
display(celsius); // converto la temperatura letta in decine ed
// unità che poi verranno visualizzate sul
// display dalla routine che gira in background.
buttonCheck(); // controllo se uno dei tasti viene premuto
// per più di due secondi e attivo la modalità
// di variazione di tMin o tMax
}
/*============================================================================================================*/
ISR(TIMER1_COMPA_vect) // routine interrupt legata al TIMER1
{
OCR1A += 20000; // riporto avanti di 20000 conteggi
// il registro comparatore.
PORTD=B00000000; // azzero entrambi i display.
if (blink) // controllo se il lampeggio è attivo (modalità modifica).
{
if (blink_timer == 0){ // se attivo, reimposto il contatore a 80,
blink_timer = 80; // che corrisponde a un ciclo di lampeggio di 0.8 Sec.
}
}
else { // se il lampeggio non è attivo
blink_timer = 0; // azzero il contatore per riattivare
} // la normale visualizzazione sul display.
if (blink_timer < 40) // se il contatore è compreso tra 0 a 40, scrivo sui display,
{ // corrispondente a una visualizzazione che dura 0.4 Sec.
if (enDig) // se enDig = true allora scrivo le unità.
{
PORTB |= _BV(1); // spengo le decine.
PORTB &= ~_BV(2); // attivo le unità.
PORTD = digitOne; // scrivo le unità.
enDig = false; // setto enDig a false così il prossimo giro
// verranno visualizzate le decine.
}
else // se enDig = false allora scrivo le decine.
{
PORTB |= _BV(2); // spengo le unità.
PORTB &= ~_BV(1); // attivo le decine.
PORTD = digitTen; // scrivo le decine.
enDig = true; // setto enDig a true così il prossimo giro
// verranno visualizzate le unità.
}
}
if (blink_timer != 0) { // decremento il contatore per il lampeggio.
blink_timer--;
}
if (timeout != 0) { // decremento il contatore generale.
timeout--;
}
}
/*============================================================================================================*/
void timerInit() // configuro Timer1 per generare un interrupt
// ogni 1/100 di secondo
{
TCCR0B = 0; // Disabilito timer0 per
TIMSK0 = 0; // evitare interferenze
TCCR1A = 0; // reset Timer1A.
TCCR1B = 0; // reset Timer1B.
TCCR1B |= _BV(WGM11); // configuro Prescaler = 8.
OCR1A = 20000; // carico il registro Compare1A.
TIMSK1 |= _BV(OCIE1A); // abilito Interrupt per Timer1_COMPA.
}
/*============================================================================================================*/
void adcInit() // inizializzazione convertitore analogico/digitale.
{
ADMUX = 0; // reset registro ADMUX.
ADCSRA = 0; // reset registro ADCSRA.
ADMUX |= _BV(REFS0); // seleziono canale ADC0 e imposto come riferimento AVcc.
ADCSRA |= _BV(ADPS0) | //
_BV(ADPS1) | // setto il prescaler a 128 e lascio disabilitato l'interrupt.
_BV(ADPS2); //
ADCSRA |= _BV(ADEN); // abilito ADC
}
/*============================================================================================================*/
void pwmInit() // inizializzazione PWM.
{
TCCR2A = 0; //reset TCCR2A.
TCCR2B = 0; //reset TCCR2B.
OCR2A = 0; // imposto pwm iniziale a 0.
TCCR2A |= _BV(WGM21) | _BV(WGM20); // imposto modalità fast PWM.
TCCR2A |= _BV(COM2A1); // attivo uscita sul pin OC2A (pin 11).
TCCR2B |= _BV(CS21) | _BV(CS20); // setto il prescaler a 32 e attivo il PWM.
}
/*============================================================================================================*/
void eepromInit() // procedura di inizializzazione eeprom.
{
tMin = eepromRead( tMin_addr ); // leggo il valore di tMin dalla eeprom
if (tMin = 0xFF) { // se è la prima volta, il valore sarà sempre FF
tMin = 35; // in questo caso imposto un valore di default
}
tMax = eepromRead( tMax_addr ); // stesso procedimento per tMax
if (tMax = 0xFF) {
tMax = 55;
}
}
/*============================================================================================================*/
void pwmSet(uint8_t temp) // routine che imposta l'uscita PWM.
{
if (temp < tMin) // se temperatura < tMin
{
TCCR2A &= ~_BV(COM2A1); // faccio tornare OC2A un normale pin della porta B.
PORTB &= ~_BV(DDB3); // imposto a 0 l'uscita del PWM (bit 3 della PORTA B).
}
else if (temp > tMax) // se temperatura > tMax
{
TCCR2A &= ~_BV(COM2A1); // faccio tornare OC2A un normale pin della porta B.
PORTB |= _BV(DDB3); // imposto a 1 l'uscita del PWM (bit 3 della PORTA B).
}
else // Se temperatura è tra min e max calcola PWM
{
TCCR2A |= _BV(COM2A1); // riporto il PIN 3 della PORTA B sotto il controllo del PWM.
uint8_t range = tMax - tMin; // calcolo il range entro il quale opererà il regolatore.
OCR2A = ((temp - tMin) * (190 / range)) + 64; // imposto l'uscita PWM
}
}
/*============================================================================================================*/
void display(uint8_t temp) // funzione di visualizzazione sul display 7 segmenti
{
uint8_t bcdTempOne = temp % 10; // calcolo le unità (il resto di temp/10).
digitOne = lupTable[bcdTempOne]; // imposto il digit delle unità = all'n-esimo byte della tabella.
uint8_t bcdTempTen = temp / 10; // calcolo le decine (temp/10).
digitTen = lupTable[bcdTempTen]; // imposto il digit delle decine = all'n-esimo byte della tabella.
}
/*============================================================================================================*/
void adcRead() // funzione che legge la temperatura dal sensore NTC
{
ADCSRA |= _BV(ADSC); // avvio conversione ADC.
while (ADCSRA & (1<<ADSC)); // attendo la fine della conversione.
ADC_RES_L = ADCL; // leggo il valore dai due registri
ADC_RES_H = ADCH; // del convertitore ADC
int analogValue = 1024- // leggo il risultato come complemento a 1024
(ADC_RES_L + // perchè l'NTC diminuisce il suo valore all'aumentare
(ADC_RES_H << 8));// della temperatura.
byte pointer = 0;
int x;
while (analogValue >= lupTemp[pointer])
{
pointer++;
}
if (analogValue == lupTemp[pointer])
{
celsius = (pointer << 3);
}
else
{
int diff = lupTemp[(pointer)] - lupTemp[pointer-1];
x = (analogValue - lupTemp[pointer-1]) << 3;
celsius = ((pointer - 1) << 3) + (x / diff);
}
if (celsius < 0) { // Poichè non possiamo visualizzare valori negativi,
celsius = 0; // se il valore è inferiore a zero lo imposto uguale a 0.
}
}
/*============================================================================================================*/
// funzione che controlla se i tasti sono premuti
void buttonCheck() // per più di 2 secondi
{
if ((PINB & tastoMin) == tastoMin) // controllo se il tasto (<) è premuto.
{
timeout = 200; // imposto timeout a 2 sec.
do {
if (timeout == 0) // controllo se timeout va a 0.
{
cambiaMin = true; // se il tasto rimane premuto più di 2 sec
} // attivo il flag per variazione temp. minima.
// il ciclo gira fino a quando il flag non è attivo
// o il tasto non è più premuto.
} while (!(cambiaMin) & ((PINB & tastoMin) == tastoMin));
if (cambiaMin) { // se è attivo il flag per la modifica della temperatura
gestioneMin(); // minima, richiamo la funzione corrispondente.
}
timeout = 0; // resetto il timeout.
}
if ((PINB & tastoMax) == tastoMax) // controllo se il tasto (>) è premuto.
{
timeout = 200; // imposto il timeout a 2 sec.
do {
if (timeout == 0) // controllo se timeout va a 0.
{
cambiaMax = true; // se il tasto rimane premuto più di 2 sec
} // attivo il flag per variazione temp. massima.
// il ciclo gira fino a quando il flag non è attivo
// o il tasto non è più premuto.
} while (!(cambiaMax) & ((PINB & tastoMax) == tastoMax));
if (cambiaMax){ // se è attivo il flag per la modifica della temperatura
gestioneMax(); // massima, richiamo la funzione corrispondente.
}
timeout = 0; // resetto il timeout.
}
}
/*============================================================================================================*/
void gestioneMin() // funzione che gestisce la variazione
{ // della temperatura minima
volatile int x;
blink = true; // attivo lampeggio.
display(tMin); // visualizzo tMin.
while ((PINB & tastoMin) == tastoMin)
{ // attendo il rilascio del tasto (<)
}
timeout = 500; // timeout = 5 sec.
x = timeout;
while ((x - timeout) < 30); // 0.3 sec ritardo per debouncing
while (timeout != 0) // in attesa del timeout...
{
bool keyPressed =
(((PINB & tastoMin) == tastoMin)); // leggo lo stato tasto del tasto (<).
if (keyPressed == true) // è premuto?
{
timeout = 500; // timeout = 5 sec
if (tMin >= 1) { tMin--; } // se possibile decrem. tMin.
display( tMin ); // visualizzo nuovo tMin.
x = timeout;
while ((x - timeout) < 80); // ritardo ripetizione.
}
keyPressed =
(((PINB & tastoMax) == tastoMax)); // leggo lo stato tasto del tasto (>).
if (keyPressed == true) // è premuto?
{
timeout = 500; // timeout = 5 sec.
if (tMin < (tMax-1))
{ tMin++; } // se possibile increm. tMin
display( tMin ); // visualizzo nuovo tMin.
x = timeout;
while ((x - timeout) < 80); // ritardo ripetizione.
}
}
eepromWrite(tMin_addr, tMin);
blink = false; // disattiva lampeggio.
cambiaMin = false; // fine variazione tMin.
}
/*============================================================================================================*/
void gestioneMax() // funzione che gestisce la variazione
{ // della temperatura massima
volatile int x;
blink = true; // attivo lampeggio.
display(tMax); // visualizzo tMax.
timeout = 500; // timeout = 5 sec.
while ((PINB & tastoMax) == tastoMax)
{ // attendo il rilascio del tasto (>)
}
timeout = 500; // timeout = 5 sec.
x = timeout;
while ((x - timeout) < 30); // 0.3 sec ritardo per debouncing
while (timeout |= 0) // in attesa del timeout...
{
bool keyPressed =
(((PINB & tastoMin) == tastoMin)); // leggo lo stato tasto del tasto (<)
if (keyPressed == true) // è premuto?
{
timeout = 500; // timeout = 5 sec.
if (tMax > (tMin + 1))
{ tMax--; } // se possibile decrem. tMax
display( tMax ); // visualizzo nuovo tMax.
x = timeout;
while ((x - timeout) < 80); // ritardo ripetizione.
}
keyPressed =
(((PINB & tastoMax) == tastoMax)); //leggo lo stato del stato tasto del (>)
if (keyPressed == true) // è premuto?
{
timeout = 500; // timeout = 5 sec.
if (tMax < 99) { tMax++; } // se possibile increm. tMax.
display( tMax ); // visualizzo nuovo tMax.
x = timeout;
while ((x - timeout) < 80); // ritardo ripetizione.
}
}
eepromWrite(tMax_addr, tMax);
blink = false; // disattivo lampeggio
cambiaMax = false; // fine variazione tMax.
}
/*============================================================================================================*/
// Legge un byte dalla EEPROM
void eepromWrite(uint16_t addr, uint8_t data)
{
char cSREG;
cSREG = SREG; // salvo lo stato del registro SREG
cli(); // disabilito gli interrupt durante la scrittura
while(EECR & (1<<EEPE)) // attende la fine dell'operazione precedente.
;
EEAR = addr; // setto l'indirizzo su cui scrivere
EEDR = data; // imposto il dato da scivere
EECR |= (1<<EEMPE); // abilito la scrittura
EECR |= (1<<EEPE); // avvio la scrittura
SREG = cSREG; // ripristino il registro SREG
}
/*============================================================================================================*/
uint8_t eepromRead(uint16_t addr) // Scrive un byte nella EEPROM
{
while(EECR & (1<<EEPE)) // attende la fine dell'operazione precedente.
;
EEAR = addr; // setto l'indirizzo da leggere
EECR |= (1<<EERE); // avvio la lettura
return EEDR; // ritorno il dato letto
}