Arduino Due

The Arduino Due is a microcontroller board based on the Atmel SAM3X8E ARM Cortex-M3 CPU, making it the first Arduino board based on a 32-bit ARM core microcontroller. This powerful and versatile board is designed for projects that require higher computational power or more input/output flexibility than the typical 8-bit boards like the Arduino Uno. It is perfect for advanced robotics, audio projects, and complex computations.

Key Specifications
Feature Description
MicrocontrollerAtmel SAM3X8E ARM Cortex-M3
Operating Voltage3.3V
Input Voltage (recommended)7-12V
Input Voltage (limits)6-20V
Digital I/O Pins54 (of which 12 provide PWM output)
Analog Input Pins12
Analog Output Pins2 (DAC)
Total DC Output Current on all I/O lines130 mA
Flash Memory512 KB
SRAM96 KB (two banks: 64KB and 32KB)
Clock Speed84 MHz
Unique Features
Programming the Arduino Due

The Arduino Due can be programmed using the Arduino IDE, just like other Arduino boards. It supports the standard Arduino programming language but takes advantage of its faster processor and larger memory for more complex applications. To upload your code, you can use the Programming port (connected via the ATmega16U2 chip) or the Native USB port.

Pin Layout

The Arduino Due has a total of 54 digital I/O pins, of which 12 can be used as PWM outputs. The board also features 12 analog inputs and 2 analog outputs for DAC. Additionally, it has SPI, I2C, and UART communication capabilities through designated pins.

Arduino Due

Arduino Due front/top view (ARM Cortex-M3 SAM3X8E based board)

Image: see Wikimedia Commons page above — check the file page for author & license (often 'own work' by Arduino or CC BY-SA).

Warnings

Please note that the Arduino Due operates at 3.3V. Applying more than 3.3V to the input/output pins could damage the board.

Applications
Conclusion

The Arduino Due is a powerful platform for users who need more computational power and I/O flexibility than the standard Arduino boards can provide. With its 32-bit ARM Cortex-M3 core, high-speed clock, and extensive I/O pins, it is ideal for advanced users looking to push their projects to the next level.

Arduino Due ADC (Analog-to-Digital Converter)

The Arduino Due has a built-in 12-bit ADC (Analog-to-Digital Converter) that can convert an analog voltage between 0 and 3.3V into a digital value. The 12-bit resolution means the ADC can represent the input voltage as one of 4096 discrete levels (from 0 to 4095). The ADC is useful for reading sensor data, measuring voltages, and other tasks where converting an analog signal to digital form is required.

Key Features of Arduino Due ADC

Standard ADC Method

The standard method to use the ADC on the Arduino Due is through the analogRead()function. This function reads an analog value from a specified analog pin and returns a digital value between 0 and 4095 (since it is a 12-bit ADC).

Standard ADC Code Example

 
// Standard ADC Example for Arduino Due
int analogPin = A0;  // Define the analog pin (A0)
int adcValue = 0;    // Variable to store the ADC value

void setup() {
    Serial.begin(115200);  // Initialize serial communication
}

void loop() {
    adcValue = analogRead(analogPin);  // Read the analog value from pin A0
    Serial.println(adcValue);          // Print the ADC value to the serial monitor
    delay(100);                        // Small delay before the next reading
}
        

Explanation

DMA ADC Method

For applications that require continuous high-speed ADC sampling, you can use the DMA (Direct Memory Access) feature of the SAM3X8E microcontroller on the Arduino Due. DMA allows the ADC to transfer data directly to memory without involving the CPU, enabling much faster sampling rates.

DMA ADC Code Example

 
// DMA-based ADC Example for Arduino Due
#include <DueDMA.h>  // Library to handle DMA
#include <ADC.h>     // Library to access ADC on Arduino Due

ADC adc;  // Create an ADC object
DueDMA dma;  // Create a DMA object

volatile uint16_t adcBuffer[256];  // Buffer to store ADC samples

void setup() {
    Serial.begin(115200);  // Initialize serial communication

    // Configure the ADC
    adc.setResolution(12);  // 12-bit resolution
    adc.setSamplesAverage(1);  // No averaging
    adc.start(ADC_FREQ_MAX);  // Start ADC with maximum frequency

    // Configure DMA to transfer ADC results to memory
    dma.allocateChannel(DueDMA::ADC, &adcBuffer, 256);
    dma.enable();
}

void loop() {
    if (dma.isFinished()) {
        // DMA has finished transferring ADC data to adcBuffer
        for (int i = 0; i < 256; i++) {
            Serial.println(adcBuffer[i]);  // Print the ADC values
        }
        
        // Restart DMA transfer
        dma.start();
    }
}
        

Explanation

When to Use DMA vs. Standard ADC

Conclusion

The Arduino Due offers a powerful 12-bit ADC with the ability to use both standard and DMA-based methods for analog-to-digital conversion. The standard method is easy to use and works well for most simple applications, while the DMA method enables continuous high-speed data acquisition, which is critical for more demanding projects.

I2C (Inter-Integrated Circuit) on Arduino Due

I2C is a serial communication protocol that allows you to connect multiple devices using only two wires: SDA (data) and SCL (clock). The Arduino Due supports I2C communication, making it easy to interface with a wide range of sensors, EEPROMs, and displays.

Key Features

Usage Example


#include <Wire.h>

void setup() {
  Wire.begin();  // Initialize I2C as master
  Serial.begin(115200);
}

void loop() {
  Wire.beginTransmission(0x68);  // Address of device
  Wire.write(0x00);               // Send data
  Wire.endTransmission();
  delay(1000);
}

SPI (Serial Peripheral Interface) on Arduino Due

The Arduino Due uses the SPI protocol to communicate with external devices such as sensors, displays, and memory chips. SPI allows for fast data transfer between the microcontroller and peripherals.

Key Features

Usage Example


#include <SPI.h>

void setup() {
  Serial.begin(115200);
  SPI.begin();  // Initialize SPI
  pinMode(SS, OUTPUT);
}

void loop() {
  digitalWrite(SS, LOW);
  SPI.transfer(0x01);  // Send a byte
  digitalWrite(SS, HIGH);
  delay(1000);
}

TIMER on Arduino Due

The Arduino Due offers multiple timers that can be used for generating PWM signals, delays, and time-based events. These timers are part of the SAM3X8E's advanced hardware features.

Key Features

Usage Example


void setup() {
  Timer1.initialize(1000000);  // Set timer to trigger every 1 second
  Timer1.attachInterrupt(timerIsr);  // Attach interrupt function
}

void loop() {}

void timerIsr() {
  Serial.println("Timer triggered");
}

PWM Output at 25kHz
This example configures Timer Counter 0, Channel 0 (TC0, Ch 0) on the Arduino Due to generate a 25kHz PWM signal on pin 2 (TIOA0). The main clock is 84 MHz, and we divide it by 2 (TIMER_CLOCK1), then set RC to 1680 to get 25kHz. RA sets the duty cycle.


void setup() {
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC0);  // Enable peripheral clock for TC0

  TC_Configure(TC0, 0,
    TC_CMR_TCCLKS_TIMER_CLOCK1 |  // MCK/2 = 42 MHz
    TC_CMR_WAVE |                 // Waveform mode
    TC_CMR_WAVSEL_UP_RC |         // Count up to RC
    TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR); // Set/Clear PWM output

  TC_SetRC(TC0, 0, 1680);  // 42 MHz / 1680 = 25 kHz
  TC_SetRA(TC0, 0, 840);   // 50% duty cycle

  PIOB->PIO_PDR |= PIO_PB25;  // Disable GPIO on pin 2
  PIOB->PIO_ABSR |= PIO_PB25; // Use Peripheral B (TIOA0)

  TC_Start(TC0, 0);  // Start Timer
}

void loop() {}

Frequency Counter using TIOA Capture
This uses Timer Counter 1 Channel 0 to measure frequency on pin 34 (TIOA3 = PC2). The input signal toggles the counter, and the time between rising edges gives the frequency.


volatile uint32_t last_capture = 0;
volatile uint32_t frequency = 0;

void setup() {
  Serial.begin(115200);
  pmc_enable_periph_clk(ID_TC3);  // Enable TC1 Ch0 (TC3)
  
  TC_Configure(TC1, 0,
    TC_CMR_TCCLKS_TIMER_CLOCK1 |
    TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG |
    TC_CMR_LDRA_RISING | TC_CMR_CPCTRG);

  TC1->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS;  // Enable interrupt
  NVIC_EnableIRQ(TC3_IRQn);

  TC_Start(TC1, 0);
}

void TC3_Handler() {
  uint32_t now = TC1->TC_CHANNEL[0].TC_RA;
  frequency = 42000000 / (now - last_capture);  // 42MHz clock
  last_capture = now;
}

void loop() {
  Serial.println(frequency);
  delay(500);
}

Pulse Width Measurement
This captures the time between rising and falling edges of a pulse on pin 34 (TIOA3). You get high time, low time, and duty cycle.


volatile uint32_t rise_time = 0, fall_time = 0;
volatile uint32_t high_time = 0;

void setup() {
  Serial.begin(115200);
  pmc_enable_periph_clk(ID_TC3);  // TC1, Ch0

  TC_Configure(TC1, 0,
    TC_CMR_TCCLKS_TIMER_CLOCK1 |
    TC_CMR_ABETRG |
    TC_CMR_LDRA_RISING |
    TC_CMR_LDRB_FALLING);

  TC1->TC_CHANNEL[0].TC_IER = TC_IER_LDRBS;
  NVIC_EnableIRQ(TC3_IRQn);
  TC_Start(TC1, 0);
}

void TC3_Handler() {
  rise_time = TC1->TC_CHANNEL[0].TC_RA;
  fall_time = TC1->TC_CHANNEL[0].TC_RB;
  high_time = fall_time - rise_time;
}

void loop() {
  Serial.print("High Time (us): ");
  Serial.println((high_time * 1000000UL) / 42000000);
  delay(500);
}

Quadrature Encoder Decoder
This configures TC0 Channel 0 in quadrature mode to read an encoder using TIOA0 and TIOB0 on pins 2 and 13. You read the counter value to determine position and direction.


void setup() {
  Serial.begin(115200);
  pmc_enable_periph_clk(ID_TC0);

  // Configure TC0 Ch0 for quadrature decoding
  TC_Configure(TC0, 0, TC_CMR_TCCLKS_TIMER_CLOCK1);
  TC0->TC_BMR = TC_BMR_QDEN |       // Enable quadrature decode
                TC_BMR_POSEN |      // Position measure mode
                TC_BMR_EDGPHA |     // Use TIOA as phase A
                TC_BMR_MAXFILT(1);  // Input filter

  TC_Start(TC0, 0);
}

void loop() {
  int32_t position = TC0->TC_CHANNEL[0].TC_CV;
  Serial.print("Encoder Count: ");
  Serial.println(position);
  delay(100);
}

SERIAL Communication on Arduino Due

Arduino Due features multiple hardware serial ports (USART) that allow communication with other devices such as computers, sensors, and displays. The Due has four UARTs (Serial0 to Serial3).

Key Features

Usage Example


void setup() {
  Serial.begin(115200);  // Initialize Serial Monitor
  Serial1.begin(115200); // Initialize another serial port
}

void loop() {
  if (Serial1.available()) {
    char received = Serial1.read();
    Serial.println(received);  // Print received data
  }
}

Interrupts on Arduino Due

Interrupts allow the Arduino Due to respond immediately to certain events or inputs, such as a pin change or timer overflow. This is useful for tasks that require high responsiveness, such as processing sensor data, handling external events, or controlling hardware.

Key Features

Usage Example


volatile int interruptCounter = 0;

void setup() {
  Serial.begin(115200);

  // Initialize interrupt on pin 2
  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), countInterrupts, FALLING);
}

void loop() {
  // Print the number of interrupts every second
  Serial.println(interruptCounter);
  delay(1000);
}

void countInterrupts() {
  interruptCounter++;  // Increment the counter on each interrupt
}
  

Configuration

Interrupts can be configured using the attachInterrupt()function. This function attaches an interrupt to a specified pin and defines the triggering condition (e.g., falling, rising, or change). The interrupt service routine (ISR) is the function that gets executed when the interrupt is triggered.

Types of Interrupts

Limitations

There are some important considerations when working with interrupts on the Arduino Due:

Arduino Due USB Ports

The Arduino Due features two USB ports: the Programming Port and the Native USB Port. These ports serve different purposes, and the Native USB Port can also be configured for USB OTG (On-The-Go) functionality.

USB Programming Port

The Programming USB Port is primarily used for uploading sketches to the Arduino Due and for serial communication between the board and the host computer. It is a standard USB 2.0 connection, providing data transfer speeds of up to 12 Mbps (full speed). This port uses the ATmega16U2 chip as a USB-to-serial converter, allowing the Arduino IDE to communicate with the board.

Key Features:

USB Native Port

The Native USB Port on the Arduino Due is a Micro-B USB connector directly connected to the ATSAM3X8E ARM Cortex-M3 microcontroller. This allows the board to act as a USB device when connected to a computer or host system. The Native USB Port can be configured for various USB functionalities such as USB OTG (On-The-Go) or even as a USB HID (Human Interface Device), Mass Storage Device, or Serial Device depending on the use case.

Key Features:

Using the Native USB Port as USB OTG

The Native USB Port on the Arduino Due can be used as both a USB host and USB device, which is the key feature of USB OTG. By using the Arduino USB Host Shield library or implementing custom code, the Due can communicate with USB devices such as a keyboard, mouse, USB storage devices, and even Bluetooth modules in a host configuration. Alternatively, the Due can be configured to act as a USB device when connected to a host (such as a computer).

Steps to Use the Native USB Port as a Client (USB Device):

  1. Connect the Arduino Due’s Native USB Port to a USB host (e.g., computer or USB OTG-enabled device).
  2. Use the USBHost library or implement USB device functionality in the sketch.
  3. Specify the device type, e.g., USB Serial, HID, or Mass Storage.
  4. Upload the sketch and test communication between the Arduino Due and the host system.

Example Sketch for Using the Native USB Port as a USB Serial Device:


    void setup() {
        Serial.begin(115200); // Initialize Serial communication
    }

    void loop() {
        if (Serial.available()) {
            char incomingByte = Serial.read();
            Serial.print("Received: ");
            Serial.println(incomingByte);
        }
    }
    

This example will allow the Arduino Due to act as a USB Serial device, communicating with a host system via the Native USB Port.


#include <Keyboard.h>

void setup() {
  Keyboard.begin();
  delay(2000);  // Wait for host to recognize device
  Keyboard.print("Hello from Arduino Due Keyboard!");
  Keyboard.end();
}

void loop() {
}

#include <Mouse.h>

void setup() {
  Mouse.begin();
}

void loop() {
  Mouse.move(10, 0);  // Move mouse right
  delay(100);
  Mouse.move(-10, 0); // Move mouse left
  delay(100);
}

#include <Keyboard.h>
#include <Mouse.h>

void setup() {
  Keyboard.begin();
  Mouse.begin();

  delay(2000);  // Wait for USB host to detect

  Keyboard.print("Keyboard and Mouse started.");
  Mouse.move(20, 0);  // Move mouse right
  delay(500);
  Mouse.move(-20, 0); // Move mouse left
  Keyboard.end();
  Mouse.end();
}

void loop() {
}

#include <USBHost.h>

USBHost usb;
KeyboardController keyboard(usb);

void setup() {
  Serial.begin(115200);
  while (!Serial);  // Wait for Serial Monitor

  Serial.println("Waiting for keyboard...");
  
  keyboard.attachPress(onKeyDown);
  keyboard.attachRelease(onKeyUp);
}

void loop() {
  usb.Task();  // Must be called regularly
}

void onKeyDown(uint8_t keycode) {
  Serial.print("Key Pressed: ");
  Serial.println(keycode);
}

void onKeyUp(uint8_t keycode) {
  Serial.print("Key Released: ");
  Serial.println(keycode);
}

Conclusion

The Arduino Due's USB capabilities are quite versatile, with two USB ports offering multiple communication options. The Programming USB Port is ideal for sketch uploading and serial communication, while the Native USB Port supports more advanced features like USB OTG and USB device emulation. With the right libraries and code, the Native USB Port can turn the Due into a USB host or device, allowing it to interface with a wide variety of USB peripherals.

CAN (Controller Area Network) on Arduino Due

The Arduino Due features a built-in CAN bus interface, which is widely used in automotive and industrial applications for communication between microcontrollers and devices such as sensors, actuators, and controllers.

Key Features

Usage Example


#include <SPI.h>
#include <mcp_can.h>

MCP_CAN CAN(10);  // Initialize CAN on pin 10

void setup() {
  Serial.begin(115200);
  if (CAN.begin(CAN_500KBPS)) {
    Serial.println("CAN Bus Initialized");
  }
}

void loop() {
  CAN.sendMsgBuf(0x100, 0, 8, data);  // Send CAN message
  delay(1000);
}

Arduino Due DAC (Digital to Analog Converter)

The Arduino Due includes two true analog output pins: DAC0 and DAC1. These provide 12-bit analog voltages from 0 to 3.3V (not PWM).

📘 Basic Example – Sine Wave Output using analogWrite()

This example outputs a sine wave on DAC0 by using precomputed values:


const int sineTable[60] = {
  2048, 2251, 2452, 2649, 2838, 3017, 3182, 3331, 3461, 3570,
  3655, 3715, 3750, 3757, 3737, 3690, 3618, 3523, 3408, 3278,
  3135, 2983, 2825, 2666, 2509, 2360, 2222, 2099, 1993, 1908,
  1846, 1809, 1799, 1817, 1862, 1933, 2028, 2143, 2276, 2422,
  2576, 2732, 2886, 3031, 3163, 3276, 3368, 3434, 3474, 3487,
  3472, 3429, 3361, 3270, 3158, 3029, 2887, 2735, 2578, 2419
};

void setup() {
  analogWriteResolution(12); // 12-bit for DAC
}

void loop() {
  for (int i = 0; i < 60; i++) {
    analogWrite(DAC0, sineTable[i]); // Write to DAC0
    delayMicroseconds(500);          // ~3.3 kHz waveform
  }
}
    

💡 Advanced Example – DAC with DMA & Timer Trigger

This example configures the DAC to be triggered by Timer Counter 0 (TC0) and transfers a waveform using DMA.

Note: This uses direct register access and is only compatible with the Arduino Due.


#define WAVE_SAMPLES 100
uint16_t sine_wave[WAVE_SAMPLES];

void setupWaveform() {
  for (int i = 0; i < WAVE_SAMPLES; i++) {
    sine_wave[i] = 2048 + 2047 * sin(2 * PI * i / WAVE_SAMPLES);
  }
}

void setup() {
  analogWriteResolution(12);
  setupWaveform();

  pmc_enable_periph_clk(ID_DACC);
  dacc_reset(DACC);
  dacc_set_transfer_mode(DACC, 0);
  dacc_set_timing(DACC, 0x08, 0, 0);
  dacc_set_channel_selection(DACC, 0);
  dacc_enable_channel(DACC, 0);
  dacc_set_trigger(DACC, 1); // external trigger

  // Timer Counter 0 Channel 2 for DAC trigger
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC2);
  TC_Configure(TC0, 2,
    TC_CMR_TCCLKS_TIMER_CLOCK1 | // MCK/2
    TC_CMR_WAVE |                // Waveform mode
    TC_CMR_WAVSEL_UP_RC);        // Count up with RC

  TC_SetRC(TC0, 2, 42000); // Adjust frequency
  TC_Start(TC0, 2);

  // Enable trigger on DAC
  DACC->DACC_PTCR = DACC_PTCR_TXTEN;
  DACC->DACC_TPR  = (uint32_t)sine_wave;
  DACC->DACC_TCR  = WAVE_SAMPLES;
  DACC->DACC_TNPR = (uint32_t)sine_wave;
  DACC->DACC_TNCR = WAVE_SAMPLES;
  DACC->DACC_PTCR = DACC_PTCR_TXTEN; // Enable PDC

  // DAC Trigger from TC0 Channel 2
  DACC->DACC_MR |= DACC_MR_TRGEN_EN | DACC_MR_TRGSEL(0); // Trigger from TIO output
}

void loop() {
  // nothing, it's DMA-driven
}
    

This DMA method creates a smooth continuous waveform with very low CPU usage.