123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- list p=16f88
- errorlevel -306 ; no page boundary warnings
- errorlevel -305 ; no default destination messages
- errorlevel -302 ; no bank 0 warnings
- errorlevel -202 ; no 'argument out of range' warning
-
- ; Configuration Bits:
- ; 32 1098 7654 3210 <-- bit number
- ; 11 1111 0001 1000 <-- value
- ; Hex: 3f18
- ; For the 16F88, which has two configuration words, we must specify which word we are writing.
- __config 0x2007 , 0x3f18 ; Config word one. All defaults, but no BOR, Power On Timer enabled, MCLR tied to Vdd internally
- __config 0x2008 , 0x3ffc ; Special features disabled.
- include <p16f88.inc>
- org 0
-
- ; Compiler instructions
- ; Connect LEDs to PORTB 0,1,6,7
- led1 equ b'00000001'
- led2 equ b'00000010'
- led3 equ b'01000000'
- led4 equ b'10000000'
- RawCount equ 20h
- BeatCount equ 21h
- BarCount equ 22h
- MsgBuffer equ 23h
- CalcBuffer equ 24h
- start
- ; Set the internal oscillator frequency
- bsf STATUS, RP0
- movlw b'01110110' ; Internal frequency = 8MHz, OSTS uses INTOSC.
- movwf OSCCON
-
- ; Set PORTA as output, PORTB as output except for the RX pin
- clrf ANSEL ; Turn Off Analog Converter
- clrf TRISA ; Make PORTA output
- clrf TRISB ; Make PORTB output
- bsf TRISB, 2
- ; Set up UART to 31.250 kBaud for MIDI.
- ; We're using the internal 8MHz oscillator clock.
- ; Baud low speed, X=3 (gives exact match)
-
- movlw 3h
- movwf SPBRG
-
- bcf TXSTA, BRGH ; Low Speed
- bcf TXSTA, SYNC
-
- bcf STATUS, RP0
-
- bsf RCSTA, SPEN ; Enable Serial Port
- bsf RCSTA, CREN ; Enable Continuous Receive
-
- ; Initialization of the setup
- reset
- clrf PORTB ; Turn off all beat LEDs
- clrf PORTA ; Set 7 seg to zero.
- movlw d'25' ; Set the Raw Count to 24
- movwf RawCount
- clrf BarCount
- clrf BeatCount
-
- ; There are two options for counting the beats:
- ; 1. Using the MIDI Timing Clock
- ;
- ; MIDI Sends the Timing Clock message 24 times per quarter note.
- ; We must therefore count to 24 before changing the beat
- ; And 4 beats to increase the bar
- ;
- ; The Timing Clock message is b'11111000'
- ;
- ; At the start of the sequence, the message b'11111010' will be sent.
- ; We can use this to reset the counter? This seems like a mess.
- ;
- ;
- ; 2. Using the Song Position Pointer
- ; This increases 4 times per beat and is a 14-bit counter.
- ; Max 16384 MIDI beats = = 4096 music beats = 1024 bars > 30 minutes @ 120 bpm.
- ;
- ; We can then decide to use absolute timing or relative (counting).
- ; Let's see where we get with absolute timing.
- ;
- ; The Song Position Counter message is b'11110010' = 0F2h
- ; Followed by the LSB (7 bit value) and the MSB (7 bit value)
- ; So we get 128 16th notes = 32 beats = 4 bars per LSB cycle.
- ; We are only interested in every 4 of these messages.
- ; We could therefore do two RRFs on this register and compare to the previous one.
- ;
- ; For the bar counter, we are interested in every 16 of these messages.
- ; We could do 4 RRFs on this register and read the bar count.
- ; This means we have 8 bars on this LSB register, which is what we need.
- ;
- ; MIDI devices will send this thing when they scrub position.
- wait_for_start
- btfss PIR1, RCIF
- goto wait_for_start
- movf RCREG, W
- movwf MsgBuffer
- sublw 0FA ; Is this a Start message?
- btfsc STATUS, Z
- goto get_started
-
- movf MsgBuffer, W
- sublw 0FB ; Is it a Continue message?
- btfsc STATUS, Z
- goto loop
-
- movf MsgBuffer, W
- sublw 0F2 ; Is it a Song Position Pointer message?
- btfsc STATUS, Z
- goto wait_for_position
- goto wait_for_start ; If it is neither of these, ignore.
- wait_for_position
- ; If we have received a Song Position Counter,
- ; we will now receive two bytes that represent the song position.
- ; The resolution is 4 per quarter note, so 16 per bar.
- ; We use this to calculate where we are by shifting the bits to the right.
- ; Since we only need the LSB (which comes first), we can safely ignore the second one.
- ; This is a 7-bit value, so the maximum can be 127.
- ; This works out to exactly 8 bars in this 7-bit register,
- ; so it's easy to convert to beats and bars in this case.
-
- btfss PIR1, RCIF
- goto wait_for_position
-
- movf RCREG, W
- movwf MsgBuffer
- movwf CalcBuffer
-
-
- ; Derive the bar number by dividing by 16:
- rrf CalcBuffer, f
- rrf CalcBuffer, f
- rrf CalcBuffer, f
- rrf CalcBuffer, W
- andlw b'00001111' ; Mask whatever was carried in
- movwf BarCount ; Copy to BarCount
-
- sublw 8 ; Are we past bar 8?
- btfsc STATUS, Z
- clrf BarCount ; If so, reset bar count.
-
- incf BarCount, W
- movwf PORTA ; Immediately display on the indicator
-
- ; Get the beat position
- ; The Song Position Counter must be multiplied by 6 to get the Count as we know it in
- ; the clock signal. The clock uses 24 per quarter, the Song Position just 4 per quarter.
- ; We first have to get the beat we are in,
- ; then reconcile the remainder of the position to the RawCount.
-
-
- ; Get the current beat: divide by 4.
- movf MsgBuffer, W
- andlw b'00001111'
- movwf CalcBuffer
- rrf CalcBuffer, f
- rrf CalcBuffer, W
- andlw b'00111111'
- movwf BeatCount
- ; Get the position within the beat.
- movf MsgBuffer, W
- andlw b'00000011' ; Only the remainder part
- movwf CalcBuffer
- bcf STATUS, C
- rlf CalcBuffer, f ; Multiply by two and update CalcBuffer
- rlf CalcBuffer, W ; Multiply by two and keep in W
- addwf CalcBuffer, W ; Now we have the 24-step raw count in W. Subtract from 24.
-
- sublw d'24'
- btfsc STATUS, Z
- movlw d'24'
-
- addlw 1
- movwf RawCount
- call get_beat ; Display the new beat count on the LEDS
- movwf PORTB
- goto wait_for_start
- get_started
- movlw d'25' ; Set the Raw Count to 25
- movwf RawCount
- clrf BarCount
- clrf BeatCount
-
- movlw led1
- movwf PORTB ; Display the new Beat Count
- incf BarCount, W
- movwf PORTA ; Display the new Bar Count
-
- goto loop
- loop ; We will now receive 24 MIDI clocks per beat.
- btfss PIR1, RCIF ; Check if there is a new MIDI message
- goto loop
- movf RCREG, W
- movwf MsgBuffer ; Copy the MIDI message to the buffer
- sublw 0FC ; Is it a Stop message?
- btfsc STATUS, Z
- goto wait_for_start
- movf MsgBuffer, W ; Read MIDI message from the buffer
- sublw 0F8 ; Is this a MIDI clock beat?
- btfss STATUS, Z
- goto loop ; If it is neither a Stop nor a Clock message, go back and wait for more.
- decf RawCount, f ; Counting down from 24 every time.
-
- movf RawCount, W
- sublw d'18'
- btfsc STATUS, C
- clrf PORTB ; If we are at a quarter of the beat, clear the leds
- movf RawCount, W
- btfss STATUS, Z
- goto loop ; If we have not reached the next beat, go back to the loop
- movlw d'24' ; Reset the Raw Counter
- movwf RawCount
-
- incf BeatCount, f ; Increase the Beat Count
- movf BeatCount, W
- sublw 4 ; Are we past beat 4?
- btfsc STATUS, Z
- call next_bar
- call get_beat
- movwf PORTB
-
- goto loop
-
- next_bar
- clrf BeatCount
- incf BarCount, f
-
- movf BarCount, W
- sublw 8
- btfsc STATUS, Z
- clrf BarCount
-
- incf BarCount, W
- movwf PORTA
- return
- get_beat
- movf BeatCount, W
- addwf PCL, f
- retlw led1
- retlw led2
- retlw led3
- retlw led4
-
- end
|