Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: DMA can't write to GPIO registers on teensy 4.0

  1. #1
    Junior Member
    Join Date
    Sep 2019
    Posts
    1

    DMA can't write to GPIO registers on teensy 4.0

    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?

  2. #2
    Junior Member
    Join Date
    Oct 2019
    Posts
    1
    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.

  3. #3
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    7,615
    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

  4. #4
    Kurt is right - DMA can only access GPIO1-4 and not GPIO6-9. In your case, you need to set your destination to GPIO2 instead of GPIO7.

    You also need to set the IOMUXC_GPR_GPR27 register to select GPIO2 and configure the pin as output using the GPIO2_GDIR register - you can't use pinMode in this case.

    Paul explained the two GPIO units here: https://forum.pjrc.com/threads/57717...l=1#post216620

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •