Skip to content

2. Implementing a custom board

Bob Frazier edited this page Dec 31, 2016 · 1 revision

Using the 'pins_arduino.h' for a custom board

One of the most flexible things the Arduino IDE allows you to do is set up a custom board via the 'variants' directory tree and 'pins_arduino.h'.
The basic use of 'pins_arduino.h' is to map out the functionality that is specific to a particular board. Not every CPU will have the same peripherals or number of ports or I/O pins. The Arduino environment has digital and analog pin assignments that are abstracted from the actual pin and port numbers. It is the job of the 'pins_arduino.h' file to map digital pins and analog pins to the actual physical ports and pins.
Additionally, not all hardware will map I/O pins to the same peripherals. In some cases, the default serial port or TWI implementation can be on a different pin/port combination than another board might have. All of these specifics are defined within 'pins_arduino.h'. Each 'variants' sub-directory has a unique 'pins_arduino.h', and you choose which one to use using the board definition in 'boards.txt'.
There is an open issue in 'issues' Issue #7 that is basically a request for board support. You can post your pin mapping here (along with a sub-directory name) to request support for your board.

Items of particular importance in pins_arduino.h

Because your board is likely to map its analog and digital pins differently from the default, 'pins_arduino.h' allows you to change the digital pin mapping via 4 different arrays. These are similar to the ones used in the ATMega Arduino code for the same purpose. A short description of the primary elements follows:

  • NUM_DIGITAL_PINS - this is a '#define' that indicates how many mapped digital pins there are, excluding analog inputs (which begin after the last mapped digital pin)
  • NUM_ANALOG_INPUTS - similarly, this describes how many mapped analog input pins there are
  • analogInputToAnalogPin - this macro converts an analog pin value such as 'A0' to a 0-15 index corresponding to the binary value needed to read an analog pin. PA0 is 0, PA1, is 1, and so forth. This must correlate to the correct pin mapping for the analog pins. NOTE: this implementation is optional if you use the default analog pin mapping of PA0-7 followed by PB0-7 (as applicable).
  • analogInputToDigitalPin - this converts an analog input index (such as '0' for A0) into the digital pin equivalent, which in the case of A0 would be the value of A0.
  • digitalPinHasPWM - returns a 'true' value if the specified digital pin ALSO supports PWM out.
  • USE_AREF - optional. Use this if you map an appropriate analog pin to 'Aref' and want to use it in all of your 'analogRead()' conversions. It will need to be assigned to the correct bit value for 'REFSEL'. The default implementation uses Vcc for the conversion by measuring the internal Vcc/2 with a gain of 1/2 whenever this is not defined.
  • DEFAULT_SPI - normally defined as 'SPIC' to indicate that SPI on port C is to be used by default. If not defined, 'SPIC' will be assumed.
  • DEFAULT_TWI - normally defined as 'TWIC' to indicate that TWI on port C is to be used by default. If not defined, TWIC will be assumed.
  • TWI_PORTx/TWI_VECTOR_Sx/TWI_VECTOR_Mx - these define the port and ISR vectors for the 2-wire interfaces. Typically an XMega processor has 2 of them, but may have more (such as on the 'A' series), or only one (as on the 'E' series). Define these correctly to implement all of the 2-wire interfaces.
  • SDA/SCL constants (and related) - these constants are assigned using their mapped digital pin numbers. Assign SDA and SCL for the default interface, and then SDA0/SCL0 for the first one, SDA1/SCL1 for the second, and so on.
  • SERIAL_n_xxx macros - these are described in their own section near the end and define the default serial port configuration. XMega processors typically have 2 serial ports. Some have more.
  • PR0, PR1 - the mapped digital pin numbers for PORTR pin 0 and 1. These pins are special and may need to be treated differently within the 'core' code.
  • LED_BUILTIN - the pin on which the default LED is connected. Typicall this is PR1.
  • port_to_mode_PGM, port_to_output_PGM, port_to_input_PGM - these arrays indicate which I/O port registers that the port index corresponds to. The port indices are defined in 'Arduino.h'. 'Mode' registers indicate whether an I/O port is an input or an output. The other 2 control the output value or allow you read the data on the corresponding pin.
  • digital_pin_to_control_PGM - an array of pin mode control register addresses (one entry per pin)
  • digital_pin_to_port_PGM - an array of indices into the 'port_to_xxx' arrays (one entry per pin)
  • digital_pin_to_bit_mask_PGM - an array of bitmasks corresponding to each pin. this is typically 'or'd or 'and'd with one of the port registers to turn a bit on/off or read its value.
  • digital_pin_to_timer_PGM - an array of timer indices (defined in Arduino.h) that correspond to each pin, indicating whether or not that pin can create PWM output, and the timer that is used to create it. If the pin cannot create PWM output, its value will be 'NOT_ON_TIMER'. Otherwise it will be a valid timer index based on the CPU type.

All of the above must be correctly implemented for your board and CPU type in order for the implementation to work.


TODO: expanded general description of how it works


### Arduino IDE capabilities

The Arduino IDE allows you to add definitions to the 'boards.txt' file for a particular CPU, 'core' and 'variant', flash protocol, serial UART speed, and so forth.

'cores' directory tree

The 'cores' directory tree contains the main implementation of the Arduino startup code, along with specific implementation for a series of CPUs. As an example, the code needed for 'robot' may be significantly different than that for an Uno, and so there are different 'core' directories for these two already. The 'XMega For Arduino' project defines the 'xmega' sub-directory, containing files specific to the xmega procesors.

When you install the 'XMega For Arduino' code, you will either use a symbolic link, or copy the 'xmega' directory into the Arduino IDE's 'cores' tree.

You will probably NOT need to make a new 'cores' directory entry unless you introduce a new family of CPUs.

variants directory tree

The 'variants' directory tree contains a large number of sub-directories, one for each supported board type, each containing a single file 'pins_arduino.h' that is unique to that particular 'variant'. When you install the 'XMega For Arduino' code, you will either use symbolic links or copy each directory individually into the Arduino IDE's 'variants' tree.

If your board has differnt pin assignments than any of the existing header files, you can create your own as necessary to implement your board. Then you would make an entry in 'boards.txt' that corresponds to this particular variant, naming your board as the board type.

boards.txt file

This file contains an entry for each board supported by the Arduino IDE. There is a patch file in the 'XMega For Arduino' project 'patch.boards.txt' that can be used to patch the existing 'boards.txt' file to include the pre-defined board types supported by this project. Adding more entries is relatively straightforward. Just make a copy of a similar section, and change names where appropriate. Make sure that the board type is unique for your board, however.

A typical section might look like this:
  ##############################################################

  myboard.name=My Board

  myboard.upload.protocol=arduino
  myboard.upload.maximum_size=65536
  myboard.upload.speed=115200
  myboard.build.mcu=atxmega64d4
  myboard.build.f_cpu=32000000L
  myboard.build.core=xmega
  myboard.build.variant=myboard

This would describe a board that uses an atxmega64d4 processor, with a bootloader that supports the 'arduino' protocol (avrdude serial) to flash it, at a baud rate of 115kbaud, and a 'variants' sub-directory name 'myboard' containing the 'pins_arduino.h' file.

If your board requires USB support, you can add entries similar to the following to define the product and vendor IDs for USB:

  myboard.build.vid=0xVVVV
  myboard.build.pid=0xPPPP

where

  '0xVVVV' is the 'VID' that was assigned for your project, and
  '0xPPPP' is the 'PID' that you have assigned to this project


Customized Serial Ports

There are '#define's you can use to customize the serial port definitions within the 'pins_arduino.h' file for your board. For some CPUs (such as the ATXMega128A1) there may be as many as 8 USARTs. You can map the pins however you like in the various mapping arrays, but making sure they are assigned correctly can take a bit of work.

The default values have 'Serial' on PORTD pins 2/3, and 'Serial2' on PORTC pins 2/3, and are defined as follows in pins_arduino.h:

 // serial port 0
 #define SERIAL_0_PORT_NAME PORTD
 #define SERIAL_0_USART_NAME USARTD0
 #define SERIAL_0_USART_DATA USARTD0_DATA
 #define SERIAL_0_RXC_ISR ISR(USARTD0_RXC_vect)
 #define SERIAL_0_DRE_ISR ISR(USARTD0_DRE_vect)
 //#define SERIAL_0_REMAP PORTD_REMAP
 #define SERIAL_0_REMAP_BIT 4
 #define SERIAL_0_RX_PIN_INDEX 2
 #define SERIAL_0_TX_PIN_INDEX 3

 // serial port 1
 #define SERIAL_1_PORT_NAME PORTC
 #define SERIAL_1_USART_NAME USARTC0
 #define SERIAL_1_USART_DATA USARTC0_DATA
 #define SERIAL_1_RXC_ISR ISR(USARTC0_RXC_vect)
 #define SERIAL_1_DRE_ISR ISR(USARTC0_DRE_vect)
 //#define SERIAL_1_REMAP PORTC_REMAP
 #define SERIAL_1_REMAP_BIT 4
 #define SERIAL_1_RX_PIN_INDEX 2
 #define SERIAL_1_TX_PIN_INDEX 3

You will notice there are 2 commented out entries for 'SERIAL_0_REMAP' and 'SERIAL_1_REMAP'. If you comment out these definitions, the remap register will put the USART on pins 6/7 of the port instead of pins 2/3. So in the case of a board where you wanted PORTD pins 6/7 for the default Serial port, your definitions would look like this:

 #define SERIAL_0_PORT_NAME PORTD
 #define SERIAL_0_USART_NAME USARTD0
 #define SERIAL_0_USART_DATA USARTD0_DATA
 #define SERIAL_0_RXC_ISR ISR(USARTD0_RXC_vect)
 #define SERIAL_0_DRE_ISR ISR(USARTD0_DRE_vect)
 #define SERIAL_0_REMAP PORTD_REMAP
 #define SERIAL_0_REMAP_BIT 4
 #define SERIAL_0_RX_PIN_INDEX 6
 #define SERIAL_0_TX_PIN_INDEX 7


Similarly, for CPUs with a lot of USART options, you should be able to arbitrarily assign ANY USART to ANY pre-defined serial port.

NOTE: you can use 'SERIAL_0_xxx' through 'SERIAL_3_xxx' which correspond to the Serial, Serial2, Serial3, and Serial4 objects, similar to the MEGA2560. These are implemented in 'HardwareSerial.cpp'. In the future, additional serial ports (up to 8) may be supported.


## Additional Information
Additional information can be found in an article on the [Arduino Playground](http://playground.arduino.cc/Main/CustomizeArduinoIDE).