精读OSAL --SPI方式串行通信(_hal_uart_spi.c)

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;
    }
  }
}


 


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值