Teensy 4.0 DMA over GPIO

Status
Not open for further replies.

Dishwasha

New member
I am trying to transfer data over raw GPIO via DMA and have been unsuccessful. I don't want to use SPI or any other protocol. I have poured through the forums and still something is alluding me.

Code:
#include "DMAChannel.h"

DMAChannel myDMA1 = new DMAChannel();
DMAChannel myDMA2 = new DMAChannel();

unsigned char cbuf[6], c1 = 15;
unsigned char tbuf[6] = {1,2,3,4,5,6}, t1 = 20;

void p(void) {
        unsigned int i;
        Serial.print("c1=");
        Serial.println(c1);
        Serial.print("cbuf=");
        for (i=0; i<sizeof(cbuf); i++) {
                Serial.print(cbuf[i]);
                Serial.print(", ");
        }
        Serial.print("\nt1=");
        Serial.println(t1);
        Serial.print("tbuf=");
        for (i=0; i<sizeof(tbuf); i++) {
                Serial.print(tbuf[i]);
                Serial.print(", ");
        }

        Serial.println();
        Serial.println();
}

void interrupt1(void) {
        myDMA1.clearInterrupt();
        Serial.println("Interrupt1 ");
}

void interrupt2(void) {
        myDMA2.clearInterrupt();
        Serial.println("Interrupt2 ");
}

void setup() {
        pinMode(0, OUTPUT);
        pinMode(2, INPUT);
        while (!Serial) ;
        delay(100);
        Serial.println("DMA Test");

        myDMA1.sourceBuffer(tbuf, sizeof(tbuf));
        myDMA1.destination(CORE_PIN0_PINREG);
        myDMA1.interruptAtCompletion();
        myDMA1.transferCount(1);
        myDMA1.attachInterrupt(interrupt1);
        myDMA1.enable();

        myDMA2.source(CORE_PIN2_PINREG);
        myDMA2.destinationBuffer(cbuf, sizeof(cbuf));
        myDMA2.interruptAtCompletion();
        myDMA2.transferCount(1);
        myDMA2.triggerAtHardwareEvent(CORE_PIN2_PINREG);
        myDMA2.attachInterrupt(interrupt2);
        myDMA2.enable();

        delay(100);

        myDMA1.triggerManual();
 }

void loop() {
        delay(1000);
        p();
}

If I set myDMA1 to a destinationBuffer, the interrupt gets called but if I try to use the GPIO register it does not.
 
I’m also keen to try and DMA to the GPIO, but I think the T4 digital I/O pins don’t correspond to bits in a single register so can be written to with a single write. I’m not sure how useful DMA is with GPIO
 
Forgot to update. I discovered a while ago there was a whole world missing from me because I didn't have the SDK from NXP. After acquiring that, I was able to discover that the full speed GPIO capabilities have some caveats that made using this chip version untenable for my needs. Will keep my eye on the future generations.
 
For an advanced view of 1062's DMA and GPIO see : (arduino)\hardware\teensy\avr\libraries\OctoWS2811\OctoWS2811_imxrt.cpp

There are some other comments - but this is the biggest one:
Code:
// Ordinary RGB data is converted to GPIO bitmasks on-the-fly using
// a transmit buffer sized for 2 DMA transfers.  The larger this setting,
// the more interrupt latency OctoWS2811 can tolerate, but the transmit
// buffer grows in size.  For good performance, the buffer should be kept
// smaller than the half the Cortex-M7 data cache.
 
Yes it would be great if there was some tutorial or web page, that discuss DMA for the different Teensy boards. There have been several threads talking about DMA with GPIO on T4.x, that you can get a lot of information from. Likewise as mentioned by @defragster the OctoWS2811 has an example of it for doing DMA output to GPIO pins.

Recently a few of us were playing with input from GPIO pins using DMA. I know some of that discussion was in the thread: https://forum.pjrc.com/threads/63195-Problem-trying-to-read-OV7670-camera-under-IRQ-Teensy-4-0 At that time a few of us was playing with reading in camera data using DMA. My later stuff was using T4.1, which had the benefit of more continuous GPIO pins on a port.
Again maybe not much help, but some of the PIP (Play in Progress) was in the file: https://github.com/KurtE/Arduino_OV...lay_ili9341_t3n/CameraDisplay_ili9341_t3n.ino

As for DMA and GPIO and XBAR and Timers and ???
With these subsystems you should have a copy of the IMXRT reference manual (PDF), which you can download from the Teensy product pages.

There are Several aspects to this including:

DMA - as with most Teensy code doing DMA, look at the #include <DMAChannel.h>
file for information.


GPIO Ports - The T4.x - The pins are actually on Two logical ports:
There are ports: 1-5 which if you look at the reference manual all of the pins actually belong to. And for example my Excel documents refer to. Then there are the High speed versions of these: 6-9.
Where Port 1 maps to Port 6... Port 2 maps to Port 7.
There are IOMUXC registers which bitmap value says which of the pins on the logical ports are on which port. Example:(IOMUXC_GPR_GPR26)
Code:
GPIO1 and GPIO6 share same IO MUX function, GPIO_MUX1 selects one GPIO function.
This register controls GPIO_MUX1 to select GPIO1 or GPIO6. For bit n,
• 0: GPIO1[n] is selected;
• 1: GPIO6[n] is selected.
At startup time, the startup.c code switches all pins to the ports 6-9... BUT DMA only works with the Ports 1-5... So you need to map those pins you are trying to do DMA to or from back down...

Note with ports you are talking to all 32 bits of the port registers. So you writing to a port register could update additional pins that are on that same port. Although if you only map those you are actually doing DMA to, the impact can be reduced. Also your choice of which register also impacts things. That is:
the DR: Is the data register that you may likely use to do output to all pins on port. PSR: is the Pad status register which you are likely to do DMA inputs from. But for output you may also use ones that only Set or Clear or Toggle the pins which have a high value (DR_SET, DR_CLEAR, DR_TOGGLE)

XBAR - Is simply a logical trigger router. For now XBAR1. Never seen any usages of XBAR2 or XBAR3... Note: XBAR1 also has IO pins that can be used with it as well.
It's main purpose in these cases, is to route whatever it is that is going to drive when the DMAs should happen to or from the GPIO pins.

In the cases of the OCTWS... Paul is using three of the four timers on Quad timer 4 to time when the DMA operations should happen. He is then routing these timers to special locations and configuring which edges of the trigger to use:
Code:
	CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);
	xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30);
	xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31);
	xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94);
	XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 |
		XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
	XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
Note: this setup is not specific to GPIO. We also do a very similar setup for reading from the ADC registers in the ADC library. In this case we again used QTIMER4 timers 0 and 3.
And again this is setup in the dma channel stuff. Like in the OctoWS.. you will see lines like:
Code:
	dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0);

Now with the Camera code, which for the T4 worked OK, but on T4.1 which exposed enough of the pins associated with the CSI (camera) system which was even better...
But one of the IOs coming out of the camera is the Pixel clock. so in the DMA code I mentioned earlier. We set it up such that this pin is connected to one of the IO pins on the T4.x that is an XBAR pin.
The code configured it to be XBAR mode and then used the XBAR system to again map that pin as the source to one of the DMA outputs mentioned above...

Again I know I only scratched a little of the surface of some of these subsystems. So not sure if this helps or not. But a minimum hopefully it directs you to the appropriate sections in the reference manual.
 
Status
Not open for further replies.
Back
Top