Robotica 3
Programma di Robotica della classe quinta (aggiornato al 2023)
Numero di ore/settimana: 3 ore
Numero di ore/a.s.: 99 ore
Modulo 1
INTERRUPT E MICROCONTROLLORE ATMEGA328/P
Interrupt con pin 2 e 3
Interrupt con pin change
Interrupt con timer
Libreria TimerOne
Frequenzimetro con Arduino
Misura posizione encoder relativo
Modulo 2
PROCESSING (vale come PCTO)
Impostazioni interfaccia
Interfacciamento con mouse
Interfacciamento con Arduino
Radar con sensore infrarossi
Modulo 3 (vale come PCTO)
LABVIEW
Programmazione visuale
Realizzazione interfaccia
Elaborazione dei dati
Applicazioni e esercitazioni
Modulo 5 (vale come PCTO)
PLC
Programmazione con Ladder
Gestione motori
Gestioni luci
Applicazioni e esercitazioni
Modulo 6
SENSORI
Sensori e trasduttori:
posizione e spostamento
velocità
peso e deformazione
temperatura
umidità
controllo della luminosità (fotoresistenza, fotodiodo, fototransistor, fotoaccoppiatore)
prossimità (induzione, effetto Hall, ultrasuoni)
Reti di condizionamento del segnale con A.O.
Modulo 7
SHIELD ARDUINO
Shield wireless Bluetooth
App Inventor
Modulo 8 - opzionale
ROBOT LINEFOLLOWER
Arduino nano
Sensori e motoriduttori
Alimentazione
Progettazione circuito elettronico
Realizzazione e collaudo scheda
Anni scolastici precedenti
Prova di verifica scritta 5A e 5B ENC
Una linea di lavorazione riceve dei pezzi da verniciare che vengono collocati in sequenza sulla parte iniziale di un nastro trasportatore in appositi alloggiamenti lunghi 15 cm.
Per avere una verniciatura ottimale il pezzo deve essere trasportato dal nastro ad una velocità di 1,5 cm/s.
Alimentando il motore del nastro trasportatore con una tensione fissa la velocità del nastro varia in base al numero dei pezzi presenti sul nastro. Con un numero elevato di pezzi si riduce, con pochi pezzi aumenta. In tale situazione il pezzo non viene verniciato correttamente e deve essere scartato. Si individua pertanto una soluzione nel controllo dell’azionamento del motore. Il driver del motore viene pertanto controllato da un segnale PWM prodotto dalla scheda di controllo a microcontrollore.
Fatte le eventuali ipotesi aggiuntive, si scriva un programma in linguaggio C per microcontrollori PIC che:
1) misuri la velocità del nastro grazie a un sensore IR che vede gli alloggiamenti ed è collocato prima della verniciatura;
2) aumenti o riduca la velocità del nastro in modo da raggiungere la velocità necessaria;
3) visualizzi la velocità del nastro in cm/s su un display a tre cifre ogni 0,5 secondi;
4) azioni un sistema per scartare i pezzi quando la velocità differisce da quella ottimale del 2%
Bozza soluzione
Ipotesi aggiuntive:
RA0 sensore IR – uscita in logica inversa
RA1 pistone che spinge i pezzi fuori dal nastro scartandoli
RC2-CCP1 uscita PWM
PORTB → encoder → display a 2 cifre
RC4-7 → encoder → display a 1 cifra
Velocità del nastro 1,5cm/s = 15 mm/s = 150 decimi di mm/s
lo spazio di 15 cm viene percorso in un tempo di t=s/v=15/1,5=10s=10000ms
la velocità in decimi di mm/s si calcola in base al tempo misurato t → v=s/t=15*100/(t/1000) con t espersso in ms
Con velocita’ di 150 decimi di mm/s allora il tempo è di 10 s V= 1500/(10000/1000)=1500/10=150 decimi di mm/s
Verniciatura=10 s → dopo 15 s si scarta o si fa passare
Dopo 17 s è davanti al sistema di scarto, il pistone
#include "pic.h"
void setup(void)
{
TRISA=0b111111; // tutti i 6 pin sono ingressi – RA0 sensore IR
TRISB=0b00000000; // tutti gli 8 pin sono uscite – diplay 2 cifre
TRISC=0b00000000; // RC2-CCP1 uscita PWM – RC4-7 display a 1 cifra
// clock = 4 MHz Tclock=0,25us
// impostazione OPTION
RBPU=0; // resistenze di pull-up abilitate
T0CS=0; // Timer0 impostato come timer
PSA=0; // Prescaler assegnato al Timer0
PS0=1; // Prescaler divide per 4
PS1=0;
PS2=0;
// impostazione ADCON1
PCFG3=0; // ingressi DDDDD
PCFG2=1;
PCFG1=1;
PCFG0=0;
// con prescaler TclockTimer=Tclock*4*4=4us
// impostazione CCP1CON
CCP1Y=0; // LSB Duty cycle 00
CCP1X=0;
CCP1M0=0; // modalità PWM 1100
CCP1M1=0;
CCP1M2=1;
CCP1M3=1;
PR2=0xFF; // PR2 è utilizzato per calcolare il periodo del PWM
// impostazione T2CON
TMR22ON=1;
T2CKPS1=0; // prescaler 1:1
T2CKPS0=0;
// impostazione CCPR1L = 128 che corrisponde a 1,5 cm/s alla pratenza (ipotesi)
CCPR1L=128; // Duty cycle CCP1
T0IE=1; // Abilita interrupt sul overflow del Timer0
GIE=1; // Abilita interrupt
}
unsigned long time=0, time1=0, time2=0, time3=0;
int misura=0;
void interrupt isr(void) // interrupt program
{
time++; // misura tempo
time1++; // conteggio per scartare pezzo difettoso
time2++; // visualizzazione su display ogni 0,5s
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
T0IF=0; // azzeramento flag di interrupt Timer0
if(time2==500)
{
display(misura);
time2=0;
}
}
void diplay(int misura) // il display a 3 cifre è collegato a PORTB e alla parte alta di PORTC
{
PORTB=misura%10;;
PORTB|=misura%100/10<<4;
PORTC&=0x0F;
PORTC|=misura/100<<4;
}
int stato=0;
int stato1=0;
int t=0;
void loop(void)
{
if (!RA0 && !stato) // attende inizio alloggiamento e azzera tempo
{
stato=1;
time=0;
}
if (stato==1 && time==5) // antivibrazioni
stato=2;
if (stato==2 && RA0) // attende fine alloggiamento e calcola velocità
{
t=time;
misura=1500*1000/t ; // v=s/t=15*100/(t/1000)
time1=0;
stato=3;
stato1=1;
}
if (stato==3 && misura!=150) // viene aggiornata la velocità almeno ogni 10 secondi
{
if (misura>150) CCPR1L--:
else CCPR1L++;
stato=0;
}
if (stato1=1 && time1==15000) // dopo 15s si scarta il pezzo se la velocità non è quella ottimale
{
time1=0;
stato1=0;
if ((misura<(1,5*0,98) || misura>(1,5*1,02)) RA1=1; // azione pistone
}
}
void main(void)
{
setup();
while(1) loop();
}
Prova di verifica scritta 5A ENC
Un serbatoio contenente acqua può essere rifornito con una portata di 6 litri al minuto.
Il serbatoio fornisce un’utenza che consuma mediamente 1900 litri al giorno.
Una scheda a microcontrollore viene utilizzata per controllare il serbatoio.
Sapendo che:
a) si vuole visualizzare la quantità d’acqua in litri, contenuta nel serbatoio,
b) il serbatoio è alto 2,1 metri e ha una base circolare di raggio 50 cm,
c) è possibile bloccare il flusso d’acqua di entrata con una elettrovalvola,
d) la quantità d’acqua viene misurata facendo la media tra i tre valori misurati da tre sensori IR analogici che misurano la distanza tra un punto del bordo del serbatoio e il pelo dell’acqua;
e) ogni sensore restituisce una tensione da 0 a 5V inversamente proporzionale al livello dell’acqua, quindi quando l’acqua arriva al fondo il sensore restituisce 5V.
Facendo le ipotesi aggiuntive sul circuito che si vuole utilizzare, si scriva un programma in linguaggio C per un microcontrollore PIC16F876 che:
1) visualizzi su un display a 7 segmenti a 4 cifre la quantità d’acqua in litri disponibile nel serbatoio;
2) faccia suonare un buzzer con una frequenza di 3 Hz quando il livello dell’acqua scende sotto i 30 cm, il buzzer si spegne quando il livello dell’acqua sale sopra i 30 cm;
3) regoli il flusso dell’acqua in entrata in modo che il livello dell’acqua non superi 1,70 metri.
Bozza soluzione
#include "pic.h"
int misura=0;
int livello=0;
void setup(void)
{
TRISA=0b101011; // 5 pin configurati come ingressi DADAA – RA2 buzzer – RA4 elettrovalvola
// RA0, RA1, RA3 sensori
TRISB=0b00000000; // tutti gli 8 pin sono uscite per visualizzare sul display
// 4 pin collegati ad un decoder BCD-7Segmenti e 4 pin collegati all’altro decoder
TRISC=0b00000000; // tutti gli 8 pin sono uscite per visualizzare sul display
// 4 pin collegati ad un decoder BCD-7Segmenti e 4 pin collegati all’altro decoder
// clock = 4 MHz Tclock=0,25us
// impostazione OPTION
nRBPU=0; // resistenze di pull-up abilitate
T0CS=0; // Timer0 impostato come timer
PSA=0; // Prescaler assegnato al Timer0
PS0=1; // Prescaler divide per 4
PS1=0;
PS2=0;
// con prescaler TclockTimer=Tclock*4*4=4us
// impostazione ADCON1
PCFG3=0; // ingressi PORTA DADAA
PCFG2=1;
PCFG1=0;
PCFG0=0;
ADFM=1; // risultato allineato a destra
// VERIFICA ERRORE con ALLINEAMENTO A SINISTRA
// ogni incremento sulla variabile “misura” vale 2000/256=7,8 mm
// volume corrispondente 7,8*1,13=8,8 litri
// l’errore e’ troppo alto rispetto alla visualizzazione
// VERIFICA ERRORE con ALLINEAMENTO A DESTRA
// ogni incremento sulla variabile “misura” vale 2000/1024=1,95 mm
// volume corrispondente 1,95*1,13=2,2 litri
// errore accettabile
// per avere una precisione di un litro si dovrebbe utilizzare un ADC a 11 bit
// in quel caso ogni incremento sulla variabile “misura” vale 2000/2048=0,97 mm
// volume corrispondente 0,97*1,13=1,1 litri
// impostazione INTCON
T0IE=1; // Abilita interrupt sul overflow del Timer0
GIE=1; // Abilita interrupt
}
unsigned long time=0;
unsigned long time1=0;
void delay(unsigned long millis)
{
time=0;
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
while(time<millis);
}
int leggianalogico(unsigned char canale)
{
// impostazione ADCON0
ADCON0=canale<<3;
ADON=1; // modulo ADC acceso
ADCS1=0; // frequenza di conversione massima Fosc/2
ADCS0=0;
delay(1); // attendi acquisizione
GO_DONE=1; // inizia conversione AD
while(GO_DONE) ; // attendi termine conversione AD
delay(1); // attendi tempo richiesto da produttore
return(ADRESH*256+ADRESL);
}
int stato=0;
void interrupt isr(void) // interrupt program
{
time++;
time1++;
if (livello<200 && time1==250) // 0,5 secondi
{
RA2=!RA2; // buzzer
time1=0;
}
else
RA2=0;
if (livello>1800)
RA4=1; // blocco elettrovalvola
else
RA4=0; // abilita elettrovalvola
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
T0IF=0; // azzeramento flag di interrupt Timer0
}
void display(char misura) // il display a 4 cifre è collegato a PORTB e a PORTC
{
PORTB=((misura%1000)%100)%10; // 2345 → 345 → 45 → 5
PORTB|=((misura%1000)%100)/10)<<4; // 2345 → 345 → 45 → 4
PORTC=(misura%1000)/100; // 2345 → 345 → 3
PORTC|=(misura/1000)<<4; // 2345 → 2
}
void loop()
{
// se si ipotizza che il serbatoio sia quasi vuoto e si sta riempiendo
// la portata di ingresso è 6 litri al minuto → 6 /1000 l/s=0,006 l/s
// quando si ottiene un litro? In un minuti
// pertanto la misura di puo’ fare ogni minuto?
// no perché dipende dal consumo
misura=(leggianalogico(0)+leggianalogico(1)+leggianalogico(3))/3; // media misure
livello=2000-misura*2000/1023; // calcolo livello in millimetri
display(livello*113/100); // livello in mm (x1000) x 1,13 m2 → m3/1000=litri disponibili
delay(100);
}
void main(void)
{
setup();
while(1) loop();
}
Prova di verifica scritta 5B ENC
Sopra un nastro trasportatore passano, alla velocità costante di 1,2 m/s, delle barre metalliche della lunghezza di circa 50 cm. In un punto del nastro è posizionato un sensore ad effetto Hall che commuta quando una barra arriva in quel punto, rimane commutato per tutto il tempo del passaggio della barra e ritorna allo stato precedente appena la barra è passata.
Si vuole realizzare un dispositivo elettronico capace di visualizzare su un display a 2 cifre, con l'approssimazione di 1 cm, la lunghezza di ogni barra che passa.
Facendo le ipotesi aggiuntive che si ritiene necessarie,
a) si descriva il principio di funzionamento del sensore, della misura e della visualizzazione,
b) si scriva un programma in linguaggio C per un microcontrollore PIC16F876.
Si vuole poi aggiungere un dispositivo elettronico capace di visualizzare la temperatura del locale solo se viene premuto un pulsante.
Facendo le ipotesi aggiuntive che si ritiene necessarie, si scriva le modifiche al programma precedente per calcolare e visualizzare su un altro display a due cifre la media delle rilevazioni di tre sensori analogici che forniscono una tensione V(t) = K·T dove K=120mV/°C.
Bozza soluzione
Il sensore ad effetto Hall ha un uscita in tensione che si può collegare tramite un opportuno circuito di condizionamento ad un ingresso digitale del PIC.
Per effettuare la misura della lunghezza della barra si può utilizzare un interrupt che viene azionato ogni millisecondo, ad un millisecondo corrisponde una lunghezza di 1,2m/1000=1,2mm
#include "pic.h"
void setup(void)
{
TRISA=0b111111; // tutti ingressi
TRISB=0b00000000; // tutti gli 8 pin sono uscite per visualizzare le due cifre delle lunghezza
// clock = 4 MHz Tclock=0,25us
// impostazione OPTION
nRBPU=1; // resistenze di pull-up disabilitate
T0CS=0; // Timer0 impostato come timer
PSA=0; // Prescaler assegnato al Timer0
PS0=1; // Prescaler divide per 4
PS1=0;
PS2=0;
// impostazione ADCON1
PCFG3=0; // ingressi DADAA
PCFG2=1;
PCFG1=0;
PCFG0=0;
ADFM=0; // risultato allineato a sinistra
// con prescaler TclockTimer=Tclock*4*4=4us
// impostazione INTCON
T0IE=1; // Abilita interrupt sul overflow del Timer0
GIE=1; // Abilita interrupt
}
unsigned long lung=0;
void interrupt isr(void) // interrupt program Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
{
lung++;
TMR0=0x06;
T0IF=0; // azzeramento flag di interrupt Timer0
}
void displayB(char misura) // il display a 2 cifre è collegato a PORTB
{
PORTB=misura%10;;
PORTB|=misura/10<<4;
}
void loop()
{
while(!RA2); // attende segnale logico alto da sensore ad effetto Hall
lung=0;
while(RA2);
int misura=lung*12/100; // cm
display(misura);
}
void main(void)
{
setup();
while(1) loop();
}
Per far funzionare la lettura analogica e per la verifica della pressione del pulsante per almeno 2 secondi occorre un sottoprogramma di delay, un’aggiunta all’interrupt e una al setup
unsigned long time=0;
void setup(void)
{
TRISC=0b00000000; // tutti gli 8 pin sono uscite per visualizzare le due cifre delle temperature
...
}
void interrupt isr(void) // interrupt program
{
time++;
… // come il precedente
}
void delay(unsigned long millis)
{
time=0;
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
while(time<millis);
}
unsigned char leggianalogico(unsigned char canale)
{
unsigned char tenp;
// impostazione ADCON0
ADCON0=canale<<3;
ADON=1; // modulo ADC acceso
ADCS1=0; // frequenza di conversione massima Fosc/2
ADCS0=0;
delay(1); // attendi acquisizione
GO_DONE=1; // inizia conversione AD
while(GO_DONE) ; // attendi termine conversione AD
delay(1); // attendi tempo richiesto da produttore
return(ADRESH);
}
Per misurare la lunghezza e visualizzare la temperatura occorre un altro sottoprogramma e nel loop si deve utilizzare una macchina a stati
void displayC(char misura) // il display a 2 cifre è collegato a PORTC
{
PORTC=misura%10;;
PORTC|=misura/10<<4;h
}
int stato=0;
int misura=0;
int temperatura[3];
int media=0;
void loop()
{
if(RA2 && stato==0) // attende segnale logico alto da sensore ad effetto Hall
{
lung=0;
stato =1;
}
if (!RA2 && stato=1) // attende segnale logico basso da sensore ad effetto Hall
{
misura=lung*12/100; // misura riportata in cm
display(misura);
stato=0;
}
if (RA4)
{
media=0;
for(int i=0; i<3; i++)
{
temperatura[i}=leggianalogico(i)*5000/256/120; // lettura riportata in °C
media+=temperatura[i];
}
media/=3;
display(media);
delay(5000);
display(0);
}
}
Il programma legge i sensori di temperatura in circa 2 ms. Il tempo di conversione previsto è di 10x2Tosc=20x0,25us=5us pertanto è trascurabile.
Prova di verifica scritta 5A ENC
Si vuole monitorare, con cadenza di acquisizione pari a 10 secondi, la temperatura media durante un processo di produzione.
Sapendo che:
a) la temperatura è compresa tra 0 °C e 99 °C e ad ogni variazione di 1 °C corrisponde una variazione di 50 mV,
b) il numero di sensori di temperatura utilizzati nell’impianto è pari a 4,
c) è necessario visualizzare su un display a due cifre le temperature,
d) sono disponibili 4 pulsanti affinché l’operatore possa visualizzare una della 4 temperature misurate dai 4 sensori.
Facendo le ipotesi aggiuntive sul circuito che si vuole utilizzare, si scriva un programma in linguaggio C per un microcontrollore PIC16F876 che:
1) visualizzi sul display la temperatura media misurata dai 4 sensori;
2) faccia lampeggiare un led con una frequenza di 1Hz quando la temperatura supera i 60°C, il led smette di lampeggiare in caso contrario;
3) visualizzi la temperatura di uno dei 4 sensori sul display per 3 secondi quando un operatore preme il rispettivo pulsante tra i 4 a disposizione.
Bozza soluzione
#include "pic.h"
void setup(void)
{
TRISA=0b111111; // tutti i 6 pin sono ingressi
TRISB=0b00000000; // tutti gli 8 pin sono uscite per visualizzare la temperatura su due display
// 4 pin collegati ad un decoder BCD-7Segmenti e 4 pin collegati all’altro decoder
TRISC=0b00001111; // RC0 -3 collegati ai 4 pulsanti
// RC4 è collegato led
// clock = 4 MHz Tclock=0,25us
// impostazione OPTION
RBPU=0; // resistenze di pull-up abilitate
T0CS=0; // Timer0 impostato come timer
PSA=0; // Prescaler assegnato al Timer0
PS0=1; // Prescaler divide per 4
PS1=0;
PS2=0;
// con prescaler TclockTimer=Tclock*4*4=4us
// impostazione ADCON1
PCFG3=0; // tutti ingressi analogici 0000
PCFG2=0;
PCFG1=0;
PCFG0=0;
ADFM=0; // risultato allineato a sinistra
// impostazione INTCON
T0IE=1; // Abilita interrupt sul overflow del Timer0
GIE=1; // Abilita interrupt
}
unsigned long time=0;
unsigned long time2=0;
unsigned long time3=0;
void delay(unsigned long millis)
{
time=0;
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
while(time<millis);
}
unsigned char leggianalogico(unsigned char canale)
{
unsigned char tenp;
// impostazione ADCON0
ADON=1; // modulo ADC acceso
ADCON0 |= canale<<3;
ADCS1=0; // frequenza di conversione massima Fosc/2
ADCS0=0;
delay(1); // attendi acquisizione
GO_DONE=1; // inizia conversione AD
while(GO_DONE) ; // attendi termine conversione AD
temp=ADRESH;
delay(1); // attendi tempo richiesto da produttore
return(temp);
}
unsigne char temperature[4]={0,0,0,0};
int leggitemperature=0;
int mediatemperature=0;
void interrupt isr(void) // interrupt program
{
time++;
time2++;
time3++;
if (time2==10000) // lettura temperature ogni 10 secondi
{
leggitemperature=1;
time2=0;
}
for(int i=0; i<4; i++)
if ((temperature[i]>60*256/99) && (time3==500))
{
RC4=!RC4;
time3=0;
}
else
RC4=0;
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
T0IF=0; // azzeramento flag di interrupt Timer0
}
void display(char temperatura) // il display a 2 cifre è collegato a PORTB
{
PORTB=temperatura%10;
PORTB|=(temperatura/10)<<4;
}
void loop()
{
if (leggitemperature)
{
for(int i=0; i<4; i++) temperature[i]=leggianalogico(i)*99/256;
leggitemperature=0;
mediatemperature=(temperature[3]+temperature[2]+temperature[1]+temperature[0])/4;
}
display( mediatemperature);
if(RC0)
{
display( leggianalogico(0)*99/256);
delay(3000);
}
if(RC1)
{
display( leggianalogico(1)*99/256);
delay(3000);
}
if(RC2)
{
display( leggianalogico(2)*99/256);
delay(3000);
}
if(RC3)
{
display( leggianalogico(3)*99/256);
delay(3000);
}
}
void main(void)
{
setup();
while(1) loop();
}
Macchina per fare il pane
Si deve gestire un macchina per fare il pane con un dispositivo che utilizza un microcontrollore PIC. Si usano due sensori di temperatura per misurare la media della temperatura del forno.
Bozza diagramma di flusso
Bozza programma
Macchina per fare pane
Programma in linguaggio C
Microcontrollore PIC16F876
#include "pic.h"
void setup(void)
{
char BrownReset=BOR; // BOR è nel registro PCON nel banco 1
if(BrownReset) RC6=1; // led batteria scarica collegato a RC6
// impostazione PIN
TRISA=0b111111; // tutti i 6 pin sono ingressi
TRISB=0b00000000; // tutti gli 8 pin sono uscite
TRISC=0b00111001; // RC0 è collegato al pulsante PROG
// RC1 è collegato al motore (PWM)
// RC2 è collegato al forno (PWM)
// RC3 è collegato al pulsante START
// RC4 è collegato al microinterruttore di sicurezza
// RC5 è collegato allo STOP
// RC6 è collegato al buzzer
// clock = 4 MHz Tclock=0,25us
// impostazione OPTION
RBPU=0; // resistenze di pull-up abilitate
T0CS=0; // Timer0 impostato come timer
PSA=0; // Prescaler assegnato al Timer0
PS0=1; // Prescaler divide per 4
PS1=0;
PS2=0;
// con prescaler TclockTimer=Tclock*4*4=4us
// impostazione ADCON1
PCFG3=0; // tutti ingressi analogici 0000
PCFG2=0;
PCFG1=0;
PCFG0=0;
ADFM=0; // risultato allineato a sinistra
// impostazione ADCON0
ADON=1; // modulo ADC acceso
ADCS1=0; // frequenza di conversione massima Fosc/2
ADCS0=0;
// impostazione CCP1CON
CCP1Y=0; // LSB Duty cycle 00
CCP1X=0;
CCP1M0=0; // modalità PWM 1100
CCP1M1=0;
CCP1M2=1;
CCP1M3=1;
PR2=0xFF; // PR2 è utilizzato per calcolare il periodo del PWM
// impostazione CCP2CON
CCP2Y=0; // LSB Duty cycle 00
CCP2X=0;
CCP2M0=0; // modalità PWM 1100
CCP2M1=0;
CCP2M2=1;
CCP2M3=1;
// impostazione T2CON
TMR22ON=1;
T2CKPS1=0; // prescaler 1:1
T2CKPS0=0;
// impostazione CCPR1L
CCPR1L=0x00; // Duty cycle CCP1
CCPR2L=0x00; // Duty cycle CCP2
// Periodo del PWM = (PR2+1) * PrescalerTimer2 * 4 * PeriodoClock
// Periodo del PWM = 256 * 1 * 4 * 0,25 us = 256 us
// Frequenza del PWM = 1/256 us = 3,9 KHz
// Duty cycle del PWM = (CCPR1L:CCP1CON<5:4>) * PrescalerTimer2 * PeriodoClock
// Duty cycle del PWM = 0b0000000000 * 1 * PeriodoClock = 0
// impostazione INTCON
T0IE=1; // Abilita interrupt sul overflow del Timer0
RBIE=1; // Abilita interrupt sul cambio di stato di RB4 RB5 RB6 RB7
GIE=1; // Abilita interrupt
}
void interrupt isr(void) // interrupt program
{
if(T0IF) // ogni 1 ms
{
time++;
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
T0IF=0; // azzeramento flag di interrupt Timer0
}
}
unsigned long time=0;
void delay(unsigned long millis)
{
time=0;
TMR0=0x06; // Timer0 da 6 a 256 => 250 volte 4 us = 1 ms
while(time<(millis));
}
void buzzer(void)
{
for (int i=0;i<4;i++)
{
RA0=!RA0; // buzzer collegato a RA0
delay(1000);
}
}
void motore(unsigned char velocita)
{
CCPR1L=velocita; // Duty cycle
}
void forno(void)
{
unsigned char temp[2];
for (char i=0; i<2; i++)
{
delay(1); // attendi acquisizione
GO_DONE=1; // inizia conversione AD
while(GO_DONE) ; // attendi termine conversione AD
temp[i]=ADRESH;
}
unsigned char media=(temp[1]+temp[0])/2;
CCPR2L=1000-(media*5); // Duty cycle CCP2
}
void display(char programma) // il display è collegato a PORTB
{
// display a 7 segmenti
// -0-
// 5| |1
// -6-
// 4| |2
// -3-
switch(programma)
{
case 0:
PORTB=0b00111111;
break;
case 1:
PORTB=0b00000110;
break;
case 2:
PORTB=0b01011011;
break;
case 3:
PORTB=0b01001111;
break;
case 4:
PORTB=0b01100110;
break;
case 5:
PORTB=0b01101101;
break;
case 6:
PORTB=0b01111101;
break;
case 7:
PORTB=0b00000111;
break;
case 8:
PORTB=0b01111111;
break;
case 9:
PORTB=0b01101111;
break;
}
void loop()
{
int programma=0; // variabile che indica il programma in esecuzione
int stop=0; // variabile che indica se è stato premuto STOP
while(1)
{
display(programma);
if(RC0) // RC0 è collegato al pulsante PROG
{
programma++;
while(RC0); // esce quando si rilascia il pulsante PROG
}
if(!RC3) break; // RC3 è collegato al pulsante START
while(RC3); // esce quando si rilascia il pulsante START
if(!RC4) break; // RC4 è collegato al microinterruttore di sicurezza
if(programma==0)
{
/************* impasto *************/
int n=1800;
motore(10); // attiva al 10% il motore
while(n)
{
delay(500);
if(RC5) {stop=1; break;} // RC5 è collegato allo STOP
n--;
// si decide di non fermare il programma se si apre
// lo sportello per aggiungere ingredienti
}
motore(0);
if(stop) {stop=0; break;}
/************* lievitazione *************/
n=3600;
while(n)
{
delay(500);
if(RC5) {stop=1; break;}
n--;
}
if(stop) {stop=0; break;}
/************* cottura *************/
n=1800;
while(n)
{
delay(500);
forno(200);
if(RC5) {stop=1; break;}
n--;
}
forno(0);
if(stop) {stop=0; break;}
/************* avviso acustico *************/
n=4;
while(n)
{
RC6=!RC6; // RC6 è collegato al buzzer
delay(1000);
n--;
}
}
// altri programmi
if(programma==9)
{
// inserire il programma
}
}
}
void main(void)
{
setup();
while(1) loop();
}
Prova di verifica 5B EBC
Esercizio 1
Si deve gestire l'irrigazione di un orto con un dispositivo che utilizza un microcontrollore PIC. L'acqua del pozzo non è sufficiente ad innaffiare tutto l'orto contemporaneamente. L'orto è diviso in 4 zone. L'irrigazione di ogni zona è controllata da una elettrovalvola che fa passare l'acqua se il PIC fornisce il livello logico alto alla sezione del driver collegata alla elettrovalvola. Per far arrivare l'acqua occorre accendere una pompa.
L'irrigazione avviene solo se viene premuto un pulsante e deve durare 2,5 minuti esatti per ogni zona.
Scrivere il programma in linguaggio C.
Suggerimenti: usare PORTA per controllare le elettrovalvole e la pompa, indicare con quale livello logico le elettrovalvole si aprono e la pompa si accende, usare PORTB per il pulsante
Esercizio 2
Scrivere solo le modifiche al programma precedente per aggiungere un diodo led che inizia a lampeggiare con una frequenza di 4 Hz quando inizia l’irrigazione, il led lampeggia senza interruzioni, solo quando si spegne la pompa il led cambia frequenza e si mette a lampeggiare alla frequenza 1 Hz.
Esercizio 3
Scrivere solo le modifiche al programma precedente per aggiungere un sensore analogico di umidità del terreno. Il sensore fornisce una tensione 0-5V corrispondente a 50-100% di umidità. Se il sensore rileva un umidità superiore al 80% l’irrigazione deve essere interdetta.
Bozza soluzione:
unsigned char LeggiADC( unsigned char Canale)
{
ADCON0 = 0b ;
ADCON0 |= Canale << 3;
GO_nDONE = ;
while(GO_nDONE);
Risultato = ADRESH;
return(Risultato);
}
Soluzione
PIC16F8777 - PIC16F876
PROVA DI VERIFICA sul PIC16F876
Fila A
1) Il seguente programma in linguaggio C per il microcontrollore PIC16F876 dovrebbe servire per tarare tre sensori di prossimità di un auto. I tre sensori sono collegati ai pin RA0, RA1, RA2. Il programma verifica che il pulsante di taratura, collegato a RA4, sia premuto per almeno tre secondi. Poi legge i valori di partenza attraverso i tre ingressi analogici, memorizza le variabili lette, accende un led rosso (collegato al pin RC1) per 1 secondo con il 50% di luminosità, memorizza il numero del sensore con la risposta più alta nella variabile SensoreMax. Poi attende di nuovo che il pulsante venga premuto e legge nuovamente i tre ingressi (il valore di contatto), accende di nuovo il led sul pin RC1 partendo dal 10% di luminosità e arrivando al 100% di luminosità in 3 secondi, memorizza il numero del sensore con la risposta più bassa nella variabile SensoreMin.
A questo punto il PIC assegna il valore a due soglie che servono a distinguere il contatto: Nero e Bianco. Il valore di Nero sarà i 2/3 della variazione, il valore di Bianco sarà 1/3. Le 2 variabili Nero e Bianco sono 2 variabili generali visibili da qualsiasi procedura. Alla fine si attendono 3 secondi e poi il programma torna all'inizio.
Correggere il programma e riscriverlo.
#include <pic.h>
unsigned int Nero, Bianco
void Delay(unsigned long j)
{
unsigned long i;
i=0;
while( i<=j )
i++;
}
void InizializzazionePin()
{
RP0=1;
ADCON1=0b10000110
TRISA=0xFF;
TRISB=0xFF;
TRISC=0x00;
RP0=0;
PORTC=0x00;
}
void InizializzazionePwm()
{
RP0=1;
PR2=0xFF;
RP0=0;
CCP1CON=0x0C;
T2CON=0x04;
CCPR1L=0x00;
}
void InizializzazioneAdc()
{
RP0=1;
ADCON1=0b 1000 0000;
RP0=0;
}
unsigned int ReadAdc(unsigned char channel)
{
unsigned char a;
int j;
channel<<=3;
ADCON0=0b10000001 | channel;
Delay(50);
GO_DONE=1;
while(GO_DONE);
RP0=1;
a=ADRESL;
RP0=0;
j=(ADRESH*256)+a;
Delay(50);
return(j);
}
void WritePwm(unsigned char pulse)
{
CCPR1L=pulse;
}
void main(void)
{
unsigned int valueAdc[2];
unsigned int SensoreMax, SensoreMin;
unsigned char ch, pulse;
while(1)
{ // 4MHz 34µs x 88000 = 2,99 s
unsigned long time3s = 88000;
while(time3s)
{
time3s--;
if(RA4) time3s=88000;
}
while(!RB0)
for(ch=0;ch<=2;ch++) valueAdc[ch]=ReadAdc(ch);
if (valueAdc[0]>valueAdc[1])
{
if (valueAdc[0]>valueAdc[2]) SensoreMax=valueAdc[0];
else if (valueAdc[2]>valueAdc[0]) SensoreMax=valueAdc[2];
}
else
{
if (valueAdc[1]>valueAdc[2]) SensoreMax=valueAdc[1];
else if (valueAdc[2]>valueAdc[1]) SensoreMax=valueAdc[2];
}
WritePwm(102); // 256*40%
delay(58000); // 34µs x 58000 = 1,97 s
WritePwm(0);
while(!RA4);
for(ch=0;ch<=2;ch++) valueAdc[ch]=ReadAdc(ch);
WritePwm(25); // 256*10%
for(ch=0;ch<=255;ch++)
{
WritePwm(); // 256*10%
delay(300); // 34µs x 300 = 3s/256
}
if (RC3) writePwm(valueAdc[0]);
if (RC4) writePwm(valueAdc[1]);
if (RC5) writePwm(valueAdc[2]);
delay(310000);
WritePwm(0);
if (valueAdc[0]<valueAdc[1])
{
if (valueAdc[0]<valueAdc[2]) SensoreMin=valueAdc[0];
else if (valueAdc[2]<valueAdc[0] SensoreMin=valueAdc[2];
}
else
{
if (valueAdc[1]<valueAdc[2]) SensoreMin=valueAdc[1];
else if (valueAdc[2]<valueAdc[1]) SensoreMin=valueAdc[2];
}
Nero=2/3*(SensoreMax-SensoreMin);
Bianco=1/3*(SensoreMax-SensoreMin);
delay(88000);
}
}
2) Descrivere il calcolo del tempo impiegato dal PIC16F876 per eseguire la seguente parte di programma
char j=0; while( j<=15) j++; /// clock = 4 MHz
Se f = 4 MHz allora un ciclo d'istruzione impiega 0,25 µs x 4 ovvero 1 µs
Le istruzioni assembly corrispondenti sono un DEC (1 ciclo) e un GOTO (2 cicli)
Il tempo totale risulta T = (1 µs + 2 µs) x 15 = 45 µs
3) Come si può attendere la fine della conversione dell'ADC mentre si sentono degli ingressi e si attivano delle uscite? Descrivere i principali elementi di programmazione di questo metodo.
E' possibile attendere la fine della conversione utilizzando l'interrupt, in questo modo è possibile fare altro mentre la conversione procede
Gli elementi di programmazione sono i seguenti:
inizializzare l'interrupt di fine conversione dell'ADC
scrivere la parte del programma di interrupt che, verificato il flag relativo alla fine conversione dell'ADC, carica il risultato della conversione in una variabile generale
5) Come si può rilevare che le batterie di alimentazione del PI16f876 sono scariche? Descrivere i principali elementi di programmazione per attivare un led di segnalazioni di batteria scarica.
Fila B
1) Il seguente programma in linguaggio C per il microcontrollore PIC16F876 dovrebbe servire per tarare tre sensori di prossimità di un carello elevatore.
I tre sensori sono collegati ai pin RA0, RA1, RA3. Il programma verifica che il pulsante di taratura, collegato a RA2, sia premuto per almeno tre secondi. Poi legge i valori di partenza attraverso i tre ingressi analogici, memorizza le variabili lette, accende un led rosso (collegato al pin RC1) per 2 secondi con il 40% di luminosità, memorizza il numero del sensore con la risposta più alta nella variabile SensoreMax. Poi attende di nuovo che il pulsante venga premuto e legge nuovamente i tre ingressi (il valore di contatto), accende di nuovo il led sul pin RC1 partendo dal 10% di luminosità e arrivando al 100% di luminosità in 3 secondi, memorizza il numero del sensore con la risposta più bassa nella variabile SensoreMin.
A questo punto il PIC assegna il valore a due soglie che servono a distinguere il contatto: Blu e Rosso. Il valore di Blu sarà i 3/4 della variazione, il valore di Rosso sarà 1/4. Le 2 variabili Blu e Rosso sono 2 variabili generali visibili da qualsiasi procedura. Alla fine si attendono 3 secondi e poi il programma torna all'inizio.
Correggere il programma e riscriverlo.
#include <pic.h>
unsigned int Blu, Rosso
void Delay(unsigned long j)
{
unsigned long i;
i=0;
while( i<=j)
i++;
}
void InizializzazionePin()
{
RP0=1;
ADCON1=0b10000110
TRISA=0xFF;
TRISB=0xFF;
TRISC=0x00;
RP0=0;
PORTC=0x00;
}
void InizializzazionePwm()
{
RP0=1;
PR2=0xFF;
RP0=0;
CCP1CON=0x0C;
T2CON=0x04;
CCPR1L=0x00;
}
void InizializzazioneAdc()
{
RP0=1;
ADCON1=0b 1000 0100;
RP0=0;
}
unsigned int ReadAdc(unsigned char channel)
{
unsigned char a;
int j;
channel<<=3;
ADCON0=0b10000001 | channel;
Delay(50);
GO_DONE=1;
while(GO_DONE);
RP0=1;
a=ADRESL;
RP0=0;
j=(ADRESH*256)+a;
Delay(50);
return(j);
}
void WritePwm(unsigned char pulse)
{
CCPR1L=pulse;
}
void main(void)
{
unsigned int valueAdc[2];
unsigned int SensoreMax, SensoreMin;
unsigned char ch;
while(1)
{ // 4MHz 34µs x 88000 = 2,99 s unsigned long time3s = 88000;
while(time3s)
{
time3s--;
if(RA2) time3s=88000;
}
while(!RB0)
for(ch=0;ch<=2;ch++) valueAdc[ch]=ReadAdc(ch);
WritePwm(102); // 256*40%
if (valueAdc[0]>valueAdc[1])
{
if (valueAdc[0]>valueAdc[2]) SensoreMax=valueAdc[0];
else if (valueAdc[2]>valueAdc[0]) SensoreMax=valueAdc[2];
}
else
{
if (valueAdc[1]>valueAdc[2]) SensoreMax=valueAdc[1];
else if (valueAdc[2]>valueAdc[1]) SensoreMax=valueAdc[2];
}
while(!RA2);
delay(58000); // 34µs x 58000 = 1,97 s
writePwm(valueAdc[0]);
WritePwm(102); // 256*40%
delay(58000); // 34µs x 58000 = 1,97 s
WritePwm(0);
while(!RA4);
for(ch=0;ch<=2;ch++) valueAdc[ch]=ReadAdc(ch);
WritePwm(25); // 256*10%
for(ch=0;ch<=255;ch++)
{
WritePwm(); // 256*10%
delay(300); // 34µs x 300 = 3s/256
}
if (RC3) writePwm(valueAdc[0]);
if (RC4) writePwm(valueAdc[1]);
if (RC5) writePwm(valueAdc[2]);
delay(310000);
WritePwm(0);
if (valueAdc[0]<valueAdc[1])
{
if (valueAdc[0]<valueAdc[2]) SensoreMin=valueAdc[0];
else if (valueAdc[2]<valueAdc[0]) SensoreMin=valueAdc[2];
}
else
{
if (valueAdc[1]<valueAdc[2]) SensoreMin=valueAdc[1];
else if (valueAdc[2]<valueAdc[1]) SensoreMin=valueAdc[2];
}
Blu=3/4*(SensoreMax-SensoreMin);
Rosso=1/4*(SensoreMax-SensoreMin);
delay(88000);
}
}
2) Come si può rilevare che le batterie di alimentazione del PI16f876 sono scariche? Descrivere i principali elementi di programmazione per attivare un led di segnalazioni di batteria scarica.
3) Come si può attendere la fine della conversione dell'ADC mentre si sentono degli ingressi e si attivano delle uscite? Descrivere i principali elementi di programmazione di questo metodo.
4) Descrivere il calcolo del tempo impiegato dal PIC16F876 per eseguire la seguente parte di programma
char j=0; while( j<=25) j++; // clock = 1 MHz
Macchina per fare il pane
CONTROLLO CALDAIA