Progetti - PIC

 

1KHz sinusoidale con PIC


Generare una sinusoide con il microcontroller.

Una forma d'onda qualsiasi può essere generata dal microcontroller utilizzando un convertitore DA. 

Purtroppo si tratta di una periferica presente solo su alcuni chip e, dove non c'è, l'alternativa è quella di utilizzare un chip esterno, che, a sua volta, richiede una comunicazione SPI o I2C, che va implementata. La soluzione più comune diventa quella di utilizzare una rete R-2R esterna. 

Qui, però, abbiamo la necessità di un numero di I/O digitali pari alla definizione della rete: per 8 bit avremo 8 pin impegnati. 

Quando ci troviamo a disporre di un numero minore di pin, come nel caso dei chip in contenitore a 8 pin, questo non è possibile ed occorre utilizzare una diversa tecnica.

Sappiamo che, in una modulazione PWM dove si mantiene fissa la frequenza e si varia il duty cycle, si trasferisce all' uscita una percentuale variabile di energia. 
Possiamo utilizzare il segnale PWM in diversi modi, di cui i più comuni sono:

  • applicarlo direttamente al pilotaggio del carico, anche attraverso un buffer, per variare, ad esempio, luminosità di LED o velocità di motori
  • oppure, inserendo un filtro passa basso che isoli solamente la componente di corrente continua, ottenendo una tensione variabile. In questo caso,il filtro passa basso eliminerà ogni componente alternata, compresa la frequenza fondamentale.

Ma possiamo anche applicare un filtro che elimini le armoniche superiori alla fondamentale, mantenendo questa ed estraendola come componente alternata a frequenza costante, il cui livello varierà seguendo il variare del duty cycle. 

Il modulo CCP/PWM è presente su molti PIC, anche con la possibilità di più canali; lo troviamo anche in piccoli chip a 8 pin, come il 12F683, microcontroller Midrange abbastanza diffuso e che impiegheremo in questa applicazione. Ovviamente è possibile replicare la cosa per qualsiasi altro PIC dotato di CCP/PWM.

Ci si può chiedere quale sia il vantaggio di utilizzare il microcontroller per generare una forma d'onda. Questo è essenzialmente legato alla elevata precisione in frequenza che dipende dall' oscillatore a cristallo del clock. La sinusoide in uscita avrà la stessa precisione dell'oscillatore del clock, precisione difficilmente ottenibile con circuiti analogici, che richiedono componenti RC di valori non sempre facilmente reperibili.

Qui, vogliamo ottenere 1kHz, frequenza base per un gran numero di misure.

L'idea è tratta da un lavoro di Roman Black.


Il circuito

Possiamo realizzare un circuito basato su questi principi.

Viene utilizzato un oscillatore a cristallo da 20MHz oppure, in alternativa, un oscillatore esterno. Nello schema sono indicati entrambi, ma, ovviamente, uno solo sarà cablato. 
L' oscillatore esterno, a fronte di un costo maggiore, offre una maggiore precisione e stabilità. 
L' oscillatore classico a cristallo prevede un compensatore per aggiustare la frequenza. I valori dei condensatori dipendono dal quarzo usato e sono compresi tra 18 e 33pF; nei prototipi sono stati usati, con quarzi di diversi produttori, dei condensatori NP0 da 22pF.

Con il clock a 20MHz, abbiamo un ciclo istruzione di 200ns. Questo ci permette di ottenere dal modulo CCP un preciso PWM con una cadenza di 20us.
Il programma prevede una uscita ausiliaria da 50kHz che può essere utilizzata, come vedremo, per il filtro successivo. Dalla stessa uscita è anche la possibilità di ottenere in alternativa 1MHz (duty cycle 40%), sia per facilitare la calibrazione che per uso generale. Questa funzione è ottenuta chiudendo il pulsante S1.

Un LED bicolore a due pin è utilizzato per segnalare il modo di funzionamento. Per minimizzare il consumo si sfrutta l'interruttore S1: quando questo è chiuso, il microcontroller porta a livello alto il pin GP0, accendendo il LED rosso (R) attraverso R2. Quando S1 è aperto, GP0 sarà portato a 0, accendendo il LED verde (G) attraverso R1.

I componenti sono:

R1 R2 R3 C1 C2 C3 C4 C5 Q1/QG1 S1 LED
820 820 47k 18-33p 3-30p 100n 100n 100u 20MHz interr. bicol.


Il segnale a 1kHz è inviato ad un filtro passa basso che sopprime le armoniche e alla cui uscita si troverà la sinusoide con una distorsione minima.
Una prima soluzione è data da una serie di filtri RC:

Si tratta di tre passa basso di cui il primo comprende anche una induttanza. Il vantaggio è dato dal consumo nullo sull' alimentazione e da un relativo basso costo; nonostante un calcolo preciso richieda valori poco comuni, ugualmente si possono ottenere risultati positivi con componenti facilmente reperibili.  Per contro, introduce una certa attenuazione. C4 è un non polarizzato che elimina la componente continua.
I valori sono:

R1 R2 R3 L1 C1 C2 C3 C4
330 330 820 2.2mH 0.33uF 68nF 68nF 10uF np

Una seconda soluzione, un poco più complessa, è quella di utilizzare un filtro digitale. Possiamo scegliere uno dei vari integrati ideati per questo scopo, basati su capacità commutate e prodotti da vari costruttori. Si prestano bene  MAX295/296 di Maxim, che hanno un rapporto 50:1 tra frequenza di clock e frequenza filtrata.
Qui usiamo un MAX296, Bessel di ottavo ordine

L' alimentazione è filtrata con un LC (L1/C5/C6) che separano molto bene il filtro dai disturbi digitali indotti sull' alimentazione.
Clock e PWM sono inviati direttamente al MAX; questo è previsto per una alimentazione duale, che, volendo implementare, garantisce i risultati migliori. Però, qui il circuito è previsto per una alimentazione singola e si rende necessaria una massa virtuale costituita da R1/R2/C8.
R6 è una resistenza di protezione che separa il connettore X2 dal circuito.
I MAX29x integrano anche un operazionale (pin 3-4) che viene utilizzato come ulteriore passa basso (topologia Sallen-Key multiple feedback):

I componenti:

R1 R2 R3 R4 R5 R6 C1 C2 C3 C4 C5 C6 C8 L1
10k 10k 16.5k 14.7k 14.7k 50 22nF 10uF
 np
100uF 4nF7 10nF 100uF 4uF7
tant.
1mH

Le resistenze del filtro sono approssimate ai valori SMD più facilmente reperibili. 

Lo sviluppo è possibile con gran facilità usando la scheda LPCuB:

I filtri sono realizzati esternamente su breadboard per una rapida variazione dei componenti.

La versione finale potrà poi essere realizzata su un circuito ad hoc.


Il programma

Nel programma, il PWM viene alimentato con il clock a 20MHz: per un ciclo, stabilito da PR2, di 100 impulsi di clock, otterremo una frequenza di 50kHz.

; initialize CCP module for PWM
; Microchip suggestion on data sheet

       clrf    CCP1CON   ; CCP Module is off
       clrf    TMR2      ; clear Timer2
       banksel PR2
       movlw   (.100-1)
       movwf   PR2
       banksel GPIO
       clrf    INTCON    ; clear T0IF
       banksel TRISIO
       bcf     Sout      ; make pin output
       banksel GPIO 
       clrf    PIR1      ; clear peripheral interrupts Flags
; CCP1 ON, and set to PWM mode

       movlw   b'00001101'
       movwf   CCP1CON
       movlw   .52       ; first value from the table
       movwf   CCPR1L
; and start TRM2
       movlw   b'00000100'
       movwf   T2CON

Ad ogni impulso in uscita del segnale PWM viene associato un diverso valore di duty cycle, prelevato da una tabella di 50 passi, ottenendo così una variazione sinusoidale del duty cycle stesso tra circa il 15 e l'85% ogni kilohertz.
La direttiva dt consente di compilare rapidamente la tabella retlw.

; "harmonic compensated" sine table
sinetable addwf PCL,f

 dt 52,57,62,66,70,74,77,80,82,84,85,86,86
 dt 86,85,83,81,78,75,72,69,65,61,56,52
 dt 48,44,39,35,31,28,25,22,19,17,15,14,14
 dt 14,15,16,18,20,23,26,30,34,38,43,48

Contemporaneamente viene generato un segnale ad onda quadra da 50kHz che sarà usato per il filtro digitale. Questo segnale ha un duty cycle del 50% in quanto i filtri attivi richiedono questo per operare al meglio; la cosa è ottenuta semplicemente all'interno del loop di aggiornamento del PWM, con un impulso a livello alto pari a 50 cicli istruzione. 

; 50kHz pulse
     bsf    Faux    ; pulse start
     movlw  0x10    ; 49 cycles
     movwf  d1
dcyc decfsz d1, f
      goto  dcyc
     bcf    Faux    ; pulse end

Dato che il prossimo impulso sarà attivato alla fine di un ciclo PWM che dura 100 cicli istruzione, ecco che abbiamo il duty cycle corretto. 

Un bonus ulteriore è fornito dall'interruttore S1, la cui chiusura devia l' esecuzione su un tratto di programma che genere un'onda quadra con duty cycle al 40% e frequenza di 1MHz, che può essere usata per la taratura dell' oscillatore. L' asimmetria è dovuta alla necessità del test sullo stato dell'interruttore che commuta le modalità e al loop, dato che il ciclo istruzione è 200ns. 

; Calibrate mode: make a 1MHz fixed freq on pin Faux
; 5 instructions per cycle,
; output squarewave 1MHz with 40% on duty.

cal_mode:            
       clrf  CCP1CON   ; CCP1 module is turned OFF

; MHz mode - LED red on
       LEDRED

loop_1MHz:
       bsf   Faux      ; output high
       nop         
       bcf   Faux      ; output low
       btfsc btn       ; test switch for SINE mode
        goto done_1MHz ; switch hi - exit go to SINE mode
       bsf   Faux      ; no - output high
       nop             
       bcf   Faux      ; output low
       goto  loop_1MHz ; loop

done_1MHz:             ; out from MHz mode
       goto  sine_mode ; back to sine mode

L' interruttore è anche dotato di un tempo di debounce, per evitare che i rimbalzi generino stati di uscite casuali.
Il LED bicolore viene comandato in funzione della posizione di S1.


La realizzazione

E' stata realizzata una versione portatile alimentata a batteria.
Qui, sono utilizzati 4 accumulatori NiMH che fornisco una tensione variabile tra 4.8V (carichi) e 4.4V (scarichi), il che rientra nelle possibilità del microcontroller, anche se è probabile che la variazione della tensione di alimentazione si rifletta negativamente sulla stabilità. E' stato usato il filtro passivo, che non consuma corrente, aumentando la durata della batteria (l' intero circuito assorbe circa  7.5 mA).
L'oscillatore è a cristallo e il trimmer capacitivo consente un aggiustamento della frequenza.

Una versione "da banco" è facilmente realizzata con il solito regolatore 7805 e un wall plug esterno. Qui è stato implementato il filtro digitale e l'oscillatore del clock è un modulo esterno.

Il firmware è identico per entrambe le versioni; va solo modificato il config iniziale dove si voglia usare l'oscillatore esterno:

; config per oscillatore a quarzo 
 __config _HS_OSC & _WDTE_OFF & _PWRTE_ON & _BOREN_OFF & _IESO_OFF & _FCMEN_OFF & _MCLRE_OFF

; config per oscillatore esterno
 __config _EC_OSC & _WDTE_OFF & _PWRTE_OFF & _BOREN_OFF & _IESO_OFF & _FCMEN_OFF & _MCLRE_OFF

Il segnale in uscita dal PWM va considerato nella sua componente alternata alla frequenza fondamentale. Però, dato che la variazione del duty cycle non è continua, ma a passi, si introducono sensibili componenti a frequenze multiple, che vanno abbattute dal filtro passa basso. In particolare la seconda armonica è la più evidente, mentre gli errori di campionamento dei valori in tabella sono secondari.
Sia il filtro passivo che quello attivo rendono risultati soddisfacenti: 

Filtro passivo, prima cella RLC e uscita. Accanto, la FFT data dall' oscilloscopio.
All' inizio del filtro si nota la forte presenza di armoniche, che vengono poi ridotte all' uscita.

La sinusoide in uscita a una ampiezza a vuoto di circa 1,4V.
Ritoccando il compensatore è possibile aggiustare il valore della frequenza dell' oscillatore. Con gli strumenti a disposizione è stato rilevato come migliore valore la frequenza di 1.000.070 Hz.
Qui vediamo l' uscita a 1MHz e quella a 50kHz:


Per quanto riguarda il filtro digitale, questa è l'uscita. 

 


C vs. Asm

L'idea originale dispone di un sorgente in C nel quale si nota una consistente intrusione di Assembly.
Questo perchè non è possibile utilizzare il C in modo da assicurare un totale controllo a livello di istruzioni singole, come in questo caso, dove le temporizzazioni, ad esempio quella per generare il MHz, sono stringenti.
L' Assembly offre un pieno controllo dei tempi, così che, ad esempio, l' inserimento del clock a 50kHz è estremamente semplice, mentre il C avrebbe richiesto ancora sezioni asm.

Qui abbiamo preferito un approccio interamente Assembly, che è molto meglio calibrato sull'applicazione, la quale non richiede calcoli o strutture logiche complesse, ma solamente una gestione ottimale dei bit degli I/O. E, per quello che riguarda i registri di controllo del modulo CCP/PWM, non esiste una reale differenza tra C e Assembly nella forma e nella comprensione del sorgente.

Come bonus, l' eseguibile prodotto dal sorgente Assembly è di dimensioni sensibilmente minori e richiede meno RAM.  

Per concludere, si può costatare che, per programmi dove si ha a che fare essenzialmente con I/O e SFR ed occorre rispettare temporizzazioni critiche, l' approccio in C non sempre è quello migliore e andrebbe  riservato ad altri ambiti, sopratutto in piccoli PIC con risorse limitate e poco ottimizzati per questo linguaggio.

Il sorgente è stato compilato per il 12F683, ma è adattabile con gran facilità a qualsiasi altro PIC dotato del modulo CCP/PWM, semplicemente adeguando la programmazione per accedere agli I/O digitali (escludendo le funzioni alternative), mentre la gestione del PWM resta sostanzialmente invariata passando ad altri Midrange, Enhanced Midrange e anche PIC18F. E' pure facilmente adattabile ai 10F32x, che hanno MSSP e ingresso del clock esterno, rinunciano al clock ausiliario o al LED di indicazione (a causa del basso numero di pin).

 E, data la semplicità del sorgente, non sarà difficile passarlo anche a microcontroller di altri produttori.

Il principio potrà essere applicato per ottenere valori diversi della frequenza in uscita, modificando il clock e/o il PWM.



Il sorgente

;******************************************************************************
; Sine1kHz.asm   Precision 1kHz sinewave generator
;                from an idea of www.RomanBlack.com/
;
; PIC 12F683 - 20MHz xtal
;-----------------------------------------------------------------------------
; I/O pins;
;
#define LED  GPIO,GP0  ; Out LED Red
#define Faux GPIO,GP1  ; Out Faux
#define Sout GPIO,GP2  ; Out CCP1
#define btn  GPIO,GP3  ; In button
                       ; GP4 oscout
                       ; GP5 oscin

;--------------------------------------------------------------------
;
           LIST p=12F683 r=DEC
           #include <p12F683.inc>


; for chrystal oscillator
 __config _HS_OSC & _WDTE_OFF & _PWRTE_ON & _BOREN_OFF & _IESO_OFF & _FCMEN_OFF & _MCLRE_OFF
; for external oscillator module
; __config _EC_OSC & _WDTE_OFF & _PWRTE_OFF & _BOREN_OFF & _IESO_OFF & _FCMEN_OFF & _MCLRE_OFF

;*********************************************************************
; Data RAM area

     CBLOCK 0x20
  pwmstep  ; PWM step 0-49
  debounce ; debouncing switch input
  d1       ; temporary for delay
     ENDC

;********************************************************************
; LOCAL MACROS

; CFLSB Compare File to Literal and Skip if Below
; Compare register with literal and jump next line if
; file < literal

CFLSB MACRO file,lit
       movlw (255-lit+1) ; W = -lit
       addwf file,W      ; W = file + W = file + (-lit)
       skpnc             ; C=0 for file<lit, than skip next line
      ENDM               ; C=1 for file>=lit, than execute next line

LEDGREEN MACRO
       bcf LED
         ENDM
LEDRED MACRO
       bsf LED
         ENDM

;=====================================================================
; MAIN

RESVEC ORG 0x00

Main goto init

; "harmonic compensated" sine table
sinetable addwf PCL,f

 dt 52,57,62,66,70,74,77,80,82,84,85,86,86
 dt 86,85,83,81,78,75,72,69,65,61,56,52
 dt 48,44,39,35,31,28,25,22,19,17,15,14,14
 dt 14,15,16,18,20,23,26,30,34,38,43,48

;--------------------------------------------------------------------
init:
; preload GPIO output latch
      movlw   0
      movwf   GPIO

;comparators OFF, all pins digital
      movlw   b'00000111'
      movwf   CMCON0

; analog off and all possible as out
      banksel ANSEL
      clrf    TRISIO
      clrf    ANSEL

; TMR0 not used, but clear T0CKI
      movlw   b'10001000'
      movwf   OPTION_REG
      banksel GPIO

; setup any variables before main loop
      clrf    pwmstep
      clrf    debounce

;--------------------------------------------------------------------
; Sine mode: use PWM to make the sine out pin
sine_mode:
; LED green is on, red is off
      LEDGREEN

; initialize CCP module for PWM
; Microchip suggestion on data sheet
      clrf    CCP1CON ; CCP Module is off
      clrf    TMR2    ; clear Timer2
      banksel PR2
      movlw   (.100-1)
      movwf   PR2
      banksel GPIO
      clrf    INTCON ; clear T0IF
      banksel TRISIO
      bcf     Sout   ; make pin output
      banksel GPIO
      clrf    PIR1   ; clear peripheral interrupts Flags
; CCP1 ON, and set to PWM mode  
      movlw   b'00001101'
      movwf   CCP1CON
      movlw   .52    ; first value from the table
      movwf   CCPR1L
; and start TRM2
      movlw   b'00000100'
      movwf   T2CON

; loop: load new PWM value every TMR2 cycle
slp0  incf  pwmstep,f    ; inc to next step in sinewave
      CFLSB pwmstep, .50 ; sine has 50 steps
      clrf  pwmstep
      movf  pwmstep,w    ; read new PWM value
      call  sinetable    ; from sinetable

slp1  btfss PIR1,TMR2IF  ; wait for TMR2 cycle to restart
      goto  slp1
      movwf CCPR1L       ; load new value
      bcf   PIR1,TMR2IF  ; clear TMR2 int flag

; 50kHz pulse
      bsf    Faux
      movlw  0x10        ;49 cycles
      movwf  d1
dcyc  decfsz d1, f
       goto  dcyc
      bcf    Faux        ; pulse end

; also check for switch being low, if so go to calibrate mode
      btfsc  btn
       goto  slp2
      incf   debounce,f
; if debounce > 250 goto cal_mode.
      CFLSB  debounce, .250
       goto  cal_mode
      goto   slp0
;else debounce = 0;
slp2  clrf   debounce
      goto   slp0       ; back to loop

;--------------------------------------------------------------------
; Calibrate mode: make a 1MHz fixed freq out pin Faux
; 5 instructions per output cycle,
; output squarewave 1MHz with 40% on duty.
cal_mode:
; CCP1 module is turned OFF
     clrf   CCP1CON

; CAL mode LED red is on
     LEDRED

loop_1MHz:
     bsf   Faux        ; output high
     nop 
     bcf   Faux        ; output low
     btfsc btn         ; test switch for SINE mode
      goto done_1MHz   ; switch HI, so exit this mode and go back to SINE
     bsf   Faux        ; output high
     nop 
     bcf   Faux        ; output low
     goto  loop_1MHz   ; loop

done_1MHz:             ; out from cal_mode
     goto sine_mode    ; cal mode is done, back to sine mode

;-----------------------------------------------------------------------------

    END

 


 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 19/04/15.