PWM software

Una modalità per la realizzazione di un segnale PWM software, generato su di un pin di un microcontrollore PIC della famiglia mid-range; metodo utile sia quando si usa un PIC senza PWM sia quando la risorsa PWM hardware sia di per sé disponibile. Il tutto realizzato in linguaggio C, con codice portabile su due compilatori: MikroC e BoostC.

Chi conosce i microcontrollori PIC di Microchip, sa perfettamente che questi dispositivi, dispongono di canali PWM hardware. Va però detto che:

» non tutti i  PIC sono dotati di PWM hardware;

» i  PIC della famiglia mid-range hanno al più uno o due canali PWM;

» per avere più canali PWM è necessario impiegare ad esempio i dsPIC destinati al motor control, che dispongono di 6 pin destinati al PWM.

Nei casi in cui il PWM non sia disponibile oppure si vogliano generare segnali PWM su diversi pin, è necessario ricorrere ad un PWM software. Questo articolo mostra quindi come realizzare un PWM software sfruttando alcune tecniche di programmazione e le risorse del microcontrollore.

Schema  elettrico

In figura 1 è mostrato lo schema con cui è possibile  realizzare il PWM software.

Figura 1: schema elettrico per la realizzazione del PWM software.

Figura 1: schema elettrico per la realizzazione del PWM software.

Alla porta PORTB del PIC16F819 è connesso un LED, con interposta la resistenze limitatrice di corrente. La scelta del PIC non è vincolante ma il PIC16F819 offre spunti interessanti; innanzitutto è dotato di oscillatore interno, per cui non è necessario il quarzo esterno; inoltre è un microcontrollore FLASH programmabile incircuit con ICD2. Per la realizzazione del PWM software sono necessari  i componenti indicati in tabella 1.

Tabella 1: i componenti necessari per la realizzazione del PWM software.

Tabella 1: i componenti necessari per la realizzazione del PWM software.

Il  progetto è realizzabile anche con altri PIC (si veda il paragrafo più avanti) come il glorioso  (ma obsoleto) PIC16F84A o il  più recente PIC16F628A ma anche il PIC16F876A. La criticità del circuito è tale per cui il montaggio può avvenire senza problemi anche breadboard oppure su una basetta mille fori. Trattandosi di un esperimento, non si è riportato lo stadio di alimentazione: va da sé che il PIC va alimentato con tensione 5V stabilizzata che può essere fornita da un alimentatore da laboratorio oppure, in alteriativa, si può optare per uno stadio di alimentazione classico con LM7805.

Il firmware del PWM

Per chi non sapesse cos’è un segnale modulato in larghezza di impulso, rimando all’approfondimento presente in queste pagine. Il primo codice da analizzare, denominato PWM_UN_PIN, è quello che genera il PWM su un solo pin del PIC; è stato preso in considerazione  il pin RB0 ma è facile sceglierne un altro al suo posto. La tecnica adottata per la generazione del PWM si affida al TIMER 0 del PIC ed al suo overflow che va a scatenare l’interrupt. La funzione ConfiguraPIC() si preoccupa quindi di abilitare l’oscillatore interno e di configurare  il TIMER 0 a 8 bit e con prescaler 1:2. Questo significa che, sfruttando l’oscillatore interno a 8MHz, si ha un overflow del timer ogni 256 µs. Una volta scatenato l’interrupt, la routine che lo gestisce si preoccupa di riattivare il TIMER 0 precaricandolo con un valore compreso tra 0 e 255; la scelta del numero è stabilita dal valore che la variabile chPwmOn aveva assunto al ciclo precedente, 1 o 0, indicando cioè che il PWM era a livello logico alto oppure basso. Ecco un esempio: si immagini che uchDuty valga 100 e che il PWM sia a livello alto, (ossia chPwmOn vale 1). Giunto l’interrupt, chPwmOn viene posta a 0 e il registro del timer viene caricato con un valore in funzione della variabile uchDuty che, in questo esempio, vale 100. Il timer quindi comincia a contare non da 0 ma da 100 e quindi giunge ad overflow in un tempo inferiore rispetto a quando lo si carichi con 0. All’overflow si scatena un altro interrupt e questa volta la routine verifica che chPwmOn vale 0 (è stato posto a 0 nell’interrupt precedente), pertanto pone chPwmOn a 1 e precarica  il registro del timer con un valore pari a 155, ottenuto dalla differenza 255-100. A questo punto è sufficiente andare a testare il valore di chPwmOn per stabilire se un pin va posto a 1 o a 0; nel main() si richiama la funzione PWM() che non fa altro che porre il pin RB0 ad assumere lo stesso valore di chPwmOn. Il  listato 1 mostra il cuore del codice scritto per la realizzazione del PWM software.

void interrupt (void)
{
        if (intcon.TMR0IF) // Interrupt su overflow
TIMER 0
    {
            intcon.TMR0IF=0;
            if (chPwmOn)
            {
                     chPwmOn=0;
                     tmr0=uchDuty;
             } else {
                     chPwmOn=1;
                     tmr0=255-uchDuty;
             }
     }
}

void main()
{

        ConfiguraPic();

// Abilitazione interrupt
       intcon=0xA0;

// Ciclo continuo

       while(1)
       {
               PWM();
       }
}

void PWM (void)
{
       if (chPwmOn)
       {
              set_bit(portb,0);
        } else {
               clear_bit(portb,0);
        }
}
Listato 1

Variazione del duty-cycle

Così come è stato proposto, il  codice PWM_UN_PIN genera un PWM a frequenza fissa e con duty-cycle fisso. Come fare quindi per la variazione del dutycycle? Proprio per il ruolo che riveste, il duty-cycle è la vera e propria variabile di comando del PWM, quello che determina l’effetto finale del PWM sui circuiti hardware. La variazione del PWM è quindi generalmente demandata ad algoritmi che, in base a considerazioni proprie, determinano il  valore che il  duty-cycle deve assumere. Per il progetto didattico che si propone in queste pagine, invece, può essere interessante variare manualmente il  dutycycle, senza dover necessariamente sottostare a qualche algoritmo. Si osservi che il PIC che è stato scelto per l’esempio dispone di ingressi analogici; configurando la porta RA0 come ingresso AN0, si può pensare di collegare a questo pin un potenziometro, la cui variazione sarà nient’altro che il valore di duty-cycle. In figura 2 è illustrato lo schema elettrico con l’aggiunta di un potenziometro (o trimmer) da 10kΩ, connesso al pin AN0.

Figura 2: aggiunta del potenziometro per la variazione del duty-cycle.

Figura 2: aggiunta del potenziometro per la variazione del duty-cycle.

Il codice che gestisce l’ingresso analogico è PWM_UN_PIN_POTI;  la funzione ConfiguraPIC() (vedi listato 2) si preoccupa quindi di configurare anche il modulo di conversione analogico/digitale mentre la gestione del convertitore avviene invece in interrupt. Il modulo ADC converte la tensione letta su AN0 in un valore a 10 bit che viene poi ridotto a 8 bit per agevolare l’associazione tra il valore convertito e il duty-cycle.

void interrupt (void)
{
        if (intcon.TMR0IF) // Interrupt su overflow TIMER 0
        {
                intcon.TMR0IF=0;
                if (chPwmOn)
                {
                      chPwmOn=0;
                      tmr0=uchDuty;
                 } else {
                         chPwmOn=1;
                         tmr0=255-uchDuty;
                 }
       }
       if (pir1.ADIF)
       {
              uiPotenziometro=(adresh);
              uiPotenziometro=(uiPotenziometro<<8)+adresl;
              uiPotenziometro=(uiPotenziometro>>2);
              pie1.ADIE=1;
              pir1.ADIF=0;
              adcon0.GO=1;
              toggle_bit(portb,1);
              uchDuty = (unsigned char)uiPotenziometro;
    }
}
void main()
{
       ConfiguraPic();

// Abilitazione interrupt
         intcon=0xE0;

// Ciclo continuo

      while(1)
      {
             PWM();
      }
}

// Configurazione del PIC16F819
void ConfiguraPic(void)
{

// Configurazione dell’oscillatore interno a 8MHz
       osccon=0x70;

// Configurazione dell’ingresso analogico
       adcon1=0x8E;
       adcon0=0x80;
       adcon0.ADON=1;

// Configurazione interrupt
       pir1.ADIF=0;
       pie1.ADIE=1;
       adcon0.GO=1;

// Configurazione degli I/O
        trisb=0;
        trisa=0xFF;

// Configurazione TIMER 0: Prescaler 1:2
        option_reg=0x80;

// Configurazione interrupt

// Variabili globali PWM
       uchDuty=180;
       chPwmOn=1;
}
Listato 2

Le macro

Il codice C utilizza due macro: set_bit() e clear_bit(). Queste macro, già disponibili per BoostC ma non per MikroC, permettono l’accesso ad un bit di un byte portandolo rispettivemente al valore logico 1 e 0. Tali macro sono state definite nel file di include di entrambi i progetti.

Come  fare  con altri  PIC

L’adattamento del progetto ad altri PIC, come il PIC16F84A, il PIC16F876A o il PIC16F628A, non è cosa impossibile ma è necessario prestare attenzione ad alcuni punti chiave da non sottovalutare, pena il malfunzionamento del circuito.

Adattamento dello  schema  elettrico

Alcuni PIC, come  il  PIC16F84A o  il PIC16F876A necessitano di un oscillatore esterno; il quarzo va connesso come da datasheet, ai pin OSC1 e OSC2 (un esempio è mostrato in figura 3).

Figura 3: esempio di collegamento dell’escillatore esterno.

Figura 3: esempio di collegamento dell’escillatore esterno.

Va altresì sottolineato come alcuni PIC non dispongano di ingresso analogico; per questi tutti gli aspetti legati alla conversione A/D vanno esclusi, a partire dal potenziometro connesso a AN0. In mancanza del convertitore A/D può essere utile installare un paio di pulsanti deputati all’incremento e al decremento della variabile duty-cycle.

Adattamento del firmware

Ogni PIC dispone di un set di configuration bit; questi permettono di fornire una configurazione hardware per diversi aspetti: dal tipo di oscillatore, se è interno oppure esterno, se si vuole utilizzare  il Watch Dog Timer eccetera. Benché esista una certa familiarità tra PIC, in funzione del dispositivo scelto è necessario stabilire il corretto set di configuration bit. Anche in questo caso torna utile sfogliare il datasheet. Nel progetto originale per PIC16F819 si è utilizzato l’oscillatore interno a 8MHz; dovendo adattare il progetto ad altri PIC, oltre all’apposizione di un (eventuale) quarzo esterno, se si opta per un oscillatore a frequenza diversa, questo va indicato sia in MikroC sia in BoostC. Si tenga conto del fatto che la frequenza fosc è tale da determinare la frequenza dell’interrupt di TIMER0. Così, se per esempio si adottasse un quarzo esteno da 4 MHz, la risposta all’interrupt avverrebbe ogni 512µs e non ogni 256µs.

 

 

 

Scrivi un commento

EOS-Academy