您好!
我使用 DMA 以19.2MHz 的频率(与微控制器时钟频率相同)写入和读取512字节的块。
SPI TX DMA 通道为通道4、RX 为通道3。 我还有 DMA 通道5、用于大型 memcpys。 这是一个代码示例
#define SPI_READ_DMA_CHANNEL (dma_channel_3) #define SPI_READ_DMA_CTL (DMA3CTL) #define SPI_WRITE_DMA_CHANNEL (DMA_CHANGE_4) #define SPI_WRITE_DMA_CTL (DMA4CTL) void SPI_init (void){ GPIO_setOutputHighOnPin (SPI_CS_PORT、SPI_CS_PIN); GPIO_setPeripheralModuleFunctionInputPin ( SPI_PORT、(SPI_MOSI_PIN | SPI_SOMI_PIN | SPI_SCLK_PIN) ); GPIO_setAsOutputPin (SPI_CS_PORT、SPI_CS_PIN); USCI_A_SPI_initMaster ( USCI_A1_BASE、 &((USCI_A_SPI_initMasterParam) {.selectClockSource = USCI_A_SPI_CLOCKSOURCE_SMCLK、 时钟源频率= 19200000、 .desedSpiClock = 19200000、 .msbFirst = USCI_A_SPI_MSB_FIRST、 时钟相位= USCI_A_SPI_PHASE_DATA_Changed_ONFIRST_captured_on_next、 时钟极性= USCI_A_SPI_CLOCKPOLARITY_INACT_HIGH }); DMA_disableTransfers (SPI_WRITE_DMA_CHANNEL); DMA_disableTransfers (SPI_READ_DMA_CHANNEL); dma_init ( &((dma_initParam) {.channelSelect = SPI_WRITE_DMA_CHANNEL、 .transfermodeSelect = dma_transfer_single、 .transferSize = 0、 triggerSourceSelect = DMA_TRIGGERSOURCE_21、// UCA1TXIFG .transferUnitSelect = DMA_SIZE SRCBYTE_DSTBYTE、 triggerTypeSelect = DMA_TRIGGER_HIGH }); dma_init ( &((dma_initParam) {.channelSelect = SPI_READ_DMA_CHANNEL、 .transfermodeSelect = dma_transfer_single、 .transferSize = 0、 triggerSourceSelect = DMA_TRIGGERSOURCE_20、// UCA1RXIFG .transferUnitSelect = DMA_SIZE SRCBYTE_DSTBYTE、 triggerTypeSelect = DMA_TRIGGER_HIGH }); USCI_A_SPI_ENABLE (USCI_A1_base); } int16_t SPI_read (uint8_t * buf、uint16_t len){ 静态 uint8_t Pending_dma_read = 0; //如果 DMA 尚未完成操作,则返回 NOT READY。 if (SPI_READ_DMA_CTL 和 DMAEN){ 返回0; } //如果有一个挂起的读取操作,这意味着 // DMA 已完成复制。 我们可以返回成功。 if (pending_dma_read){ Pending_DMA_read = 0; SPI_READ_DMA_CTL &&~DMAIFG; 返回1; } // DMA 非忙且无挂起读取,启动读取操作。 Pending_DMA_read = 1; DMA_setTransferSize (SPI_WRITE_DMA_CHANNEL、len); DMA_setTransferSize (SPI_READ_DMA_CHANNEL、len); dma_disableTransferDuringReadModifyWrite ();//DMA4权变措施-在读取-修改-写入 CPU 操作期间禁用 DMA 传输 DMA_setSrcAddress (SPI_WRITE_DMA_CHANNEL、(无符号长整型)&SPI_READ_TOKEN、DMA_DIRECTION 不变); DMA_setDstAddress (SPI_WRITE_DMA_CHANNEL、USCI_A_SPI_getTransmitBufferAddressForDMA (USCI_A1_BASE)、DMA_Direction 不变); DMA_setSrcAddress (SPI_READ_DMA_CHANNEL、USCI_A_SPI_getReceiveBufferAddressForDMA (USCI_A1_base)、DMA_Directive_unchanged); DMA_setDstAddress (SPI_READ_DMA_CHANNEL、(无符号长整型) buf、DMA_DIRECTION _IN递增); USCI_A_SPI_receiveData (USCI_A1_base); //虚拟读取 USCI_A_SPI_clearInterrupt (USCI_A1_base、USCI_A_SPI_Receive_interrupt); DMA_enableTransferDuringReadModifyWrite ();//DMA4权变措施-在读取-修改-写入 CPU 操作期间重新启用 DMA 传输 //开始传输 DMA_enableTransfers (SPI_READ_DMA_CHANNEL); DMA_enableTransfers (SPI_WRITE_DMA_CHANNEL); 返回0; } #define memcpy_dma_channel (dma_channel_5) #define memcpy_dma_CTL (DMA5CTL) void dma_memcpy_init (void){ dma_disableTransfers (memcpy_dma_channel); dma_init ( &((dma_initParam) {.channelSelect = memcpy_dma_channel、 .transfermodeSelect = dma_transfer_BURSTBLOCK、 .transferSize = 0、 triggerSourceSelect = DMA_TRIGGERSOURCE_0、// DMAREQ .transferUnitSelect = DMA_SIZE SRCBYTE_DSTBYTE、 triggerTypeSelect = DMA_TRIGGER_HIGH }); } void dma_memcpy (uint8_t * dst、const uint8_t * src、uint16_t len){ 如果(len > 0){ dma_setTransferSize (memcpy_dma_channel、len); dma_disableTransferDuringReadModifyWrite ();//DMA4权变措施-在读取-修改-写入 CPU 操作期间禁用 DMA 传输 dma_setSrcAddress (memcpy_dma_channel、(uint32_t) src、dma_direction、增量); dma_setDstAddress (memcpy_dma_channel、(uint32_t) dst、dma_Directoration_Increment); DMA_enableTransferDuringReadModifyWrite ();//DMA4权变措施-在读取-修改-写入 CPU 操作期间重新启用 DMA 传输 dma_enableTransfers (memcpy_dma_channel); dma_enableInterrupt (memcpy_dma_channel);//以便在传输完成后能够睡眠和唤醒 dma_startTransfer (memcpy_dma_channel); while (memcpy_dma_CTL 和 DMAEN){ BIS_SR (LPM0_Bits+GIE);//睡眠 }; } }
我发现 dma_memcpy 有时发生在512字节 SPI 事务期间。 发生这种情况时、RX DMA 传输永远不会完成。 如果我之后设置一个断点、我会看到 DMA3SZ 为1、DMA4SZ 为512 (这意味着它已完成)、UCRXIFG 为0。
因此、这必须意味着由于 memcpy、事务中的某个位置错过了 RX 字节。 但是、我无法分析它。 通常、在 TX 数据被发送后会有一个 RX 中断标志。 由于 RX DMA 的优先级高于 TX DMA、即使 TX DMA 已准备好发送另一个字节、它也应该等待 RX DMA 被清除、对吧?
谢谢、
Fred