Midi In on 18M2, 2bytes only: Programm Change messages

tschrama

New Member
Hi all,

I've been trying to receive Midi Programm Change messages on a 18m2 chip.. but unsuccesfull... Can you guys see anything wrong with my cde:

Code:
[COLOR=Blue]symbol led1 [/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Blue]C.6
symbol led2 [/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Blue]C.7
symbol [/COLOR][COLOR=Purple]status [/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Purple]b1[/COLOR]
[COLOR=Blue]symbol [/COLOR][COLOR=Purple]program [/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Purple]b2[/COLOR]
[COLOR=Blue]symbol [/COLOR][COLOR=Purple]previous [/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Purple]b3

previous [/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Purple]program[/COLOR]

[COLOR=Blue]hsersetup b31250_4[/COLOR][COLOR=Black],[/COLOR][COLOR=Navy]%00000000

   [/COLOR][COLOR=Blue]do 
         
      
      hserin [/COLOR][COLOR=Purple]status
      [/COLOR][COLOR=Blue]hserin [/COLOR][COLOR=Purple]program
      
      [/COLOR][COLOR=Blue]if [/COLOR][COLOR=Purple]program [/COLOR][COLOR=Black]![/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Purple]previous [/COLOR][COLOR=Blue]then
            high led1
            pause [/COLOR][COLOR=Navy]500
            [/COLOR][COLOR=Blue]low led1
            [/COLOR][COLOR=Purple]previous [/COLOR][COLOR=DarkCyan]= [/COLOR][COLOR=Purple]program
      [/COLOR][COLOR=Blue]end if
   
   pause [/COLOR][COLOR=Navy]10

   [/COLOR][COLOR=Blue]loop     [/COLOR]
 

hippy

Technical Support
Staff member
It might depend on what you are receiving from. MIDI comes in quite fast and messages are not all the same length so you could be getting out of sync or even missing bytes. You aren't checking that the status is for Program Change and that could be messing things up. Or it could be that the interface polarity is wrong or there are incorrect connections.

You could try displaying the results of what you are receiving which may help identify the problem, though I would personally not recommend an M2 for MIDI receive; one would be better off using an X2 and its background receive capability.

There was some earlier discussion on capturing Program Change commands in the thread below and also other discussions on MIDI receive in the forum -

http://www.picaxeforum.co.uk/showthread.php?15848-Opening-the-MIDI-can-of-worms

Full MIDI message parsing can be a bit tricky but I have included my own MIDI parser below from a while ago. It should be possible to port that to an X2 and trim down to just the command(s) one wants -

Code:
#Picaxe 40X1
#No_Data
#No_Table
#Terminal 9600

Symbol CHANNEL           = 1 ' MIDI Channel = 1 To 16

Symbol CHANNEL_MINUS_1   = CHANNEL - 1

'                           .--- Bytes in message
'                           |.-- Status byte
'                           ||_

Symbol NOTE_OFF          = $380 + CHANNEL_MINUS_1
Symbol NOTE_ON           = $390 + CHANNEL_MINUS_1
Symbol POLY_KEY_PRESSURE = $3A0 + CHANNEL_MINUS_1
Symbol CONTROL_CHANGE    = $3B0 + CHANNEL_MINUS_1
Symbol PROGRAM_CHANGE    = $2C0 + CHANNEL_MINUS_1
Symbol AFTERTOUCH        = $2D0 + CHANNEL_MINUS_1
Symbol PITCH_BEND        = $3E0 + CHANNEL_MINUS_1

Symbol TIME_CODE_FRAME   = $2F1
Symbol SONG_POSITION_PTR = $3F2
Symbol SONG_SELECT       = $2F3

Symbol status            = w0 ' b1:b0
Symbol statusByte        = b0
Symbol statusCount       = b1

Symbol argW              = w1 ' b3:b2
Symbol arg2              = b2
Symbol arg1              = b3

SetFreq M8

HserSetup B31250_8, %001

MainLoop:

  Do

    Do : Loop While ptr = hSerPtr

    If @ptr >= $80 Then
      statusByte  = @ptrInc
      If statusByte >= $F0 Then
        Select Case statusByte
          Case $F0 : serTxd( "System Exclusive ...", CR, LF )
          Case $F6 : serTxd( "Tune Request", CR, LF )
          Case $F7 : serTxd( "End of System Exclusive", CR, LF )
          Case $F8 : serTxd( "Timing Clock", CR, LF )
          Case $FA : serTxd( "Start Sequence", CR, LF )
          Case $FB : serTxd( "Continue Sequence", CR, LF )
          Case $FC : serTxd( "Stop Sequence", CR, LF )
          Case $FE : serTxd( "Active Sensing", CR, LF )
          Case $FF : serTxd( "System Reset", CR, LF )
        End Select
      End If
    Else
      arg1 = arg2
      arg2 = @ptrInc
      statusCount = statusCount + 1
      ; status is statusCount << 8 | statusByte
      Select Case status

        Case NOTE_ON
          If arg2 <> 0 Then
            SerTxd( "Note On", 9, #arg1, 9, #arg2, CR, LF )
          Else
            SerTxd( "Note Off ( via Note On )", 9, #arg1, CR, LF )
          End If

        Case NOTE_OFF
          SerTxd( "Note Off", 9, #arg1, 9, #arg2, CR, LF )

        Case POLY_KEY_PRESSURE
          SerTxd( "Poly Key Pressure", 9, #arg1, 9, #arg2, CR, LF )

        Case CONTROL_CHANGE
          Select Case arg1
            Case $00 : SerTxd( "Bank Select MSB", 9, #arg2, CR, LF )
            Case $20 : SerTxd( "Bank Select LSB", 9, #arg2, CR, LF )
            Case $78 : SerTxd( "All Sound Off", CR, LF )
            Case $79 : SerTxd( "Reset All Controllers", CR, LF )
            Case $7A : If arg2 >= $40 Then
                         SerTxd( "Local Conrol On", CR, LF )
                       Else
                         SerTxd( "Local Conrol Off", CR, LF )
                       End If
            Case $7B : SerTxd( "All Notes Off", CR, LF )
            Case $7C : SerTxd( "Omni Off", CR, LF )
            Case $7D : SerTxd( "Omni On", CR, LF )
            Case $7E : SerTxd( "Mono On / Poly Off ( Channels = ", #arg2, " )",CR, LF )
            Case $7F : SerTxd( "Poly On / Mono Off", CR, LF )
            Else     : SerTxd( "Control Change", 9, #arg1, 9, #arg2, CR, LF )
          End Select

        Case PROGRAM_CHANGE
          SerTxd( "Program Change", 9, #arg2, CR, LF )

        Case AFTERTOUCH
          SerTxd( "Aftertouch", 9, #arg2, CR, LF )

        Case PITCH_BEND
          arg2 = arg2 * 2
          argW = argW / 2
          SerTxd( "Pitch Bend", 9, #argW, CR, LF )

        Case TIME_CODE_FRAME
          arg1 = arg2 / 16
          arg2 = arg2 & 15
          SerTxd( "Time Code Quarter Frame", 9, #arg1, "/", #arg2, CR, LF )
          statusByte = 0

        Case SONG_POSITION_PTR
          statusByte = arg2
          arg2 = arg1 * 2
          arg1 = statusByte
          argW = argW / 2
          SerTxd( "Song Position Pointer", 9, #argW, CR, LF )
          statusByte = 0

        Case SONG_SELECT
          SerTxd( "Song Select", 9, #arg2, CR, LF )
          statusByte = 0

        Else
          Goto MainLoop

      End Select
    End If

    statusCount = 1
  
  Loop
 

tschrama

New Member
Thanks! I will definitely read that thread and your code.

The messages come from my midi controller, build with a 08M2. There are only Program Change messages... maximum rate is 1 per second.

Should I add a pause between the two HSERIN commands?

Or could I reset the HSERIN, to prevent receiving halfe mesages?
 

hippy

Technical Support
Staff member
If it's your own MIDI transmitter then it should be reasonably easy to get it all working. It might just be a matter of running at a higher system speed in the 18M2 or adding a PAUSE between the status and data byte of sending the HSEROUT data in the 08M2.

Added: I was going to say don't add a PAUSE between the HSERIN as it will make it worse. But given as there's no check that data has arrived that may well fix things. You really need to do something to check that data has arrived or you can easily get out of sync.

Although it will feel like a step back, the first step would be to get the 08M2 sending single bytes and have the 18M2 check it can receive them ...

Code:
#Picaxe 08M2
HSerSetup B31250_4, %000
Do
  HSerOut 0, (b0)
  b0 = b0 + 1
  Pause 1000
Loop
Code:
#Picaxe 18M2
#Terminal 4800
HSerSetup B31250_4, %000
Do
  w0 = -1
  Do
    HSerIn w0
  Loop Until b1 = 0
  SerTxd( #b0, " " )
Loop
Once connected and working that should show an incrementing count on the 18M2. If not seeing anything there's probably a wiring issue. If getting random numbers it's probably a polarity issue. If the 08M2 works when controlling MIDI keyboards etc, any polarity error is likely to be in the receiver.

Then I would move on to the 08M2 sending a Program Change command every second, seeing if that gets through okay.
 

tschrama

New Member
Good ideas! Yes the 08M2 midi controller works perfect with my other midi equipment (guitar amps, only used for channel selection). very happy with that!
 

tschrama

New Member
Houreeeehh the little number increment test program work at B31250_4 bout! Had to manually invert the input signal going to the opto coupler. Thx!!
 

tschrama

New Member
and this:

code

HSerOut 0, (%11000000, b0)


%11000000 being the status data for signaling midi-channel0 and programm change function

b0 being the programnumber


works kind of.. but it seems to be skioing a bytes now and then. I am thinking I shiuld use a interupt to catch a midi signal comming in?
 

hippy

Technical Support
Staff member
There are no interrupts for HSERIN on an M2 and that will probably over-complicate things. It should be possible to get it working with polling. I would try ...

Code:
#Picaxe 18M2
#Terminal 38400
SetFreq M32
HSerSetup B31250_32, %000  <-- Adjust %000 as required
Do
  b0 = 0
  Do
    HSerIn b0
  Loop Until b0 = $C0
  b0 = $80
  Do
    HSerIn b0
  Loop Until b0 < $80
  SerTxd( "Change", #b0, CR, LF)
Loop
 

tschrama

New Member
Well.. again it kinda works... The good news is that non of the midi-status-bytes are missed. It detects every status byte signaling the Program change, but it gives errorous program numbers in about 30% of the time.

Since I am ditecting every midi-status-byte, I don't think a synchronisation problem is happening.. maybe a pulse-width problem?
 

hippy

Technical Support
Staff member
Post the full code of your transmitter and receiver. That might reveal what the problem is.
 

hippy

Technical Support
Staff member
Post your full circuit diagram as well as your code :)

You can go back to the earlier code which sent an incrementing byte every second and check that still works. If not then something has possibly changed in your hardware set-up.

If that still works you can try decreasing the PAUSE time to see if that introduces any problems.
 

tschrama

New Member
OK here goes: midi-pedal code (the controller)

code

' The Midi Pedal
' PICAXE 08M2
' buttun1 = Program Change to next program, button2 = PC to previous program
' with fast forward function if a button is pressed lomger than 1000ms



symbol button1 = pinc.3
symbol button2 = pinc.4
symbol program = b13
symbol button_was = b14
program = 0
button_was = 0
hsersetup b31250_4,%00010000

main:

if button1 = 1 then

button_was = button_was + 1

if button_was = 1 then
program = program - 1
if program > 99 then
program = 99
endif
hserout 0,(192,program)
endif

if button_was > 10 then
button_was = 10
program = program - 1
if program > 99 then
program = 99
endif
hserout 0,(192,program)
endif
pause 100
endif

if button2 = 1 then

button_was = button_was + 1

if button_was = 1 then
program = program + 1
if program > 99 then
program = 0
endif
hserout 0,(192,program)
endif

if button_was > 10 then
button_was = 10
program = program + 1
if program > 99 then
program = 0
endif
hserout 0,(192,program)
endif
pause 100
endif



if button1 = 0 and button2 = 0 then
button_was = 0
endif


goto main
 

tschrama

New Member
Well I cann't semm to solve this: about 1 in 10 data bytes are corrupted. I tried it tonight on 2 different 08M2 prototypeboards, but both have the same behavior. Any suggestions?

here's the receiver code:

#picaxe 08M2
#terminal 4800

'bit 0 - background receive serial data to the scratchpad (not M2 parts)
'bit 1 - invert serial output data (0 = ?T?, 1 = ?N?)
'bit 2 - invert serial input data (0 = ?T?, 1 = ?N?)
'bit 3 - disable hserout (1 = hserout pin normal i/o)
'bit 4 - disable hserin (1 = hserin pin normal i/o

HSerSetup B31250_4, %1000

Do
b0 = 0
Do
HSerIn b0
Loop Until b0 = $C0
HSerIn b1
SerTxd( #b1, CR, LF)

Loop
 

tschrama

New Member
This is an example of the values of the second midi-byte I receives (the 'data' bytes). They should be just incrementing.

1
2
224
4
193
6
7
8
8
8
11
248
13
14
248
16
252
18
19
20
21
241
23
24
25
26
27
28
29
252
31
31
33
34
35
36
37
38
254
 

AllyCat

Senior Member
Hi,

The PICaxe might not be operating fast enough, see if adding/changing the following lines in your program make any difference:

Code:
setfreq m8           ; or m16
#terminal 9600           ; or 19200 with m16
HSerSetup B31250_8, %1000      ; or _16
Strictly, the program should be checking that a new byte has actually been written into b1, but that would probably make the timing even more problematic.

Personally, I had more success by reading the serial buffer directly with a few SFR commands, but let's try the "simple" solutions first. ;)

Cheers, Alan.
 

tschrama

New Member
Thanks for the suggestion. I tried various speeds, only to find out that it worsens the problem. That got me thinking: it might be a sinchronisation problem. and YES.. hoerrreee.. adding a 33ms pause after the Hserin b0 reduces the error enourmously to about 6 in 1000 errors.

So I will try to understand how I can avoid a synchronisation error. Any ideas are welcome!

Thanks all.

code:
#picaxe 08M2
setfreq m4 ; m4 , m8 or m16
#terminal 4800 ; 9600 with m8 or 19200 with m16
HSerSetup B31250_4, %1000 ; _8 or _16

'bit 0 - background receive serial data to the scratchpad (not M2 parts)
'bit 1 - invert serial output data (0 = ?T?, 1 = ?N?)
'bit 2 - invert serial input data (0 = ?T?, 1 = ?N?)
'bit 3 - disable hserout (1 = hserout pin normal i/o)
'bit 4 - disable hserin (1 = hserin pin normal i/o

Do
b0 = 0 ; reset the b0 value = the status byte

Do
HSerIn b0
pause 33
Loop Until b0 = $C0 ; stop loop als status byte (b) gelijk is aan 192

HSerIn b1

SerTxd(#b1, CR, LF)

Loop
 

tschrama

New Member
Well, there seems to be an optimum around 33ms + 1 ms pause to let the second midi byte arive.


Code:
#picaxe 08M2
setfreq m4                  ; m4 , m8 or m16
#terminal 4800              ; 9600 with m8 or 19200 with m16
HSerSetup B31250_4, %1000   ; _8 or _16

; NB midi message is fired every 100ms 
; pause 76ms dan 20% error 
; pause  5ms dan 6% error
; pause 33ms + 0ms dan 1.0% error 
; pause 33ms + 1ms dan 0.2% error
; pause 32ms + 1ms dan 1.6% error
; pause 40ms + 1ms dan 0.5% error
; pause 33ms + 2ms dan 0.6% error
; pause 33ms + 8ms dan 0.5% error
; pause 46ms + 1ms dan 0.3% error
; pause 46ms + 1ms uit loop dan 0.6% error

Do
  b0 = 0   ; reset the b0 value = the status byte
  Do    	  
    pause 33
    HSerIn b0
    pause 1
  Loop Until b0 = $C0   ; stop loop als status byte (b0) gelijk is aan 192
  'pause 1
  HSerIn b1      
  SerTxd(#b1, CR, LF)
Loop
 
Last edited:

AllyCat

Senior Member
Hi,

Code:
Do    	  
    HSerIn b0
    pause 33
  Loop Until b0 = $C0   ; stop loop als status byte (b) gelijk is aan 192
   HSerIn b1
That seems to be an enormous delay, particularly inside the "waiting" loop. If the individual bytes are separated by a period as long as 33 ms, then you shouldn't have any synchronising problems at all.

If there is a sufficient time delay between bytes, then you should be checking to see if a byte has been received, as described in the HSERIN command reference; "If there is received data in the internal buffer the first byte is copied into the variable, if not the variable is left unaltered and the program continues on the next line".

If the "data" byte can be any (byte) value then you should receive it into a "word" variable to test if anything has been received. Also, perhaps you need to "flush" the buffer before you test for the status byte. For example:
Code:
w1 = 256
hserin b0 : hserin b0    ; Flush the serial input buffer (b0 is not used) 
do
   hserin w1
loop until w1 < 256   ; Repeat until a byte has been received
Cheers, Alan.
 

tschrama

New Member
I am think it has something to do with a new b1 byte arriving while allready switching form reading byte 0 to reading byte 1.

BTW, the midi-message sender was using this code:
Code:
hserout 0,(192,program)
(192 being the Midi status byte, 'program' being the midi data byte) every 100ms. There is none addition time between the status-byte and data-bytes being send at all.

The 1ms pause is to be sure that the data-bytes has been finished being send, after a '192'status byte has been catched.
The 33ms is to poll as slow as possible, while still never miss a 192 status byte, which can be send every 100ms.

If the errors have something to do with synchronization, I need a smart way to catch a '192' (b0) value, then be sure that the data-byte is written, before attempting to read the data-byte (b1). ..

Well I still cannt get my head around it...
 
Last edited:

hippy

Technical Support
Staff member
If it is corruption due to data arriving while the data is being read ( and I believe that has been discussed in the past ), there are two options -

1) Put a PAUSE between sending the $C0 byte and the byte which indicates the program number. You then need to check the second byte is received, not assume it will have been as in your earlier code.

2) Peek the internal status SFR to determine when the first byte is received, then PAUSE long enough for both bytes to have be received before reading anything. Something like ...

Code:
Do
  Do
    PeekSfr ?, b0
  Loop Until bit? = ?
  Pause  ?
  HSerIn b0
  HSerIn b1
Loop
It would be recommended to still have checks that b0 = $C0 and b1 < $80 to avoid issues when it gets out of sync. It will, even if seeming to work.
 

tschrama

New Member
The more I experiment with this, the more I get convinced that
1] data gets corrupted due to data arriving while the data is being read (If I let the chip poll many times per second, more corrupted arrive, If I limit it to slight more than 10Hz , only about 0.5% of data gets corrupted)

and/or

2] a small pause is needed to let the 2nd byte fully arrive before attempting to read it. (Since I added the "pause 1" in between receiving bytes b0 and b1, I no longer get duplicate numbers)

So I want to try to use Hippy's "peek the internal status SFR " method. Gues I need to go to the chips datasheet to find the correct SFR position value...
 

AllyCat

Senior Member
Hi,

Yes, several different sections of the relevant base PIC data sheet and also the PICaxe's PEEK/POKESFR commands. But I'm not sure that you're using the HSERIN command "correctly" yet.

I refrained from giving this thread link before, because it's rather complicated, but you might find the information that you need. There is quite a lot of input from "Technical" there.

Cheers, Alan.
 

hippy

Technical Support
Staff member
So I want to try to use Hippy's "peek the internal status SFR " method. Gues I need to go to the chips datasheet to find the correct SFR position value...
It was a bit late at when I posted and did not have time to dig through datasheets. This is what I think it should be ...

Code:
#Picaxe 18M2
#Terminal 4800
#No_Data

Symbol PIR1      = $11
Symbol PIR1_RCIF = bit5

                    ;  .------ 0 = Enable Hserin pin
                    ;  |.----- 1 = Disable Hserout pin
                    ;  ||.---- 0 = Don't invert input
                    ;  |||.--- 0 = Don't invert output
                    ;  ||||.-- 0 = No scratchpad receive
Receiver:           ;  |||||
  HSerSetup B31250_4, %01000
  Pause 2000
  SerTxd( "Started", CR,LF )
  Do
    Do
      Do
        PeekSfr PIR1, b0
      Loop Until PIR1_RCIF = 1
      Pause 100
      w0 = $FFFF
      HSerIn b0
      HSerIn b1
    Loop Until b0 = $C0 And b1 < $80
    SerTxd( "Program=", #b1, CR, LF )
  Loop
I would test it with this code in the transmitter ...

Code:
#Picaxe 08M2
#Terminal Off
#No_Data
                    ;  .------ 1 = Disable Hserin pin
                    ;  |.----- 0 = Enable Hserout pin
                    ;  ||.---- 0 = Don't invert input
                    ;  |||.--- 0 = Don't invert output
                    ;  ||||.-- 0 = No scratchpad receive
Transmitter:        ;  |||||
  HSerSetup b31250_4, %10000
  Do
    Pause 1000
    HSerOut 0, ( $C0, b0 )
    b0 = b0 + 1 // 100
  Loop
Both sets of code should work on any M2 chips. If it works; the next step is to reduce the PAUSE 100 in the receiver to make it as responsive as possible.
 

tschrama

New Member
Good read Allycat!

I definitely think something wrong is happening with reading the Hserin buffer while it's being written. Since a midi-byte takes about 0.32millisecond, and my midi-transmitter is sending a message every 100ms..... and I seem to bottum out at about 0.3% errors... 0.32ms is about 0.3% of 100ms.

From reading Technical's reply's: it seems to me the errors are due to the fine-details of "the RCIF flag" , and not in the way the 08M2 PIC deals buffer overruns by silently reset the UART.

Big thanks for that link!
 

tschrama

New Member
Great, going to try and get my head around that code and try it out. Not tnight I am afraid.

Big thanks for your input Hippy!!
 

tschrama

New Member
Fantastic..!!!!! 2000 messages without skipping a single beat, no errors, no missing messages and no duplicate values. 100% correct!!!
 
Top