Created
April 26, 2024 15:07
-
-
Save thentenaar/4b4eb419da5e85a94743638c7b3bb41b to your computer and use it in GitHub Desktop.
Happy Birthday on a PIC16f (for my daughter)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Happy Birthday on a PIC16f (for my daughter) | |
; | |
; Copyright (C) 2020 Tim Hentenaar. | |
; This code is licenced under the Simplified BSD License. | |
; https://opensource.org/license/bsd-2-clause | |
; | |
; Plays the classic tune on a piezoelectric buzzer (PWM), and blink some LEDs. | |
; | |
list p=16f628a | |
include <p16f628a.inc> | |
; Configuration words | |
CONFIG FOSC = INTOSCIO ; Internal Oscillator (RA6/7 are I/O pins) | |
CONFIG MCLRE = ON ; Enable /MCLR | |
CONFIG BOREN = OFF ; Disable Brown-out Reset | |
CONFIG WDTE = OFF ; Disable WDT | |
CONFIG CPD = OFF ; Data prot. Off | |
CONFIG CP = OFF ; Code prot. Off | |
CONFIG LVP = OFF ; Disable Low-Voltage programming | |
; Number of TMR0 overflows before updating | |
led_time equ 2 | |
note_time equ 4 | |
; Music note periods | |
_r equ 0 ; Rest | |
_c equ D'238' ; 261 Hz | |
_d equ D'212' ; 293 Hz | |
_e equ D'189' ; 329 Hz | |
_f equ D'178' ; 349 Hz | |
_g equ D'158' ; 392 Hz | |
_a equ D'141' ; 440 Hz | |
_b equ D'126' ; 493 Hz | |
_C equ D'118' ; 523 Hz | |
_D equ D'103' ; 597 Hz | |
_E equ D'94' ; 659 Hz | |
_F equ D'89' ; 698 Hz | |
_G equ D'78' ; 784 Hz | |
; Note durations | |
_whole equ 0x08 | |
_half equ 0x04 | |
_quarter equ 0x02 | |
_8th equ 0x01 | |
_16th equ 0x00 | |
; Variables | |
counter equ 0x70 ; Timer overflow counter | |
state_a equ 0x71 ; Local port state copy | |
state_b equ 0x72 | |
direction equ 0x73 ; LED Pattern direction | |
note_counter equ 0x74 ; Note timer | |
note_counter2 equ 0x75 | |
note equ 0x76 ; Current note | |
; Reset Vector | |
org 0x00 | |
goto init | |
; ISR | |
org 0x04 | |
goto int | |
; | |
; Happy Birthday (in C major) | |
; | |
n_notes equ D'35' | |
note_period: | |
addwf PCL, F | |
dt _r | |
dt _g, _r, _g, _r, _a, _g, _C, _b | |
dt _g, _r, _g, _r, _a, _g, _D, _C | |
dt _g, _r, _g, _r, _G, _E, _C, _b, _a | |
dt _F, _r, _F, _r, _E, _C, _D, _C | |
note_duration: | |
addwf PCL, F | |
dt _half | |
dt _8th, _16th, _8th, _16th, _quarter, _quarter, _quarter, _half | |
dt _8th, _16th, _8th, _16th, _quarter, _quarter, _quarter, _half | |
dt _8th, _16th, _8th, _16th, _quarter, _quarter, _quarter, _quarter, _quarter | |
dt _8th, _16th, _8th, _16th, _quarter, _quarter, _quarter, _half | |
init: | |
banksel INTCON ; Ensure we're on bank 0 | |
clrf INTCON ; Disable interrupts | |
movlw 0x07 ; Disable comparitors | |
movwf CMCON | |
movlw 0x0b ; 4 MHz | |
movwf PCON | |
; Set the prescaler for TMR0 (1:256) | |
; and ensure Port B pullups are disabled | |
banksel OPTION_REG | |
movlw 0xd7 | |
movwf OPTION_REG | |
; Enable CCP1 for PWM mode | |
banksel CCP1CON | |
movlw 0x0f | |
movwf CCP1CON | |
clrw | |
movwf CCPR1L | |
; Initialize ports A & B (output) | |
banksel TRISA | |
movlw 0x20 | |
movwf TRISA | |
clrf TRISB | |
banksel PORTA | |
movlw 0x10 | |
movwf state_a | |
clrf PORTA | |
movlw 0x20 ; PWM Uses B3 | |
movwf state_b | |
movwf PORTB | |
; Initialize our variables | |
movlw led_time | |
movwf counter | |
clrf direction | |
; Load the first note of the tune on the first | |
; overflow of TMR0 | |
movlw 1 | |
movwf note_counter | |
movwf note_counter2 | |
movlw 0xFF | |
movwf note | |
; TMR2: Enabled with the 1:16 prescaler | |
movlw 0x07 | |
movwf T2CON | |
clrf TMR2 | |
banksel PR2 | |
clrf PR2 | |
; Enable interrupts | |
banksel INTCON | |
clrf TMR0 | |
bcf INTCON, T0IF | |
bsf INTCON, T0IE ; Enable the interrupt for TMR0 | |
bsf INTCON, GIE | |
idle: | |
nop | |
nop | |
nop | |
goto idle | |
; Interrupt handler | |
int: | |
; We only service TMR0 interrupts | |
banksel INTCON | |
btfss INTCON, T0IF | |
retfie | |
; TMR0 overflows at a rate of ~3.9 us | |
; note_counter2 overflows at ~1 ms. | |
; note_counter overflows at ~256 ms. | |
decfsz note_counter2, F | |
goto leds | |
movlw note_time | |
movwf note_counter2 | |
decfsz note_counter, F | |
goto leds | |
; Our note duration has elapsed | |
banksel CCPR1L | |
clrf CCPR1L | |
; Set-up the next note | |
incf note, F | |
; Ensure we wrap-around when we get to the end of the tune | |
movfw note | |
bcf STATUS, Z | |
sublw n_notes | |
btfsc STATUS, Z | |
movwf note | |
; Get the next note period | |
movfw note | |
call note_period | |
; See if it's a rest | |
iorlw 0 | |
btfsc STATUS, Z | |
goto duration | |
; Load it into PR2 | |
banksel PR2 | |
movwf PR2 | |
; Set the duty cycle | |
banksel CCPR1L | |
movlw 8 | |
movwf CCPR1L | |
duration: | |
; Load the note duration | |
banksel INTCON | |
movfw note | |
call note_duration | |
movwf note_counter | |
iorlw 0 | |
btfss STATUS, Z | |
goto leds | |
; Zero duration (16th note) | |
incf note_counter, F | |
rrf note_counter2, F | |
leds: | |
decfsz counter, F | |
goto ack | |
movlw led_time | |
movwf counter | |
; Check the movement direction | |
btfsc direction, 0 | |
goto left | |
; 0 = Right-moving pattern | |
bcf STATUS, C | |
rrf state_a, F | |
bcf STATUS, C | |
rrf state_b, F | |
; Skip B3 since we're using it for PWM | |
bcf STATUS, C | |
btfsc state_b, 3 | |
rrf state_b, F | |
btfsc state_b, 0 | |
incf direction, F | |
goto write_ports | |
left: | |
; 1 = Left-moving pattern | |
bcf STATUS, C | |
rlf state_a, F | |
bcf STATUS, C | |
rlf state_b, F | |
btfsc state_b, 5 | |
decf direction, F | |
; Skip B3 since we're using it for PWM | |
bcf STATUS, C | |
btfsc state_b, 3 | |
rlf state_b, F | |
write_ports: | |
banksel PORTA | |
movfw state_a | |
movwf PORTA | |
movfw state_b | |
movwf PORTB | |
ack: | |
; Ack the interrupt | |
banksel INTCON | |
bcf INTCON, T0IF | |
retfie | |
END |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment