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的时序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RAG学习笔记系列(一)

RAG 介绍 RAG 全称为 Retrieval Augmented Generation&#xff08;检索增强生成&#xff09;。是基于LLM构建系统的一种架构。 RAG 基本上可以理解为&#xff1a;搜索 LLM prompting。根据用户的查询语句&#xff0c;系统会先使用搜索算法获取到相关内容作为上下文&#xff0…

JavaScript如何设置定时器,怎么清除定时器

在JavaScript中&#xff0c;你可以使用setTimeout()和setInterval()函数来设置定时器&#xff0c;以及使用clearTimeout()和clearInterval()函数来清除定时器。 setTimeout() setTimeout()函数用于在指定的毫秒数之后执行一个函数或计算表达式。它返回一个表示定时器的ID&…

pnpm 报错: ERR_PNPM_META_FETCH_FAIL

今天突然遇到一个报错&#xff0c;pnpm 报错&#xff1a; ERR_PNPM_META_FETCH_FAIL  GET https://registry.npm.taobao.org/vue%2Fcli-service: request to https://registry.npm.taobao.org/vue%2Fcli-service failed, reason: certificate has expired问题原因&#xff1a;…

when语法学习及判断主机别名实例

目录 1、概念 2、实例配置 2.1 编写剧本体验when的用法 2.2 执行剧本文件 2.3 使用ansible临时命令进行测试 1、概念 when是用于判断的语法&#xff0c;将其用在每个动作的下方进行判断&#xff0c;使得只有在满足条件才会执行。 2、实例配置-判断主机别名 在每个客户端中…

移动端uniapp下载文件并保存到手机,下载准考证保存到手机

移动端uniapp下载文件并保存到手机&#xff0c;下载准考证保存到手机 1. 使用uni.request下载文件 uni.request({ url: http://example.com/file_url, // 要下载的文件的URL method: GET, responseType: arraybuffer, // 响应类型为arraybuffer (二进制形式) success: functi…

Compose 基础组件

文章目录 Compose 基础组件Modifier 修饰符Scaffold 脚手架 Compose 基础组件 Modifier 修饰符 在传统视图体系中&#xff0c;使用XML文件描述组件的样式&#xff0c;而在Compose中使用Modifier&#xff0c;每个基础的Composable组件都有一个modifier参数&#xff0c;通过Mod…

使用CCS软件查看PID曲线

在刚开始学习PID的时候&#xff0c;都需要借助PID的曲线来理解比例&#xff0c;积分&#xff0c;微分这三个参数的具体作用。但是这些曲线生成一般都需要借助上位机软件或者在网页上才能实现。如果是在单片机上调试程序的话&#xff0c;想要看曲线&#xff0c;一般就是通过串口…

【YOLOv5】使用yolov5训练模型时报错合集

文章目录 前言问题1 -- VsCode终端无法进入Anaconda创建的虚拟环境【问题描述】【问题分析】【解决方式】方法一方法二 问题2 -- 怎么在VsCode中为项目配置Anaconda创建的虚拟环境【问题描述】【解决方式】 问题3 -- yolov5训练模型时报错RuntimeError: result type Float cant…

web自动化测试系列-selenium常用方法定位(五)

目录 1.selenium的定位方法 2.操作案例 3.实现代码 前面我们介绍了html页面元素主要是通过标签和属性来进行定位 &#xff0c;只要满足唯一&#xff0c;无论是标签还是属性 &#xff0c;都能进行定位 。当然 &#xff0c;我们要通过selenium来进行定位 &#xff0c;同样还是…

富格林:利用正规方法提升出金收益

富格林悉知&#xff0c;黄金现货是众多黄金产品当中交易规则优势较为明显的存在&#xff0c;尤其是近段时间的行情走势十分强劲&#xff0c;这使得投资者们获得了更充足的做单机会。然而&#xff0c;要想在炒黄金的市场上取得成功&#xff0c;除了对市场有深刻的理解外&#xf…

总结:css中水平居中

css水平居中功能很常用&#xff0c;但一直没有系统的总结过&#xff0c;今天来总结水平下常见的方式&#xff1a; 1. 文本或内联元素的居中&#xff1a; 使用 text-align: center; 属性可以让内部的文本和内联元素在容器中水平居中。 .container {text-align: center; }2. 块…

小公司应该选择docker swarm还是k8s

对于小公司选择 Docker Swarm 还是 Kubernetes&#xff08;简称 K8s&#xff09;作为容器编排平台&#xff0c;应考虑以下几个关键因素&#xff1a; 技术复杂度与学习曲线&#xff1a; Docker Swarm&#xff1a;因其与 Docker 紧密集成&#xff0c;使用 Docker Compose 语法作…

MongoDB聚合运算符:$ne

文章目录 语法举例 $ne聚合运算符用于比较两个值是否不相等&#xff0c;如果他们的值不相等&#xff0c;返回true&#xff1b;如果他们的值相等&#xff0c;返回false。 语法 { $ne: [ <expression1>, <expression2> ] }$ne可以用来比较任何类型的值&#xff0c;…

【C++风云录】梦幻般的机器人世界:探索ROS、PCL、OpenCV和更多顶尖技术

机器人技术与计算机视觉世界&#xff1a;ROS、PCL、OpenCV等综合指南 前言 本文旨在深入探讨ROS&#xff08;机器人操作系统&#xff09;、PCL&#xff08;点云库&#xff09;、OpenCV&#xff08;开源计算机视觉库&#xff09;、Eigen&#xff08;线性代数库&#xff09;、B…

基于SpringBoot的在线五子连珠的设计与实现,前端采用vue框架;后端采用SpringBoot,mybatis

介绍 基于SpringBoot的在线五子连珠的设计与实现&#xff0c;主要是设计一款五子棋游戏&#xff0c;涉及登录注册的功能&#xff0c;人机对战、联机对战和积分排行榜的功能。其中人机对战中&#xff0c;电脑采用的是采用了一种基于局面分析的评分算法来确定机器人的下一步落子…

线程池学习(通俗易懂)

线程池 线程池是什么ThreadPoolExecutor模拟实现线程池结语 线程池是什么 假设我们要频繁的创建线程和销毁线程,但是创建线程和销毁线程是有成本的. 所以我们可以提前创建一批线程,后面需要使用的时候,直接拿就可以了,这就是线程池. 当线程不再使用的时候,就归还到池子里.为什…

图片懒加载

看下tuniu的页面&#xff0c;这有两个属性src和data-src <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><t…

SpringBoot3 + Kotlin + mybatis-plus + Swagger3后端开发样例

前言&#xff1a; Kotlin 是一种在 JVM&#xff08;Java 虚拟机&#xff09;、Android 和浏览器端运行的静态类型编程语言。以下是关于 Kotlin 的总结介绍&#xff1a; 1、语言特性&#xff1a; 简洁性&#xff1a;Kotlin 旨在提供简洁且安全的代码&#xff0c;同时保持与 Jav…

什么是反向 ETL?为什么它很有价值?

提取、转换、加载 &#xff08;ETL&#xff09; 过程已经成熟并被广泛采用。 它只涉及从各种源系统中获取数据&#xff0c;将其转换为标准化数据模型&#xff0c;然后将其加载到数据仓库中。从那里&#xff0c;您的团队使用其商业智能 &#xff08;BI&#xff09; 和分析工具中…

云轴科技ZStack入选中国信通院《高质量数字化转型产品及服务全景图(2023年度)》

近日&#xff0c;由中国互联网协会主办、中国信通院承办的“2024高质量数字化转型创新发展大会”暨“铸基计划”年度会议在北京成功召开。 本次大会发布了2024年度行业数字化转型趋势&#xff0c;总结并展望了“铸基计划”2023年取得的工作成果及2024年的工作规划。同时&#…