Friday, December 13, 2013

Converting UART data to I2C data

I've managed to get the PIC to receive data over UART and then transmitting the data over I2C, to the DAC.
 Since my aim is to convert key pressings on a MIDI keyboard to voltage control levels for a synthesizer, I only process the "Note on", 0x9X, and "Note off", 0x8X (symbols edited, I had them mixed up) symbols from the UART stream. Right now I'm only reformatting the data so it can be sent over I2C, but this will later be done using a function or a look up table of some sort, so that UART data will translate to the correct control voltage. "Note on" also turns on an LED, and "Note off" turns it off. This will later be used as a gate signal for the synthesizer.
 I used the pickit 2 for sending UART symbols. It's user interface lets you write in HEX, which PUTTY doesn't. The ASCII symbols for 0x8X and 0x9X where pretty strange, so it was a lot easier using the pickit tool. In the other end i used a borrowed Aardwark I2C protocol analyzer in monitoring mode.
 The source code is pretty well commented, so anyone interested in the details can read the comments. The code is starting to become a bit big for posting in the blog, but I don't expect to grow very much more. I intend to add some more features at a later time. These will be MIDI channel settings and handling of several keys being pressed simultaneously.

Successful conversion from UART data to I2C data

1:  //UART to I2C test using PIC12F1822  
2:  #include <xc.h>  
3:    
4:  //Configuration bits  
5:  #pragma config WDTE=OFF, PWRTE = OFF, MCLRE=OFF, BOREN=OFF, FCMEN=OFF, CLKOUTEN = OFF, IESO=OFF, FOSC=INTOSC, CPD=OFF, LVP = ON, BORV = 0  
6:    
7:  //I2C Global variables  
8:  volatile int i2cTransmitCycle; //Counter for I2C transmission cycle steps, which are carried out in the interrupt service routine  
9:  volatile int i2cTransmitInProgress;  
10:  volatile int i2c7bitAddress;  
11:  volatile int i2cData[2];   
12:  volatile int i2cDataCounter; //For indication of which byte is to be sent  
13:  volatile int i2cDataNoBytes; //Number of bytes to be sent  
14:  volatile int i2cDataToBeSent; //For polling in main()  
15:    
16:  //UART Global variables  
17:  volatile int UARTBufferCounter;  
18:  volatile int UARTBuffer[2];  
19:    
20:  //Function prototypes  
21:  int i2cTransmit(int, int [], int);  
22:  void i2cTransferDataArray(int [], int);  
23:  void processUARTSymbol(int);  
24:  void processUARTBuffer(int []);  
25:  void lightLED();  
26:  void toggleLED();  
27:  void unlightLED();  
28:  void init();  
29:  void i2cTransmitProcess();  
30:    
31:  void main() {  
32:    init();  
33:    
34:    extern int i2cTransmitInProgress;  
35:    extern int i2cData[2];  
36:    extern int UARTBufferCounter;  
37:    extern int UARTBuffer[2];  
38:    extern int i2cDataToBeSent;  
39:    
40:    i2cDataToBeSent = 0;  
41:    UARTBufferCounter = 0;  
42:    i2cTransmitInProgress = 0;  
43:    
44:    //Loop forever  
45:    while (1) {  
46:    
47:      //Polling to see if there is any I2C data to be sent  
48:      //I2C data cannot be sent from within the interrupt service routine  
49:      //since the sending I2C data uses several interrupts  
50:      if (i2cDataToBeSent) {  
51:        while (i2cTransmit(0x60, i2cData, 2)) {  
52:        }  
53:        i2cDataToBeSent = 0;  
54:      }  
55:    
56:    }  
57:    
58:  }  
59:    
60:    
61:  //***********************************************************  
62:  //**************Function isr****************  
63:  //Interrupt service routine  
64:  //***********************************************************  
65:  interrupt void isr(void) {  
66:    
67:    
68:    //I2C interrupt  
69:    if (SSP1IF) {  
70:      //Reset SSP interrupt flag  
71:      SSP1IF = 0;  
72:    
73:      i2cTransmitProcess();  
74:    }  
75:    
76:    //USART receive interrupt  
77:    if (RCIF) {  
78:      processUARTSymbol(RCREG);  
79:    }  
80:    
81:    return;  
82:  }  
83:    
84:    
85:  //**************************************************  
86:  //**************Function i2cTransmit****************  
87:  //Takes data array and address of I2C slave*********  
88:  //Sets up appropiate global variables***************  
89:  //Handles transmit in progress flags****************  
90:  //Initializes I2C start condition*******************  
91:  //**************************************************  
92:    
93:  int i2cTransmit(int i2cAddressF, int i2cDataF[], int i2cDataNoBytesF) {  
94:    extern int i2cTransmitInProgress;  
95:    extern int i2c7bitAddress;  
96:    extern int i2cData[];  
97:    extern int i2cDataCounter;  
98:    extern int i2cDataNoBytes;  
99:    
100:    //Just return 1 if I2C transmission is already in progress  
101:    if (i2cTransmitInProgress) {  
102:      return 1;  
103:    }  
104:    
105:    
106:    i2cTransmitInProgress = 1;  
107:    i2cTransmitCycle = 1;  
108:    
109:    i2cTransferDataArray(i2cDataF, i2cDataNoBytesF);  
110:    i2cDataCounter = 0;  
111:    i2cDataNoBytes = i2cDataNoBytesF;  
112:    i2c7bitAddress = i2cAddressF;  
113:    SEN = 1; //Initialize I2C start condition  
114:    
115:    //Don't return function until data is sent and stop condition is initialized  
116:    while (i2cTransmitInProgress) {  
117:    }  
118:    
119:    return 0;  
120:  }  
121:    
122:    
123:  //***********************************************************  
124:  //**************Function i2cTransferDataArray****************  
125:  //Copies data array to global data array for I2C transmission  
126:  //***********************************************************  
127:    
128:  void i2cTransferDataArray(int i2cDataF[], int i2cDataNoBytesF) {  
129:    int i = 0;  
130:    for (i; i < i2cDataNoBytesF; i++) {  
131:      i2cData[i] = i2cDataF[i];  
132:    }  
133:    return;  
134:  }  
135:    
136:    
137:  //***********************************************************  
138:  //**************Function processUARTSymbol*******************  
139:  //Checks to see if received UART symbol is 0x8X or 0x9X, or  
140:  //a symbol that follows one of those symbols  
141:  //The function puts 0x8X (or 0x9X) and it's following symbol  
142:  //in a two symbol buffer and then sends that buffer to  
143:  //function processUARTBuffer  
144:  //***********************************************************  
145:    
146:  void processUARTSymbol(int UARTSymbol) {  
147:    extern int UARTBufferCounter;  
148:    extern int UARTBuffer[];  
149:    
150:    //If symbol is MIDI "Note On"(0x9X) or MIDI "Note Off"(0x80),  
151:    //place symbol and it's following symbol in UART buffer  
152:    if (((UARTSymbol & 0xF0) == 0x80) || ((UARTSymbol & 0xF0) == 0x90)) {  
153:      UARTBufferCounter = 1;  
154:      UARTBuffer[0] = UARTSymbol;  
155:    } else if (UARTBufferCounter == 1) {  
156:      UARTBufferCounter = -1;  
157:      UARTBuffer[1] = UARTSymbol;  
158:      processUARTBuffer(UARTBuffer);  
159:    }  
160:    
161:    return;  
162:  }  
163:    
164:    
165:  //***********************************************************  
166:  //**************Function processUARTBuffer*******************  
167:  //Interprets UARTBuffer, toggles LED and sends appropiate  
168:  //I2C data to DAC  
169:  //***********************************************************  
170:    
171:  void processUARTBuffer(int UARTBufferF[]) {  
172:    extern int i2cData[];  
173:    
174:    //If UART symbol is MIDI "Note on", light LED and  
175:    //send data to DAC  
176:    if ((UARTBufferF[0] & 0xF0) == 0x90) {  
177:      i2cData[0] = (UARTBufferF[0] & 0x0F);  
178:      i2cData[1] = UARTBufferF[1];  
179:    
180:      lightLED();  
181:      i2cDataToBeSent = 1;  
182:    }    //If UART symbol is MIDI "Note off", turn off LED and  
183:      //send zeros to DAC  
184:    else if ((UARTBufferF[0] & 0xF0) == 0x80) {  
185:      i2cData[0] = 0x00;  
186:      i2cData[1] = 0x00;  
187:    
188:      unlightLED();  
189:      i2cDataToBeSent = 1;  
190:    
191:    }  
192:    
193:    return;  
194:  }  
195:    
196:  //***********************************************************  
197:  //**************Function lightLED****************************  
198:  //Turn on RA0 to light LED  
199:  //***********************************************************  
200:    
201:  void lightLED() {  
202:    int GPORTA;  
203:    //Toggle LED  
204:    //Read latch A into ghost register  
205:    GPORTA = LATA;  
206:    //Toggle bit 0  
207:    GPORTA |= 1;  
208:    //Write back into port register  
209:    PORTA = GPORTA;  
210:    
211:    return;  
212:  }  
213:    
214:    
215:  //***********************************************************  
216:  //**************Function toggleLED***************************  
217:  //Toggle RA0 to toggle LED  
218:  //***********************************************************  
219:    
220:  void toggleLED() {  
221:    int GPORTA;  
222:    //Toggle LED  
223:    //Read latch A into ghost register  
224:    GPORTA = LATA;  
225:    //Toggle bit 0  
226:    GPORTA = GPORTA^(1 << 0);  
227:    //Write back into port register  
228:    PORTA = GPORTA;  
229:    
230:    return;  
231:  }  
232:    
233:  //***********************************************************  
234:  //**************Function unlightLED**************************  
235:  //Turn off RA0 to turn off LED  
236:  //***********************************************************  
237:    
238:  void unlightLED() {  
239:    int GPORTA;  
240:    //Toggle LED  
241:    //Read latch A into ghost register  
242:    GPORTA = LATA;  
243:    //Toggle bit 0  
244:    GPORTA &= ~1;  
245:    //Write back into port register  
246:    PORTA = GPORTA;  
247:    
248:    return;  
249:  }  
250:    
251:    
252:  //***********************************************************  
253:  //**************Function init****************************  
254:  //PIC12F1822 Register setup  
255:  //***********************************************************  
256:    
257:  void init() {  
258:    PORTA = 0x00; //Clear RA0 to unlight LED  
259:    
260:    //I2C baud rate generator set to Fclock = Fosc/(4(SSP1ADD+1)) = 4MHz/(4(2+1))=333kHz  
261:    SSP1ADD = 0b00000010;  
262:    
263:    //SSP1STAT SSP1 STATUS REGISTER  
264:    //SSP1STAT = 0b0000 0000; //default  
265:    
266:    //SSP1CON1 SSP1 CONTROL REGISTER 1  
267:    //I2C Master mode  
268:    //Serial port pins SDA and SCL enabled  
269:    SSP1CON1 = 0b00101000;  
270:    
271:    //SSP1CON2 SSP1 CONTROL REGISTER 2  
272:    //SSP1CON2 = 0b00000000; //default  
273:    
274:    //SSP1CON3 SSP1 CONTROL REGISTER 3  
275:    //SSP1CON3 = 0b00000000; //default  
276:    
277:    //Set all I/O's to digital  
278:    ANSELA = 0x00;  
279:    
280:    //ALTERNATE PIN FUNCTION CONTROL REGISTER  
281:    //Set UART RX/TX to pins RA5/RA4  
282:    APFCON = 0b10000100;  
283:    
284:    //0 Internal oscillator, 3 <fosc> on, 6-4 4MHz  
285:    OSCCON = 0b01101000;  
286:    
287:    //Interrupt controller  
288:    //6 Peripheral interrupt enabled  
289:    //7 Global interrupt enabled  
290:    INTCON = 0b11000000;  
291:    
292:    //TRISA  
293:    //RA1,2,5 set as input, all other IO's set as output  
294:    TRISA = 0b00100110;  
295:    
296:    //Free running bad rate timer is 7  
297:    SPBRGH = 0x00;  
298:    SPBRGL = 0x07;  
299:    
300:    //TXSTA: TRANSMIT STATUS AND CONTROL REGISTER  
301:    //8-bit transmission, transmit enable, asynchronous mode, high baud rate selected  
302:    //Baud rate is FOSC/[16 (n+1)] = 4MHz/(16 (7+1)) = 31250, approx 31500 symbols/sec  
303:    TXSTA = 0b10100110;  
304:    
305:    //RECEIVE STATUS AND CONTROL REGISTER  
306:    //Serial port enabled, continuous receive enabled  
307:    RCSTA = 0b10010000;  
308:    
309:    //PERIPHERAL INTERRUPT ENABLE REGISTER  
310:    //USART Receive interrupt enabled  
311:    //Synchronous Serial Port (MSSP) Interrupt Enable  
312:    PIE1 = 0b00101000;  
313:    return;  
314:  }  
315:    
316:    
317:  //***********************************************************  
318:  //**************Function i2cTransmitProcess******************  
319:  //I2C transmission process that is called from the  
320:  //interrupt service routine  
321:  //***********************************************************  
322:    
323:  void i2cTransmitProcess() {  
324:    
325:    extern int i2cTransmitCycle;  
326:    extern int i2c7bitAddress;  
327:    extern int i2cData[];  
328:    extern int i2cDataCounter;  
329:    extern int i2cDataNoBytes;  
330:    extern int i2cTransmitInProgress;  
331:    
332:    //I2C transmission concists of several interrupt events, variable i2cTransmitCycle is used to step through these events  
333:    //The first interrupt comes with the start condition, SEN, is set.  
334:    //Send address  
335:    if (i2cTransmitCycle == 1) {  
336:      SSP1BUF = (i2c7bitAddress << 1); //converting 7-bit address to 8-bit write address  
337:    }  
338:    
339:    //Step through and send data in data array  
340:    if (i2cTransmitCycle == 2) {  
341:      if (i2cDataCounter < i2cDataNoBytes) {  
342:        SSP1BUF = i2cData[i2cDataCounter];  
343:        i2cDataCounter += 1;  
344:        i2cTransmitCycle = 1;  
345:      } else {  
346:        i2cTransmitCycle = 3;  
347:      }  
348:    }  
349:    
350:    //Send stop condition  
351:    if (i2cTransmitCycle == 3) {  
352:      PEN = 1;  
353:    }  
354:    
355:    //Stop is complete, ready to transmit again  
356:    if (i2cTransmitCycle == 4) {  
357:      i2cTransmitInProgress = 0;  
358:    }  
359:    
360:    i2cTransmitCycle += 1;  
361:    
362:    return;  
363:  }  

No comments:

Post a Comment