avrdude and the chipKIT Platform

The bootloader on the chipKIT is based on the same bootloader that I developed for the mega2560. It uses stk500v2 protocol.

Avrdude can be used from the command line with the chipKIT boards or any other board that has the bootloader installed The command would be something like this >avrude -c stk500v2 -p pic32 -P /dev/tty.usbmodem1234 -b 115200 -U flash:w:filename.hex Or, under Windows(If you’ve copied avrdude.exe and avrdude.conf into the MPLABX bin folder): “C:\Program Files\Microchip\MPLABX\mplab_ide\bin\avrdude.exe” -C”C:\Program Files\Microchip\MPLABX\mplab_ide\bin\avrdude.conf” -v -p32MX795F512L -cstk500v2 -P COM40 -b 115200 -U flash:w:pic32_template_1.production.hex

Getting an hex file for chipKIT

When using mpide, default is to remove all the intermediate files between the mpide source of your project (.mpe file) and code uploaded on the chipKIT. But all the files generated by mpide (including the hex file) can be kept by changing mpide preference file. From Linux:
  1. close any running mpide
  2. edit ~/.mpide/preferences.txt
  3. look for preproc.save_build_files attribute
  4. Set it to true: preproc.save_build_files=true
  5. save the preference file
Now, all temporary files from build command will be kept. To locate the right directory with your build, from mpide, press the “shift” key while starting the build/verify command. You will have details about the build process (commands, outputs, path …) in mpide output console. Example:
/opt/mpide-0023-linux32-20111221/hardware/pic32/compiler/pic32-tools/bin/pic32-g++  -O2  -c  -mno-smart-io  -w  -fno-exceptions  -ffunction-sections  -fdata-sections  -G1024  -g  -mdebugger  -Wcast-align  -mprocessor=32MX320F128H  -DF_CPU=80000000L  -DARDUINO=23  -D_BOARD_UNO_  -Danything_you_want  -Danything=1   -I/home/fred/Arduino/Serial2   -I/opt/mpide-0023-linux32-20111221/hardware/pic32/cores/pic32   -I/opt/mpide-0023-linux32-20111221/hardware/pic32/variants/Uno32    /tmp/build6773645236763377018.tmp/Serial2.cpp  -o  /tmp/build6773645236763377018.tmp/Serial2.cpp.o

...

/opt/mpide-0023-linux32-20111221/hardware/pic32/compiler/pic32-tools/bin/pic32-ar  rcs  /tmp/build6773645236763377018.tmp/core.a  /tmp/build6773645236763377018.tmp/main.cpp.o
/opt/mpide-0023-linux32-20111221/hardware/pic32/compiler/pic32-tools/bin/pic32-g++  -Os  -Wl,--gc-sections  -mdebugger  -mprocessor=32MX320F128H  -o  /tmp/build6773645236763377018.tmp/Serial2.cpp.elf  /tmp/build6773645236763377018.tmp/Serial2.cpp.o    /tmp/build6773645236763377018.tmp/core.a  -L/tmp/build6773645236763377018.tmp  -lm  -T  /opt/mpide-0023-linux32-20111221/hardware/pic32/cores/pic32/chipKIT-UNO32-application-32MX320F128L.ld
/opt/mpide-0023-linux32-20111221/hardware/pic32/compiler/pic32-tools/bin/pic32-objcopy  -O  ihex  -j  .eeprom  --set-section-flags=.eeprom=alloc,load  --no-change-warnings  --change-section-lma  .eeprom=0  /tmp/build6773645236763377018.tmp/Serial2.cpp.elf  /tmp/build6773645236763377018.tmp/Serial2.cpp.eep
/opt/mpide-0023-linux32-20111221/hardware/pic32/compiler/pic32-tools/bin/pic32-bin2hex  -a  /tmp/build6773645236763377018.tmp/Serial2.cpp.elf
Binary sketch size: 10748 bytes (of a 126976 byte maximum)
  • temporary directory is /tmp/build6773645236763377018.tmp/
  • Hex file will be named /tmp/build6773645236763377018.tmp/Serial2.cpp.hex
WARNING: the temporary directory will be removed when mpide will be closed. You have to backup the hex file before closing mpide. To go back to initial configuration (files removed after each build), just set the preproc.save_build_files attribute to false
preproc.save_build_files=false

Using AVRDUDE from Linux console

copy avrdude.conf file from mpide to ~/.avrduderc, example:
cp /opt/mpide-0023-linux32-20111221/hardware/tools/avrdude.conf ~/.avrdude.rc
Use ever avrdude from mpide or from your own Linux distro… Here are details about avrdude from Ubuntu Precise Penguin:
dpkg-query -l avrdude
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                             Version                          Description
+++-================================-================================-================================================================================
ii  avrdude                          5.11.1-1                         software for programming Atmel AVR microcontrollers
Check if the avrdude configuration file from mpide is used, and which PIC32 devices are available:
fred@kat:~/MPLABXProjects/Blink$ avrdude -p ?

Valid parts are:
  pic32-440 = 32MX440F512H    [/home/fred/.avrduderc:16438]
  pic32-460 = 32MX460F512L    [/home/fred/.avrduderc:16251]
  pic32-360 = 32MX320F128H    [/home/fred/.avrduderc:16054]
  pic32-360 = 32MX320F064H    [/home/fred/.avrduderc:15863]
  pic32-360 = 32MX360F512L    [/home/fred/.avrduderc:15672]
  pic32 = 32MX795F512L    [/home/fred/.avrduderc:15483]

...
We can see that pic32-360 has to be used for Uno32 (32MX320F128H) Example, with:
  • avrdude from Ubuntu distro (Precise Penguin – avrdude 5.11.1)
  • Uno32 located on /dev/ttyUSB0
  • hex file build by using mpide
fred@kat:~/MPLABXProjects$ avrdude -P /dev/ttyUSB0 -p pic32-360  -b 115200 -c stk500v2 -v -v -U flash:w:Haribo.cpp.hex

avrdude: Version 5.11.1, compiled on Oct 30 2011 at 10:37:28
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"
         User configuration file is "/home/fred/.avrduderc"

         Using Port                    : /dev/ttyUSB0
         Using Programmer              : stk500v2
         Overriding Baud Rate          : 115200
avrdude: ser_recv(): programmer is not responding
avrdude: stk500v2_ReceiveMessage(): timeout
avrdude: ser_recv(): programmer is not responding
avrdude: stk500v2_ReceiveMessage(): timeout
avrdude: ser_recv(): programmer is not responding
avrdude: stk500v2_ReceiveMessage(): timeout
Oops… Upload error ? I have to press the Uno32 reset button, then code is uploaded:
         AVR Part                      : 32MX320F128H
         Chip Erase delay              : 9000 us
         PAGEL                         : PD7
         BS2                           : PA0
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65    10     8    0 no       4096    8      0  9000  9000 0x00 0x00
           flash         65    10   256    0 yes    131072  256    512  4500  4500 0x00 0x00
           lfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           lock           0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

         Programmer Type : STK500V2
         Description     : Atmel STK500 Version 2.x firmware
         Programmer Model: AVRISP
         Hardware Version: 15
         Firmware Version Master : 2.10
         Vtarget         : 0.0 V
         SCK period      : 0.1 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x504943
avrdude: safemode: lfuse reads as 0
avrdude: safemode: hfuse reads as 0
avrdude: safemode: efuse reads as 0
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: current erase-rewrite cycle count is -1145324613 (if being tracked)
avrdude: erasing chip
avrdude: reading input file "Haribo.cpp.hex"
avrdude: input file Haribo.cpp.hex auto detected as Intel Hex
avrdude: writing flash (32664 bytes):

Writing | ################################################## | 100% 9.15s

avrdude: 32664 bytes of flash written
avrdude: verifying flash memory against Haribo.cpp.hex:
avrdude: load data flash data from input file Haribo.cpp.hex:
avrdude: input file Haribo.cpp.hex auto detected as Intel Hex
avrdude: input file Haribo.cpp.hex contains 32664 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 3.29s

avrdude: verifying ...
avrdude: 32664 bytes of flash verified

avrdude: safemode: lfuse reads as 0
avrdude: safemode: hfuse reads as 0
avrdude: safemode: efuse reads as 0
avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Using AVRDUDE from within MPLAB X

To use avrdude to upload a hex file you just built in MPLAB X, there are a couple steps you need to follow:
  1. (optional) From the MPIDE install folder, copy (as an example) C:\Program Files\mpide-0023-windows-20120122-test\hardware\tools\avr\bin\avrdude.exe to someplace you want to put avrdude and its configuration file, like C:\Program Files\Microchip\MPLABX\mplab_ide\bin\avrdude.exe
  2. (optional) From the MPIDE install folder, copy (as an example) C:\Program Files\mpide-0023-windows-20120122-test\hardware\tools\avr\etc\avrdude.conf to someplace you want to put avrdude and its configuration file, like C:\Program Files\Microchip\MPLABX\mplab_ide\bin\avrdude.conf
  3. From within MPLABX, open the properties for your current project. Select the Building/Loading screen, then enter the following for “Execute this line after build” (and check the box as well):
    • “C:\Program Files\Microchip\MPLABX\mplab_ide\bin\avrdude.exe” -C”C:\Program Files\Microchip\MPLABX\mplab_ide\bin\avrdude.conf” -v -p32MX795F512L -cstk500v2 -P COM40 -b 115200 -U flash:w:${ImagePath}
    • (Note that you will need to substitute the proper COM port number of your board, as well as the actual location of avrdude.exe and avrdude.conf into this command line. Also, if you’re not using an MX795 you’ll have to update that as well.)
  4. Make sure you have copied the proper linker script into your project. For example, if you’re using a chipKIT MAX32 board, just copy the chipKIT-MAX32-application-32MX795F512L.ld file from C:\Program Files\mpide-0023-windows-20120122-test\hardware\pic32\cores\pic32 into your MPLABX project folder, and then add that file to the Linker Files folder in the Project Properties.
If all of the paths are set up properly, you should get an automatic download every time you rebuild your project in MPLABX. Many times, I need to reset my chipKIT board in order to get the download to work, even though I don’t need to do this when using MPIDE. I’m not sure why this is – if anyone can suggest a workaround, that would really help.
VN:F [1.9.22_1171]
Rating: 10.0/10 (1 vote cast)
VN:F [1.9.22_1171]
Rating: +1 (from 1 vote)

Header Files

Header files listed in alphabetical order. Files are denoted as either (AVR) or (PIC32) specific, if they apply to both platforms they are marked as (AVR|PIC32). For headers that are marked (AVR|PIC32) they can be either “generic” or “ported”. Generic files will work on either platform without modification where ported files are different for AVR or PIC32.

avr/interrupt.h (AVR)

avr/io.h (AVR)

avr/progmem.h (AVR)

avr/pgmspace.h (AVR)

Tools to access program space of the AVR processor, not needed on PIC32, but some macros can be used in its place to make AVR code run on a PIC32. <source>
  1. if defined(__PIC32MX__)
   // neither PROGMEM or PSTR are needed for PIC32, just define them as null
   #define PROGMEM
   #define PSTR(s) (s)
 
   #define pgm_read_byte(x)	        (*((char *)x))
   #define pgm_read_byte_near(x)	(*((char *)x))
   #define pgm_read_byte_far(x)	(*((char *)x))
   #define pgm_read_word(x)    	(*((short *)x))
   #define pgm_read_word_near(x)	(*((short *)x))
   #define pgm_read_workd_far(x)	(*((short *)x))

   #define	prog_void	 const void
   #define	prog_char	 const char
   #define	prog_uchar	 const unsigned char
   #define	prog_int8_t	 const int8_t
   #define	prog_uint8_t	const uint8_t
   #define	prog_int16_t	const int16_t
   #define	prog_uint16_t	const uint16_t
   #define	prog_int32_t	const int32_t
   #define	prog_uint32_t	const uint32_t
   #define	prog_int64_t	const int64_t
   #define	prog_uint64_t	const uint64_t
  1. else
  2. include <avr/pgmspace.h>
  3. endif
</source>

coffee.h

The start of all good programming sessions.

cpudefs.h

Ethernet/Ethernet.h

Ethernet/Client.h

i2cmaster.h

plib.h (PIC32)

Contains type definitions for PIC32 registers.

SdFat.h

Servo.h (?)

SPI.h (AVR|PIC32) ported

Library to provide SPI communications.

stdint.h (AVR|PIC32) ported

stdio.h

wire.h (?)

wiring.h (?)

WProgram.h (?)

 
VN:F [1.9.22_1171]
Rating: 7.0/10 (3 votes cast)
VN:F [1.9.22_1171]
Rating: -1 (from 1 vote)

About PIC32 Interrupt Vectors

This is a dump of the interrupt vector from the Max32 (32MX795F512L) It gives you an idea what is available and what is already in use.
Arduino-32MX795F512L>V show interrupt Vectors
FLASH_PROG_BASE=9D000000
EBASE          =9D000000
IntCtl         =00000020
VectorSpacing  =00000001
+++ 0= 02 00---0B4017F0 jump 9D005FC0  _CORE_TIMER_VECTOR
+++ 1= 00 00---FFFFFFFF unused         _CORE_SOFTWARE_0_VECTOR
+++ 2= 00 00---FFFFFFFF unused         _CORE_SOFTWARE_1_VECTOR
+++ 3= 00 00---FFFFFFFF unused         _EXTERNAL_0_VECTOR
+++ 4= 00 00---0B401E5A jump 9D007968  _TIMER_1_VECTOR
+++ 5= 00 00---FFFFFFFF unused         _INPUT_CAPTURE_1_VECTOR
+++ 6= 00 00---FFFFFFFF unused         _OUTPUT_COMPARE_1_VECTOR
+++ 7= 00 00---FFFFFFFF unused         _EXTERNAL_1_VECTOR
+++ 8= 00 00---FFFFFFFF unused         _TIMER_2_VECTOR
+++ 9= 00 00---FFFFFFFF unused         _INPUT_CAPTURE_2_VECTOR
+++10= 00 00---FFFFFFFF unused         _OUTPUT_COMPARE_2_VECTOR
+++11= 00 00---FFFFFFFF unused         _EXTERNAL_2_VECTOR
+++12= 00 00---FFFFFFFF unused         _TIMER_3_VECTOR
+++13= 00 00---FFFFFFFF unused         _INPUT_CAPTURE_3_VECTOR
+++14= 00 00---FFFFFFFF unused         _OUTPUT_COMPARE_3_VECTOR
+++15= 00 00---FFFFFFFF unused         _EXTERNAL_3_VECTOR
+++16= 00 00---FFFFFFFF unused         _TIMER_4_VECTOR
+++17= 00 00---FFFFFFFF unused         _INPUT_CAPTURE_4_VECTOR
+++18= 00 00---FFFFFFFF unused         _OUTPUT_COMPARE_4_VECTOR
+++19= 00 00---FFFFFFFF unused         _EXTERNAL_4_VECTOR
+++20= 00 00---FFFFFFFF unused         _TIMER_5_VECTOR
+++21= 00 00---FFFFFFFF unused         _INPUT_CAPTURE_5_VECTOR
+++22= 00 00---FFFFFFFF unused         _OUTPUT_COMPARE_5_VECTOR
+++23= 00 00---FFFFFFFF unused         _SPI_1_VECTOR
+++24= 00 00---0B401B67 jump 9D006D9C  _I2C_3_VECTOR _UART_1A_VECTOR _UART_1_VECTOR _SPI_1A_VECTOR _I2C_1A_VECTOR _SPI_3_VECTOR
+++25= 01 00---FFFFFFFF unused         _I2C_1_VECTOR
+++26= 00 00---FFFFFFFF unused         _CHANGE_NOTICE_VECTOR
+++27= 01 00---FFFFFFFF unused         _ADC_VECTOR
+++28= 00 00---FFFFFFFF unused         _PMP_VECTOR
+++29= 00 00---FFFFFFFF unused         _COMPARATOR_1_VECTOR
+++30= 00 00---FFFFFFFF unused         _COMPARATOR_2_VECTOR
+++31= 00 00---0B401BDD jump 9D006F74  _UART_2A_VECTOR _I2C_2A_VECTOR _SPI_2_VECTOR _SPI_2A_VECTOR _I2C_4_VECTOR _UART_3_VECTOR
+++32= 00 00---0B401C53 jump 9D00714C  _UART_2_VECTOR _SPI_3A_VECTOR _I2C_3A_VECTOR _UART_3A_VECTOR _SPI_4_VECTOR _I2C_5_VECTOR
+++33= 00 00---FFFFFFFF unused         _I2C_2_VECTOR
+++34= 00 00---FFFFFFFF unused         _FAIL_SAFE_MONITOR_VECTOR
+++35= 01 00---FFFFFFFF unused         _RTCC_VECTOR
===36= 00 00---FFFFFFFF unused         _DMA_0_VECTOR
===37= 00 00---FFFFFFFF unused         _DMA_1_VECTOR
===38= 00 00---FFFFFFFF unused         _DMA_2_VECTOR
===39= 00 00---FFFFFFFF unused         _DMA_3_VECTOR
===40= 00 00---FFFFFFFF unused         _DMA_4_VECTOR
===41= 00 00---FFFFFFFF unused         _DMA_5_VECTOR
===42= 00 00---FFFFFFFF unused         _DMA_6_VECTOR
===43= 00 00---FFFFFFFF unused         _DMA_7_VECTOR
===44= 00 00---FFFFFFFF unused         _FCE_VECTOR
===45= 00 00---FFFFFFFF unused         _USB_1_VECTOR
===46= 00 00---FFFFFFFF unused         _CAN_1_VECTOR
===47= 00 00---FFFFFFFF unused         _CAN_2_VECTOR
===48= 00 00---FFFFFFFF unused         _ETH_VECTOR
===49= 00 00---0B401BA2 jump 9D006E88  _UART_4_VECTOR _UART_1B_VECTOR
===50= 00 00---0B401C18 jump 9D007060  _UART_6_VECTOR _UART_2B_VECTOR
===51= 00 00---0B401C8E jump 9D007238  _UART_5_VECTOR _UART_3B_VECTOR
VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Programming Hints

Use defines

At the abstraction layer, everything is the same, at least we hope so. At the low level you will have to re-write it. You should NEVER include anything from the avr libraries i.e. #include <avr/io.h> for any project that does not use an AVR chip. From now on, you should be doing this:
/* For AVR */
#if defined(__AVR__)
    #include <avr/io.h>
#endif

/* For PIC32 */
#if defined(__PIC32MX__)
    #include <p32xxxx.h>    /* this gives all the CPU/hardware definitions */
    #include <plib.h>       /* this gives the i/o definitions */
#endif
There are also a few predefined macros which may be useful as arguments to #ifdef:
/* For PIC32 */
#define __PIC32MX__ 1

/* For Uno32 */
#define __32MX320F128H__ 1

/* For Max32 */
#define __32MX795F512L__ 1

I/O: PIC32 vs AVR

The I/O port modules on the PIC32 microcontrollers and the AVR microcontrollers are implemented differently. The ports on PIC32 parts are organized as 16 bit ports. The ports on AVR parts are organized as 8 bit ports. Each I/O port on an AVR part has an associated data direction register (DDRx) used to control whether the pins in the port are inputs or outputs. For example, DDRD is the data direction register for PORTD. On an AVR microcontroller, setting a DDR register bit to ‘1’ makes the associated pin anoutput. Each I/O port on a PIC part has an associated tristate register (TRISx) used to control pin direction. For example, the equivalent register in a PIC part would be TRISD. On a PIC microcontroller, setting a TRIS bit to ‘1’ makes the corresponding pin an input. On an AVR part, writing to the PORT register writes to a latch. If the pin is configured as an output the value in the latch sets the state of the pin. If the pin is configured as an input, it controls turning on or off an internal pull-up resistor. On a PIC, writing to the PORT register actually writes to a register called LAT (latch). Writing to the PORT or the LAT register has the same effect. If the pin is configured as an output, it sets the state of the pin. If the pin is configured as an input, it doesn’t do anything but write to the latch. On an AVR part you read the pin state by reading the PIN register. On a PIC part, you read the pin state by reading the PORT register. On an AVR part if you read from the PORT register, you read the last value written. To read the last value written on a PIC part, you read the LAT register. AVR parts have a programmable internal pull-up resistor on every I/O pin. On pins that are configured as inputs, the pull-up resistor is enabled by writing a ‘1’ to the corresponding bit in the PORT register. The pull-up is disabled by writing a ‘0’ to the bit. PIC32 parts only have programmable pull-up resistors on the pins that are capable of generating a pin change interrupt (CNx pins). The pull-ups are enabled by setting the appropriate bits in the CNPUE register. PIC32 parts have the ability to make any pin an open drain output. Each port has an associated open drain control (ODC) register. A pin is made open drain by setting the corresponding ODC bit to ‘1’. Setting an ODC bit to ‘0’ makes the pin a normal digital output. PIC32 parts also have SET, CLR, and INV registers associated with the TRIS, LAT, and PORT registers for each each I/O port. The SET register is used to set one or more bits to ‘1’. The CLR register is used to clear one or more bits to ‘0’. The INV register is used to toggle the state of one or more bits. In each case a ‘1’ bit in the value written will cause the effect (SET, CLR, or INV) to occur to the corresponding bit in the register . Each bit written as ‘0’ has no effect on the bits in the register. For example, writing the value 0x0001 to TRISxSET will set bit 0 of the TRISx register to 1, leaving all other bits unchanged. Writing the value 0x0001 to the TRISxCLR register will clear bit 0 of the TRISx register to 0. Writing the value 0x0001 to the TRISxINV register will toggle the state of bit 0 in the TRISx register. When writing to a SET, CLR, or INV register, the hardware reads the associated register, modifies the value based on the ‘1’ bits in the value written, and writes the result back to the register. This is performed as an atomic (uninterruptible) operation, and is more efficient than the typical read-modify-write sequence used on most other processors. The output compares in the PIC32 aren’t associated with timers the way they are in the AVR. Each output compare can be set to trigger from timer 2 or timer 3. So, you can toggle up to 5 pins from the same timer.

Adding libraries

To add libraries, put them in a folder in your Arduino sketch folder called libraries. These are picked up as contributed libraries and will show up for both PIC and AVR environments. Libraries placed here are also preserved when you upgrade MPIDE.

Fastest way to toggle bits on a PIC32

The fastest way to toggle a pin on PIC32 is going to be something like
      while(1)
        {
          LATGINV = B01000000;
        }
For PIC32, usually the LAT registers are best for output and the PORT registers are best for input. The *INV (invert) registers are the fastest way to toggle an SFR bit. There are also *SET and *CLR registers that allow for quick bit sets and bit clears.

Interrupt handler setup

void setup() {
........
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT, 0xFFFF);
ConfigIntTimer2((T2_INT_ON | T2_INT_PRIOR_3));
........
}

#ifdef __cplusplus
extern "C" {
#endif

void __ISR(_TIMER_2_VECTOR,IPL3AUTO) comms_handler(void)
{
  mT2ClearIntFlag();  // Clear interrupt flag
digitalWrite(LED, HIGH);
}
#ifdef __cplusplus
}
#endif
Do not put INTEnableSystemMultiVectoredInt in your setup code or any place else, it is already called BEFORE your setup code gets called. Look at wiring.c for details.

Device configuration words and the bootloader

#pragma config will not work in an MPIDE sketch because, like the Arduino’s fuses, the PIC32’s configuration words are set in the bootloader code and the chipKIT application linker scripts are set to discard the configuration-word values. To change the configuration-word values, change them in the bootloader project (with the bootloader linker script). If for some reason, you need the configuration-word values in the application linker script, you can modify the linker script with regards to the config regions and .config_xxx sections. You can use the bootloader linker script as a guide.

Missing header files?

If the main sketch does not #include all needed header files then it will not compile and you will get missing file errors. This also includes any #include statements that your 3rd party libraries may have. So, check those libraries and make sure that all #include statements in them are also in your sketch.  

Using attachInterrupt() / detachInterrupt()

 /************************************************************************
  ** ExtIntTest - Sketch to Test External Interrupts
  *************************************************************************
  ** This sketch tests the attachInterrupt and detachInterrupt functions.
  ** It assumes that pin 3 has been looped back to the external interrupt
  ** pin to be tested. It generates edges on pin 3 to trigger interrupts.
  ** The interrupt service routine then blinks the LED to show that it
  ** has been entered.
  **
  ** Uno32 External Interrupt pins:
  ** INT0 = 38, INT1 = 2, INT2 = 7, INT3 = 8, INT4 = 35
  **
  ** Max32 External Interrupt pins:
  ** INT0 = 3, INT1 = 2, INT2 = 7, INT3 = 21, INT4 = 20
  **
  *************************************************************************
  ** History:
  **
  ** 08/08/2011(GeneA): created
  **
  ************************************************************************/

 /* ------------------------------------------------------------ */
 /* Local Symbol Definitions */
 /* ------------------------------------------------------------ */

 #if defined(_BOARD_UNO_)
 #define pinINT0 38
 #define pinINT1 2
 #define pinINT2 7
 #define pinINT3 8
 #define pinINT4 35
 #define pinSrc 3 // used as external interrupt source

 #elif defined(_BOARD_MEGA_)
 #define pinINT0 3
 #define pinINT1 2
 #define pinINT2 7
 #define pinINT3 21
 #define pinINT4 20
 #define pinSrc 4 // external interrupt source

 #else
 #error "No supported board specified.
 #endif

 #define INT0 0
 #define INT1 1
 #define INT2 2
 #define INT3 3
 #define INT4 4

 /* Since the Max32 doesn't have the second LED on pin 43,
 ** this test assumes that an external LED has been connected
 ** to pin 43 on that board.
 */

 #define pinLED1 13
 #define pinLED2 43

 #define intTest INT1

 /* ------------------------------------------------------------ */
 /* Local Variables */
 /* ------------------------------------------------------------ */

 volatile int intStat;

 /* ------------------------------------------------------------ */
 /* Forward Declarations */
 /* ------------------------------------------------------------ */

 void IsrTest();

 /* ------------------------------------------------------------ */
 /* Procedure Definitions */
 /* ------------------------------------------------------------ */
 /** setup
  **
  ** Parameters:
  ** none
  **
  ** Return Value:
  ** none
  **
  ** Errors:
  ** none
  **
  ** Description:
  ** Initialization function called at the beginning of execution.
  */

 void setup() {

 /* Use the LEDs to indicate activity
  */

 pinMode(pinLED1, OUTPUT); // indicates foreground task activity
 pinMode(pinLED2, OUTPUT); // indicates ISR activity
 digitalWrite(pinLED1, LOW);
 digitalWrite(pinLED2, LOW);

 /* Make all of the external interrupt pins be inputs.
  */

 pinMode(pinINT0, INPUT);
 pinMode(pinINT1, INPUT);
 pinMode(pinINT2, INPUT);
 pinMode(pinINT3, INPUT);
 pinMode(pinINT4, INPUT);

 /* Make the stimulus pin be an output.
  */

 pinMode(pinSrc, OUTPUT);
 digitalWrite(pinSrc, LOW);
 }

 /* ------------------------------------------------------------ */
 /** loop
  **
  ** Parameters:
  ** none
  **
  ** Return Value:
  ** none
  **
  ** Errors:
  ** none
  **
  ** Description:
  ** Application event loop
  */

 void loop() {
 int itrTest;

 /* Install the handler for the interrupt being tested.
 ** Test rising edge triggering first.
 */

 intStat = 0;
 attachInterrupt(intTest, IsrTest, RISING);

 /* For rising edge triggered interrupts, both LEDs
 ** should go on and off at the same time.
 */

 for (itrTest = 0; itrTest < 10; itrTest++) {
     digitalWrite(pinLED1, HIGH);
     digitalWrite(pinSrc, HIGH);
     delay(500);
     digitalWrite(pinSrc, LOW);
     digitalWrite(pinLED1, LOW);
     delay(500);
 }

 /* Uninstall the handler and test to make sure that
 ** we aren't getting interrupts any more.
 */

 detachInterrupt(intTest);
 digitalWrite(pinLED2, LOW);

 /* Only one LED should be blinking here.
 */

 for (itrTest = 0; itrTest < 5; itrTest++) {
     digitalWrite(pinLED1, HIGH);
     digitalWrite(pinSrc, HIGH);
     delay(500);
     digitalWrite(pinSrc, LOW);
     digitalWrite(pinLED1, LOW);
     delay(500);
 }

 intStat = 0;
 digitalWrite(pinLED1, HIGH);
 digitalWrite(pinSrc, HIGH);
 delay(1000);

 attachInterrupt(intTest, IsrTest, FALLING);

 /* For falling edge triggered interrupts, the LEDs
 ** should blink out of phase with each other.
 */

 for (itrTest = 0; itrTest < 10; itrTest++) {
     digitalWrite(pinLED1, LOW);
     digitalWrite(pinSrc, LOW);
     delay(500);
     digitalWrite(pinSrc, HIGH);
     digitalWrite(pinLED1, HIGH);
     delay(500);
 }

 /* Uninstall the handler and test to make sure that
 ** we aren't getting interrupts any more.
 */

 detachInterrupt(intTest);
 digitalWrite(pinLED2, LOW);

 /* Only one LED should be blinking here.
 */

 for (itrTest = 0; itrTest < 5; itrTest++) {
     digitalWrite(pinLED1, LOW);
     digitalWrite(pinSrc, LOW);
     delay(500);
     digitalWrite(pinSrc, HIGH);
     digitalWrite(pinLED1, HIGH);
     delay(500);
 }

 /* Show that this iteration of the test has completed
 */

 for (itrTest = 0; itrTest < 5; itrTest++) {
     digitalWrite(pinLED1, HIGH);
     delay(100);
     digitalWrite(pinLED1, LOW);
     delay(100);
 }

 delay(1000);

 }

 /* ------------------------------------------------------------ */
 /** IsrTest
  **
  ** Parameters:
  ** none
  **
  ** Return Value:
  ** none
  **
  ** Errors:
  ** none
  **
  ** Description:
  ** Interrupt handler routine.
  ** This toggles the state of LED2 each time it is entered.
  */

 void IsrTest() {

 intStat = 1 - intStat;
 if (intStat != 0) {
     digitalWrite(pinLED2, HIGH);
 }
 else {
     digitalWrite(pinLED2, LOW);
 }

 }

 /* ------------------------------------------------------------ */

 /***********************************************************************/

Notes on porting issues from one Novice’s perspective

There are several challenges in porting stuff from Arduino to Max32

Overloaded pins

Almost every pin on the MAX32 does many different things. This is, in part, due to the fact that the attached pins on the PIC32 do many different things. However, it can make it hard to find functional pins not in use to do what you want to do. Especially if your application wants to manipulate several bits at once (e.g. an 8 bit data path to an LCD display).

Crazy pin layouts

Not sure why they chose to do things this way, but many of the pin functions on the Max 32 are in very different places from the Mega 2560. Both platforms, for example, suffer from the fact that there is only one place where you can get 8 pins mapped to consecutive bits on a single register (at least if you want them all on the same connector). On the Mega 2560, these are 22-29 where 22 is bit0 and 29 is bit 7 of register D. On the Max32, pin 30 is bit 7 and pin 37 is bit 0 of register E. The I2C bus is still on 20-21. SPI is still on 50-53, but 53 can’t be used for SS if you are using the built-in Ethernet MAC. The Ethernet MAC<->Phy connection ties up a lot of pins. It would have been nice if they’d put the Phy on-board and moved the conflicting pins on to the pins 70-84 connector and made some of the Arduino-intersecting pins available for more Arduino-like purposes. Unfortunately, instead, they blocked off pins 7, 40-43, 45-49, and 53 and those pins are tied up for a stack-on Phy daughter card. Thankfully, they used the RMII and not the MII. It takes some digging in the documentation to find all the pin reservations and whatnot for the various peripherals. The most useful guide to resolving pin conflicts is the Max32 Reference Manual from the diligent web site, but, one must constantly go back and forth between the text describing each advanced peripheral and the pin-map at the end of the document in order to decipher all the conflicts. It would be nice if someone could produce a table which listed each of the built-in peripherals on one axis and each of the pins on the other axis with the squares filled in for each pin used by each peripheral. Another table that would be handy would be one with each peripheral on both axis and squares filled in showing which peripherals were mutually exclusive. For example, if you’re using CAN1, UART3 conflicts. If you’re using CAN2, I2C conflicts, etc.

Ethernet uses MANY pins all over the place (bad) and a software stack (maybe good)

The built-in Ethernet MAC uses a lot of pins to talk to the RMII Phy. Someone made a post on the forum asking why no MII. While it’s true that MII would allow faster communications with less processor overhead, the simple reality is that for a board with 83 pins, this board already has surprisingly few functionally available pins. Especially if you’re building something that you want to be a shield that can be used with both Mega2560 and Max32. (In fact, see crazy pin layouts for one reason this is near impossible). The Ethernet RMII interface is the worst offender in the pin conflict world. It ties up pins 7, 40-43, 45-49, and 53 for exclusive use. Of course there are different pins tied up by the W5100 interface which has a built-in phy and a built-in TCP/IP stack. The built-in phy means not as many pins tied up on the Mega 2560 using the W5100. Even with a software-stack based MAC chip with an external Phy, on the Mega 2560 platform this is usually implemented with the MAC and Phy on the same shield so that the MAC<->Phy interface doesn’t use any GPIO pins from the ATMega chip. However, this isn’t (entirely) Digilent/ChipKit’s fault. In this case, the PIC32MX975 implements the MAC requiring an external Phy. This means that the point where the MAC<->Phy pins are tied up is inside the MCU itself. The implementation of a simple MAC that isn’t overloaded with a built-in TCP-IP stack does make programs larger (add about 45k for the IPv4 stack, for example), but it allows you to implement whatever protocol you want on top of the MAC. (For example, I am hoping to take the IPv6 library contributed by someone else and combine it with the existing chipKITEthernet library to produce a dual-stack Max32. Well, actually, I’m hoping someone else will do that soon, but, if they don’t, I’ll end up tackling the project). OTOH, the W5100 chip commonly used on the Arduino platform requires only 1 dedicated SS pin and uses the SPI bus for everything else. It’s slower and can only do IPv4 (unless you use RAW packet IO mode to emit hand-crafted ethernet frames). Since IPv4 will likely be 99% obsolete in less than 5 years, that seems like a serious limitation to the platform. In my discussions with Wizard, they’ve pretty much blown off the idea of supporting IPv6 on their chips so far, so, I’m very glad the PIC32 has a raw MAC built-in and that Max32 went with this implementation.

Differing approaches to low-level IO

The AVR and the PIC have radically different low-level IO mechanisms. Unfortunately, the standard libraries for both the Arduino and the Max32 platforms give you two choices for dealing with this. This is more a natural result of the way MPIDE evolved than something the developers set out to do wrong, but, the end result is still a relatively high level of dysfunctionality. The Arduino developers didn’t conceive of the need to support anything but ATMEGA and wrote accordingly. The MPIDE developers had a lot of work to do just to add multiple-platform support to existing abstraction libraries, so, abstracting more direct chip-level interactions to provide new functionality that would require library surgery for both platforms was probably perceived as an unnecessary additional adventure at the time. However, to make for clean integrated portable development in the future, this kind of needs to happen. A highly abstracted (slow) compatible library (digitalRead/Write, analogRead/Write) for dealing with a single PIN at a time and essentially raw access to the hardware registers. What is needed is a standard abstraction layer at the lowest level possible that provides a standard mechanism for doing multiple-pin simultaneous register manipulation using the most efficient hardware method available while remaining just abstract enough to hide the hardware differences. It definitely doesn’t help that the PIC32 has 16 bit hardware registers while the ATMega uses 8-bit registers, but, this could still be handled: For example, having a library that provided an API such as assign()/set()/clear()/invert() each of which took the form set(RegisterC, 0xffc3) where RegisterC was a handle for hardware register C (PIC32) or C & D (ATMega) and 0xffc3 were the bits to set. On the PIC32, this would be implemented as LATC = 0xffc3. On the ATMega it would be implemented as PORTC |= (0xffc3 >> 8); PORTD |= (0xffc3 & 0x00ff);. assign() would simply assign the argument value to the appropriate register(s). set(), clear(), and invert() would function along the lines of the SET/CLR/INV register aliases on the PIC32. It might be better to implement 8-bit based arguments, however. This will make the code more ATMega native and slower on the PIC, but, wouldn’t require ATMega programmers to understand that PORTC means PORTC at 0xff00 and PORTD at 0x00ff in the arguments. I’m really not sure what the ideal solution is, which at the end of the day may be a hard question to answer and could be part of the reason this hasn’t happened yet.

Weird AVR-specific mechanisms like PROGMEM peppered throughout libraries

The best thing about the Arduino platform (arguably it’s only saving grace) is that there are a ton of libraries out there for supporting an incredibly wide variety of hardware. This is no small feat and is the only explanation I can find for Arduino’s extreme popularity, especially as a micro controller entry-level platform. (Though the IDE is helpful for beginners too, but, with MPIDE, that’s no longer an Arduino-exclusive advantage). Unfortunately, no effort whatsoever has been made in the Arduino world to make the libraries at all portable to other platforms and many libraries make use of non-abstracted low-level hardware functions that are AVR specific. In particular, it seems to be very popular in the Arduino to load static arrays into PROGMEM and then use special functions for accessing their contents. (see any graphical device library with built-in font(s) for an example). Beyond that, there’s the low-level IO stuff mentioned in the previous subtopic. Both of these things mean that I’ve had to perform surgery on almost every library I’ve used.

MPIDE is both ahead of and behind Arduino IDE

MPIDE development drove many of the changes/enhancements incorporated into Arduino IDE 1.00. Unfortunately, MPIDE did not keep up with the changes to Arduino IDE in 1.00 that it was not driving, so, MPIDE is trapped in this weird limbo between 0023 and 1.00 for now. (March 6, 2012) Work is ongoing to resolve this and I plan to make an effort to contribute to that work, but, this means even more libraries need surgery to back port them to the Max32 environment for the time being. One place where this is hideously obvious is in the Ethernet library(ies).

Both platforms suck at networking

On the Arduino, for example, the W5100 chip exports link state only through a single hardware pin. There is no way to read the value of this pin short of performing hardware surgery on the Arduino board. The pin goes from the W5100 chip through an LED (and possibly a resistor) to ground and that’s all she wrote. You’d have to intercept the trace before the first external component and tie that to a digital pin to be able to detect link state in your program. The W5100 chip, among its other faults does not export this value in a software-readable register. The situation is slightly better in the Max32. Because the RMII is external to the PIC32 MAC, the PIC32 has to make this information (from the Phy) available in a register so that software can correctly control the MAC<->Phy interface. The chipKITEthernet library has a function for reading this called MACIsLinked(). For some bizarre reason, this function is in the lower-level utility/… part of the library and is not exported as part of the public API. There seems to be an assumption that if you’re using the Ethernet Library, you don’t necessarily have any concerns about how long it takes for calls to return (sort of). The Chipkit library is actually written with these performance capabilities in mind at the lower level, but, for unknown reasons, they are only partially exported to the application. For example, you can set the timeout (in seconds) for a client.connect() call, but you can’t set the timeout for begin() with DHCP. The lower level library allows this to be done, but, does not export the capability. Worse, the Chipkit Ethernet.begin() returns void while the Arduino 1.00 returns int. With the Arduino, if int=0, something bad happened. With Chipkit, determining whether or not something bad happened is a bit of an adventure. Both have standard DHCP timeouts taken directly from desktop computer implementations (around 30 seconds). This is fine if networking is critical to your application and you don’t mind pending until the network works, but, if the network is a “nice to have” feature your application will take advantage of when it is working (as in my particular application), you want DHCP to be almost non-blocking (a 5 second timeout is tolerable in my application, but a 30-second hang is untenable). Ideally this should be controllable from the application. In the case of the Chipkit library, the needed functionality is almost there, but, tDHCPTimeout in the low level library is defined as a local (?!!) constant (?!) within the begin() function. Making this a global variable and exporting it to the API is all that is needed to provide the necessary functionality.

Miscellaneous additional rants

For an “Arduino Compatible” board, getting my hardware working on the Max32 has been an unexpected adventure. Don’t get me wrong, I LOVE that my frame rate has gone from 250ms+/frame to ~25ms/frame and that my buttons have become reasonably responsive. I LOVE that the TCP/IP is a library and not a chip-based mechanism (I want to dual-stack my Max32 eventually). I think I found an IPv6 library for it and there is already the IPv4 library that ships with the MPIDE, so, hopefully I can marry them. (additional note: Don’t use the Ethernet library that Chipkit ships with the MPIDE. Download the package that includes the chipKITEthernet library. It’s a much better starting point). Another example, however, is that the MPIDE Ethernet Library has functionality in 0023 beyond the Arduino 0023 Ethernet Library, but, falls short of the Ethernet Library in Arduino 1.0.

Concluding thought

I hope these notes on my experience are helpful to other developers. I also hope that the IDE developers at both Chipkit and Arduino will work on implementing these suggestions and merging the two IDEs into a single standardized environment for multiple-platform development.
VN:F [1.9.22_1171]
Rating: 10.0/10 (2 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 2 votes)

Tips on porting Arduino libraries

Why do I need to port in the first place?

Arduino is many things but goal that is attempted with the project and not always accomplished is to abstract out the specific details of the Atmel microcontroller used on these boards. Abstracting out the microcontroller has a specific consequence of allowing a beginner to not have to dig in to the details of how the chip works to get a project up and running. It also has an unintended consequence of making the specific microcontroller that is used to implement a solution less relevant. Although the API (Application programmers Interface) for the Arduino is quite abstracted, some specific details within a library may not utilize these abstractions and talk directly to the chip. This can happen for several reasons. One, maybe there is no abstraction for the library writer to leverage. Two, it is difficult to use libraries within libraries in the Arduino environment (though work is on its way to resolve this). Three, there are some specific limitations of the Atmel chips that prevent “standard” C calls from being used, when this happens specific workarounds have been used. Four, C datatypes are not always consistent between compilers.

Porting Tips

When starting with a working Arduino library that you want to port to chipKIT you should have in mind the goal to keep it working on Arduino while adding chipKIT functionality. One way to do this is to make use of compiler preprocessor directives such as #ifdef to conditionally compiler for the currently selected target platform. There is detailed information on how to do this the Programming hints page.

Atmel Specific Code

When Atmel registers are used, as mentioned above, try to convert to abstractions. If this is not possible use #ifdef’s to switch between Arduino and chipKIT. For example:
#if defined(__AVR__)
    /* Atmel-specific code goes here */
#endif

#if defined(__PIC32MX__)
    /* chipKIT-specific code goes here */
#endif

Compiler Workarounds

The Atmel compiler uses some tricks for storing literal strings in program space that need to be converted to a standard C to work on chipKIT. Again, use of #ifdefs is a good way to do this. For example:
#if defined(__AVR__)
    /* Atmel-specific code goes here */
#else
    /* standard C code goes here */
#endif

C/C++ datatypes

The C/C++ programming language can be quite portable with a major exception. The size of a datatype in one compiler may not be the same a s datatype in another compiler. This is especially true when one complier is for an 8-bit architecture such as is used with Arduino Atmel chips and the other is a 32-bit architecture as is used in chipKIT PIC32 chips. For example, an int on a Atmel complier may be 16-bits and an int on a PIC32 may be 32-bits, so when an Atmel library is used on a PIC32 that relies on a int overflowing at a specific value the library will fail because of the larger storage type on the PIC32. One way to solve this problem is to use the stdint.h include that has types that call out a specific storage capacity such as the following: uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t. Example needed.
VN:F [1.9.22_1171]
Rating: 8.3/10 (3 votes cast)
VN:F [1.9.22_1171]
Rating: +1 (from 1 vote)