FT93x支持7个USB interface,interface 1,2,3,4作为UART使用,可以用interface 5作为USB2SPI Master的interface,不过这里想试试VCP驱动,所以需要用到interface 1, 2, 3, 4,考虑只有FT930支持4个UART,选择1,2作为UART Interface,3选做SPI Master,4作为GPIO和配置控制,5选做I2C Master, 6选做SPI Slaver,7选做I2C Slaver,
FT93x支持4路SPI Master(可以支持SPI、DSPI、QSPI)。这里先只考虑SPI的情况,其中SCK、MISO、MOSI共用,4个CS脚分别控制,这里只考虑SPI0的情况。
1. 初始化IO
#define GPIO_SPIM_CLK 34
#define GPIO_SPIM_MISO 35
#define GPIO_SPIM_MOSI 36
#define GPIO_SPIM_SS0 30
#define GPIO_SPIM_SS1 31
#define GPIO_SPIM_SS2 32
#define GPIO_SPIM_SS3 33
#define pad_spim_ss0 pad30_spim_ss0
//SPI IO initial
gpio_function(GPIO_SPIM_CLK, pad_spim_sck);
gpio_function(GPIO_SPIM_MOSI, pad_spim_mosi);
gpio_function(GPIO_SPIM_MISO, pad_spim_miso);
gpio_function(GPIO_SPIM_SS0, pad_spim_ss0);
gpio_function(GPIO_SPIM_SS1, pad_spim_ss1);
gpio_function(GPIO_SPIM_SS2, pad_spim_ss2);
gpio_function(GPIO_SPIM_SS3, pad_spim_ss3);
gpio_dir(GPIO_SPIM_CLK, pad_dir_output);
gpio_dir(GPIO_SPIM_MOSI, pad_dir_output);
gpio_dir(GPIO_SPIM_MISO, pad_dir_input);
gpio_dir(GPIO_SPIM_SS0, pad_dir_output);
2. 初始化SPI
/* Enable the SPI Master device... */
sys_enable(sys_device_spi_master);
/* Init/configure SPI master with fifo enabled and depth of 64 bytes */
spi_init(SPIM, spi_dir_master, spi_mode_0, 8); //FREQ = 100M / 8, Value: 4/8/16/.../256
spi_option(SPIM,spi_option_fifo_size,64); //FIFO size: 16(default) or 64
spi_option(SPIM,spi_option_fifo,1); //Enable FIFO
spi_option(SPIM,spi_option_fifo_receive_trigger,1);
3. 主循环中SPIM处理函数,当接收到USB数据时,把数据写入SPI,并从SPI读回相应长度的数据,然后把读到的数据再回传给USB。
void usb2spimHandle(uint8_t idx)
{
int32_t count = 0;
uint8_t bufIn[USB_BUF_MAX] = {0};
uint8_t bufOut[USB_BUF_MAX] = {0};
count = D2XX_Read(USB_INTERFACE_SPIM, bufOut, USB_BUF_MAX);
if(count > 0)
{
spi_xchangen(SPIM, bufIn, bufOut, count);
D2XX_Write(USB_INTERFACE_SPIM, bufIn, count);
}
}
3. 为了通信速度,选择Interface 4作为配置通道(理论上也可以只使用Interface 3完成SPI通信,只需要自定义一套协议,这样不需要打开2路),设置选择哪个SPI作为通信,这里自定义一套协议。
void usb2cfgLoop(void)
{
int32_t count = 0;
uint8_t buf[USB_BUF_MAX] = {0};
count = D2XX_Read(USB_INTERFACE_CFG, buf, USB_BUF_MAX);
if(count > 0)
{
//dbg("cfg cmd:%d\r\n", buf[0]);
switch(buf[0]) //cfg command
{
case USB2CFG_CMD_CFG_UART:
break;
case USB2CFG_CMD_CFG_SPI: //Config SPI
break;
case USB2CFG_CMD_SEL_SPI: //Select SPI
if(buf[2] == 0)
spi_open(SPIM, buf[1]);
else
spi_close(SPIM, buf[1]);
break;
case USB2CFG_GPIO_OUTPUT:
gpio_write(buf[1], buf[2]);
break;
case USB2CFG_NULL:
default:
break;
}
D2XX_Write(USB_INTERFACE_CFG, buf, count);
}
}
4. 测试平台采用C# +VCP驱动(非D2XX驱动) + VM816C,即PC端看起来是一个USB转UART设备,但是FT930实现的SPI功能,C#编程采用的是普通串口的API。C#端开2个serialport,分别为serialPort和serialPortCfg,先实现GPIO复位BT816,GPIO15复位BT816。
public override void reset()
{
byte[] buf = new byte[3];
buf[0] = (byte)eCfgCmd.USB2CFG_GPIO_OUTPUT;
buf[1] = 15;
buf[2] = 0;
serialPortCfg.Write(buf, 0, 3);
while (serialPortCfg.BytesToRead < 3) ;
serialPortCfg.Read(buf, 0, 3);
Thread.Sleep(10);
buf[0] = (byte)eCfgCmd.USB2CFG_GPIO_OUTPUT;
buf[1] = 15;
buf[2] = 1;
serialPortCfg.Write(buf, 0, 3);
while (serialPortCfg.BytesToRead < 3) ;
serialPortCfg.Read(buf, 0, 3);
Thread.Sleep(10);
}
6. 实现SPIM的CS选择
public override void ft8xxCSEn()
{
byte[] buf = new byte[3];
buf[0] = (byte)eCfgCmd.USB2CFG_CMD_SEL_SPI;
buf[1] = 0;
buf[2] = 0;
serialPortCfg.Write(buf, 0, 3);
while (serialPortCfg.BytesToRead < 3) ;
serialPortCfg.Read(buf, 0, 3);
}
public override void ft8xxCSDis()
{
byte[] buf = new byte[3];
buf[0] = (byte)eCfgCmd.USB2CFG_CMD_SEL_SPI;
buf[1] = 0;
buf[2] = 1;
serialPortCfg.Write(buf, 0, 3);
while (serialPortCfg.BytesToRead < 3) ;
serialPortCfg.Read(buf, 0, 3);
}
7. 实现SPI的通信API函数,因为串口通信不能一次把所有数据发送完(大笔数据时),所以API函数中采用分批写入数据,因为USB通信采用相同的全双工的方式,写入多少数据就会得到多少数据,所以发送完后等待接收数据(实际应该要加Timeout处理)
private int comTranserSize = 2048 * 1;
public override void spiTransferBytes(byte[] wrBuf, byte[] rdBuf, UInt32 len)
{
int offset = 0;
int length = (int)len;
while(length > 0)
{
int count = length > comTranserSize ? comTranserSize : (int)length;
//Console.WriteLine("len:" + length + " offset:" + offset + " cnt:" + count);
serialPort.Write(wrBuf, offset, count);
while (serialPort.BytesToRead < count)
{
Thread.Sleep(1);
}
serialPort.Read(rdBuf, offset, count);
offset += count;
length -= count;
}
}
8. 通信速度的研究
每次通信的字节数(comTranserSize )会影响到通信速度,目前发现配置为2048时是最快的,可以达到约1MByte/s,如果配置成4096通信就会失败。
注意:该速度是在COM通信中延时计时器默认是16ms。
而4096可能是和USB传输大小设置有关,最大只能到4096。
如果修改延时计时器为1ms,通信也会失败,应该是MCU端处理不过来了,改成8ms测试通信速度也并不会变快。
在Loop测试中使用D2XX验证数据可以达到7MByte/s,这里使用VCP却只能到1MByte/s,感觉如果使用D2XX的话速度应该可以做到更快(待验证)。