Thursday, January 1, 2015

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...

Monday, November 24, 2014

PIC24FJ64GB004, first successful USB data transfer

I just got my first successful data transfer through to my PIC.The first transfer in a enumeration process is the "get device descriptor" transfer, which consists of three transfers, setup, in and out transfer. Here is the UART output of the transfer:

***************************

Welcome to MIDI2VC+
Version 0.15
Build date Nov 24 2014

***************************
0x01bf> USB device attached.
0x01bf> Waiting for device power stabilization
0x01ca> Device power stabilized
0x01ca> Full speed device found
0x01ca> Device reset initiation
0x01d3> Device reset complete
0x01d3> Setup token sent
0x01d3> Setup token transfer complete
0x01d3> Handshake was: 0002
0x01d3> In token sent
0x01d3> In token transfer complete
0x01d3> Handshake was: 000b

***************************
Selected register dump:
***************************
0x01d3> U1CON   = 0x0089
0x01d3> U1ADDR  = 0x0000
0x01d3> U1TOK   = 0x0090
0x01d3> U1BDTP1 = 0x0012
0x01d3> U1EP0   = 0x000d
0x01d3> U1CNFG1 = 0x0000
0x01d3> BDT[0]  = 0x6c12
0x01d3> BDT[1]  = 0x0800
0x01d3> BDT[2]  = 0x0808
0x01d3> BDT[3]  = 0x0840
0x01d3> &BDT[0] = 0x1200
0x01d3> &ep0TxBuffer[0] = 0x0840
0x01d3> ep0TxBuffer[0] = 0x0680
0x01d3> &ep0RxBuffer[0] = 0x0800
0x01d3> ep0RxBuffer[0] = 0x0112
0x01d3> U1IR = 0x0044
0x01d3> U1STAT = 0x0000
***************************
***************************


***************************
ep0RxBuffer dump:
***************************
0x01d3> ep0RxBuffer = 0x0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 0201 0150 0121 0944 4000 0000 0110 0112 
***************************
***************************

0x01d3> Out token sent
0x01d3> Out token transfer complete
0x01d3> Handshake was: 000e

Now, everything is not great here. The last transfer has an 0xe handshake, which is a stall handshake. This can't be right since I have disabled stalling. The second thing that is fishy is the content of the RxBuffer. I analyzed the USB data transfer of my MicroKey keyboard, by using  Device Monitoring Studio (demo version). Here I can see that the transferred data should actually be:

12 01 10 01 00 00 00 40 44 09 21 01 50 01 01 02 00 01

As you can see, bytes are in the wrong order. I'll look into these issues next...
I'm really happy for this road mark, it means that I get the hardware and it's working! 

Thursday, November 13, 2014

PIC24FJ64GB004, enumeration process

Last post I wrote about the first steps of the enumeration process on the USB host, finding an attached device. The whole process includes quite a number of steps, which are not that easy to wrap your head around.
Firstly, I hadn't configured the clocks properly. The USB host mode is quite picky about the clocks, so getting it right is essential. One problem was that I haven't designed in a crystal as oscillator. The data sheet says the internal RC-clock isn't precise enough. This also means that even if everything actually seems to be working with the RC-clock, the instructions don't address this configuration. The setup is available in the source code.
When I troubleshooted the enumeration process, I made a pseudocode version of it, and I thought it would be suitable to post that here. I'm getting as far as to get a transfer complete-interrupt, which probably is a good step on the way to get through the whole enumeration. So here it is:

 [setup]  
   
 [global variables]  
 static uint16_t __attribute__((aligned(512))) BDT[4];  
 volatile uint16_t ep0RxBuffer[32];  
 volatile uint16_t ep0TxBuffer[32];  
   
 [in main()]  
 //USB1 host mode setup  
 U1CONbits.HOSTEN = 1; //enable host mode  
 U1OTGCONbits.DPPULDWN = 1; //enable pulldowns on lanes  
 U1OTGCONbits.DMPULDWN = 1;  
 U1OTGCONbits.DPPULUP = 0;  
 U1OTGCONbits.DMPULUP = 0;  
   
 U1IRbits.ATTACHIF = 0; //setup and enable attach interrupt  
 U1IEbits.ATTACHIE = 1;  
 IEC5bits.USB1IE = 1;  
   
 U1EP0 = 0x4d; //EP0 Setup for bidirectional control transfers  
 U1CNFG1 = 0x00; // PING-PONG disabled  
   
 U1BDTP1 = (uint16_t) (&BDT) >> 8; //set BDT upper byte  
 BDT[1] = (uint16_t) & ep0RxBuffer; //point first BDT entry, second byte, to RX buffer adress  
 BDT[3] = (uint16_t) & ep0TxBuffer;//point second BDT entry, second byte, to RX buffer adress  
   
   
   
 [enumeration]  
 after attach interrupt is thrown:  
 wait 100ms  
   
 if device is low speed:  
           U1ADDRbits.LSPDEN = 1;//module operates at low speed  
           U1EP0bits.LSPD = 1;  
 else:  
           U1ADDRbits.LSPDEN = 0; //module operates at full speed  
           U1EP0bits.LSPD = 0;  
   
   
 U1CONbits.USBRST = 1; //reset device  
 U1CONbits.SOFEN = 1; //enable start of frame generation  
   
 wait 60ms  
   
 U1CONbits.USBRST = 0;//release reset  
   
 wait 10ms  
   
 //send GET_DEVICE_DESCRIPTOR  
 ep0TxBuffer[0] = 0b0000 0110 1000 0000; //GET_DESCRIPTOR  
 ep0TxBuffer[1] = 0x01; //DEVICE  
 ep0TxBuffer[2] = 0x00; //NO LANGUAGE ID  
 ep0TxBuffer[3] = 0x40; //DESCRIPTOR LENGTH  
 U1ADDR = 0x00; //device adress, 0 until enumeration is finsished  
 BDT[2] = 0x8008; //Byte count=8 and BD1STAT handed over to USB module  
 U1TOK = 0xd0; //send setup token  
   

UART output functions

Also, I have done quite a bit of work in functions to output important data on the UART. This way I can program the PIC with the PICKIT 2 tool and use the same tool for reading the UART output. Even more useful than the debug tool available when using the PICKIT 3. Here are the functions:

 //*********************************************************  
 //*******Function timeStamp**********************  
 //Returns a string that begins with the hex value of the  
 //10ms system tick followed by bracket and space: "0xabcd> "  
   
 char* timeStamp() {  
   extern volatile uint16_t ms10Tick;  
   static char hexChars[9];  
   char* tempChars;  
   uint16_t i;  
   
   hexChars[0] = '0';  
   hexChars[1] = 'x';  
   hexChars[6] = '>';  
   hexChars[7] = 0x20; //space  
   hexChars[8] = 0;  
   
   
   tempChars = uint16_t2String(ms10Tick);  
   for (i = 0; i < 4; i++) {  
     hexChars[i + 2] = tempChars[i];  
   }  
   
   return hexChars;  
 }  
   
   
 //*********************************************************  
 //*******Function string2UARTTXQueue**********************  
 //Takes a c-string and sends it to the UART transmit Queue  
   
 void string2UARTTXQueue(char str[]) {  
   
   uint16_t i = 0;  
   while (str[i] != 0) {  
   
     pushUARTTXQueue(str[i]);  
     i++;  
   }  
   return;  
 }  
   
   
   
 //*********************************************************  
 //*******Function popUARTTXQueue**********************  
 //Returns next value in UART transmit Queue and  
 //sets queue values apropriately  
   
 char popUARTTXQueue() {  
   char UARTVal;  
   extern volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength;  
   extern volatile char UARTTXQueue[UART_TXQueue_SIZE];  
   
   if (UARTTXQueueLength == 0) {  
     return 0;  
   }  
   
   
   UARTTXQueueTail = (UARTTXQueueTail + 1) & (UART_TXQueue_SIZE - 1);  
   UARTVal = UARTTXQueue[UARTTXQueueTail];  
   UARTTXQueueLength--;  
   
   return UARTVal;  
 }  
   
   
 //*********************************************************  
 //*******Function pushUARTTXQueue**********************  
 //Puts a char to the UART transmit Queue and  
 //sets queue values apropriately  
   
 void pushUARTTXQueue(char UARTVal) {  
   extern volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength;  
   extern volatile char UARTTXQueue[UART_TXQueue_SIZE];  
   
   if (UARTTXQueueLength == (UART_TXQueue_SIZE - 1)) {  
     return;  
   }  
   
   UARTTXQueueHead = (UARTTXQueueHead + 1) & (UART_TXQueue_SIZE - 1);  
   UARTTXQueue[UARTTXQueueHead] = UARTVal;  
   UARTTXQueueLength++;  
   
   return;  
 }  
   
 //*********************************************************  
 //*******Function uint16_t2String**********************  
 //converts a uint16_t to a string in hex format without 0x  
   
 char* uint16_t2String(uint16_t buffer) {  
   static char hexChars[5];  
   const uint16_t asciiTable[16] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};  
   uint16_t i;  
   for (i = 4; i > 0; i--) {  
     hexChars[i - 1] = (char) asciiTable[((0xf000 >> 4 * (i - 1)) & buffer) >> 4 * (4 - i)];  
   }  
   hexChars[4] = 0;  
   return hexChars;  
 }  
   
 void dumpRegisters() {  
   string2UARTTXQueue("\r\n***************************\r\n");  
   string2UARTTXQueue("Selected register dump:\r\n");  
   string2UARTTXQueue("***************************\r\n");  
   
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1CON  = 0x");  
   string2UARTTXQueue(uint16_t2String(U1CON));  
   string2UARTTXQueue("\r\n");  
    
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1ADDR = 0x");  
   string2UARTTXQueue(uint16_t2String(U1ADDR));  
   string2UARTTXQueue("\r\n");  
   
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1TOK  = 0x");  
   string2UARTTXQueue(uint16_t2String(U1TOK));  
   string2UARTTXQueue("\r\n");  
   
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1BDTP1 = 0x");  
   string2UARTTXQueue(uint16_t2String(U1BDTP1));  
   string2UARTTXQueue("\r\n");  

   
   string2UARTTXQueue("***************************\r\n");  
   string2UARTTXQueue("***************************\r\n\r\n");  
   return;  
 }  

Tuesday, October 14, 2014

MIDI2VC+, USB device detection

Work on the MIDI2VC+ is slow but steady. The USB protocol is pretty complicated, especially for a host. But I'm taking it one step at a time. The implementation of a UART with functions for outputting strings, makes the whole thing more manageable. I've gotten so far as to identifying an attached device as "Full Speed" or "Low Speed" and managing the detaching. Here's the code for the project so far:

 #include <xc.h>  
 #include <stdint.h>  
 #define STRING_LENGTH 64  
 #define VERSION "0.1"  
 #define POWER_ON_LED PORTCbits.RC3  
 #define LED1 PORTBbits.RB2  
 #define USB_CONNECTED PORTCbits.RC1  
 #define LED4 PORTCbits.RC0  
 #define UART_TXQueue_SIZE 256  
 _CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & FWDTEN_OFF & WINDIS_OFF & ICS_PGx3)  
 _CONFIG2(IESO_OFF & POSCMOD_NONE & OSCIOFNC_ON & FCKSM_CSDCMD & FNOSC_FRCPLL & PLL96MHZ_ON & PLLDIV_DIV2)  
 _CONFIG3(WPFP_WPFP0 & SOSCSEL_IO & WUTSEL_FST & WPDIS_WPDIS & WPCFG_WPCFGDIS & WPEND_WPENDMEM)  
 _CONFIG4(DSWDTPS_DSWDTPS3 & DSWDTOSC_LPRC & RTCOSC_LPRC & DSBOREN_OFF & DSWDTEN_OFF)  
 volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength, USBEnumerationProcess;  
 volatile char UARTTXQueue[UART_TXQueue_SIZE];  
 void string2UARTTXQueue(char str[]);  
 char popUARTTXQueue();  
 void pushUARTTXQueue(char UARTVal);  
 //*** UART1 Receive interrupt function ***  
 //**************************************** k  
 void __attribute__((__interrupt__, __auto_psv__)) _U1RXInterrupt(void) {  
   IFS0bits.U1RXIF = 0; //Reset UART1 Receive interrupt bit  
   U1TXREG = U1RXREG; //Transmit received character  
   return;  
 }  
 //*** USB1 interrupt function ***  
 //****************************************   
 void __attribute__((__interrupt__, __auto_psv__)) _USB1Interrupt(void) {  
   extern volatile uint16_t USBEnumerationProcess;  
   if (U1IRbits.ATTACHIF == 1 && U1IEbits.ATTACHIE == 1) {  
     string2UARTTXQueue("USB device attached.\r\n");  
     USBEnumerationProcess = 1;  
     U1IR = 0x41;  
     U1IE = 0x01;  
     PORTCbits.RC1 = 1; //Light an LED  
   } else if (U1IRbits.DETACHIF == 1 && U1IEbits.DETACHIE == 1) {  
     string2UARTTXQueue("USB device detached.\r\n");  
     USBEnumerationProcess = 0;  
     U1IR = 0x41;  
     U1IE = 0x40;  
     U1ADDRbits.LSPDEN = 0; //The low speed registers need to be reset here  
     U1EP0bits.LSPD = 0; //or the device will be misidentified at the next attachement  
     PORTCbits.RC1 = 0; //Unlight an LED  
   }  
   IFS5bits.USB1IF = 0; //Clear  
   return;  
 }  
 int16_t main() {  
   extern volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength, USBEnumerationProcess;  
   UARTTXQueueTail = 0;  
   UARTTXQueueHead = 0;  
   UARTTXQueueLength = 0;  
   USBEnumerationProcess = 0;  
   TRISB = 0x00;  
   TRISC = 0x00;  
   PORTC = 0x08;  
   uint16_t pll_startup_counter = 600;  
   CLKDIVbits.PLLEN = 1;  
   while (pll_startup_counter--);  
   PORTC = 0x09; //Turn on LED when PLL is locked  
   U1PWRCbits.USBPWR = 1;  
   CLKDIVbits.CPDIV = 0b10;  
   TRISA = 0x02; //RA1 as input  
   AD1PCFG = 0xffff; //No analogue inputs  
   IFS0 = 0x0000; //Interrupts reset  
   IEC0 = 0x0800; //Uart recieve interrupt enabled  
   POWER_ON_LED = 1;  
   //Reconfigurable pin setup  
   __builtin_write_OSCCONL(OSCCON & 0xBF);  
   RPOR2bits.RP5R = 3; //RP5 as UART1 TX  
   RPINR18bits.U1RXR = 0x06; //RP6 as UART1 RX  
   __builtin_write_OSCCONL(OSCCON | 0x40);  
   U1BRG = 8; //Baud rate set to 56k, U1BRG = (8MHz/2)/(4*56000) - 1  
   U1MODE = 0x8808; //UART1 enabled in simplex mode  
   U1STA = 0x8400; //Interrupt when char is transferred to TSR and Transmit enabled  
   U1MODEbits.BRGH = 1;  
   //USB1 host mode setup  
   U1CONbits.HOSTEN = 1;  
   U1OTGCONbits.DPPULDWN = 1;  
   U1OTGCONbits.DMPULDWN = 1;  
   U1OTGCONbits.DPPULUP = 0;  
   U1OTGCONbits.DMPULUP = 0;  
   U1IRbits.ATTACHIF = 0;  
   U1IEbits.ATTACHIE = 1;  
   IEC5bits.USB1IE = 1;  
   char build_date[] = __DATE__;  
   string2UARTTXQueue("***************************");  
   string2UARTTXQueue("\r\n\r\n");  
   string2UARTTXQueue("Welcome to MIDI2VC+\r\nVersion ");  
   string2UARTTXQueue(VERSION);  
   string2UARTTXQueue("\r\n");  
   string2UARTTXQueue("Build date ");  
   string2UARTTXQueue(build_date);  
   string2UARTTXQueue("\r\n\r\n");  
   string2UARTTXQueue("***************************\r\n");  
   while (1) {  
     switch (USBEnumerationProcess) {  
       case 0:  
         break;  
       case 1:  
         if (U1CONbits.JSTATE == 0) {  
           string2UARTTXQueue("Low speed device found\r\n");  
           U1ADDRbits.LSPDEN = 1;  
           U1EP0bits.LSPD = 1;  
           USBEnumerationProcess = 2;  
         } else {  
           string2UARTTXQueue("Full speed device found\r\n");  
           U1ADDRbits.LSPDEN = 0;  
           U1EP0bits.LSPD = 0;  
           USBEnumerationProcess = 2;  
         }  
         break;  
       default:  
         break;  
     }  
     if (UARTTXQueueLength > 0 && U1STAbits.UTXBF == 0) {  
       U1TXREG = popUARTTXQueue();  
     }  
   }  
   return 0;  
 }  
 //*********************************************************  
 //*******Function string2UARTTXQueue**********************  
 //Takes a c-string and sends it to the UART transmit Queue  
 void string2UARTTXQueue(char str[]) {  
   uint16_t i = 0;  
   while (str[i] != 0) {  
     pushUARTTXQueue(str[i]);  
     i++;  
   }  
   return;  
 }  
 //*********************************************************  
 //*******Function popUARTTXQueue**********************  
 //Returns next value in UART transmit Queue and  
 //sets queue values apropriately  
 char popUARTTXQueue() {  
   char UARTVal;  
   extern volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength;  
   extern volatile char UARTTXQueue[UART_TXQueue_SIZE];  
   if (UARTTXQueueLength == 0) {  
     return 0;  
   }  
   UARTTXQueueTail = (UARTTXQueueTail + 1) & (UART_TXQueue_SIZE - 1);  
   UARTVal = UARTTXQueue[UARTTXQueueTail];  
   UARTTXQueueLength--;  
   return UARTVal;  
 }  
 //*********************************************************  
 //*******Function popUARTTXQueue**********************  
 //Puts a char to the UART transmit Queue and  
 //sets queue values apropriately  
 void pushUARTTXQueue(char UARTVal) {  
   extern volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength;  
   extern volatile char UARTTXQueue[UART_TXQueue_SIZE];  
   if (UARTTXQueueLength == (UART_TXQueue_SIZE - 1)) {  
     return;  
   }  
   UARTTXQueueHead = (UARTTXQueueHead + 1) & (UART_TXQueue_SIZE - 1);  
   UARTTXQueue[UARTTXQueueHead] = UARTVal;  
   UARTTXQueueLength++;  
   return;  
 }  

Wednesday, October 8, 2014

SA0***

I finally got my amateur radio license, which feels nice. The test was quite hard. Today I got a call from the license issuer, who asked what I call sign I wanted. Apparently I'm the first in the region who got to choose my own call sign, as long as it wasn't taken previously.

Sunday, September 21, 2014

MIDI2VC+ UART loop test and analogue synth prototype demo

MIDI2VC+ UART loop test

Work on the MIDI2VC+ has been a bit down lately, since I'm studying for my ham radio license! It's seems like a fun hobby, so maybe I'll be building RF-stuff soon...
Anyway, my MIDI2VC submission to the hackaday prize got med a T-shirt, which I think is nice. I'm happy to get anything. I mean, the contest was about electronics connected to the Internet, which my project very much wasn't!
 In the MIDI2VC project I've finally gotten the mandatory UART loop test working. This is an important step when working with a new MCU, since a successful test means that the following is working:
  1. Oscillators are running
  2. Correct bad rate calculated means that oscillators are running at the frequency we expect them to be running at
  3. Interrupts are working
  4. IO's are working
Also, we now have a great output for debug info.
Here's the code:

 //PIC24FJ64GB004 UART loop test using interrupts
 //http://electev.blogspot.com  
 /*@ignore@*/  
 #include <xc.h>  
 /*@end@*/  
 _CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & FWDTEN_OFF & WINDIS_OFF & ICS_PGx3)  
 _CONFIG2(IESO_OFF & POSCMOD_NONE & OSCIOFNC_ON & FCKSM_CSDCMD & FNOSC_FRC & PLL96MHZ_OFF)  
 _CONFIG3(WPFP_WPFP0 & SOSCSEL_IO & WUTSEL_FST & WPDIS_WPDIS & WPCFG_WPCFGDIS & WPEND_WPENDMEM)  
 _CONFIG4(DSWDTPS_DSWDTPS3 & DSWDTOSC_LPRC & RTCOSC_LPRC & DSBOREN_OFF & DSWDTEN_OFF)  
 //*** UART1 Receive interrupt function ***  
 //**************************************** k  
 void __attribute__((__interrupt__, __auto_psv__)) _U1RXInterrupt(void) {  
   IFS0bits.U1RXIF = 0; //Reset UART1 Receive interrupt bit  
   U1TXREG = U1RXREG; //Transmit received character  
   return;  
 }  
 int main() {  
   TRISA = 0x02; //RA1 as input  
   AD1PCFG = 0xffff; //No analogue inputs  
   IFS0 = 0x0000; //Interrupts reset  
   IEC0 = 0x0800; //Uart recieve interrupt enabled  
   //Reconfigurable pin setup  
   __builtin_write_OSCCONL(OSCCON & 0xBF);  
   RPOR2bits.RP5R = 3; //RP5 as UART1 TX  
   RPINR18bits.U1RXR = 0x06; //RP6 as UART1 RX  
   __builtin_write_OSCCONL(OSCCON | 0x40);  
   U1BRG = 25;//Baud rate set to 9600, U1BRG = (8MHz/2)/(16*9600) - 1  
   U1MODE = 0x8800; //UART1 enabled in simplex mode  
   U1STA = 0x8400; //Interrupt when char is transferred to TSR and Transmit enabled  
   while (1) {  
   }  
   return 0;  
 }  

Analogue synth prototype demo

I realized that I don't have a proper demo video of the analogue synth prototype I built, so I made one and here it is: