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 |
| Microcontroller | Atmel SAM3X8E ARM Cortex-M3 |
| Operating Voltage | 3.3V |
| Input Voltage (recommended) | 7-12V |
| Input Voltage (limits) | 6-20V |
| Digital I/O Pins | 54 (of which 12 provide PWM output) |
| Analog Input Pins | 12 |
| Analog Output Pins | 2 (DAC) |
| Total DC Output Current on all I/O lines | 130 mA |
| Flash Memory | 512 KB |
| SRAM | 96 KB (two banks: 64KB and 32KB) |
| Clock Speed | 84 MHz |
Unique Features
- 32-bit ARM Cortex-M3 Processor: With 84 MHz clock speed, the Arduino Due provides a significant speed boost over typical 8-bit boards.
- 512 KB Flash Memory: It offers ample storage space for your sketches, though it's not available for dynamic use by your applications.
- 12 Analog Inputs: The board has a high number of analog input pins, supporting more sensor inputs in complex projects.
- 2 Analog Outputs (DAC): Unlike most Arduino boards, the Due includes two true analog output DAC pins (pins DAC0 and DAC1).
- 54 Digital I/O Pins: Of these, 12 can be used for Pulse Width Modulation (PWM), which allows for flexible control of LEDs, servos, and motors.
- Native USB Port and Programming USB Port: The board includes two USB ports, one for debugging and the other for communication via USB to serial.
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
Image: see Wikimedia Commons page above — check the file page for author & license (often 'own work' by Arduino or CC BY-SA).
Official: Arduino Due - Product page
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
- Robotics and automation
- Audio processing and synthesizers
- Complex mathematical calculations
- Data logging and real-time monitoring
- Advanced sensor networks
- IoT projects requiring higher processing power
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
- 12-bit Resolution: Provides higher accuracy compared to the standard 10-bit ADC found in other Arduino boards (like the Uno).
- Channels: The Due has 12 analog input pins (A0 to A11), each of which can be used for ADC readings.
- Sampling Rate: The default ADC clock rate is 1 MHz, but this can be adjusted for faster or slower conversions.
- DMA Support: The ADC supports Direct Memory Access (DMA), which allows for continuous high-speed ADC sampling without CPU intervention.
- Voltage Range: The input range is from 0V to 3.3V. Exceeding this voltage can damage the board.
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
- analogRead(pin): This function reads the voltage on the specified analog pin (A0 in this case) and converts it to a digital value between 0 and 4095.
- The result is printed to the Serial Monitor. This method is simple but can be relatively slow, especially if multiple readings are required in a short period of time.
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
- DueDMA Library: This library is used to configure and manage DMA transfers on the Arduino Due.
- adc.setResolution(12): Configures the ADC to operate at 12-bit resolution.
- dma.allocateChannel(): Allocates a DMA channel to continuously transfer ADC data into the
adcBuffer.
- dma.isFinished(): Checks if the DMA transfer has finished. Once it has, the program prints the buffer values and restarts the DMA transfer for continuous sampling.
- This method allows for much higher sampling rates without blocking the CPU.
When to Use DMA vs. Standard ADC
- Standard ADC Method: Use this method when you need occasional analog readings and do not require a high sampling rate.
- DMA ADC Method: This method is ideal for applications that require continuous, high-speed sampling (e.g., data logging, audio processing) without interrupting the CPU's main task.
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
- Two-wire communication: SDA (Data) and SCL (Clock)
- Supports multiple devices on the same bus
- Default I2C frequency is 100 kHz (can be increased up to 400 kHz)
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
- Supports SPI modes 0, 1, 2, and 3
- High-speed data transfer up to 10 MHz (or higher depending on clock configuration)
- Multiple slave devices can be connected using chip select pins
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
- Supports 3 timers: Timer 0, Timer 1, and Timer 2
- Configurable for PWM, interrupts, and delays
- Timer frequency is based on the system clock (typically 84 MHz)
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
- Supports four hardware serial ports
- Baud rates up to 1 Mbps
- Full-duplex communication
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
- Supports external interrupts on several pins (digital pins 2-13)
- Can trigger on rising edge, falling edge, or change in state
- Supports interrupts for timers and other hardware peripherals
- Interrupts are handled in real-time, allowing non-blocking behavior in your programs
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
- External Interrupts: These interrupts are triggered by a change in voltage level on specific pins, such as when a button is pressed or a sensor detects a change.
- Timer Interrupts: You can configure a timer to generate interrupts at regular intervals. This is useful for time-critical tasks, such as sampling sensors at precise intervals.
- Pin Change Interrupts: The Arduino Due can handle interrupts on several pins, typically digital pins 2-13. These interrupts allow the board to respond quickly to external signals or changes in state.
Limitations
There are some important considerations when working with interrupts on the Arduino Due:
- Interrupt service routines (ISRs) should be kept as short and fast as possible to avoid blocking other interrupts.
- Interrupts should not use
delay(), as this will block the entire program.
- Using interrupts with other hardware peripherals, such as timers or ADCs, can require careful management to prevent conflicts.
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:
- Used for uploading sketches to the board.
- Acts as a USB-to-Serial converter.
- Supports Serial Monitor communication with the Arduino IDE.
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:
- Allows the Arduino Due to act as a USB device (e.g., USB keyboard, mouse, etc.).
- Supports USB OTG functionality, enabling the Due to connect to other USB devices as a client.
- Uses the ATSAM3X8E microcontroller’s built-in USB peripheral.
- Available for communication via USB Serial or other protocols.
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):
- Connect the Arduino Due’s Native USB Port to a USB host (e.g., computer or USB OTG-enabled device).
- Use the USBHost library or implement USB device functionality in the sketch.
- Specify the device type, e.g., USB Serial, HID, or Mass Storage.
- 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
- Supports CAN 2.0 A/B protocols
- Built-in CAN controller (using the SAM3X8E's hardware support)
- Allows communication at up to 1 Mbps
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.