当前位置: 首页 > news >正文

(六)ASCLIN_UART模块串口DMA模式

文章目录

  • DMA简介
  • 采用DMA完成串口收发
    • mydma.c文件:
    • 简单分析:
    • 大致流程:
    • 注意事项:
    • mydma.h文件:
    • 主函数调用:
      • 测试现象1:
      • 测试现象2:
      • 测试现象3:
  • 简单补充几个其它配置的测试
    • 修改发送的测试
    • 修改接收的测试


DMA简介

DMA(Direct Memory Access,直接存储器访问)是一种在不需要CPU干预的情况下,实现外设与存储器之间 或 存储器与存储器之间高速数据传输的技术。DMA的主要目的是减轻CPU的负担,使其能够专注于更复杂的计算和控制任务

简单理解:俩地址之间进行数据搬运,不需要CPU干涉

可以有软件触发DMA进行搬运,也可以硬件触发DMA搬运,取决于配置。

比如:(五)ASCLIN_UART模块串口中断模式 中采用的是借助串口的接收发送中断进行数据传输的。这里可以配置DMA硬件触发,将串口的接收/发送中断,路由到DMA上,每一次触发串口的中断,就触发一次DMA搬运即可。

当然DMA配置很复杂,可以直接借助iLLD库函数进行配置,应用为王。直接分析代码。

采用DMA完成串口收发

mydma.c文件:

#include "IfxAsclin_Asc.h"

#include "IfxDma_Dma.h"
#include "IfxDma.h"

#include "Bsp.h"
#include "string.h"

/*********************************************************************************************************************/
/*------------------------------------------------------Macros-------------------------------------------------------*/
/*********************************************************************************************************************/
#define UART_BAUDRATE           460800                                  /* UART baud rate in bit/s                  */

#define UART_PIN_RX             IfxAsclin2_RXE_P33_8_IN                 /* UART receive port pin                    */
#define UART_PIN_TX             IfxAsclin2_TX_P33_9_OUT                 /* UART transmit port pin                   */

#define DMA_CHANNEL_RX             INTPRIO_ASCLIN2_RX
#define DMA_CHANNEL_TX             INTPRIO_ASCLIN2_TX

IfxDma_Dma_Channel g_rxchn;                                             /* DMA channel handle                       */
IfxDma_Dma_Channel g_txchn;

/* Definition of the interrupt priorities */
#define INTPRIO_ASCLIN2_TX      11                                       /* Triggered when AscLin transmits         */
#define INTPRIO_ASCLIN2_RX      12                                       /* Triggered when AscLin receives          */

#define INTPRIO_DMA_TX          13                          /* Triggered when a DMA transaction is finished         */
#define INTPRIO_DMA_RX          14                          /* Triggered when a DMA transaction is finished         */

#define UART_RX_BUFFER_SIZE     16                                      /* Definition of the receive buffer size    */
#define UART_TX_BUFFER_SIZE     16                                      /* Definition of the transmit buffer size   */


/* Declaration of the ASC handle */
static IfxAsclin_Asc g_ascHandle;

/* Declaration of the FIFOs parameters */
_Alignas(16) static  uint8 g_ascTxBuffer[UART_TX_BUFFER_SIZE];//只要开启了自增+循环,必须保证与循环范围保持一致!!!!!!
_Alignas(16) static  uint8 g_ascRxBuffer[UART_RX_BUFFER_SIZE];

/* Definition of txData  */
_Alignas(16) uint8 g_txData[] = "Hello World!";//只要开启了自增+循环,必须保证与循环范围保持一致!!!!!!

/* Size of the message */
Ifx_UReg_32Bit g_count = sizeof(g_txData)-1;

int rxcnt = 0;
int txcnt = 0;
IFX_INTERRUPT(prio_DMA_RX, 0, INTPRIO_DMA_RX);
void prio_DMA_RX(void)
{
    rxcnt++;
    IfxDma_clearChannelInterrupt(&MODULE_DMA, g_rxchn.channelId);
}

/* Interrupt triggered when the DMA finishes a transaction*/
IFX_INTERRUPT(prio_DMA_TX, 0, INTPRIO_DMA_TX);
void prio_DMA_TX(void)
{
    txcnt++;
    IfxDma_clearChannelInterrupt(&MODULE_DMA, g_txchn.channelId);
}

/* This function initializes the ASCLIN UART module */
void init_asclin_uart(void)
{
    /* Initialize an instance of IfxAsclin_Asc_Config with default values */
    IfxAsclin_Asc_Config ascConfig;
    IfxAsclin_Asc_initModuleConfig(&ascConfig, &MODULE_ASCLIN2);

    /* Set the desired baud rate */
    ascConfig.baudrate.baudrate = UART_BAUDRATE;

    /* ISR priorities and interrupt target */
    ascConfig.interrupt.txPriority = INTPRIO_ASCLIN2_TX;
    ascConfig.interrupt.rxPriority = INTPRIO_ASCLIN2_RX;
    ascConfig.interrupt.typeOfService = IfxSrc_Tos_cpu0 ;

    /* FIFO configuration */  //没用到串口接收/发送的缓存可以不绑定,直接删除即可(如出现问题,可以直接删除)
    ascConfig.txBuffer = &g_ascTxBuffer;
    ascConfig.txBufferSize = UART_TX_BUFFER_SIZE;
    ascConfig.rxBuffer = &g_ascRxBuffer;
    ascConfig.rxBufferSize = UART_RX_BUFFER_SIZE;

    /* Pin configuration */
    const IfxAsclin_Asc_Pins pins =
    {
        NULL_PTR,       IfxPort_InputMode_pullUp,     /* CTS pin not used */
        &UART_PIN_RX,   IfxPort_InputMode_pullUp,     /* RX pin           */
        NULL_PTR,       IfxPort_OutputMode_pushPull,  /* RTS pin not used */
        &UART_PIN_TX,   IfxPort_OutputMode_pushPull,  /* TX pin           */
        IfxPort_PadDriver_cmosAutomotiveSpeed1
    };
    ascConfig.pins = &pins;

    IfxAsclin_Asc_initModule(&g_ascHandle, &ascConfig); /* Initialize module with above parameters */

    /* Modification of the TOS for the Rx related interruption */
    /* Change from CPU0 (previously defined above) to DMA */
    volatile Ifx_SRC_SRCR *src;
    src = IfxAsclin_getSrcPointerTx(ascConfig.asclin);

    /* Assign DMA as Service Provider when INTPRIO_ASCLIN2_TX is triggered */
    IfxSrc_init(src, IfxSrc_Tos_dma, INTPRIO_ASCLIN2_TX);
    IfxAsclin_enableTxFifoFillLevelFlag(ascConfig.asclin, TRUE);
    IfxSrc_enable(src);

    /* Modification of the TOS for the Rx related interruption */
    /* Change from CPU0 (previously defined above) to DMA */
    src = IfxAsclin_getSrcPointerRx(ascConfig.asclin);
    /* Assign DMA as Service Provider when INTPRIO_ASCLIN2_RX is triggered */
    IfxSrc_init(src, IfxSrc_Tos_dma, INTPRIO_ASCLIN2_RX);
    IfxAsclin_enableRxFifoFillLevelFlag(ascConfig.asclin, TRUE);
    IfxSrc_enable(src);
}

/* This function is called from main in order to initialize the DMA module */
void init_dma(void)
{
    /* Initialize an instance of IfxDma_Dma_Config with default values */
    IfxDma_Dma_Config dmaConfig;
    IfxDma_Dma_initModuleConfig(&dmaConfig, &MODULE_DMA);

    /* Initialize module */
    IfxDma_Dma dma;
    IfxDma_Dma_initModule(&dma, &dmaConfig);

    /* Initial configuration for all channels */
    IfxDma_Dma_ChannelConfig cfg;
    IfxDma_Dma_initChannelConfig(&cfg, &dma);

    /* Following configuration is used by the DMA channel */
    cfg.moveSize = IfxDma_ChannelMoveSize_8bit;
    cfg.blockMode = IfxDma_ChannelMove_1;

    /*********************************************** TX部分**************************************************************/

    cfg.transferCount = 0;//一次触发搬运多少个字节数据(后面写入的时候会修改)
    /* DMA completes a full transaction on requests */
    cfg.requestMode = IfxDma_ChannelRequestMode_oneTransferPerRequest;  //一个请求触发一个单一DMA传输 即请求启动单个事务

    /* DMA as Interrupt Service Provider */
    cfg.hardwareRequestEnabled = TRUE;                         //  使能UART发送中断触发Dma(硬件触发)
    /* DMA channel stays enabled after one request */
    cfg.operationMode = IfxDma_ChannelOperationMode_single;   //DMA单次搬运

    /*************** Source and destination addresses ***************/
    cfg.sourceCircularBufferEnabled = TRUE;                           //开启源地址自增
    cfg.sourceAddressIncrementStep = IfxDma_ChannelIncrementStep_1;  //源地址自增加+1
    cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_16; //源地址16字节循环

    cfg.destinationCircularBufferEnabled = TRUE;                     //开启目的地址自增
    cfg.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_none; //目的地址循环范围为0(就是原地不动,即使上面开启了也是在原地踏步)等同于上面FALSE

    /*************** Channel specific configurations ***************/
    /* Select the Channel 11, related to the interruption on AscLin TX */
    cfg.channelId = (IfxDma_ChannelId) DMA_CHANNEL_TX;
    /* Address of the UART TX FIFO */
    cfg.sourceAddress = (uint32)&g_ascTxBuffer;
    cfg.destinationAddress = (uint32) &g_ascHandle.asclin->TXDATA.U;

    /*  DMA中断部分(如果采用环形缓存区,就没必要开启了哈)           */
    cfg.channelInterruptEnabled = TRUE;
    /* DMA triggers an interrupt once the full transaction is done */
    cfg.channelInterruptControl = IfxDma_ChannelInterruptControl_thresholdLimitMatch;
    /* Priority of the channel interrupt trigger */
    cfg.channelInterruptPriority = INTPRIO_DMA_TX;
    /* Interrupt service provider */
    cfg.channelInterruptTypeOfService = IfxSrc_Tos_cpu0;

    IfxDma_Dma_initChannel(&g_txchn, &cfg);


    /********************************************************* RX部分***********************************************************/
    cfg.transferCount = 1;   //一次触发搬运多少个字节数据
    /* DMA completes a full transaction on requests */
    cfg.requestMode = IfxDma_ChannelRequestMode_completeTransactionPerRequest;  //一个请求触发一个完整事务 即请求启动完整的事务

    /* DMA as Interrupt Service Provider */
    cfg.hardwareRequestEnabled = TRUE;                         // UART接收中断触发Dma

    /* DMA channel stays enabled after one request */
    cfg.operationMode = IfxDma_ChannelOperationMode_continuous; // DMA连续搬运模式

    /*************** Source and destination addresses ***************/
    cfg.sourceCircularBufferEnabled = TRUE;                 //源地址自增
    cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_none;源地址循环范围为0(就是原地不动,即使上面开启了也是在原地踏步)等同于上面FALSE

    cfg.destinationCircularBufferEnabled = TRUE;
    cfg.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_16;

    /*************** Channel specific configurations ***************/
    /* Select the Channel 12, related to the interruption on AscLin RX */
    cfg.channelId = (IfxDma_ChannelId) DMA_CHANNEL_RX;
    /* Address of the UART RX FIFO */
    cfg.sourceAddress = (uint32) &g_ascHandle.asclin->RXDATA.U;
    cfg.destinationAddress = (uint32)&g_ascRxBuffer;

    /* DMA中断部分(如果采用环形缓存区,就没必要开启了哈)  */
    cfg.channelInterruptEnabled = TRUE;
    /* DMA triggers an interrupt once the full transaction is done */
    cfg.channelInterruptControl = IfxDma_ChannelInterruptControl_thresholdLimitMatch;
    /* Priority of the channel interrupt trigger */
    cfg.channelInterruptPriority = INTPRIO_DMA_RX;
    /* Interrupt service provider */
    cfg.channelInterruptTypeOfService = IfxSrc_Tos_cpu0;

    IfxDma_Dma_initChannel(&g_rxchn, &cfg);
}

void send_data(char* g_txData,Ifx_UReg_32Bit len)   //发送数据--指定长度大小
{
    Ifx_DMA_CH *channel = g_txchn.channel;

    IfxDma_disableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    channel->SADR.U   = (uint32)g_txData;
    channel->CHCFGR.B.TREL     = len;
    IfxDma_enableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    IfxDma_Dma_startChannelTransaction(&g_txchn);
   //g_ascHandle.asclin->FLAGSSET.B.TFLS = 1;  //发送完成设置,用于通知其它程序的,可以不设置
}

void send_1s_pack(void) //对上面 send_data封装而已
{
    send_data((char *)g_txData,g_count);
    waitTime(IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, 1000));
}

void recive_data(void)    //发送接收到的数据缓存区的数据--需要指定数据长度(一般和整个接收缓存数组长度一致)
{
    Ifx_DMA_CH *channel = g_txchn.channel;
    IfxDma_disableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    channel->SADR.U   = (uint32)&g_ascRxBuffer;
    channel->CHCFGR.B.TREL     = 16;
    IfxDma_enableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    IfxDma_Dma_startChannelTransaction(&g_txchn);
    //g_ascHandle.asclin->FLAGSSET.B.TFLS = 1; //发送完成设置,用于通知其它程序的,可以不设置
    waitTime(IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, 1000));
}

简单分析:

编写DMA俩个通道的中断:

int rxcnt = 0;
int txcnt = 0;
IFX_INTERRUPT(prio_DMA_RX, 0, INTPRIO_DMA_RX);
void prio_DMA_RX(void)
{
    rxcnt++;
    IfxDma_clearChannelInterrupt(&MODULE_DMA, g_rxchn.channelId);
}

/* Interrupt triggered when the DMA finishes a transaction*/
IFX_INTERRUPT(prio_DMA_TX, 0, INTPRIO_DMA_TX);
void prio_DMA_TX(void)
{
    txcnt++;
    IfxDma_clearChannelInterrupt(&MODULE_DMA, g_txchn.channelId);
}

初始化ASCLIN的UART模块代码和之前的一模一样,多加了串口中断路由到DMA请求触发上面,也就是本来串口中断CPU0来提供服务,现在变成了DMA(就是串口触发DMA搬运,):

    /* Modification of the TOS for the Rx related interruption */
    /* Change from CPU0 (previously defined above) to DMA */
    volatile Ifx_SRC_SRCR *src;
    src = IfxAsclin_getSrcPointerTx(ascConfig.asclin);

    /* Assign DMA as Service Provider when INTPRIO_ASCLIN2_TX is triggered */
    IfxSrc_init(src, IfxSrc_Tos_dma, INTPRIO_ASCLIN2_TX);
    IfxAsclin_enableTxFifoFillLevelFlag(ascConfig.asclin, TRUE);
    IfxSrc_enable(src);

    /* Modification of the TOS for the Rx related interruption */
    /* Change from CPU0 (previously defined above) to DMA */
    src = IfxAsclin_getSrcPointerRx(ascConfig.asclin);
    /* Assign DMA as Service Provider when INTPRIO_ASCLIN2_RX is triggered */
    IfxSrc_init(src, IfxSrc_Tos_dma, INTPRIO_ASCLIN2_RX);
    IfxAsclin_enableRxFifoFillLevelFlag(ascConfig.asclin, TRUE);
    IfxSrc_enable(src);

接着初始化DMA,里面采用的还是,初始化DMA配置参数的结构体,然后初始化DMA模块,再初始化配置DMA通道的结构体,根据需求,修改对应的结构体成员配置,然后调用DMA通道初始化函数进行初始化(这里配置很多哈)。当然DMA也可以开启搬运完成的中断。

void init_dma(void)
{
    /* Initialize an instance of IfxDma_Dma_Config with default values */
    IfxDma_Dma_Config dmaConfig;
    IfxDma_Dma_initModuleConfig(&dmaConfig, &MODULE_DMA);

    /* Initialize module */
    IfxDma_Dma dma;
    IfxDma_Dma_initModule(&dma, &dmaConfig);

    /* Initial configuration for all channels */
    IfxDma_Dma_ChannelConfig cfg;
    IfxDma_Dma_initChannelConfig(&cfg, &dma);
    /* Following configuration is used by the DMA channel */
    cfg.moveSize = IfxDma_ChannelMoveSize_8bit;
    cfg.blockMode = IfxDma_ChannelMove_1;
    cfg.transferCount = 0;
    
//..........

    IfxDma_Dma_initChannel(&g_rxchn, &cfg);
}

封装的发送函数,就是将源地址设为字节要发送的数组,然后根据数组个数,设置DMA要传输多少次数据,然后触发DMA即可。

void send_data(char* g_txData,Ifx_UReg_32Bit len)   //发送数据--指定长度大小
{
    Ifx_DMA_CH *channel = g_txchn.channel;

    IfxDma_disableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    channel->SADR.U   = (uint32)g_txData;
    channel->CHCFGR.B.TREL     = len;
    IfxDma_enableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    IfxDma_Dma_startChannelTransaction(&g_txchn);
   //g_ascHandle.asclin->FLAGSSET.B.TFLS = 1;  //发送完成设置,用于通知其它程序的,可以不设置
}

void send_1s_pack(void) //对上面 send_data封装而已
{
    send_data((char *)g_txData,g_count);
    waitTime(IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, 1000));
}

编写接收并发送的回环(16字节接收缓存数组,就发送16字节):

void recive_data(void)    //发送接收到的数据缓存区的数据--需要指定数据长度(一般和整个接收缓存数组长度一致)
{
    Ifx_DMA_CH *channel = g_txchn.channel;
    IfxDma_disableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    channel->SADR.U   = (uint32)&g_ascRxBuffer;
    channel->CHCFGR.B.TREL     = 16;
    IfxDma_enableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    IfxDma_Dma_startChannelTransaction(&g_txchn);
    //g_ascHandle.asclin->FLAGSSET.B.TFLS = 1; //发送完成设置,用于通知其它程序的,可以不设置
    waitTime(IfxStm_getTicksFromMilliseconds(BSP_DEFAULT_TIMER, 1000));
}


大致流程:

1.初始化ASCLIN的UART模块:

    /* Initialize an instance of IfxAsclin_Asc_Config with default values */
    IfxAsclin_Asc_Config ascConfig;
    IfxAsclin_Asc_initModuleConfig(&ascConfig, &MODULE_ASCLIN2);

    /* Set the desired baud rate */
    ascConfig.baudrate.baudrate = UART_BAUDRATE;

    /* ISR priorities and interrupt target */
    ascConfig.interrupt.txPriority = INTPRIO_ASCLIN2_TX;
    ascConfig.interrupt.rxPriority = INTPRIO_ASCLIN2_RX;
    ascConfig.interrupt.typeOfService = IfxSrc_Tos_cpu0 ;

    /* FIFO configuration */
    ascConfig.txBuffer = &g_ascTxBuffer;
    ascConfig.txBufferSize = UART_TX_BUFFER_SIZE;
    ascConfig.rxBuffer = &g_ascRxBuffer;
    ascConfig.rxBufferSize = UART_RX_BUFFER_SIZE;

    /* Pin configuration */
    const IfxAsclin_Asc_Pins pins =
    {
        NULL_PTR,       IfxPort_InputMode_pullUp,     /* CTS pin not used */
        &UART_PIN_RX,   IfxPort_InputMode_pullUp,     /* RX pin           */
        NULL_PTR,       IfxPort_OutputMode_pushPull,  /* RTS pin not used */
        &UART_PIN_TX,   IfxPort_OutputMode_pushPull,  /* TX pin           */
        IfxPort_PadDriver_cmosAutomotiveSpeed1
    };
    ascConfig.pins = &pins;
    IfxAsclin_Asc_initModule(&g_ascHandle, &ascConfig); /* Initialize module with above parameters */

2.,再初始化完成串口后,需将CPU0服务串口中断,改成DMA服务串口中断(即将中断路由到DMA上):

    /* Modification of the TOS for the Rx related interruption */
    /* Change from CPU0 (previously defined above) to DMA */
    volatile Ifx_SRC_SRCR *src;
    src = IfxAsclin_getSrcPointerTx(ascConfig.asclin);

    /* Assign DMA as Service Provider when INTPRIO_ASCLIN2_TX is triggered */
    IfxSrc_init(src, IfxSrc_Tos_dma, INTPRIO_ASCLIN2_TX);
    IfxAsclin_enableTxFifoFillLevelFlag(ascConfig.asclin, TRUE);
    IfxSrc_enable(src);

    /* Modification of the TOS for the Rx related interruption */
    /* Change from CPU0 (previously defined above) to DMA */
    src = IfxAsclin_getSrcPointerRx(ascConfig.asclin);
    /* Assign DMA as Service Provider when INTPRIO_ASCLIN2_RX is triggered */
    IfxSrc_init(src, IfxSrc_Tos_dma, INTPRIO_ASCLIN2_RX);
    IfxAsclin_enableRxFifoFillLevelFlag(ascConfig.asclin, TRUE);
    IfxSrc_enable(src);

3.初始化DMA模块(先初始化参数,直接用默认参数初始化DMA模块):

    /* Initialize an instance of IfxDma_Dma_Config with default values */
    IfxDma_Dma_Config dmaConfig;
    IfxDma_Dma_initModuleConfig(&dmaConfig, &MODULE_DMA);

    /* Initialize module */
    IfxDma_Dma dma;
    IfxDma_Dma_initModule(&dma, &dmaConfig);

4.配置DMA通道,先初始化配置参数,以及配置有点公共参数(当然公共参数,也可以分别给TX/RX通道各自,自己再配置一遍哈),这里配置DMA一次搬运8位也就是一个字节,只使用一个DMA进行搬运

    /* Initial configuration for all channels */
    IfxDma_Dma_ChannelConfig cfg;
    IfxDma_Dma_initChannelConfig(&cfg, &dma);
    /* Following configuration is used by the DMA channel */
    cfg.moveSize = IfxDma_ChannelMoveSize_8bit;
    cfg.blockMode = IfxDma_ChannelMove_1;

接下里配置TX/RX各自通道,这里配置参数,还有一样的配置,也被单独拎出来了,只是为了方便理解,甚至可以定义俩变量配置,当然也完全可以不重复配置,

5.配置TX的DMA通道部分:

每配置一步细节
(1)配置一次触发DMA搬运多少个数据,以及配置每次 DMA 请求只触发一次数据传输。每次接收到一个请求信号时,DMA 控制器只会执行单次的数据传输。一般配合DMA单次搬运模式使用(这个就是串口发送中断,触发一个字节发送,DMA搬运一个)。
(2)使能DMA硬件触发(这里就是串口的发送中断触发DMA)
(3)DMA单次搬运模式(就是触发一次DMA,搬运一次)
(4)开启源地址自增模式,设置源地址自增+1,设置源地址16字节循环(这三个是配套使用,这里可以理解在DMA在每搬运一个数据后,DMA源地址指针,自动往后+1,然后达到16字节后,又回到第一个字节位置)这里注意, 一旦开启了循环模式,定义的数组一定要对齐到相应的循环范围,比如16字节循环,那定义的数组要16字节对齐,比如代码里的:(_Alignas(16) uint8 g_txData[] = “Hello World!”; //只要开启了自增+循环,必须保证与循环范围保持一致!!!!!!)
(5)开启目的地地址自增模式,设置目的地址自增范围为0字节循环(这里虽然开启了自增+循环模式,但是也是在原地不动,因为范围是0,圈住了,好似不开启自增模式也就是FALSE模式, 其实不然,即使不开启自增模式,也就是FALSE模式,那么地址依然还是会自增的,但是每一次启动DMA搬运,DMA指针得重新指向头,要不然就是自增后的地址往后了,这样会造成指针越界问题,后面会验证,说白了这个循环模式,就是用来防止指针越界的,可以这么理解)
(6)设置DMA发送的通道号,设置源地址,以及目的地地址
(7)开启DMA中断==(如果开启,一定要编写中断服务函数,否则程序崩溃)==
(8)最后初始化通道配置。
    /*********************************************** TX部分**************************************************************/

    cfg.transferCount = 0;//一次触发搬运多少个字节数据(后面写入的时候会修改)
    /* DMA completes a full transaction on requests */
    cfg.requestMode = IfxDma_ChannelRequestMode_oneTransferPerRequest;  //单一传输

    /* DMA as Interrupt Service Provider */
    cfg.hardwareRequestEnabled = TRUE;                         //  使能UART发送中断触发Dma(硬件触发)
    /* DMA channel stays enabled after one request */
    cfg.operationMode = IfxDma_ChannelOperationMode_single;   //DMA单次搬运

    /*************** Source and destination addresses ***************/
    cfg.sourceCircularBufferEnabled = TRUE;                           //开启源地址自增
    cfg.sourceAddressIncrementStep = IfxDma_ChannelIncrementStep_1;  //源地址自增加+1
    cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_16; //源地址16字节循环

    cfg.destinationCircularBufferEnabled = TRUE;                     //开启目的地址自增
    cfg.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_none; //目的地址循环范围为0(就是原地不动,即使上面开启了也是在原地踏步)等同于上面FALSE

    /*************** Channel specific configurations ***************/
    /* Select the Channel 11, related to the interruption on AscLin TX */
    cfg.channelId = (IfxDma_ChannelId) DMA_CHANNEL_TX;
    /* Address of the UART TX FIFO */
    cfg.sourceAddress = (uint32)&g_ascTxBuffer;
    cfg.destinationAddress = (uint32) &g_ascHandle.asclin->TXDATA.U;

    /*  DMA中断部分(如果采用环形缓存区,就没必要开启了哈)           */
    cfg.channelInterruptEnabled = TRUE;
    /* DMA triggers an interrupt once the full transaction is done */
    cfg.channelInterruptControl = IfxDma_ChannelInterruptControl_thresholdLimitMatch;
    /* Priority of the channel interrupt trigger */
    cfg.channelInterruptPriority = INTPRIO_DMA_TX;
    /* Interrupt service provider */
    cfg.channelInterruptTypeOfService = IfxSrc_Tos_cpu0;

    IfxDma_Dma_initChannel(&g_txchn, &cfg);

6.配置RX的DMA通道部分:

每配置一步细节)
(1)配置一次触发DMA搬运多少个数据,以及配置DMA 请求触发一整笔事务的传输,理解就是接收到一个请求信号后,DMA 控制器会自动完成整个事务(例如,传输一个完整的数据块或一组数据,接收串口数据一般都是大量涌入的,不可能是一次发1个或者几个字节把,像那种mavlink数据包,都是很多的,因此这里配置了一次触发,一直搬运), 这里有个思考,明明串口的接收,就是一个字节触发一个中断,为什么DMA和它可以不保持一致,个人理解,没错串口触发一直在触发,DMA只要有数据也是一直在搬运的(好比这样一种场景:串口中断告诉DMA来了一个数据,然后DMA就一直在干活了,串口接收中断呢,一直在那边喊话,又来了一个数据,又来了一个数据)
(2)使能DMA硬件触发(这里就是串口的接收中断触发DMA)
(3)DMA连续搬运模式(就是触发一次DMA,一直搬运)
(4)开启源地址自增,设置地址循环范围为0字节(这里虽然开启了自增+循环模式,但是也是在原地不动,因为范围是0,圈住了,好似不开启自增模式也就是FALSE模式, 其实不然,即使不开启自增模式,也就是FALSE模式,那么地址依然还是会自增的,但是每一次启动DMA搬运,DMA指针得重新指向头,要不然就是自增后的地址往后了,这样会造成指针越界问题,后面会验证)
(5)开启目的地地址自增模式,设置目的地地址自增+1,设置目的地地址16字节循环(这三个是配套使用,这里可以理解在DMA在每搬运一个数据后,DMA目的地地址指针,自动往后+1,然后达到16字节后,又回到第一个字节位置)这里注意,==一旦开启了循环模式,定义的数组一定要对齐到相应的循环范围,比如16字节循环,那定义的数组要16字节对齐,使用关键字!!!!!! Alignas(16) C99版本还支持__attribute_ ((aligned (16)))) 目前英飞凌官方ide只支持_Alignas(16) ==

(6)设置DMA接收的通道号,设置源地址,以及目的地地址
(7)开启DMA中断==(如果开启,一定要编写中断服务函数,否则程序崩溃)==
(8)最后初始化通道配置。

 /********************************************************* RX部分***********************************************************/
    cfg.transferCount = 1;   //一次触发搬运多少个字节数据
    /* DMA completes a full transaction on requests */
    cfg.requestMode = IfxDma_ChannelRequestMode_oneTransferPerRequest;  //一个请求触发一个完整事务 即请求启动完整的事务

    /* DMA as Interrupt Service Provider */
    cfg.hardwareRequestEnabled = TRUE;                         // UART接收中断触发Dma

    /* DMA channel stays enabled after one request */
    cfg.operationMode = IfxDma_ChannelOperationMode_continuous; // DMA连续搬运模式

    /*************** Source and destination addresses ***************/
    cfg.sourceCircularBufferEnabled = TRUE;                 //源地址自增
    cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_none;源地址循环范围为0(就是原地不动,即使上面开启了也是在原地踏步)等同于上面FALSE

    cfg.destinationCircularBufferEnabled = TRUE;
    cfg.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_16;

    /*************** Channel specific configurations ***************/
    /* Select the Channel 12, related to the interruption on AscLin RX */
    cfg.channelId = (IfxDma_ChannelId) DMA_CHANNEL_RX;
    /* Address of the UART RX FIFO */
    cfg.sourceAddress = (uint32) &g_ascHandle.asclin->RXDATA.U;
    cfg.destinationAddress = (uint32)&g_ascRxBuffer;

    /* DMA中断部分(如果采用环形缓存区,就没必要开启了哈)  */
    cfg.channelInterruptEnabled = TRUE;
    /* DMA triggers an interrupt once the full transaction is done */
    cfg.channelInterruptControl = IfxDma_ChannelInterruptControl_thresholdLimitMatch;
    /* Priority of the channel interrupt trigger */
    cfg.channelInterruptPriority = INTPRIO_DMA_RX;
    /* Interrupt service provider */
    cfg.channelInterruptTypeOfService = IfxSrc_Tos_cpu0;

    IfxDma_Dma_initChannel(&g_rxchn, &cfg);

然后就是编写了一个发送函数,以及接收并发送的函数,这里发送使用g_txData数组,当然也是可以使用g_ascTxBuffer数组的哈,这里只是防止和上一章混淆,不使用同一个数组而已。


注意事项:

1.只要DMA通道开启了循环模式,一定要相应的字节对齐:

比如代码DMA发送通道的配置和发送缓存区字节对齐

    cfg.sourceCircularBufferEnabled = TRUE;                           //开启源地址自增
    cfg.sourceAddressIncrementStep = IfxDma_ChannelIncrementStep_1;  //源地址自增加+1
    cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_16; //源地址16字节循环

    cfg.destinationCircularBufferEnabled = TRUE;                     //开启目的地址自增
    cfg.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_none; //目的地址循环范围为0(就是原地不动,即使上面开启了也是在原地踏步)等同于上面FALSE


_Alignas(16) uint8 g_txData[] = "Hello World!";//只要开启了自增+循环,必须保证与循环范围保持一致!!!!!!

同理DMA接收通道的配置和定义接收缓存区对齐:

    cfg.sourceCircularBufferEnabled = TRUE;                 //源地址自增
    cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_none;源地址循环范围为0(就是原地不动,即使上面开启了也是在原地踏步)等同于上面FALSE

    cfg.destinationCircularBufferEnabled = TRUE;
    cfg.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_16;
    
_Alignas(16) static  uint8 g_ascRxBuffer[UART_RX_BUFFER_SIZE];

2.如果启动了DMA中断,一定要编写相应的中断服务函数(可以为空,但不能不写);

3.设置DMA源地址,目标地址配置时,一定要检查好,否则会出现问题。

4.配置串口时,默认参数不可以,一定要改,比如过采因子,采样点等。

mydma.h文件:

#ifndef __MYDMA_H_
#define __MYDMA_H_


void init_asclin_uart(void);                    /* Initialization function                                          */
void init_dma(void);

void send_receive_ASCLIN_UART_message(void);    /* Send and receive function    */
void read2send(void);


void send_data(char* g_txData,Ifx_UReg_32Bit len);   //发送数据--指定长度大小
void send_1s_pack(void);
void recive_data(void) ;   //发送接收到的数据缓存区的数据--需要指定数据长度(一般和整个接收缓存数组长度一致)


#endif

主函数调用:

调用send_1s_pack();是1s的“Hello world”的心跳包;
调用:recive_data();1s发送16字节的接收缓存区的数据;

#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"


#include "Blinky_LED.h"
#include "interrupt.h"
#include "mydma.h"


IFX_ALIGN(4) IfxCpu_syncEvent g_cpuSyncEvent = 0;

void core0_main(void)
{
    IfxCpu_enableInterrupts();

    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdogs and service them periodically if it is required
     */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());

    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);
    initLED();
    initGtmTom();
    init_asclin_uart();
    init_dma();

    while(1)
    {
        send_1s_pack();
        //recive_data();
    }
}

测试现象1:

调用send_1s_pack();

这里发送是每一次发送,都将DMA的指针,重新指向发送数组头部,并且设置一次搬运多少个数据。(因此这里可以不使用循环,就是上面说的FALSE模式,这个模式也是自增的,因为每次都初始化了指针指向头,不会造成指针越界问题,解决了指针越界问题,因此可以取消循环,现象也是一样的),推荐还是用循环模式,后面采用环形数组就很完美。

    cfg.sourceCircularBufferEnabled = FALSE;                           //开启源地址自增

在这里插入图片描述

然后验证,一次DMA传输个数,可以关闭send_data里面每次设置的每次DMA传输多少个数据,然后将默认参数分别改成2或5试试

void send_data(char* g_txData,Ifx_UReg_32Bit len)   //发送数据--指定长度大小
{
    Ifx_DMA_CH *channel = g_txchn.channel;

    IfxDma_disableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    channel->SADR.U   = (uint32)g_txData;
    //channel->CHCFGR.B.TREL     = len;  //这里屏蔽掉
    IfxDma_enableChannelTransaction(&MODULE_DMA, g_txchn.channelId);
    IfxDma_Dma_startChannelTransaction(&g_txchn);
   //g_ascHandle.asclin->FLAGSSET.B.TFLS = 1;  //发送完成设置,用于通知其它程序的,可以不设置
}
    cfg.transferCount = 2; //一次触发搬运多少个字节数据(后面写入的时候会修改)

改成2,现象:

txcnt是DMA中断次数哈

在这里插入图片描述
在这里插入图片描述

改成5,现象:

在这里插入图片描述
在这里插入图片描述

验证成功。

测试现象2:

调用:recive_data();

第一次发送123456:
在这里插入图片描述
第二次发送123456,可以看到依次往后了:
在这里插入图片描述

第三次发送123456,可以看到依次往后,DMA接收指针到了最后,循环回来指到头,然后覆盖了12,变成了56:
在这里插入图片描述
第四次发送123456,继续往后覆盖:
在这里插入图片描述
至此能感受到DMA循环模式的魅力,后面会使用环形换缓存区对接收发;

测试现象3:

两个同时调用,也是没问题的哈,已实测。

简单补充几个其它配置的测试

修改发送的测试

调用的是send_1s_pack();以发送为例

1.修改DMA发送通道的源地址循环范围4字节,则必须修改发送缓存数组4字节对齐:

    cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_4; 
    
_Alignas(4) uint8 g_txData[] = "Hello World!";//

可以看到重复发送了发送数组前四个字节,就是源地址指针只在前四个字节循环了,这就是循环模式。

在这里插入图片描述

2.还可以再修改发送DMA通道的循环范围0字节,则修改数组不对齐即可,当然对齐也没关系:

cfg.sourceAddressCircularRange = IfxDma_ChannelIncrementCircular_none; 

可以看到,发送的12个字节全是H,那是因为DMA发送指针,一直原地不动,发送了12个字节。
在这里插入图片描述

修改接收的测试

调用的是recive_data();以接收为例

修改DMA接收通道的目的地地址循环范围4字节,则必须修改接收缓存数组4字节对齐

    cfg.destinationAddressCircularRange = IfxDma_ChannelIncrementCircular_4;
_Alignas(4) static  uint8 g_ascRxBuffer[UART_RX_BUFFER_SIZE];

用debug看数组数值,可以看到我发送12345,然后接收数组g_ascRxBuffer里面 只有5234,那是因为目的地地址设置了4字节范围循环,其实12345都接收到了,只是5把1给覆盖了,只是接收了1234指针又回到了头部:

在这里插入图片描述

此时指针在第二个位置,然后再发一次12345,123把234覆盖了 ,然后指针又回到了头部,45又把51给覆盖,所以发送回来的是4523


http://www.dtcms.com/a/107156.html

相关文章:

  • 完美解决Tensorboard: No dashboards are active for the current data set.问题
  • 云曦3月断网考
  • 48. 旋转图像
  • 图神经网络实战(PyTorch Geometric处理学术网络)
  • Rock Pi 5B Linux虚拟串口设置方法
  • 无人机无线图像回传技术解析!
  • 如果数据包的最后一段特别短,如何处理?
  • 【GPT入门】第31课 ollama运行私有化部署的模型与调试
  • Linux:线程的同步与互斥
  • 大模型提示工程中,提示、补全、指令、上下文和样本这几个概念的区别是什么?
  • C/C++归纳2
  • with queue_lock: 是什么意思
  • 跨境贸易之常见的贸易术语(贸易模式)
  • 代码随想录第三十三天|动态规划part04--494.目标和、1049.最后一块石头的重量Ⅱ、474.一和零
  • 二叉树 —— 数据结构基础刷题路程
  • Linux驱动入门——设备树详解
  • 海外SD-WAN专线网络部署成本分析
  • Adv. Sci.:在精神分裂症中绘制大脑协同功能障碍图谱:理解个体差异和潜在的分子机制
  • 鸿蒙项目笔记(1)
  • 26考研资料分享考研资料合集 百度网盘(仅供参考学习)
  • [250330] OpenAI 发布 GPT-4o 图像生成:实用主义的图像生成时代到来 | Maple Mono v7.0 发布
  • AI 智能体(AI Agent):概念、历史、现状与展望
  • OpenManus安装部署和基础使用说明
  • 【boost搜索引擎】下
  • 鸿蒙开发踩坑记录 - 2024S1
  • PROMPT 速查
  • 对象和面向对象三大特征:封装
  • RAG 在 AI 助手、法律分析、医学 NLP 领域的实战案例
  • 并行治理机制对比:Polkadot、Ethereum 与 NEAR
  • DDR简介