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