Enter your credentials to continue
Join our secure network
Reset your password via email
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.
| 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 |
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.
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.
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
Please note that the Arduino Due operates at 3.3V. Applying more than 3.3V to the input/output pins could damage the board.
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.
The Arduino Due has a 12-bit ADC that converts analog voltages (0–3.3V) into digital values (0–4095). It is suitable for sensor readings, voltage measurements, and other analog input tasks.
Use analogRead() to read analog values from a pin. Returns 0–4095 corresponding to 0–3.3V.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
// Standard ADC Example for Arduino Due
int analogPin = A0;
int adcValue = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
adcValue = analogRead(analogPin);
Serial.println(adcValue);
delay(100);
}
For high-speed continuous sampling, DMA transfers ADC data directly to memory without CPU intervention.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
// DMA-based ADC Example for Arduino Due
#include
#include
ADC adc;
DueDMA dma;
volatile uint16_t adcBuffer[256];
void setup() {
Serial.begin(115200);
adc.setResolution(12);
adc.setSamplesAverage(1);
adc.start(ADC_FREQ_MAX);
dma.allocateChannel(DueDMA::ADC, &adcBuffer, 256);
dma.enable();
}
void loop() {
if (dma.isFinished()) {
for (int i = 0; i < 256; i++) {
Serial.println(adcBuffer[i]);
}
dma.start();
}
}
Arduino Due’s 12-bit ADC provides both simple and high-speed sampling methods. Standard ADC is suitable for basic applications, while DMA allows continuous, CPU-efficient high-speed data acquisition.
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.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
#include
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);
}
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.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
#include
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);
}
The Arduino Due offers multiple timers for PWM, delays, and time-based events. These timers are part of the SAM3X8E microcontroller’s advanced hardware.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
void setup() {
Timer1.initialize(1000000); // Trigger every 1 second
Timer1.attachInterrupt(timerIsr); // Attach ISR
}
void loop() {}
void timerIsr() {
Serial.println("Timer triggered");
}
Using TC0, Ch0 to generate 25kHz PWM on pin 2 (TIOA0). Main clock 84 MHz, divided by 2 (TIMER_CLOCK1), RC=1680, RA sets duty cycle.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
void setup() {
pmc_set_writeprotect(false);
pmc_enable_periph_clk(ID_TC0);
TC_Configure(TC0, 0,
TC_CMR_TCCLKS_TIMER_CLOCK1 |
TC_CMR_WAVE |
TC_CMR_WAVSEL_UP_RC |
TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR);
TC_SetRC(TC0, 0, 1680);
TC_SetRA(TC0, 0, 840);
PIOB->PIO_PDR |= PIO_PB25;
PIOB->PIO_ABSR |= PIO_PB25;
TC_Start(TC0, 0);
}
void loop() {}
Measure frequency using TC1 Ch0 (TIOA3 = PC2). Rising edge capture calculates frequency.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
volatile uint32_t last_capture = 0;
volatile uint32_t frequency = 0;
void setup() {
Serial.begin(115200);
pmc_enable_periph_clk(ID_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;
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);
last_capture = now;
}
void loop() {
Serial.println(frequency);
delay(500);
}
Captures high and low time on TIOA3 using input capture.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
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);
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);
}
TC0 Ch0 configured in quadrature mode. Reads encoder position using TIOA0/TIOB0 (pins 2 & 13).
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
void setup() {
Serial.begin(115200);
pmc_enable_periph_clk(ID_TC0);
TC_Configure(TC0, 0, TC_CMR_TCCLKS_TIMER_CLOCK1);
TC0->TC_BMR = TC_BMR_QDEN |
TC_BMR_POSEN |
TC_BMR_EDGPHA |
TC_BMR_MAXFILT(1);
TC_Start(TC0, 0);
}
void loop() {
int32_t position = TC0->TC_CHANNEL[0].TC_CV;
Serial.print("Encoder Count: ");
Serial.println(position);
delay(100);
}
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).
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
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 allow the Arduino Due to respond immediately to certain events or inputs, such as a pin change or timer overflow. Useful for tasks requiring high responsiveness, like sensor data processing, external event handling, or hardware control.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
volatile int interruptCounter = 0;
void setup() {
Serial.begin(115200);
pinMode(2, INPUT);
attachInterrupt(digitalPinToInterrupt(2), countInterrupts, FALLING);
}
void loop() {
Serial.println(interruptCounter);
delay(1000);
}
void countInterrupts() {
interruptCounter++;
}
Interrupts can be configured using attachInterrupt(), which attaches an interrupt to a pin and sets the triggering condition (falling, rising, or change). The ISR (Interrupt Service Routine) executes when the interrupt occurs.
delay() inside interruptsThe Arduino Due is based on the SAM3X8E ARM Cortex-M3 microcontroller, which includes an internal RTC peripheral. However, the Due board does not provide a dedicated 32.768 kHz crystal or battery backup, so the RTC cannot maintain time across power cycles by default.
While powered, the internal RTC can track time and generate alarms or interrupts, which is useful for scheduling tasks during runtime or low-power sleep periods.
For applications requiring accurate, persistent timekeeping, you have two main options:
Direct Memory Access (DMA) is a critical feature of the SAM3X8E microcontroller on the Arduino Due. It allows hardware peripherals to transfer data directly to and from main memory without involving the CPU for every byte. This significantly reduces processor overhead and enables high-speed data throughput for complex tasks.
The SAM3X8E features two distinct types of DMA mechanisms to optimize performance across different peripherals:
The PDC is integrated directly into peripherals. Here are examples for UART transmission and continuous multi-channel ADC sampling.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
uint8_t txBuffer[128];
void setupUART_PDC() {
UART0->UART_TPR = (uint32_t)txBuffer;
UART0->UART_TCR = 128;
UART0->UART_PTCR = UART_PTCR_TXTEN;
}
By enabling multiple ADC channels and the PDC, results from all active channels are sequentially stored in a single memory block.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
#define SAMPLES 512
uint16_t adcData[SAMPLES]; // Storage for interleaved channel data
void setupADC_PDC() {
PMC->PMC_PCER1 = 1 << (ID_ADC - 32); // Enable ADC clock
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR = ADC_MR_PRESCAL(1) | ADC_MR_STARTUP_SUT0; // Basic config
// Enable Channels 7 and 6 (A0 and A1 on Due)
ADC->ADC_CHER = ADC_CHER_CH7 | ADC_CHER_CH6;
// Configure PDC
ADC->ADC_TPR = (uint32_t)adcData;
ADC->ADC_TCR = SAMPLES; // Total 16-bit transfers
// Start PDC and Free-run mode
ADC->ADC_MR |= ADC_MR_FREERUN;
ADC->ADC_PTCR = ADC_PTCR_RXTEN;
ADC->ADC_CR = ADC_CR_START;
}
void loop() {
if (ADC->ADC_TSR & ADC_TSR_ENDRX) {
// Buffer is full with interleaved data: [CH7, CH6, CH7, CH6...]
ADC->ADC_TPR = (uint32_t)adcData; // Reload pointer
ADC->ADC_TCR = SAMPLES; // Reload count
}
}
The DMAC provides a flexible framework for advanced data movement. Below is a complete implementation of a parameterized memory transfer function.
/*
* Copyright (c) 2026 Peter Ivan Dunne
* Licensed under the Mozilla Public License, v. 2.0.
*
* This code is provided "as is", without warranty of any kind, express or implied.
* In no event shall the author be liable for any claim, damages or other liability.
*/
// Global DMAC Configuration and Execution
uint32_t dataA[256];
uint32_t dataB[256];
/**
* Parameterized DMAC Transfer
* @param addrA: Source Address
* @param addrB: Destination Address
* @param count: Number of transfers
* @param mode: 0 = Mem-to-Mem, 1 = Mem-to-Peri, etc.
*/
void dmac_transfer(uint32_t addrA, uint32_t addrB, uint32_t count, uint8_t mode) {
// Ensure DMAC is enabled
DMAC->DMAC_EN = DMAC_EN_ENABLE;
// Configure Channel 0 for the specific transfer
DMAC->DMAC_CH_NUM[0].DMAC_SADDR = addrA;
DMAC->DMAC_CH_NUM[0].DMAC_DADDR = addrB;
// Set Control A: Burst size, Width, and Total Count
DMAC->DMAC_CH_NUM[0].DMAC_CTRLA = DMAC_CTRLA_BTSIZE(count) |
DMAC_CTRLA_SRC_WIDTH_WORD |
DMAC_CTRLA_DST_WIDTH_WORD;
// Set Control B: Incremental addressing based on mode
uint32_t ctrlB = DMAC_CTRLB_SRC_DSCR | DMAC_CTRLB_DST_DSCR;
if (mode == 0) {
ctrlB |= DMAC_CTRLB_FC_MEM2MEM;
} else {
// Additional modes (e.g. Memory to Peripheral) logic here
}
DMAC->DMAC_CH_NUM[0].DMAC_CTRLB = ctrlB;
// Start Channel 0
DMAC->DMAC_CHER = DMAC_CHER_ENA0;
}
void setup() {
Serial.begin(115200);
// Initialize dummy data
for(int i=0; i<256; i++) dataA[i] = i * 10;
// Enable DMAC clock via Peripheral Management Controller
PMC->PMC_PCER1 = 1 << (ID_DMAC - 32);
Serial.println("Starting DMAC transfer...");
dmac_transfer((uint32_t)dataA, (uint32_t)dataB, 256, 0);
}
void loop() {
// Check Status Register (EBF bit for Channel 0)
if (DMAC->DMAC_EBCISR & DMAC_EBCISR_BTC0) {
Serial.println("Transfer Complete!");
// Verify a sample
Serial.print("DataB[10]: ");
Serial.println(dataB[10]);
while(1); // Stop
}
}
| Feature | PDC | DMAC |
|---|---|---|
| Complexity | Low (Easy to configure) | High (More registers) |
| Flexibility | Fixed point-to-point | Fully programmable routing |
| Mem-to-Mem | No | Yes |
| Channels | 22 (Peripheral specific) | 6 (General purpose) |
DMA is an indispensable tool for maximizing the potential of the SAM3X8E. By offloading data transfers to dedicated controllers, the Arduino Due can handle high-performance audio processing, rapid sensor logging, and responsive user interfaces simultaneously, maintaining the "real-time" responsiveness required for advanced embedded systems.
The Arduino Due features two USB ports: the Programming Port and the Native USB Port. The Native USB Port can also support USB OTG functionality.
Used primarily for uploading sketches and serial communication. It uses the ATmega16U2 as a USB-to-serial converter.
Connected directly to the ATSAM3X8E microcontroller. Supports USB device mode, USB OTG, HID, Mass Storage, or Serial communication.
void setup() {
Serial.begin(115200);
}
void loop() {
if (Serial.available()) {
char incomingByte = Serial.read();
Serial.print("Received: ");
Serial.println(incomingByte);
}
}
#include <Keyboard.h>
void setup() {
Keyboard.begin();
delay(2000);
Keyboard.print("Hello from Arduino Due Keyboard!");
Keyboard.end();
}
void loop() {}
#include <Mouse.h>
void setup() {
Mouse.begin();
}
void loop() {
Mouse.move(10, 0);
delay(100);
Mouse.move(-10, 0);
delay(100);
}
#include <Keyboard.h>
#include <Mouse.h>
void setup() {
Keyboard.begin();
Mouse.begin();
delay(2000);
Keyboard.print("Keyboard and Mouse started.");
Mouse.move(20, 0);
delay(500);
Mouse.move(-20, 0);
Keyboard.end();
Mouse.end();
}
void loop() {}
#include <USBHost.h>
USBHost usb;
KeyboardController keyboard(usb);
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("Waiting for keyboard...");
keyboard.attachPress(onKeyDown);
keyboard.attachRelease(onKeyUp);
}
void loop() {
usb.Task();
}
void onKeyDown(uint8_t keycode) {
Serial.print("Key Pressed: ");
Serial.println(keycode);
}
void onKeyUp(uint8_t keycode) {
Serial.print("Key Released: ");
Serial.println(keycode);
}
Arduino Due's USB ports provide versatile options: the Programming Port is ideal for sketch upload and serial communication, while the Native USB Port supports USB OTG, device emulation, and host communication with a variety of peripherals.
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.
#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);
}
The Arduino Due includes two true analog output pins: DAC0 and DAC1. These pins provide 12-bit analog voltages from 0 to 3.3V (not PWM), useful for audio, waveform generation, and analog signal output.
Output a sine wave on DAC0 using precomputed values with analogWrite():
const int sineTable[60] = {2048, 2251, 2452, 2649, /* ... */ 2419};
void setup() {
analogWriteResolution(12); // 12-bit DAC
}
void loop() {
for (int i = 0; i < 60; i++) {
analogWrite(DAC0, sineTable[i]);
delayMicroseconds(500); // ~3.3 kHz
}
}
Configure the DAC to be triggered by Timer Counter 0 (TC0) and transfer a waveform using DMA for smooth, continuous output with minimal CPU usage.
#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
pmc_set_writeprotect(false);
pmc_enable_periph_clk(ID_TC2);
TC_Configure(TC0, 2,
TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC);
TC_SetRC(TC0, 2, 42000);
TC_Start(TC0, 2);
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;
DACC->DACC_MR |= DACC_MR_TRGEN_EN | DACC_MR_TRGSEL(0);
}
void loop() {
// CPU-free waveform output via DMA
}
DMA-driven DAC output allows smooth, continuous waveforms with minimal CPU load, ideal for high-frequency analog signal generation.
The Arduino Due is based on the SAM3X8E ARM Cortex-M3 microcontroller, which includes an internal RTC peripheral. However, the Due board does not provide a dedicated 32.768 kHz crystal or battery backup, so the RTC cannot maintain time across power cycles by default.
While powered, the internal RTC can track time and generate alarms or interrupts, which is useful for scheduling tasks during runtime or low-power sleep periods.
For applications requiring accurate, persistent timekeeping, you have two main options:
The Arduino Due (ATSAM3X8E) supports high-speed parallel data transfer using its General Purpose I/O (PIO) ports and the Static Memory Controller (SMC). A parallel bus transfers multiple data bits simultaneously, providing much higher throughput than serial interfaces when driving displays, memory devices, or external logic.
A basic 8-bit parallel bus can be implemented using a single PIO port. For example, PIOC pins PC0–PC7 can be used as D0–D7, with additional GPIO pins acting as control signals such as WR, RD, or CS.
Using one full port allows writing all 8 bits in a single CPU cycle.
/*
Arduino Due Parallel Bus Example
Uses PIOC pins PC0–PC7 as an 8-bit data bus
*/
void setup() {
// Enable clock for PIOC
PMC->PMC_PCER0 |= (1 << ID_PIOC);
// Disable PIO control, enable PIO
PIOC->PIO_PER = 0xFF; // Enable PIO on PC0–PC7
PIOC->PIO_OER = 0xFF; // Set PC0–PC7 as outputs
// Optional: disable pull-ups
PIOC->PIO_PUDR = 0xFF;
}
void writeParallel(uint8_t value) {
// Clear lower 8 bits
PIOC->PIO_CODR = 0xFF;
// Set new data
PIOC->PIO_SODR = value;
}
void loop() {
for (uint8_t i = 0; i < 256; i++) {
writeParallel(i); // Output value on parallel bus
delay(10);
}
}
digitalWrite() and use registers directly