|
@@ -0,0 +1,276 @@
|
|
|
+ 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
|