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.

[参考译文] TM4C1294NCPDT:SSI UDMA 传输

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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/602658/tm4c1294ncpdt-ssi-udma-transfer

器件型号:TM4C1294NCPDT

您好!

我尝试使用 SPI 将数据从外部 ADC 传输到 TM4C。 ADC 是从器件、每次转换后发送 24个字节。 我已经配置了 SSI0 DMA TX 和 RX。 我发送一组24个虚拟字节来从 ADC 接收实际样本。 但是、出于某种原因、连续调用 RX DMA 末尾的中断函数。 此外、我会得到大量 FIFO 溢出错误和 Rx 超时错误。 无论如何、看起来 DMA 传输的运行方式非常不正常。 因此、为了找出问题、我简化了代码。 我启用了回送模式、只发送一组24个虚拟字节。 在主函数中、我只调用

ssi0_Init();
ssi0_DmaTransferSetup();
ssi0_DmaTransferStart();

但是、我观察到相同的行为、并且接收缓冲区中没有数据被写入。 到目前为止、我还没有找到代码有什么问题。 感谢您的帮助(复制如下)。

谢谢、

SINA

extern uint32_t g_systemClockFrequency;//120MHz

#define SAMPLE_buffer_size 24
uint8_t g_dummyByte = 8;
uint8_t g_rxSampleBuffer[SAMPLE_Buffer_SIZE];

uint32_t g_Overlation nbBufCount = 0;
uint32_t g_t g_rxSampleBuffer = 0;uint_h = 0


;uinteg_void



= 0;uintse_sum_0 (void = 0) void 设置= 0;uintsum_sample (void = 0)

G_rxBufCount = 0;
g_nbOverflag = 0;
g_nbTimeout = 0;

ssi0_HwSetup();
GPIOPinWrite (GPIO_PORTN_BASE、 GPIO_PIN_4、0);
GPIOPinWrite (GPIO_PORTN_BASE、GPIO_PIN_5、0);
}


void ssi0_HwSetup (void)
{
SysCtlPeripheralEnable (sysctl_Periph_GPIOA);
GPIOPinTypeSSI (GPIO_PORta_base、GPIO_PIN_2 | GPIO_PIN_5
);配置 GPIO0PIN_GPIO5
(GPIO_PIN_GPIO5);配置 GPIO0_GPIO5 (GPIO5)

SysCtlPeripheralEnable (SYSCTL_Periph_SSI0);

//等待外设就绪
(!SysCtlPeripheralReady (SYSCTL_Periph_SSI0)){}

SSIClockSourceSet (SSI0_BASE、SSI_CLOCK 系统);

// ADC_SPI_CLOCK = 4MClock_MASTER
、SSI0_SCON_CLUS_CLUS_CLUS_CLUS_CLUST_SSI0、SSIP_CLUST_SSIRFF (SSI0)
adc_data_format_bits);

SSIEnable (SSI0_BASE);

g_dummyByte = 0;
uint32_t dummy = 0;
while (SSIDataGetNonBlocking (SSI0_BASE、&dummy))}


}void ssi0_DmaTransferSetup (void)
{
//为 TX 和 RX 通道启用 uDMA 接口。
SSIDMAEnable (SSI0_BASE、SSI_DMA_TX|SSI_DMA_RX);

//DMA TX 设置
//将 UDMA SSI0TX 通道的属性置于已知状态。
uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0TX、
UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
UDMA_ATTR_HIGH_PRIOR|
UDMA_ATTR_REQMASK);


//源上没有递增,因为我们正在发送相同的虚拟字节
//由于我们要复制到同一寄存器位置,所以目的不增加
uDMAChannelControlSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE |
UDMA_ARB_8);



//DMA RX 设置
//将 UDMA SSI0RX 通道的属性置于已知状态。 这些
默认情况下、//应已禁用。
uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0RX、
UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST |
UDMA_ATTR_HIGH_PRIOR|
UDMA_ATTR_REQMASK);


//源不递增,因为我们正在读取同一个寄存器
//在每个 DMA 传输中将目标地址增加一个字节,因为我们正在写入接收缓冲区
uDMAChannelControlSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
UDMA_ARB_8);

//启用用于调试的环回
HWREG (SSI0_BASE + SSI_O_CR1)|= SSI_CR1_LBM;

//清除任何挂起的中断
SSIIntClear (SSI0_BASE、0xFF);

SSIIntEnable (SSI0_BASE、SSI_DMARX|SSI_RXOR|SSI_RXTO);

//在 NVIC 中启用中断
IntEnable (INT_SSI0);
}



void ssi0_DmaTransferStart (void)
{
uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
UDMA_MODE_BASIC、
&g_dummyByte、
(void *)(SSI0_BASE + SSI_O_DR)、
sample_buffer_size);

//最终代码:在此处获取新的缓冲区
uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
UDMA_MODE_BASIC、
(void *)(SSI0_BASE + SSI_O_DR)、
G_rxSampleBuffer、sample_buffer_size);

uDMAChannelEnable (UDMA_CHANGE_SSI0TX|UDMA_CHANGE_SSI0RX);
}

void ssi0_IntHandler (void)
{
uint32_t ui32Status;

//读取中断状态
ui32Status = SSIIntStatus (SSI0_BASE、1);

IF (ui32Status & SSI_RXOR)
{
++g_nbOverf溢;
}

IF (ui32Status 和 SSI_RXTO)
{
++g_nbTimeout;
}

//清除任何挂起状态,即使由于没有 SSI 而应该没有等待状态
//中断已启用。
SSIIntClear (SSI0_BASE、ui32Status);

//如果 DMA 通道被禁用,则表示传输已完成
if (!uDMAChannelIsEnabled (UDMA_CHANGE_SSI0RX))
{
G_rxBufCount++;
}
}

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

    我看不到您为 SSI RX 功能配置 GPIO 引脚。 您只有以下两行。 SSI0DATA0需要 PA4。 请看一下 SPI_MASTER.c 示例。

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

    除非出现错误、否则根据芯片用户指南(SPMS433B)的表17-1、SSI0RX 为 PA5。

    此致、
    SINA
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Sina、
    你是对的。 我把它们弄混了。 我建议您先解决 UDMA 问题、然后查看是否可以在不使用 UDMA 的情况下使 SSI 环回工作。 如果回送正常工作、则继续使用外部 ADC 并确认您可以从外部 ADC 接收24个字节、而无需 UDMA。 如果工作正常、则表示 SSI 设置和到外部 ADC 的硬件连接都是正确的。 然后、您将添加 UDMA 以提高性能。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Charles、

    感谢您的建议、我将尝试一下。 您是否具有与此芯片配合使用的工作示例代码以及使用 DMA 进行 TX 和 Rx 的 SSI 通信?

    此致、
    SINA
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Sina、
    我们的 DMA 示例使用 UART、而不是 SSI。 但您可以参考它。 它是 TivaWare 包中的 UDMA_DEMO。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Charles、

    我已经看到了这个代码。 但是、最好有一个使用 SSI 和 DMA 的工作示例、以确保在 Tivaware 或芯片中没有潜在问题。

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

    我在没有 DMA 的情况下尝试了 SSI 环回、它工作正常。 但是、带有 DMA 和环回的代码仍然显示与之前相同的行为。 我不认为有必要使用外部 ADC 进行测试、因为使用 DMA 的回送不起作用。

    此致、
    SINA
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Sina、
    您可能会发现这篇文章 e2e.ti.com/.../367620 对您有所帮助、其中包含用于具有 DMA 的 SSI 的示例代码。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Charles、

    我发现我的代码中有什么错误(如上所述):
    1 - uDMAChannelEnable (UDMA_CHANGE_SSI0TX|UDMA_CHANGE_SSI0RX)错误。 对于每个通道(即 uDMAChannelEnable (UDMA_CHANGE_SSI0TX)和 uDMAChannelEnable (UDMA_CHANGE_SSI0RX)、必须调用一次 uDMAChannelEnable。 奇怪的是、当我检查 UDMA DMAENASET 寄存器时、第一次对 uDMAChannelEnable 的调用会设置相关的位、但第二次调用会清除寄存器。 我对这种行为感到惊讶。 但是、这似乎不会影响 DMA 引擎的行为。 对此有什么想法吗?

    2 -如果只需要一个一次性 DMA、为了正确清除 ssi0_IntHandler 函数中的 RX 和/或 TX 中断、必须在调用 SSIIntClear 之前调用相关的 SSIDMADisable 函数、否则中断会持续触发。

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

    您好、Sina、

     您是否打算在第二次调用后清除两个通道使能位? 我建议您执行单步执行以查看在第二次调用后写入的值。 请注意、相关位在通道传输后自动清零。 如何清除 ISR 中的中断状态? 调用 SSIIntClear 后、您可以调用 SSIIntStatus 以确保真正清除标志。 Cortex-M 处理器中有一个写入缓冲器、在实际清除中断源之前、可能需要几个时钟周期。 因此、建议在中断处理程序的早期清除中断源(与最后的操作相反)、以避免在中断源实际被清除之前从中断处理程序返回。

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

    您好、Charles、

    请尝试以下代码。 在 InitSPITransfer 函数中的 uDMAChannelEnable (UDMA_CHANGE_SSI0RX)处设置断点、并检查 DMAENASET 寄存器。 当我执行这个示例代码时、DMAENASET 在调用 uDMAChannelEnable (UDMA_CHANGE_SSI0TX)后被清除。

    此致、

    SINA

    #include 
    #include 
    #include 
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "inc/hw_uart.h"
    #include "inc/hw_ssi.h"
    #include "driverlib/GPIO.h"
    
    #include "driverlib/interrupt.h"#include "driverlib/driver.h"#include "driverlib/secnecne.h/driverlib"#include "driverlib/driver.h"#include "driverlib"#driverlib/secnecnecne.h/driverlib"#include "driverlib.driverlib"#driver.h"
    
    
    
    
    
    
    
    
    //
    //
    ////系统时钟速率,单位为 Hz。
    ////
    *************
    uint32_t g_ui32SysClock;
    
    //*********
    //
    // SysTick 中断使用的每秒 SysTick 数。
    ////
    *****************
    #define SYSTICKS_PER_second 100
    
    
    
    //*************
    ////
    SSI0缓冲器
    //
    //*********
    #define SSI_buffer_size 256
    静态 uint8_t g_ui8SSITxBuf[SSI_buffer_size];
    静态 uint8_t g_ui8SSIRxBuf[SSI_buffer_size];
    
    //*********
    //
    // uDMA 错误的计数。 此值由 UDMA 错误
    //处理程序递增。
    ////
    *****************
    静态 uint32_t g_ui32uDMAErrCount = 0;
    
    
    //*********
    //
    填充的 SSI0缓冲区计数
    //
    //*********
    静态 uint32_t g_ui32SSIRxCount = 0;
    静态 uint32_t g_ui32SSITxCount = 0;
    静态 uint32_t g_nbInterrupts = 0;
    
    //*********
    //
    // uDMA 控制器使用的控制表。 此表必须与
    1024字节边界对齐//。
    ////
    *****************
    #if defined (ewarm)
    #pragma DATA_alignment=1024
    uint8_t pui8ControlTable[1024];
    #Elif Defined (CCS)
    #pragma DATA_ALIGN (pui8ControlTable、1024)
    uint8_t pui8ControlTable[1024];
    #else
    uint8_t pu8ControlTable[1024]____(dio_attend_)(_1024)
    
    
    *(_1024)(_diforitudi8ControlTable_)*(_1024)
    //
    //如果驱动程序库遇到错误,则调用的错误例程。
    ////
    *****************
    #ifdef debug
    void
    __error__(char *pcFilename、uint32_t ui32Line)
    {
    while (1);
    }
    #endif
    
    //*********
    //
    // SysTick 计时器的中断处理程序。
    每当出现适当的节拍数时、该处理程序将递增//秒计数器。 它
    //还将调用 CPU 使用率节拍函数来查找 CPU 使用率百分比。
    ////
    *****************
    void SysTickHandler (void)
    {
    
    }
    
    //*********
    //
    // uDMA 错误的中断处理程序。 如果
    // uDMA 在尝试执行传输时遇到总线错误,则会发生此中断。 此
    //处理程序仅在发生错误时递增计数器。
    ////
    *****************
    void uDMAErrorHandler (void)
    {
    uint32_t ui32Status;
    
    //
    //检查 uDMA 错误位
    //
    ui32Status = uDMAErrorStatusGet ();
    
    //
    //如果存在 uDMA 错误,则清除该错误并递增
    //错误计数器。
    //
    if (ui32状态)
    {
    uDMAErrorStatusClear ();
    G_ui32uDMAErrCount++;
    }
    }
    
    
    
    
    
    void ConfigureSSI0 (void)
    {
    uint32_t trashbin[1]={0};
    
    //
    //启用 SSI0外设。
    //
    SysCtlPeripheralEnable (SYSCTL_Periph_SSI0);
    SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA);
    
    //
    //将 GPIOA_3配置为 SSI 芯片选择
    //
    GPIOPinTypeGPIOOutput (GPIO_Porta_base、GPIO_PIN_3);
    GPIOPinWrite (GPIO_Porta_base、GPIO_PIN_3、GPIO_PIN_3);
    
    //
    //为 SSI0模式配置 GPIO 引脚。
    //
    GPIOPinConfigure (GPIO_PA2_SSI0CLK);
    GPIOPinConfigure (GPIO_PA5_SSI0XDAT1);
    GPIOPinTypeSSI (GPIO_Porta_base、GPIO_PIN_5 | GPIO_PIN_2);
    
    SSIConfigSetExpClk (SSI0_BASE、g_ui32SysClock、SSI_FRF_MOTO_MODE_2、SSI_MODE_MASTER、4000000、 8);
    
    SSIEnable (SSI0_BASE);
    
    /*清除 SSI0 RX 缓冲器*/
    while (SSIDataGetNonBlocking (SSI0_BASE、&trashbin[0]){}
    
    //启用用于调试的环回
    HWREG (SSI0_BASE + SSI_O_CR1)|= SSI_CR1_LBM;
    }
    
    空 InitSPITransfer (void)
    {
    //为 TX 和 RX 通道启用 uDMA 接口。
    //
    SSIDMAEnable (SSI0_BASE、SSI_DMA_RX | SSI_DMA_TX);
    
    //
    //uDMA SSI0 TX
    //
    
    //
    //将 UDMA SSI0TX 通道的属性置于已知状态。 这些
    默认情况下、//应已禁用。
    //
    uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0TX、
    UDMA_ATTR_ALTSELECT |
    UDMA_ATTR_HIGH_PRIOR|
    UDMA_ATTR_REQMASK);
    
    //
    //设置 UDMA SSI0TX 通道的 USEBURST 属性。 这将会
    //强制控制器在传输数据时始终使用突发
    //将 TX 缓冲器连接到 SSI0。 这是比较有效的总线使用
    //不是允许单次或突发传输的默认值。
    //
    //uDMAChannelAttributeEnable (UDMA_CHANGE_SSI0TX、UDMA_ATTR_USEBURST);
    
    //
    //配置 SSI0 TX 的控制参数。
    //
    uDMAChannelControlSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
    UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE |
    UDMA_ARB_8);
    
    
    
    
    //
    //uDMA SSI0 RX
    //
    
    //
    //将 UDMA SSI0RX 通道的属性置于已知状态。 这些
    默认情况下、//应已禁用。
    //
    uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0RX、
    UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT |
    (UDMA_ATTR_HIGH_PRIOR|
    uDMA_attr_REQMASK);
    
    uDMAChannelAssign (UDMA_CHANGE_SSI0RX);
    //
    //为配置主控制结构体的控制参数
    // SSIORX 通道。
    //
    uDMAChannelControlSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
    UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 |
    UDMA_ARB_8);
    
    
    //设置 uDMA SSI0 TX 通道的传输参数。 这将会
    //配置传输源和目的以及传输大小。
    //使用基本模式是因为外设正在进行 UDMA 传输
    //请求。 源是 TX 缓冲区、目的是 UART0
    //数据寄存器。
    //
    uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、
    UDMA_MODE_BASIC、
    G_ui8SSITxBuf、
    (void *)(SSI0_BASE + SSI_O_DR)、
    sizeof (g_ui8SSITxBuf));
    
    //
    //设置 SSI0RX 通道的传输参数
    //
    uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、
    UDMA_MODE_BASIC、
    (void *)(SSI0_BASE + SSI_O_DR)、
    G_ui8SSIRxBuf、
    sizeof (g_ui8SSIRxBuf));
    
    
    
    //
    //现在,UDMA SSI0 TX 和 RX 通道都要以启动 A 为底
    //传输。 一旦启用通道、外设将会启动
    //发出传输请求,数据传输将开始。
    //
    
    uDMAChannelEnable (UDMA_CHANGE_SSI0RX);
    uDMAChannelEnable (UDMA_CHANGE_SSI0TX);
    
    //
    //启用 SSI0 DMA TX/RX 中断。
    //
    //SSIIntEnable (SSI0_BASE、SSI_DMATX | SSI_DMARX);
    SSIIntEnable (SSI0_BASE、SSI_DMARX);
    
    //
    //启用 SSI0外设中断。
    //
    内部使能(INT_SSI0);
    
    }
    
    //*************
    //
    // SSI0中断处理程序
    //
    //*********
    void SSI0IntHandler (void)
    {
    uint32_t ui32Status;
    uint32_t ui32模式;
    
    ++g_nbInterrupts;
    
    ui32Status = SSIIntStatus (SSI0_BASE、1);
    
    SSIIntClear (SSI0_BASE、ui32Status);
    
    ui32Mode = uDMAChannelModeGet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT);
    
    if (ui32Mode = uDMA_MODE_STOP)
    {
    G_ui32SSIRxCount++;
    
    if (((ui32Status & SSI_MIS_DMARXMIS)== SSI_MIS_DMARXMIS)
    SSIDMADisable (SSI0_BASE、SSI_DMA_RX);
    }
    
    if (!uDMAChannelIsEnabled (UDMA_CHANGE_SSI0TX))
    {
    G_ui32SSITxCount++;
    
    if (((ui32Status & SSI_MIS_DMATXMIS)== SSI_MIS_DMATXMIS)
    SSIDMADisable (SSI0_BASE、SSI_DMA_TX);
    }
    }
    
    int main (void)
    {
    //将时钟设置为直接从频率为120MHz 的晶体运行。
    //
    G_ui32SysClock = SysCtlClockFreqSet ((SYSCTL_XTAL_25MHz |
    SYSCTL_OSC_MAIN |
    SYSCTL_USE_PLL |
    SYSCTL_CFG_VCO_480)、120000000);
    
    
    //
    //在系统级别启用 uDMA 控制器。 使其继续
    //在处理器处于睡眠状态时运行。
    //
    SysCtlPeripheralEnable (SYSCTL_Periph_UDMA);
    
    //
    //启用 uDMA 控制器错误中断。 将发生该中断
    //如果在传输过程中出现总线错误。
    //
    IntEnable (INT_UDMAERR);
    
    //
    //启用 UDMA 控制器。
    //
    uDMAEnable();
    
    //
    //指向控制表以用于通道控制结构体。
    //
    uDMAControlBaseSet (pui8ControlTable);
    
    //初始化 SSI0。
    //
    ConfigureSSI0();
    
    uint_fast16_t ui16Idx;
    
    //
    //用简单的数据模式填充 TX 缓冲区。
    //
    对于(ui16Idx = 0;ui16Idx < SSI_buffer_size;ui16Idx++)
    {
    G_ui8SSITxBuf[ui16Idx]= 58;
    }
    
    memset (&g_ui8SSIRxBuf、0、SSI_buffer_size);
    G_nbInterrupts = 0;
    
    //
    //初始化 UDMA SPI 传输。
    //
    InitSPITransfer();
    
    while (1)
    {
    
    }
    
    } 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    您好、Sina、
    很抱歉耽误你的回答。 我目前已离开办公室两周。 我无法运行您的代码。 我只有 iPad。 您能否单步执行 uDMAChannelEnable API 并查看 DMAENASET 在您单步执行 API 时的行为?