Robotica 3

Programma di Robotica  della classe quinta (aggiornato al 2023)

Numero di ore/settimana: 3 ore

Numero di ore/a.s.: 99 ore

Libro di testo


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: 

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

PIC16F876-877 - Datasheet

PIC16F876-877 - Programmi

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:

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