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 闪存芯片通信。 当我在紧凑的优化环路中直接访问 SSI 外设时、代码工作完美。 我已尝试通过将数据传输从直接 SSI 访问更改为 UDMA 来提高性能。 我在直接 SSI 访问中保留了 Flash 存储器命令字节和地址字节(通常为4字节地址)、因此只有较长的 Flash 数据传输由 UDMA 处理。
为了提高效率、我将 UDMA 通道控制设置为 UDMA_SIZE_8 | UDMA_ARB_4。 通道传输为 UDMA_MODE_PING_PONG。 通道属性设置了 UDMA_ATTR_USEBURST、我认为我要小心等待 SSI 忙线取消置位、然后只将 UDMA 大小编程为4个字节的倍数。
无论我重复该命令多少次、在系统复位后、读取 SPI 闪存都能完美地工作。 由于这些命令涉及到在命令和地址的直接 SSI 访问与数据的 UDMA 之间交替、因此我的代码似乎被正确写入。 但是、如果我写入 SPI 闪存(仅使用直接访问、无 UDMA)、则从 SPI 闪存读取的下一个 UDMA 会受到以下问题的影响:在开始时接收到一个额外的字、然后是预期的数据、一个字太晚。 通过逻辑分析仪查看实际的引脚、可以明显看出 SPI 闪存芯片在正确的时间发送正确的数据、但 UDMA 正在提供额外的字。 一旦系统进入此状态、在每次新传输开始时、重复 UDMA 读取总是会有该额外的字。
除了寻求一般性帮助、基于上述问题、我还想知道是否有某种方法可以清除或复位我缺失的 UDMA 外设。 由于有一个来自 UDMA 的额外字、我最初怀疑我的写代码在 SSI FIFO 中留下了未读数据、但由于我清空了 FIFO、这似乎是不可能的。
相关说明:在启动 UDMA 之前、在 SSI 外设不忙之前循环是否足够? 或者、我是否还需要检查 FIFO 是否为空?
[引用 USER="Ralph Jacobi">此时我想我能做的最有用的事情是查看代码的相关部分、了解您的情况:
配置 SPI/UDMA
SSI:
MAP_SysCtlPeripheralEnable (SYSCTL_Periph_SSI0); MAP_SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA); MAP_GPIOPinConfigure (GPIO_PA2_SSI0CLK); // FSS 的软件控制需要跳过 GPIO_PA3_SSI0FSS //对于 FSS 的硬件控制,取消注释此行 // GPIOPinConfigure (GPIO_PA3_SSI0FSS); MAP_GPIOPinConfigure (GPIO_PA4_SSI0XDAT0); MAP_GPIOPinConfigure (GPIO_PA5_SSI0XDAT1); MAP_GPIOPinTypeSSI (SSI0_BASE、GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5); MAP_GPIOPinTypeGPIOOutput (SSI0_BASE、GPIO_PIN_3); HWREG (SSI0_BASE + SSI_O_CR1)= SSI_CR1_MS | SSI_CR1_SSE | SSI_ADV_MODE_READ_WRITE; HWREG (SSI0_BASE + SSI_O_CPSR)= 2; HWREG (SSI0_BASE + SSI_O_CR0)= 0x0307;
uDMA:
MAP_SysCtlPeripheralEnable (SYSCTL_Periph_UDMA); MAP_SysCtlPeripheralSlepEnable (SYSCTL_Periph_UDMA); map_uDMAEnable(); MAP_uDMAControlBaseSet (UDMA_CONTROL_TABLE); //开始事务: ROM_uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0RX、UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORY | UDMA_ATTR_REQMASK); ROM_uDMAChannelAttributeEnable (UDMA_CHANGE_SSI0RX、UDMA_ATTR_USEBURST); ROM_uDMAChannelControlSet (UDMA_CHANNEL_SSI0RX | UDMA_PRI_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4); ROM_uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_PRI_SELECT、UDMA_MODE_PINGONG、SSI0_BASE + SSI_O_DR、dstPri、SIZE); ROM_uDMAChannelControlSet (UDMA_CHANNEL_SSI0RX | UDMA_ALT_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4); ROM_uDMAChannelTransferSet (UDMA_CHANGE_SSI0RX | UDMA_ALT_SELECT、UDMA_MODE_PINGONG、SSI0_BASE + SSI_O_DR、dstAlt、SIZE); ROM_uDMAChannelEnable (UDMA_CHANGE_SSI0RX); ROM_uDMAChannelAttributeDisable (UDMA_CHANGE_SSI0TX、UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORY | UDMA_ATTR_REQMASK); ROM_uDMAChannelAttributeEnable (UDMA_CHANGE_SSI0TX、UDMA_ATTR_USEBURST); ROM_uDMAChannelControlSet (UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); ROM_uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_PRI_SELECT、UDMA_MODE_PINGONG、srcPri、SSI0_BASE + SSI_O_DR、SIZE); ROM_uDMAChannelControlSet (UDMA_CHANNEL_SSI0TX | UDMA_ALT_SELECT、UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); ROM_uDMAChannelTransferSet (UDMA_CHANGE_SSI0TX | UDMA_ALT_SELECT、UDMA_MODE_PINGONG、srcAlt、SSI0_BASE + SSI_O_DR、SIZE); ROM_uDMAChannelEnable (UDMA_CHANGE_SSI0TX); ROM_SSIIntEnable (SSI0_BASE、SSI_DMATX | SSI_DMARX); ROM_IntEnable (INT_SSI0);
[引用]写入 SPI 闪存[/引用]
MAP_SSIAdvModeSet (SSI0_BASE、SSI_ADV_MODE_READ_WRITE); _select (nPort); //在标准 SPI 模式下发送命令字节(实际上是高级 R/W) MAP_SSIDataPut (SSI0_BASE、COMMAND_2READ); MAP_SSIDataGet (SSI0_BASE、\ui32); MAP_SSIDataPut (SSI0_BASE、地址>> 24); MAP_SSIDataGet (SSI0_BASE、\ui32); MAP_SSIDataPut (SSI0_BASE、地址>> 16); MAP_SSIDataPut (SSI0_BASE、地址>> 8); MAP_SSIDataPut (SSI0_BASE、地址); // SIO1和 SIO0上的8位数据虚拟周期 a.k.a. 4添加周期 MAP_SSIDataPut (SSI0_BASE、0x00); 对于(I = 0;I < 4;I++) MAP_SSIDataGet (SSI0_BASE、\ui32); while (MAP_SSIBusy (SSI0_BASE)) ; MAP_SSIAdvModeSet (SSI0_BASE、SSI_ADV_MODE_BI_READ); while (HWREG (SSI0_BASE + SSI_O_SR)& SSI_SR_BSY) ; //确保读取 FIFO 为空 while (HWREG (SSI0_BASE + SSI_O_SR)& SSI_SR_RNE) 数据= HWREG (SSI0_BASE + SSI_O_DR); //参见上面的 UDMA"开始事务"
[引用]在发生错误状态时、正在执行何种重置操作来尝试和解决该问题。
到目前为止、只有 CCS 中"Reset"图标下的"System Reset"菜单项看起来可以正常工作。 我已经寻找了一些其他方法来重置 UDMA、但尚未找到它。 等待 SSI 忙线使 Rx FIFO 失效和清空 Rx FIFO 的所有方式似乎对 UDMA 没有影响。 在每次交易之前、我想整体重置 uDMA 外设、但它可能会被其他外设使用、因此我甚至没有尝试重锤。
[引用]我会遇到的另一个问题是、当额外的字出现时、它是完全非感应数据、还是有迹象表明它与之前的事务或之前加载到 UDMA 中的数据有关?
有一种模式、即当问题首次出现时、额外的字是一个值。 之后、额外的字在第二个事务上具有不同的值、然后在后续每次重复时保持不变。 这似乎表示 SSI 或 SSI Rx FIFO 有一些来自之前事务的陈旧数据、但我有代码在启动 UDMA 之前清除 Rx FIFO、因此我不知道会留下什么陈旧数据。
另请注意、在触发错误之前、读取 SPI 闪存的 UDMA 事务可以重复多次、而不会有任何过时的数据。 即、第一个字是正确的、所有后续字也是正确的。 不过、在触发错误后、无论有多少个 PIO 与 DMA 存取交错、都会始终存在额外的字(直到系统复位)。 此外、请注意、即使在错误触发后与 UDMA 事务交错、对 SPI 闪存(无 UDMA)的 PIO 访问始终是成功的。 出现问题后、似乎什么也不能解决、在同一 SSI 端口上的 PIO 传输甚至不成功!
注意:我已将许多子例程连接到上面的线性代码流中、因此可能会出现轻微错误。 您看到的冗余忙等待出现在那里、因为它出现在一个函数中、然后调用另一个也有等待的子例程。
您好 Brian、
查看您的 SSI 配置、我认为您可能缺少一些参数、因为您没有使用建议的 TivaWare API。 具体而言、我看不到可确保设置正确模式和数据大小的设置。 请注意、对于高级模式、您必须根据以下条件使用 Motorola Mode 0:
//! 当使用一个高级运行模式时、SSI 模块必须已经// ! 配置为八个数据位和\b SSI_FRF_MOTO_MODE_0协议。 //! 所选的高级模式操作仅适用于新数据 //! 写入 FIFO;FIFO 中已存在的数据为 //! 当数据为 //时、使用有效的高级操作模式进行处理! 书面。
除此之外、我遇到的另一个提供一些线索或想法的问题是勘误表、它听起来与您的情况不匹配、但您可以查看并比较您的系统的行为、即 www.ti.com/.../spmz850g.pdf 上的 SSI#05
[引用 USER="Ralph Jacobi">浏览您的 SSI 配置、我认为您可能缺少一些参数、因为您没有使用推荐的 TivaWare API。 具体而言、我看不到可确保设置正确模式和数据大小的设置。 请注意、对于高级模式、您必须使用 Motorola Mode 0
感谢您的评论、Ralph。 请注意、SSI 配置的最后一行是:
HWREG (SSI0_BASE + SSI_O_CR0)= 0x0307;
(笑声) 在所有情况下都应强制 FRF=0x0 (Freescale SPI)和 DSS=7 (8位数据)。
是否有其他先决条件?
另请注意、在写入 SPI 闪存之前、先进行 SSI 配置。 因此、在外设已处于 Motorola 模式0后、会调用 SSIAdvModeSet (SSI_base、SSI_ADV_MODE_BI_READ)。
[引用 USER="Ralph Jacobi]]我遇到的另一个提供一些线索或想法的问题是勘误表、它听起来与您的案例不匹配、但您可能可以查看并比较您的系统的行为、即 www.ti.com/.../spmz850g.pdf上的 SSI#05
你是对的-我认为 SSI#05不适用。 无论我使用双模 SSI 还是常规1位高级模式、都会出现此问题。
新信息:
我在一段时间内没有运行1位读取测试、因此我编码了一个测试用例并发现了一些新的行为。 当使用纯1位 SPI 闪存命令(和数据)时、这个问题出现在一个长链读取的中间、而没有任何其他(不相关)命令。
背景:
当运行配置代码如上所示的双模式测试时、大约64 MB 的数据会被完美读取(在系统复位后)。 由于 TM4C129E SRAM 仅为256KB、因此 UDMA 将每条命令写入仅16KB (0x004000)、然后循环以在下一个地址继续。 此测试无误地重复4、000次以上(非 DMA 命令和地址阶段后跟 UDMA 数据阶段)。 我连续重复该命令五次、并且始终成功。
新的1位读取测试基本相同、在16 KB 块大小的命令中从 SPI 闪存读取大约64 MB 的数据。 第一个运行在块数1、411的开头显示了缺少的字。 随后的运行在数字为289的块开头显示了缺少的字。 伪波字是第一次为0xBFBFBFBF、第二次为0x3F3F3F。
我不确定这是什么、但现在我知道它与触发双模错误的非 DMA 擦除/程序命令无关。 1位模式读取将在环路中间触发此问题。 因此、每次循环迭代写入 SSI 的数据几乎完全相同、不同之处在于 SPI 存储器地址会针对循环的每次重复递增16 KB。 我看不到这会如何触发错误、因为在双模式下访问相同的地址。
感谢您继续研究这一问题。
一些其他信息:
我已将 FIFO 设置为处理32位数据块(突发)、并仔细编写固件以确保 uDMA 传输大小始终是4字节的倍数。 任何与 SSI 外设的非 UDMA 传输都将通过等待 BUSY 位清零、并在开启 UDMA 前确保接收 FIFO 为空来清零。 这应该保证所有的 UDMA 传输都涉及一整数个32位字。
也就是说、当我关闭突发时、我的代码停止工作。 我没有找到相关的解释、因此我重新打开了它、既是为了让事情正常工作、也是为了提高性能。 我仍然担心32位异常在某种程度上与突发模式有关。
在这方面、我不确定当数据大小不是4个字节的倍数时、在突发模式下会出现什么预期行为。 是否会传递额外的字词? (笑声) 还是奇数数据只会在添加额外数据之前一直保留在 FIFO 中? 也就是说、我不认为我看到的症状可能是由于滥用突发模式所致、但我想提及这种可能性。
感谢您的状态更新。
供参考:多家芯片供应商- Macronix 和 Micron -以及不同尺寸(128 MB 和1 GB)都再次出现了这些问题。
[引用 user="Bob Crosby">我建议您在例程开始时将 RX FIFO 清空、以写入闪存、而不是写入末尾 这就是为什么:在写入闪存的例程中,您在每个 SSIDataPUT()之后跟随一个 SSIDataGet ()。 六个压点和六个压点。 如果 RX FIFO 开始为空、那么它在末尾应该为空。 假设在写入闪存的例程开始时 RX FIFO 中有4个字节。 最后需要刷新4个字节 但是、由于 TX FIFO 以及开始发送和完成接收之间的延迟、在最后四个字节完成传输之前可能会对接收空进行测试。[/QUERP]
感谢 Bob 的建议。 我相信我的代码已经在例程开始的_select()函数中处理了这个。
静态内联 void _select (unsigned nPort) { SPI_xfer_flush_Rx (nPort); MAP_GPIOPinWrite (g_SSi[nPort].port_base、g_SSi[nPort].fss_pin、0); } SPI_xfer_error_t SPI_xfer_flush_Rx (无符号 nPort) { uint32_t ui32DataRx; IF (nPort >= SSI_ports) 返回 kSPIXferErrorInvalidIndex; while (MAP_SSIDataGetNonBlocking (g_SSI[nPort].SSI_BASE、&ui32DataRx)) ; 返回 kSPIXferErrorNone; }
这可确保在 SPI 闪存存储器芯片选择被置为有效前、任一 FIFO 中没有数据保持。
[引用 user="Ralph Jacobi">简化内容可能有助于发现故障点。
要开始简化、您是否可以改用传统模式? 我们对 UDMA +高级模式并不是很熟悉、以前也不是很熟悉。
我可以尝试使用传统模式。
[引用 USER="Ralph Jacobi">您还提到您使用 MCU 将命令发送到 SPI 闪存、但您也有用于 UDMA 的 TX 代码。 可能会触发某个操作、因此现在注释掉 UDMA 的 TX 代码将会更好。
我有用于删除 UDMA 的所有 Tx 代码的编译器指令。 即使所有 Tx 代码都是 PIO (非 DMA)、RX UDMA 问题仍然会发生。
[报价用户="Ralph Jacobi"]尝试使用传统模式是否成功?[/quot]
感谢您的跟进。
不,我没有运气。 传统模式产生的错误与我之前看到的完全相同。
Brian、我使用 SSI2和 UDMA 独立创建了一个项目。 我没有遇到你提到的任何问题。 您可以查看该示例、看看是否存在可能会导致您所看到行为的差异。
/cfs-file/__key/communityserver-discussions-components-files/908/2318.udma_2D00_ssi2_5F00_demo.zip
版本、使用缓冲 UARTprintf 来避免丢失某些传输的验证。 将 PD0连接到 PD1、以便 SSI2接收其传输的内容。
/cfs-file/__key/communityserver-discussions-components-files/908/7462.udma_2D00_ssi2_5F00_demo.zip
是否可以扩展这个基于回送的 SSI+FIFO+UDMA 示例代码、以便它使用实际的外部存储器芯片执行完整的 SPI 命令?
我怀疑、我们在这里看到的问题将会暴露在非 uDMA 和 uDMA 传输更复杂的交错中。
我看到 TM4C129X 开发板具有某种 SPI 存储器芯片。 它的连接与我们的某个固件外设分配冲突、因此我很难在那里运行我们的代码。 是否有可能扩展这个德州仪器示例代码来与 TM4C129X 电路板上的 SPI 芯片通信? 只需检查 SPI 芯片状态、然后执行较长的 UDMA 读取和/或写入命令、将非 UDMA 用于 SPI 命令和地址阶段、将 UDMA 用于数据阶段即可。
好消息是、我看到示例代码设置了一次外设、并且在测试迭代之间不会重置任何外设。 这至少与我们的固件类似、在固件中只执行一次设置、所有操作都将一直运行、直到启动某些交互式命令序列。 我怀疑可能有一个外设状态需要复位。 如果我当前为恢复功能而执行的完全系统复位没有达到要求、将会有所帮助、因为当多个外设同时处于活动状态时、显然无法复位整个芯片。