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

Thread: Teensy4.1 Serial RX to DMA problem

  1. #1
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14

    Teensy4.1 Serial RX to DMA problem

    I'm trying to transfer the data received by UART to RAM by DMA, but it fails.

    When I execute the code below and enter 4 characters from the terminal software, an unintended result is output.

    Code:
    #include "DMAChannel.h"
    
    unsigned char  DMA_TxBuf[50];          //transfer buffer 50
    unsigned char  DMA_RxBuf[50];         //receive buffer  50
    
    DMAChannel dmachannel1;
    void INT_DMA1(void)
    {
      int i;
      dmachannel1.clearInterrupt();
      
      for(i=0;i<8;i++){
      Serial8.printf("%d\n\r",DMA_RxBuf[i]);
      }
    }
    
    void DMA_Init(void)
    {
    //*****************DMA1****************************************************
      dmachannel1.begin();
      dmachannel1.source((uint8_t&)LPUART5_DATA);
      dmachannel1.destinationBuffer(DMA_RxBuf,8);
      dmachannel1.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART5_RX);
      dmachannel1.interruptAtCompletion();
      dmachannel1.attachInterrupt(INT_DMA1);
    
      dmachannel1.enable();
    }
    
    void setup() {
    
      Serial8.begin(115200);           //115200kbps
       LPUART5_BAUD|=0x100000;           //RDMAE
      DMA_Init();
    
      Serial8.printf("Hellow!!\n\r");
    }
    
    void loop() {
    
      
    }

    The execution result will be like this...

    Hellow!!
    97
    255
    115
    255
    100
    255
    102
    255

    I typed in 4 numbers, so I want the RAM to contain 4 numbers, but for some reason 255 is inserted.
    I want someone to point out my mistake or correct it.

  2. #2
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14
    DMA in Teensy 4.1 is hopelessly difficult.
    I wander around the labyrinth but I was able to escape from it temporarily.
    I succeeded in receiving 8 bytes from the serial.

    Code:
    /*
        Receive 8 bytes from serial 8 and transfer to RAM with DMA
        Use a 4-byte FIFO buffer for reception
      
     */
    #include "DMAChannel.h"
    
    //unsigned char  DMA_TxBuf[50];          //transfer buffer 50
    unsigned char  DMA_RxBuf[50];         //receive buffer  50
    
    DMAChannel dmachannel1,dmachannel2;
    
    void INT_DMA1(void)
    {
      dmachannel1.clearInterrupt();
      dmachannel1.clearComplete();
    
      dmachannel1.disable();
      dmachannel2.enable();
    
    }
    
    void INT_DMA2(void)
    {
      int i;
      dmachannel2.clearInterrupt();
      dmachannel2.clearComplete();
    
      dmachannel2.disable();
      dmachannel1.enable();
      for(i=0;i<8;i++){
      Serial8.printf("%c\n\r",DMA_RxBuf[i]);
      }
    }
    
    void DMA_Init(void)
    {
    //*****************DMA1****************************************************
      dmachannel1.begin();
      dmachannel1.source((uint8_t&)LPUART5_DATA);
      dmachannel1.destinationBuffer(DMA_RxBuf,4);
      dmachannel1.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART5_RX);
      dmachannel1.interruptAtCompletion();
      dmachannel1.attachInterrupt(INT_DMA1);
    
    //  dmachannel1.transferSize(4);    // Set the data size used for each triggered transfer
    //  dmachannel1.transferCount(1);   // Set the number of transfers (number of triggers until complete)
      
      dmachannel1.TCD->CITER = 1;
      dmachannel1.TCD->BITER = 1;
      dmachannel1.TCD->NBYTES = 4;
    
    //*****************DMA2****************************************************
      dmachannel2.begin();
      dmachannel2.source((uint8_t&)LPUART5_DATA);
      dmachannel2.destinationBuffer(&DMA_RxBuf[4],4);
      dmachannel2.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART5_RX);
      dmachannel2.interruptAtCompletion();
      dmachannel2.attachInterrupt(INT_DMA2);
    
    //  dmachannel2.transferSize(4);    // Set the data size used for each triggered transfer
    //  dmachannel2.transferCount(1);   // Set the number of transfers (number of triggers until complete)
      
      dmachannel2.TCD->CITER = 1;
      dmachannel2.TCD->BITER = 1;
      dmachannel2.TCD->NBYTES = 4;
      
      dmachannel1.enable(); 
    //  dmachannel2.enable();
    }
    
    void setup() {
       Serial8.begin(256000);           //256000kbps
       LPUART5_CTRL =0xC0000;            //only RE,TE
       LPUART5_FIFO|=0x08;              //RXFE(Receive FIFO Enable)  
       LPUART5_BAUD|=0x200000;         //RDMAE(Receiver Full DMA Enable)
       LPUART5_WATER|=0x30000;         //WATER MARK changed
       DMA_Init();
       Serial8.printf("Hellow!!Input 8 char!!\n\r");
    }
    
    void loop() {
    }

  3. #3
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    11,462
    Sorry not sure what your goals here and why necessary to convert Serail8 over to DMA...
    There are several different ways to do this. Personally I would not use two DMAChannels for this.
    Can probably do it with just one DMAChannel ro with one channel and multiple DMASettings which is what I often do.

    That is with DMASettings, you can setup each setting to chain to the next one, and optionally have them setup to interrupt on completion and/or half completion like the main DMAChannel.

    There are several examples of this for different devices. For example I use that to output full frames of images over SPI to some different displays. Some of them are installed as part of Teensyduino.
    For example if you look at the ST7735_t3 code: https://github.com/KurtE/ST7735_t3/b...5_t3.cpp#L4337
    You will see where I am setting up the chain of settings. Then you simply copy the first item of the chain into your one Channel and then start up the channel.

    Good luck

  4. #4
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14
    Thank you for your help KurtE-san.
    In order to communicate with other microcomputers, I need to send 8 bytes and then receive 8 bytes.
    These communications need to use very little CPU resources.
    In addition, communication is very high speed (20 Mbps).


    I am dissatisfied with generating an interrupt every 4 bytes.
    So as you say, it would be great if I could process 8 bytes in one setting.

    I will refer to the sample code you showed.

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,735
    Just out of curiosity: Which devices transmit at 20MHz?

  6. #6
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14
    I have succeeded in increasing the serial of Teensy 4.1 to 20Mbps.

    https://forum.pjrc.com/threads/67150...787#post278787

    The other devices are PIC32MZ and RX72T.

  7. #7
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Posts
    854
    I’m curious why you don’t just receive the 8 bytes from one DMAChannel instead of chaining 2 together that read 4 bytes each?

  8. #8
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14
    This is because the serial receive FIFO buffer is 4 bytes.
    DMA trigger when the 4-byte FIFO buffer is full.

    I think that is the most efficient ...

  9. #9
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Posts
    854
    Quote Originally Posted by MO_YA_NE View Post
    This is because the serial receive FIFO buffer is 4 bytes.
    DMA trigger when the 4-byte FIFO buffer is full.

    I think that is the most efficient ...
    That is true, but it's not the end of the story, I see you had these lines in your code so you may or may not have experimented with them too much:
    Code:
    //  dmachannel1.transferSize(4);    // Set the data size used for each triggered transfer
    //  dmachannel1.transferCount(1);   // Set the number of transfers (number of triggers until complete)
    The transferSize of course corresponds to the 4 bytes of the FIFO buffer, but you can set the transferCount to more than one and it won't trigger the interrupt until it completes the number supplied to transferCount, in this case it would be 2 for a total of 8 bytes. Of course you also have to change the 4 bytes to 8 bytes in your destinationBuffer as well.

  10. #10
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    11,462
    Quote Originally Posted by MO_YA_NE View Post
    This is because the serial receive FIFO buffer is 4 bytes.
    DMA trigger when the 4-byte FIFO buffer is full.

    I think that is the most efficient ...
    I agree with vjmuzik, that you should be able to do it with one DMAChannel, with simply updating the count to 2.

    However not really sure if doing the 4 byte DMA transfer versus DMA out each byte as it is received. will make much difference? Maybe but for sure a lot less overhead than using an ISR call for each 4 bytes.

  11. #11
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14
    Quote Originally Posted by vjmuzik View Post
    The transferSize of course corresponds to the 4 bytes of the FIFO buffer, but you can set the transferCount to more than one and it won't trigger the interrupt until it completes the number supplied to transferCount, in this case it would be 2 for a total of 8 bytes. Of course you also have to change the 4 bytes to 8 bytes in your destinationBuffer as well.
    Do you suggest code like this?
    The FIFO buffer capacity was set to 1 byte. This works fine.

    Code:
    /*
        Receive 8 bytes from serial 8 and transfer to RAM with DMA
        Use a 4-byte FIFO buffer for reception
      
     */
    #include "DMAChannel.h"
    
    //unsigned char  DMA_TxBuf[50];          //transfer buffer 50
    unsigned char  DMA_RxBuf[50];         //receive buffer  50
    
    DMAChannel dmachannel1,dmachannel2;
    
    void INT_DMA1(void)
    {
      int i;
      
      dmachannel1.clearInterrupt();
      dmachannel1.clearComplete();
    
    //  dmachannel1.disable();
    //  dmachannel2.enable();
    
      for(i=0;i<8;i++){
      Serial8.printf("%c\n\r",DMA_RxBuf[i]);
      }
      
    }
    
    void INT_DMA2(void)
    {
      int i;
      dmachannel2.clearInterrupt();
      dmachannel2.clearComplete();
    
      dmachannel2.disable();
      dmachannel1.enable();
      for(i=0;i<8;i++){
      Serial8.printf("%c\n\r",DMA_RxBuf[i]);
      }
    }
    
    void DMA_Init(void)
    {
    //*****************DMA1****************************************************
      dmachannel1.begin();
      dmachannel1.source((uint8_t&)LPUART5_DATA);
      dmachannel1.destinationBuffer(DMA_RxBuf,8);
      dmachannel1.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART5_RX);
      dmachannel1.interruptAtCompletion();
      dmachannel1.attachInterrupt(INT_DMA1);
    
      dmachannel1.transferSize(1);    // Set the data size used for each triggered transfer
      dmachannel1.transferCount(8);   // Set the number of transfers (number of triggers until complete)
      
    //  dmachannel1.TCD->CITER = 1;
    //  dmachannel1.TCD->BITER = 1;
    //  dmachannel1.TCD->NBYTES = 4;
    /*
    //*****************DMA2****************************************************
      dmachannel2.begin();
      dmachannel2.source((uint8_t&)LPUART5_DATA);
      dmachannel2.destinationBuffer(&DMA_RxBuf[4],4);
      dmachannel2.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART5_RX);
      dmachannel2.interruptAtCompletion();
      dmachannel2.attachInterrupt(INT_DMA2);
    
    //  dmachannel2.transferSize(4);    // Set the data size used for each triggered transfer
    //  dmachannel2.transferCount(1);   // Set the number of transfers (number of triggers until complete)
      
      dmachannel2.TCD->CITER = 1;
      dmachannel2.TCD->BITER = 1;
      dmachannel2.TCD->NBYTES = 4;
     */
      dmachannel1.enable(); 
    //  dmachannel2.enable();
    }
    
    void setup() {
       Serial8.begin(256000);           //256000kbps
       LPUART5_CTRL =0xC0000;            //only RE,TE
       LPUART5_FIFO|=0x08;              //RXFE(Receive FIFO Enable)  
       LPUART5_BAUD|=0x200000;         //RDMAE(Receiver Full DMA Enable)
    //   LPUART5_WATER|=0x30000;         //WATER MARK set to 3
       LPUART5_WATER&=~0x30000;         //WATER MARK set to 0
       DMA_Init();
       Serial8.printf("Hellow!!Input 8 char!!\n\r");
    }
    
    void loop() {
    }
    Last edited by MO_YA_NE; 07-29-2021 at 02:11 PM.

  12. #12
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    11,462
    Again usually I try to keep things like this simple. Assuming you have the watermark set to 0.
    Code:
      dmachannel1.begin();
      dmachannel1.source((uint8_t&)LPUART5_DATA);
      dmachannel1.destinationBuffer(DMA_RxBuf,8);
      dmachannel1.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART5_RX);
      dmachannel1.interruptAtCompletion();
      dmachannel1.attachInterrupt(INT_DMA1);
      dmachannel1.enable();
    Also depending on your needs, you can have it interrupt at end, disable at end ...

    Hopefully at that point the sizes will be correct.
    If not you might need to add: dmachannel1.transferCount(8);
    Transfer size should already be correct

  13. #13
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14
    Quote Originally Posted by KurtE View Post
    Again usually I try to keep things like this simple.
    Hopefully at that point the sizes will be correct.
    If not you might need to add: dmachannel1.transferCount(8);
    Transfer size should already be correct
    O.K.
    I understand how it works little by little.
    I got rid of the extra code.
    This works enough.

    Code:
    /*
        Receive 8 bytes from serial8 and transfer to RAM with DMA
     */
    #include "DMAChannel.h"
    
    unsigned char  DMA_RxBuf[50];         //receive buffer  50
    
    DMAChannel dmachannel1;
    
    void INT_DMA1(void)
    {
      int i;
      
      dmachannel1.clearInterrupt();
      dmachannel1.clearComplete();
    
      for(i=0;i<8;i++){
      Serial8.printf("%c\n\r",DMA_RxBuf[i]);
      }
      
    }
    
    
    void DMA_Init(void)
    {
    //*****************DMA1****************************************************
      dmachannel1.begin();
      dmachannel1.source((uint8_t&)LPUART5_DATA);
      dmachannel1.destinationBuffer(DMA_RxBuf,8);
      dmachannel1.triggerAtHardwareEvent(DMAMUX_SOURCE_LPUART5_RX);
      dmachannel1.interruptAtCompletion();
      dmachannel1.attachInterrupt(INT_DMA1);
      dmachannel1.enable(); 
    }
    
    void setup() {
       Serial8.begin(256000);           //256000kbps
       LPUART5_CTRL =0xC0000;           //only RE,TE
       LPUART5_FIFO|=0x08;              //RXFE(Receive FIFO Enable)  
       LPUART5_BAUD|=0x200000;         //RDMAE(Receiver Full DMA Enable)
       LPUART5_WATER&=~0x30000;         //WATER MARK set to 0
       
       DMA_Init();
       Serial8.printf("Hellow!!Input 8 char!!\n\r");
    }
    
    void loop() {
    }

  14. #14
    I don't understand the connection between the Serial8.begin and the LPUART5.
    LPUART6 or 8 if used in place of the 5 strings does not work, but 5 does.
    I can find no documentation in the IMXRT1062 that shows the connection or how the LPUARTx relates to the Serial ports on the teensy 4.x processors.
    Do you know or did you get this working just by testing?
    Thanks in advance.

  15. #15
    Junior Member MO_YA_NE's Avatar
    Join Date
    May 2021
    Location
    Japan
    Posts
    14
    Hi,Mike.
    I was confused at first, but Teensy4's HW serial uses registers with different numbers.
    See HardwareSerialX.h in the teensy4 folder.(X=1~8)
    (C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy4)

    You should know which number of registers to use in that file.

  16. #16
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    11,462
    Quote Originally Posted by Mike Maurice View Post
    I don't understand the connection between the Serial8.begin and the LPUART5.
    LPUART6 or 8 if used in place of the 5 strings does not work, but 5 does.
    I can find no documentation in the IMXRT1062 that shows the connection or how the LPUARTx relates to the Serial ports on the teensy 4.x processors.
    Do you know or did you get this working just by testing?
    Thanks in advance.
    Serial8 is an Arduino concept where all of the SerialX are of the class HardwareSerial.

    But underlying each one is a Hardware device lpusart: See chapter 49 IMXRT manual.

    In this case Serial8 is built using LpUart5...
    You can find out this information a few different ways: You can look at what IO pins you are talking to and then map those through the IMXRT reference...
    Which I have in my spreadsheets like:
    Click image for larger version. 

Name:	screenshot.jpg 
Views:	51 
Size:	275.0 KB 
ID:	25550
    Which you can see Alt1 for those pins are LPUART5 RX and TX pins...
    Or you can look at sources as the serial objects are table driven: SO looking at HardwareSerial8.cpp you will see:
    Code:
    static HardwareSerial::hardware_t UART5_Hardware = {
    	7, IRQ_LPUART5, &IRQHandler_Serial8, 
    	&serialEvent8, &_serialEvent8_default,
    	CCM_CCGR3, CCM_CCGR3_LPUART5(CCM_CCGR_ON),
        {{34,1, &IOMUXC_LPUART5_RX_SELECT_INPUT, 1}, {48, 2, &IOMUXC_LPUART5_RX_SELECT_INPUT, 0}},
        {{35,1, &IOMUXC_LPUART5_TX_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
    
    	50, // CTS pin
    	2, //  CTS
    	IRQ_PRIORITY, 38, 24, // IRQ, rts_low_watermark, rts_high_watermark
    	XBARA1_OUT_LPUART5_TRG_INPUT
    };
    HardwareSerial Serial8(&IMXRT_LPUART5, &UART5_Hardware, tx_buffer8, SERIAL8_TX_BUFFER_SIZE,
    	rx_buffer8,  SERIAL8_RX_BUFFER_SIZE);
    #endif
    Several hints in that table including the one in RED

Posting Permissions

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