Pedal switching matrix with the AD75019

Started by R.G., November 26, 2015, 12:39:26 AM

Previous topic - Next topic

amptramp

Take the blue pill, Neo, you're not ready for the matrix.

G. Hoffman

Still haven't tried anything on the hardware, but it is all simulating properly.  I've got the transmission of data to the AD79015 all set up (not sure if the order of the data is right, but that can be cleaned up later easily enough), and the main loop is good.  I've started on the MIDI reception, but I'm not done with that yet. 

Now, send any good thoughts you might have, I have to go wait for my dad to get out of his brain surgery.  It's considered routine, the surgeon is supposed to be the best, and all that, but it is still brain surgery!


Gabriel

PRR

It's not rocket science!!

Seriously, my thoughts will be with your dad, with hopes for a sweet outcome.
  • SUPPORTER

G. Hoffman


Quote from: PRR on January 28, 2016, 07:52:52 PM
It's not rocket science!!

Seriously, my thoughts will be with your dad, with hopes for a sweet outcome.

Everything went well, so now we just have to see about the outcome.  Fingers crossed. 


Gabriel

G. Hoffman

#124
OK, I think I've got it.  It sims right, at least, though I still don't have the hardware to try it on.

It's got a few .asm files, all relocatable.  Happy to hear comments.  I'll put the code in separate posts.

I'm using the newest MPLAB X.  If you are not used to relocatable code, some of this might look odd (it did to me, at first, but is nice!)

Non-commercial open source license, and all that.

;**********************************************************************
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Filename:      AD79015_16F887_Main.asm                           *
;    Date:          14 January, 2016                                  *
;    File Version:  0.1                                               *
;                                                                     *
;    Author:        Gabriel Hoffman                                   *
;    Company:                                                         *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Files required: P16F877.INC                                      *
;                                                                     *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;Notes:     The Disco 8 is a eight location, eight loop, any order,   *
;   series/parallel effects switcher.  It is based on an Analog       *
;   Devices AD79015, controlled by a PIC16F887.  Each loop can be     *
;   assigned by to any single location by MIDI CC# command.  Each     *
;   loop has it's own CC#:                                            *
;                                                                     *
; loop 1 =   CC#0x00                                   *
; loop 2 =   CC#0x01                                   *
; loop 3 =   CC#0x02                                   *
; loop 4 =   CC#0x03                                   *
; loop 5 =   CC#0x04                                   *
; loop 6 =   CC#0x05                                   *
; loop 7 =   CC#0x06                                   *
; loop 8 =   CC#0x07                                   *
;                                                                     *
;   Each loop is assigned to a location by the data byte as follows:  *
;                                                                     *
; at 0, the loop is unassigned       *
;               at 1, the loop is assigned to location 1              *
;               at 2, the loop is assigned to location 2       *
;               at 3, the loop is assigned to location 3              *
;               at 4, the loop is assigned to location 4       *
;               at 5, the loop is assigned to location 5              *
;               at 6, the loop is assigned to location 6              *
;               at 7, the loop is assigned to location 7              *
;               at 8, the loop is assigned to location 8       *
;                                                                     *
;   The AD79015 uses syncronous serial communication.  The 16F887     *
;   transmits the 32 bytes (256 bits) on the DATA pin (RE1) with a    *
;   serial clock (RE2).  This is followed, within 5ms, by a positive  *
;   pulse of the PClk pin (RE0) to shift the data into the latches.   *
;                                                                     *
;                                                                     *
;   There are two 74HC574 latches used to control sixteen relays,     *
;   which are used to control the dry signal input to each location,  *
;   and to control the each location's output to the final output     *
;   buss.  The switches each has it's own CC#, from 0x08 - 0x17.  Any *
;   value higher than 0 turns the switch ON.                          *
;                                                                     *
;                                                                     *
;   There is an LED matrix, 8 rows high, by 8 columns wide, which     *
;   indicates which loop is in which location.  The main loop of the  *
;   program maintains the LED matrix.  PortD remains low until that   *
;   string's data is set up in PortA.  A low bit in PortA turns the   *
;   LED on.                                                                     *
;                                                                     *
;                                                                     *
;                                                                     *
;**********************************************************************

    list        p=16F887, r=dec
;Processor is PIC16F887 (p=16F887)
;Default Radix is Decimal (r=dec)
    #include    <p16f887.inc>   ; processor specific variable definitions

    errorlevel -302
    errorlevel -312
   
;*******************************************************************************
;     External routines        *
;*******************************************************************************   
    extern delay10
    extern MIDI_setup
    extern get_MIDI
    extern ser_AD79015
    extern set_SW
   
;*******************************************************************************
;     External variables        *
;*******************************************************************************
   
    extern   midi_status
    extern   midi_data_1
    extern   midi_data_2
    extern   data_reg
    extern   shift_reg
    extern   count_2
    extern   count_3
   
   
; '__CONFIG' directive is used to embed configuration data within .asm file.
; The labels following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.

    __CONFIG    _CONFIG1, _DEBUG_ON & _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT
    __CONFIG    _CONFIG2, _WRT_OFF & _BOR21V

   
   
   
;***** VARIABLE DEFINITIONS ******************************************

INT_VAR     UDATA_SHR   
w_temp RES     1     ; variable used for context saving
status_temp RES     1     ; variable used for context saving
temp_data RES 1

TEMP_VAR    UDATA           ; explicit address specified is not required
pclath_temp RES     1     ; variable used for context saving
fsr_temp RES 1



COUNT_VAR   UDATA

count_1 RES 1     ; LED matrix count

;*******************************************************************************
RESET_VECTOR     CODE    0x0000 ; processor reset vector
    nop
    goto    start              ; go to beginning of program

;*******************************************************************************
; Interrupt, context save, restoration, and return from interrupt       *
;*******************************************************************************
   
INTERRUPT_VECTOR    CODE    0X0004
    movwf   w_temp ;Copy W to TEMP register
    swapf   STATUS,W ;Swap status to be saved into W
;Swaps are used because they do not affect the status bits
    movwf   status_temp ;Save status to bank zero STATUS_TEMP register
    movf    PCLATH,W
    banksel pclath_temp ;save PCLATH context
    movwf   pclath_temp
    movf    FSR,W ;save FSR context
    movwf   fsr_temp
   
    btfsc   PIR1,RCIF
    pagesel get_MIDI
    call    get_MIDI
    pagesel set_SW
    call    set_SW
    pagesel ser_AD79015
    call    ser_AD79015
    pagesel $
   
   
   
    banksel pclath_temp
    movf    fsr_temp,w
    movwf   FSR
    movf    pclath_temp,w
    movwf   PCLATH
    swapf   status_temp,W ;Swap STATUS_TEMP register into W
;(sets bank to original state)
    movwf   STATUS ;Move W into STATUS register
    swapf   w_temp,F ;Swap W_TEMP
    swapf   w_temp,W ;Swap W_TEMP into W
    retfie

MAIN_PROG       CODE

start
    banksel    INTCON
    clrf    INTCON      ;disable interrupts
    banksel    OSCCON      ;bank 1
    movlw   b'01100000' ;8MHz Internal Oscillator
    movwf   OSCCON
;   btfss   OSCCON,HTS  ;is oscillator stable?
;   goto    $-1     ;no, check again
    banksel    ANSEL       ;yes, bank 3
    movlw   b'00000000' ;all ports digital I/O
    movwf   ANSEL
    movwf   ANSELH
    banksel    PORTA       ;bank 0
    clrf    PORTA       ;initialize all ports
    clrf    PORTB
    clrf    PORTC
    clrf    PORTD
    clrf    PORTE
    banksel    TRISA       ;bank 1
    movlw    b'00000000' ;ports A, B and E outputs
    movwf    TRISA
    movwf    TRISB
    movwf    TRISE
    movlw    b'10000000' ;ports C and D inputs
    movwf    TRISC
    movlw    b'00000000'
    movwf    TRISD
   
    pagesel MIDI_setup
    call    MIDI_setup
    pagesel $
   
    banksel INTCON       ;bank 0
    bsf     INTCON,GIE     ;Turn on global interrupts
    bsf     INTCON,PEIE     ;Turn on peripheral interrupts
   
       
       
       
; remaining code goes here

       
;**********************************************************************
;     MAIN LOOP       *
; This maintains the LED matrix.        *
;**********************************************************************
       
       
main_loop
    banksel midi_data_1
    movf    midi_data_1,W
    movwf   shift_reg
    banksel PORTA
    movlw   0
    movwf   PORTD
    movlw   0xff
    movwf   PORTA
    movlw   b'00000001'
    movwf   temp_data
    movlw   8
    movwf   count_1
    bankisel data_reg
    movlw   data_reg
    movwf   FSR
main_loop1       
    movf    INDF,W
    movwf   PORTA
    incf    FSR,F
    movf    temp_data,W
    rlf     temp_data,F
    bcf     temp_data,0
    movwf   PORTD
    movlw   .1
    decfsz  count_1,F
    goto    main_loop1
    goto    main_loop
   
   


   
   
   

   
   
   
   


   
   
   
   

    END                       ; directive 'end of program'

G. Hoffman

Incoming MIDI Control Change messages



;*******************************************************************************
;   MIDI inturupt routine                                                      *
;   This is designed to store the incomming MIDI data to three data registers, *
;   to be sent back to the main program.  It uses the PIC16F887, and the EUSART*
;   module.                                                                    *
;   The SETUP routine is used to set the SFR's for MIDI reception.             *
;              Baud rate =                                                                 *
;                                                                              *
;                                                                              *
;                                                                              *
;*******************************************************************************
   
   #include    <p16f887.inc>   ; processor specific variable definitions
   
   constant MIDI_Ch = .7
   
   
    errorlevel -302
    errorlevel -312
   
   global   MIDI_setup
   global   get_MIDI
   global   midi_status
   global   midi_data_1
   global   midi_data_2
   
    UDATA
midi_status RES 1     ; MIDI Data register - Status byte
midi_data_1 RES 1     ; MIDI Data register - Data Byte 1
midi_data_2 RES 1     ; MIDI Data register - Data Byte 2
MIDI_temp_data RES 1     ; Store data while working on it.
   
   




;*******************************************************************************
; MAIN PROGRAM
;*******************************************************************************

    CODE                      ; let linker place main program

MIDI_setup
    banksel TRISC ;EUSART SETUP
    bsf     TRISC,7 ;EUSART input
    banksel SPBRG ;set up EUSART for MIDI
    movlw   1 ;BAUD=31,250/sec
    movwf   SPBRG
    clrf    SPBRGH ;
    bcf     TXSTA,BRGH ;turn off BRGH bit
    banksel BAUDCTL
    bcf     BAUDCTL,BRG16 ;turn off BRG16 bot
    banksel TXSTA
    bcf     TXSTA,SYNC
    banksel RCSTA
    bsf     RCSTA,SPEN
    bcf     RCSTA,RX9 ;8 bit standard
    bsf     RCSTA,CREN
    banksel PIE1
    bsf     PIE1,RCIE
    return   
   
   
   
get_MIDI

    banksel RCREG
    movf    RCREG,W
    movwf   MIDI_temp_data
    movlw   b'10110000' ;check status, is it CC#, and MIDI Channel?
    addlw   MIDI_Ch
    subwf   MIDI_temp_data,W
    BTFSS   STATUS,Z
    return ;ignore message if wrong channel   
    movf    MIDI_temp_data,W
    movwf   midi_status ;move to status byte
    btfss   PIR1,RCIF ;check for new byte
    goto    $-1
    movf    RCREG,W ;move first data byte to register
    movwf   midi_data_1
    btfss   PIR1,RCIF ;check for last byte
    goto    $-1
    movf    RCREG,W ;move second data byte to register
    movwf   midi_data_2
    return
   
   
    END

G. Hoffman

Sending data to the AD79015



   
   
   #include    <p16f887.inc>   ; processor specific variable definitions
   
   global   ser_AD79015
   global   data_reg
   global   shift_reg
   global   count_2
   global   count_3

;************Pin Assignments****************************************************
   
    #define P_Clk PORTE,0
    #define S_Data PORTE,1
    #define S_Clk PORTE,2
    #define Extra_Clk1  PORTC,2
    #define Extra_Clk2  PORTC,6

   
;************Variables**********************************************************   

SW_DATA_VAR UDATA
data_reg RES .8     ; 32 bytes to store the data
shift_reg RES 1     ; the transmision and shift register for the 79015 data
count_2 RES 1     ; These registers are used to count down the 79015 serial data
count_3 RES 1     ; 1 counts down the bits per byte, 2 the bytes per packet   

   
   
SW_AD79015 CODE


ser_AD79015     ;sends data to the AD79015
    bankisel data_reg    
    movlw   data_reg
    movwf   FSR
    movlw   .8     ;set the limits of the switch loop
    movwf   count_2     ;outer loop

SW_loop_1
    movlw   .8     ;limits of the data register
    movwf   count_3     ;inner loop
    movf    INDF,W     ;put data in the Shift Register
    movwf   shift_reg
    incf    FSR,F     ;move to next data register
SW_loop_2
    btfsc   shift_reg,0
    goto    set_bit
    bcf     S_Data     ;clear Data bit for 0, then clock    
    call    clock_bit
    rlf     shift_reg,F
    decfsz  count_3,F
    goto    SW_loop_2
    call    SW_empty
    decfsz  count_2,F     ;check if we're done with the first half
    goto    SW_loop_1
    goto    SW_loop_4
set_bit
    bsf     S_Data     ;set Data bit for 1, then clock
    call    clock_bit
    rlf     shift_reg,F
    decfsz  count_3,F
    goto    SW_loop_2
    call    SW_empty
    decfsz  count_2,F     ;check if we're done with the first half
    goto    SW_loop_1
    goto    SW_loop_4

SW_loop_4
    bankisel data_reg    
    movlw   data_reg
    movwf   FSR
    movlw   .8     ;set the limits of the switch loop
    movwf   count_2     ;outer loop
SW_loop_4a
    call    SW_empty
    movlw   .8     ;limits of the data register
    movwf   count_3     ;inner loop
    movf    INDF,W     ;put data in the Shift Register
    movwf   shift_reg
    incf    FSR,F     ;move to next data register
SW_loop_4b
    btfsc   shift_reg,0
    goto    set_bit1
    bcf     S_Data     ;clear Data bit for 0, then clock    
    call    clock_bit
    rlf     shift_reg,F
    decfsz  count_3,F
    goto    SW_loop_4b
    decfsz  count_2,F     ;check if we're done with the first half
    goto    SW_loop_4a
    bsf     P_Clk
    nop
    nop
    nop
    nop
    bcf     P_Clk
    nop
    nop
    nop
    return
set_bit1
    bsf     S_Data     ;set Data bit for 1, then clock
    call    clock_bit
    rlf     shift_reg,F
    decfsz  count_3,F
    goto    SW_loop_4b
    decfsz  count_2,F     ;check if we're done with the first half
    goto    SW_loop_4a
    bsf     P_Clk
    nop
    nop
    nop
    nop
    bcf     P_Clk
    nop
    nop
    nop
    return
   
clock_bit   
    bsf     S_Clk
    nop
    bcf     S_Clk
    return   
   
SW_empty ;sends zeros to unused switches
    movlw   .8     ;limits of the data register
    movwf   count_3     ;inner loop
SW_loop_3a
    bcf     S_Data     ;clear Data bit for 0, then clock
    bsf     S_Clk    
    call    clock_bit
    decfsz  count_3,F
    goto    SW_loop_3a
    return   

    END

G. Hoffman

#127
This is to interpret the incoming MIDI data and set the data registers prior to actually sending the data to the AD79015.  Frustratingly, I had to put the lookup table at an absolute address, because the linker kept putting it at like, 0xf8 on the page, and when it went to look up, it would jump back to the interrupt service routine!!!!! 


   #include    <p16f887.inc>   ; processor specific variable definitions
   
    global  set_SW
   
   
    extern  midi_status
    extern  midi_data_1
    extern  midi_data_2
    extern  data_reg

SW_INTERP CODE                  ; let linker place program

set_SW
movf    midi_data_1,W   ;is this a valid CC#
andlw   b'11111000' ;get rid of some bits
btfss   STATUS,Z
goto clear ;no
movf    midi_data_1,W   ;yes!
addlw   data_reg
movwf   FSR ;where's the data going?
movf    midi_data_2,W
pagesel lookup
call lookup
pagesel $
movwf   INDF
banksel midi_status
clear   clrf    midi_status
clrf    midi_data_1
clrf    midi_data_2
return

   
   
   
   
LOOKUP_SEC  CODE    0x0800
lookup
   
    andlw   b'00001111'     ;make sure the cc# data 2 doesn't take us off the lookup table
    addwf   PCL,1     ;convert cc# data2 to switch bit
    retlw   b'00000001'
    retlw   b'00000010'
    retlw   b'00000100'
    retlw   b'00001000'
    retlw   b'00010000'
    retlw   b'00100000'
    retlw   b'01000000'
    retlw   b'10000000'
    retlw   b'10000000'
    retlw   b'10000000'
    retlw   b'10000000'
    retlw   b'10000000'
    retlw   b'10000000'
    retlw   b'10000000'
    retlw   b'10000000'
    retlw   b'10000000'   
    END

G. Hoffman

#128
This is NOT the final program.  There are things I just haven't bothered with - this only has one MIDI channel (7), and I haven't implemented the extra switches yet.  Those are both relatively easy, though, and I'll deal with them when I've made sure this is all working. 

Also, the AD79015 loop is ugly right now.  I branched the wrong way, and I've got one part of the code written twice because of it.  I should go back and rewrite it, and perhaps I will, but not tonight!

And if I'm honest, I hate the way I've done the MIDI loop.  I'm just leaving the thing dead for better than 400 instruction cycles while I wait for the MIDI data to come in.  There has to be a better way to do this!!!!  (It works fine, but is inelegant.  Which is about the best I can expect, given my limited programming skills, but still.)


Gabriel

ElectricDruid

Quote from: G. Hoffman on February 03, 2016, 12:59:26 AM
And if I'm honest, I hate the way I've done the MIDI loop.  I'm just leaving the thing dead for better than 400 instruction cycles while I wait for the MIDI data to come in.  There has to be a better way to do this!!!!

You need to treat each byte individually. Enter the interupt. Look at each byte, work out what it is, then act accordingly.Exit the interrupt.

In your case, if it was a status byte, it'd store it in midi_status. If it's the first data byte, it'd store it in midi_data_1, and increment a "data bytes received" counter. If it's the second data byte, store it in midi_data_2, and then call the routine. If a new status byte comes in before all the data bytes have arrived, store the status byte and reset the data bytes counter to zero.

You could add some "potential problem filtering" before that stage too. Like check whether the incoming byte is MIDI Realtime (0xF8 or above) and just exit the interrupt straight away. That way, your code will ignore MIDI Clock and Sequencer Start/Stop/Continue commands.

One problem with the way you've done it is that unfinished/broken/lost midi bytes will leave the code hanging until another message comes in, whereupon it'll be out of sync and won't work. Not every midi message is three bytes, and even if they were, bytes sometimes go missing.

HTH,
Tom

G. Hoffman

Quote from: ElectricDruid on February 04, 2016, 07:12:17 PM
Quote from: G. Hoffman on February 03, 2016, 12:59:26 AM
And if I'm honest, I hate the way I've done the MIDI loop.  I'm just leaving the thing dead for better than 400 instruction cycles while I wait for the MIDI data to come in.  There has to be a better way to do this!!!!

You need to treat each byte individually. Enter the interupt. Look at each byte, work out what it is, then act accordingly.Exit the interrupt.

In your case, if it was a status byte, it'd store it in midi_status. If it's the first data byte, it'd store it in midi_data_1, and increment a "data bytes received" counter. If it's the second data byte, store it in midi_data_2, and then call the routine. If a new status byte comes in before all the data bytes have arrived, store the status byte and reset the data bytes counter to zero.

You could add some "potential problem filtering" before that stage too. Like check whether the incoming byte is MIDI Realtime (0xF8 or above) and just exit the interrupt straight away. That way, your code will ignore MIDI Clock and Sequencer Start/Stop/Continue commands.

One problem with the way you've done it is that unfinished/broken/lost midi bytes will leave the code hanging until another message comes in, whereupon it'll be out of sync and won't work. Not every midi message is three bytes, and even if they were, bytes sometimes go missing.

HTH,
Tom


Yeah.  I kind of rushed through all this.

Good point about the real time messages and such.  I'm going to be using MIDI clock in the rig this will go in, so, YEAH!


Gabriel

G. Hoffman

What I really want to do is put together a MIDI routine that will receive and store any kind of MIDI message, so I can use it for any MIDI projects I might want to do in the future.  But then I sat down to write this, and just went, "screw it, ignore anything I don't need right now!"


Gabriel

G. Hoffman

#132
How's this look for some pseudo-code for a (near) universal MIDI receive routine?  By checking the two Mess variables (Mess_type, and Mess_status) you can decide in your main loop if you need to service the incoming MIDI messages.  It throws out anything in the real time or system common ranges (0xF0 and above), so no system exclusive messages, and no MIDI clock or MTC either.


MIDI message routine

;variables
MIDI_temp
Note_off
Note_off_velo
Note_on
Note_on_velo
After
After_press
CC_1
CC_2
Prog_change
Chan_Touch
PItch
Pitch_2
Chan_mode
Chan_mode_2
Mess_Type        ;what kind of message are we receiving?
            ;bit7=Channel Mode
            ;bit6=Pitch Bend
            ;bit5=Channel Aftertouch
            ;bit4=Program Change
            ;bit3=CC
            ;bit2=Polyphonic Aftertouch
            ;bit1=Note on
            ;bit0=Note off

Mess_status        ;bit2=message complete
            ;bit1=second byte received
            ;bit0=status byte received

;Retrieve MIDI data from RCREG, place in MIDI_temp
;ignore System Common and System Real-Time messages
;is it a status byte?  Is it the right channel?
;yes, what type? Set bit in Mess_type, and reset Mess_status
;no, what type is incoming (check Mess_Type) go to (the right place)

(the right place) ;repeated for different Message types
;how many bits have been received? (check Mess_ status)
;place byte
;set Mess_Status byte, as appropriate
RETFIE

Hatredman

#133
I think these guys are reading this topic. They've just implemented "loops in arbitrary order" with an update on their firmware. If you read the manual and look at the illustrations, their "G2 relay matrix" seems to be exactly the AD75019.

http://www.thegigrig.co.uk
Kirk Hammet invented the Burst Box.

G. Hoffman

Quote from: Hatredman on February 05, 2016, 09:00:31 PM
I think these guys are reading this topic. They've just implemented "loops in arbitrary order" with an update on their firmware. If you read the manual and look at the illustrations, their "G2 relay matrix" seems to be exactly the AD75019.

http://www.thegigrig.co.uk

Yeah, I saw that.  But the thing is, I can think of at least three other units out there right now which I'm 90% certain are using this chip, or at least one very similar.  The Boss ES-8, the Liquid Router A16, and the Sound Sculpture Switchblade systems.  I'm reasonably certain they got the idea from them!  And given they CAN do it with a software patch, I'd bet they've been planning it since their initial designs.


Gabriel

G. Hoffman

#135
OK, a better MIDI receive routine.  I had to make a couple changes in other parts, but I like this routine for receiving MIDI - it can take any MIDI Message other than real time and sys-ex messages, and returns to the main routine between bytes!  As of yet, it is still dependent on a 4MhZ FOSC, but I'm going to try to come up with a formula so you can use it with any FOSC.  I haven't fully tested it with any other messages, so still some testing to do, but so far, so good.  And so far, I'm only testing it with the Sim, of course.  I should have the hardware early next week, though.


;*******************************************************************************
;  This is a better MIDI receive routine.  It takes any MIDI Message below 0xF0*
;and sends it back in a dedicated register.  Other routines can determine if   *
;there is data to be read by interogating Mess_Type and Mess_status registers. *
;*******************************************************************************    
   
   #include    <p16f887.inc>   ; processor specific variable definitions

errorlevel -302
errorlevel -312    
   

Global     Note_off   
Global     Note_on    
Global     After    
Global     CC    
Global     Prog_change
Global     Chan_Touch 
Global     Pitch    
Global     Mess_Type   
Global     Mess_status
Global     MIDI_setup
Global     get_MIDI

constant MIDI_Ch     = .7
constant Note0     = b'10000000'
constant Note1     = b'10010000'
constant Poly_touch     = b'10100000'
constant CC_stat     = b'10110000'
constant PC_stat     = b'11000000'
constant chan_touch_stat    = b'11010000'
constant pitch_stat     = b'11100000'

;variables
   
MIDI_data   UDATA    

MIDI_temp   RES 1
MIDI_temp2  RES 1
Note_off    RES 2
Note_on     RES 2
After     RES 2
CC     RES 2
Prog_change RES 1
Chan_Touch  RES 1
Pitch     RES 2
Mess_Type   RES 1 ;what kind of message are we receiving?
;bit6=Pitch Bend
;bit5=Channel Aftertouch
;bit4=Program Change
;bit3=CC
;bit2=Polyphonic Aftertouch
;bit1=Note on
;bit0=Note off

Mess_status RES 1     ;bit2=message complete
    ;bit1=second byte received
    ;bit0=status byte received

   
   

MAIN_PROG CODE                 

MIDI_setup
    banksel TRISC ;EUSART SETUP
    bsf     TRISC,7 ;EUSART input
    banksel SPBRG ;set up EUSART for MIDI
    movlw   1 ;BAUD=31,250/sec
    movwf   SPBRG
    clrf    SPBRGH
    bcf     TXSTA,BRGH ;turn off BRGH bit
    banksel BAUDCTL
    bcf     BAUDCTL,BRG16 ;turn off BRG16 bot
    banksel TXSTA
    bcf     TXSTA,SYNC
    banksel RCSTA
    bsf     RCSTA,SPEN
    bcf     RCSTA,RX9 ;8 bit standard
    bsf     RCSTA,CREN
    banksel PIE1
    bsf     PIE1,RCIE
    return   

   
get_MIDI
    banksel RCREG     ;Retrieve MIDI data from RCREG, place in MIDI_temp
    movf    RCREG,W
    movwf   MIDI_temp
    movf    MIDI_temp,W     ;ignore System Common and System Real-Time messages
    andlw   b'11110000'
    movwf   MIDI_temp2
    sublw   b'11110000'
    btfsc   STATUS,Z
    return
    btfss   MIDI_temp,7     ;is it a status byte?  Is it the right channel?
    goto    data_byte
    movf    MIDI_temp,W
    andlw   b'00001111'
    sublw   MIDI_Ch
    btfss   STATUS,Z    
    return
    movf    MIDI_temp2,W    ;yes, what type?
    sublw   Note0     ;note off
    btfsc   STATUS,Z
    goto    Note00
    movf    MIDI_temp2,W
    sublw   Note1     ;note on
    btfsc   STATUS,Z
    goto    Note11
    movf    MIDI_temp2,W
    sublw   Poly_touch     ;Polyphonic aftertouch
    btfsc   STATUS,Z
    goto    Poly_touch2
    movf    MIDI_temp2,W
    sublw   CC_stat     ;Control Change
    btfsc   STATUS,Z
    goto    CC_STATUS
    movf    MIDI_temp2,W
    sublw   PC_stat     ;Program Change
    btfsc   STATUS,Z
    goto    PC_STATUS    
    movf    MIDI_temp2,W
    sublw   chan_touch_stat ;Channel Aftertouch
    btfsc   STATUS,Z
    goto    chan_status
    movf    MIDI_temp2,w
    sublw   pitch_stat     ;Pitch Change
    btfsc   STATUS,Z
    goto    pitch_status
    return
   
   
   
;*********************status byte routines**************************************   
Note00
    bsf Mess_Type,0 ;Set bit in Mess_type, and set Mess_status
    bsf Mess_status,0
    return
   
Note11   
    bsf Mess_Type,1
    bsf Mess_status,0
    return
   
Poly_touch2   
    bsf Mess_Type,2
    bsf Mess_status,0
    return
   
CC_STATUS   
    bsf Mess_Type,3
    bsf Mess_status,0
    return
   
PC_STATUS   
    bsf Mess_Type,4
    bsf Mess_status,0
    return
   
chan_status   
    bsf Mess_Type,5
    bsf Mess_status,0
    return
   
pitch_status
    bsf Mess_Type,6
    bsf Mess_status,0
    return
   
data_byte
    ;what kind of message are we receiving?
    ;bit6=Pitch Bend
    ;bit5=Channel Aftertouch
    ;bit4=Program Change
    ;bit3=CC
    ;bit2=Polyphonic Aftertouch
    ;bit1=Note on
    ;bit0=Note off
    btfsc   Mess_Type,0     ;what type is incoming (check Mess_Type)
    goto    Note0_data     ;go to the right place.
    btfsc   Mess_Type,1
    goto    Note1_data
    btfsc   Mess_Type,2
    goto    Poly_touch_data
    btfsc   Mess_Type,3
    goto    CC_data
    btfsc   Mess_Type,4
    goto    PC_data
    btfsc   Mess_Type,5
    goto    Chan_touch_data
    btfsc   Mess_Type,6
    goto    Pitch_data
    return

   
;*************************Data bytes********************************************   
    ;how many bits have been received? (check Mess_ status)
    ;place byte
    ;set Mess_Status byte, as appropriate
    ;bit2=message complete
    ;bit1=second byte received
    ;bit0=status byte received
Note0_data
    btfsc   Mess_status,1
    goto    Note0_data2
    movf    MIDI_temp,W
    movwf   Note_off
    bsf     Mess_status,1
    return   
Note0_data2   
    movf    MIDI_temp,W
    movwf   Note_off+1
    bsf     Mess_status,2
    return
Note1_data
    btfsc   Mess_status,1
    goto    Note1_data2
    movf    MIDI_temp,W
    movwf   Note_on
    bsf     Mess_status,1
    return
Note1_data2   
    movf    MIDI_temp,W
    movwf   Note_on+1
    bsf     Mess_status,2
    return
Poly_touch_data
    btfsc   Mess_status,1
    goto    Poly_touch_data2
    movf    MIDI_temp,W
    movwf   After
    bsf     Mess_status,1
    return
Poly_touch_data2
    movf    MIDI_temp,W
    movwf   After+1
    bsf     Mess_status,2
    return
CC_data
    btfsc   Mess_status,1
    goto    CC_data2
    movf    MIDI_temp,W
    movwf   CC
    bsf     Mess_status,1
    return
CC_data2 
    movf    MIDI_temp,W
    movwf   CC+1
    bsf     Mess_status,2
    return
PC_data
    movf    MIDI_temp,W
    movwf   Prog_change
    bsf     Mess_status,1
    bsf     Mess_status,2
    return
Chan_touch_data
    movf    MIDI_temp,W
    movwf   Chan_Touch
    bsf     Mess_status,1
    bsf     Mess_status,2
    return
Pitch_data
    btfsc   Mess_status,1
    goto    Pitch_data2
    movf    MIDI_temp,W
    movwf   Pitch
    bsf     Mess_status,1
    return
Pitch_data2
    movf    MIDI_temp,W
    movwf   Pitch+1
    bsf     Mess_status,2
    return
   
    END



Gabriel

G. Hoffman



HARDWARE!!!


Time to do some soldering.


Gabriel

G. Hoffman

So, here is a good piece of advice - don't try to hand solder surface mount PLCC sockets!!!!!!!


Freaking sucks.  I wrecked one of the boards removing a socket that was misaligned.  I normally LIKE soldering surface mount, but these sockets are really tough, and I wrecked my last one, so now I'm waiting.  Definitely use through hole PLCC sockets, in the future.


Gabriel

ElectricDruid

Quote from: G. Hoffman on February 06, 2016, 03:19:23 AM
OK, a better MIDI receive routine.  I had to make a couple changes in other parts, but I like this routine for receiving MIDI - it can take any MIDI Message other than real time and sys-ex messages, and returns to the main routine between bytes!  As of yet, it is still dependent on a 4MhZ FOSC, but I'm going to try to come up with a formula so you can use it with any FOSC.  I haven't fully tested it with any other messages, so still some testing to do, but so far, so good.  And so far, I'm only testing it with the Sim, of course.  I should have the hardware early next week, though.

That is massively improved! Now you're getting somewhere!

One thing that I can see straight away, though - the Sysex exclusion won't work. It'll throw away the "Start Sysex" and "End Sysex" bytes, but I can't see any flag that tells it to ignore the data in-between. You code will try to interpret Sysex data as if it were standard MIDI.

HTH,
Tom



G. Hoffman


Quote from: ElectricDruid on February 11, 2016, 04:30:52 AM
Quote from: G. Hoffman on February 06, 2016, 03:19:23 AM
OK, a better MIDI receive routine.  I had to make a couple changes in other parts, but I like this routine for receiving MIDI - it can take any MIDI Message other than real time and sys-ex messages, and returns to the main routine between bytes!  As of yet, it is still dependent on a 4MhZ FOSC, but I'm going to try to come up with a formula so you can use it with any FOSC.  I haven't fully tested it with any other messages, so still some testing to do, but so far, so good.  And so far, I'm only testing it with the Sim, of course.  I should have the hardware early next week, though.

That is massively improved! Now you're getting somewhere!

One thing that I can see straight away, though - the Sysex exclusion won't work. It'll throw away the "Start Sysex" and "End Sysex" bytes, but I can't see any flag that tells it to ignore the data in-between. You code will try to interpret Sysex data as if it were standard MIDI.

HTH,
Tom


Well, I gotta mess something up.  I'm finding it a lot easier than the last time I messed with programming, but I'm still very minimally skilled at this stuff. 

Perhaps, if there is nothing in the MIDI status byte, incoming gets ignored?   Maybe not.  I'm not sure - can sys ex interrupt other messages?  If so, I suppose I could use one of the spare bits to ignore everything until the sys ex closing byte is read. 


Gabriel