Jon Wilder
Active Member
Here is a simple MIDI message handler for PIC18 written in assembly. It's a receive routine that is meant to be "included" as a module within an interrupt handler. Currently it will receive both Program Change and Control Change messages. It does not support real time or running status type messages.
After the last byte of each message is received, the UART is disabled and a software flag known as "MSGRDY" is set. This flag should be read in main in order for main to know when to process the received message.
Here is the source file -
And here is the associated include file to add to the very top of your main file -
After the last byte of each message is received, the UART is disabled and a software flag known as "MSGRDY" is set. This flag should be read in main in order for main to know when to process the received message.
Here is the source file -
Code:
;================================================================================
; Build Date: October 9th, 2015
; MIDI Message Receive Handler for PIC18 PICmicro Devices
; By Jon Wilder
;
; (c) 2015 Jon Wilder
;================================================================================
;================================================================================
; GNU General Public License v3
;
; MIDI Message Receive Handler for PIC18 PICmicro Devices
;
; Copyright (c) 2015 Jon Wilder
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
;================================================================================
;================================================================================
; Important Instructions
;
; In order to use this source file, you must include the associated header file
; in the beginning of your main source file as it contains very important label
; definitions used by both midiuart.asm as well as your main source file.
;
; You must also create a main.inc file and place it in your main project directory.
; In this file you can define all of your own custom register name labels as well
; as redefine certain constants which override default constants supplied with this
; source code.
;
; You should use the following directory tree -
;
; MPLABXProjects
; /PIC18
; /ASM
; /include /projects /sources
; <.inc files> <project directories> <.asm files>
;
; As an example, assuming the aforementioned directory tree is used, if we were to
; use the PIC18F46K22, we would include it in the following manner -
;
; list p=18F46K22, r=dec
; include <p18f46k22.inc> ;.inc file supplied by MPLAB
; include main.inc ;create this file and place it in your
;main project directory
; include ../../include/midiuart.inc
;
;
; You can then include the source file right before the "end" directive in your
; main .asm soure file in the following manner -
;
; include "../../sources/midiuart.asm"
;
; Failure to include midiuart.inc in your main source file as shown above will
; result in "Symbol not defined" assembler errors.
;================================================================================
;<label:>
; <inst> <operand> <comments>
;================================================================================
; UART Initialization for MIDI Operation
;================================================================================
; This software function will initialize the on chip UART for MIDI operation.
; Pin assignable variables are defined by default to the following -
;
; _MIDI_TxD_ANSEL - ANSELC,ANSC6
; _MIDI_RxD_ANSEL - ANSELC,ANSC7
; _MIDI_TxD_TRIS - TRISC,RC6
; _MIDI_RxD_TRIS - TRISC,RC7
;
; For most 28 pin and 40 pin devices, these settings will suffice. However, if your
; PIC's TxD & RxD pins either reside on different pins OR you wish to use UART2 for
; the MIDI handling (for PIC's so equipped with a second UART), you must redefine
; these variables in your main.inc file.
;
; There is no reason to modify midiuart.inc to accomplish this. You can override the
; settings by simply defining the aforementioned variables to different ANSEL and
; TRIS pin assignments within your main.inc file.
uartInit:
clrf MIDIFLAGS,BANKED ;clear all MIDI receive flags
clrf MIDI_TEMP,BANKED ;clear MIDI temp register
clrf DATA1_RX,BANKED ;clear data1 register
clrf DATA2_RX,BANKED ;clear data2 register
movlw _UART_BRG ;set baud rate
movwf SPBRG,ACCESS
movlw b'00100100' ;async serial, high speed baud clock, TxD enable
movwf TXSTA,ACCESS
movlw b'10010000' ;serial port enable, continuous receive enable
movwf RCSTA,ACCESS
movlb 0xF ;bank 15
bcf _MIDI_TxD_ANSEL,BANKED ;RC6 digital I/O mode
bcf _MIDI_RxD_ANSEL,BANKED ;RC7 digital I/O mode
movlb 0x0 ;bank 0
bsf _MIDI_TxD_TRIS,ACCESS ;hand control of RC6 to TxD
bsf _MIDI_RxD_TRIS,ACCESS ;hand control of RC7 to RxD
bsf PIE1,RCIE,ACCESS ;enable UART receive interrupts
return
;================================================================================
; Begin MIDI UART Receive
;================================================================================
; First, we read the UART receive buffer into a temp register to clear the UART
; receive interrupt flag. We then check to see if a valid MIDI status byte was
; received on a previous pass. If a valid status byte has already been received
; (indicated by the state of the MIDISTAT bit in the MIDIFLAGS register), we then
; jump down to data1Rx to receive the first MIDI data byte. If not, then we continue
; on to receive and decode the MIDI status byte.
uartRx:
movlb _MIDI_BANK ;select MIDI bank in GPR
movff RCREG,MIDI_TEMP ;yes, read UART receive buffer
btfsc MIDIFLAGS,MIDISTAT,BANKED ;has status byte been received yet?
goto data1Rx ;yes, receive data byte 1
; Since we're here, this indicates that a valid status byte has not been received
; yet. So now we'll check bit 7 of the received byte to see if it's high, indicating
; that the byte is a status byte. If it is not high, then we will ignore the byte
; and exit the MIDI handler.
btfss MIDI_TEMP,STATBIT,BANKED ;MIDI status byte?
goto uartRx_Exit ;no, exit
; So the previous bit test revealed that our received byte is a MIDI status byte.
; Now we will compare the byte to the device's assigned MIDI channel and supported
; MIDI message types, starting with program change messages.
midiPCRx:
movf MIDI_CHAN,W,BANKED ;fetch MIDI channel assignment
addlw 0xC0 ;add 0xC0 to MIDI channel
xorwf MIDI_TEMP,W,BANKED ;XOR received byte with this value
btfss STATUS,Z,ACCESS ;is it a program change message?
goto midiCCRx ;no, check for CC message type
bcf MIDIFLAGS,CC,BANKED ;yes, clear CC message type flag
goto statFlagSet
; If we made it here, this reveals that either the message type or the MIDI channel
; of our status byte did not match up. Now we will check our status byte against
; the device's MIDI channel and see if it is a control change message. If either
; of these two do not match, we will ignore the byte and exit the MIDI handler.
; If, however, it is determined that the status byte is a valid control change
; message, we will set the CC flag in the MIDIFLAGS register, then continue on to the
; statFlagSet function, where we will also set the MIDISTAT flag in the MIDIFLAGS
; register, then exit.
midiCCRx:
movf MIDI_CHAN,W,BANKED ;fetch MIDI channel assignment
addlw 0xB0 ;add 0xB0 to MIDI channel
xorwf MIDI_TEMP,W,BANKED ;XOR received byte with this value
btfss STATUS,Z,ACCESS ;is it a control change message?
goto uartRx_Exit ;no, exit
bsf MIDIFLAGS,CC,BANKED ;yes, set CC message type flag
; Upon determining that our received MIDI status byte is a valid status byte, we
; will now set the MIDISTAT flag in the MIDIFLAGS register. As long as this bit
; is set, the code will jump to data1Rx to receive the first data byte upon entering
; the MIDI receive handler and reading the received byte from the receive buffer.
;
;
; Once we set this bit, we exit the handler and go back to the main function until
; the next byte is received.
statFlagSet:
bsf MIDIFLAGS,MIDISTAT ;yes, set status byte received flag
goto uartRx_Exit
; Upon entering the MIDI handler, the code will jump here after reading the UART
; receive buffer, then determining if the MIDISTAT flag in the MIDIFLAGS register
; is set. We will also check the D1RCV flag in MIDIFLAGS and determine if it is
; set, indicating that we've already received the 1st data byte. If it is high,
; we will jump to data2Rx and receive the 2nd data byte (control change messages
; only), else we will continue with normal code flow.
data1Rx:
btfsc MIDIFLAGS,D1RCV,BANKED ;has 1st data byte been received?
goto data2Rx ;yes, receive data byte 2
; Data byte 1 has not yet been received. So we will copy our received byte from
; MIDI_TEMP to DATA1, where it can be accessed in the main function.
movff MIDI_TEMP,DATA1_RX ;no, store data byte 1
; Here we check to see if the CC flag in MIDIFLAGS is set, telling us that we are
; receiving a control change message. If it is not set, then we will proceed to
; messageComplete. However, if it is set, then we will set the D1RCV flag in
; MIDIFLAGS to indicate that we've received the first data byte in the control
; change message.
btfss MIDIFLAGS,CC ;are we receiving a CC message?
goto messageComplete ;no, jump to message complete routine
bsf MIDIFLAGS,D1RCV ;yes,set data 1 received flag
goto uartRx_Exit ;exit
; If we are receiving a control change message and the first data byte has already
; been received, we will now write the second data byte from MIDI_TEMP to DATA2,
; where it can be accessed in the main function.
data2Rx:
movff MIDI_TEMP,DATA2_RX ;receive data byte 2
; We have now completed receiving our MIDI message. Here we will clear all of the
; flags in MIDIFLAGS, but set the MSGRDY flag, which will notify the main function
; that it has a MIDI message waiting to be processed.
; We will also disable the UART here. This prevents any further MIDI messages from
; being received until the UART is enabled in main. This allows the main function
; to process the received message without further incoming MIDI messages interfering
; with the process.
; Once main has completed processing the received MIDI message, the message is
; discarded and main will then re-enable the UART and listen for further MIDI
; messages.
messageComplete:
bcf MIDIFLAGS,MIDISTAT,BANKED ;clear MIDISTAT flag
bcf MIDIFLAGS,D1RCV,BANKED ;clear D1RCV flag
bsf MIDIFLAGS,MSGRDY,BANKED ;set MSGRDY flag
bcf RCSTA,CREN ;disable UART receiver
uartRx_Exit:
return
And here is the associated include file to add to the very top of your main file -
Code:
;================================================================================
; Build Date: October 9th, 2015
; MIDI Message Receive Handler for PIC18 PICmicro Devices
; Header File
; By Jon Wilder
;
; (c) 2015 Jon Wilder
;================================================================================
;================================================================================
; GNU General Public License v3
;
; MIDI Message Receive Handler for PIC18 PICmicro Devices
;
; Copyright (c) 2015 Jon Wilder
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
;================================================================================
;================================================================================
; Important Instructions
;
; This header file is required by midiuart.asm. It must be included in your main
; source file. See midiuart.asm for further instructions.
;================================================================================
;------MIDI Handler Registers----------------------------------------------------
MIDIFLAGS EQU 0x10 ;MIDI software flags register
MIDI_TEMP EQU 0x11 ;MIDI temp buffer register
DATA1_RX EQU 0x12 ;MIDI data byte 1 buffer register
DATA2_RX EQU 0x13 ;MIDI data byte 2 buffer register
MIDI_CHAN EQU 0x14 ;MIDI channel assignment register
;------MIDI Handler Flag Bits----------------------------------------------------
MSGRDY EQU 0x0000 ;MIDI message ready flag
MIDISTAT EQU 0x0001 ;MIDI status byte received flag
CC EQU 0x0002 ;MIDI control change message flag
D1RCV EQU 0x0003 ;MIDI data byte 1 received flag
STATBIT EQU 0x0007 ;MIDI status byte flag
;------Hard Defined Constants----------------------------------------------------
#define _UART_BAUD 31250 ;default UART baud = 31.25kbps for MIDI
#define _UART_BRG ((_XTAL_FREQ / _UART_BAUD) / 16) - 1
;------Redefinable Defined Constants---------------------------------------------
;
; The values of the following defined constants can be overridden in your main.inc
; file by simply defining them with a #define statement in your main.inc file. As
; shown, these are the default settings for these constants.
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 ;default Fosc = 4MHz
#endif
#ifndef _MIDI_BANK
#define _MIDI_BANK 0x1 ;default GPR bank for MIDI = bank 1
#endif
#ifndef _MIDI_TxD_TRIS
#define _MIDI_TxD_TRIS TRISC,RC6 ;default TxD pin = RC6
#endif
#ifndef _MIDI_RxD_TRIS
#define _MIDI_RxD_TRIS TRISC,RC7 ;default RxD pin = RC7
#endif
#ifndef _MIDI_TxD_ANSEL
#define _MIDI_TxD_ANSEL ANSELC,ANSC6 ;default TxD ANSEL bit = ANSC6
#endif
#ifndef _MIDI_RxD_ANSEL
#define _MIDI_RxD_ANSEL ANSELC,ANSC7 ;default RxD ANSEL bit = ANSC7
#endif