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 條評論

發布
問題