This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP432P401R:通过 SPI 的 DMA 散聚问题

Guru**** 2049780 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/736064/msp432p401r-issue-with-dma-scatter-gather-over-spi

器件型号:MSP432P401R

我正在使用散聚模式将 SPI 传感器与 DMA 连接。 我在这里一定会错过一些东西。

为了从传感器 FIFO 中提取192个字节、我填充了一个任务表、每个任务表都是 DMA_ControlTable 类型的元素。 此类表如下所示;其中385个条目、需要大约6 KB、但我有足够的内存:

/* SPI 读取 DMA 序列*/
DMA_ControlTable SpiAccReadoutSEQ[N_of _ACC_DMA_tasks]; 

该序列倾向于重复相同的任务、只需更改几个参数;因此我编写了以下代码以快速初始化任务列表:

// SPI 读取任务
DMA_ControlTable SendAccReadoutAddr = DMA_TaskStructEntry (1、UDMA_SIZE_8、
UDMA_SRC_INC_NONE、&AccOutputAddr、
UDMA_DST_INC_NONE、&SPI_Module_Address->TXBUF、
UDMA_ARB_1、(UDMA_MODE_MEM_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
DMA_ControlTable SendFirstDummyByte = DMA_TaskStructEntry (1、UDMA_SIZE_8、
UDMA_SRC_INC_NONE、&dummyTx、
UDMA_DST_INC_NONE、&SPI_Module_Address->TXBUF、
UDMA_ARB_1、(UDMA_MODE_PER_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
DMA_ControlTable SendDummyByte = DMA_TaskStructEntry (1、UDMA_SIZE_8、
UDMA_SRC_INC_NONE、&dummyTx、
UDMA_DST_INC_NONE、&SPI_Module_Address->TXBUF、
UDMA_ARB_1、(UDMA_MODE_MEM_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
DMA_ControlTable 检索字节 FromAcc = DMA_TaskStructEntry (1、UDMA_SIZE_8、
UDMA_SRC_INC_NONE、&SPI_Module_Address->RXBUF、
UDMA_DST_INC_NONE、&AccDataBuffer[0]、
UDMA_ARB_1、(UDMA_MODE_PER_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
DMA_ControlTable 检索 LastByteFromAcc = DMA_TaskStructEntry (1、UDMA_SIZE_8、
UDMA_SRC_INC_NONE、&SPI_Module_Address->RXBUF、
UDMA_DST_INC_NONE、&AccDataBuffer[ACC_DATA_Buffer_LEN - 1]、
UDMA_ARB_1、UDMA_MODE_BASIC);

SpiAccReadoutSEQ[0]= SendAccReadoutAddr;
SpiAccReadoutSEQ[1]= SendFirstDummyByte;
SpiAccReadoutSEQ[2]=检索字节从 Acc;
对于(I = 3;I < N_of _ACC_DMA_tasks;I += 2)
{
SpiAccReadoutSEQ[i]= SendDummyByte;
if (i = N_of _acc_dma_tasks - 2)
中断;
SpiAccReadoutSEQ[i+1]=检索字节从 Acc;
SpiAccReadoutSEQ[i+1].dstEndAddr =&AccDataBuffer[(I-1)/2];
}
SpiAccReadoutseq[N_of _ACC_DMA_tasks - 1]=获取 LastByteFromAcc; 

DMA 的配置如下:

  • 控制表:我使用6个通道以及替代通道、因此根据 uDMA 寻址方案、我分配了16个插槽:
/* DMA 控制表*/
#if defined (__TI_Compiler_version__)
#pragma DATA_ALIGN (DmaControlTable,1024)
#Elif defined (__IAR_SYSTEMS_ICC__)
#pragma DATA_ALIGNing=1024
#Elif defined (__GNUC__)
__(aligned ((aligned (__IAR_systems_cus))))__dmaControlTable_241024_ad_ad_(dirt_arm_en_en_ador_en_en_en_(#elift_(_status_(1024
)


  • 外设初始化(我保留了正在使用的其他通道的跟踪、不管怎样、它们工作正常):
//启用 DMA
DMA_enableModule();

//分配控制表
DMA_setControlBase (DmaControlTable);

//配置 DMA 传输
DMA_setChannelControl (DMA_Wired_UART_TX_CH|UDMA_PRI_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_ARB_NONE | UDMA_INC_1);
DMA_setChannelControl (DMA_Wired_UART_RX_CH|UDMA_PRI_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_ARB_8 | UDMA_INC_1);
DMA_setChannelControl (DMA_OPTIC_UART_TX_CH|UDMA_PRI_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_ARB_NONE | UDMA_INC_1);
DMA_setChannelControl (DMA_OPTIC_UART_RX_CH|UDMA_PRI_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_ARC_8 | UDMA_INC_1);
DMA_setChannelControl (DMA_append_packet_ch|UDMA_PRI_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_ARB_8 | UDMA_INC_1024);

//分配通道
dma_assignChannel (dma_optical_uart_TX_CH);
DMA_assignChannel (DMA_wired_UART_TX_CH);
dma_assignChannel (dma_optical_uart_RX_CH);
DMA_assignChannel (DMA_wired_UART_RX_CH);
DMA_assignChannel (DMA_SPI_TX_CH);
DMA_assignChannel (DMA_SPI_RX_CH);
dma_assignChannel (dma_append_packet_CH);

//清除 IFG 寄存器
DMA_Channel->INT0_CLRFLG = 0xFFFFFFFF;

//将中断分配给特定的 DMA ISR
DMA_assignInterrupt (INT_DMA_SPI、DMA_SPI_RX_CH_NUM);

//在启用前清除特定的 DMA 中断标志
DMA_clearInterruptFlag (DMA_SPI_RX_CH 和0x0F);

//启用 SPI DMA 中断
DMA_enableInterrupt (INT_DMA_SPI); 
  • 调用传感器读取过程:
    void FetchInertialData (SpiDeviceHandle *句柄)
    {
    //超时计数器
    volatile uint16_t Timeout;
    
    //在开始数据传输之前,等待一些时间
    超时= handle->CsInterval_T;
    while (--Timeout);
    
    //预载建立时间
    超时= handle->setup_T;
    
    //清除 SPI Rx 中断标志
    Handle->SpiModuleAddress->IFG &=~SPI_Receive_interrupt_FLAG;
    
    //降低 CS
    GPIO_setOutputLowOnPin (handle->CsPort、handle->CsPin);
    
    //等待建立时间
    while (--Timeout);
    
    //在散聚模式下执行数据提取
    if (handle =&AccSpiHandle)
    DMA_setChannelScatterGather (DMA_SPI_RX_CH、N_of _ACC_DMA_tasks、SpiAccReadoutSeq、0);
    DMA_enableChannel (DMA_SPI_RX_CH_NUM);
    DMA_requestSoftwareTransfer (DMA_SPI_RX_CH_NUM);
    } 

SPI 外设工作正常、可在启动时读取和写入传感器设置。 当我尝试通过 DMA 获取外部 FIFO 内容时、会出现问题:MOSI 和 MISO 不移动、并且在示波器上对 SCK 脉冲进行计数、我只看到10字节的传输尝试。 DMA_INT1正确触发、显然太早。 我怀疑触发器有问题、有些任务只是重叠、但我现在看不出原因。

是否有线索?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Giovanni、
    您能否解释一下您希望如何触发散聚模式中的任务以及您为什么要使用软件传输 API? 散聚模式面临的一个挑战是、只允许使用一个外部触发器。 您的任务可以自动触发(上一个任务完成后)、也可以从所选的外部触发器手动触发。 在您的代码中、您好像在尝试使用软件触发器、RX 触发器和自动触发器。

    由于您将手动触发器配置为 RX IFG、因此您需要手动设置 RX IFG 以启动散聚任务序列。 让我看看我是否能找到一个示例。

    此致、
    Chris
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好 Chris、

    让我检查一下我是否掌握了散聚触发器背后的概念:

    • 当 DMA 通道在散聚模式下与外设关联时、如果相应的中断标志变为高电平、则会照常触发该通道。
    • 选择"0"作为  DMA_setChannelScatterGather 函数的 isPeriphSG 参数、我们声明通道备用数据结构必须使用下一个任务自动更新、而无需触发。
    • 任务可以是 MEM_散 射收集类型、在将其加载到备用数据结构后立即执行、也可以是 PER_散 射收集类型、该结构会一直等到通道相关中断标志为高电平。
    • 在 SPI 读取情况下、最佳选项是将虚拟字节传输任务触发器设置为自动(MEM)、并使用 SPI_Receive_interrupt_FLAG 来触发字节提取任务(PER)。

    任务列表长度有何限制?

    查看我发布的跟踪、可以清楚地看到软件触发器实际上会触发某种操作(SCLK 运行很长时间并且输入了 DMA ISR)。 我之所以选择软件触发器、是因为我无法通过手动驱动中断标志来看到任何效果、这是启动自动模式存储器到存储器 DMA 传输的典型方法、因为任务加载就是这样。 我重新组织了任务列表、并根据上面的四个点更正了代码、但我仍然无法检索数据、数据传输似乎甚至没有开始。

    让我们来回顾一下我要做的事情、我一定犯了一些愚蠢的错误、这些错误会一直隐藏在明暗之中。

    • 首先:控制表。 对于六个通道、它有16个(=nextpow2 (12))条目、因为通道#5在执行散聚时还需要使用替代数据结构。 此处还定义了虚拟字节、输出数据缓冲区和 FIFO 地址的位置。
      #define ACC_DATA_buffer_LEN inertial_FIFO_DEPTH * accumor_active_Axs * inertial_data_size
      
      /* DMA 控制表*/
      #if defined (__TI_Compiler_version__)
      #pragma DATA_ALIGN (dma_CONTRAL_ACT_REFERENCE Table、1024)
      #Elif defined (__Seq_systems_ma_ic_)
      #if (_accordinatured_dma_dma_dma_trador_dma_dma_tras_tras_tras_ination_
      
      
      
      
      
      
      
      
      (#_dma_dma_trador_dma_dma_dma_dma_deltas_dma_dma_dma_dma_dma_dma_dma_dma_tagc)*
      
      
      (#define)* 24)
      
      /*与 DMA 相关的存储器位置和常数*/
      const uint8_t dummyTx = 0x00;
      volatile uint8_t dummyRx;
      
      /*传感器输出地址*/
      const uint8_t AccReadOutputAddr = LIS3DSH_OUT_X_L_ADDR |(1 << 7); 
    • 表/通道分配:
      #define DMA_SPI_RX_CH DMA_CH5_EUSCIB1RX1
      #define DMA_SPI_RX_INT_FLAG DMA_INT0_SRCFG_CH5
      #define DMA_SPI_RX_CH_NUM DMA_CHANNEL_5
      
      //启用 DMA
      DMA_enableModule ();
      
      //分配控制表
      DMA_setControlBase (DmaControlTable);
      
      //分配通道
      //[...]
      DMA_assignChannel (DMA_SPI_RX_CH); 
    • 中断设置:
      #define INT_DMA_SPIINT_DMA_INT1
      
      //清除 IFG 寄存器
      DMA_Channel->INT0_CLRFLG = 0xFFFFFFFF;
      
      //将中断分配给特定的 DMA ISR
      DMA_enableInterrupt (INT_DMA_SPI、DMA_SPI_RX_CH_NUM);
      
      //在启用
      CH_clearInterrupt (INT_DMA_INT_INT_ENABLETE)之前清除特定的 DMA 中断标志;
      
      
      
      
      
      
      
      
      //
      启用中断(INT_AMA_INT_INT_INT_INT_INT_INT_INT_INT_INT_Interrupt (INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_CONTINT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_INT_
      
      
      // main() IDLE 例程
      while (1)
      {PCM_gotoLPM0InterruptSafe()
      ;
      } 
    • 任务列表初始化:
      #define N_OV_ACC_DMA_Tasks 2 * inertial_FIFO_DEPTH * accorize_active_Axs * inertial_data_size + 3
      
      SpiAccReadoutSeq[0]= SendAccReadoutAddr;
      SpiAccoutSeq[1]= ClearRxIfg;
      for (i = 2;i < N_OV_ACC_DMA_tases - 1)
      
      SpiAccReadoutSEQ[i]= SendDummyByte;
      SpiAccReadoutSEQ[i+1]=检索字节从 Acc;
      SpiAccReadoutSEQ[i+1].dstEndAddr =&AccDataBuffer[(I-2)/2];
      }
      SpiReadoutSEQ[N_of _ACC_DMA_tases - 1]= DummyMemMove; 

    • 任务定义:
      // SPI 读取任务
      DMA_ControlTable SendAccReadoutAddr = DMA_TaskStructEntry (1、UDMA_SIZE_8、
      UDMA_SRC_INC_NONE、&AccReadOutputAddr、
      UDMA_DST_INC_NONE、&SPI_Module_Address->TXBUF、
      UDMA_ARB_1、(UDMA_MODE_MEM_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
      DMA_ControlTable ClearRxIfg = DMA_TaskStructEntry (1、UDMA_SIZE_8、
      UDMA_SRC_INC_NONE、&SPI_Module_Address->RXBUF、
      UDMA_DST_INC_NONE、&dummyRx、
      UDMA_ARB_1、(UDMA_MODE_PER_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
      DMA_ControlTable SendDummyByte = DMA_TaskStructEntry (1、UDMA_SIZE_8、
      UDMA_SRC_INC_NONE、&dummyTx、
      UDMA_DST_INC_NONE、&SPI_Module_Address->TXBUF、
      UDMA_ARB_1、(UDMA_MODE_MEM_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
      DMA_ControlTable 检索字节 FromAcc = DMA_TaskStructEntry (1、UDMA_SIZE_8、
      UDMA_SRC_INC_NONE、&SPI_Module_Address->RXBUF、
      UDMA_DST_INC_NONE、&AccDataBuffer[0]、
      UDMA_ARB_1、(UDMA_MODE_PER_SP散 射_收集+ UDMA_MODE_ALT_SELECT);
      DMA_ControlTable DummyMemMove = DMA_TaskStructEntry (1、UDMA_SIZE、
      UDMA_SRC_INC_NONE、&dummyTx、
      UDMA_DST_INC_NONE、&dummyRx、
      UDMA_ARB_1、UDMA_MODE_BASIC); 
    • 读取功能:
      #define SPI_RECEIVE_INTERRUPT_FLAG EUSCI_B_IFG_RXIFG
      
      void FetchInertialData (SpiDeviceHandle *句柄)
      {
      //超时计数器
      volatile uint16_t Timeout;
      
      //在开始数据传输之前,等待一些时间
      超时= handle->CsInterval_T;
      while (--Timeout);
      
      //预载建立时间
      超时= handle->setup_T;
      
      //降低 CS
      GPIO_setOutputLowOnPin (handle->CsPort、handle->CsPin);
      
      //等待建立时间
      while (--Timeout);
      
      //在散聚模式下执行数据提取
      if (handle =&AccSpiHandle)
      DMA_setChannelScatterGather (DMA_SPI_RX_CH、N_of _ACC_DMA_tasks、SpiAccReadoutSeq、0);
      
      DMA_enableChannel (DMA_SPI_RX_CH_NUM);
      
      handle->SpiModuleAddress->IFG |= SPI_Receive_interrupt_FLAG;
      //dma_requestSoftwareTransfer (dma_spi_RX_ch_NUM);
      } 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我想您已经准确地描述了散聚触发。  我认为、查看 TRM 中的图表也很有帮助。

    任务数的限制为256。 这意味着主设备实际上被设置为1024、执行4次传输(从任务列表到备用数据结构) 256次。

    在表/通道分配中、您选择了 DMA_CH5_EUSCIB1RX1。 这是一个无效的 SPI 通道。 RX1/TX1、RX2/TX2和 RX3/TX3只适用于 I2C 从地址 UCBxI2COA1、UCBxI2COA2和 UCBxI2COA3。 仅适用于 UART 和 SPI、RX0/TX0适用。

    希望这有所帮助、

    Chris

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    非常感谢!

    我不会在一百万年后发现这个问题、手册中记录了 SPI/UART 和 I2C DMA 触发器之间的区别在哪里?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    这是文档中已知的遗漏。 我将与团队合作解决这一问题。

    此致、
    Chris
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Chris:


    我知道我可以直接在试验电路板上进行测试、但您可以通过最快、最可靠的方式为我提供这些信息:
    对于 SPI 模式中使用的 UCAx 外设、DMA 通道分配是否有任何限制?
    在这种情况下、如果没有 I2C 选项、则 UART 和 SPI 的 DMA 触发器是相同的、对吧?

    在我的新系统设计中、我需要更多的串行链路、因此我不能像我以前一直在做的那样仅依赖 UCB SPI 外设。

    非常感谢和问候

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    对于在 SPI 模式中使用的 UCAx 外设的分配没有限制。 您正确、UART 和 SPI 的触发器是相同的。

    此致、
    Chris