initial commit
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
#include <Adafruit_ZeroDMA.h>
|
||||
|
||||
#define ADC_PIN A4
|
||||
#define SAMPLE_BLOCK_LENGTH 256
|
||||
|
||||
Adafruit_ZeroDMA ADC_DMA;
|
||||
DmacDescriptor *dmac_descriptor_1;
|
||||
DmacDescriptor *dmac_descriptor_2;
|
||||
|
||||
uint16_t adc_buffer[SAMPLE_BLOCK_LENGTH * 2];
|
||||
uint16_t adc_sample_block[SAMPLE_BLOCK_LENGTH];
|
||||
volatile bool filling_first_half = true;
|
||||
volatile uint16_t *active_adc_buffer;
|
||||
volatile bool adc_buffer_filled = false;
|
||||
|
||||
|
||||
uint16_t peak_to_peak(uint16_t *data, int data_length){
|
||||
int signalMax = 0;
|
||||
int signalMin = 4096; // max value for 12 bit adc
|
||||
|
||||
for(int i=0; i<data_length; i++){
|
||||
if ( data[i] > signalMax ) {
|
||||
signalMax = data[i];
|
||||
}
|
||||
if (data[i] < signalMin ){
|
||||
signalMin = data[i];
|
||||
}
|
||||
}
|
||||
return signalMax - signalMin;
|
||||
}
|
||||
|
||||
|
||||
void dma_callback(Adafruit_ZeroDMA *dma) {
|
||||
(void) dma;
|
||||
if (filling_first_half) {
|
||||
// DMA is filling the first half of the buffer, use data from the second half
|
||||
active_adc_buffer = &adc_buffer[SAMPLE_BLOCK_LENGTH];
|
||||
filling_first_half = false;
|
||||
} else {
|
||||
// DMA is filling the second half of the buffer, use data from the first half
|
||||
active_adc_buffer = &adc_buffer[0];
|
||||
filling_first_half = true;
|
||||
}
|
||||
adc_buffer_filled = true;
|
||||
}
|
||||
|
||||
|
||||
static void ADCsync() {
|
||||
while (ADC->STATUS.bit.SYNCBUSY == 1);
|
||||
}
|
||||
|
||||
|
||||
void adc_init() {
|
||||
analogRead(ADC_PIN);
|
||||
ADC->CTRLA.bit.ENABLE = 0;
|
||||
ADCsync();
|
||||
ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_DIV2;
|
||||
ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC1;
|
||||
ADCsync();
|
||||
ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[ADC_PIN].ulADCChannelNumber;
|
||||
ADCsync();
|
||||
ADC->AVGCTRL.reg = 0;
|
||||
ADC->SAMPCTRL.reg = 2;
|
||||
ADCsync();
|
||||
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV32 | ADC_CTRLB_FREERUN | ADC_CTRLB_RESSEL_12BIT;
|
||||
ADCsync();
|
||||
ADC->CTRLA.bit.ENABLE = 1;
|
||||
ADCsync();
|
||||
}
|
||||
|
||||
void dma_init(){
|
||||
ADC_DMA.allocate();
|
||||
ADC_DMA.setTrigger(ADC_DMAC_ID_RESRDY);
|
||||
ADC_DMA.setAction(DMA_TRIGGER_ACTON_BEAT);
|
||||
dmac_descriptor_1 = ADC_DMA.addDescriptor(
|
||||
(void *)(&ADC->RESULT.reg),
|
||||
adc_buffer,
|
||||
SAMPLE_BLOCK_LENGTH,
|
||||
DMA_BEAT_SIZE_HWORD,
|
||||
false,
|
||||
true);
|
||||
dmac_descriptor_1->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT;
|
||||
|
||||
dmac_descriptor_2 = ADC_DMA.addDescriptor(
|
||||
(void *)(&ADC->RESULT.reg),
|
||||
adc_buffer + SAMPLE_BLOCK_LENGTH,
|
||||
SAMPLE_BLOCK_LENGTH,
|
||||
DMA_BEAT_SIZE_HWORD,
|
||||
false,
|
||||
true);
|
||||
dmac_descriptor_2->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT;
|
||||
|
||||
ADC_DMA.loop(true);
|
||||
ADC_DMA.setCallback(dma_callback);
|
||||
}
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
adc_init();
|
||||
dma_init();
|
||||
ADC_DMA.startJob();
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
if(adc_buffer_filled){
|
||||
adc_buffer_filled=false;
|
||||
memcpy(adc_sample_block, (const void*)active_adc_buffer, SAMPLE_BLOCK_LENGTH*sizeof(uint16_t));
|
||||
Serial.println(peak_to_peak(adc_sample_block, SAMPLE_BLOCK_LENGTH));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Simple ZeroDMA example -- an equivalent to the memcpy() function.
|
||||
// Decause it uses DMA, unlike memcpy(), your code could be doing other
|
||||
// things simultaneously while the copy operation runs.
|
||||
|
||||
#include <Adafruit_ZeroDMA.h>
|
||||
#include "utility/dma.h"
|
||||
|
||||
Adafruit_ZeroDMA myDMA;
|
||||
ZeroDMAstatus stat; // DMA status codes returned by some functions
|
||||
|
||||
// The memory we'll be moving:
|
||||
#define DATA_LENGTH 1024
|
||||
uint8_t source_memory[DATA_LENGTH],
|
||||
destination_memory[DATA_LENGTH];
|
||||
|
||||
volatile bool transfer_is_done = false; // Done yet?
|
||||
|
||||
// Callback for end-of-DMA-transfer
|
||||
void dma_callback(Adafruit_ZeroDMA *dma) {
|
||||
(void) dma;
|
||||
transfer_is_done = true;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
uint32_t t;
|
||||
pinMode(LED_BUILTIN, OUTPUT); // Onboard LED can be used for precise
|
||||
digitalWrite(LED_BUILTIN, LOW); // benchmarking with an oscilloscope
|
||||
Serial.begin(115200);
|
||||
while(!Serial); // Wait for Serial monitor before continuing
|
||||
|
||||
Serial.println("DMA test: memory copy");
|
||||
|
||||
Serial.print("Allocating DMA channel...");
|
||||
stat = myDMA.allocate();
|
||||
myDMA.printStatus(stat);
|
||||
|
||||
Serial.println("Setting up transfer");
|
||||
myDMA.addDescriptor(source_memory, destination_memory, DATA_LENGTH);
|
||||
|
||||
Serial.println("Adding callback");
|
||||
// register_callback() can optionally take a second argument
|
||||
// (callback type), default is DMA_CALLBACK_TRANSFER_DONE
|
||||
myDMA.setCallback(dma_callback);
|
||||
|
||||
// Fill the source buffer with incrementing bytes, dest buf with 0's
|
||||
for(uint32_t i=0; i<DATA_LENGTH; i++) source_memory[i] = i;
|
||||
memset(destination_memory, 0, DATA_LENGTH);
|
||||
|
||||
// Show the destination buffer is empty before transfer
|
||||
Serial.println("Destination buffer before transfer:");
|
||||
dump();
|
||||
|
||||
Serial.println("Starting transfer job");
|
||||
stat = myDMA.startJob();
|
||||
myDMA.printStatus(stat);
|
||||
|
||||
Serial.println("Triggering DMA transfer...");
|
||||
t = micros();
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
myDMA.trigger();
|
||||
|
||||
// Your code could do other things here while copy happens!
|
||||
|
||||
while(!transfer_is_done); // Chill until DMA transfer completes
|
||||
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
t = micros() - t; // Elapsed time
|
||||
|
||||
Serial.print("Done! ");
|
||||
Serial.print(t);
|
||||
Serial.println(" microseconds");
|
||||
|
||||
Serial.println("Destination buffer after transfer:");
|
||||
dump();
|
||||
|
||||
// Now repeat the same operation, but 'manually' using memcpy() (not DMA):
|
||||
t = micros();
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
memcpy(destination_memory, source_memory, DATA_LENGTH);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
t = micros() - t; // Elapsed time
|
||||
Serial.print("Same operation without DMA: ");
|
||||
Serial.print(t);
|
||||
Serial.println(" microseconds");
|
||||
}
|
||||
|
||||
// Show contents of destination_memory[] array
|
||||
void dump() {
|
||||
for(uint32_t i=0; i<DATA_LENGTH; i++) {
|
||||
Serial.print(destination_memory[i], HEX); Serial.print(' ');
|
||||
if ((i & 15) == 15) Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() { }
|
||||
@@ -0,0 +1,98 @@
|
||||
// DMA-based SPI buffer write. This is transmit-only as written, i.e.
|
||||
// not equivalent to Arduino's SPI.transfer() which both sends and
|
||||
// receives a single byte or word. Also, this is single-buffered to
|
||||
// demonstrate a simple SPI write case. See zerodma_spi2.ino for an
|
||||
// example using double buffering (2 buffers alternating fill & transmit).
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_ZeroDMA.h>
|
||||
#include "utility/dma.h"
|
||||
|
||||
Adafruit_ZeroDMA myDMA;
|
||||
ZeroDMAstatus stat; // DMA status codes returned by some functions
|
||||
|
||||
// The memory we'll be issuing to SPI:
|
||||
#define DATA_LENGTH 2048
|
||||
uint8_t source_memory[DATA_LENGTH];
|
||||
|
||||
volatile bool transfer_is_done = false; // Done yet?
|
||||
|
||||
// Callback for end-of-DMA-transfer
|
||||
void dma_callback(Adafruit_ZeroDMA *dma) {
|
||||
(void) dma;
|
||||
transfer_is_done = true;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
uint32_t t;
|
||||
pinMode(LED_BUILTIN, OUTPUT); // Onboard LED can be used for precise
|
||||
digitalWrite(LED_BUILTIN, LOW); // benchmarking with an oscilloscope
|
||||
Serial.begin(115200);
|
||||
while(!Serial); // Wait for Serial monitor before continuing
|
||||
|
||||
Serial.println("DMA test: SPI data out");
|
||||
|
||||
SPI.begin();
|
||||
|
||||
Serial.println("Configuring DMA trigger");
|
||||
#ifdef __SAMD51__
|
||||
// SERCOM2 is the 'native' SPI SERCOM on Metro M4
|
||||
myDMA.setTrigger(SERCOM2_DMAC_ID_TX);
|
||||
#else
|
||||
// SERCOM4 is the 'native' SPI SERCOM on most M0 boards
|
||||
myDMA.setTrigger(SERCOM4_DMAC_ID_TX);
|
||||
#endif
|
||||
myDMA.setAction(DMA_TRIGGER_ACTON_BEAT);
|
||||
|
||||
Serial.print("Allocating DMA channel...");
|
||||
stat = myDMA.allocate();
|
||||
myDMA.printStatus(stat);
|
||||
|
||||
Serial.println("Setting up transfer");
|
||||
myDMA.addDescriptor(
|
||||
source_memory, // move data from here
|
||||
#ifdef __SAMD51__
|
||||
(void *)(&SERCOM2->SPI.DATA.reg), // to here (M4)
|
||||
#else
|
||||
(void *)(&SERCOM4->SPI.DATA.reg), // to here (M0)
|
||||
#endif
|
||||
DATA_LENGTH, // this many...
|
||||
DMA_BEAT_SIZE_BYTE, // bytes/hword/words
|
||||
true, // increment source addr?
|
||||
false); // increment dest addr?
|
||||
|
||||
Serial.println("Adding callback");
|
||||
// register_callback() can optionally take a second argument
|
||||
// (callback type), default is DMA_CALLBACK_TRANSFER_DONE
|
||||
myDMA.setCallback(dma_callback);
|
||||
|
||||
// Fill the source buffer with incrementing bytes
|
||||
for(uint32_t i=0; i<DATA_LENGTH; i++) source_memory[i] = i;
|
||||
|
||||
// Start SPI transaction at 12 MHz
|
||||
SPI.beginTransaction(SPISettings(12000000, MSBFIRST, SPI_MODE0));
|
||||
|
||||
Serial.println("Starting transfer job");
|
||||
t = micros();
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
|
||||
// Because we've configured a peripheral trigger (SPI), there's
|
||||
// no need to manually trigger transfer, it starts up on its own.
|
||||
stat = myDMA.startJob();
|
||||
|
||||
// Your code could do other things here while SPI write happens!
|
||||
|
||||
while(!transfer_is_done); // Chill until DMA transfer completes
|
||||
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
t = micros() - t; // Elapsed time
|
||||
|
||||
SPI.endTransaction();
|
||||
myDMA.printStatus(stat); // Results of start_transfer_job()
|
||||
|
||||
Serial.print("Done! ");
|
||||
Serial.print(t);
|
||||
Serial.println(" microseconds");
|
||||
}
|
||||
|
||||
void loop() { }
|
||||
@@ -0,0 +1,107 @@
|
||||
// Double-buffered DMA on auxiliary SPI peripheral on pins 11/12/13.
|
||||
// Continuously alternates between two data buffers...one is filled
|
||||
// with new data as the other is being transmitted.
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Adafruit_ZeroDMA.h>
|
||||
#include "utility/dma.h"
|
||||
#include "wiring_private.h" // pinPeripheral() function
|
||||
|
||||
// Declare our own SPI peripheral 'mySPI' on pins 11/12/13:
|
||||
// (Do not call this SPI1; Arduino Zero and Metro M0 already
|
||||
// have an SPI1 (the EDBG interface) and it won't compile.)
|
||||
#if defined(ARDUINO_SAMD_ADAFRUIT)
|
||||
SPIClass mySPI( // Adafruit SAMD board support calls this SPIClass
|
||||
#else
|
||||
SPIClassSAMD mySPI( // Arduino SAMD now calls it SPIClassSAMD
|
||||
#endif
|
||||
&sercom1, // -> Sercom peripheral
|
||||
34, // MISO pin (also digital pin 12)
|
||||
37, // SCK pin (also digital pin 13)
|
||||
35, // MOSI pin (also digital pin 11)
|
||||
SPI_PAD_0_SCK_1, // TX pad (MOSI, SCK pads)
|
||||
SERCOM_RX_PAD_3); // RX pad (MISO pad)
|
||||
|
||||
Adafruit_ZeroDMA myDMA;
|
||||
ZeroDMAstatus stat; // DMA status codes returned by some functions
|
||||
|
||||
// Data we'll issue to mySPI. There are TWO buffers; one being
|
||||
// filled with new data while the other's being transmitted in
|
||||
// the background.
|
||||
#define DATA_LENGTH 512
|
||||
uint8_t source_memory[2][DATA_LENGTH],
|
||||
buffer_being_filled = 0, // Index of 'filling' buffer
|
||||
buffer_value = 0; // Value of fill
|
||||
|
||||
volatile bool transfer_is_done = true; // Done yet?
|
||||
|
||||
// Callback for end-of-DMA-transfer
|
||||
void dma_callback(Adafruit_ZeroDMA *dma) {
|
||||
(void) dma;
|
||||
transfer_is_done = true;
|
||||
}
|
||||
|
||||
DmacDescriptor *desc; // DMA descriptor address (so we can change contents)
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while(!Serial); // Wait for Serial monitor before continuing
|
||||
|
||||
Serial.println("DMA test: SPI data out");
|
||||
|
||||
mySPI.begin();
|
||||
// Assign pins 11, 12, 13 to SERCOM functionality
|
||||
pinPeripheral(11, PIO_SERCOM);
|
||||
pinPeripheral(12, PIO_SERCOM);
|
||||
pinPeripheral(13, PIO_SERCOM);
|
||||
|
||||
// Configure DMA for SERCOM1 (our 'mySPI' port on 11/12/13)
|
||||
Serial.println("Configuring DMA trigger");
|
||||
myDMA.setTrigger(SERCOM1_DMAC_ID_TX);
|
||||
myDMA.setAction(DMA_TRIGGER_ACTON_BEAT);
|
||||
|
||||
Serial.print("Allocating DMA channel...");
|
||||
stat = myDMA.allocate();
|
||||
myDMA.printStatus(stat);
|
||||
|
||||
desc = myDMA.addDescriptor(
|
||||
source_memory[buffer_being_filled], // move data from here
|
||||
(void *)(&SERCOM1->SPI.DATA.reg), // to here
|
||||
DATA_LENGTH, // this many...
|
||||
DMA_BEAT_SIZE_BYTE, // bytes/hword/words
|
||||
true, // increment source addr?
|
||||
false); // increment dest addr?
|
||||
|
||||
Serial.println("Adding callback");
|
||||
// register_callback() can optionally take a second argument
|
||||
// (callback type), default is DMA_CALLBACK_TRANSFER_DONE
|
||||
myDMA.setCallback(dma_callback);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Fill buffer with new data. The other buffer might
|
||||
// still be transmitting in the background via DMA.
|
||||
memset(source_memory[buffer_being_filled], buffer_value, DATA_LENGTH);
|
||||
|
||||
// Wait for prior transfer to complete before starting new one...
|
||||
Serial.print("Waiting on prior transfer...");
|
||||
while(!transfer_is_done) Serial.write('.');
|
||||
mySPI.endTransaction();
|
||||
Serial.println("Done!");
|
||||
|
||||
// Modify the DMA descriptor using the newly-filled buffer as source...
|
||||
myDMA.changeDescriptor(desc, // DMA descriptor address
|
||||
source_memory[buffer_being_filled]); // New src; dst & count don't change
|
||||
|
||||
// Begin new transfer...
|
||||
Serial.println("Starting new transfer job");
|
||||
mySPI.beginTransaction(SPISettings(12000000, MSBFIRST, SPI_MODE0));
|
||||
transfer_is_done = false; // Reset 'done' flag
|
||||
stat = myDMA.startJob(); // Go!
|
||||
myDMA.printStatus(stat);
|
||||
|
||||
// Switch buffer indices so the alternate buffer is filled/xfer'd
|
||||
// on the next pass.
|
||||
buffer_being_filled = 1 - buffer_being_filled;
|
||||
buffer_value++;
|
||||
}
|
||||
Reference in New Issue
Block a user