CHU
in Chemnitz
Willkommen auf der Webseite von Chris Hübsch
Sie befinden sich hier: Startseite > Elektronik > Buzzer
~/tmp/buzzer/buzzer.asm.html
; Copyright 2006
; Marco Gubka, Chris Huebsch
;
#include p16f84a.inc

        __CONFIG  _WDT_OFF & _RC_OSC
        ;PROCESSOR p16F84A

; Konstanten

w0      equ     d'2'
w1      equ     d'6'
w2      equ     d'12'
w3      equ     d'14'
w4      equ     d'16'
w5      equ     d'32'

; Variable

        cblock  0x0c
        _dividend
        _divisor
        w_temp
        STATUS_temp
        wait1
        wait2
        wait3
        blinkaddr
        delay
        endc

; Makros

; dividiert register (dividend) durch konstante (divisor)
; ganzzahlig
; Ergebnis in w
div     macro   dividend, divisor
        movf    dividend, w
        movwf   _dividend
        movlw   divisor
        movwf   _divisor
        call    division
        endm

waitn   macro   anz
        movlw   anz
        movwf   wait3
        call    wait            ; warten
        endm

rt_an   macro
        bsf     PORTA, 0        ; ALLE ROTEN AN
        bsf     PORTA, 2
        endm

gn_an   macro
        bsf     PORTA, 1
        bsf     PORTA, 3
        endm

rt_aus  macro
        bcf     PORTA, 0        ; ALLE ROTEN AUS
        bcf     PORTA, 2
        endm

gn_aus  macro
        bcf     PORTA, 1
        bcf     PORTA, 3
        endm

nur_rt  macro
        movlw   b'00000101'
        movwf   PORTA
        endm

nur_gn  macro
        movlw   b'00001010'
        movwf   PORTA
        endm

; Programm

        org     0
        goto start

        org     4 ;interrupt
        movwf   w_temp
        swapf   STATUS,w
        movwf   STATUS_temp

        ; interrupt-routine
        ; movfw TMR0

w0test
        btfsc   PORTB, 1        ; ds1 steht auf 1 - dh. PORTB[1] == 0
        goto    w1test          ; wenn nicht, dann test der naechsten ds-stufe
        div     TMR0, w0        ; teile TMR0 durch 2 - 50 von 100
        btfsc   STATUS, Z       ; wenn teilbar ohne rest - gewonnen
        goto    win
        goto    lose

w1test
        btfsc   PORTB, 2        ; ds1 steht auf 2 - dh. PORTB[2] == 0
        goto    w2test          ; wenn nicht, dann test der naechsten ds-stufe
        div     TMR0, w1        ; teile TMR0 durch 5 -  20 von 100
        btfsc   STATUS, Z       ; wenn teilbar ohne rest - gewonnen
        goto    win
        goto    lose

w2test
        btfsc   PORTB, 3        ; ds1 steht auf 3 - dh. PORTB[3] == 0
        goto    w3test          ; wenn nicht, dann test der naechsten ds-stufe
        div     TMR0, w2        ; teile TMR0 durch 10 - 10 von 100
        btfsc   STATUS, Z       ; wenn teilbar ohne rest - gewonnen
        goto    win
        goto    lose

w3test
        btfsc   PORTB, 4        ; ds1 steht auf 4 - dh. PORTB[4] == 0
        goto    w4test          ; wenn nicht, dann test der naechsten ds-stufe
        div     TMR0, w3        ; teile TMR0 durch 13 - 7.6 von 100
        btfsc   STATUS, Z       ; wenn teilbar ohne rest - gewonnen
        goto    win
        goto    lose

w4test
        btfsc   PORTB, 5        ; ds1 steht auf 5 - dh. PORTB[5] == 0
        goto    w5test          ; wenn nicht, dann test der naechsten ds-stufe
        div     TMR0, w4        ; teile TMR0 durch 25 - 4 von 100
        btfsc   STATUS, Z       ; wenn teilbar ohne rest - gewonnen
        goto    win
        goto    lose

w5test
        btfsc   PORTB, 6        ; ds1 steht auf 6 - dh. PORTB[6] == 0
        goto    w6test          ; wenn nicht, dann test der naechsten ds-stufe
        div     TMR0, w5        ; teile TMR0 durch 50 - 2 von 100
        btfsc   STATUS, Z       ; wenn teilbar ohne rest - gewonnen
        goto    win
        goto    lose

w6test ; ds steht auf "7" - d.h. es ist eigentlich aus
        movlw   0xff
        movwf   PORTA
        bsf     PORTB, 7
        waitn   3

        ; ende interrupt-routine        

int_done
        bcf     INTCON, INTF    ; externen Interrupt als bearbeitet markieren
        swapf   STATUS_temp,w
        movwf   STATUS
        swapf   w_temp,f
        swapf   w_temp,w
        retfie

; Hauptprogramm
start
        bsf     PORTB, 7        ; sound nicht spielen
        bsf     STATUS, RP0     ; Registerbank 1 waehlen
        ; Portrichtungen festlegen
        movlw   b'01111111'     ; PortB7 auf Ausgang und
        movwf   TRISB           ; PortB0-PortB6 auf Eingang
        movlw   b'00000000'
        movwf   TRISA           ; PortA0-PortA4 auf Ausgang
        ; Optionen definieren
        bcf     OPTION_REG, NOT_RBPU ; Register B Pullups aktivieren
        bcf     OPTION_REG, INTEDG   ; Interrupt auf fallende Flanke
        bcf     OPTION_REG, T0CS     ; Interner Clock fuer Timer0
        ; Prescaler dem WDT-Zuordnen
        ; das muss so sein laut Anleitung!
        bcf     STATUS, RP0
        clrf    TMR0            ; Timer 0 loeschen
        bsf     STATUS, RP0
        clrwdt
        bsf     OPTION_REG, PSA ; Prescaler aus
        bcf     STATUS, RP0

        ; Interrupts aktivieren
        bcf     INTCON, INTF    ; PortB0-Interrupt als bearbeitet markieren 
        bcf     INTCON, RBIF    ; Port-Change B0-3 als bearbeitet markieren
        bcf     INTCON, T0IF    ; Timer-Interrupt als bearbeitet markieren
        bsf     INTCON, INTE    ; Interrupt auf PortB0-Aktivitaet aktivieren
        bsf     INTCON, GIE     ; Interrupts global aktivieren

        clrf    blinkaddr
main    movfw   blinkaddr
        call    table
        movwf   PORTA
        movlw   0x10
        call    pause
        incf    blinkaddr, f
        btfsc   blinkaddr, 3
        clrf    blinkaddr
        goto    main

        sleep ;hier sollte er nie hinkommen!

; funktion bestimmt Teilbarkeit von _dividend durch _divisor
; Ergebnis in Zero-Flag
; wenn Division ohne Rest, dann Z=0, sonst Z=1
; funktion berechnet NICHT das Divisionsergebnis
division
        movf    _divisor, w
divnext
        subwf   _dividend, f
        btfsc   STATUS, Z       ;letzte Subtraktion hat Null ergeben
        goto    divende
        btfsc   STATUS, C       ;letzte Subtraktion hat negativen Wert gebracht
        goto    divnext
divende return

; funktion lose wird bei verloren aufgerufen
; wird nicht mit call, sondern mit goto aufgerufen
; ausschliesslich aus interrupt-routine verwenden,
; da zurueck in interrupt gesprungen wird
lose
        clrf    PORTA           ; Alle LED aus

        movfw   TMR0
        andlw   b'00000111'
        movwf   delay
        bsf     STATUS, C
        rlf     delay, f

; tmr0<3:0> mal schnell blinken
delay_cl        nur_gn
        movlw   0x20
        call    pause

        nur_rt
        movlw   0x20
        call    pause

        decfsz  delay, f
        goto delay_cl

        nur_gn
        movlw   0x20
        call    pause

        ; langsam langsamer werden
        movlw   0x10 ; durch rlf weiter unten wird eigentlich mit 20 begonnen
        movwf   delay

delay_ll nur_rt
        rlf     delay, w
        call    pause

        nur_gn
        bcf     STATUS, C
        rlf     delay, w
        call    pause

        rlf     delay, f
        btfss   STATUS, C
        goto    delay_ll


        nur_rt
        bcf     PORTA, 4        ; Auswahl Adresse 00000000 auf Soundchip
        nop
        bcf     PORTB, 7        ; Ton abspielen ausloesen

        waitn   5

        bsf     PORTB, 7        ; Erneutes Abspielen vorbereiten
        goto    int_done

; funktion win wird bei gewinn aufgerufen
; wird nicht mit call, sondern mit goto aufgerufen
; ausschliesslich aus interrupt-routine verwenden,
; da zurueck in interrupt gesprungen wird
win
        clrf    PORTA           ; Alle LED aus

        movfw   TMR0
        andlw   b'00000111'
        movwf   delay
        bsf     STATUS, C
        rlf     delay, f

; tmr0<3:0> mal schnell blinken
delay_cw        nur_gn
        movlw   0x20
        call    pause

        nur_rt
        movlw   0x20
        call    pause

        decfsz  delay, f
        goto delay_cw

        ; langsam langsamer werden
        movlw   0x10
        movwf   delay

delay_lw nur_gn
        rlf     delay, w
        call    pause

        nur_rt
        bcf     STATUS, C
        rlf     delay, w
        call    pause

        rlf     delay, f
        btfss   STATUS, C
        goto    delay_lw

        ; jetzt gewinn ausgeben
        nur_gn
        bsf     PORTA, 4        ; Auswahl Adresse 01000000 auf Soundchip
        nop
        bcf     PORTB, 7        ; Ton abspielen ausloesen

        waitn   5

        bsf     PORTB, 7        ; Erneutes Abspielen vorbereiten
        goto    int_done

; ruft pause wait3-mal auf
wait    movlw   0x00    ; nicht erschrecken - das bedeutet 256 mal
        call    pause
        decfsz  wait3, f
        goto    wait
        return

; Verzoegert W*0xff Takte
; in W steht die Anzahl d. Verzoegerungen
; sollten ca. 1/16 sek sein
pause   movwf   wait1           ;- Inhalt Arbeitsregister nach wait1 kopieren
pause1  movlw   0xff            ;- Kopiere Zahl 255 ins Arbeitsregister
        movwf   wait2           ;- Inhalt Arbeitsregister nach wait2 kopieren
pause2  nop                     ;- Taktverzoegerung
        decfsz  wait2, f        ;- dekrementiere, falls wert 0,
                                ;  dann ueberspringe naechsten befehl
                                ;  dann ueberspringe naechsten befehl
        goto    pause2          ;- gehe zu label pause2
        decfsz  wait1, f        ;- dekrementiere, falls wert 0, 
                                ;  dann ueberspringe naeaehsten befehl
        goto    pause1          ;- gehe zu Label pause1
        return                  ;- zurueck ins Hauptprogramm

; Tabelle mit LED-Werten

table   addwf   PCL, f
        retlw   b'0001'
        retlw   b'0011'
        retlw   b'0010'
        retlw   b'0110'
        retlw   b'0100'
        retlw   b'1100'
        retlw   b'1000'
        retlw   b'1001'

        end