GPIO2 DMA access issue

hyalinr

New member
Hi,

I'm trying to access the builtin LED(pin13) on Teensy4.1 through DMA by writing to the GPIO2_DR_TOGGLE register but it doesn't work. Code is included below, is there anything missing from the DMA setup?

#include <Arduino.h>
#include <imxrt.h>
const int gpio = LED_BUILTIN;
static volatile uint32_t mode = (0b1<<3);
static volatile uint32_t toggle = (0b1<<3);
void setup() {
while (!Serial) {
;// wait for Arduino Serial Monitor to be ready
}
CCM_CCGR0 |= CCM_CCGR0_GPIO2(CCM_CCGR_ON);//turn on gpio2 clock
IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 0b101; //IOMuxing for pin 10 0b101 for alt5 GPIO, 0b011 for alt3 PCS[0]
CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON); // enables DMA clock if not already enabled
__enable_irq();
DMAMUX_CHCFG0 = ((0b1<<31)|(0b1<<30));
IMXRT_DMA_TCD[0].CITER = 1;
IMXRT_DMA_TCD[0].BITER = 1;
IMXRT_DMA_TCD[0].NBYTES = 4;
IMXRT_DMA_TCD[0].SADDR = &mode;
IMXRT_DMA_TCD[0].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
IMXRT_DMA_TCD[0].SLAST = 0; // Source address adjustment after major loop completion
IMXRT_DMA_TCD[0].DADDR = &GPIO2_GDIR;
IMXRT_DMA_TCD[0].DOFF = 0;
IMXRT_DMA_TCD[0].DLASTSGA = 0;
IMXRT_DMA_TCD[0].ATTR = DMA_TCD_ATTR_DSIZE(2)|DMA_TCD_ATTR_SSIZE(2);//source data transfer size 32bit, d data transfer size 32bit
IMXRT_DMA_TCD[0].CSR =0;//set major channel linking to ch4
DMA_SERQ = 0;
DMA_SSRT = 1;

DMAMUX_CHCFG1 = ((0b1<<31)|(0b1<<30));
IMXRT_DMA_TCD[1].CITER = 1;
IMXRT_DMA_TCD[1].BITER = 1;
IMXRT_DMA_TCD[1].NBYTES = 4;
IMXRT_DMA_TCD[1].SADDR = &toggle;
IMXRT_DMA_TCD[1].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
IMXRT_DMA_TCD[1].SLAST = 0; // Source address adjustment after major loop completion
IMXRT_DMA_TCD[1].DADDR = &GPIO2_DR_TOGGLE;
IMXRT_DMA_TCD[1].DOFF = 0;
IMXRT_DMA_TCD[1].DLASTSGA = 0;
IMXRT_DMA_TCD[1].ATTR = DMA_TCD_ATTR_DSIZE(2)|DMA_TCD_ATTR_SSIZE(2);//source data transfer size 32bit, d data transfer size 32bit
IMXRT_DMA_TCD[1].CSR =0;//set major channel linking to ch4
DMA_SERQ = 1;
Serial.println(GPIO2_GDIR,BIN);
}
void loop() {
delay(1000);
DMA_SSRT = 1;
Serial.print("Reading");
Serial.println(GPIO2_DR,BIN);
//GPIO7_DR_TOGGLE = (0b1<<3);// non dma access to pin13
}
 
IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 0b101; //IOMuxing for pin 10 0b101 for alt5 GPIO, 0b011 for alt3 PCS[0]
You also need to select GPIO2 instead of GPIO7 for pin 13, by clearing bit 3 of IOMUXC_GPR_GPR27; see page 323 of the reference manual. That is, add IOMUXC_GPR_GPR27 &= ~(0b1 << 3); in setup().
 
Last edited:
You also need to select GPIO2 instead of GPIO7 for pin 13, by clearing bit 3 of IOMUXC_GPR_GPR27; see page 323 of the reference manual. That is, add IOMUXC_GPR_GPR27 &= ~(0b1 << 3); in setup().
Thank you for your reply. After adding that line of code, I think I'm getting the same issue where the DMA doesn't seem to load the values into the registers properly. GPIO2_GDIR is always 0 even after I trigger DMA ch0 as well as GPIO2_DR after writing values to GPIO_DR_TOGGLE using DMA. I also checked the DMA_ES register which reports no error from DMA. What else could be stopping me from loading values into GPIO2 registers? I am aware that 32 bits transfers are required.
 
Try running this. It may not answer all your questions, but the LED does blink and reading the register shows the bit changing. Hopefully that helps?

Code:
#include <Arduino.h>
#include <imxrt.h>
const int gpio = LED_BUILTIN;
static volatile uint32_t mode = (0b1 << 3);
static volatile uint32_t toggle = (0b1 << 3);

void setup() {
  while (!Serial) {
    ;// wait for Arduino Serial Monitor to be ready
  }
  IOMUXC_GPR_GPR27 &= ~(0b1 << 3);
  CCM_CCGR0 |= CCM_CCGR0_GPIO2(CCM_CCGR_ON);//turn on gpio2 clock
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 0b101; //IOMuxing for pin 10 0b101 for alt5 GPIO, 0b011 for alt3 PCS[0]
  CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON); // enables DMA clock if not already enabled
  GPIO2_GDIR |= mode;

  __enable_irq();
  DMAMUX_CHCFG0 = ((0b1 << 31) | (0b1 << 30));
  IMXRT_DMA_TCD[0].CITER = 1;
  IMXRT_DMA_TCD[0].BITER = 1;
  IMXRT_DMA_TCD[0].NBYTES = 4;
  IMXRT_DMA_TCD[0].SADDR = &mode;
  IMXRT_DMA_TCD[0].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
  IMXRT_DMA_TCD[0].SLAST = 0; // Source address adjustment after major loop completion
  IMXRT_DMA_TCD[0].DADDR = &GPIO2_GDIR;
  IMXRT_DMA_TCD[0].DOFF = 0;
  IMXRT_DMA_TCD[0].DLASTSGA = 0;
  IMXRT_DMA_TCD[0].ATTR = DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_SSIZE(2); //source data transfer size 32bit, d data transfer size 32bit
  IMXRT_DMA_TCD[0].CSR = 0; //set major channel linking to ch4
  DMA_SERQ = 0;
  DMA_SSRT = 1;

  DMAMUX_CHCFG1 = ((0b1 << 31) | (0b1 << 30));
  IMXRT_DMA_TCD[1].CITER = 1;
  IMXRT_DMA_TCD[1].BITER = 1;
  IMXRT_DMA_TCD[1].NBYTES = 4;
  IMXRT_DMA_TCD[1].SADDR = &toggle;
  IMXRT_DMA_TCD[1].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
  IMXRT_DMA_TCD[1].SLAST = 0; // Source address adjustment after major loop completion
  IMXRT_DMA_TCD[1].DADDR = &GPIO2_DR_TOGGLE;
  IMXRT_DMA_TCD[1].DOFF = 0;
  IMXRT_DMA_TCD[1].DLASTSGA = 0;
  IMXRT_DMA_TCD[1].ATTR = DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_SSIZE(2); //source data transfer size 32bit, d data transfer size 32bit
  IMXRT_DMA_TCD[1].CSR = 0; //set major channel linking to ch4
  DMA_SERQ = 1;
  Serial.println(GPIO2_GDIR, BIN);
}
void loop() {
  delay(1000);
  DMA_SSRT = 1;
  Serial.print("Reading");
  Serial.println(GPIO2_DR, BIN);
  //GPIO2_DR_TOGGLE = (0b1 << 3); // non dma access to pin13
}
 
To answer this question:

GPIO2_GDIR is always 0 even after I trigger DMA ch0

I'm pretty sure the problem is writing the wrong value to DMA_SSRT. You configured IMXRT_DMA_TCD[0] but wrote DMA_SSRT = 1. You need to write zero to manually trigger channel 0.

Here's a copy that doesn't "cheat" by writing to GPIO2_GDIR normally. I believe this is using both DMA channels the way you intended.

Getting DMA working is tricky because so many little details all need to be just right for it to have any useful effect. Hopefully this can get you over that initial challenge.

Code:
#include <Arduino.h>
#include <imxrt.h>
const int gpio = LED_BUILTIN;
static volatile uint32_t mode = (0b1 << 3);
static volatile uint32_t toggle = (0b1 << 3);

void setup() {
  while (!Serial) {
    ;// wait for Arduino Serial Monitor to be ready
  }
  IOMUXC_GPR_GPR27 &= ~(0b1 << 3);
  CCM_CCGR0 |= CCM_CCGR0_GPIO2(CCM_CCGR_ON);//turn on gpio2 clock
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 0b101; //IOMuxing for pin 10 0b101 for alt5 GPIO, 0b011 for alt3 PCS[0]
  CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON); // enables DMA clock if not already enabled

  __enable_irq();
  DMAMUX_CHCFG0 = ((0b1 << 31) | (0b1 << 30));
  IMXRT_DMA_TCD[0].CITER = 1;
  IMXRT_DMA_TCD[0].BITER = 1;
  IMXRT_DMA_TCD[0].NBYTES = 4;
  IMXRT_DMA_TCD[0].SADDR = &mode;
  IMXRT_DMA_TCD[0].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
  IMXRT_DMA_TCD[0].SLAST = 0; // Source address adjustment after major loop completion
  IMXRT_DMA_TCD[0].DADDR = &GPIO2_GDIR;
  IMXRT_DMA_TCD[0].DOFF = 0;
  IMXRT_DMA_TCD[0].DLASTSGA = 0;
  IMXRT_DMA_TCD[0].ATTR = DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_SSIZE(2); //source data transfer size 32bit, d data transfer size 32bit
  IMXRT_DMA_TCD[0].CSR = 0; //set major channel linking to ch4
  DMA_SERQ = 0;
  DMA_SSRT = 0;

  DMAMUX_CHCFG1 = ((0b1 << 31) | (0b1 << 30));
  IMXRT_DMA_TCD[1].CITER = 1;
  IMXRT_DMA_TCD[1].BITER = 1;
  IMXRT_DMA_TCD[1].NBYTES = 4;
  IMXRT_DMA_TCD[1].SADDR = &toggle;
  IMXRT_DMA_TCD[1].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
  IMXRT_DMA_TCD[1].SLAST = 0; // Source address adjustment after major loop completion
  IMXRT_DMA_TCD[1].DADDR = &GPIO2_DR_TOGGLE;
  IMXRT_DMA_TCD[1].DOFF = 0;
  IMXRT_DMA_TCD[1].DLASTSGA = 0;
  IMXRT_DMA_TCD[1].ATTR = DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_SSIZE(2); //source data transfer size 32bit, d data transfer size 32bit
  IMXRT_DMA_TCD[1].CSR = 0; //set major channel linking to ch4
  DMA_SERQ = 1;
  Serial.println(GPIO2_GDIR, BIN);
}
void loop() {
  delay(1000);
  DMA_SSRT = 1;
  Serial.print("Reading");
  Serial.println(GPIO2_DR, BIN);
  //GPIO2_DR_TOGGLE = (0b1 << 3); // non dma access to pin13
}
 
To answer this question:



I'm pretty sure the problem is writing the wrong value to DMA_SSRT. You configured TCD 0 but wrote DMA_SSRT = 1. You need to write zero to manually trigger channel 0.

Here's a copy that doesn't "cheat" by writing to GPIO2_GDIR normally. I believe this is using both DMA channels the way you intended. Getting DMA working is tricky because so many little details all need to be just right for it to have any useful effect. Hopefully this code can get you over that initial challenge.

Code:
#include <Arduino.h>
#include <imxrt.h>
const int gpio = LED_BUILTIN;
static volatile uint32_t mode = (0b1 << 3);
static volatile uint32_t toggle = (0b1 << 3);

void setup() {
  while (!Serial) {
    ;// wait for Arduino Serial Monitor to be ready
  }
  IOMUXC_GPR_GPR27 &= ~(0b1 << 3);
  CCM_CCGR0 |= CCM_CCGR0_GPIO2(CCM_CCGR_ON);//turn on gpio2 clock
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 0b101; //IOMuxing for pin 10 0b101 for alt5 GPIO, 0b011 for alt3 PCS[0]
  CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON); // enables DMA clock if not already enabled

  __enable_irq();
  DMAMUX_CHCFG0 = ((0b1 << 31) | (0b1 << 30));
  IMXRT_DMA_TCD[0].CITER = 1;
  IMXRT_DMA_TCD[0].BITER = 1;
  IMXRT_DMA_TCD[0].NBYTES = 4;
  IMXRT_DMA_TCD[0].SADDR = &mode;
  IMXRT_DMA_TCD[0].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
  IMXRT_DMA_TCD[0].SLAST = 0; // Source address adjustment after major loop completion
  IMXRT_DMA_TCD[0].DADDR = &GPIO2_GDIR;
  IMXRT_DMA_TCD[0].DOFF = 0;
  IMXRT_DMA_TCD[0].DLASTSGA = 0;
  IMXRT_DMA_TCD[0].ATTR = DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_SSIZE(2); //source data transfer size 32bit, d data transfer size 32bit
  IMXRT_DMA_TCD[0].CSR = 0; //set major channel linking to ch4
  DMA_SERQ = 0;
  DMA_SSRT = 0;

  DMAMUX_CHCFG1 = ((0b1 << 31) | (0b1 << 30));
  IMXRT_DMA_TCD[1].CITER = 1;
  IMXRT_DMA_TCD[1].BITER = 1;
  IMXRT_DMA_TCD[1].NBYTES = 4;
  IMXRT_DMA_TCD[1].SADDR = &toggle;
  IMXRT_DMA_TCD[1].SOFF = 0; // Adjustment to the source address made after every minor loop iteration
  IMXRT_DMA_TCD[1].SLAST = 0; // Source address adjustment after major loop completion
  IMXRT_DMA_TCD[1].DADDR = &GPIO2_DR_TOGGLE;
  IMXRT_DMA_TCD[1].DOFF = 0;
  IMXRT_DMA_TCD[1].DLASTSGA = 0;
  IMXRT_DMA_TCD[1].ATTR = DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_SSIZE(2); //source data transfer size 32bit, d data transfer size 32bit
  IMXRT_DMA_TCD[1].CSR = 0; //set major channel linking to ch4
  DMA_SERQ = 1;
  Serial.println(GPIO2_GDIR, BIN);
}
void loop() {
  delay(1000);
  DMA_SSRT = 1;
  Serial.print("Reading");
  Serial.println(GPIO2_DR, BIN);
  //GPIO2_DR_TOGGLE = (0b1 << 3); // non dma access to pin13
}
It works now, that is the problem. Thank you very much for the help :)
 
Back
Top