I built a little circuit that takes midi input from RX on a 16f628 usart and turns on some LEDS based on what notes are played.
I have two MIDI keyboards. When I connect my 1982 Roland which sends 6 midi bytes per keyboard note hit 90 xx xx b0 xx xx my PIC project device works perfectly.
I can play all 8 test notes at once and all 8 leds turn on and off super fast and correctly in time with when each note is hit and released.
When I connect it to my newer 1990's Roland keyboard which only sends 90 xx xx for a note the device seems to miss fast played notes. I can't hit 2 notes at a time...if I go slowly it works ok, but anything faster than half a second (only when playing different notes!?) and it acts all sticky and gummed up and misses notes.
I can hit the same not repeatedly and my test device turns that one LED on and off as fast as I can hit the key following the note on and note off messages, but if I try to play 2 at a time or more...only one note (led) registers. If I slowly roll over the 8 notes taking up to half a second per note, it works as expected.
Why is the USART so STICKY in this case? I moved it from direct code to a buffered interrupt (following many online examples) and it exhibits the exact same behavior.
I am using a 16F628 20/p in 4MHZ (internal oscillator) mode.
I compile using GPASM on a mac, flashing the chip using PICkit 2 and a little custom board I made for the 16F628. The USART behaved the same way when I used to use an old DOS PC to compile and burn.
Is the sticky USART a known issue? What on earth am I doing wrong? Is the 1982 keyboard actually sending the MIDI bytes more slowly? I don't understand why this won't work. I have written tons of Machine Language MIDI input routines for 6502, 6510 etc with no issues at all. Must I code a bit-wise input for the 16F628? I thought the USART supported MIDI speed? Is the sluggish USART a known issue? Did I miss something important? 4Mhz should be plenty fast...and IS when I plug in the 1982 keyboard.
Please offer any assistance you can. Thanks.
ivlenk
MIDI Note examples showing what each keyboard is sending during my tests:
90 is note on channel 1, 28 is note # 28 hex (on the lower left of the musical keyboard), velocity is how hard the note was hit
[NoteOn Command and channel] [Note Number][Velocity of key hit]
B0 is a controller command followed by 2 data bytes. Old Roland sends a controller message for each note hit (before midi spec standardized)
1982 KBD:
Note On 90 28 44 B0 00 00 (sends a note on plus a controller message each time)
Note Off 90 28 00 B0 00 00 (before standard spec, note off was just a note on with 00 velocity)
1990's KBD:
Note On 90 28 44 (same note as above example on channel 1) 44 would vary with each key hit strength
Note Off 80 28 44 (standard midi implements note off on channel 1 as 80) 44 would vary with each key lift up speed
Source code follows:
;------------------------------------------------------------------------------------
LIST P=16F628A
#include "P16F628A.INC"
__CONFIG _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_ON & _MCLRE_ON & _DATA_CP_OFF & _CP_OFF
; Variables
Loop1 equ 0x20
Loop2 equ 0x21
shLoop equ 0x22
UsartClear equ 0x23
tmpMidi equ 0x24
MIDI equ 0x25
channel equ 0x26
REG1 equ 0x27
REG2 equ 0x28
byte1 equ 0x29
byte2 equ 0x2a
byte3 equ 0x2b
ntonvalue equ 0x2c
ntoffvalue equ 0x2d
temp_w equ 0x2e
status_temp equ 0x2f
tempchar equ 0x30
buffer_head equ 0x31
buffer_read equ 0x32
constant buffer_begin=0x40 ; Buffer starts 0x40
constant buffer_end=0x60 ; Buffer end 0x60 32 midi values
org 0x0000
goto INIT
;-----Interrupt-----
org 0x0004 ; Interrupt start
intread
MOVWF temp_w ; backup W register
MOVF STATUS, W
MOVWF status_temp ; backup STATUS register
btfsc RCSTA,OERR ; Is overrun bit set?
call ClearOverrun ; Call clear OERR routine
btfss PIR1,RCIF
goto exitread
movf RCREG,W ; Get the midi character store in tempchar
movwf tempchar
movf buffer_head, W
movwf FSR
movf tempchar, W
movwf INDF
incf buffer_head,f
movlw buffer_end
xorwf buffer_head, W
btfss STATUS, Z
goto exitread
movlw buffer_begin
movwf buffer_head
exitread ; interrupt end
BCF PIR1, RCIF ; clear the RCIF flag bit
MOVF status_temp, W ; restore saved STATUS and W
MOVWF STATUS
SWAPF temp_w, F
SWAPF temp_w, W
RETFIE ; Return from interrupt
INIT
bcf STATUS,RP0
bcf STATUS,RP1
clrf PORTA
clrf PORTB
;movlw 0x00
;movwf VRCON
movlw 0x07
movwf CMCON
bsf STATUS,RP0 ; bank 1
clrf TRISB ; init all port B to outputs
bsf TRISB,1 ; Midi input on port B1
;bsf TRISB,0 ; button input on port B0
bsf TRISB,6 ; Channel Switch input 8
bsf TRISB,7 ; Channel Switch input 4
clrf TRISA ; init all port A to outputs
movlw 0xE0
movwf TRISA
movlw 0x07; use 07
movwf SPBRG ; 31250 baud at 4mhz in HighSpeed
BCF TXSTA,SYNC ; Setup Asyncronous Serial Port
BSF TXSTA,BRGH ; set highspeed bit
bsf TXSTA,TXEN ; enable async transmission
bcf STATUS,RP0 ; bank 0
bsf RCSTA,SPEN
bsf RCSTA,CREN
clrf UsartClear
movlw buffer_begin ; Init interrupt buffer
movwf buffer_head
movwf buffer_read
;----- Interrupt enable -----
BSF STATUS,RP0
BSF PIE1,RCIE ; Enable receive interrupts triggered from USART RX (RB1)
BCF STATUS,RP0
clrf PIR1 ; Clear interrupt flags
BSF INTCON,GIE ; enable all interrupts
BSF INTCON,PEIE
BCF STATUS,RP0 ; bank 0
settle
decfsz UsartClear,F
goto settle
MOVWF RCREG ; Clear receive register
MOVWF RCREG
MOVWF RCREG ; flush receive buffer
clrf tmpMidi
btfsc RCSTA,OERR ; Is overrun bit set?
call ClearOverrun ; Clear OERR routine
BSF PORTA,0 ; flash led on
CALL delay
BCF PORTA,0 ; flash led off
CALL shortdelay
BSF PORTA,1 ; flash led on
CALL delay
BCF PORTA,1 ; flash led off
CALL shortdelay
BSF PORTA,2 ; flash led on
CALL delay
BCF PORTA,2 ; flash led off
CALL shortdelay
BSF PORTA,3 ; flash led on
CALL delay
BCF PORTA,3 ; flash led off
CALL shortdelay
BSF PORTB,0 ; flash led on
CALL delay
BCF PORTB,0 ; flash led off
CALL shortdelay
BSF PORTB,3 ; flash led on
CALL delay
BCF PORTB,3 ; flash led off
CALL shortdelay
BSF PORTB,4 ; flash led on
CALL delay
BCF PORTB,4 ; flash led off
CALL shortdelay
BSF PORTB,5 ; flash led on
CALL delay
BCF PORTB,5 ; flash led off
CALL delay
goto MAIN
MAIN
CALL GETMIDI
btfsc tmpMidi,6 ; Skip System common
goto MAIN
MOVF ntonvalue, W
XORWF tmpMidi,W
BTFSC STATUS,Z
GOTO NoteOn
MOVF ntoffvalue, W
XORWF tmpMidi,W
BTFSC STATUS,Z
GOTO NoteOff
GOTO MAIN
NoteOn
CALL GETMIDI
MOVF tmpMidi,W
MOVWF byte2
;CALL GETMIDI
;MOVF tmpMidi,W
;MOVWF byte3
;btfsc STATUS,Z
;GOTO VelocityZero
MOVLW 0x24
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note0
MOVLW 0x25
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note1
MOVLW 0x26
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note2
MOVLW 0x27
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note3
MOVLW 0x28
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note4
MOVLW 0x29
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note5
MOVLW 0x2A
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note6
MOVLW 0x2B
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note7
GOTO MAIN
NoteOff
CALL GETMIDI
MOVF tmpMidi,W
MOVWF byte2
;CALL GETMIDI
;MOVF tmpMidi,W
;MOVWF byte3
;VelocityZero
MOVLW 0x24
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off0
MOVLW 0x25
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off1
MOVLW 0x26
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off2
MOVLW 0x27
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off3
MOVLW 0x28
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off4
MOVLW 0x29
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off5
MOVLW 0x2A
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off6
MOVLW 0x2B
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off7
GOTO MAIN
getChannel
MOVLW 0x00
BTFSC PORTA,7
call addone
BTFSC PORTA,6
call addtwo
BTFSC PORTB,7
call addfour
BTFSC PORTB,6
call addeight
; ANDLW 0x0F
MOVWF channel
MOVLW 0x90
IORWF channel,W
MOVWF ntonvalue
MOVLW 0x80
IORWF channel,W
MOVWF ntoffvalue
RETURN
addone
ADDLW 0x01
RETURN
addtwo
ADDLW 0x02
RETURN
addfour
ADDLW 0x04
RETURN
addeight
ADDLW 0x08
RETURN
Note0
BSF PORTA,0
GOTO MAIN
Note1
BSF PORTA,1
GOTO MAIN
Note2
BSF PORTA,2
GOTO MAIN
Note3
BSF PORTA,3
GOTO MAIN
Note4
BSF PORTB,0
GOTO MAIN
Note5
BSF PORTB,3
GOTO MAIN
Note6
BSF PORTB,4
GOTO MAIN
Note7
BSF PORTB,5
GOTO MAIN
Off0
BCF PORTA,0
GOTO MAIN
Off1
BCF PORTA,1
GOTO MAIN
Off2
BCF PORTA,2
GOTO MAIN
Off3
BCF PORTA,3
GOTO MAIN
Off4
BCF PORTB,0
GOTO MAIN
Off5
BCF PORTB,3
GOTO MAIN
Off6
BCF PORTB,4
GOTO MAIN
Off7
BCF PORTB,5
GOTO MAIN
; It reads the character from the buffer.
GETMIDI
call getChannel
movf buffer_read, W
xorwf buffer_head, W
btfsc STATUS, Z
goto GETMIDI
; Read character into tmpMidi
movf buffer_read, W
movwf FSR ; buffer_read to FSR
movf INDF, W ; buffer_read position
movwf tmpMidi ; store in tmpMidi
incf buffer_read, f ; move buffer_read
movlw buffer_end ; buffer_end in W
xorwf buffer_read, W
btfss STATUS, Z ; end of buffer check
return
movlw buffer_begin ;start at buffer top
movwf buffer_read ;reset read
return
ClearOverrun
bcf RCSTA,CREN ; Disable USART receive
bsf RCSTA,CREN ; Enable USART receive
; This clears OERR!
movf RCREG,W ; Flush receive register
movf RCREG,W
movf RCREG,W
clrf tmpMidi
return
ClearMem
movf RCREG,W ; Flush receive register
movf RCREG,W
movf RCREG,W
clrf tmpMidi
return
; delay routines
delay
MOVLW 0xf0
MOVWF Loop1
Outer MOVLW 0xc8
MOVWF Loop2
Inner
NOP
NOP
DECFSZ Loop2,F
GOTO Inner
DECFSZ Loop1,F
GOTO Outer
RETURN
shortdelay
MOVLW 0x80
MOVWF shLoop
shInner NOP
NOP
DECFSZ shLoop,F
GOTO shInner
RETURN
END
I have two MIDI keyboards. When I connect my 1982 Roland which sends 6 midi bytes per keyboard note hit 90 xx xx b0 xx xx my PIC project device works perfectly.
I can play all 8 test notes at once and all 8 leds turn on and off super fast and correctly in time with when each note is hit and released.
When I connect it to my newer 1990's Roland keyboard which only sends 90 xx xx for a note the device seems to miss fast played notes. I can't hit 2 notes at a time...if I go slowly it works ok, but anything faster than half a second (only when playing different notes!?) and it acts all sticky and gummed up and misses notes.
I can hit the same not repeatedly and my test device turns that one LED on and off as fast as I can hit the key following the note on and note off messages, but if I try to play 2 at a time or more...only one note (led) registers. If I slowly roll over the 8 notes taking up to half a second per note, it works as expected.
Why is the USART so STICKY in this case? I moved it from direct code to a buffered interrupt (following many online examples) and it exhibits the exact same behavior.
I am using a 16F628 20/p in 4MHZ (internal oscillator) mode.
I compile using GPASM on a mac, flashing the chip using PICkit 2 and a little custom board I made for the 16F628. The USART behaved the same way when I used to use an old DOS PC to compile and burn.
Is the sticky USART a known issue? What on earth am I doing wrong? Is the 1982 keyboard actually sending the MIDI bytes more slowly? I don't understand why this won't work. I have written tons of Machine Language MIDI input routines for 6502, 6510 etc with no issues at all. Must I code a bit-wise input for the 16F628? I thought the USART supported MIDI speed? Is the sluggish USART a known issue? Did I miss something important? 4Mhz should be plenty fast...and IS when I plug in the 1982 keyboard.
Please offer any assistance you can. Thanks.
ivlenk
MIDI Note examples showing what each keyboard is sending during my tests:
90 is note on channel 1, 28 is note # 28 hex (on the lower left of the musical keyboard), velocity is how hard the note was hit
[NoteOn Command and channel] [Note Number][Velocity of key hit]
B0 is a controller command followed by 2 data bytes. Old Roland sends a controller message for each note hit (before midi spec standardized)
1982 KBD:
Note On 90 28 44 B0 00 00 (sends a note on plus a controller message each time)
Note Off 90 28 00 B0 00 00 (before standard spec, note off was just a note on with 00 velocity)
1990's KBD:
Note On 90 28 44 (same note as above example on channel 1) 44 would vary with each key hit strength
Note Off 80 28 44 (standard midi implements note off on channel 1 as 80) 44 would vary with each key lift up speed
Source code follows:
;------------------------------------------------------------------------------------
LIST P=16F628A
#include "P16F628A.INC"
__CONFIG _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_ON & _MCLRE_ON & _DATA_CP_OFF & _CP_OFF
; Variables
Loop1 equ 0x20
Loop2 equ 0x21
shLoop equ 0x22
UsartClear equ 0x23
tmpMidi equ 0x24
MIDI equ 0x25
channel equ 0x26
REG1 equ 0x27
REG2 equ 0x28
byte1 equ 0x29
byte2 equ 0x2a
byte3 equ 0x2b
ntonvalue equ 0x2c
ntoffvalue equ 0x2d
temp_w equ 0x2e
status_temp equ 0x2f
tempchar equ 0x30
buffer_head equ 0x31
buffer_read equ 0x32
constant buffer_begin=0x40 ; Buffer starts 0x40
constant buffer_end=0x60 ; Buffer end 0x60 32 midi values
org 0x0000
goto INIT
;-----Interrupt-----
org 0x0004 ; Interrupt start
intread
MOVWF temp_w ; backup W register
MOVF STATUS, W
MOVWF status_temp ; backup STATUS register
btfsc RCSTA,OERR ; Is overrun bit set?
call ClearOverrun ; Call clear OERR routine
btfss PIR1,RCIF
goto exitread
movf RCREG,W ; Get the midi character store in tempchar
movwf tempchar
movf buffer_head, W
movwf FSR
movf tempchar, W
movwf INDF
incf buffer_head,f
movlw buffer_end
xorwf buffer_head, W
btfss STATUS, Z
goto exitread
movlw buffer_begin
movwf buffer_head
exitread ; interrupt end
BCF PIR1, RCIF ; clear the RCIF flag bit
MOVF status_temp, W ; restore saved STATUS and W
MOVWF STATUS
SWAPF temp_w, F
SWAPF temp_w, W
RETFIE ; Return from interrupt
INIT
bcf STATUS,RP0
bcf STATUS,RP1
clrf PORTA
clrf PORTB
;movlw 0x00
;movwf VRCON
movlw 0x07
movwf CMCON
bsf STATUS,RP0 ; bank 1
clrf TRISB ; init all port B to outputs
bsf TRISB,1 ; Midi input on port B1
;bsf TRISB,0 ; button input on port B0
bsf TRISB,6 ; Channel Switch input 8
bsf TRISB,7 ; Channel Switch input 4
clrf TRISA ; init all port A to outputs
movlw 0xE0
movwf TRISA
movlw 0x07; use 07
movwf SPBRG ; 31250 baud at 4mhz in HighSpeed
BCF TXSTA,SYNC ; Setup Asyncronous Serial Port
BSF TXSTA,BRGH ; set highspeed bit
bsf TXSTA,TXEN ; enable async transmission
bcf STATUS,RP0 ; bank 0
bsf RCSTA,SPEN
bsf RCSTA,CREN
clrf UsartClear
movlw buffer_begin ; Init interrupt buffer
movwf buffer_head
movwf buffer_read
;----- Interrupt enable -----
BSF STATUS,RP0
BSF PIE1,RCIE ; Enable receive interrupts triggered from USART RX (RB1)
BCF STATUS,RP0
clrf PIR1 ; Clear interrupt flags
BSF INTCON,GIE ; enable all interrupts
BSF INTCON,PEIE
BCF STATUS,RP0 ; bank 0
settle
decfsz UsartClear,F
goto settle
MOVWF RCREG ; Clear receive register
MOVWF RCREG
MOVWF RCREG ; flush receive buffer
clrf tmpMidi
btfsc RCSTA,OERR ; Is overrun bit set?
call ClearOverrun ; Clear OERR routine
BSF PORTA,0 ; flash led on
CALL delay
BCF PORTA,0 ; flash led off
CALL shortdelay
BSF PORTA,1 ; flash led on
CALL delay
BCF PORTA,1 ; flash led off
CALL shortdelay
BSF PORTA,2 ; flash led on
CALL delay
BCF PORTA,2 ; flash led off
CALL shortdelay
BSF PORTA,3 ; flash led on
CALL delay
BCF PORTA,3 ; flash led off
CALL shortdelay
BSF PORTB,0 ; flash led on
CALL delay
BCF PORTB,0 ; flash led off
CALL shortdelay
BSF PORTB,3 ; flash led on
CALL delay
BCF PORTB,3 ; flash led off
CALL shortdelay
BSF PORTB,4 ; flash led on
CALL delay
BCF PORTB,4 ; flash led off
CALL shortdelay
BSF PORTB,5 ; flash led on
CALL delay
BCF PORTB,5 ; flash led off
CALL delay
goto MAIN
MAIN
CALL GETMIDI
btfsc tmpMidi,6 ; Skip System common
goto MAIN
MOVF ntonvalue, W
XORWF tmpMidi,W
BTFSC STATUS,Z
GOTO NoteOn
MOVF ntoffvalue, W
XORWF tmpMidi,W
BTFSC STATUS,Z
GOTO NoteOff
GOTO MAIN
NoteOn
CALL GETMIDI
MOVF tmpMidi,W
MOVWF byte2
;CALL GETMIDI
;MOVF tmpMidi,W
;MOVWF byte3
;btfsc STATUS,Z
;GOTO VelocityZero
MOVLW 0x24
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note0
MOVLW 0x25
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note1
MOVLW 0x26
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note2
MOVLW 0x27
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note3
MOVLW 0x28
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note4
MOVLW 0x29
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note5
MOVLW 0x2A
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note6
MOVLW 0x2B
XORWF byte2,W
BTFSC STATUS,Z
GOTO Note7
GOTO MAIN
NoteOff
CALL GETMIDI
MOVF tmpMidi,W
MOVWF byte2
;CALL GETMIDI
;MOVF tmpMidi,W
;MOVWF byte3
;VelocityZero
MOVLW 0x24
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off0
MOVLW 0x25
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off1
MOVLW 0x26
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off2
MOVLW 0x27
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off3
MOVLW 0x28
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off4
MOVLW 0x29
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off5
MOVLW 0x2A
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off6
MOVLW 0x2B
XORWF byte2,W
BTFSC STATUS,Z
GOTO Off7
GOTO MAIN
getChannel
MOVLW 0x00
BTFSC PORTA,7
call addone
BTFSC PORTA,6
call addtwo
BTFSC PORTB,7
call addfour
BTFSC PORTB,6
call addeight
; ANDLW 0x0F
MOVWF channel
MOVLW 0x90
IORWF channel,W
MOVWF ntonvalue
MOVLW 0x80
IORWF channel,W
MOVWF ntoffvalue
RETURN
addone
ADDLW 0x01
RETURN
addtwo
ADDLW 0x02
RETURN
addfour
ADDLW 0x04
RETURN
addeight
ADDLW 0x08
RETURN
Note0
BSF PORTA,0
GOTO MAIN
Note1
BSF PORTA,1
GOTO MAIN
Note2
BSF PORTA,2
GOTO MAIN
Note3
BSF PORTA,3
GOTO MAIN
Note4
BSF PORTB,0
GOTO MAIN
Note5
BSF PORTB,3
GOTO MAIN
Note6
BSF PORTB,4
GOTO MAIN
Note7
BSF PORTB,5
GOTO MAIN
Off0
BCF PORTA,0
GOTO MAIN
Off1
BCF PORTA,1
GOTO MAIN
Off2
BCF PORTA,2
GOTO MAIN
Off3
BCF PORTA,3
GOTO MAIN
Off4
BCF PORTB,0
GOTO MAIN
Off5
BCF PORTB,3
GOTO MAIN
Off6
BCF PORTB,4
GOTO MAIN
Off7
BCF PORTB,5
GOTO MAIN
; It reads the character from the buffer.
GETMIDI
call getChannel
movf buffer_read, W
xorwf buffer_head, W
btfsc STATUS, Z
goto GETMIDI
; Read character into tmpMidi
movf buffer_read, W
movwf FSR ; buffer_read to FSR
movf INDF, W ; buffer_read position
movwf tmpMidi ; store in tmpMidi
incf buffer_read, f ; move buffer_read
movlw buffer_end ; buffer_end in W
xorwf buffer_read, W
btfss STATUS, Z ; end of buffer check
return
movlw buffer_begin ;start at buffer top
movwf buffer_read ;reset read
return
ClearOverrun
bcf RCSTA,CREN ; Disable USART receive
bsf RCSTA,CREN ; Enable USART receive
; This clears OERR!
movf RCREG,W ; Flush receive register
movf RCREG,W
movf RCREG,W
clrf tmpMidi
return
ClearMem
movf RCREG,W ; Flush receive register
movf RCREG,W
movf RCREG,W
clrf tmpMidi
return
; delay routines
delay
MOVLW 0xf0
MOVWF Loop1
Outer MOVLW 0xc8
MOVWF Loop2
Inner
NOP
NOP
DECFSZ Loop2,F
GOTO Inner
DECFSZ Loop1,F
GOTO Outer
RETURN
shortdelay
MOVLW 0x80
MOVWF shLoop
shInner NOP
NOP
DECFSZ shLoop,F
GOTO shInner
RETURN
END
Last edited: