マイコンを使った工作
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