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);
            else
                gpio.level &= (uint16_t)(~((uint16_t)1 << spi[port].mosi_io0));

            if(wrDat & (1 << 5))
                gpio.level |= ((uint16_t)1 << spi[port].miso_io1);
            else
                gpio.level &= (uint16_t)(~((uint16_t)1 << spi[port].miso_io1));

            if(wrDat & (1 << 6))
                gpio.level |= ((uint16_t)1 << spi[port].io2);
            else
                gpio.level &= (uint16_t)(~((uint16_t)1 << spi[port].io2));

            if(wrDat & (1 << 7))
                gpio.level |= ((uint16_t)1 << spi[port].io3);
            else
                gpio.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 output
    spiCommandWrite(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);
    }
    #else
    sflashEraseChip(port);
    #endif
    clock_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的时序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值