Rechteckgenerator mit seriellem Eingang

mit PIC16F628

zurück zu Lernbeispiele , PIC-Prozessoren , Elektronik , Homepage


Gesucht war ein einfacher Rechteckgenerator, dessen Frequenz mit Hilfe eines synchronen seriellen Interfaces eingestellt werden kann.


Schaltung
Stromlaufplan der IR-Fernbedienung Die Schaltung besteht aus einem PIC16F628 und einer 20-MHz Taktquelle

Vier Pins des Port B dienen als serielles Interface. Alle Pins des Port A dienen als Taktausgang. In der Schaltung sind stellvertretend Pin RA0 (normaler Ausgang) und RA4 (open-Drain-Ausgang) dargestellt.

Zum Aufbau eignet sich z.B. die 18-Pin-Testplatine. oder die 18-Pin-Minitestplatine.



einen Rechteck erzeugen

Viele PICs haben PWM-Ausgänge, mit denen sie Frequenzen erzeugen können. Der Frequenzbereich dieser Ausgänge ist aber beschränkt, und genügte in diesem Beispiel nicht den Anforderungen. Aus diesem Grund wird die Rechteckschwingung hier mit einer Softwareschleife erzeugt.

Rechteckerzeugung Dafür verwende ich zwei 24-Bit breite Register (die aus jeweils 3 Speicherzellen von je 1 Byte Größe bestehen). Im Register f steht ein Zahlenwert. Dieser wird alle 3,8 Mikrosekunden auf den Zahlenwert im Register Akku aufaddiert. Dadurch steigt der Wert in Akku an, bis das Register schließlich "überläuft" und auf einen Wert nahe Null zurückfällt. Danach steigt Akku wieder. Der Wert in Akku folgt also in etwa einer Sägezahnfunktion.
Das höchstwertigste Bit von Akku (MSB) ist als Ausgang herausgeführt. Nach jedem Überlauf steht es auf "0". Etwa in der Mitte der Sägezahlkurve wechselt MSB auf  "1". Dadurch beschreibt MSB eine Rechteckfunktion mit der Frequenz des Sägezahns.
Die Frequenz des Rechtecks bestimmen der Zahlenwert in f und der feste Abstand zwischen den Additionen von 3,8us.

Da Akku 24 Bit groß ist, läuft es beim Überschreiten von 16777215 (0xFFFFFF) über. Die Frequenz des Rechtecks kann man wie folgt berechnen:

Frequenz = f  /  (16777216  *  3,8 us) = f / (63.7534 s)

Folgende Näherung hat einen Fehler von nur 0,4%, und sollte meistens ausreichend sein:

Frequenz  =  f / 64  Hz

Die erzeugte Frequenz lässt sich also einstellen, wenn man den Wert in f ändert. Um beispielsweise 100 Hz zu erzeugen, schreibt man 100 x 64 = 6400 in das Register f. Dadurch lässt sich die erzeugte Frequenz im Bereich von 0 Hz bis zu etwa 64 kHz frei einstellen.

Die gewählte Lösung ist zwar sehr einfach, hat aber auch einen kleinen Schönheitsfehler. Da Akku nur alle 3,8 Mikrosekunden verändert wird, kann sich das MSB von Akku auch nur im 3,8-us-Rythmus ändern. Um mit dieser Einschränkung z.B. 100 Hz zu erzeugen, können beispielsweise die High- und Low-Zeiten einer 100Hz-Welle nicht genau 5000 Mikrosekunden (1/200 s) lang sein, sondern entweder 4997 us (3,8 us x 1315) oder 5000,8 us (3,8 us x 1316). Der erzeugte Rechteck wechselt ständig zwischen diesen beiden Werten und garantiert einen Durchschnittswert von 5000 us. Bei vielen Anwendungen mag das nicht störend auffallen.
Lediglich wenn im Register f eine Zweierpotenz steht, tritt dieser störende Nebeneffekt nicht auf.



ein synchrones serielles Interface

Serielle Anschlüsse sind beliebt, da sie mit wenigen Anschlusspins und folglich mit wenigen Verbindungen zwischen Datenquelle und Datenempfänger auskommen. Typische Beispiele sind RS232, USB, I2C, SPI oder CAN, die an anderer Stelle beschrieben werden.

Das hier verwendete synchrone serielle Interface besteht aus einem Schieberegister, das lang genug sein muss, um die gesamte zu empfangene Information aufzunehmen. In unserem Fall ist es 16 Bit lang. Mit Hilfe einer Taktleitung (serial clock in) und einer Datenleitung (serial data in)  werden alle zu empfangenden 16 Bits nacheinander in das Schieberegister hineingeschoben. Danach wird mit einem Ladesignal auf einer Load-Leitung (serial load) der 16-Bit Wert in die Schaltung übernommen. In diesem Fall soll ja der Wert des f-Registers verändert werden, also laden wir den 16-Bit-Wert in dieses Register. In diesem speziellen Fall laden wir den 16-Bit Wert um 6 Bit verschoben in das f-Register. Das entspricht einer Multiplikation mit 64. Folglich erzeugt der Rechteckgenerator nun die Frequenz (in Herz), die als Zahlenwert in das Schieberegister geschoben wurde (siehe oben).

Die nachfolgende Grafik illustriert das. Achtung: In dieser Abbildung steht das höchtswertige Bit rechts und das niederwertigste Bit links.

Interface

Das letzte Bit des Schieberegisters liegt immer an der Datenausgangsleitung (serial data out) an. Das erlaubt es, mehrere Schieberegister (z.B. von mehreren Rechteckgeneratoren) in Reihe zu schalten und über den selben Bus zu steuern.
Sowohl "serial clock in" wie auch "serial load" sind high-aktiv, dass bedeutet das Schieberegister reagiert auf den low-high-Übergang des Signals.



Programmablauf .


Hinweise

Die Schaltung ist so voreingestellt, dass sie nach dem Einschalten 100 Hz erzeugt.

Das hier verwendete serielle Interface kann vom USB4all erzeugt werden (ab V.5). Damit kann man dann den Rechteckgenerator per USB steuern. Das mit USB4all mitgelieferte Testprogramm (usbtest.exe) ist für die Ansteuerung des Rechteckgenerators vorbereitet. Als Anschluss "serial load" ist am USB4all dann das Pin RB2 zu verwenden.

Signal am PIC16F628
Pin-Name am USB4all
Pin-Nummer am USB4all
serial  load (Pin 6)
RB2
23
serial data in (Pin 7) SDO / RC7
18
serial data out (Pin 9) SDI / RB0
21
serial clock (Pin 10) SCK / RB1
22
Vss / GND (Pin 5)
Vss
8 & 19



Programmlisting

        list p=16f628a
;**************************************************************
;*      PORTA:  0 >> Ausgang
;*              1 >> Ausgang
;*              2 >> Ausgang
;*              3 >> Ausgang
;*              4 >> Ausgang
;*      PORTB:  0 < load        - Interrupt
;*              1 < (RB1/RX/DT) Dateneingang
;*              2
;*              3 > Datenausgang
;*              4 < (RB2/TX/CK) Takteingang - Interrupt
;*              5 >
;*              6 >
;*              7 >
;**************************************************************
;
; sprut (zero) Bredendiek 04/2010
;
; Rechteckgenerator 1 .. 65535 Hz
;
; serielles Interface kann von USB4all (ab V.5) erzeugt werden
;
; serieller Eingang fuer 16-Bit-Sollfrequenz in Herz
; Frequenzeinstellfehler ca. +0.4%
;
; Jitter im Rechtecksignal: 3,8 us
;
; Prozessor 16F628 o.ä.
; Prozessor-Takt 20 MHz
;
;**************************************************************

; Includedatei für den 16F628 einbinden
        #include <P16f628a.INC>

        ERRORLEVEL      -302    ; SUPPRESS BANK SELECTION MESSAGES

; Configuration festlegen:
; Power on Timer, kein Watchdog, HS-Oscillator, kein Brown out, kein LV-programming, kein Reset
        __CONFIG        _PWRTE_ON & _WDT_OFF & _HS_OSC & _BODEN_OFF & _LVP_OFF & _MCLRE_OFF


; Akku  XXXXXXXX XXXXXXXX XXXXXXXX
; s     --XXXXXX XXXXXXXX XX------
;**************************************************************
; Variablen festlegen

; Speicherbereich für Variablen
; Bank0: 0x20 .. 0x7F ; 96 Byte
; Bank1: 0xA0 .. 0xEF ; 80 Byte
; Bank2:0x120 ..0x14F ; 46 Byte

w_copy          equ     0x4D    ; nur für INT
s_copy          equ     0x4E    ; nur für INT
p_copy          equ     0x4F    ; nur für INT

; Akkumulationsregister
akku0           equ     0x51    ;
akku1           equ     0x52    ;
akku2           equ     0x53    ;

;Frequenz
f0              equ     0x54    ;
f1              equ     0x55    ;
f2              equ     0x56    ;

;Schieberegister
s0              equ     0x57    ;
s1              equ     0x58    ;

; Konstanten festlegen
#define Load            PORTB,0
#define serin           PORTB,1
#define serout          PORTB,3
#define clock           PORTB,4


Ini_opt         equ     B'10000100'     ; Timer0 int 32:1, pull-up on

;**************************************************************
        org     0
        goto    Init


;**************************************************************
; die Interuptserviceroutine
; wird vom capture-Modul ausgeloest

        org     4       
intvec
        movwf   w_copy          ; w retten
        swapf   STATUS, w       ; STATUS retten
        clrf    STATUS
        movwf   s_copy          ;
        movf    PCLATH, W
        movwf   p_copy
        clrf    PCLATH          ; Bank 0

; Intrupt service routine
        btfsc   INTCON, INTF
        goto    Int_Load
        btfsc   INTCON, RBIF
        goto    Int_shift
        clrf    INTCON          ; da ging was schief, failback
        goto    Int_end


Int_shift
        btfss   clock
        goto    Int_shift_skip  ; falsche flanke, nichts tun
        rlf     s0,f
        rlf     s1,f
        bcf     serout          ; letztes bit herausschieben
        btfsc   s1,7
        bsf     serout
        bcf     s0,0            ; neues bit einlesen
        btfsc   serin
        bsf     s0,0
Int_shift_skip
        bcf     INTCON, RBIF
        goto    Int_end


Int_Load
        ; f = s x 64
        clrf    f0
        movfw   s0
        movwf   f1
        movfw   s1
        movwf   f2

        bcf     STATUS,C
        rrf     f2,f
        rrf     f1,f
        rrf     f0,f
        rrf     f2,f
        rrf     f1,f
        rrf     f0,f

        bcf     INTCON, INTF
        goto    Int_end


Int_end
        movf    p_copy, W
        movwf   PCLATH
        swapf   s_copy, w       ; STATUS zurück
        movwf   STATUS
        swapf   w_copy, f       ; w zurück mit flags
        swapf   w_copy, w

        retfie


;**************************************************************
; Das Programm beginnt mit der Initialisierung

Init
        bsf     STATUS, RP0     ; Bank 1
        movlw   Ini_opt         ; pull-up on; Timer0 32:1 interner Takt
        movwf   OPTION_REG
        movlw   B'00010111'     ; PortB alle inputs außer RB3 ,5..7
        movwf   TRISB
        movlw   B'11100000'     ; PortRA0..4 outputs
        movwf   TRISA
        bcf     STATUS, RP0     ; Bank 0

; 16F628 alle Comparatoreingänge auf Digital umschalten
        BSF     CMCON, CM0
        BSF     CMCON, CM1
        BSF     CMCON, CM2

; Interupt disable  
        clrf    INTCON      

; 100 Hz einstellen
        clrf    f2
        movlw   0x19
        movwf   f1
        movlw   0x00
        movwf   f0

        clrf    s0
        clrf    s1

; Interrupts
        clrf    INTCON
        ; interrupt fuer load-pulse an RB0
        bsf     STATUS, RP0             ; Bank 1
        bsf     OPTION_REG, INTEDG      ; RB0-int auf steigende flanke
        bcf     STATUS, RP0             ; Bank 0
        bsf     INTCON, INTE

        ; int fuer clock an RB4
        movfw   PORTB
        bsf     INTCON, RBIE            ; int on change RB4..7
        
        bsf     INTCON,GIE



;**************************************************************
; symmetrischer Rechteck
; Frequenz: f   
; bei Clk = 20 MHz
square
_clr    clrf    akku0
        clrf    akku1
        clrf    akku2

next    ; 19 Tcycl = 3,8 us
        movf    f0,w
        addwf   akku0,f
        movf    f1,w
        btfsc   STATUS,C
        incfsz  f1,w
        addwf   akku1,f
        movf    f2,w
        btfsc   STATUS,C
        incfsz  f2,w
        addwf   akku2,f         ; akku(24) = akku(24)+f(24)
        nop
        nop
        nop
        clrw
        btfsc   akku2,7
        movlw   0xFF
        movwf   PORTA   
        goto    next

;**************************************************************
        end





Ergänzung
Das ist noch mal ein ähnliches Programm, nur das diesmal der 16-Bit-Eingangswert keine Hexadezimalzahl sondern eine 4-stellige BCD-Zahl darstellt (MSD zuerst). Damit lassen sich natürlich nur Frequenzen bis zu 9999 Hz erzeugen.



zurück zu Lernbeispiele , PIC-Prozessoren , Elektronik , Homepage
Autor: sprut
erstellt : 20.04.2010
letzte Änderung: 01.06.2010