1. 概述

    本文档是对NUC970平台上的SylixOS SPI总线数据传输的详细分析。

     

  2. SPI总线数据传输流程

    NUC970平台上SPI总线数据传输流程如图 21所示。

    当一个消息准备传输时,第一步判断发送和接收缓存区是否为空,若为空,就没有要接收或发送的数据,直接返回。

    第二步,判断消息长度是否大于指定的值(可以改变,但要大于16个字节),若大于,以16字节为单位传输数据,否则以单字节为单位传输数据。

    SylixOS NUC970平台SPI总线数据传输_SylixOS

    21 SPI数据传输流程图

  3. SPI总线的消息类型

    在SylixOS中,SPI总线的消息类型,如程序清单 21所示。

    程序清单 21 SPI总线消息类型


    /*********************************************************************************************************
      SPI 总线传输控制消息
    *********************************************************************************************************/
    
    typedef struct lw_spi_message {
        UINT16                   SPIMSG_usBitsPerOp;                        /*  操作单位bits数              */
        UINT16                   SPIMSG_usFlag;                             /*  传输控制参数                */
        
    #define LW_SPI_M_CPOL_0      0x0000                                     /*  CPOL 配置                   */
    #define LW_SPI_M_CPOL_1      0x0001
    #define LW_SPI_M_CPHA_0      0x0000                                     /*  CPHA 配置                   */
    #define LW_SPI_M_CPHA_1      0x0002
    
    #define LW_SPI_M_CPOL_EN     0x0004                                     /*  是否设置新的 CPOL 配置      */
                                                                            /*  否则 CPOL 配置与上次传输相同*/
    #define LW_SPI_M_CPHA_EN     0x0008                                     /*  是否设置新的 CPHA 配置      */
                                                                            /*  否则 CPHA 配置与上次传输相同*/
    
    #define LW_SPI_M_WRBUF_FIX   0x0010                                     /*  发送缓冲区仅发送第一个字符  */
    #define LW_SPI_M_RDBUF_FIX   0x0020                                     /*  接收缓冲区仅接收第一个字符  */
    
    #define LW_SPI_M_MSB         0x0040                                     /*  从高位到低位                */
    #define LW_SPI_M_LSB         0x0080                                     /*  从低位到高位                */
    
        UINT32                   SPIMSG_uiLen;                              /*  长度(缓冲区大小)            */
        
        UINT8                   *SPIMSG_pucWrBuffer;                        /*  发送缓冲区                  */
        UINT8                   *SPIMSG_pucRdBuffer;                        /*  接收缓冲区                  */
        
        VOIDFUNCPTR              SPIMSG_pfuncComplete;                      /*  传输结束后的回调函数        */
        PVOID                    SPIMSG_pvContext;                          /*  回调函数参数                */
    } LW_SPI_MESSAGE;
    typedef LW_SPI_MESSAGE      *PLW_SPI_MESSAGE;



  4. 技术实现

    SPI数据传输方式可分为两种:以单个字节为单位传输数据和以16个字节为单位传输数据。

    在SylixOS中SPI总线数据传输的代码实现,如程序清单 31所示。

    程序清单 31 SPI数据传输函数

    /*********************************************************************************************************
    ** 函数名称: __trySpiTransfer
    ** 功能描述: spi 传输函数
    ** 输 入  : pSpiChannel     spi 通道
    **           pSpiAdapter     spi 适配器
    **           pSpiMsg         spi 传输消息组
    ** 输 出  : ERROR_CODE
    *********************************************************************************************************/
    static INT  __trySpiTransfer (UINT              uiChannel,
                                  PLW_SPI_ADAPTER   pSpiadapter,
                                  PLW_SPI_MESSAGE   pSpimsg)
    {
        UINT          uiNum;
        UINT8        *pucTxBuf  = pSpimsg->SPIMSG_pucWrBuffer;              /*  发送缓存区                  */
        UINT8        *pucRxBuf  = pSpimsg->SPIMSG_pucRdBuffer;              /*  接收缓存区                  */
        UINT          uiByteLen = pSpimsg->SPIMSG_uiLen;                    /*  缓存区长度                  */
    
        if (!pucTxBuf && !pucRxBuf) {                                       /*  TxBuf和Rxbuf不能全为空      */
            SPI_DEBUG("transfer message is invalid\n");
            return (PX_ERROR);
        }
    
        if (uiByteLen > SPI_BIG_NUM) {
            /*
             *  大于SPI_BIG_NUM字节按16字节传输,否则按单字节传输
             */
            writel(readl(REG_SPI_CNTRL(uiChannel)) & SPI_TXNUM32,           /*  设置为32位传输单元          */
                   REG_SPI_CNTRL(uiChannel));
            writel(readl(REG_SPI_CNTRL(uiChannel)) | SPI_TX_FOUR,           /*  设置每次传输单元个数为4     */
                   REG_SPI_CNTRL(uiChannel));
            for (uiNum = 0; (uiNum + 16) <= uiByteLen; uiNum += 16) {
                if (pucTxBuf) {
                    writel_ESwap(*(UINT*)pucTxBuf,        REG_SPI_TX0(uiChannel));
                    writel_ESwap(*(UINT*)(pucTxBuf + 4),  REG_SPI_TX1(uiChannel));
                    writel_ESwap(*(UINT*)(pucTxBuf + 8),  REG_SPI_TX2(uiChannel));
                    writel_ESwap(*(UINT*)(pucTxBuf + 12), REG_SPI_TX3(uiChannel));
                    pucTxBuf += 16;                                         /*  将TxBuf指针偏移16个字节     */
                }
                writel(readl(REG_SPI_CNTRL(uiChannel)) | SPI_CNTRL_GOBUSY,  /*  使能开始传输位              */
                       REG_SPI_CNTRL(usheiChannel));
                API_SemaphoreBPend(__GspiChannels[uiChannel].hSignal,       /*  挂起,等待中断完成          */
                                   LW_OPTION_WAIT_INFINITE);
    
                if (pucRxBuf) {
                    *(UINT*)pucRxBuf        = readl_ESwap(REG_SPI_RX0(uiChannel));
                    *(UINT*)(pucRxBuf + 4)  = readl_ESwap(REG_SPI_RX1(uiChannel));
                    *(UINT*)(pucRxBuf + 8)  = readl_ESwap(REG_SPI_RX2(uiChannel));
                    *(UINT*)(pucRxBuf + 12) = readl_ESwap(REG_SPI_RX3(uiChannel));
                     pucRxBuf += 16;                                        /*  将RxBuf指针偏移16个字节     */
                }
            }
            if (uiNum < uiByteLen) {
                /*
                 *  将剩下的不足16字节的数据,按8位传输
                 */
                writel((readl(REG_SPI_CNTRL(uiChannel)) & SPI_TXNUM32) | SPI_TXNUM8,
                       REG_SPI_CNTRL(uiChannel));                           /*  设置8位传输单元             */
                writel(readl(REG_SPI_CNTRL(uiChannel)) & SPI_TX_ONE,        /*  设置每次传输单元个数为1     */
                       REG_SPI_CNTRL(uiChannel));
    
                for (; uiNum < uiByteLen; uiNum++) {
                    if (pucTxBuf) {
                        writel(*(pucTxBuf++), REG_SPI_TX0(uiChannel));
                    }
                    writel(readl(REG_SPI_CNTRL(uiChannel)) | SPI_CNTRL_GOBUSY,
                           REG_SPI_CNTRL(uiChannel));                       /*  使能开始传输位              */
                    API_SemaphoreBPend(__GspiChannels[uiChannel].hSignal,   /*  挂起,等待中断完成          */
                                       LW_OPTION_WAIT_INFINITE);
    
                    if (pucRxBuf) {
                        *(pucRxBuf++) = (UINT8)readl(REG_SPI_RX0(uiChannel));
                    }
                }
            }
        } else {
            /*
             *  小于16字节的数据,一直按8位传输
             */
            writel((readl(REG_SPI_CNTRL(uiChannel)) & SPI_TXNUM32) | SPI_TXNUM8,
                   REG_SPI_CNTRL(uiChannel));                               /*  设置8位传输单元             */
            writel(readl(REG_SPI_CNTRL(uiChannel)) & SPI_TX_ONE,            /*  设置每次传输单元个数为1     */
                   REG_SPI_CNTRL(uiChannel));
    
            for (uiNum = 0; uiNum < uiByteLen; uiNum++) {
                if (pucTxBuf) {
                    writel(*(pucTxBuf++), REG_SPI_TX0(uiChannel));
                }
                writel(readl(REG_SPI_CNTRL(uiChannel)) | SPI_CNTRL_GOBUSY,  /*  使能开始传输位              */
                       REG_SPI_CNTRL(uiChannel));
                API_SemaphoreBPend(__GspiChannels[uiChannel].hSignal,       /*  挂起,等待中断完成          */
                                   LW_OPTION_WAIT_INFINITE);
    
                if (pucRxBuf) {
                    *(pucRxBuf++) = (UINT8)readl(REG_SPI_RX0(uiChannel));
                }
            }
        }
        return  (ERROR_NONE);
    }



     

  5. 16字节数据传输流程

    在SPI总线的数据传输中,当缓存区长度大于指定的长度(指定的长度要大于16字节),会以16字节为单位进行数据传输,传输流程如图 31所示。

    SylixOS NUC970平台SPI总线数据传输_NUC970_02

    31 16字节数据传输流程图

  6. 单字节数据传输流程

    在SPI总线的数据传输中,当缓存区长度小于指定的长度时,会以单字节为单位进行数据传输。如图 32所示。

    SylixOS NUC970平台SPI总线数据传输_SPI_03

    32单字节数据传输流程图

     

  7. 免责声明

    内部交流文档,仅针对NUC970相关平台,若发现相关错误或者建议,请及时联系文章档创建者进行修订和更新。