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 条评论

发布
问题