Some general hints for using PIC microcontrollers, mostly with the Microchip C30 compiler.
Table 8-1 and subsequent tables in the C30 compiler user's guide define all the interrupt vectors available. I started by defining a simple ISR that toggles an LED as follows:
void __attribute__((__interrupt__, no_auto_psv)) _U1RXInterrupt(void) { LATBbits.LATB15 = LATBbits.LATB15; _U1RXIF = 0; }
The compiler manual states that you can do a shortcut of void _ISR _U1RXInterrupt(void);
but in practice this caused a warning about PSV that I'd sooner not have to see.
In your initialisation phase, clear the interrupt bit and enable interrupts respectively.
IFS0bits.U1RXIF = 0; IEC0bits.U1RXIE = 1;
Then actually enable it
U1MODE = 0x8000; /* Reset UART to 8-n-1, alt pins, and enable */ U1STA = 0x0440; /* Reset status register and enable TX & RX*/
You'll need to have mapped RP pins to somewhere exciting, of course.
After this, the ISR will run whenever it receives a character, but only four times. After this, the receive buffer will fill up and you'll be screwed. Let's empty the buffer.
void __attribute__((__interrupt__, no_auto_psv)) _U1RXInterrupt(void) { int a; a = U1RXREG; LATBbits.LATB15 = LATBbits.LATB15; _U1RXIF = 0; }
This works fine until your ISR takes too long to execute - presumably new characters coming in during while the function is being called and the buffer fills up again. After some fiddling I realised that you should either do slow things somewhere other than the ISR (obviously) or check U1RXREG after you've done something slow.
void __attribute__((__interrupt__, no_auto_psv)) _U1RXInterrupt(void) { int a; slow_operation(1000); while (U1STAbits.URXDA) a = U1RXREG; _U1RXIF = 0; }
Whether this is appropriate for you depends on what you're doing with the input, but at least checking at the beginning of the ISR rather than the end had me stumped for a little while.
The ISR then needs to notify the main program that data is available or else you get annoying race conditions. With characters hitting the serial port in quick succession and a main loop that checked the buffer and did things with it (in my case, reassigning the UART pins - this probably didn't help my cause at all!), I'd lose all but the first character.
Nicholas noticed that the dsPIC33F allows you to generate software interrupts by enabling that interrupt and setting the flag. This means you can jump into your U1TXInterrupt routine by doing _U1TXIF = 1; which is handy if your ISR checks a buffer to send characters and you need a way to kick it off in the first place. Thanks!
My development board comes with a potentiometer and four LEDs. I wrote a program that displays a binary value from 0 to 15 depending on the position as a test. 240 is a magic number representing a bit more than maximum value of ADC1BUF0 when the potentiometer was turned fully on. I determined this by setting a breakpoint and examining ADC1BUF0.
Routine assumes that you've set up a timer already.
void __attribute__((__interrupt__, no_auto_psv)) _T1Interrupt(void) { int foo2 = 0xf - (int)(ADC1BUF0/240); LATB = (LATB & 0x0fff ) + (foo2 << 12); _T1IF = 0; // clear interrupt flag }
I initialised the ADC as follows:
void initADC(void) { AD1CON1bits.FORM = 0; // Integer AD1CON1bits.SSRC = 7; // Auto-convert AD1CON1bits.ASAM = 1; AD1CON1bits.AD12B = 1; AD1CON2bits.CHPS = 0; AD1CON3bits.ADRC = 0; AD1CON3bits.ADCS = 63; AD1CON2bits.SMPI = 0; AD1CHS0bits.CH0SA = 5; AD1CHS0bits.CH0NA = 0; AD1PCFGL = 0xFFFF; AD1PCFGLbits.PCFG5 = 0; // AN5 = potentiometer IFS0bits.AD1IF = 0; // IFS0bits.AD1IE = 0; AD1CON1bits.ADON = 1; }
I wired up an XBee Pro Zigbee radio to my MCU using four wires.
Module | PIC |
1 Vcc | Vcc (3.3V) |
2 Dout | RP10 |
3 Din | RP11 |
10 Gnd | Gnd |
Configure UART to talk to these pins at 9600 bps, the default speed.
Send +++ to the module and check for response - you should get an OK, apparently. I can see data hitting the module using a logic probe, but nothing comes back. Not quite sure why yet :( Unfortunately my PIC33F MCU only has a single UART so using it to talk to the module and the USB serial debug port is a bit complicated.
Okay, the problem seems to be that it likes to get something non-escape-like before +++, so transmit some junk, wait, and then +++. Now I have a problem with my ISR only getting one character (e.g. 'O' instead of 'OK'), though this may actually be a consequence of breakpoints in ISRs screwing up serial communications. I managed to get the ISR putting data into a buffer and then had another loop that would switch to the USB-serial and dump it out there, and this worked splendidly.