Teensy 3.6 Direct Memory Access port B read problem

Status
Not open for further replies.
I'm trying to read a parallel data bus from a 10 MHz 14 bit ADC and cobbled together a test program to verify that this is possible, as well as to learn how to use the direct memory access (DMA) system.
The pins in the portPins array (in the following code) are being driven with a 1 microsecond wide pulse once per second, so this code should toggle the built-in LED. Unfortunately nothing happens, not even an error message, which is characterized by the LED turning off and a message over serial.
Could someone who understands the DMA please take a look at this code and hopefully find my mistake? I've been reading through forum posts (Interrupt Jitter, DMA Modes, DMAChannel::triggerAtHardwareEvent digital pin source?, Teensy 3.6 DMA to read multiple digital inputs on pin interrupt., etc), Pedvide's ADC library, and the reference manual, but I'm stuck.
The deadline for this is two days away, so I will appreciate any help very much.
Code:
#include <Arduino.h>
#include <DMAChannel.h>
#define pinCount 16

uint16_t toggleData = 0x0;
bool state = 0;

DMAChannel dmachannel0;

void dma_check() {
  if(dmachannel0.complete()){
    Serial.println("DMA completed");
    dmachannel0.clearComplete();
    digitalWriteFast(13,!state);
    state = !state;
    dmachannel0.enable(); 
  }
  if(dmachannel0.error()){
    Serial.println("DMA error");
    digitalWriteFast(13,0);
    dmachannel0.clearError();
  }
}

void dma_isr() {
    Serial.println("DMA ISR triggered");
    dma_check();
    dmachannel0.clearInterrupt();
}

void setup()   {                
//  FMC_PFAPR |= 0x00FF0000; // Disable prefetching
//  SCB_SHPR3 = 0x20200000; // Systick = priority 32 (defaults to zero)
  uint8_t portPins[pinCount] = {16, 17, 19, 18, 49, 50, 31, 32, 0, 1, 29, 30, 43, 46, 44, 45}; // Port B bits 0, 1, 2, 3, 4, 5, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23
  
  PORTB_PCR13 |= PORT_PCR_IRQC(1);
  
  dmachannel0.source(GPIOB_PDIR);
  dmachannel0.destination(toggleData);

  dmachannel0.transferSize(2);
  dmachannel0.transferCount(1);
  
  dmachannel0.interruptAtCompletion();

  dmachannel0.triggerAtHardwareEvent(DMAMUX_SOURCE_PORTB);
  dmachannel0.enable();
  dmachannel0.attachInterrupt(dma_isr);

  Serial.begin(115200);
  for (int i = 0; i < pinCount; i++) {
    pinMode(portPins[i],INPUT);
  }
  
  pinMode(13, OUTPUT);
  digitalWriteFast(13,!state);
}

void loop() {
    dma_check();
    delay(100);
}
 
It's not clear why you need DMA?? FWIW, here is a sketch/discussion of reading low order 8-bits of GPIOC_PDIR into 1024-byte vector with DMA.
https://forum.pjrc.com/threads/3872...in-read-method?p=121468&viewfull=1#post121468
takes about 45 us to collect the 1024 bytes.

if you only need to read 14-bits/pins from PORTB every second, just do uint32_t Bbits = GPIOB_PDIR; then you'll need to construct the 14-bit value by testing the appropriate bits in Bbits in the proper order and accumulating the result.
 
Thanks for the suggestion. Although 45 us is faster than what my attempt at direct port reading provided, it is 3 orders of magnitude too slow.
I ought to have specified the following in my initial post:
The 10 MHz ADC changes the state of the parallel data bus every 100 ns, so the code needs to take less than 100 ns to record the pin states and prepare for a new measurement.
For testing, I am sending an 80ns wide pulse into all of the PORTB pins once per second to allow time to print the results over serial, however I will switch to a 10MHz signal once this initial code is working and a ring buffer is implemented.
The ring buffer in Pedvide's ADC library is close to what I need in the final program, except I need to pull from PORTB using a trigger on PORTB.
 
DMA time of 45 us was for 1024 samples, so that's about 45 ns/sample. Using direct read of GPIOB_PDIR with FASTRUN
Code:
FASTRUN void capture() {
    for(size_t i = 0; i < capture_count; i++) {
        buffer[i] = GPIOB_PDIR;
    }
}
also takes about 45 us for 1024 samples on T3.6@180 mhz, 34 us for 1024 samples @240mhz.

You probably can't get the performance you need with an ISR trigger. ISR latency will limit you to 1 us per sample, or if you're lucky 400 ns. Maybe you can just read ADC pins (GPIOB) continuously with DMA ping-pong buffers (interrupt at buffer half-full and full).

To convert the GPIOB bits to a value will take about a 205 cycles, 854 ns with T3.6@240mhz
Code:
  t1 = SYST_CVR;
  val = 0;
  for (int i = 0; i < 14; i++) {
    if (Bbits & 1 << offset[i]) val |= 1 << i;
  }
  t2 = SYST_CVR;

Good luck.
 
Status
Not open for further replies.
Back
Top