mubase

MIDI to CV converter using Papilio..

15 posts in this topic

Hi all. Its a long time since I used my papilio board but I have cme up with an idea and was hoping someone might be able to tell me if it ossible using a Papilio one 500K and thwe schematic editor.

The idea is to build a MIDI to CV controller for use with modular synthesizers.

 

I understand that the schematic editor can enable the user to add as many peripherals as they wish to their design.

So would it be possible for me to add say 12 or more DACs to my design for use as outputs having had data converted from a serial port running at the MIDI baud rate (31250 baud) as MIDI messages (note on/off, controller messages etc)  to be converted to analog voltages to be sent out of the DACs implemented on the design.

The reason for this is that although I have a KEnton MIDI to CV converter it only has 2 channels. I would like loads more channels so I could control my modular through any MIDI platform.

Does anyone understand what I am saying and would it be pssible?

 

If someone could help I would be very appreciative...

 

Thanks.

Steve.

 

Share this post


Link to post
Share on other sites

Hello Steve,

 

Absolutely, I think this should be very doable. If you download the current version of the ZAP IDE and take a look at the Audio_ModFile_simple example that should do what you want for a single channel. It will need to be modified but should have most of what you need for the schematic already in place.

 

There is an Audio Passthrough core which is connected to Wishbone slot 5 for the input and a DeltaSigma DAC for the output. Whenever you write a value to the Audio Passthrough core from a sketch that value will be converted to a voltage on the output off the DetlaSigma DAC. 

 

In this example the sketch is much more complicated then you need, it is decoding Amiga Mod files and putting the audio values on the Audio Passthrough device fast enough to create audio. But you should be able to look at the Wishbone_Template example for a simplified method of writing your own value to the Audio Passthrough core.

 

If you want, see how far you can get and we will be here to help out and answer any questions. Or if needed, I can put together a better example project for you when I get some free time.

 

Thanks,

Jack.

Share this post


Link to post
Share on other sites

Will the DeltaSigma DAC have enough speed/resolution?

 

To get the resolution it will need a pretty good low pass filter (100Hz), and I guess that gives about 16 bits of resolution 

 

It might need to be slightly enhanced, with a second FPGA pin that can be used to discharge the filter

Share this post


Link to post
Share on other sites

Hi guys and thanks for the advice so far. Its nice to be getting back into the Papilio board again. :) (500K- i have a Logicstart board too.. :) )

 

So am I to understand that the audio passthrough can be called simply by defining it like in the wishbone example? >>

#define MYBASE IO_SLOT(5)#define MYREG(x) REGISTER(MYBASE,x) 

and then just writing a value to the MYREG define?

 

What about the DAC's registers?

 

SIGMADELTACTL and SIGMADELTADATA

 

if there is more than one DAC?

 

how would I set up and use them??

 

I found a good example of using an interrupt to send a low and then a high value through the DAC to produce a square wave:

int level = 0; void _zpu_interrupt (){  if ( TMR0CTL & (1 << TCTLIF))  {    /* Interrupt comes from timer 0. */    /* Change level from low to hi and back again. */    if (level == 0)      level = 0xFFFFFFFF;    else      level = 0;    SIGMADELTADATA = level;     /* Clear the interrupt flag on timer register */    TMR0CTL &= ~ (1 << TCTLIF );  }} void setup (){  // Configure sigma-delta output pin.  // WINGA PIN0 and PIN1 are the outputs.  pinMode(WING_A_0, OUTPUT);  pinMode(WING_A_1, OUTPUT);  pinModePPS(WING_A_0, HIGH);  pinModePPS(WING_A_1, HIGH);  outputPinForFunction(WING_A_0, 0);  outputPinForFunction(WING_A_1, 0);   // Enable channel 0 and 1.  SIGMADELTACTL = 0x03;   unsigned frequency = 1200;   // Clear timer counter.  TMR0CNT = 0;   // Set up timer , no prescaler.  TMR0CMP = ( CLK_FREQ / frequency ) - 1;  TMR0CTL = (1 << TCTLENA)| (1 << TCTLCCM)| (1 << TCTLDIR)| (1 << TCTLIEN);   // Enable timer 0 interrupt on mask.  INTRMASK = (1 << INTRLINE_TIMER0);   // Globally enable interrupts.  INTRCTL = (1 << 0);} void loop(){}

How would I go about sending more than one signal out of the audio Passthroughs/DACs I have set up on the schematic in the sketch?? 

Share this post


Link to post
Share on other sites

Ok, if I'm not mistaken, those DAC registers are for an older VHDL only project that uses the following wishbone implementation its more complicated, having more channels and needs to be setup by using the control register:

https://github.com/GadgetFactory/ZPUino-HDL/blob/master/zpu/hdl/zpuino/zpuino_sigmadelta.vhd

 

What I've done in the schematic library is a little bit different, it's simplified and only has one channel and no control register, the VHDL can be found here:

https://github.com/GadgetFactory/Papilio-Schematic-Library/blob/master/Libraries/Wishbone_Peripherals/AUDIO_zpuino_sa_sigmadeltaDAC.vhd

https://github.com/GadgetFactory/Papilio-Schematic-Library/blob/master/Libraries/Wishbone_Peripherals/AUDIO_zpuino_wb_passthrough.vhd

 

If I remember correctly you just need to write the data you want to go to the DAC to MYREG. Something like:

MYREG = 0xFFFF;

If you want more then one channel then you would attach multiple single channel DACs to the available Wishbone slots and setup registers like so:

#define MYBASE1 IO_SLOT(5)#define MYREG1(x) REGISTER(MYBASE1,x) #define MYBASE2 IO_SLOT(6)#define MYREG2(x) REGISTER(MYBASE2,x) #define MYBASE3 IO_SLOT(8)#define MYREG3(x) REGISTER(MYBASE3,x) 

I would start out that way, then we can get fancy later and make a single wishbone core that implements multiple DACs and has a control reg to set which DACs are active.

 

I'm working on a way to more easily create custom wishbone cores and it will be better to wait until I have that ready to go for that task. 

 

Jack.

Share this post


Link to post
Share on other sites

Hi Jack. :) OK I think I understand and have been reading the ZPUINO user manual however, I am unsure as to whether I am getting the schematic right. I am using the PSL example in the examples folder as a template and have connected an Audio passthrough to slot 5 and a DAC to the audio passthrough with a connection to the 96MHz clock. Now, should the output of the DAC have an IO tag assigned to it ?

rI tried this and attempted using a name tag of WING_CH0 and deleting the WING_CH0 tag from the papilio default pinout bus.. is this correct or am I totally wrong?

Or do I need to use the audio wing to connect the DAC to??

I apologize for my slowness but this is very new to me.

 

Thanks,

Steve.

Share this post


Link to post
Share on other sites

Steve, 

 

That should be correct, you would delete one of the I/O markers and connect it to the output of the DAC instead. One thing though, when you delete the I/O marker make sure you delete the little bit of wire that gets left behind. If you do that does it synthesize? If not, what are the error messages?

 

Jack

Share this post


Link to post
Share on other sites

Ok Sack that last message. I think Ive got it.  :D

Your tutorial on adding 10 UARTs helped a lot!

So I have now got a schematic consisting of 4 audio passthrough units connected to 4 sigmadelta DACs that  have I/Os going out to pins CL0-CL3.

I've tested them all using a simple program that outputs varying values from 0x0 to 0xffff and they measure up on my oscilloscope.

Adding a basic RC filter (470 ohms / 100nF ) I also wrote a small program in ZAP to output a sawtooth wave (count up to 0xffff and back to 0.)

I took a picture of the resulting wave but I dont know how to post it to this page !  :wacko:

So its a start. I am in absolute awe at what a brilliant thing the schematic editor is!

Share this post


Link to post
Share on other sites

Sweet! I'm glad you got it all working. :)

 

I'm super happy to hear that the Schematic Editor work is paying off and someone was able to do something cool with it. :)

 

To attach a picture just choose the "More Reply Options" button and then Attach your image using the Attach Files option. It will then let you post the image into your message.

 

Man, I'm glad to get such great feedback, thanks.

 

Jack.

Share this post


Link to post
Share on other sites

It looks good, and it makes me realize... You might be interested in the waveform generator symbol! It creates those waveforms in hardware instead of C code, but it is not as flexible as what you are doing. But worth taking a look, check out the Benchy_Waveform_Generator example.

 

Jack.

Share this post


Link to post
Share on other sites

Hi Jack. :) I'm making progress with the MIDI - CV and have a UART installed on WB 10 with pins on BH1 and 2. I've built a small MIDI in circuit using a 6n138 opto and have its output connected to the UART RX. My program takes MIDI note on messages and CC messages and converts either the note value or the CC value into a voltage which then lights and LED through the DAC outputs.

There is a problem with speed. The CC values update alright but only if I turn the knob really slowly. If I turn it fast, the data recieved on the papilio isn't continuous...? 

I wrote a simpler program just t print what comes out f the MIDI serial port and it was fine but my LED program is slow.. I have pasted it below. Can you see anything in my code that would slow the serial RX down?

 

Also, the map function doesn't work (hence the equation to turn MIDI values to DAC voltages...

 

Any help appreciated.

Cheers,

Steve.

//MIDI to CV on Papilio one 500K running ZAP 2.3.0 using Papilio Schematic Library 1.6 (GadgetFactory-Jack Gassett.) 2014.byte commandByte;byte noteByte;byte velocityByte;byte CCNum;byte CCVal;byte oldCC;#define MYBASE IO_SLOT(5)    // DAC 1#define MYREG(x) REGISTER(MYBASE,x) // DAC 1 register#define MYBASE2 IO_SLOT(6)    // DAC 2#define MYREG2(x) REGISTER(MYBASE2,x) // DAC 2 registerbyte noteOn = 144;byte CCon=0xb0;int CVTest;int noteTest;HardwareSerial midi(10);//light up led at pin 13 when receiving noteON message with note = 60void setup(){   midi.begin(31250);  Serial.begin(115200); // midi.flush();}    void loop(){      while (midi.available()>0){           commandByte = midi.read();//read first byte               switch (commandByte) {        case 0x90 ://if note on message        //check if note == 60 and velocity > 0         Serial.print("CMD: ");      Serial.println(commandByte);      noteByte = midi.read();//read next byte      Serial.print("VAL: ");      Serial.println(noteByte);      noteTest=(0+((65535-0)/(127-0))*noteByte-0);      MYREG2(1)=noteTest;      velocityByte = midi.read();//read final byte      Serial.print("VEL/VAL: ");      Serial.println(velocityByte);      delay(1);        break;             case 0xb0:                  Serial.print("CMD: ");         Serial.println(commandByte);        CCNum=midi.read();      Serial.print("CCNum: ");            //float V=3.3/CVTest;      Serial.println(CCNum);      CCVal=midi.read();      oldCC=CCVal;      CVTest=(0+((65535-0)/(127-0))*CCVal-0);      Serial.print("CV: ");      Serial.println(CVTest);      MYREG(1)=CVTest;      delay(1);      break;       default:       break;             }             }}

Share this post


Link to post
Share on other sites

This is awesome! I just ordered a Papilio Duo for a similar purpose ( I wont have it for another 20-30 days! I want it now!! :) 

 

Anywho, am I correct if I say you created a DAC that has high enough resolution directly from the FPGA chip, without any additional DAC chips added? 

What is the max voltage these guys can output? Are you able to get a full 5 octaves out of this converter? 

Share this post


Link to post
Share on other sites

 

Hi Jack. :) I'm making progress with the MIDI - CV and have a UART installed on WB 10 with pins on BH1 and 2. I've built a small MIDI in circuit using a 6n138 opto and have its output connected to the UART RX. My program takes MIDI note on messages and CC messages and converts either the note value or the CC value into a voltage which then lights and LED through the DAC outputs.

There is a problem with speed. The CC values update alright but only if I turn the knob really slowly. If I turn it fast, the data recieved on the papilio isn't continuous...? 

I wrote a simpler program just t print what comes out f the MIDI serial port and it was fine but my LED program is slow.. I have pasted it below. Can you see anything in my code that would slow the serial RX down?

 

Also, the map function doesn't work (hence the equation to turn MIDI values to DAC voltages...

 

Any help appreciated.

Cheers,

Steve.

//MIDI to CV on Papilio one 500K running ZAP 2.3.0 using Papilio Schematic Library 1.6 (GadgetFactory-Jack Gassett.) 2014.byte commandByte;byte noteByte;byte velocityByte;byte CCNum;byte CCVal;byte oldCC;#define MYBASE IO_SLOT(5)    // DAC 1#define MYREG(x) REGISTER(MYBASE,x) // DAC 1 register#define MYBASE2 IO_SLOT(6)    // DAC 2#define MYREG2(x) REGISTER(MYBASE2,x) // DAC 2 registerbyte noteOn = 144;byte CCon=0xb0;int CVTest;int noteTest;HardwareSerial midi(10);//light up led at pin 13 when receiving noteON message with note = 60void setup(){   midi.begin(31250);  Serial.begin(115200); // midi.flush();}    void loop(){      while (midi.available()>0){           commandByte = midi.read();//read first byte               switch (commandByte) {        case 0x90 ://if note on message        //check if note == 60 and velocity > 0         Serial.print("CMD: ");      Serial.println(commandByte);      noteByte = midi.read();//read next byte      Serial.print("VAL: ");      Serial.println(noteByte);      noteTest=(0+((65535-0)/(127-0))*noteByte-0);      MYREG2(1)=noteTest;      velocityByte = midi.read();//read final byte      Serial.print("VEL/VAL: ");      Serial.println(velocityByte);      delay(1);        break;             case 0xb0:                  Serial.print("CMD: ");         Serial.println(commandByte);        CCNum=midi.read();      Serial.print("CCNum: ");            //float V=3.3/CVTest;      Serial.println(CCNum);      CCVal=midi.read();      oldCC=CCVal;      CVTest=(0+((65535-0)/(127-0))*CCVal-0);      Serial.print("CV: ");      Serial.println(CVTest);      MYREG(1)=CVTest;      delay(1);      break;       default:       break;             }             }}

 

Hello mubase, 

 

Sorry for the slow reply, been working like crazy to get a stable DesignLab release done. I finally finished a .20 release and just uploaded it here. You might want to try your project in the new DesignLab release because it will be easier to share...

 

For the Retrocade Synth there was no problem with capturing CC data full speed, I was able to twist a knob and have it all captured without a hitch. 

 

Looking at your code there is one thing I can recommend, I saw some strange behavior and unexpected results with switch statements, especially when they got bigger. I don't know if that is what you are running into but I would try to move everything from inside the case sections into its own function and then just call the function from that case statement. That way the switch structure is simple, see if that makes any difference.

 

Jack.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now