17
Syed Tahmid Mahbub Introduction to the PIC32: The Basics, Getting Started, IO ports and the First Program

Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

Embed Size (px)

DESCRIPTION

Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

Citation preview

Page 1: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

Syed Tahmid Mahbub

Introduction to the PIC32: The Basics, Getting Started,

IO ports and the First Program

Page 2: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

2

Image Sources:

Cover photo -

http://www.flickr.com/photos/microchiptechnology/6847596741/sizes/m/in/photostream/

Fig. 1 – PIC32MX250F128B datasheet

Fig. 2 – PIC32MX250F128B datasheet

Page 3: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

3

INTRODUCTION

I have recently starting using the PIC32 series. I’ve been experimenting with the Microchip Microstick II

board that comes with the PIC32MX250F128B microcontroller (along with a few other non-PIC32

microcontrollers). Conveniently, the Microstick II has the microcontroller programmer on the board

itself. Here’s a link to the Microstick II information page:

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en556

208

From now on, the specific PIC32 microcontroller I’ll be referring to will be the PIC32MX250F128B. Here’s

the datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/61168E.pdf

The compiler I’m using is the Microchip XC32:

http://www.microchip.com/pagehandler/en_us/devtools/mplabxc/

The IDE is MPLABX: http://www.microchip.com/pagehandler/en-us/family/mplabx/

In this article, I’ll talk about some basics of starting with the PIC32, the IO lines, the configuration

settings and a basic starting program to show all these in action. I assume that you know how to and can

install the MPLABX IDE and the XC32 compiler. I also assume that you can create a new project and the

necessary source file. Furthermore, I assume that you know the basics of embedded C programming and

the absolute basics of microcontrollers – bits, bytes, number systems, ports and registers.

Page 4: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

4

PIN DESCRIPTION

Let’s start by taking a look at the PIC32MX250F128B pinout:

Fig. 1 – Pinout diagram of the PIC32MX250F128B

The microcontroller power supply pins are pins 8 (VSS), 13 (VDD) and 19 (VSS). With respect to VSS

(which will just be grounded), VDD must be at least 2.3V and at max 3.6V for the microcontroller to

operate properly: 2.3V ≤ VDD ≤ 3.6V. In most cases, VDD is chosen to be 3.3V. It is required that you use

decoupling capacitors on all power lines. 100nF capacitors are recommended. These should be placed as

close to the microcontroller as possible. The Microstick II board has these capacitors on the board.

For the PIC32, there is an internal core voltage regulator. To filter the output voltage of the internal

voltage regulator, a capacitor is placed between VCAP (pin 20) and VSS. This capacitor should be at least

8µF, but is typically 10µF. The capacitor should be a low-ESR capacitor. The Microstick II board has a

10µF capacitor.

Pin 28 is AVDD and pin 27 is AVSS. These are the voltage reference supplies for the internal analog-to-

digital converter (ADC) module. I’ll covert this in a later article. But for now, you need to know that the

AVDD pin must always be connected to the VDD pin, and the AVSS pin must always be connected to the

VSS pin, even if the ADC module is not being used, or even if the ADC module is being used but isn’t

Page 5: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

5

using AVDD and AVSS. You should have at least a 100nF decoupling capacitor on the AVDD-AVSS line. On

the Microstick II board, the AVDD and AVSS pins are connected to the VDD and VSS lines. The

decoupling capacitor has also been placed on the Microstick II board.

As you can see, pin 1 is MCLR (Master Clear). This does the job of resetting the microcontroller. When

the MCLR pin is held high, the microcontroller operates and executes the program. But when this pin is

held low, the microcontroller is in a reset state where it does not execute the program. You can think of

it as the microcontroller being “off”. When the pin is held back high, the microcontroller turns on again

but starts executing the program from the very beginning again, as if it was just turned on. So, on a high-

to-low-to-high transition, the microcontroller is reset. There is a minimum time, however, for which the

MCLR pin must be held low before being high (for reset). This minimum time is 2µs.

Now, notice on the diagram that some pins are filled in gray. These are the pins that are capable of

tolerating higher than 3.6V – up to 5V. These pins are 5V tolerant when acting as output pins. All input

pins are tolerant to 5V, so the gray-filled pins are 5V tolerant when output pins as well.

Now notice the pins that are labeled RAx and RBx (where x is a number). These are the port pins. These

pins can act as inputs or outputs. All these pins are multiplexed with other peripheral modules that you

can use. I’ll cover these as I go to those modules in later articles.

PORT IO LINES

Each of these pins is an IO pin – it can be configured for either an input or an output. The register

controlling the direction of the pin – whether it will be an input or an output – is the corresponding bit in

a TRIS register.

To make a certain pin input, the corresponding TRIS bit has to be set high. To make the pin output, the

corresponding TRIS bit has to be cleared to low. The PIC32MX250F128B has two ports – PORTA and

PORTB. PORTA has five pins – RA0, RA1, RA2, RA3 and RA4. PORTB has fifteen pins – RB0 through RB5

and RB7 through RB15. Each of the ports has a corresponding TRIS register (TRISA for PORTA and TRISB

for PORTB), and each of the port pins has a corresponding TRIS bit.

Page 6: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

6

So let’s take pin 2 – RA0. This is PORT A, bit 0 (or you can call it PORTA0). The TRIS register

corresponding to PORTA is TRISA. The TRIS bit corresponding to PORTA0 is TRISA0. Similarly, the TRIS bit

corresponding to PORTA14 is TRISA14. To make RA0 an output, you have to clear TRISA0 to zero, and

similarly, to make RA0 an input, you have to set TRISA0 to one:

TRISAbits.TRISA0 = 0; // Make RA0 an output

TRISAbits.TRISA0 = 1; // Make RA0 an input

If we wanted to set all of all the PORTA and PORTB pins to outputs, then we would do:

TRISA = 0; // Make all PORTA pins output

TRISB = 0; // Make all PORTB pins output

If we wanted to set all PORTA pins to inputs, and all PORTB pins to outputs, then we would do:

TRISA = 0x1F; // Make all PORTA pins input

TRISB = 0; // Make all PORTB pins output

Now, notice how in the above code, I wrote TRISB to zero. This sets all the TRISB bits to zero, making all

the PORTB pins to be outputs. I said before that PORTB has bits 0 through 5 and 7 through 15, but no bit

6. So, a pin RB6 doesn’t exist. So, what happens when we clear TRISB6 to zero? Nothing. It’s completely

fine. While the physical pin RB6 doesn’t exist, there is a PORTB bit 6 in the PORTB register and a TRISB

bit 6 in the TRISB register. But they do nothing. But do keep in mind that no such thing as RB6 is defined

for your compiler (as this doesn’t physically exist). So, if you tried to do something like:

reg_ra6 = PORTAbits.RA6;

The compiler would generate an error since RA6 doesn’t exist.

So, now that we’ve initialized the direction of the ports/pins, how do we use them?

Page 7: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

7

Let’s say that we’ve initialized all the PORTA pins to input and all the PORTB pins as outputs. So, now

let’s say we want to send a high to PORTB0. To write to any of the port pins, we have to write to the

corresponding bits in the LAT registers. Each PORT has a corresponding LAT register and each port pin

has a corresponding LAT bit. So, to write to PORTB0, we have to write to LATB0. So, to send a high to

PORTB0, we would do:

LATBbits.LATB0 = 1;

Another thing to notice in the above statement is how we’re accessing individual bits of a register. If we

were to write to bit 10 of LATB, we would do:

LATBbits.LATB10 = 1;

Alternately we could make use of another feature of the PIC32, for bit manipulation. Each of these

registers has 3 other registers associated with it: a SET register, a CLR register and an INV register. These

are extremely convenient for atomic bit manipulation. Instead of writing:

LATBbits.LATB10 = 1; // Set RB10 high, leave the rest unaffected

you can write:

LATBSET = 0x400; // Set RB10 high, leave the rest unaffected

What the SET, CLR and INV registers do is that, they affect only the bits that are set high, but leave those

bits unaffected that are set low. So, the SET register sets to one all the PORT bits corresponding to all

the bits that are set high in the SET register. The CLR register clears to zero all the PORT bits

corresponding to the bits that are set high in the CLR register. The INV register inverts all the PORT bits

corresponding to all the bits that are set high in the INV register.

Page 8: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

8

So, let’s say we initially did:

LATB = 0xFFFF; // Set all PORTB pins to high

Then, for example, to turn off/low RB10 and RB11, we could do:

LATBbits.LATB10 = 0; // Clear to low RB10, leave rest unaffected

LATBbits.LATB11 = 0; // Clear to low RB11, leave rest unaffected

Or, we could do:

LATBCLR = 0xC00; // Clear RB10 and RB11, and leave the rest unaffected

Now, if we wanted to output high to all PORTB pins, we could just do:

LATB = 0xFFFF; // Turn on all PORTB pins

Now, let’s look at reading digital inputs from a port pin. To read the digital state of any pin, you need to

read the corresponding PORT pin. So, if we were trying to read the state of RA2, we’d just need to read

the bit 2 of register PORTA. So, let’s say we want to output a high on all PORTB pins if RA2 is high, we’d

do:

if (PORTAbits.RA2 == 1){ // If RA2 is high

LATB = 0xFFFF; // Turn on all PORTB pins

}

So now, you’ve seen how to send outputs to the PORT pins and to read digital inputs from them.

Page 9: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

9

There’s one last thing we need to do before we start using the digital IO lines. Remember when I said

that the PORT pins are multiplexed with other peripheral modules? These peripheral modules can mess

with the PORT. Some of the PORT pins are multiplexed to the ADC module (inputs) and comparator

module. So when you’re trying to use those pins as digital pins, you should ensure that you configure

them as digital pins. To do that, you should disable the three internal comparators and configure two

ADC registers that control whether the pins are analog or digital. The three comparator registers are

CM1CON, CM2CON and CM3CON. All you need to know for now is that writing these three registers to

zero disables the comparators. The registers controlling the configuration of the pins as analog or digital

are ANSELA and ANSELB. ANSELA controls whether PORTA pins are analog or digital, whereas ANSELB

controls whether PORTB pins are analog or digital. Each of the pins (that are multiplexed to the ADC

module) has a corresponding bit in the ANSEL register. So, the analog/digital set bit corresponding to

RA0 would be ANSA0; that for RB13 would be ANSB13.

Now go back to the pinout diagram. You will see pins labeled as ANx (where x is an integer). These are

the pins multiplexed with the ADC module (inputs) and these are the pins you need to configure as

digital. For pins that are not multiplexed to the ADC, for example RB4, it doesn’t matter what you write

to that location (ANSELB bit 4 in this case). However, if you try to write to or read from ANSB4, you’ll

find that this does not exist. ANSB4 is not defined and if you try to build your program, it will generate

an error.

Now let’s say we want to make RA0 digital. RA0 is also labeled as AN0. So, to make RA0 digital, we need

to configure ANSA0. Whenever an ANSEL bit is set to one, that corresponding pin is set to be analog. So,

to make RA0 digital, we need to clear ANSELA bit 0 (ANSA0) to zero. So, we do:

ANSELAbits.ANSA0 = 0;

Since in our codes we aren’t bothered with the ADC module at all (at least for now), we can just

configure all pins to be digital. So, we do that by writing:

ANSELA = 0; // PORTA pins digital

ANSELB = 0; // PORTB pins digital

Page 10: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

10

Similarly, we disable the comparators:

CM1CON = 0;

CM2CON = 0;

CM3CON = 0;

FURTHER CONSIDERATIONS

Now, to complete this article, I’ll have to talk a little about the oscillator in short (I’ll cover the details in

another article on oscillators), some functions, header files and the configuration settings before I

present an example code to demonstrate what I’ve talked about thus far.

For the PIC32MX250F128B oscillator, we have several options: external crystal, external resonator,

external clock or internal FRC (Fast RC) oscillator. For now, I’m going to use the internal FRC (Fast RC)

oscillator as this means that we don’t need an external oscillator.

Now let’s move on to the configuration bits settings. There are certain settings that you need to define

at the beginning of your code to configure the operation of your microcontroller. These are the

configuration settings. You can configure them in MPLAB by setting them in Window > PIC Memory

views > Configuration Bits.

I prefer setting the configuration settings in the code itself so that I know what the configuration

settings are when I look at the code and for portability. If you go to Window > PIC Memory views >

Configuration Bits, you can see the default settings. I made some changes for our code and here is the

complete list of configuration settings. Notice how the settings are configured with #pragma. You don’t

need to put too much thought into this now if you don’t get it. I’ll cover it in more detail in a later article.

//Configuration bits settings:

// DEVCFG3

// USERID = No Setting

#pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration

//(Allow only one reconfiguration)

#pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow

Page 11: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

11

//only one reconfiguration)

#pragma config FUSBIDIO = ON // USB USID Selection (Controlled by the USB

//Module)

#pragma config FVBUSONIO = ON // USB VBUS ON Selection (Controlled by USB

//Module)

// DEVCFG2

#pragma config FPLLIDIV = DIV_12 // PLL Input Divider (12x Divider)

#pragma config FPLLMUL = MUL_24 // PLL Multiplier (24x Multiplier)

#pragma config UPLLIDIV = DIV_12 // USB PLL Input Divider (12x Divider)

#pragma config UPLLEN = OFF // USB PLL Enable (Disabled and Bypassed)

#pragma config FPLLODIV = DIV_1 // System PLL Output Clock Divider (PLL Divide

//by 1)

// DEVCFG1

#pragma config FNOSC = FRC // Oscillator Selection Bits (Fast RC Osc

//(FRC))

#pragma config FSOSCEN = ON // Secondary Oscillator Enable (Enabled)

#pragma config IESO = OFF // Internal/External Switch Over (Disabled)

#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary

//osc disabled)

#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin

//(Disabled)

#pragma config FPBDIV = DIV_8 // Peripheral Clock Divisor (Pb_Clk is

//Sys_Clk/8)

#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection

//(Clock Switch Disable, FSCM Disabled)

#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)

#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog

//Timer is in Non-Window Mode)

#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled (SWDTEN

//Bit Controls))

#pragma config FWDTWINSZ = WISZ_25 // Watchdog Timer Window Size (Window Size is

//25%)

// DEVCFG0

#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)

#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on

//PGEC1/PGED1)

#pragma config PWP = OFF // Program Flash Write Protect (Disable)

#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection

//Disabled)

#pragma config CP = OFF // Code Protect (Protection Disabled)

In the configuration settings, I’ve set the oscillator to be the internal FRC (Fast RC) oscillator. The FRC (Fast RC) oscillator is set to 8MHz but the system clock is 4MHz by default. Why is that, and how can we change the FRC (Fast RC) oscillator frequency? We need to take a look at the OSCCON register. Read on in the next page.

Page 12: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

12

Fig. 2 – OSCCON register

The FRC (Fast RC) oscillator frequency is 8MHz and that’s set in the PIC. But the system clock is not necessarily equal to the FRC (Fast RC) oscillator frequency. It is actually equal to the FRC oscillator frequency divided by a certain number, as set by the FRCDIV bits in the OSCCON register (bits 24, 25 and 26). I’ve shown these bits in the diagram above.

So, you can see in the diagram that the default divider value is 2. That’s why the default system clock is 4MHz. If we change the default value to 1, we can have a system clock of 8MHz. If we change the divider value to 8, then the system clock will be 1MHz. And so on.

The other bits I want to talk about here are the PBDIV bits (bits 19 and 20). These set the peripheral bus clock. The peripheral bus clock is the clock to the peripherals (for example, the timers, the ADC module,

Page 13: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

13

etc.) in the PIC. The default value is system clock divided by 8. We can change this by writing 0 to PBDIV (bits 19 and 20). We can also change this by altering one of the configuration bit settings:

#pragma config FPBDIV = DIV_8

// Peripheral Clock Divisor (Pb_Clk is Sys_Clk/8)

to

#pragma config FPBDIV = DIV_1

// Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)

Now on to the header files. All the register descriptions, essential definitions, etc are defined in the specific device’s header file. You can add this header file called p32xxxx.h which will include the required header file for your device (determining using the device you selected in project settings). So all you need to do is just add the p32xxxx.h header file.

There are a lot of macros and functions for different peripherals of the PIC32 that we can take advantage of. To do that, we have to include the relevant library header files. Instead, we can just add the header file plib.h that will include the peripheral header files.

We add the two header files as:

#include <p32xxxx.h>

#include <plib.h>

There are certain features of the PIC32 that can be configured for maximum performance at the given clock. We can do this all manually (I won’t cover that al here), but instead we can simply just use a library function that will do that for us:

SYSTEMConfigPerformance(8000000L);

// Configure system for max performance for 8MHz

For this function, the header file plib.h must be included.

Page 14: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

14

So, the basic code layout for my code looks like:

/*

* File: main.c

* Author: Syed Tahmid Mahbub

*

* Created on

*

* Program description:

*

*/

// Insert the configuration settings here. I’m not including them here

for conciseness and readability

#include <p32xxxx.h>

#include <plib.h>

void main(void) // same as main()

{

// Configure clock

OSCCONbits.FRCDIV = 0; // 8MHz

OSCCONbits.PBDIV = 0; // 8Mhz for peripherals

// Configure system for max performance

SYSTEMConfigPerformance(8000000L);

// Disable watchdog timer

WDTCON = 0;

// do further initializations, as required

while (1){

// … do tasks

}

}

Page 15: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

15

FINAL CODE

Now, let’s look at our final code – to light an LED that is connected to RA0 (the Microstick II board has a red LED connected to RA0). Here I’ve applied all (or at least most) of what I’ve talked about above.

/*

* File: main.c

* Author: Syed Tahmid Mahbub

*

* Created on 04 November 2013

*

* Program description: Light an LED that is connected to RA0

*/

#include <p32xxxx.h>

#include <plib.h>

// Configuration bits set in Window > PIC Memory views > Configuration Bits

// Source code generated there and pasted here for readability/portability

//Configuration bits settings:

// DEVCFG3

// USERID = No Setting

#pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration

//(Allow only one reconfiguration)

#pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow

//only one reconfiguration)

#pragma config FUSBIDIO = ON // USB USID Selection (Controlled by the USB

//Module)

#pragma config FVBUSONIO = ON // USB VBUS ON Selection (Controlled by USB

//Module)

// DEVCFG2

#pragma config FPLLIDIV = DIV_12 // PLL Input Divider (12x Divider)

#pragma config FPLLMUL = MUL_24 // PLL Multiplier (24x Multiplier)

#pragma config UPLLIDIV = DIV_12 // USB PLL Input Divider (12x Divider)

#pragma config UPLLEN = OFF // USB PLL Enable (Disabled and Bypassed)

#pragma config FPLLODIV = DIV_1 // System PLL Output Clock Divider (PLL Divide

//by 1)

// DEVCFG1

#pragma config FNOSC = FRC // Oscillator Selection Bits (Fast RC Osc

//(FRC))

#pragma config FSOSCEN = ON // Secondary Oscillator Enable (Enabled)

#pragma config IESO = OFF // Internal/External Switch Over (Disabled)

#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary

//osc disabled)

#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin

//(Disabled)

#pragma config FPBDIV = DIV_8 // Peripheral Clock Divisor (Pb_Clk is

//Sys_Clk/8)

Page 16: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

16

#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection

//(Clock Switch Disable, FSCM Disabled)

#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)

#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog

//Timer is in Non-Window Mode)

#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled (SWDTEN

//Bit Controls))

#pragma config FWDTWINSZ = WISZ_25 // Watchdog Timer Window Size (Window Size is

//25%)

// DEVCFG0

#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)

#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on

//PGEC1/PGED1)

#pragma config PWP = OFF // Program Flash Write Protect (Disable)

#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection

//Disabled)

#pragma config CP = OFF // Code Protect (Protection Disabled)

// 8MHz internal Fast RC oscillator selected

// System clock cycle = 125ns when system clock = 8MHz

main(){

// Configure clock

OSCCONbits.FRCDIV = 0; // 8MHz

OSCCONbits.PBDIV = 0; // 8Mhz for peripherals

// Configure system

SYSTEMConfigPerformance(8000000L); // Configure system for max performance

// Disable watchdog timer

WDTCON = 0;

// Disable comparators:

CM1CON = 0;

CM2CON = 0;

CM3CON = 0;

// Configure pins to be digital:

ANSELA = 0; // Make all PORTA pins digital

ANSELB = 0; // Make all PORTB pins digital

// Initialize PORT direction:

TRISA = 0; // Make all PORTA pins output

// Start:

LATAbits.LATA0 = 1; // Turn on LED at RA0

while (1) { // LED has been turned on. Do nothing now

}

}

Page 17: Introduction to the PIC32 - The Basics, Getting Started, IO ports and the First Program

17

CONCLUSION

Here I end this article. In the next article, I’ll talk a little bit more about the IO lines including reading inputs from switches (I’ll present an example of using the IO lines as inputs), using the pull-ups and pull-downs. I’ll also explain the code for blinking an LED, and in the process explain the very important Core Timer of the PIC32MX250F128B and how to create time delays with it.

In this article, I’ve talked about some basics of the PIC32MX250F128B, getting started with it, using the IO lines, the initial necessary header files and configuration settings. I hope you have found this helpful. Let me know what you thought in the comments section. Feel free to ask any questions or provide any suggestions you may have.