W800 HSPI 使用简介 (1)

发布于 2024-04-12 15: 30: 43

W800 HSPI 是做为 SPI SLAVE 设备来使用的, 最高支持 50M 的时钟, 与主设备的连接如下图所示: CS, CLK, MOSI, MISO 四根线很容易理解, INT 对于 MCU 端找一个 GPIO 设置为 input, 平时为高电平, 当从设备有数据需要主动上报时, 会变成低电平, 直到 MCU 发送查询中断状态寄存器命令后, 才会恢复高电平.
26. png
1, W800 端收发数据
W800 端的代码比较简单, 可以参考 wm_slave_spi_demo. c, type 设置为 HSPI_INTERFACE_SPI 按如下代码依次调用接口即可完成初始化.

static void HspiInit (int type) 
{

    if (type == HSPI_INTERFACE_SPI) 
    {   
        wm_hspi_gpio_config (0) ; 
    }
    else if  (type == HSPI_INTERFACE_SDIO) 
    {
        wm_sdio_slave_config (0) ; 
    }
    else
    {
        printf ("do not support interface\n") ; 
        return; 
    }

    tls_slave_spi_init () ; 
    tls_set_high_speed_interface_type (type) ; 
    tls_set_hspi_user_mode (1) ; 
    tls_hspi_rx_data_callback_register (HspiRxDataCb) ; 
    tls_hspi_rx_cmd_callback_register (HspiRxCmdCb) ; 

}

当收到数据时, 底层驱动会调用 static s16 HspiRxDataCb (char buf) 回调函数, buff 里面就是收到的数据的内容. 当收到命令时, 底层驱动会调用 static s16 HspiRxCmdCb (char buf) 回调函数, buff 里面是收到的命令内容. 这里的数据和命令并没有区别, 可以都认为是通信内容, 只不过是 MCU 端调用了不同的接口发下来的, 比如也可以不使用命令接口, 都用数据接口发送也是可以的, 那这样就只会调用数据接收回调函数. 当 W800 需要发送数据时, 可以调用 int tls_hspi_tx_data (char *txbuf, int len) 接口.

2, MCU 端收发数据
2. 1SPI 初始化

void SPI1Init (void) 
{
    GPIO_InitTypeDef GPIO_InitStructure;  
    SPI_InitTypeDef SPI_InitStructure;  

    memset (gsSPIRxDesc,  0,  sizeof (gsSPIRxDesc) ) ; 
    //GPIOA Periph clock enable
    RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1,  ENABLE) ;  
    
    GPIO_InitStructure. GPIO_Pin                = GPIO_Pin_5 | GPIO_Pin_7;  
    GPIO_InitStructure. GPIO_Speed            = GPIO_Speed_50MHz;  
    GPIO_InitStructure. GPIO_Mode            = GPIO_Mode_AF_PP;  //复用推挽输出
    GPIO_Init (GPIOA,  &GPIO_InitStructure) ;  //Configure SPI2 pins:  SCK,  MOSI 

    GPIO_InitStructure. GPIO_Pin                = GPIO_Pin_6;  
    GPIO_InitStructure. GPIO_Mode            = GPIO_Mode_IPU;  //复用推挽输出
    GPIO_Init (GPIOA,  &GPIO_InitStructure) ;  //Configure SPI2 pins:  MISO 
    
    GPIO_InitStructure. GPIO_Pin                = GPIO_Pin_4;  
    GPIO_InitStructure. GPIO_Speed            = GPIO_Speed_50MHz;  
    GPIO_InitStructure. GPIO_Mode            = GPIO_Mode_Out_PP;  //推挽输出
    GPIO_Init (GPIOA,  &GPIO_InitStructure) ;  //Configure PB2 pin:  TP_CS pin 
    
    GPIO_SetBits (GPIOA,  GPIO_Pin_4) ; 
    GPIO_SetBits (GPIOA,  GPIO_Pin_5) ; 
    GPIO_SetBits (GPIOA,  GPIO_Pin_7) ; 
    
    // SPI1 Config  
    SPI_InitStructure. SPI_Direction            = SPI_Direction_2Lines_FullDuplex;  
    SPI_InitStructure. SPI_Mode                = SPI_Mode_Master;  
    SPI_InitStructure. SPI_DataSize            = SPI_DataSize_8b;  
    SPI_InitStructure. SPI_CPOL                = SPI_CPOL_Low;  
    SPI_InitStructure. SPI_CPHA                = SPI_CPHA_1Edge;  
    SPI_InitStructure. SPI_NSS                = SPI_NSS_Soft; 
    SPI_InitStructure. SPI_BaudRatePrescaler    = SPI_BaudRatePrescaler_128;  
    SPI_InitStructure. SPI_FirstBit            = SPI_FirstBit_MSB;  
    SPI_InitStructure. SPI_CRCPolynomial        = 7;  
    SPI_Init (SPI1,  &SPI_InitStructure) ;  
    SPI_Cmd (SPI1,  ENABLE) ;  // SPI1 enable    
    SPIINTInit () ; 
}

2. 2 接收数据时序
INT 需要设置为 GPIO INPUT, 下降沿触发模式. 当从设备有数据需要上报时, 会先拉低 INT, 此时 MCU 端会收到 GPIO 中断, 就知道需要读数据了.
27. png

static void SPIRxData (void) 
{
    INT16U temp = 0; 
    INT16U i; 
    WM_SPI_RX_DESC* rxdesc; 
    INT8U tempdata; 
    
    SPINSS (0) ; 
    SPIReadWriteByte (SPI_REG_INT_STTS) ;         //查询 SPI_INT_HOST_STTS
    temp |= SPIReadWriteByte (0xff) ;             //读寄存器, 字节序为小端
    temp |= SPIReadWriteByte (0xff)   " " 8; 
    SPINSS (1) ; 
    if ( (temp ! = 0xffff)  &&  (temp & 0x01) )     //数据或命令已准备好
    {
        SPINSS (0) ; 
        SPIReadWriteByte (SPI_REG_RX_DAT_LEN) ;     //查询 RX_DAT_LEN                                                                                                                                                                                                             
        temp |= SPIReadWriteByte (0xff) ; 
        temp |= SPIReadWriteByte (0xff)   " " 8; 
        SPINSS (1) ; 

        if (temp "  0) 
        {
            if (temp % 4) 
            {
                temp =  ( (temp + 3)  / 4)   " " 2; 
            }
            rxdesc = SPIGetRxBuff (temp) ; 
            if (rxdesc) 
            {
                SPINSS (0) ; 
                SPIReadWriteByte (SPI_CMD_RX_DATA) ;     //读数据命令
                for (i = 0;  i  " temp;  i++) 
                {
                    * (rxdesc-" buff+ i)  = SPIReadWriteByte (0xff) ; 
                //    SPI_PRINT ("[%d]=[%x]\r\n",  i,  * (rxdesc-" buff + i) ) ; 
                }
                SPINSS (1) ; 
                AppSendMsg (MSG_SPI,   (INT32U) rxdesc) ; 
            }
            else
            {
                SPINSS (0) ; 
                SPIReadWriteByte (SPI_CMD_RX_DATA) ;     //读数据命令
                for (i = 0;  i  " temp;  i++) 
                {
                    tempdata = SPIReadWriteByte (0xff) ; 
                //    SPI_PRINT ("[%d]=[%x]\r\n",  i,  * (rxdesc-" buff + i) ) ; 
                }
                SPINSS (1) ; 
                printf ("SPIRXData no buf\r\n") ; 
            }
        }
        else
        {
        //    printf ("SPIRXData data len = %04X\r\n",  temp) ; 
        }
    }
    else
    {
    //    printf ("SPIRXData SPI_REG_INT_STTS = %04X\r\n",  temp) ; 
    }
}

这里面有三个命令, 1, 查询中断状态命令, 是通过 spi 读 W800 的 0x06 寄存器, 然后接收返回的 16bit 回复, 回复的 16bit 数据中, 只有 bit0 有效, bit0=1 表示从设备准备好发送数据, bit0=0 表示从设备没有准备好数据. 2, 查询数据长度, 是通过 spi 读 W800 的 0x02 寄存器, 然后接收返回的 16bit 回复, 代表主设备需要读取的数据长度, 如果不是 4 的整数倍, 需要按 4 的整数倍读取, 因为 W800 的 SPI 使用 DMA 按字发送的. 3, 读数据命令, 是通过 spi 读取 W800 的 0x10 寄存器, 然后接收返回的数据, 长度在第二条命令里已经知道. 注意前两条命令返回的 16bit 结果都是小端模式.

2. 3 发送数据时序
发送数据时有两条命令, 1, 查询从设备的接收 buff 是否可用, 是通过 spi 读 W800 的 0x03 寄存器, 然后接收返回的 16bit 回复, 回复的 16bit 数据中, 只有 bit1 和 bit0 有效, bit1=1 表示 W800 的 cmd buff 可用, bit1=0 表示 W800 的 cmd buff 不可用, bit0=1 表示 W800 的数据 buff 可用, bit1=0 表示 W800 数据 buff 不可用. 2, 发送数据或命令, 是通过 spi 发送 0x91 (命令) 或者 0x90 (数据) , 后面跟要发送的数据内容, 不够四字节的, 需要补齐. 可以看到, 发送命令或者数据, 只是下发的寄存器地址不同, 对应到 W800 从设备接收调用的回调函数不同, 跟内容并无关系. 使用者可以自行选择.

INT8U SPITxCmd (INT8U *TXBuf,  INT16U CmdLen) 
{
    INT8U temp = 0; 
    INT16U i; 
    INT32U retry = 0; 
    
    if (NULL == TXBuf) 
    {
        SPI_PRINT ("SPITxCmd buff == NULL\r\n") ; 
        return 0; 
    }
    SPINSS (0) ; 
    while ( (temp ! = 0xffff)  &&  (0 ==  (temp & 0x02) ) )     
    {
        retry++; 
        SPIReadWriteByte (SPI_REG_TX_BUFF_AVAIL) ;         //查询 TX_BUFF_AVAIL
        temp |= SPIReadWriteByte (0xff) ;                     //读寄存器, 字节序为小端
        temp |= SPIReadWriteByte (0xff)   " " 8; 
        OSTimeDly (1) ; 
        if (retry "  SPI_TIMEOUT) 
        {
            SPI_PRINT ("SPI_CMD_TIMEOUT\r\n") ; 
            return 0; 
        }
    }
    SPINSS (1) ; 
    if (CmdLen "  0) 
    {
        if (CmdLen % 4) 
        {
            CmdLen =  ( (CmdLen + 3)  / 4)   " " 2; 
        }
    //    SPI_PRINT ("TX_BUFF_AVAIL = %d,  cmdlen=%d\r\n",  temp,  CmdLen) ; 
        SPINSS (0) ; 
        SPIReadWriteByte (SPI_CMD_TX_CMD) ;     //写发送命令命令
        for (i = 0;  i  " CmdLen;  i ++) 
        {
            SPIReadWriteByte (* (TXBuf + i) ) ; 
        }
        SPINSS (1) ; 
    }
    return 1;     
}

INT8U SPITxData (INT8U *TXBuf,  INT16U DataLen) 
{
    u16 temp = 0; 
    u16 i; 
    u16 retry=0; 
    
    if (NULL == TXBuf) 
    {
        return 0; 
    }
    SPINSS (0) ; 
    while ( (temp ! = 0xffff)  &&  (0 ==  (temp & 0x01) ) )     
    {
        retry ++; 
        SPIReadWriteByte (SPI_REG_TX_BUFF_AVAIL) ;     //查询 TX_BUFF_AVAIL
        temp |= SPIReadWriteByte (0xff) ;                 //读寄存器, 字节序为小端
        temp |= SPIReadWriteByte (0xff)   " " 8; 
    //    OSTimeDly (1) ; 
        if (retry "  SPI_TIMEOUT) 
        {
            SPI_PRINT (" TX_BUFF_AVAIL  SPI_TIMEOUT \r\n") ; 
            return 0; 
        }
    }
    SPINSS (1) ; 
    if (DataLen "  0) 
    {
        if (DataLen % 4) 
        {
            DataLen =  ( (DataLen + 3)  / 4)   " " 2; 
        }
        SPINSS (0) ; 
        SPIReadWriteByte (SPI_CMD_TX_DATA) ;     //写发送数据命令
        for (i = 0;  i  " DataLen;  i ++) 
        {
            SPIReadWriteByte (* (TXBuf + i) ) ; 
        }
        SPINSS (1) ; 
    }    
    return 1; 
}

3, 另外需要注意的是主设备和从设备之间的连线需要串一个阻值为 100 的电阻, 对于时钟频率高得情况下会有帮助, 避免出错.
4, STM32 做 HOST 驱动程序实现参考如下:
spi. rar

0 条评论

发布
问题