SPI种方式是可以在DMA的方式上同时共享缓冲区,来节省内存.
spiRxBuf = dmaCfg.rxBuf;
piRxDat = dmaCfg.txBuf;
spiTxPkt[SPI_MAX_PKT_LEN]
spiRxDat = dmaCfg.txBuf[0];//这个最难懂,其实就当是缓冲区就好子.解包后的数据放在这,让读函数来读.
spiTxPkt = dmaCfg.txBuf[1];
一帧的结构:
SOF //开始标志,EF
LEN //长度
DATA //数据,长度是上面LEN定义
FCS //检验字
rxBuf 是接收正个帧,解出数据放RxDat中.
读函数很简单,就是从RxDat中读多少数据,这时砂说了.
写函数有两种实现方法,一种是有发送队列,另一种是无发送队列.
先按帧要求填数据.
无发送队列就是配置好DMA通道,然后ARM就行了.
而有发送队列,看注释:
/************************************************************************************************** * @fn HalUARTWriteSPI * * @brief Transmit data bytes as a SPI packet. * * input parameters * * @param buf - pointer to the memory of the data bytes to send. * @param len - the length of the data bytes to send. * * output parameters * * None. * * @return Zero for any error; otherwise, 'len'. */ static spiLen_t HalUARTWriteSPI(uint8 *buf, spiLen_t len) { if (spiTxLen != 0)//spiTxLen在这是标志是否有数据正在DMA中发送,它在完成中断里清零. #if HAL_SPI_QUEUED_TX uint8 *txMsg = osal_msg_allocate(len); if (txMsg != NULL) { (void)memcpy(txMsg, buf, len); osal_msg_enqueue(&spiTxQ, txMsg); return len; } #endif return 0; } //之后就打包数据ARM就行了. if (len > SPI_MAX_DAT_LEN) { len = SPI_MAX_DAT_LEN; } spiTxLen = len; #if HAL_SPI_QUEUED_TX spiTxMT = FALSE; #endif spiTxPkt[SPI_LEN_IDX] = len; (void)memcpy(spiTxPkt + SPI_DAT_IDX, buf, len); spiCalcFcs(spiTxPkt); spiTxPkt[SPI_SOF_IDX] = SPI_SOF; halDMADesc_t *ch = HAL_DMA_GET_DESC1234(HAL_SPI_CH_TX); HAL_DMA_SET_LEN(ch, SPI_PKT_LEN(spiTxPkt)); #if defined HAL_SPI_MASTER #if HAL_SPI_WAKEUP /* A Full-Duplex Slave will keep SRDY active low if it has Tx ready; otherwise it will toggle its * SRDY with every falling edge of MRDY. So if SRDY is high and the wait after writing each byte * is not long enough, the master may loop here, infinitely out of sync with the toggling SRDY. * Thus in a threaded OS, it would be advisable to send one zero and yield for 1-msec or more, or * be able to receive and check here for the SRDY ISR. */ spiRdyIsr = 0; while (!SPI_RDY_IN() && (spiRdyIsr == 0)) { SPI_CLOCK_RX(1); for (uint16 cnt = 0; cnt < 512; cnt++) { if (SPI_RDY_IN() || (spiRdyIsr != 0)) { break; } } } #endif SPI_SET_CSn_OUT(); HAL_DMA_ARM_CH(HAL_SPI_CH_TX); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); HAL_DMA_MAN_TRIGGER(HAL_SPI_CH_TX); #elif !defined HAL_SPI_MASTER HAL_DMA_ARM_CH(HAL_SPI_CH_TX); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); SPI_SET_RDY_OUT(); #endif return len; }
/************************************************************************************************** * @fn HalUART_DMAIsrSPI * * @brief Handle the Tx done DMA ISR. * * input parameters * * None. * * output parameters * * None. * * @return None. */ void HalUART_DMAIsrSPI(void) { #if !defined HAL_SPI_MASTER if (!SPI_RDY_IN()) #endif { SPI_CLR_RDY_OUT(); } spiTxLen = 0; //这里清零了,注意.!! 这后就判断是否还有队列要发送,再调用写函数. #if HAL_SPI_QUEUED_TX if (spiTxQ != NULL) { uint8 *txMsg = osal_msg_dequeue(&spiTxQ); (void)HalUARTWriteSPI(txMsg, OSAL_MSG_LEN(txMsg)); (void)osal_msg_deallocate(txMsg); } else { spiTxMT = TRUE; } #else spiCB((HAL_UART_SPI - 1), HAL_UART_TX_EMPTY); #endif }
接收都在POLL里处理,主要调用了static void spiParseRx(void);
spiRxBug 是用来扫描整个缓冲区. spiRxIdx是用来指明帧数据解释到哪里.
/************************************************************************************************** * @fn spiParseRx * * @brief Parse all available bytes from the spiRxBuf[]; parse Rx data into the spiRxDat[]. * * input parameters * * None. * * output parameters * * None. * * @return None. */ static void spiParseRx(void) { while (1) { if (!SPI_NEW_RX_BYTE(spiRxIdx))//spiRxIdx指向的数据是旧数据则,循环扫描spiRxBug. #if defined HAL_SPI_MASTER if (SPI_RDY_IN() && (spiTxLen == 0)) { SPI_CLOCK_RX(1); continue; } #endif if (SPI_NEW_RX_BYTE(spiRxBug))//如果找到数据 { while (!SPI_NEW_RX_BYTE(spiRxIdx)) { SPI_LEN_T_INCR(spiRxIdx); } spiRxBug = spiRxIdx; continue; } SPI_LEN_T_INCR(spiRxBug);//如果空则一直循环 break; } //这里开始解帧: uint8 ch = SPI_GET_RX_BYTE(spiRxIdx); SPI_CLR_RX_BYTE(spiRxIdx); SPI_LEN_T_INCR(spiRxIdx); switch (spiRxSte) { case spiRxSteSOF: if (ch == SPI_SOF) { spiRxSte = spiRxSteLen; // At this point, the master has effected the protocol for ensuring that the SPI slave is // awake, so set the spiRxLen to non-zero to prevent the slave from re-entering sleep until // the entire packet is received - even if the master interrupts the sending of the packet // by de-asserting/re-asserting MRDY one or more times. spiRxLen = 1; } break; case spiRxSteLen: if ((ch == 0) || (ch > SPI_MAX_DAT_LEN)) { spiRxSte = spiRxSteSOF; spiRxLen = 0; } else { spiRxFcs = spiRxLen = ch; spiRxTemp = spiRxTail; spiRxCnt = 0; spiRxSte = spiRxSteData; #if defined HAL_SPI_MASTER SPI_CLOCK_RX(ch + 1); // Clock out the SPI Frame Data bytes and FCS. #endif } break; case spiRxSteData: spiRxFcs ^= ch; spiRxDat[spiRxTemp] = ch; SPI_LEN_T_INCR(spiRxTemp); if (++spiRxCnt == spiRxLen) { spiRxSte = spiRxSteFcs; } break; case spiRxSteFcs: spiRxSte = spiRxSteSOF; if (ch == spiRxFcs) { spiRxTail = spiRxTemp; } spiRxCnt = spiRxLen = 0; break; default: HAL_ASSERT(0); break; } } }