Introduction to dsPIC33 programming

From DP

Jump to: navigation , search

This introduction aims to help you start writing and compiling your own applications for the web platform. We cover lots of basics like toggling pins, configuration bits, clock settings, and peripheral setup. The programming language used is C.

The example application will allow you to control the web platform indicator LEDs from a serial terminal, but it can be expanded for lots of other uses too.


IDE and compiler


The web platform microcontroller is a Microchip PIC 33FJ128GP204. The PIC 33F- series is often called dsPIC, referring to the digital signal processing hardware in the chip. The 128GP204 has basic digital signal processing hardware that multiplies and divides large numbers quickly.

You’ll need:

Helpful references

You’ll need two free Microchip programs to compile the demo application. First, download and install MPLAB. MPLAB is an IDE for lots of different Microchip compilers. Download and install MPLAB before installing the compiler.

Next, get the free demonstration C30 compiler for the 33F PIC family. We prefer the C30 ‘LITE’ version that compiles for both PIC24 and PIC33. After 60 days some advanced features like procedural abstraction are disabled, but we never use those features because they produce unexpected results if you don’t properly feed and care for the compiler.

Download and extract the source code for this demo to a folder. Start MPLAB and open the project file ds33Intro.mcw. You may need to locate your own p33FJ128GP204.gld linker file, there’s a copy in the C30 compiler install directory, usually \{C30}\support\dsPIC33F\gld\.

Hardware profile

Most of the code for this demo is located in main.c, open it now.

1  #include "HardwareProfile.h"

This statement at the top of the file includes code from HardwareProfile.h which contains a bunch of shortcut definitions that give the web platform hardware user friendly names.

1  #include <p33Fxxxx.h>
3  //these are shortcut names for the LED and IO pins
4  #define SD_TRIS         (TRISAbits.TRISA10)
5  #define SD_I            (PORTAbits.RA10)
6  #define SD_O            (LATAbits.LATA10)

This is an excerpt from HardwareProfile.h. The #define statements give the hardware names that are easier to remember and use. We control the SD LED with the code ‘SD_O=1′, and the C compiler will use the definition in HardwareProfile.h to replace SD_O with the full LATAbits.LATA10 pin name.

The only absolutely necessary line in HardwareProfile.h is #include <p33Fxxxx.h>, this line includes a file that tells the compiler about the features of the PIC and it must be included in all source code.

Configuration bits

Configuration bits are settings that control how the PIC is configured when it first starts. This is particularly important for clock settings and watchdog timers. If the configuration bits are set for an external clock source, but none is present (as on the web platform), the PIC will never start. The configuration bits are stored in the last few bytes of the flash program space on 16 bit PICs.

1  _FOSCSEL(FNOSC_FRCPLL)              //set clock for internal OSC with PLL
2  _FOSC(OSCIOFNC_OFF & POSCMD_NONE)   //no clock output, external OSC disabled
3  _FWDT(FWDTEN_OFF)                   //disable the watchdog timer
4  _FICD(JTAGEN_OFF & ICS_PGD1);       //disable JTAG, enable debugging on PGx1 pins

The web platform is designed to use the 7.37MHz oscillator inside the PIC chip, this is later boosted to 80MHz using a clock multiplier. If the configuration bits are ever changed to another clock source, the bootloader will be ‘bricked’ and you’ll need to use a PIC programmer like a PICkit2 or ICD2 to restore correct configuration bits.

When you compile your own firmware, be sure to include the first two config lines shown in the example. They set the clock source to the internal oscillator, and disable external clock hardware. The bootloader in the web platform does program the configuration bits included in firmware files. This seems dangerous, but it’s required to change more benign settings too.

See the end of p33FJ128GP204.h in \{C30}\support\dsPIC33F\h\ for a complete list of configuration bits and the keywords that set them.

The main function

1  int main(void){
2      char c;

After power on and a few initialization operations, the microcontroller executes user code in the main() function. Every C program should include this function.

We also created a single byte variable ‘c’ to use later in the program.

Our program starts by configuring the PIC pins and other hardware we’ll need for the demo application.

Configure the oscillator

1  // setup internal clock for 80MHz/40MIPS
2  // 7.37/2=3.685*43=158.455/2=79.2275
3  CLKDIVbits.PLLPRE=0;        // PLLPRE (N2) 0=/2
4  PLLFBD=41;                  // pll multiplier (M) = +2
5  CLKDIVbits.PLLPOST=0;       // PLLPOST (N1) 0=/2
6      while(!OSCCONbits.LOCK);    // wait for PLL ready

The clock system on dsPICs is fairly complicated compared to previous PIC microcontrollers. We use the internal oscillator to derive clock frequencies up to 80MHz. The 7.37MHz internal oscillator with frequency multiplier (PLL) should already be set as the clock source by the configuration bits.

First, we set the prescaler (PLLPRE) to divide the 7.36MHz oscillator output by 2, feeding a 3.685MHz clock source to the PLL. We set the PLL (PLLFBD) to multiply the clock by 43, yielding a 158.4MHz output frequency. Finally, the postscaler (PLLPOST) is set to divide the clock in half once more for a system clock speed of 79.2MHz. Once configured, we have to wait for the clock to stabilize. The PLL LOCK bit will be set to 1 when the clock is ready.

Note that the minimum prescaler and postscaler values are 2 (0=2), and other minimum and maximum conditions apply to the PLL input frequency. See section 9.1.4 (page 139) of the datasheet for more details.


1  //uart
2  //assign pin 14 to the UART1 RX input register
3  //RX RP14 (input)
4  U1RXR_I = 14;
5  //assign UART1 TX function to the pin 15 output register
6  //TX RP15 (output)
7  RP15_O=U1TX_O;

A fantastic feature of 16 bit PIC microcontrollers is peripheral pin select (PPS). Any pin labeled RPx can be configured for UART, SPI, PWM, timer input, interrupt, and more. This feature makes possible the simple routing used on the web platform. Without it, we’d need a lot more vias and crazy routing to connect all the hardware to fixed pins.

The serial UART connected to the FTDI USB->serial chip (IC4) is assigned with PPS.

PPS inputs are configured by assigning an RPx number to a peripheral, PPS outputs are configured by assigning a peripheral to an RPx pin. PIC pin 14 (also RP14) is the UART receiver, we put the RPx pin number in the UART receiver’s PPS register (U1RXR_I). PIC pin 15 (also RP15) is the UART transmitter, we put the UART transmitter PPS number (U1TX_O) in the RP15 PPS register.

Seven of the 8 I/O header pins have PPS features. If you want SPI, a UART, or PWM on these pins, you can just assign it with PPS. All the PPS registers and numbers are defined towards the end of HardwareProfile.h. See section 11.4 (page 157) of the datasheet for a complete list of the pins and hardware that can be assigned with PPS.

UART setup

1  //setup UART
2  U1BRG = 85;              //86@80mhz,
3  U1MODE = 0;              //clear mode register
4  U1MODEbits.BRGH = 1;     //use high precision baud generator
5  U1STA = 0;               //clear status register
6  U1MODEbits.UARTEN = 1;   //enable the UART RX
7  IFS0bits.U1RXIF = 0;     //clear the receive flag

Next we configure the serial UART module that connects to the FTDI USB->serial chip. The speed is determined by the value in UxBRG. Use a baud rate calculator to calculate the value for your desired serial port speed (key constants:ds33, 79.22MHz, BRG16=0, BRGH=1).

We enable the UART receiver, but leave the transmitter off. This is to protect the FTDI chip from being back-powered by the UART TX pin when it’s not connected to a USB port. The UART transmit function is written to enable the transmitter after the first byte is successfully received from the FTDI chip. We have multiple assurances from FTDI engineers that a high level on the pin of an unpowered FTDI chip isn’t a problem, but we try to avoid it anyways.

We included the UART configuration code in the main function for clarity and simplicity, but you could replace it with a call to the InitializeUART1() function that contains the same code. See section 18 (page 211) of the data sheet for more about the UART configuration options.

Analog pins to digital

1  AD1PCFGL = 0xFFFF;   //digital pins

PIC pins start as analog inputs to protect any sensitive externally connected components from unwanted pin output. To use a pin for digital input and output, we need to disable the analog functions on a pin by writing 1 to the corresponding bits of the AD1PCFGL register.

We set all the pins to digital by writing all 1s (0xffff) to AD1PCFGL. The pins are now digital inputs, a state that still protects any external connections from unwanted output.

LED setup

1  //setup LEDs
2  SD_TRIS  = 0;
3  IO1_TRIS = 0;
4  LD1_TRIS = 0;
5  SD_O     = 1;
6  LD1_O    = 1;
7  IO1_O    = 1;

At this point all the PIC pins are configured as digital inputs. In order to drive a LED the pins must be outputs. Each pin is controlled by one bit in a TRIS, LAT, and PORT register.

The TRIS register controls pin direction. A ‘0′ in the TRIS register configures a pin for output. The first three lines of code configure LEDs LD1, LD2, and SD for output. SD_TRIS and the other macros are defined in HardwareProfile.h. You could use the actual pin TRIS bit names instead, but they’re not as convenient: SD_TRIS is the same as TRISAbits.TRISA10.

The LAT register controls pin state. A ‘1′ in the LAT register connects the pin to the chip power supply (3.3volts output), a ‘0′ connects it to ground (low). The next three lines of code turn on the LEDs. These macros are also defined in HardwareProfile.h, SD_O is the same as LATAbits.LATA10.

Use the PORT register to read pins. We didn’t use it in this demo, but it’s important to note that if you test the LAT register bits you’re actually reading the high/low pin configuration. Read the PORT register to see the actual state of input pins. The _I macros in HardwareProfile.h define the correct PORTxbit.Rxx bits for the web platform IO pins.

Main program loop

1   while(1){    //never ending loop
3       if(UART1RXRdy()==1){     // check for data waiting
4           c = UART1RX();       // get the character
5           if(c == '0'){        // if the character is 0
6               LD1_O = 0;       // turn off LED1
7               IO1_O = 0;       // turn off LED2
8               SD_O  = 0;       // turn off the SD LED
9           }else if(c == '1'){  // if the character is 1
10              LD1_O = 1;       // turn on LED1
11          }else if(c == '2'){  // if the character is 2
12              IO1_O = 1;       // turn on LED2
13          }else if(c == '3'){  // if the character is 3
14              SD_O = 1;        // turn on the SD LED
15          }
16          UART1TX(c);          // echo the character back
17     }
18  }

Finally we get to the simple program. We want the code to execute in a loop, so we wrap everything inside while(1){}. Since while(1) is always true, the PIC will run this code forever.

The loop constantly checks the UART for incoming data from the USB->serial connection. If there’s data available, it’s copied into the variable ‘c’. If c is ASCII character 0, all the LEDs are turned off. If it’s equal to ASCII characters 1, 2, or 3, an individual LED is turned on. At the end, c is echoed back.


Download the full project source code ZIP file from here.

Now that we’ve walked through the source code, compile the program (Project->Make, or F10).

A HEX file will be created in the \output folder. Load it with the ds30 Loader bootloader app, or flash it with a PIC programmer like a PICkit or ICD.

Testing the UART


Power the web platform and connect a USB cable. Install the FTDI device drivers if it’s the first time you’ve connected to an FTDI chip. The FTDI chip will appear as a new virtual serial port on your system.

Press the reset button on the web platform. LEDs LD1, LD2, and SD will light.

Open a terminal, we like Tera Term, and connect to the web platform’s virtual serial port.

Type 0 into the terminal to turn off all the LEDs. Type 1,2,or 3 to light different LEDs. Each character will be echoed back to the terminal.

Taking it further