本文主要是介绍libftdi1学习笔记 6 - MPSSE QSPI,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
1. 写
2. 读
3. 验证
QSPI采用4根线为数据口,SCK和CS保留同样的功能,一般4个数据线采用MSB的方式发送数据,即高位在前。
QSPI只能是半双工工作。
1. 写
int qspiWriteBytes(uint8_t port, uint8_t* wrBuf, uint16_t len)
命令缓存的大小小于SPI的方式
int commandlength = size * 2 * 3 * 3 * spi[port].freq + 1 + 3 + 3;
在写之前把4个数据口设置为输出
gpio.dir |= ((uint16_t)1 << spi[port].mosi_io0) | ((uint16_t)1 << spi[port].miso_io1)| ((uint16_t)1 << spi[port].io2) | ((uint16_t)1 << spi[port].io3);
QSPI的写和SPI的模式0写类似,区别是只发送,不需要读入。最大的区别是每个字节的发送
while(size > 0){int i;uint8_t wrDat;wrDat = wrBuf[j]; for(i = 0; i < 2; i++){if(wrDat & (1 << 4))gpio.level |= ((uint16_t)1 << spi[port].mosi_io0);elsegpio.level &= (uint16_t)(~((uint16_t)1 << spi[port].mosi_io0));if(wrDat & (1 << 5))gpio.level |= ((uint16_t)1 << spi[port].miso_io1);elsegpio.level &= (uint16_t)(~((uint16_t)1 << spi[port].miso_io1));if(wrDat & (1 << 6))gpio.level |= ((uint16_t)1 << spi[port].io2);elsegpio.level &= (uint16_t)(~((uint16_t)1 << spi[port].io2));if(wrDat & (1 << 7))gpio.level |= ((uint16_t)1 << spi[port].io3);elsegpio.level &= (uint16_t)(~((uint16_t)1 << spi[port].io3));wrDat <<= 4;spiCommandWrite(port, 1);spiSCKHigh(port);spiCommandWrite(port, 1);spiSCKLow(port);spiCommandWrite(port, 1);}j++;size--;}
写完后要把gpio的方向改回来
gpio.dir &= (uint16_t)(~(((uint16_t)1 << spi[port].miso_io1)| ((uint16_t)1 << spi[port].io2) | ((uint16_t)1 << spi[port].io3)));spiCommandWrite(port, 1);
2. 读
int qspiReadBytes(uint8_t port, uint8_t* rdBuf, uint16_t len)
同样在读之前设置4个io为输入
gpio.dir &= (uint16_t)(~(((uint16_t)1 << spi[port].mosi_io0) | ((uint16_t)1 << spi[port].miso_io1)| ((uint16_t)1 << spi[port].io2) | ((uint16_t)1 << spi[port].io3)));
命令缓存拼接:
int size = len * 2;int commandlength = size * (2 * 3 * spi[port].freq + 1) + 3 + 1 + 3;spi[port].pCommand = (uint8_t *)malloc(commandlength);spi[port].iCommand = 0;gpio.dir &= (uint16_t)(~(((uint16_t)1 << spi[port].mosi_io0) | ((uint16_t)1 << spi[port].miso_io1)| ((uint16_t)1 << spi[port].io2) | ((uint16_t)1 << spi[port].io3)));while(size > 0){spiSCKHigh(port);spiCommandWrite(port, 1);spi[port].pCommand[spi[port].iCommand++] = gpioReadCommand[gpioCommand(port)];spiSCKLow(port);spiCommandWrite(port, 1);size--;}
同样将gpio的方向改为默认
gpio.dir |= (uint16_t)1 << spi[port].mosi_io0; //mosi outputspiCommandWrite(port, 1);
发送完命令缓存后,需要读回数据
int rdLen = len * 2;uint8_t *pReadBuf = (uint8_t *)malloc(rdLen);int readtimeout = spi[port].ftdi->usb_read_timeout;spi[port].ftdi->usb_read_timeout = spi[port].iCommand + rdLen * spi[port].freq;ret = ftdi_read_data(spi[port].ftdi, pReadBuf, rdLen);//printf("read data number:%d\n", ret);if(ret < rdLen){//try again//printf("retry read:%d\n", ret);int remain;if(ret < 0){remain = rdLen;ret = ftdi_read_data(spi[port].ftdi, pReadBuf, rdLen);} else{remain = rdLen - ret;ret = ftdi_read_data(spi[port].ftdi, pReadBuf + ret, remain);}if(ret + remain < rdLen){if(pReadBuf)free(pReadBuf);spi[port].ftdi->usb_read_timeout = readtimeout;printf("spi transfer read fail\n");return -4;}}spi[port].ftdi->usb_read_timeout = readtimeout;
对读回来的数据进行拼接
if(rdBuf != NULL){int j = 0;for(int i = 0; i < rdLen; i++){int max = i + 2;uint8_t tmp = 0;for(; i < max; i++){if(rdBuf[i] & ((uint8_t)1 << (spi[port].mosi_io0 % 8)))tmp |= 0x01;if(rdBuf[i] & ((uint8_t)1 << (spi[port].miso_io1 % 8)))tmp |= 0x02;if(rdBuf[i] & ((uint8_t)1 << (spi[port].io2 % 8)))tmp |= 0x04;if(rdBuf[i] & ((uint8_t)1 << (spi[port].io3 % 8)))tmp |= 0x08;tmp <<= 4;}rdBuf[j++] = tmp;i--;}if(pReadBuf)free(pReadBuf);}
3. 验证
基于SPI Nor Flash的验证方式,增加io2和io3的初始化
spiSetting.io2 = 4;spiSetting.io3 = 5;
将SFlash的io类型改为QSPI
sflashSetting.ioType = SFLASH_QSPI;//SFLASH_SPI;
增加计算时间差
struct timespec start, end; long long diff; clock_gettime(CLOCK_REALTIME, &start);#if (EX_SFLASH_SIZE < 4096 * 10)for(i = EX_SFALSH_ADDR / 4096; i < (EX_SFALSH_ADDR + EX_SFLASH_SIZE + 4095) / 4096; i++){sflashEraseSector(port, i * 4096);}#elsesflashEraseChip(port);#endifclock_gettime(CLOCK_REALTIME, &end);diff = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);diff /= 1000000;printf("erase time:%dms\n", (int)diff); clock_gettime(CLOCK_REALTIME, &start);sflashWrite(port, EX_SFALSH_ADDR, wrBuf, EX_SFLASH_SIZE);clock_gettime(CLOCK_REALTIME, &end);diff = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);diff /= 1000000;printf("write time:%dms\n", (int)diff); clock_gettime(CLOCK_REALTIME, &start);sflashRead(port, EX_SFALSH_ADDR, rdBuf, EX_SFLASH_SIZE);clock_gettime(CLOCK_REALTIME, &end); diff = (end.tv_sec - start.tv_sec) * 1000000000LL + (end.tv_nsec - start.tv_nsec);diff /= 1000000;printf("read time:%dms\n", (int)diff); for(i = 0; i < EX_SFLASH_SIZE; i++){if(wrBuf[i] != rdBuf[i])//if(rdBuf[i] != 0xff){printf("sflash test fail %d: %x!=%x\n", i, wrBuf[i], rdBuf[i]);break;}}printf("sflash test finish\n");
设置1MB的读写数据,验证结果如下:
Open device OK: 0
sflash Winbond
sflash size 16MB, 24bits address
status:3e
sflash id is:ef4018
Erase Chip Finish
erase time:41035ms
write time:16402ms
read time:4180ms
sflash test finish
这里读是体现QSPI速度的,1MB/0.418 = 2.4MB/s。
可以反过来看一下SPI的速度。sflashSetting.ioType改为SPI。
erase time:42027ms
write time:33931ms
read time:21780ms
速度是:1MB/2.178 = 470KB/s。
可以看出模拟SPI的方式速度有点慢,看样子还是要采用官方的方式来实现SPI的时序。
这篇关于libftdi1学习笔记 6 - MPSSE QSPI的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!