W800 HSPI是做為SPI SLAVE設備來使用的,最高支持50M的時鐘,與主設備的連接如下圖所示:CS、CLK、MOSI、MISO四根線很容易理解,INT對於MCU端找一個GPIO設置為input,平時為高電平,當從設備有數據需要主動上報時,會變成低電平,直到MCU發送查詢中斷狀態寄存器命令後,才會恢複高電平。
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中斷,就知道需要讀數據了。
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