DMA can't write to GPIO registers on teensy 4.0

Status
Not open for further replies.

valence55

New member
Hello, I am working on a project that requires me to send a precise sequence of signals through a GPIO pin while leaving the CPU free for other tasks, so I'm trying to use DMA to control the pin. However, the DMA channel seems to be unable to write any data to the GPIO registers, and causes an error every time it tries. Here is my test code so far:

Code:
#include "DMAChannel.h"

DMAChannel dma1;

//0x8 is the bit address for GPIO 13, which is on the GPIO7 register.
const uint32_t dmaData[] = {0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000008,
                             0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000008,
                             0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000008,
                             0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000008};

uint32_t testNum = 0;

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(115200);
  dma1.begin();

  //configure DMA using DMAChannel.h
  dma1.sourceBuffer(dmaData, sizeof(dmaData));
  dma1.destination(GPIO7_DR_TOGGLE);// I have also tried GPIO7_DR and GPIO7_DR_SET
  //dma1.destination(testNum); DMA can write to testNum correctly, but not GPIO7_DR_TOGGLE
  dma1.attachInterrupt(dmaInterrupt);
  dma1.interruptAtCompletion();
  dma1.transferSize(4);
  dma1.enable();

  //configure DMA registers directly. This doesn't work for GPIO either
  /*
  dma1.TCD->SADDR = dmaData;
  dma1.TCD->SOFF = 4;
  dma1.TCD->NBYTES = 4;
  dma1.TCD->SLAST = -sizeof(dmaData);
  dma1.TCD->BITER = sizeof(dmaData) / 4;
  dma1.TCD->CITER = sizeof(dmaData) / 4;
  dma1.TCD->ATTR_SRC = 2;
  dma1.TCD->DADDR = &GPIO7_DR_TOGGLE;
  dma1.TCD->DOFF = 0;
  dma1.TCD->ATTR_DST = 2;
  dma1.TCD->DLASTSGA = 0;
  dma1.enable();*/
  
}

void loop() {
  //using manual trigger and delay just for testing purposes
  dma1.triggerManual();
  delay(100);

  Serial.println(GPIO7_DR);
  //Serial.println(testNum);

  //error occurs only when trying to write to GPIO 
  if(dma1.error()){
    Serial.print("DMA error: ");
    Serial.println(DMA_ES, HEX);
  }
}

//gets called when writing to testNum, but not GPIO
void dmaInterrupt(){
  dma1.clearInterrupt();
  Serial.println("DMA finished");
}

when I set the destination address to a normal variable, the DMA works perfectly; print statements show the variable changing correctly, no error is detected, and the interrupt is called when the DMA finishes a cycle. However, when i set the destination to a GPIO port register (GPIO7), the data in the register does not change and the following error code is printed:


Code:
DMA error: 0x80000001

According to the IMXRT1060 reference manual, that code means "The last recorded error was a bus error on a destination write". I have tried several different settings for the DMA channel but the same thing always happens. Does anyone know the proper way to write to GPIO using a DMA channel?
 
Were you able to resolve the DMA to GPIO issue? I'm in the same situation and NXP has not been helpful. They have asserted that the chip is capable of DMA peripheral communication but have zero examples. The documentation is lacking critical information.
 
I have not done much playing with it. But I know that @Paul was able to do some of this with the OctoWS2811 library to work with doing DMA to some of the pins.

So first place to look might be at the released version of OctoWS2811/OctWS2811_imxrt.cpp

Some of the things I remembered was I am not sure you can do DMA to the high speed GPIO port versions GPIO6-GPIO9
Instead you need to work with instead of GPIO6 you would use GPIO1 to talk to these pins.

I don't remember if you need to also remap those pins that you are doing DMA to to be back to not using the Fast version. These are set in startup.c


Code:
#if defined(__IMXRT1062__)
	// Use fast GPIO6, GPIO7, GPIO8, GPIO9
	IOMUXC_GPR_GPR26 = 0xFFFFFFFF;
	IOMUXC_GPR_GPR27 = 0xFFFFFFFF;
	IOMUXC_GPR_GPR28 = 0xFFFFFFFF;
	IOMUXC_GPR_GPR29 = 0xFFFFFFFF;
#endif
So you would probably need to unset those bits associated with those pins in the correct register here.

As for then what trigger you can use. I believe Paul did it using a Quad Timer through XBAR system.
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0);

Not sure what you are wanting for a trigger

But hopefully that might give some hints
 
Status
Not open for further replies.
Back
Top