Saturday, January 31, 2015

MIDI to VC conversion is working

I am now actually having a working path from USB to control voltage. I tried hooking it to my messy synthesizer,which doesn't seem to work. I then discovered that the converter only works if PicKit2 or the I2C analyzer is connected to it. My thought is that there is some issue with grounding in the board. I'll look into this. The actual working code and HEX is here. It still contains a lot of debug code though, and it doesn't sport all features yet...

Tuesday, January 20, 2015

I2C, UART and USB

I finally got all three interfaces working at the same time. While the PIC24FJ64GB004 hammers out I2C data, it configures and reports about an USB device! This is so cool, I took a not so cool screenshot:
All three interfaces used at the same time!
All that is left now is to write the algorithms for how to handle received data.

Monday, January 19, 2015

PIC24FJ64GB004 I2C setup

I've been working on the I2C-setup on the MIDI2VC+ for a couple of days. First off I soldered the DAC and accompanying components to the board. I have been a bit worried about the I2C bus since it is connected to both 5V and 3V3 interfaces. The PIC has 3.3V logic and the DAC 5V. The I2C bus runs on 5V and the PIC I2C pins can tolerate 5.5V, so I thought that it might work since both devices compatible logic levels.
Running an I2C-bus this way is out of spec and may mean that you cannot run it full speed. Some testing proved that it was possible to run the bus at 360MHz without errors. I used the Beagle I2C analyzer from TotalPhase, and it was really good for analyzing lots of I2C data to see how much errors there were in the transfers. I ended up running the bus at 100kHz, to save power and add robustness.
Since I didn't have a digital oscilloscope previously, I adopted an old Tektronix 4030 that was being scrapped at work. This must be one of the earliest generations of DSO's, the scope looks very similar to my Tek 4065. I did get it running the way I wanted it to, but it wasn't that user friendly. You had to go into a menu blip yourself to the "Normal" trigger mode, a function that usually have it's own front panel button on modern scopes...
Tektronix 4030 capture of I2C transmission. Data on top trace, clock on bottom trace.


//I2C setup:
    I2C1CON = 0x8000; //I2C module enabled
    I2C1BRG = 0x13c; //= (32000000/100000 - 32000000/10000000) - 1, 100kHz SCL 

//Function i2cTransmission
//Polls flag to see if there is I2C data to be sent and
//walks through the transmission process
uint16_t i2cTransmission(uint16_t i2cTransmitCycle, uint16_t *i2cstatus, uint16_t i2cData[]) {  
   //Polls transmit cycle flag, I2C interrupt flag and ACK-status  
   switch (i2cTransmitCycle) {  
     case 0:  
       if (*i2cstatus == 1) {  
         i2cTransmitCycle = 1;  
         I2C1CONbits.SEN = 1; //start condition  
       }  
       break;  
     case 1:  
       if (I2C1CONbits.SEN == 0) { //when start condition is complete, load slave address  
         IFS1bits.MI2C1IF = 0;  
         I2C1TRN = (0x60 << 1);//load DAC address  
         i2cTransmitCycle++;  
       }  
       break;  
     case 2:  
       //when address is ACKed:  
       if (IFS1bits.MI2C1IF == 1 && I2C1STATbits.ACKSTAT == 0) {  
         IFS1bits.MI2C1IF = 0;  
         I2C1TRN = i2cData[0]; //load first data byte  
         i2cTransmitCycle++;  
       }  
       break;  
     case 3:  
       if (IFS1bits.MI2C1IF == 1 && I2C1STATbits.ACKSTAT == 0) {  
         IFS1bits.MI2C1IF = 0;  
         I2C1TRN = i2cData[1]; //load second data byte  
         i2cTransmitCycle++;  
       }  
       break;  
     case 4:  
       if (IFS1bits.MI2C1IF == 1 && I2C1STATbits.ACKSTAT == 0) {  
         I2C1CONbits.PEN = 1; //stop condition  
         i2cTransmitCycle++;  
       }  
       break;  
     case 5:  
       if (I2C1CONbits.PEN == 0) { //when stop condition is finished, transfer is complete  
         i2cTransmitCycle = 0;  
         *i2cstatus = 0;  
       }  
       break;  
     default:  
       break;  
   }  
   return i2cTransmitCycle;  
 }  

Sunday, January 4, 2015

Receiveing MIDI data!

Finally! I am receiving MIDI data from the keyboard! The data is piped to the UART after zero padding has been removed. This means that I can start reusing the functionality of the old MIDI2VC, to start converting received data to I2C (received by DAC).
Received MIDI data

Thursday, January 1, 2015

USB reached configured state!

OK, I kind of gave up on extracting and reading all configuration data from the Microkey-25, so I cheated a bit and read the USB packets from Free Device Monitoring Studio. Anyway, I sent the SetConfiguration command to the Device, and lo and behold, the red LED  on the keyboard lit up. This must mean that the device is configured! Now all I need to do is start extracting data from the keyboard...

USB getConfigurationDescription

Let's continue by receiving the descriptor for configuration 1 (the only configuration supplied by the device)
4002 0105 0900 2002 0224 0604 0120 0130 0103 2409 0001 1001 4002 0324 0903 1001 0224 0600 2501 0001 2407 0000 0301 0200 0004 0932 8000 0101 0053 0209 
Byte Field Value Meaning
0 bLength 0x09 The Configuration description consists of 9 bytes
1 bDescriptorType 0x02 Descriptor type CONFIGURATION
2-3 wTotalLength 0x0053 Total length of configuration, including descriptors for configuration, interfaces and endpoints
4 bNumInterfaces 0x01 Configuration has 1 interface
5 bConfigurationValue 0x01 The value of this configuration is 1
6 iConfiguration 0x00 String index 0 describes this configuration
7 bmAttributes 0x80 Device powered by bus and has no remote wake up feature
8 bMaxPower 0x32 Max power consumption is 50*2mA = 100mA

The rest of the supplied data should be descriptors for interfaces and endpoints:

Interface descriptor

This descriptor starts with byte 9 in the data above. The first value is the length of the descriptor, which we can see is 0x09. That means that the data to interpret is:
0000 0301 0200 0004 09

Byte Field Value Meaning
0 bLength 0x09 The Interface description consists of 9 bytes
1 bDescriptorType 0x04 Descriptor type INTERFACE
2 bInterfaceNumber 0x00 Interface is number 0
3 bAlternateSetting 0x00 Value to select this alternate setting
4 bNumEndpoints 0x02 This interface uses two endpoints
5 bInterfaceClass 0x01 Interface class is 1
6 bInterfaceSubClass 0x03 Interface sub class is 3
7 bInterfaceProtocol 0x00 Device does not use class-specific protocol
8 iInterface 0x00 String index for this descriptor is 0

Now, the next entry puzzles me, descriptor is of type 0x24. I found this post, that explaines that this is a class specific interface descriptor. I'll have to look into this some more.



USB getDeviceDescriptor

In my last post I talked about making a successful USB transfer. It was a getDeviceDescriptor transfer, which transfers info about the device to the host. The transferred data was:0100 0201 0150 0121 0944 4000 0000 0110 0112
The data was received from the same Korg Microkey-25 as previously used.
Byte Field Value Meaning
0 bLength 0x12 The Device description consists of 18 bytes
1 bDescriptorType 0x01 Descriptor type DEVICE
2-3 BCD 0x0110 USB Spec Release Number 1.10
4 Class 0x00, No class per interface defined
5 SubClass 0x00 Must be 0, since Class code is 0
6 bDeviceProtocol 0x00 No class specific protocol used
7 bMaxPacketSize0 0x40 Max packet size 64 for endpoint 0
8-9 idVendor 0x0944 Korg
10-11 idProduct 0x0121
12-13 bcdDevice 0x0150 Device release number 1.50
14 iManufacturer 0x01 Index of string descriptor describing manufacturer
15 iProduct 0x02 Index of string descriptor describing product
16 iSerialNumber 0x00 Index of string descriptor describing serial number
17 bNumConfigurations 0x01 Device has only one configuration

So all of this looks fine to me. Apart from this, I have also addressed the device, which should mean that I only need to set the device configuration to get the Microkey up and going. I do need to spend some time on the code though, I can't keep hard coding all this stuff, I will need some level of abstraction, even though I'd prefer to keep things as simple as possible...