Schaltung
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.
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.
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:
Folgende Näherung hat einen Fehler von nur 0,4%, und sollte
meistens ausreichend sein:
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.
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.
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.
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.