PIC-Lernbeispiel: Spannung mit Rotary-Encoder oder Tasten einstellen


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



Einleitung
Diese Schaltung erzeugt eine Gleichspannung, deren Höhe zwischen 0V und 5V in Schritten eingestellt werden kann. Die Einstellung erfolgt mit zwei Drucktasten (16 Schritte) oder mit einem Rotary-Encoder (32 Schritte). Die Höhe der eingestellten Spannung wird im EEPROM gespeichert, so dass nach dem Ausschalten und Wiedereinschalten sofort die zuletzt verwendete Spannung wieder ausgegeben wird. Die Schaltung verwendet einen 8-beinigen PIC12F683. Ich habe sie auch mit einem 14-pinigen PIC16F684 getestet, dafür nötige Abweichungen/Änderungen sind im Text und Quellcode enthalten. Es wird der interne Oszillator (4 MHz) verwendet.


Rotary-Encoder und Tasten
Sowohl Rotary-Encoder wie auch die beiden Drucktaster "UP" und "DOWN" dienen dem einstellen der Ausgangsspannung. Es ist nicht nötig beides anzuschließen. Je nach Anwendung kann man auch nur den Encoder oder nur die beiden Tasten verwenden. (siehe nebenstehendes Bild ohne Encoder)
Die Funktion eines Rotary Encoder wurde an anderer Stelle ausführlich erläutert. Er dient hier zur Einstellung der Ausgangsspannung. Durch ein Rechtsdrehen wird die Spannung in 32 Schritten von 0V bis auf 5V erhöht. Durch Linksdrehung wird sie wieder verringert. Einige Encoder rasten nur in jeder zweiten Position. Sie machen also immer zwei Schritte auf ein Mal. Wer einen solchen Encoder einsetzt, muss das in der Software berücksichtigen oder hat nur 16 Spannungsschritte. Mein Encoder rastet aber in jeder Position.
Für saubere Signalpegel benötigen Encoder und Tasten pull-up-Widerstände. Ich verwende die internen pull-up-Widerstände des PIC.

In der Schaltung fehlt leider noch ein Widerstand (10 .. 100 kOhm) zwischen Pin 4 (MCLR) und Pin 1 (Vdd) des PIC.
Minimalversion ohne Encoder


Gleichspannungserzeugung
PICs besitzen keinen internen Analog-Digital-Wandler. Um den Schaltungsaufwand minimal zu halten, erzeuge ich mit dem CCP-Modul des PIC ein PWM-Signal (pulsweitenmodulierte Spannung) und wandle diese mit einem RC-Glied in eine dem Tastverhältnis proportionale Gleichsspannung.


Wertspeicherung
Damit beim Ausschalten der Schaltung der zuletzt eingestellte Spannungswert nicht verloren geht, speichere ich ihn im EEPROM des PIC. Um den EEPROM dabei nicht zu schnell "abzunutzen" prüfe ich nur alle 5 Sekunden, ob der momentane Spannungswert noch mit dem im EEPROM gespeicherten übereinstimmt. Ist das nicht der Fall, wird der aktuelle Wert in den EEPROM geschrieben. Dadurch ergibt sich der kleine Nachteil, das Spannungsänderungen, die durch Tasten oder Encoder in den letzten 5 Sekunden vor dem Abschalten der Spannung eingegeben wurden, möglicherweise nicht im EEPROM gespeichert wurden. In der Praxis dürfte das kaum von Belang sein.
Den 5-sekündigen Test habe ich mit dem Timer1 und einer Interuptroutine realisiert. Da der EEPROM für mindestens 1 Million Schreibzyklen ausgelegt ist, kommt der EEPROM nun erst an seine Alterungsgrenze, wenn 2 Monate lang ununterbrochen am Encoder gedreht oder mit den Tasten gespielt wird. Da sind Encoder oder Tasten eher defekt.



Schaltung
Zur Erzeugung der PWM wird das CCP-Modul verwendet. Dessen Ausganspin ist beim PIC12F683 das Pin5 (GPIO2). Auch beim 14-beinigen PIC16F684 wäre es das Pin 5 (RC5).

Der Rotary Encoder hat zwei digitale Impulsausgänge (Pin5 & 4 des Encoders), für die Drehfunktion. Diese beiden Pins werden an je ein Pin des PIC geführt (Pin 2 & 3). Die internen pull-up-Widerstände des PIC sorgen für normgerechte Pegel.
Der Rotary-Encoder benötigt eine 5V-Betriebsspannung, deshalb wird ihm Vss und Vdd (Pin 1 & 6) zugeführt.

Die Tasten (UP und DOWN) werden an die Pins 6 & 7 des PIC12F683 (Pins 12 & 13 des PIC16F684) angeschlossen. Die internen pull-up-Widerstände des PIC sorgen auch hier für normgerechte Pegel.

Der Reset-Taster ist nicht zwingen erforderlich. In der Schaltung fehlt leider noch ein Widerstand (10 .. 100 kOhm) zwischen Pin 4 (MCLR) und Pin 1 (Vdd) des PIC.

Das erzeugte PWM-Signal kann abschließend mit einem RC-Glied in eine Gleichspannung gewandelt werden.

Für den Aufbau eignet sich eine 14Pin/8Pin-Testplatine  mit angesteckter Taster-Platine.
vollständige Schaltung


Programmablauf

parallel erzeugt Timer1 alle 0.5 Sekunden einen Interrupt:



Programmlisting

; Listing der Version für 16F684

;**************************************************************
;*
;* Pinbelegung
;*      ----------------------------------                683 / 684
;*      GPIO:   0 < Taster up                           Pin 7 / 13
;*              1 < Taster down                         Pin 6 / 12
;*              2 > PWM-Ausgang (32 kHz) (12F683)       Pin 5
;*              3 < Reset (MCLR)                        Pin 4
;*              4 < Encoder B                           Pin 3
;*              5 < Encoder A                           Pin 2
;*
;*      PORTC:  5 > PWM-Ausgang (32 kHz) (16F684)               Pin 5
;*
;**************************************************************
;
; sprut (zero) Bredendiek 11/2008
;
; PWM-Poti
;
; 12F683 erzeugt mit einer PWM eine Gleichspannung
; gesteuert wird das durch:
;       2 Taster in 16 Stufen (4 Bit)
;       Rotary-Encoder in 32 Stufen (5 Bit)
;
; 12F683 erzeugt an GP2 ein 32 kHz-PWM-Signal
; PS=1; PR2=0x1F; resolution 5-Bit (32 Stufen)
;
;
; Prozessortakt:  4 MHz intern, Zyklustakt 1 MHz
;
; Test mit 16F684
;
;**************************************************************
; Includedatei einbinden

;       list p=12f683
;       #include <P12f683.INC>
        list p=16f684
        #include <P16f684.INC>

        ERRORLEVEL      -302            ;SUPPRESS BANK SELECTION MESSAGES

; kein Power on Timer, kein Watchdog, int-Oscillator, kein Brown out
        __CONFIG        _MCLRE_ON & _PWRTE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT & _BOD_OFF


;**************************************************************
; Variablen
loops2          EQU     0x20            ; fuer waitms
dc              EQU     0x21            ; Taktverhaeltnis als 5-Bit Wert fuer CCPR1L
flags           EQU     0x22
dcold           EQU     0x23            ; alter dutycycle
neu             EQU     0x24            ; neue Encoder-Stellung
alt             EQU     0x25            ; alte Encoder-Stellung
temp            EQU     0x26            ; fuer Encoder
t1count         EQU     0x27

W_TEMP          EQU     0xF0
STATUS_TEMP     EQU     0xF1


; Definitionen
#define KeyUp           GPIO,0
#define KeyDown         GPIO,1
#define UpKeyFlag       flags,0
#define DownKeyFlag     flags,1

#define encoder         GPIO

; fuer 16F684
#define GPIO            PORTA
#define TRISIO          TRISA

;**************************************************************
;voreingestellte Default-Werte im EEPROM
        org     H'2100'         ; EEPROM
        de      0x0F            ; Adr 0 = 0x0F



;**************************************************************
        org     0               ; Anfang des Flash
        GOTO    init

        org     0x04            ; Int
Int
        MOVWF   W_TEMP          ; Copy W to TEMP register
        SWAPF   STATUS, W       ; Swap status to be saved into W
        CLRF    STATUS          ; bank 0, regardless of current bank, Clears IRP,RP1,RP0
        MOVWF   STATUS_TEMP     ; Save status to bank zero STATUS_TEMP register
       
        ; Timer1 loest mit 2 Hz Int aus
        BTFSS   PIR1, TMR1IF    ; ist es Timer1?
        GOTO    IntEnd          ; nein
        DECFSZ  t1count, f      ; Zeit rum?
        GOTO    IntT1End        ; nein
        MOVLW   D'10'
        MOVWF   t1count         ; 5 Sekunden einstellen
        MOVFW   dc
        xorwf   dcold, w        ; ist neu = alt?
        bz      IntT1End        ; ja: nichts tun
        ; neuen Wert im EEPROM speichern
        MOVFW   dc
        MOVWF   dcold
        CALL    EEWrite
IntT1End
        BCF     PIR1, TMR1IF    ; Flag loeschen

IntEnd
        SWAPF   STATUS_TEMP,W   ; Swap STATUS_TEMP register into W
                                ; (sets bank to original state)
        MOVWF   STATUS          ; Move W into Status register
        SWAPF   W_TEMP,F        ; Swap W_TEMP
        SWAPF   W_TEMP,W        ; Swap W_TEMP into W
        RETFIE


;**************************************************************
; Anfangsinitialisierung
init

; internen Takt 4MHz
        ; reset selects 4 MHz default
        BSF     STATUS,RP0      ; Bank1
        BSF     OSCCON, SCS     ; intosc is used
        BCF     STATUS,RP0      ; Bank0
        CLRF    flags


; PWM vorbereiten
; 4MHz-Takt -> 1 MHz-Zyklustakt
; Vorteiler 1:1 und Timer2 einschalten
        CLRF    T2CON           ; Vorteiler 1:1
        BSF     T2CON,TMR2ON    ; Timer2 ein

; Frequenz auf 32 kHz einstellen
        BSF     STATUS,RP0      ; Bank1
        MOVLW   0x1E            ;
        MOVWF   PR2             ; 32 kHz
        BCF     STATUS,RP0      ; Bank0

; Tastverhältnis auf alten Wert einstellen
        CLRW                    ; Adresse fuer EEPROM = 0
        CALL    EERead          ; alten Potiwert aus EEPROM lesen
        MOVWF   dc
        MOVWF   dcold
        MOVWF   CCPR1L          ; PWM auf alten Wert einstellen

; GP2/CCP1 auf Ausgang stellen
        BSF     STATUS,RP0      ; Bank1
        BCF     TRISIO, 2       ; GP2: output=0 12F683
        BCF     TRISC, 5        ; outout 16F684
        BCF     STATUS,RP0      ; Bank 0

; PWM MODE mit CCP1 initialisieren
        CLRF    CCP1CON         ; CCP1-Modus aus
        BSF     CCP1CON,CCP1M3  ; CCP1-Modus PWM-Mode, active high
        BSF     CCP1CON,CCP1M2  ;


; IO-Pins vorbereiten
        CLRF    GPIO            ; Init GPIO
        MOVLW   0x07            ; Set GP<2:0> to
        MOVWF   CMCON0          ; digital I/O
        BSF     STATUS,RP0      ; Bank1
        CLRF    ANSEL           ; digital I/O
        MOVLW   0x33            ; pull-up fuer GP0,1,4,5
        MOVWF   WPU
        BCF     OPTION_REG, 7   ; pull-up master
        BCF     STATUS,RP0      ; Bank 0


; Start-Encoderstand merken
        MOVFW   encoder         ; Port lesen
        MOVWF   alt             ; aktuelle encoder-Stellung nach alt
        MOVLW   B'00110000'     ; Schablone für die beiden Bits
        ANDWF   alt, f          ; nur die 2 Bits stehen lassen


; Timer 1 zum EEPROM-schreiben , testet alle 2 Sekunden auf Aenderung
; 1 MHz / 8 / 65536 = 1,9 Hz
        MOVLW   D'10'
        MOVWF   t1count         ; 5 Sekunden einstellen
        CLRF    TMR1L
        CLRF    TMR1H
        MOVLW   B'00110001'     ; Timer1 on, Vorteiler 8:1, interner Takt
        MOVWF   T1CON
        BSF     STATUS,RP0      ; Bank1
        CLRF    PIE1
        BSF     PIE1, TMR1IE    ; Int on
        BCF     STATUS,RP0      ; Bank0
        CLRF    INTCON
        BSF     INTCON, PEIE
        BSF     INTCON, GIE


;**********************************************************
; Hauptprogrammschleife
Main


; Tasten abfrage
UpKeyTest
        BTFSC   KeyUp
        GOTO    DownKeyTest
        CALL    wait1ms
        BTFSC   KeyUp
        GOTO    DownKeyTest
        INCF    dc,f
        INCF    dc,f
        BSF     UpKeyFlag
DownKeyTest
        BTFSC   KeyDown
        GOTO    Encoder
        CALL    wait1ms
        BTFSC   KeyDown
        GOTO    Encoder
        DECF    dc,f
        DECF    dc,f
        BSF     DownKeyFlag


; Encoder abfragen
Encoder
;       Schritt A       B
;               GP4     GP5
;       0       -       -
;       1       H       -
;       2       H       H
;       3       -       H
;       0       -       -
;       1       H       -
;       2       H       H
;       3       -       H

;       clrf    RotorFlags
        MOVFW   encoder                 ; Port lesen
        MOVWF   neu                     ; aktuelle encoder-Stellung nach new
        MOVLW   B'00110000'             ; Schablone für die beiden Bits
        ANDWF   neu, f                  ; nur die 2 Bits stehen lassen
        MOVFW   neu                     ; wurde der encoder bewegt?
        MOVWF   temp
        MOVFW   alt
        XORWF   temp, w                 ; ist neu = alt?
        BZ      Limits                  ; ja: nicht verdreht, also nichts tun
                                        ; nein: encoder verdreht, aber in welche Richtung?
                                        ; drehen wir mal Bit0 des alten werts zum Vergleich
                                        ;  nach links
        BCF     alt, 5
        CLRC                            ; Carry-Flag löschen
        RLF     alt, f                  ; alten Encoder-Stand um 1 Stelle nach links drehen
        MOVFW   neu
        XORWF   alt, f                  ; falls xorf >1 ergibt, dann war es eine Rechtsdrehung
        BZ      links                   ; links:  decrement counter
        BTFSS   alt, 4
        GOTO    rechts
        BTFSS   alt, 5
        GOTO    links
rechts
        INCF    dc,f                    ; rechts:  increment counter
        GOTO    weiter
links
        DECF    dc,f                    ; links:  decrement counter
weiter
        MOVFW   neu
        MOVWF   alt                     ; neu für nächsten Vergleich als alt speichern


; pruefen ob dc zwischen 0 und 1F liegt
Limits
        BTFSC   dc, 7           ; negativ?
        CLRF    dc              ; dc = 0
        MOVLW   B'11100000'
        ANDWF   dc, w           ; > 0x1F ?
        BTFSC   STATUS, Z
        GOTO    LimitsEnd
        MOVLW   0x1F
        MOVWF   dc
LimitsEnd


; Wert an PWM übergeben
        MOVFW   dc
        MOVWF   CCPR1L 


; warten auf freie Tasten
Free
        CALL    wait1ms         ; unterdruecken von Tasterprellen
FreeUp
        BTFSS   UpKeyFlag
        GOTO    FreeDown
FreeUpL
        BTFSS   KeyUp  
        GOTO    FreeUpL
        CALL    wait1ms         ; unterdruecken von Tasterprellen
FreeUpL1
        BTFSS   KeyUp  
        GOTO    FreeUpL1
        BCF     UpKeyFlag

FreeDown
        BTFSS   DownKeyFlag
        GOTO    FreeEnd
FreeDownL
        BTFSS   KeyDown
        GOTO    FreeDownL
        CALL    wait1ms         ; unterdruecken von Tasterprellen
FreeDownL1
        BTFSS   KeyDown
        GOTO    FreeDownL1
        BCF     DownKeyFlag
FreeEnd


        GOTO    Main


;**************************************************************
; Warteschleife 1 ms für einen 4-MHz-PIC-Takt
wait1ms
        MOVLW   .110           ; Zeitkonstante für 1ms
        MOVWF   loops2
Wai2    NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        DECFSZ  loops2, F      ; 1 ms vorbei?
        GOTO    Wai2           ; nein, noch nicht
        RETURN


;**************************************************************
;  EEPROM-Routine für 12F683
;**************************************************************
; 12F683 hat 256 EEPROM-Zellen
; 1 000 000 Schreibzyklen sind garantiert       = 6 Tage lang mit 2 Hz Tasten druecken
;                                               = 60 Tage lang alle 5 Sekunden

; lesen der Zelle, deren Adresse in W steht, nach W
EERead
        BSF    STATUS, RP0     ; EEADR liegt in der Bank 1
        MOVWF  EEADR           ; schreibe die Adresse in EEADR
        BSF    EECON1, RD      ; EEPROM Leseprozeß starten
        MOVF   EEDAT, W        ; Die Daten der EEPROM Zelle nach W kopieren
        BCF    STATUS, RP0     ; Bank 0
        RETURN


; beschreiben des EEPROM; Adresse ist 0; Wert in w
EEWrite
        BSF     STATUS, RP0     ; der Bank 1
        CLRF    EEADR           ; Die Zelle 0x00 soll beschrieben werden
        MOVWF   EEDATA          ; w wollen wir schreiben
        BSF     EECON1, WREN    ; nun ist Schreiben erlaubt
; folgende Zeilen sind nicht noetig, wenn aus Int gerufen
;EEW_GIE
;       BCF     INTCON, GIE     ; verbieten aller Interrupts
;       BTFSC   INTCON,GIE      ; See AN576
;       GOTO    EEW_GIE
        ; Die folgenden 5 Zeilen müssen genau so im Code stehen!!!
        MOVLW   0x55            ;
        MOVWF   EECON2          ; schreibe 55h nach EECON2
        MOVLW   0xAA            ;
        MOVWF   EECON2          ; schreibe AAh nach EECON2
        BSF     EECON1, WR      ; starte den Schreibzyklus
;       BSF     INTCON, GIE     ; Interrupts wieder erlauben , falls noetig
        BCF     STATUS, RP0     ; Bank 0
        RETURN

;**************************************************************



        end

;**************************************************************


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