Hey all;
I'm looking into designing and building a PIC based MIDI controller that will send MIDI messages to a laptop to switch on and off different VST effects. Surprisingly, I'm having a very difficult time finding any info on any PIC based MIDI controller. Has anyone made one, or can anyone point me in the right direction? I have some idea of what to do hardware wise, but seeing some sample code would be a huge help.
Thanks
I built a PIC based MIDI foot controller long ago. But it was programmed in ASM. What PIC are you planning to use? Do you need to program it in C?
Ermmm...google? There's a ton of stuff on PIC MIDI controllers, surely?
That search query "PIC MIDI controllers" brings up a lot of useful results. What makes you say this is very difficult?
Tom
Ive got a couple of 16f737s around, and I figured I'd use one of them. I've programmed my previous PIC projects in asm.
ED, I have searched it up, but so far have found few useful results. There are a lot more things for Arduino, and a lot of the PIC articles just kind of explain how to accomplish it without going into any useful detail. Do you have a link that would be helpful?
Thanks
Disclaimer: Possible conflict of interest in this post since I am the creator of FlexFX and therefore may not be the most objective person to respond ...
Look at FlexFX the module. It's a small 1"x1.4" module with a USB connector on it. It has pins for power, ground, I2C, UART, and I2S (for audio). Can be USB powered or self powered. I2C and UART pins can be used for GPIO to sense foot switches or you can use I2C to interface to a port expander that interfaces to foot switches. FlexFX was intended to support stomp box and high channel count USB/I2S audio applications with lots of DSP processing power ... of which you need none of.
But you can still use a FlexFX module and the free SDK (on GitHub) to sense switches and send MIDI to the host computer over USB. You'd then have a USB MIDI controller with GPIO's. The host computer would then see a MIDI device when you plug this in via USB. If interested I can write up a quick code example - this might be a good example to add to the GitHub doco.
FlexFX will also have MIDI beat clock, MTC and MPC generation support.
https://github.com/markseel/flexfx_kit/blob/master/README.md
BTW the production of FlexFX modules, potentiometer/ADC/DAC main boards (to plug module into), and custom stomp box cases are being KickStarted in a couple of weeks.
Hi Tempus,
I've got some example code. It's for a set of MIDI bass pedals that I got off a dead electric organ and turned into a midi pedalboard.
I was going to post it here, but it exceeds the 20K message limit (didn't know about that til now!). Email me (link below) and I'll send you a copy:
https://electricdruid.net/contact/ (https://electricdruid.net/contact/)
It uses a buffer to store outgoing MIDI bytes, and an interrupt pulls another byte out of the buffer when the last one has sent. That way the main code isn't held up waiting for a MIDI message to send. You can quickly dump a whole lot of stuff into the buffer and you're good to go. It will continue to send in the background. It uses 16F73 which isn't a million miles for the chip you suggested. If you're handy with ASM, you should be able to pull out the MIDI bits you need and chuck the rest away.
Tom
I have a MIDI controller project using a 16f88
check it out here:
http://www.diystompboxes.com/smfforum/index.php?topic=91291.0 (http://www.diystompboxes.com/smfforum/index.php?topic=91291.0)
You can see how the basic MIDI is done in the code.
Mat
You guys are awesome (and much more adventuresome than me)! Thank you so much for the help. Seeing the sample code clears up a lot of things and gets me on my way nicely. Your projects are much more involved and complex than mine, but I think I've figured some things out:
All I want to do is send a MIDI program change message to my laptop (via a recording interface), so I don't really need most of the stuff you guys have in your designs do I? Could I get away with just:
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to MIDI device
call senddla ;wait 35ms for byte to be sent
movlw 0x00 ;program #0
movwf TXREG ;send prog change to #0 to MIDI device
?
I have the delay in there because my osc is running at 4MHz, so I figure the first byte won't be sent by the time the 2nd byte gets there without it. Oh, and I'm pretty sure I sorted out how to set up my UART.
Thanks again
Yes, if you don't mind hanging about waiting for the bytes to send, something like that will work.
One way to minimise the wait is to do a polled loop rather than a fixed delay of X msecs. You should be able to test either TXIF (in PIR4) or TRMT in TXSTA, with slightly different but equivalent results.
So something like this:
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to MIDI device
<memory page?>
btfss PIR4, TXIF ; Has TXREG cleared yet?
goto. $-1
; Yes, it's clear, so send the next byte
movlw 0x00 ;program #0
movwf TXREG ;send prog change to #0 to MIDI device
HTH,
Tom
Hey Tom
Thanks again for your ongoing help.
That's definitely a better way to do it. A couple of questions though:
1. what do you mean by <memory page?>
2. What does $-1 do? I've seen this command in various code examples, and searches don't turn anything up - I figured this one out - it just goes to the previous line, so TXIF will continue to be tested until it is actually clear, yes?
3. Does the TXREG register clear once the byte has been sent (I'm assuming yes)?
Thanks
Quote from: tempus on March 31, 2018, 09:07:38 AM
Hey Tom
Thanks again for your ongoing help.
That's definitely a better way to do it. A couple of questions though:
1. what do you mean by <memory page?>
I mean that I can't remember off the top of my head what memory page PIR4 is in, and you may not be in the correct page when you enter the code, in which case you'd have to change it. I'm talking about "bank switching" which is another way of saying the same thing. The <memory page?> was just a reminder to think about which bank you're in.
The code I sent you included some macros for bank switching - just type "Bank0" or "Bank1" or whatever and the compiler pastes in the correct code to set up the RP0 and RP1 STATUS flags.
Quote
2. What does $-1 do? I've seen this command in various code examples, and searches don't turn anything up - I figured this one out - it just goes to the previous line, so TXIF will continue to be tested until it is actually clear, yes?
Yep, exactly. In some situations this can be dangerous because you can finish up in a loop which never ends. But if you're reasonably sure that the condition *has* to occur sooner or later, it's safe enough. In this case, we can be sure the byte will send - we just don't know if it will be received!
Quote
3. Does the TXREG register clear once the byte has been sent (I'm assuming yes)?
I don't know, and it doesn't matter. What we do know is that once it's been sent, we can safely stick another byte in there ready for sending. Whether it gets zeroed or not in between isn't really important.
Quote
Thanks
You're welcome.
HTH,
Tom
Making a MIDI controller using a Teensy LC (https://www.pjrc.com/teensy/teensyLC.html) would be very easy way since you've got the Arudino MIDI library at your disposal, as well as Debounce and Rotary Encoder libraries ,not to mention analog libraries for reading pot values.
Disclosure, I'm the creator of the Teensy Guitar audio board (https://www.tindie.com/products/Blackaddr/arduino-teensy-guitar-audio-shield/?pt=ac_prod_search), so I'm biased as well in favour of the Teensy platform. The Teensy LC does not work with the Audio library (no sound procesing) but it's ideal for a MIDI controlller IMO.
I totally agree that using a Teensy LC or any other variant is a very easy way to make a MIDI controller and there are many available libraries that make this simple.
However...I can't really go along with such a course of action because it's COMPLETE OVERKILL!! Even the Teensy LC is a 32-bit, 48MHz processor with 62K flash and 8K RAM.
The 16F767 you were talking about is a 20MHz 8-bit chip with 8KWord of Flash and 368 *bytes* of RAM. And it's more than adequate for the task. If you don't believe me, check out the processors in any synth from the 1980's and then get an idea of what they made them do!! About ten times what we'd expect nowadays, in short.
So, yeah. One solution to any problem of this sort is "throw more computing power at it" and often that's the easiest solution. It may not be cheapest, but unless you're building 10,000 or 100,000 units, how much do you care about the extra $5? Probably not much. Multiply up and it becomes a big deal, but that's not our situation so go for your life.
Sorry, not trying to be a downer, and specifically not trying to diss the excellent work of those in the Teensy world (Blackaddr amongst them). I'm just a big fan of "doing more with less", rather than "doing less with more". I'm old-skool, I guess - I am...learned assembly on 6502.
Tom
Quote from: ElectricDruid on March 31, 2018, 06:21:47 PM
I totally agree that using a Teensy LC or any other variant is a very easy way to make a MIDI controller and there are many available libraries that make this simple.
However...I can't really go along with such a course of action because it's COMPLETE OVERKILL!! Even the Teensy LC is a 32-bit, 48MHz processor with 62K flash and 8K RAM.
The 16F767 you were talking about is a 20MHz 8-bit chip with 8KWord of Flash and 368 *bytes* of RAM. And it's more than adequate for the task. If you don't believe me, check out the processors in any synth from the 1980's and then get an idea of what they made them do!! About ten times what we'd expect nowadays, in short.
So, yeah. One solution to any problem of this sort is "throw more computing power at it" and often that's the easiest solution. It may not be cheapest, but unless you're building 10,000 or 100,000 units, how much do you care about the extra $5? Probably not much. Multiply up and it becomes a big deal, but that's not our situation so go for your life.
Sorry, not trying to be a downer, and specifically not trying to diss the excellent work of those in the Teensy world (Blackaddr amongst them). I'm just a big fan of "doing more with less", rather than "doing less with more". I'm old-skool, I guess - I am...learned assembly on 6502.
Tom
I agree, but lately I find myself going the arduino route as well for this kind of projects. You can find arduino nano clones for around 3 $, and it's very convenient for a number of reasons:
-Tons of libraries available
-Easy programming
-No need for a programmer
-Power regulator included
On the downside, I still haven't found a way to emulate it that I like. I always develop and debug my software in an emulator, and MPLAB was my friend for PICs.
BTW, teensy is really an expensive overkill here. There are much cheaper small arduinos that can do the job
After having double (and triple checked) my code, I have been unsuccessful in sending any MIDI data. I've changed things slightly so that now I'm attempting to send button press messages using CCs (the software that I'm trying to control via MIDI accepts CC data as inputs). Here's an example of my switching code:
movlw 0xB1 ;status byte = CC on Ch1
movwf TXREG ;send status CC on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x00 ;CC 0
movwf TXREG ;send CC 0 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x7f ;CC value 127
movwf TXREG ;send CC value 127 to laptop
Does it look OK? Also, I noticed this when reading through the 16f737 data sheet:
"The TXREG register is loaded with data in
software. The TSR register is not loaded until the Stop
bit has been transmitted from the previous load. As
soon as the Stop bit is transmitted, the TSR is loaded
with new data from the TXREG register (if available)."
What is a Stop bit and how do I transmit one? Is it already present in my MIDI command/data bytes?
Also, when I was testing my wiring, I discovered that there was +5v sitting on my TX pin. Is that normal? Referring again to the datasheet, I found:
"Bit SPEN (RCSTA<7>) and bits TRISC<7:6> have
to be set in order to configure pins RC6/TX/CK and
RC7/RX/DT as the Universal Synchronous
Asynchronous Receiver Transmitter."
I have this in my code:
movlw 0x00 ;load w with 0's
movwf TRISC ;make PORTC outputs
which should make PORTC all outputs, but it appears that I have to do something else to enable the TX pin, but I'm not sure what. I know that TRISC<7> means "TRISC bit 7, but what does TRISC<7:6> mean?
Thanks
Quote from: tempus on April 02, 2018, 10:41:51 AM
Here's an example of my switching code:
movlw 0xB1 ;status byte = CC on Ch1
movwf TXREG ;send status CC on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x00 ;CC 0
movwf TXREG ;send CC 0 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x7f ;CC value 127
movwf TXREG ;send CC value 127 to laptop
Does it look OK?
Yes, it looks ok. PIR1 and TXREG are both in Bank0, so that should be fine.
Quote
Also, I noticed this when reading through the 16f737 data sheet:
"The TXREG register is loaded with data in
software. The TSR register is not loaded until the Stop
bit has been transmitted from the previous load. As
soon as the Stop bit is transmitted, the TSR is loaded
with new data from the TXREG register (if available)."
What is a Stop bit and how do I transmit one? Is it already present in my MIDI command/data bytes?
Yes. you don't have to send it yourself. As long as the UART is configured correctly for MIDI, it gets sent as part of the byte.
QuoteAlso, when I was testing my wiring, I discovered that there was +5v sitting on my TX pin.
Is that normal?
Yes.
Quote
Referring again to the datasheet, I found:
"Bit SPEN (RCSTA<7>) and bits TRISC<7:6> have
to be set in order to configure pins RC6/TX/CK and
RC7/RX/DT as the Universal Synchronous
Asynchronous Receiver Transmitter."
I have this in my code:
movlw 0x00 ;load w with 0's
movwf TRISC ;make PORTC outputs
which should make PORTC all outputs, but it appears that I have to do something else to enable the TX pin, but I'm not sure what. I know that TRISC<7> means "TRISC bit 7, but what does TRISC<7:6> mean?
It means "TRISC bits 6 through 7" - so both of them. <3:6> would be bits 3,4,5, and 6.
The SPEN bit ("EN" is an enable of some kind; "Serial Port" in this case) of the RCSTA register (one of the set-up/status registers for the UART) needs to be set too. Is that covered in your UART config code?
Sounds to me like you're very close. You've probably got one small bug in the UART config code.
Do you have a MIDI monitor so that you can see if you're sending valid bytes, even if not valid MIDI messages? I once struggled for ages with some MIDI code only to eventually connect it to a MIDI monitor and discover that it had been sending bytes correctly all along, but because I'd got a stray byte inversion command in the code somewhere (so 255 was being turned into 0) it wasn't producing any valid MIDI messages at all.
HTH,
Tom
Thanks again for your reply ED.
I do have a MIDI monitor, which shows no MIDI activity at all when I try to send a command. Searching more through the datasheet, I found this:
"When setting up an Asynchronous Transmission,
follow these steps:
1. Initialize the SPBRG register for the appropriate
baud rate. If a high-speed baud rate is desired,
set bit BRGH (see Section 11.1 "AUSART
Baud Rate Generator (BRG)").
2. Enable the asynchronous serial port by clearing
bit SYNC and setting bit SPEN.
3. If interrupts are desired, then set enable bit TXIE.
4. If 9-bit transmission is desired, then set transmit
bit TX9.
5. Enable the transmission by setting bit TXEN
which will also set bit TXIF.
6. If 9-bit transmission is selected, the ninth bit
should be loaded in bit TX9D.
7. Load data to the TXREG register (starts
transmission).
8. If using interrupts, ensure that GIE and PEIE
(bits 7 and 6) of the INTCON register are set."
I had already done these things, but does the order they are executed in matter? Here's what I've got in my code:
start bsf STATUS,RP0
bcf STATUS,RP1 ;bank 1
bsf OSCCON,IRCF2 ;set int osc 110=4Mhz
bsf OSCCON,IRCF1
bcf OSCCON,IRCF0
movlw 0x01 ;load w with 00000001
movwf SPBRG ;Set Transmit Baud Rate to 31.25KHz
movlw 0x22 ;load w with 00100010
movwf TXSTA ;Set up to transmit 8-bit, asynch, lo-speed, BRGH lo
movlw 0x0F ;conifgure all pins as
movwf ADCON1 ; digital inputs
bcf OPTION_REG,7 ;enable PORTB pullups
movlw 0x00 ;load w with 00000000
movwf TRISA ;make PORTA outputs
movlw 0xFF ;load w with 1's
movwf TRISB ;make PORTB inputs
movlw 0x60 ;load w with 01100000
movwf TRISC ;make PORTC outputs, enable TX and RX (pin 17 and 18)
bcf STATUS, RP0 ;bank 0
movlw 0x80 ;load w with 10000000
movwf RCSTA ;enable UART
movlw 0x00 ;load PORTA with 00000000
movwf PORTA ;turn all LEDs off
Anything out of line there?
Hi Tempus,
Ok, let's have a look.
First: Have you *actually got* a 4MHz clock? I usually do a LED flash "hello world" to test this. Something like this:
FlashLED:
bsf TEST_LED
nop
nop
nop
bcf TEST_LED
nop
nop
goto FlashLED
4MHz is a 1MHz instruction cycle, so 8 instructions gives us a 125KHz flash rate. Worth a check.
Next, you're using the low speed baud rate mode, so we have:
Baud Rate = FOSC/(64(X + 1))
You stick 1 into SPBRG, so X=1, so:
Baud rate = 4MHz/128 = 31250
So that looks fine.
Setting up the ports...Aha!
RC7 is RX, RC6 is TX. You've got those the wrong way around, Input should be Output, and vice versa.
Try that and see if it springs to life.
HTH,
Tom
Hey Tom;
Thanks again for your reply.
OK, so I loaded PORTC with 11000000, which had no effect on the results. Also, the datasheet says:
"Bit SPEN (RCSTA<7>) and bits TRISC<7:6> have
to be set in order to configure pins RC6/TX/CK and
RC7/RX/DT as the Universal Synchronous
Asynchronous Receiver Transmitter. "
Does that mean the both TRISC bits 7 and 6 need to be ones (which would make them both inputs), or do they mean set bit 7 to 1 and bit 6 to 0 (thus making 7 an in and 6 an out)? You would think that the TX bit would need to be configured as an output and RX an input....
Great idea to check the FOSC - I never would have thought of that technique. Checking with my scope I get a frequency of 110.1kHz, which would give 110.1 x 8 = 880.8 x 4 = 3.5232MHz. This would give us a baud rate of 3.5232MHz/128 = 27525. Is this close enough, or should I do some oscillator tweaking? Also, can we trust that my digital scope is actually giving an accurate reading of the flash rate? I messed around with OSCTUNE and got it up to 125KHz, but it had no effect on the results
On another note, when I build the project in MPASM, I get the following error messages:
...25 : Register in operand not in bank 0. Ensure that bank bits are correct.
26 : Register in operand not in bank 0. Ensure that bank bits are correct.
27 : Register in operand not in bank 0. Ensure that bank bits are correct.
etc
The line numbers are all commands executed on Bank 1 (which is set in the 1st 2 lines of code). I'm assuming that MPASM is in err, since we've demonstrated that the OSC has been set at 4MHz?
Quote from: tempus on April 03, 2018, 06:48:48 PM
Hey Tom;
Thanks again for your reply.
OK, so I loaded PORTC with 11000000, which had no effect on the results. Also, the datasheet says:
"Bit SPEN (RCSTA<7>) and bits TRISC<7:6> have
to be set in order to configure pins RC6/TX/CK and
RC7/RX/DT as the Universal Synchronous
Asynchronous Receiver Transmitter. "
Does that mean the both TRISC bits 7 and 6 need to be ones (which would make them both inputs), or do they mean set bit 7 to 1 and bit 6 to 0 (thus making 7 an in and 6 an out)? You would think that the TX bit would need to be configured as an output and RX an input....
Exactly! They mean that "you need to set the bits" not "the bits need to be set". You need TRISC set with bit 7 to 1/Input and bit 6 set to 0/output. With Bit 6 set to 1, you've got TX set as an input and the UARTs output can't reach the pin - nothing will happen.
Also, you mean you changed TRISC, not PORTC. Messing with PORTC won't do anything useful.
Quote
Great idea to check the FOSC - I never would have thought of that technique. Checking with my scope I get a frequency of 110.1kHz, which would give 110.1 x 8 = 880.8 x 4 = 3.5232MHz. This would give us a baud rate of 3.5232MHz/128 = 27525. Is this close enough, or should I do some oscillator tweaking? Also, can we trust that my digital scope is actually giving an accurate reading of the flash rate? I messed around with OSCTUNE and got it up to 125KHz, but it had no effect on the results
No, that probably means that I forgot that "goto" uses two instruction cycles, which makes that loop nine instructions. 1MHz/9 = 111KHz.
So your 'scope is close enough.
Quote
On another note, when I build the project in MPASM, I get the following error messages:
...25 : Register in operand not in bank 0. Ensure that bank bits are correct.
26 : Register in operand not in bank 0. Ensure that bank bits are correct.
27 : Register in operand not in bank 0. Ensure that bank bits are correct.
etc
The line numbers are all commands executed on Bank 1 (which is set in the 1st 2 lines of code). I'm assuming that MPASM is in err, since we've demonstrated that the OSC has been set at 4MHz?
Yep, it's useful to check that these lines match up with the ones where you've actually set Bank1. This is a "warning" not an "error", so its just a reminder and doesn't stop the code compiling. If it gets to bugging you, you can disable the warnings by sticking the following directive at the top of the code:
Errorlevel -302
I usually put this under the usual "INCLUDE <p16fxxx.inc>" line.
HTH,
Tom
Thanks again for your help Tom.
Moving 10000000 to TRISC had no effect on the outcome. I mentioned the error messages because I wondered if they may be having some effect on the programming.
I'm beginning to run out of ideas, but here are some other things I've uncovered:
"11.2 AUSART Asynchronous Mode
In this mode, the AUSART uses standard Non-Returnto-
Zero (NRZ) format (one Start bit, eight or nine data
bits and one Stop bit). The most common data format
is 8 bits."
My programming is sending the 8 bits (or at least it's supposed to), but where are the start and stop bits coming from? I haven't done anything in the programming to send them, have I? Here's the code for one of my button pushes:
lead call dbdelayd ;debounce switch down
; switching
bsf STATUS, RP0 ;bank 1
btfss TXSTA, 1 ;is TSR register empty?
goto $-1 ;yes, it's empty, load status byte
bcf STATUS, RP0 ;bank 0
movlw b'10110001' ;status byte = CC Ch1
; movlw 0xB1 ;status byte = CC on Ch1
movwf TXREG ;send status CC on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw b'00000001' ;CC 1
; movlw 0x01 ;CC 1
movwf TXREG ;send CC 1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw b'00111111' ;CC value 63
; movlw 0x7f ;CC value 127
movwf TXREG ;send CC value 63 to laptop
movlw 0x03 ;load PORTA with 00000011
movwf PORTA ;turn Lead LED on, send clear signal to other PIC
leadup btfss PORTB,1 ;lead switch hi?
goto leadup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTA, 0 ;turn clear bit off
goto switch ;check switches again
You can see that I added a check to see if the TSR register is empty, and commented out some of the hex and replaced it with binary in case that was the problem. Also, notice that after all of the MIDI code is sent an LED is lit. In every one of the tests since I posted here the LED has come on. Does this mean that the MIDI code is getting sent? I'm thinking yes, since each byte has to be sent for the register to be empty, and we're checking to make sure that the register is empty before proceeding to the next command. If the registers weren't empty (i.e. bytes weren't being sent) then the code would never get to the turn on the LED stage, but would get stuck in the flag-check loop.
Can you see any problems here?
Ok, so if the LED is getting lit, then the code runs and the bytes get sent, in some form or another. That's all good.
What can you see on the TX output? Are you getting any activity? Get the o'scope on that output pin, and if you're sending three bytes, you should be able to see the line jumping up and down. If not, I'd guess the pin assignments still aren't right or the TX output isn't turned on or something of the type.
In fact, I'd guess that the set-up code still isn't right anyway. You can spend days bashing your head against some "send byte" routine, only to discover that you had some register set wrong way back in the "setup UART" routine. So we need to be sure that's sorted first. Most problems are getting the thing set up right. Often this stuff is easy to use once it's running correctly, but the hard part is getting there, since it doesn't give you any clues as to what's wrong - it just simply doesn't work.
Start by having a look at that TX output. Is it moving? If it is, we're getting warm, and all we've got to do it get it talking MIDI. If not, we first need to get it talking, then we need to get it talking MIDI. One step at a time and we'll get there (slowly maybe, but we will!).
Tom
Thanks again Tom.
Yes, that's been my experience with the other uC projects I've done - it takes a while to sort out how to get everything set up correctly, but once it is, it's fairly easy to work with things.
Good idea regarding the scope; I tested the TX pin and found that there was activity on it, but it's hard to see exactly what. My digital scope has an output window that summarizes what's been probed as well as the usual trace window. The output window showed f of 7.8KHz, +pulse width of 32uS and -PW of 96uS every time I grounded an input, and the trace screen sometimes (but not always) displayed (very quickly) a several drops (like negative going square waves - could have been 8 bits long, but it was too quick to see) from the +5v it was reading without grounding the input.
From this I'd conclude that there is something at the TX output, which would mean that the UART at least should be set up properly. This means that something is wrong with the bytes I'm sending or that something's wrong with my MIDI connection, unless it's possible that the UART is set up mostly correctly but is not sending information correctly.
What are your thoughts?
My thought is that 7.8KHz x 4 = 31.2, which looks suspiciously like it's 31.25KHz MIDI baud rate divided by four. Is it running at quarter speed? That would explain why your MIDI monitor isn't seeing anything.
Tom
Good eye man
We know that FOSC is 4MHz, thanks to the flash LED test you devised. There is also no value for X (it would have to be 7) in the BRG speed tables that would result in a 7.8KHz Baud rate (incidentally, and perhaps significantly, I left off the last digit in the frequency - the scope is showing 7.81KHz, adding weight to your argument). Do you mean is the BRG running at 1/4 speed? How would I test that?
Here's my code again, in case I've changed anything. Do you see any problems?
start bsf STATUS,RP0
bcf STATUS,RP1 ;bank 1
bsf OSCCON,IRCF2 ;set int osc 110=4Mhz
bsf OSCCON,IRCF1
bcf OSCCON,IRCF0
movlw 0x01 ;load w with 00000001
movwf SPBRG ;Set Transmit Baud Rate to 31.25KHz
movlw 0x22 ;load w with 00100010
movwf TXSTA ;Set up to transmit 8-bit, asynch, lo-speed, BRGH lo
;movlw 0x10 ;load w with 00010000
;movwf PIE1 ;enable AUSART transmit interrupt
movlw 0x0F ;conifgure all pins as
movwf ADCON1 ; digital inputs
bcf OPTION_REG,7 ;enable PORTB pullups
movlw 0x00 ;load w with 00000000
movwf TRISA ;make PORTA outputs
movlw 0xFF ;load w with 1's
movwf TRISB ;make PORTB inputs
movlw 0x80 ;load w with 10000000
movwf TRISC ;make PORTC outputs, except RC7 (RX - pin18)
bcf STATUS, RP0 ;bank 0
movlw 0x80 ;load w with 10000000
movwf RCSTA ;enable UART
movlw 0x00 ;load PORTA with 00000000
movwf PORTA ;turn all LEDs off
Thanks again Tom
Wait though... Didn't you say that a 4MHz clock speed is a 1MHz instruction cycle? Would that /4 also apply to the measured baud rate?
Quote from: tempus on April 05, 2018, 08:13:10 PM
Wait though... Didn't you say that a 4MHz clock speed is a 1MHz instruction cycle? Would that /4 also apply to the measured baud rate?
That depends on whether the BRG uses the raw oscillator or the instruction clock. That varies from peripheral to peripheral.
Checking it again now, it definitely says "Fosc" which is the oscillator frequency, not the instruction clock, so it's 4MHz like we thought.
Checking the MIDI spec too, we're expecting a MIDI byte (10 bits in total - 8 data bits, plus start and stop "wrappers") to take 320uS. Now, you said you'd got 32uS pulses, which would be right. 7.8KHz data rate might be a red herring - we're not measuring a nice regular square wave after all. If the individual pulses are 32usec long, I'd say that's looking pretty likely.
Another test which I like to do is send an incrementing count. So you send a MIDI byte of 0, then 1, then 2 etc up to 255. Obviously this isn't a valid MIDI message, but the repeating pattern is easily viewable on a 'scope and you can see if it's doing what you expect.
I'm starting to run out of ideas though. We've looked at most of the obviously stuff already. There must be something in there somewhere...
Regards,
Tom
I plugged the MIDI out of a keyboard into my scope to see what I got from it. The results were inconsistent (probably due to the difficulty in getting a suitable sampling rate), but revealing nonetheless. A note on/not off event registered a frequency of 7.81KHZ with a + and - PW of 64 uS. Program changes were less consistent, but gave similar results - either a frequency of 7.81KHz or a multiple of it, and PWs of 32 - 128uS in 32uS intervals. From this, it seems like what I'm sending is fairly typical.
I'm wondering if the problem is in the actual MIDI messages I'm sending, i.e., wrong binary numbers, etc that may make what I'm trying to transmit not an actual MIDI message. Do these look right?
lead call dbdelayd ;debounce switch down
; switching
Bank1
btfss TXSTA, 1 ;is TSR register empty?
goto $-1 ;yes, it's empty, load status byte
Bank0
movlw b'10110001' ;status byte = CC Ch1
movwf TXREG ;send status CC on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw b'00000001' ;CC 1
movwf TXREG ;send CC 1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw b'00111111' ;CC value 63
movwf TXREG ;send CC value 127 to laptop
movlw 0x03 ;load PORTA with 00000011
movwf PORTA ;turn Lead LED on, send clear signal to other PIC
leadup btfss PORTB,1 ;lead switch hi?
goto leadup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTA, 0 ;turn clear bit off
goto switch ;check switches again
Thanks again Tom
Maybe write the bytes in hex rather than binary, since that's usually how MIDI messages are expressed. But binary's fine if you're comfortable with it. I guess it depends what you've got on your crib sheet!
But that should be valid. &B1 is a control change on channel *2* not channel 1 (0-15 becomes channels 1-16). But that shouldn't matter - it's still valid data. then you send a control change number, and some data - fine; anything will do.
So you can see the TX line jumping up and down, and it's doing it in 32usec segments...this is all good. But you've still got nothing on the MIDI monitor?
What does the hardware look like between TX and whatever the MIDI monitor is? Perhaps that's the problem, and all this digging through software will get us nowhere.
Tom
Yes, I switched the code from hex to binary thinking that my hex might be inaccurate and that was the problem, and earlier had switched the channel # in case my MIDI device was fussy about the 0 being 1 thing. My status byte starts with 1011 (designating CC) and ends with 0001 (for MIDI channel 2) giving me a binary number 10110001 for my status byte. Then I've used 00000001 for CC #2, then 00111111 for (randomly picked) CC value 63. Is all that correct?
On the hardware end, I've connected pin 17 (TX) to a 220 ohm resistor to pin 4 on the MIDI jack, pin 2 (on the MIDI jack) to ground and pin 5 (on the MIDI jack) to a 220 ohm resistor to +5v. I'm not doing any receiving, so nothing else is connected to the MIDI jack, and nothing is connected to the RX pin. Is everything good there?
Thanks again Tom
Yes, that's all fine, as far as I can see. Your data being sent is fine. Your hardware sounds fine - after all, it ain't super-complicated for this job!
Ok, let's try something else. What happens if you just send &F8 (MIDI clock) or &FA (Start)? Those bytes don't depend on anything else to be interpreted, so they're about as minimal as it gets.
If that fails, do the sequential count test and see if you can see a binary count going up and up on the scope.
Tom
One other thing I forgot to mention about the MIDI keyboard probe - it is not at 5v like the uC is.
MIDI is a 5mA current loop, so the voltage isn't important.
Unless it's one of those "MIDI powered" gizmos that ignores the specification and assumes that there's X volts available. They might or might not work. Even in the 1980's when practically everything ran MIDI at 5V they weren't that reliable. Nowadays with a slew of 3.3V (and less) processors flying about, the idea is even worse.
Tom
I tried the 0xF8 idea, with no luck. Using your counting idea, I tried this code into a scope:
test movlw 0x00 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x01 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x02 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x03 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x04 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x05 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x06 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x07 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x08 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x09 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
goto test
(most of the commenting can be ignored; I did a lot of copy and pasting and just didn't bother to clear out the comments). This did not produce the results I was expecting to see, so I thought I'd ask. I was expecting to see 1 pulse , followed by 2 pulses, etc., but instead there almost seems to be random pulses on the scope. The output window tells me that the + pulse width is always 32uS, but the -PW varies from 32 to 288uS in 32uS intervals. The output window results seem logical, but I can't actually slow things down enough to see a 32uS progression; I have to freeze the display at various points to get this, but it's probably safe to assume that it's going 32, 64, 96, 128, 160, 192, 224, 256 and 288 (though I never actually saw the 224 and 256).
Is this what I should be seeing?
Update to my last post:
I modified the code somewhat to insert a ~1s delay after each of the 10 bytes were sent to turn on an LED followed by another ~1s delay for the LED to turn off. This caused the LED to turn on and off 10 times. From this I've concluded that the instructions associated with each transmission are being completed. This also slowed down the transmissions to the scope so I could see things more clearly. In order for the output window to display results on the screen long enough for me to actually read them, I had to slow down the sampling rate such that I could see the pulse on the screen but without sufficient definition to read it. Going by the output window's results, I got:
32, 224 32, 192 64, 192 32,160 32, 32 64, 156 92, 160 25, 128 32, 64
with the 1st number in the pair being the +PW and the 2nd being the -PW. So, while I am getting 10 distinct transmissions, the PWs are not what I would have expected.
What do you think?
I think you're doing it the hard way. A basic binary count going out could be done something like this:
test move COUNT, w ; Get current count
movwf TXREG ; Stick it in register to send
btfss PIR1, TXIF ; Has TXREG cleared yet?
goto $-1 ; No, so go back and keep checking
; OK, it's cleared, the byte is sent
incf COUNT, f ; Move to the next value
goto test
I haven't tried it, but that's the gist. It'll produce an incrementing binary count on the output, from 0-255 (If you wanted to limit it to data bytes, you could AND the WREG with 127 before you stick it into TXREG). That means that you'll see the ten bits of each MIDI byte (Start bit, data byte, Stop bit) and the middle 8 bits will be incrementing in binary. That is to say, they'll be doing something like this:
0000
0001
0010
0011
0100
0101
0110
0101
0111
1000
1001
1010
1011
1100
1101
1110
1111
Now, there's something important to notice there, which is that each column flashes on and off half as fast as the column to its right. E.g. the rightmost column goes on then off every time, the next column stays on twice then goes off twice, next column is off four times, then on four times, etc.
That has the (useful) effect of slowing things that we can't keep up with down to a speed we can keep up with. If those bytes are coming in at roughly 3 per msec as they should be, the first column is flashing at 1562Hz, but the next one is only at 781Hz, and the next at 390, and the next at 195, and the next at 97, and the next at 48, the next at 24, and the last at 12.2Hz. That forms a distinctive pattern of flickering that'll you'll recognise. If it's going too fast, rather than sticking a delay between the bytes, change the BRG setting and send the output at a much slower speed. You need to be sure that the bytes you're outputting are the bytes you think you're outputting, and the best way to do that is to *see* them.
HTH,
Tom
Do you have your full code? I can try to test it on my side and see if I can spot something...
Mat
Do you have any means to monitor the serial output? something like this is useful to monitor from a term software in a PC.
https://www.ebay.com/itm/CP2102-USB-2-0-to-UART-TTL-6PIN-Module-Serial-Converter-Adapter-Red-Silver-TS/222617611551?epid=1286993754&hash=item33d50b2d1f:g:FqYAAOSwxbpZl5x6 (https://www.ebay.com/itm/CP2102-USB-2-0-to-UART-TTL-6PIN-Module-Serial-Converter-Adapter-Red-Silver-TS/222617611551?epid=1286993754&hash=item33d50b2d1f:g:FqYAAOSwxbpZl5x6)
at least to check the bytes are sent correctly and at the proper speed.
Hi Mat;
Thanks for your reply. Here is the code I'm using:
;=============PIC MIDI pedalboard===============2018 03 29===
list p=pic16f737
#include <P16F737.inc> ; processor specific variable definitions
radix hex
;
__CONFIG _CONFIG1, _CP_OFF & _CCP2_RB3 & _DEBUG_OFF & _VBOR_2_0 & _BOREN_0 & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO
__CONFIG _CONFIG2, _BORSEN_0 & _IESO_OFF & _FCMEN_OFF
;------------------------------------------------------------
; Counter locations
cblock 0x20
dbcount
dc1
dc2
dc3
endc
;------------------------------------------------------------
;----------------------------------------
; Bank Switching Macros
Bank0 macro ; Select register bank 0
bcf STATUS,RP0
bcf STATUS,RP1
endm
Bank1 macro ; Select register bank 1
bsf STATUS,RP0
bcf STATUS,RP1
endm
;----------------------------------------
org 0x000
;
;Initialization
start Bank1
bsf OSCCON,IRCF2 ;set int osc 110=4Mhz
bsf OSCCON,IRCF1
bcf OSCCON,IRCF0
Bank0
movlw 0x80 ;load w with 10000000
movwf RCSTA ;enable UART
Bank1
movlw 0x22 ;load w with 00100010
movwf TXSTA ;Set up to transmit 8-bit, asynch, lo-speed, BRGH lo
movlw 0x01 ;load w with 00000001
movwf SPBRG ;Set Transmit Baud Rate to 31.25KHz
movlw 0x0F ;conifgure all pins as
movwf ADCON1 ; digital inputs
bcf OPTION_REG,7 ;enable PORTB pullups
movlw 0x00 ;load w with 00000000
movwf TRISA ;make PORTA outputs
movlw 0xFF ;load w with 1's
movwf TRISB ;make PORTB inputs
movlw 0x80 ;load w with 10000000
movwf TRISC ;make PORTC outputs, except RC7 (RX - pin18)
Bank0
movlw 0x00 ;load PORTA with 00000000
movwf PORTA ;turn all LEDs off
;Main Program
switch btfss PORTB,0 ;bypass switch hi?
goto bypass ;no - switch pressed
btfss PORTB,1 ;lead (1) switch hi?
goto test ;no - switch pressed
btfss PORTB,2 ;chdla (2) switch hi?
goto chdla ;no - switch pressed
btfss PORTB,3 ;cl (3) switch hi?
goto cl ;no - switch pressed
btfss PORTB,4 ;clchdla (4) switch hi?
goto clchdla ;no - switch pressed
btfss PORTB,5 ;cllngdla (5) switch hi?
goto cllngdla ;no - switch pressed
btfss PORTB,6 ;wah (6) switch hi?
goto wah ;no - switch pressed
btfss PORTB,7 ;clear LEDs from other PIC in hi?
goto clear ;no - lo
goto switch ;check all switches again
; Switch Actions
bypass call dbdelayd ;debounce switch down
; switching
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x00 ;patch 0
movwf TXREG ;send patch change 0 to laptop
movlw 0x01 ;load PORTA with 00000001
movwf PORTA ;turn all LEDs off, send clear signal to other PIC
bypassup btfss PORTB,0 ;bypass switch hi?
goto bypassup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTC, 0 ;turn clear bit off
goto switch ;check switches again
lead call dbdelayd ;debounce switch down
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x01 ;patch 1
movwf TXREG ;send patch change 1 to laptop
movlw 0x03 ;load PORTA with 00000011
movwf PORTA ;turn Lead LED on, send clear signal to other PIC
leadup btfss PORTB,1 ;lead switch hi?
goto leadup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTA, 0 ;turn clear bit off
goto switch ;check switches again
chdla call dbdelayd ;debounce switch down
; switching
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x02 ;patch 2
movwf TXREG ;send patch change 2 to laptop
movlw 0x05 ;load PORTA with 00000101
movwf PORTA ;turn chdla LED on, send clear signal to other PIC
chdlaup btfss PORTB,2 ;chdla switch hi?
goto chdlaup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTC, 0 ;turn clear bit off
goto switch ;check switches again
cl call dbdelayd ;debounce switch down
; switching
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x03 ;patch 2
movwf TXREG ;send patch change 3 to laptop
movlw 0x09 ;load PORTA with 00001001
movwf PORTA ;turn cl LED on, send clear signal to other PIC
clup btfss PORTB,3 ;lead switch hi?
goto clup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTC, 0 ;turn clear bit off
goto switch ;check switches again
clchdla call dbdelayd ;debounce switch down
; switching
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x04 ;patch 4
movwf TXREG ;send patch change 4 to laptop
movlw 0x11 ;load PORTA with 00010001
movwf PORTA ;turn clchdla LED on, send clear signal to other PIC
clchdlup btfss PORTB,4 ;lead switch hi?
goto clchdlup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTC, 0 ;turn clear bit off
goto switch ;check switches again
cllngdla call dbdelayd ;debounce switch down
; switching
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x05 ;patch 5
movwf TXREG ;send patch change 5 to laptop
movlw 0x21 ;load PORTA with 00100001
movwf PORTA ;turn cllngdla LED on, send clear signal to other PIC
cllngdup btfss PORTB,5 ;lead switch hi?
goto cllngdup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTC, 0 ;turn clear bit off
goto switch ;check switches again
wah call dbdelayd ;debounce switch down
; switching
movlw 0xC0 ;status byte = program change on Ch1
movwf TXREG ;send status program change on Ch1 to laptop
btfss PIR1, TXIF ;has TXREG cleared yet?
goto $-1 ;yes, it's clear, so send the next byte
movlw 0x06 ;patch 6
movwf TXREG ;send patch change 6 to laptop
movlw 0x21 ;load PORTA with 01000001
movwf PORTA ;turn wah LED on, send clear signal to other PIC
wahup btfss PORTB,6 ;lead switch hi?
goto wahup ;no - switch pressed, check again
call dbdelayu ;debounce switch up
bcf PORTC, 0 ;turn clear bit off
goto switch ;check switches again
clear call dbdelayd ;debounce switch down
; switching
movlw 0x00 ;load PORTA with 00000000
movwf PORTA ;clear all LEDs
goto switch ;check switches again
end
Also, I'm using MIDI-Ox on my laptop to monitor the MIDI transmissions.
Thanks for your help
Hi Tom;
I tried the count routine you suggested (using the AND to make it a byte though), so my code was:
clrf dbcount
test movf dbcount, w ; Get current count
andlw 0x7F ;add 127
movwf TXREG ; Stick it in register to send
btfss PIR1, TXIF ; Has TXREG cleared yet?
goto $-1 ; No, so go back and keep checking
; OK, it's cleared, the byte is sent
incf dbcount, f ; Move to the next value
goto test
I'm not exactly sure what I'm supposed to be seeing, but I've enclosed a screenshot. Is this the expected output?
(https://s14.postimg.org/bexnz8okd/Count_test.jpg) (https://postimg.org/image/bexnz8okd/)
Hi
I don't see anything at first glance....
I would suggest you use a serial TTL to usb converter and monitor the data somehow....
Does your scope have a digial analyser? Maybe you could get the values from there.
Another topic I have come up more than once is... did you connect the MIDI OUT corretly? It is not trivial to know what goest to pin 4 or 5 of the DIN connector... I've failed multiple times, so if it does not work, I always try it reversed.
Thank you so much - I switched my connections on pins 4 and 5 and I've got it running now. I originally got my pinout from this site:
http://www.rossbencina.com/code/midipic where it shows pin 4 being the TX pin and pin 5 being the V+ pin, but checking this site:
https://www.midi.org/specifications/item/midi-din-electrical-specification
shows the opposite. The funny thing is that I tried switching the pins on a whim a week or so ago, with no success. Clearly my code wasn't up to snuff either. Just goes to show that you can't believe everything you read on a website.
Thanks a ton to you as well Tom for persevering and helping me to get this sorted out. You've been a great help.
Excellent! Glad to hear you've got it running! Nice work!
T.
Good to see it works now. As I mentioned,... you are not the first one to fall into the trap.
In my case, I always have trouble figuring out which pin is 4 and 5 in the connector. I never remember if the schematics you find around are looking from behind or from the front of the connector.
I ended up taking pictures of my build and labeling the cables in the picture so that I can check it when needed.