Here's how I got started developing on a Microchip dsPIC33F.
I wanted to speed up the flame-detecting vision system on Pokey from 3fps to 30fps by upgrading from an 8-bit, 20MHz ATmega328P to a 16-bit, 80MHz dsPIC33F. The main performance boost comes from the dsPIC's very fast ADC.
|
Pokey with Game Boy Camera |
I chose a dsPIC33FJ128GP802-I/P in a 28-PDIP package. It features 128KB flash, 16KB RAM, 16 remappable pins, a bunch of peripherals, DMA, and 1.1MSPS 10-bit ADC, among other cool features that make it a good choice for a low resolution grayscale vision system.
If you're just starting out, this
Dangerous Prototypes Introduction will be quite helpful. I'll cover some of the same stuff in this post.
MPLAB IDE
I'm using the MPLAB IDE v8.6.0 which features a pretty familiar interface not unlike AVRStudio, Netbeans, Eclipse, IAR EWARM, and the like.
The IDE features workspaces in which you develop projects. The left panel features a file explorer for the workspace. The bottom panel features various types of output.
Code is written in the big panel. Toolbar buttons are used to build code, program the MCU, manage files, project, and perform other routine tasks. As with AVR Studio and other standard IDEs, you can double-click on compiler errors to find the offending source.
Turning on line numbers is helpful, sometimes. Select Properties from the Edit menu, click the 'C' File Types tab, and click the Line Numbers check box.
Programmer
I'm using the PICkit3. I took advantage of promotional pricing at the time. I figured, why hassle with a clone when I can increase my chances of success without spending much more.
They now cost around $45 so it's not exactly a bargain but it's acceptable compared to other programmers (I'm thinking particularly of the JTAG ICE MkII I've been using).
You could also get a Microstick for the dsPIC which has a built in programmer and debugger and costs $25.
Breadboard Setup
Taking baby steps, first is breadboarding the dsPIC. The hello world programs will come after that.
|
Breadboarding the dsPIC33FJ128GP802-I/P |
In the picture above, Pin 1 is the lower left. That's the !MCLR (reset) pin. Note the tiny 10K pullup resistor, partially hidden.
Pin 8 is VSS (ground). Pin 13 is VDD (3.3V) with a 0.1uF decoupling capacitor installed.
Pin 28 is the AVDD (analog 3.3V) pin, 27 is the AVSS pin (the brown ground wire is hidden somewhat). I put a 0.1uF decoupling capacitor here, too.
The 10uF capacitor at pins 19 and 20 bypass V
CAP to VSS. And that's all you need. You don't even need an external crystal, although you could use one.
To program the chip you need power. I've used a Sparkfun FTDI breakout to provide 3.3V. It is also connected to UART1 which is setup for transmit on pin 25 (PB15), receive on pin 24 (PB14).
Here's the full pinout reproduced for educational purposes:
|
28-DIP dsPIC33 pinout |
To program the device, connect the PICkit3. Pin 1 with the arrow connects to !MCLR, the green jumper above. Pin 2 is target VDD (telling the PICkit3 if the device is working), the red jumper at the bottom of the breadboard. Pin 3 is VSS, the bottom black jumper. Pin 3 is PGD (PGED1, dsPIC pin 4), the orange jumper. Pin 4 is PGC (PGEC1, dsPIC pin 5), the white jumper.
|
PICkit pinout |
Hello World
The first version of Hello World for an embedded system is blinking an LED. You can learn a lot about an MCU just by getting it to that point. An AVR is dead simple, an ARM7 like the one on my LPC2103 breakout board, is a nightmare--it took days. The dsPIC33F is pretty easy.
MCU Setup
The dsPIC uses an interesting method of configuring the clock source, PLL, watchdog, and other options. One places a set of statements before your code. Then, in the main() routine, one sets up the PLL multipliers and divisors. The following code shows setting up a dsPIC33F for 80MHz operation.
LEDs and GPIO Pins
Three registers control GPIO on the dsPIC. A tri-state register,
TRISx, controls direction, a latch register, LATx, and a port register,
PORTx. You can write to the
PORTx register which writes to the latch, or you can write to
LATx directly. Reading from LAT reads the latch, reading from PORTx reads the actual pin state.
On the dsPIC33 I'm using, there's an A and B port. So you'd use
TRISA and
TRISB,
PORTA and
PORTB,
LATA and
LATB. Let's use RA0 for our LED.
All pins start out as analog inputs after a reset. To disable the analog functionality of a pin, write a 1 to the corresponding bit in the
AD1PCFGL register.
Next, configure the RA0 pin as an output by writing a 1 to the
TRISA register's bit for RA0. In C, use
TRISAbits.TRISA0 to reference the A0 bit of the
TRISA register.
In general you can reference bits of any register like this. To turn the pin on, write a 1 to
TRISAbits.TRISA0 To turn the pin off, write a 0 instead.
You can toggle a pin using
LATAbits.LATA0 = ~LATAbits.LATA0.
a shorter notation for the above bits is to use LD1_TRIS, LD1_I, LD1_O defined as follows in HardwareProfile.h
#define LD1_TRIS (TRISAbits.TRISA0)
#define LD1_I (PORTAbits.RA0)
#define LD1_O (LATAbits.LATA0)
Delays
If you want to blink an LED in a simple loop you need a delay. Ideally one that is carefully and correctly timed. Microchip provides such a function, and macros, through the C30 compiler. In the C30 compiler directory under the folder
src, unzip the
libpic30.zip archive to the folder pic30 and find delay32.s.
I copied this into my project folder. I also had to copy null_signature.s in as well. Then, add both files to the project source.
Then, in your C program, define FCY the instruction cycle frequency. If you're running a high frequency, define this as a long long.
After this definition, include libpic30.h Finally, you can call __delay_ms() with an integer parameter representing the number of milliseconds to delay. Other functions include __delay_us() for microsecond delays and __delay32() delays the specified number of instruction cycles.
UART
The second version of Hello World actually prints the string "Hello World" over a serial connection. I like to play with UART next as it's useful for debugging, and you can learn a little about the MCU's peripherals without too much complexity. Set up the pins, the baud rate and maybe a couple other options, and then start sending data.
Setting baud rate with the UART peripheral can be done using the high precision baud rate generator for higher speeds or the low precision generator for lower speeds. Select which to use via the
BRGH bit of the
UxMODE register. Set it to 1 for high precision, 0 for low.
For the high precision generator, baud rate divisor,
UxBRG, is calculated by (FCY/(4*baud)) - 1 where FCY is the frequency of the instruction cycle clock.
The low precision baud rate generator uses BRG = (FCY/(16*baud)) - 1
Here are the values I calculated given FCY=40000000ULL
|
BRGH=0 |
BRGH=1 |
Baud |
BRG |
1200 |
2082 |
8332 |
2400 |
1040 |
4165 |
4800 |
519 |
2082 |
9600 |
259 |
1040 |
19200 |
129 |
519 |
28800 |
85 |
346 |
38400 |
64 |
259 |
57600 |
42 |
172 |
115200 |
20 |
85 |
Once baud rate is set, clear the status register,
UxSTA, enable the UART with the
UARTEN bit of the
UxMODE register, setting it to 1. Finally, clear the receive flag,
UxRXIF bit of the
IFS0 register.
You're not quite ready. The dsPIC offers a really cool capability, namely, mapping any peripheral to any pin. Even the ARMs I've worked with can't boast that kind of flexibility! So, let's setup pin 14 as the UART receive pin, and 15 as the transmit. Set the pin assignment in the UxRXR_I and UxTX_O registers, respectively.
RP15_O is shorthand for RPOR7bits.RP15R and is defined in HardwareProfile.h
Finally, you're ready to send and receive. The bit
U1STAbits.URXDA indicates data is ready to be read out of the
U1RXREG data register. When it's time to transmit, put a byte in the
UARTxTX register to send it. Here's the code to do all the above.
That's All
So, that's about it. I'll post more articles as I progress, featuring other peripherals, tips, and tricks, and the like.