マイコンを使った工作
midimon.asm
最終更新:
匿名ユーザー
-
view
midimon.asm
;**********************************************************************
; Template file assembled with MPLAB V3.99.18 and MPASM V2.15.06. *
;**********************************************************************
; *
; MIDI monitor program *
; *
;**********************************************************************
; *
; Filename: midimon.asm *
; Date: 2001/03/24 *
; File Version: *
; *
; Author: Chuck *
; Company: *
; *
; *
;**********************************************************************
; *
; Notes: *
; MIDI monitor program *
; receive MIDI & display on 16x2 LCD w/ ISR receiving *
; *
; *
;**********************************************************************
list p=16F84a ; list directive to define processor
#include ; processor specific variable definitions
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC
__IDLOCS H'0100' ;V1.00, 24 MAR 2001
; '__CONFIG' directive is used to embed configuration data within .asm file.
; The lables following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.
;**********************************************************************
; Definition to switches, ports, flags, registar files *
;**********************************************************************
;
; Debugging information
#DEFINE DEBUG
#UNDEFINE DEBUG
; Presentation switch
; ifdef RICH, "strings" expression comes.
; ifndef RICH, "Hex format" expression selected
#DEFINE RICH
; Setting for LCD control
; modified Akizuki kit implementation
lcdRW EQU 1 ; RA1
lcdRS EQU 2 ; RA2
lcdE EQU 3 ; RA3
; MIDI in
midiIN EQU 4 ; RA4
#DEFINE MIDIIN PORTA,midiIN
; RESULT Flag
#DEFINE RXBUSY RESULT,2 ; bit2 RX busy
#DEFINE ERROR RESULT,4 ; bit4 receive error
;***** VARIABLE DEFINITIONS
w_temp EQU 0x0C ; variable used for context saving
status_temp EQU 0x0D ; variable used for context saving
fsr_temp EQU 0x0E ; variable used for context saving
CNT EQU 0x0F ; for 10u, 100usec counter use
CNT5m EQU 0x10 ; for 5msec counter use
LCDBUF EQU 0x11 ; LCD command buffer
RESULT EQU 0x12 ; serial I/F Result flag
CNT9BIT EQU 0x13 ; MIDI: 1start, 8data, 1stop bits
RXBUF EQU 0x14 ; receiving buffer
SWTMP EQU 0x15 ; temp. for swap
MIDIBUF EQU 0x16 ; MIDI data buffer
STR_CNT EQU 0x17 ; counter for strings output
STR_PTR EQU 0x18 ; pointer to strings
WAITCNT EQU 0x19 ; display timer counter (strings mode)
; display position marker (hex mode)
CLRCNT EQU 0x1A ; counter for LCD clear
; 0x30 - 0x4F MIDI receive FIFO (circular buffer)
#IFNDEF DEBUG
READP EQU 0x1E ; read pointer
WRITEP EQU 0x1F ; write pointer
BUFBASE EQU 0x20 ; MIDI buffer top address
BUFEND EQU 0x50 ;
#ELSE
READP EQU 0x1E ; read pointer
WRITEP EQU 0x1F ; write pointer
BUFBASE EQU 0x20 ; MIDI buffer top address
BUFEND EQU 0x30 ;
#ENDIF
;**********************************************************************
; EEPROM initial value *
;**********************************************************************
;
ORG 0x2100
#IFDEF RICH
de "MIDI monitor (strings) by S.Takagi",0
#ELSE
de "MIDI monitor (hex code) by S.Takagi",0
#ENDIF
;**********************************************************************
; Program code starts here *
;**********************************************************************
;
ORG 0x000 ; processor reset vector
goto main ; go to beginning of program
;**********************************************************************
; Interrupt Service Routine *
;**********************************************************************
ORG 0x004 ; interrupt vector location
;
ISR
BTFSS INTCON,T0IF ; TMR0 interrupt?
RETFIE ; if not, return
BCF INTCON,T0IF ; reset T0IF bit
PUSH movwf w_temp ; save off current W register contents
movf STATUS,w ;
movwf status_temp ; save off contents of STATUS register
MOVF FSR,W ;
MOVWF fsr_temp ; save off FSR register content
BCF STATUS,RP0 ; surely switching to bank0
; isr code can go here or be located as a call subroutine elsewhere
BTFSC RXBUSY ; receive process on-going?
GOTO RXDATA ; yes, proc data bits
GOTO STARTBIT ; no. new data coming. proc start-bit
POP MOVF fsr_temp,W ;
MOVWF FSR ; restore pre-FSR register content
movf status_temp,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
retfie ; return from interrupt
;**********************************************************************
; *
; Receiving MIDI data (part of ISR) *
; *
; MIDI physical layer: *
; 31.25kbps, 1-start bit(Low), 8-data bit, 1-stop bit(Hi) *
; MIDI cycle 31.25kbps => 80cycles (10MHz external clock) *
; *
;**********************************************************************
;
STARTBIT
CALL TIME10u ; wait 10usec
BTFSC MIDIIN ; double check MIDI-IN port
GOTO NOISE ; would be noise
BCF ERROR
BSF RXBUSY ; Start bit comes in
CLRF RXBUF ; clear receive buffer
;
BSF STATUS,RP0 ; bank1
MOVLW b'10011000' ; b'1000-1000' PSA:WDT, 1:1
MOVWF OPTION_REG ; use internal clock
BCF STATUS,RP0 ; bank0
MOVLW d'255'-d'46' ;
MOVWF TMR0 ; pre scaler WDT, 1:1
MOVLW 9 ; 8bit data + 1stop bit counter
MOVWF CNT9BIT ;
; [start bit edge -> TMR0 count up = 53]
GOTO POP
NOISE
MOVLW 0xFF ; wait start bit again
MOVWF TMR0
GOTO POP ; escaping ISR
RXDATA
MOVLW d'255'-d'62' ; 62 = 80 - 17
; 80 => 32usec
; 17 => cycles from ISR->TMR0cnt
MOVWF TMR0 ; pre scaler WDT, 1:1
DECFSZ CNT9BIT,F ; dec. counter
GOTO NEXTRXBIT ;
STOPBIT ; stop bit processing
BTFSS MIDIIN ; make sure STOP bit
BSF ERROR
BCF RXBUSY
MOVF WRITEP,W
MOVWF FSR
MOVF RXBUF,W
MOVWF INDF
INCF WRITEP,W ; wp++
SUBLW BUFEND ; wp > buffer upper bound?
BTFSC STATUS,Z ;
GOTO WADR0 ; yes
INCF WRITEP,W ; no
GOTO SSKIP
WADR0 MOVLW BUFBASE ; write address rewind
SSKIP MOVWF WRITEP
;
BSF STATUS,RP0 ; bank1
MOVLW b'10111000' ; b'1011-1000' PSA:WDT, 1:1
MOVWF OPTION_REG ; use T0CKI, down edge
BCF STATUS,RP0 ; bank0
MOVLW 0xFF
MOVWF TMR0 ; set full count to TMR0
;
GOTO POP
NEXTRXBIT
BCF STATUS,C ; clear Carry
BTFSC MIDIIN ; test midiIN
BSF STATUS,C ; set Carry
RRF RXBUF,F ;
; Overhead ISR->data sampling = 22
GOTO POP
;************** remaining code goes here **************
;**********************************************************************
; "PCL" related subroutines placed here (before 0x0FF) *
; because 'addwf PCL' is limited to 8bit result *
;**********************************************************************
;
;***** return ASCII code *****
ASCII
ANDLW 0x0F
addwf PCL,F
DT "0123456789ABCDEF"
; no RETURN needed
#IFDEF RICH
;------------------------------------------------ Strings expression
;***** return ASCII code *****
;CH_ASCII
; MOVF MIDIBUF,W
; ANDLW 0x0F
; addwf PCL,F
; DT "0123456789ABCDEF"
; no RETURN needed
;***** return ASCII code *****
CH_ASCII2
addwf PCL,F
DT " 1 2 3 4 5 6 7 8 910111213141516"
; no RETURN needed
;***** return character *****
GET_CHAR
MOVWF PCL
;
STR_TBL ; "0123456789ABCDEF"
STR_NOTEON DT "Note On ", 0
STR_NOTEOFF DT "Note Off ", 0
STR_POLYPRE DT "Poly Pres.", 0
STR_CTRLCHG DT "Ctrl Chnge", 0
STR_PROGCHG DT "Prog Chnge", 0
STR_CHPRES DT "Ch. Press.", 0
STR_PITBEND DT "Pitch Bend", 0
STR_SYSTEM DT "System RTM", 0
; no RETURN needed
GET_STR
SWAPF MIDIBUF,W
ANDLW b'00000111'
ADDWF PCL,F
RETLW STR_NOTEON ; 0 8x
RETLW STR_NOTEOFF ; 1 9x
RETLW STR_POLYPRE ; 2 Ax
RETLW STR_CTRLCHG ; 3 Bx
RETLW STR_PROGCHG ; 4 Cx
RETLW STR_CHPRES ; 5 Dx
RETLW STR_PITBEND ; 6 Ex
RETLW STR_SYSTEM ; 7 Fx
;***** display MIDI status & parameter *****
;
; 0123456789ABCEDF <- LCD DDRAM address
; ----------------
; Ch:__ Prog Chnge
; C0 14
; ----------------
;
DISPMIDI
MOVLW d'100' ; display timer 500msec
MOVWF WAITCNT
WAIT CALL TIME5m
DECFSZ WAITCNT,F
GOTO WAIT
MOVLW 'C'
CALL LCD_DATA
MOVLW 'h'
CALL LCD_DATA
MOVLW ':'
CALL LCD_DATA
; MOVLW 0x83 ; DDRAM addr = 03
; CALL LCD_CMD
; Disp channel
RLF MIDIBUF,W ; x2 MIDI ch.
ANDLW b'00011110'
CALL CH_ASCII2 ; get channel char (10^0)
CALL LCD_DATA
BSF STATUS,C
RLF MIDIBUF,W ; x2 + 1 MIDI ch.
ANDLW b'00011111'
CALL CH_ASCII2 ; get channel char (10^1)
CALL LCD_DATA
; Disp message strings
MOVLW 0x86 ; DDRAM addr = 06
CALL LCD_CMD
CALL DISP_MSG ; display MIDI message
MOVLW 0xC0 ; clean up 2nd line
CALL LCD_CMD
MOVLW d'16'
MOVWF CLRCNT
CLRS MOVLW ' '
CALL LCD_DATA
DECFSZ CLRCNT,F
GOTO CLRS
MOVLW 0xC0 ; put disp pointer = DDRAM 0x40
CALL LCD_CMD
RETURN
;***** Display MIDI message *****
DISP_MSG
CALL GET_STR ; get string table top address
MOVWF STR_PTR ; set strings pointer to the top
MOVLW d'10' ; 10 letters display counter
MOVWF STR_CNT
NEXTCHAR
MOVF STR_PTR,W
CALL GET_CHAR ; get character from the table
ADDLW 0 ; check end of strings
BTFSC STATUS,Z ;
RETURN ;
CALL LCD_DATA
INCF STR_PTR,F
DECFSZ STR_CNT,F
GOTO NEXTCHAR
RETURN
;-------------------------------------------------- end of selection
#ENDIF
;**** Get MIDI data from FIFO *****
GETMIDI
MOVF READP,W ; no, get data from FIFO
MOVWF FSR ;
MOVF INDF,W ;
MOVWF MIDIBUF ; store MIDI buffer
INCF READP,W ; rp++
SUBLW BUFEND ; rp > buffer upper bound
BTFSC STATUS,Z ; zero?
GOTO RADR0 ; yes
INCF READP,W ; no
GOTO GSKIP
RADR0 MOVLW BUFBASE ; write address rewind
GSKIP MOVWF READP
;
RETURN
;
;**********************************************************************
; *
; MAIN *
; *
;**********************************************************************
main
; Initialization
BCF INTCON,GIE ; make sure disalbe Interrupt
CALL PORT_INIT ; port A/B initialization
CALL LCD_INIT ; LCD initialization
CALL LCD_CLR
CALL MIDI_INIT ; MIDI related parameter init.
CALL ISR_INIT ; Iunterrupt related parameter init.
BSF INTCON,GIE ; enable interrupt
#IFDEF RICH
;------------------------------------------------ Strings expression
INIT_OUT
MOVLW 0x0C ; Display on (no Cursor,Blink)
CALL LCD_CMD
INFINITE
MOVF READP,W ; compare READ & WRITE pointer
SUBWF WRITEP,W
BTFSC STATUS,Z ; READP == WRITEP ?
GOTO INFINITE ; yes, then loop
CALL GETMIDI ; get MIDI data in FIFO
BTFSC MIDIBUF,7 ; test MSB
CALL DISPMIDI ; it's a MIDI status
SWAPF MIDIBUF,W ;
ANDLW 0x0F ; display upper 4bit
CALL ASCII
CALL LCD_DATA
MOVF MIDIBUF,W
ANDLW 0x0F ; display lower 4bit
CALL ASCII
CALL LCD_DATA
MOVLW ' ' ; put space
CALL LCD_DATA
GOTO INFINITE
#ELSE
;----------------------------------------------- HEX code expression
INIT_OUT
MOVLW 0x0F ; Display on (Cursor,Blink)
CALL LCD_CMD
MOVLW 0x80 ; DDRAM addr = 00
CALL LCD_CMD
CLRF WAITCNT ; display positioner
INFINITE
MOVF READP,W
SUBWF WRITEP,W
BTFSC STATUS,Z ; READP == WRITEP ?
GOTO INFINITE ; yes, then loop
CALL GETMIDI ; get MIDI data in FIFO
SWAPF MIDIBUF,W ; disp MIDI data (hex form)
ANDLW 0x0F ; upper hex
CALL ASCII
CALL LCD_DATA
MOVF MIDIBUF,W ; lower hex
ANDLW 0x0F
CALL ASCII
CALL LCD_DATA
MOVLW ' ' ; put space
CALL LCD_DATA
INCF WAITCNT,F
MOVF WAITCNT,W
SUBLW d'5' ; if display pos = 5
BTFSC STATUS,Z
GOTO INF5 ; move cursor to second line
MOVF WAITCNT,W
SUBLW d'10' ; if display pos = 10
BTFSC STATUS,Z
GOTO INF10 ; move cursor to first line
GOTO INFINITE
INF5 ; move cursor to second line
MOVLW h'C0' ; DDRAM <- 0x40
CALL LCD_CMD
GOTO INFINITE
INF10 ; move cursor to first line
MOVLW h'80' ; DDRAM <- 0x00
CALL LCD_CMD
CLRF WAITCNT ; and clear positioner
GOTO INFINITE
#ENDIF
;-------------------------------------------------- end of selection
;**********************************************************************
; Followings are Initializer routines *
;**********************************************************************
;
;**** ISR Initialize *****
ISR_INIT
CLRF TMR0
CLRWDT
BSF STATUS,RP0 ; bank1
MOVLW b'10111000' ; b'1011-1000' PSA:WDT, 1:1
MOVWF OPTION_REG ; use T0CKI, down edge
BCF STATUS,RP0 ; bank0
MOVLW 0xFF
MOVWF TMR0 ; set full count to TMR0
BCF INTCON,T0IF ; reset TMRO INT flag
BSF INTCON,T0IE ; enable TMR0 interrupt
RETURN
;**** MIDI related status INIT *****
MIDI_INIT
CLRF RESULT ; clear comm RESULT flag
MOVLW BUFBASE ; init pointers
MOVWF READP
MOVWF WRITEP
RETURN
;**** PORTA/B Initialize *****
PORT_INIT
BSF STATUS,RP0 ; bank1
MOVLW b'00000000' ; RB[7:0] output
MOVWF TRISB
MOVLW b'00010000' ; RA4 input
MOVWF TRISA
BCF STATUS,RP0 ; bank0
CLRF PORTA
CLRF PORTB
RETURN
;**********************************************************************
; LCD related subroutines *
;**********************************************************************
;
;**** LCD clear command *****
LCD_CLR
MOVLW 0x01 ; LCD CLEAR command
CALL LCD_CMD
RETURN
;**** LCD command out *****
LCD_CMD
MOVWF LCDBUF ; Store command data
ANDLW 0F0H ; output upper 4bits
; MOVWF PORTB ; output to RB[7:4]
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BCF PORTA,lcdRW ; R/~W -> 0
BCF PORTA,lcdRS ; RS -> 0
BSF PORTA,lcdE ; E high/output strobe
BCF PORTA,lcdE ; E low
SWAPF LCDBUF,W ;
ANDLW 0F0H ; output lower 4bits
; MOVWF PORTB ; output to RB[7:4]
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BSF PORTA,lcdE ; E high/strobe
BCF PORTA,lcdE ; E low
CALL LCD_BUSY ; wait while Busy
;
RETURN
;**** LCD Data Write ****
LCD_DATA
MOVWF LCDBUF ; store display data
ANDLW 0F0H ; xfer upper 4bits
; MOVWF PORTB
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BCF PORTA,lcdRW ; R/~W -> 0
BSF PORTA,lcdRS ; RS -> 1
BSF PORTA,lcdE ; E high/strobe
BCF PORTA,lcdE ; E low
SWAPF LCDBUF,W ; get lower 4bits
ANDLW 0xF0 ; xfer lower 4bits
; MOVWF PORTB ; PORTB[7:4]
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BSF PORTA,lcdE ; E high/strobe
BCF PORTA,lcdE ; E low
CALL LCD_BUSY ; wait while Busy
;
RETURN
;**** LCD Busy Check ************
LCD_BUSY
CLRF LCDBUF ; clear data buffer
BSF STATUS,RP0 ; bank1
; BSF OPTION_REG,7 ; PORTB pull-up off
; MOVLW 0xFE ; PORTB[7:1] input
MOVLW b'00001111' ; PORTB[3:0] input
MOVWF TRISB
BCF STATUS,RP0 ; bank0
BCF PORTA,lcdRS ; RS -> 0
BSF PORTA,lcdRW ; R/~W -> 1, Busy In mode
BSF PORTA,lcdE ; E high/strobe
MOVF PORTB,W ; get upper data to RB[3:0]
BCF PORTA,lcdE ; E low
; ANDLW 0xF0 ; mask out upper 4bits
MOVWF SWTMP
SWAPF SWTMP,W ; RB[3:0] -> [7:4]
ANDLW 0xF0 ; get upper 4bits
MOVWF LCDBUF ; store temporarily
BSF PORTA,lcdE ; E high/strobe
MOVF PORTB,W ; get lower 4bits to RB[3:0]
BCF PORTA,lcdE ; E low
; ANDLW 0x0F0 ; mask out upper 4bits
MOVWF SWTMP
SWAPF SWTMP,W ; RB[3:0] -> [7:4]
ANDLW 0xF0 ; mask out upper 4bits
MOVWF SWTMP
SWAPF SWTMP,W
IORWF LCDBUF,F ; combine upper & lower
BTFSC LCDBUF,7 ; check busy flag
GOTO LCD_BUSY ; if busy, try again
BCF PORTA,lcdRW ; R/~W -> 0 (output mode)
BSF STATUS,RP0 ; bank1
; MOVLW 0x0E ; output RB7,6,5,4,0
MOVLW b'00000000' ; output RB[7:0]
MOVWF TRISB ; PORTB mode set
BCF STATUS,RP0 ; bank0
RETURN
;**** Initialize *****
LCD_INIT
CALL TIME5m ; wait 15msec
CALL TIME5m
CALL TIME5m
MOVLW 0x30 ; 8bit mode setting control
; MOVWF PORTB
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BCF PORTA,lcdRW ; R/~W -> 0
BCF PORTA,lcdRS ; RS -> 0
BSF PORTA,lcdE ; E high/strobe
BCF PORTA,lcdE ; E low
CALL TIME5m ; wait over 4.1msec
MOVLW 0x30 ; 8bit mode setting again
; MOVWF PORTB
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BCF PORTA,lcdRW ; R/~W -> 0
BCF PORTA,lcdRS ; RS -> 0
BSF PORTA,lcdE ; E high/strobe
BCF PORTA,lcdE ; E low
CALL TIME100u ; wait over 100usec
MOVLW 0x30 ; 8bit mode setting
; MOVWF PORTB
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BCF PORTA,lcdRW ; R/~W -> 0
BCF PORTA,lcdRS ; RS -> 0
BSF PORTA,lcdE ; E high/strobe
BCF PORTA,lcdE ; E low
CALL TIME100u ; wait 100usec
MOVLW 0x20 ; 4bit mode setting
; MOVWF PORTB ;
MOVWF SWTMP
SWAPF SWTMP,W ; [7:4] -> [3:0]
MOVWF PORTB ; output to RB[3:0]
BCF PORTA,lcdRW ; R/~W -> 0
BCF PORTA,lcdRS ; RS -> 0
BSF PORTA,lcdE ; E high/strobe
BCF PORTA,lcdE ; E low
CALL TIME100u ; wait 100usec
;After this, works 4bit mode, Busy flag available
MOVLW 0x2C ; Function Set (2lines,5x10Dot)
CALL LCD_CMD
MOVLW 0x08 ; Display off (no Cursor,Blink)
CALL LCD_CMD
MOVLW 0x0C ; Display on (no Cursor,Blink)
CALL LCD_CMD
MOVLW 0x06 ; Entry Mode Set(Incremental)
CALL LCD_CMD
RETURN
;**********************************************************************
; TIMER subroutines *
;**********************************************************************
; note: register CNT is commonly used in TIME10u, TIME100u.
; so, DO NOT CALL TIME10u, TIME100u hybridly
;
;***** 10usec timer w/ 10MHz clock *****
;
TIME10u
MOVLW 7 ; 1 cycle
MOVWF CNT ; 1
NOP ; 1
LOOP10u ;
DECFSZ CNT,F ; 1*6+2
GOTO LOOP10u ; 2*6
RETURN ; 2 total 25 cycles
;
;***** 100usec timer w/ 10MHz clock *****
;
TIME100u
MOVLW 0x52 ; 1 cycle, 52h = 82
MOVWF CNT ; 1
NOP ; 1
LOOP100u ;
DECFSZ CNT,F ; 1*81+2
GOTO LOOP100u ; 2*81
RETURN ; 2 total 250 cycles
;
;***** 5msec timer w/ 10MHz clock *****
;
TIME5m
MOVLW d'49' ; 1
MOVWF CNT5m ; 1
GOTO $+1 ; 2
LOOP5m ;
CALL TIME100u ; 49*(250+2)
DECFSZ CNT5m,F ; 48+2
GOTO LOOP5m ; 2*48
RETURN ; 2 total 12500 cycles
END