Web platform: Introduction to dsPIC33 programming

See the latest version on the documentation wiki.

There are reports of web platform deliveries in the forum, so we thought this would be a good time for an introduction to dsPIC33 programming.

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 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.

Assembled web platforms are available at Seeed Studio now for $40, including worldwide shipping.

IDE and compiler

The web platform microcontroller is a Microchip PIC 33F128GP204. 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 p33F128GP204.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.

#include "HardwareProfile.h"

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

#include <p33Fxxxx.h>

//these are shortcut names for the LED and IO pins
#define SD_TRIS            (TRISAbits.TRISA10)
#define SD_I            (PORTAbits.RA10)
#define SD_O            (LATAbits.LATA10)

This is an excerpt of 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 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 file 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 16bit PICs.

_FOSCSEL(FNOSC_FRCPLL)		//set clock for internal OSC with PLL
_FOSC(OSCIOFNC_OFF & POSCMD_NONE)	//no clock output, external OSC disabled
_FWDT(FWDTEN_OFF)  //disable the watchdog timer
_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.

Update: Thanks to Markus Gritsch for pointing out that the PGx1 pin should be set with the correct macro ICS_PGD1. If you’re having a problem debugging this firmware with PICkit or ICD2 this might be the problem.

The main function

int main(void){
	char c;

After power on and a few initialization operations, the microcontroller executes user code in the main() function. Every 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

	//setup internal clock for 80MHz/40MIPS
	CLKDIVbits.PLLPRE=0; // PLLPRE (N2) 0=/2
	PLLFBD=41; //pll multiplier (M) = +2
	CLKDIVbits.PLLPOST=0;// PLLPOST (N1) 0=/2
        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.


	//assign pin 14 to the UART1 RX input register
	//RX RP14 (input)
	U1RXR_I = 14;
	//assign UART1 TX function to the pin 15 output register
	//TX RP15 (output)

A fantastic feature of 16bit PIC microcontrollers is peripheral pin select. 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

    //setup UART
    U1BRG = 85;//86@80mhz, 85@79.xxx=115200
    U1MODE = 0; //clear mode register
    U1MODEbits.BRGH = 1; //use high precision baud generator
    U1STA = 0;	//clear status register
    U1MODEbits.UARTEN = 1; //enable the UART RX
    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

	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

	//setup LEDs
	SD_TRIS = 0;
	IO1_TRIS = 0;
	LD1_TRIS = 0;
	SD_O = 1;
	LD1_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

    while(1){//never ending loop

		if(UART1RXRdy()==1){//check for data waiting
			c=UART1RX();//get the character
				LD1_O = 0;
				SD_O = 0;
			}else if(c=='1'){
				LD1_O = 1;
			}else if(c=='2'){
			}else if(c=='3'){
				SD_O = 1;
			UART1TX(c);//echo the character back

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.


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

Join the Conversation


  1. Hi

    Very nice post !

    I can’t wait til the weekend when I will be trying this guide.

    Keep posting this demos and tutorials :-)

    Bo :o)

  2. My understanding so far is, that when using the bootloader, the ds30 loader application replaces the goto at address 0000 with a goto to the bootloader, stores the original goto just before the bootloader and start the program using this goto on bootloader exit.

    The article says “Load it with the ds30 Loader bootloader app, or flash it with a PIC programmer like a PICkit or ICD”.

    Would using a PICkit not render the bootloader useless, since the goto at address 0000 just starts the example program, and the bootloader is never entered again?

  3. I think using 0b11 in the line
    _FICD(JTAGEN_OFF & 0b11);
    is not correct. 0b11 is expanded to the 16 bit value 0b0000000000000011 which sets all other bits in FICD to zero. Better use ICS_PGD1 which is defined as 0xFFFF:

    1. This is *really an issue* when using a PIC programmer to program the firmware. It took me quite some time to figure out, why most of the examples do not work on the Web Platform when programmed with a programmer, but work fine when transferred using the bootloader. The bootloader seems to not program the config bits, at least not the bits in FICD, which hides the bug introduced when using & 0b11.

      MCstackDemo MCstack-SDcard-server, and ds33Intro all contain this bug in either HardwareProfile.h or main.c. It should really be corrected in the SVN (and ideally also in the provides .zip files in the download section) to prevent others from stumbling over this issue.

  4. Why is PPS (for the uart) working in this example? Section “ Control Register Lock” in the data sheet says, one has to execute the special command sequence to clear and another sqeuence to set the IOLOCK bit in OSCCON. I am confused.

      1. IOLOCK cannot be disabled. According to section “ Configuration Bit Pin Select Lock” an *additional* level of safety can be added, which allows only *one* write session, if IOL1WAY is left unprogrammed. On the other hand:

        “Programming IOL1WAY allows user applications unlimited access (with the proper use of the unlock sequence) to the peripheral pin select registers.”

        Mind the part in the parenthesis. As I understand this, either the data sheet is not correct, or there is a bug in the silicon.

    1. Check page 143 with the description of the OSCCON register. In the table IOLOCK is R/W-0, meaning read/write, reset startup value of 0. Below, the definition of bit 6 (IOLOCK) shows that 0= PPS is not locked.

      1. Ian, thank you for the directions. I misunderstood section 11.6.3, not recognizing that all those safety measures are optional. Section 30.4.5. of the Reference Manual makes it clear: “Since the IOLOCK bit resets in the unlocked state, it is not necessary to execute the unlock sequence after the device has come out of reset.”

        Thanks again.

  5. it appears that the Boot loader is not responding now since i flashed it with the hex output from MPLAB TCPIP MDD SD card Demo APP-C30. is there any way of restoring the boot loader through the USB port to the dspic33? as it is a SMD i will not be able to remove it from the board to flash it in a pic programmer.

    Any help will be appreciated. Jason.

    1. Hi Jason – the bootloader should be ok. If not, it can be reprogrammed through the five pin ICSP header with a PIC programmer.

Leave a comment

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.