Skip to content

Adding a new variant

Thibaut VIARD edited this page Sep 1, 2015 · 5 revisions

ℹ️ Variant information

Each variant information must be provided in ExperimentalCore-sam/variants/[variant_name].

Alternatively, a variant can be provided as a separate module based on this one, like ASME (based on samd Arduino core): https://github.com/ameltech/sme-arduino-core.

Current included (or planned to be) variants are:

📂 Variant folder content

📁 debug_scripts/

A file named variant.gdb must be present here. This file is a GDB script to be used when starting a debug session.

⚠️ The sketch/application binary file must have been written to device's flash previously. See wiki page about Debugging using OpenOCD and GDB.

# provide information about available hardware breakpoints
set remote hardware-breakpoint-limit 6
set remote hardware-watchpoint-limit 4

# connect to target, OpenOCD listens on port 3333 by default. Change this value in case of customization.
target remote localhost:3333

# reset and halt core
monitor reset halt

# print ARM Cortex-M registers values
info reg

# create 2 one-stop breakpoints at Reset_Handler() and at main()
thbreak Reset_Handler
thbreak main

# print breakpoints information
info breakpoints

# switch to asynchronous mode
monitor poll

📁 linker_scripts/

2 files named variant_with_bootloader.ld and variant_without_bootloader.ld must be present here. While the first defines the memory mapping in case of using a bootloader, the second is allowing to use the whole of available memories.

The main difference should be at begin of files in the MEMORY section:

extract of variant_with_bootloader.ld
MEMORY
{
  FLASH (rx) : ORIGIN = 0x00400000+0x2000, LENGTH = 0x00200000-0x2000
  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00028000
}
extract of variant_without_bootloader.ld
MEMORY
{
  FLASH (rx) : ORIGIN = 0x00400000, LENGTH = 0x00200000
  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00028000
}

📁 openocd_scripts/

2 files named variant_upload.cfg and variant_debug_start.cfg must be present here. While the first is used to program the board (sketch binary upload), the second is used when initiating a debug session using OpenOCD.

Please refer to OpenOCD documentation and deliveries.

These files should have similar content like the one for SAM4S-Xplained Pro:

source [find interface/cmsis-dap.cfg]
transport select swd

# chip name
set CHIPNAME at91sam4sd32c
set ENDIAN little

# choose a port here
set telnet_port 0

source [find target/at91sam4sXX.cfg]

📜 pins_arduino.h

This file contains only these lines for historical reasons and compatibility with existing sketches.

// API compatibility
#include "variant.h"

📜 variant.h or variant.hpp

This file contains various information about the variant to be used by sketches and libraries. This information can be useful compatibility macro commands like

#define digitalPinHasPWM(P) ( g_aPinMap[P].ulPWMChannel != NOT_ON_PWM || g_aPinMap[P].ulTCChannel != NOT_ON_TIMER )

or some specific pin index definitions like

// LEDs
#define PIN_LED_13           (2u)
#define PIN_LED              PIN_LED_13
#define LED_BUILTIN          PIN_LED_13

📜 variant.cpp

This file mainly instantiates the pin description table. See https://github.com/aethaniel/ExperimentalCore-sam/blob/master/cores/arduino/port_sam/core_variant.h for the structure definition:

typedef struct _PinDescription
{
  PortNumber      iPort ;
  uint32_t        ulPin ;
  EGPIOType       ulPinType ;
  EAnalogChannel  ulADCChannelNumber ; /* ADC Channel number in the SAM device */
  EPWMChannel     ulPWMChannel ;
  ETimerChannel   ulTimerChannel ;
} PinDescription ;

/* Pins table to be instantiated into variant.cpp */
extern const PinDescription g_aPinMap[] ;

The resulting list should be somehow like that (including welcomed comments 😉):

/*
 * Pins descriptions
 */
const PinDescription g_aPinMap[]=
{
/*
 * +------------+------------------+--------+-----------------+--------------------------------------------------------------------------------------------------------
 * + Pin number +  Board pin       |  PIN   | Label/Name      | Comments (* is for default peripheral in use)
 * +------------+------------------+--------+-----------------+--------------------------------------------------------------------------------------------------------
 * |            | Serial           |        |                 |
 * +------------+------------------+--------+-----------------+--------------------------------------------------------------------------------------------------------
 * | 0          |                  |  PB2   | ICE USB Serial  | URXD1
 * | 1          |                  |  PB3   | ICE USB Serial  | UTXD1
 * +------------+------------------+--------+-----------------+--------------------------------------------------------------------------------------------------------
*/
  { PORTB, PIO_PB2A_URXD1, GPIO_PERIPH_A, No_Analog_Channel, NOT_ON_PWM,  NOT_ON_TIMER }, // URXD1
  { PORTB, PIO_PB3A_UTXD1, GPIO_PERIPH_A, No_Analog_Channel, NOT_ON_PWM,  NOT_ON_TIMER }, // UTXD1
...
};

where the first field (iPort) is an index from a given table (kind of abstraction of device specific peripherals):

const Port Ports[NUM_PORTS]=
{
  { .pGPIO=PIOA, .ulId=ID_PIOA },
  { .pGPIO=PIOB, .ulId=ID_PIOB },
  { .pGPIO=PIOC, .ulId=ID_PIOC }
};

Second parameter (ulPin) is the pin bit on a given GPIO port. For example, PIOC_23 constant is defined in Atmel SAM4S header files as

#define PIO_PC23            (1u << 23) /**< \brief Pin Controlled by PC23 */

ulPinType is one of the EGPIOType values defining the chosen multiplexer for the given variant board:

typedef enum _EGPIOType
{
  GPIO_NOMUX,
  GPIO_PERIPH_A,
  GPIO_PERIPH_B,
#ifdef PIOC
  GPIO_PERIPH_C, // Only on SAM4S and SAM4E
#endif /* PIOC */
#ifdef PIOD
  GPIO_PERIPH_D // Only on some SAM4S
#endif /* PIOD */
} EGPIOType ;

It is similar for last 3 parameters ulADCChannelNumber, ulPWMChannel, ulTimerChannel and the corresponding types can be found in https://github.com/aethaniel/ExperimentalCore-sam/blob/master/cores/arduino/port_sam/core_variant.h

📜 variant_init.cpp

This file must contain the function

#include "Arduino.h"
#include "core_private.h"

void initVariant( void )
{
  // Add board specific initialization code here
  // e.g. external components reset and configuration
  // e.g. configuration of specific pins like Serial ones
  // etc...
}

as it may allowing board specific initialization

📜 variant_startup.cpp

This file must contain the microcontroller vector table and the SystemInit() function. SystemInit() purpose is to put in place the internal clocks considering the meaning of the system/board (e.g. IoT node, Robot controller, Home Automation human interface, etc...).

DeviceVectors exception_table=
{
  /* Configure Initial Stack Pointer, using linker-generated symbols */
  .pvStack = (void*) (&__StackTop),

  .pfnReset_Handler      = (void*) Reset_Handler,
...
};

/**
 * \brief Setup the microcontroller system.
 *
 * Initialize the System and update the SystemFrequency variable.
 */
static void SystemInit( void )
{
  /* Set Flash Wait States for Embedded Flash Access, according to CPU frequency */
  ...
  /* Configure the external oscillators, if present */
  ...
  /* Setup the internal clock flow and PLL(s) */
  ...
}

📦 Variant package

The packaging scripts are placed in ExperimentalCore-sam/extras, no matter if it is a variant included in this github repository or designed in another place.