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.
您好!
我尝试使用 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、
您是否打算在第二次调用后清除两个通道使能位? 我建议您执行单步执行以查看在第二次调用后写入的值。 请注意、相关位在通道传输后自动清零。 如何清除 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) { } }