基于ZYNQ PS-SPI的Flash驱动开发
本文使用PS-SPI实现Flash读写,PS-SPI的基础资料参考Xilinx UG1085的文档说明,其基础使用方法是,配置SPI模式,控制TXFIFO/RXFIFO,ZYNQ的IP自动完成发送TXFIFO数据,接收数据到RXFIFO,FIFO深度为128Byte。本文介绍了使用PS-SPI的Flash开发。
软硬件介绍:
- 硬件平台:Xilinx ZYNQ
- Flash芯片:华邦W25Q80
- 软件平台:Vitis Standalone
芯片信息/配置:
- 容量:8Mbit
- SPI时钟:25MHZ
- IO电平:3.3V
- SPI FIFO深度:128Byte
- SPI 标准模式
方案:
在ZYNQ平台上使用PS的SPI进行读写Flash芯片,约束EMIO芯片管脚,在Vitis上读写SPI总线。
测试项目:
- 擦除、读、写功能
- 芯片容量
- 擦除、读、写速度
硬件设计
- 使能PS端的SPI(SPI0)模块,FIFO位宽8Bit
- 约束CS/DI/DO/CLK管脚
- 生成XSA,提供给软件
软件设计
- 使用PS SPI功能读写寄存器
- 封装读ID、写使能、读取状态、擦除、读、写接口(C语言)。
调试和测试流程
- 读取芯片ID,验证SPI通路
- 验证全片擦除、页写入、读功能
- 页写入、读功能, 验证数据读写正确性
- 容量测试
- 测试读写时间
调试手段
- 发送数据:写PS-SPI对应的写缓存地址,写入数据到“写FIFO缓冲区”,等待发送完成
- 读取数据:读PS-SPI对应的读缓存地址,读取“读FIFO缓冲区”数据,等待读取完成
- 信号分析:测试过程中使用逻辑分析仪抓取CS/DI/DO/CLK信号。
#define SPIPS_RECV_BYTE(BaseAddress) \Xil_In8((BaseAddress) + XSPIPS_RXD_OFFSET)#define SPIPS_SEND_BYTE(BaseAddress, Data) \Xil_Out8((BaseAddress) + XSPIPS_TXD_OFFSET, (Data))void spi_read(int byte_count)
{int count;u32 status_reg;status_reg = XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);/** Polling the Rx Buffer for Data*/do{status_reg = XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);}while(!(status_reg & XSPIPS_IXR_RXNEMPTY_MASK));/** Reading the Rx Buffer*/for(count = 0; count < byte_count; count++){g_read_buffer[count] = SPIPS_RECV_BYTE(g_spi0_handle.Config.BaseAddress);}}void spi_write(u8 *send_buffer, int byte_count)
{u32 status_reg;int trans_count = 0;status_reg = XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);while ((byte_count > 0) &&(trans_count < XSPIPS_FIFO_DEPTH)) {SPIPS_SEND_BYTE(g_spi0_handle.Config.BaseAddress,*send_buffer);send_buffer++;++trans_count;byte_count--;}/** Wait for the transfer to finish by polling Tx fifo status.*/do {status_reg = XSpiPs_ReadReg(g_spi0_handle.Config.BaseAddress,XSPIPS_SR_OFFSET);} while ((status_reg & XSPIPS_IXR_TXOW_MASK) == 0);}
代码:SPI读写接口
图:逻辑分析仪
1.读取芯片ID,验证SPI通路
- 写入"唤醒寄存器0xAB",后面再发送3个字节(数据0),共发送4个字节;再发送一个字节(为了提供时钟),读取FIFO数据5字节。
char release_powerdown_and_read_id()
{memset(g_write_buffer, 0x00, sizeof(g_write_buffer));g_write_buffer[0] = 0xAB;//CS = 1set_csn();usleep(10);//CS = 0set_cs0();spi_write(g_write_buffer,5);set_csn();spi_read(5);return g_read_buffer[4];
}
代码:读ID
注意的是,SPI只要有时钟,“读FIFO缓冲区”就会写入数据(MISO)。主机发送5个字节,接着读取5个字节,丢弃前4个数据,第5个就是读到的ID。 0xFF 0xFF 0xFF 0xFF 0x13
小结:读到的ID是0x13,和datasheet一致。
2.验证全片擦除
执行操作前,先配置写使能能为1,读取状态寄存器
全片擦除,写入"擦除全片寄存器0xC7"
等待BUSY信号为0时结束,判断信号间隔为1ms(参考官方驱动)
注意的是写完后,写时能标记为会置0
int wait_busy(int max_count)
{int r,busy,busy_cnt = 0x00;r = 0;do {busy = is_busy();if(busy == 1)usleep(1000);busy_cnt += 1;if (max_count > 0){if (busy == 0){r = 0;break;}if (busy_cnt > max_count){r = -1;break;}}} while(busy == 1);return r;
}void erase_entire()
{set_write_enable();g_write_buffer[0] = 0xC7;set_csn();usleep(10);set_cs0();spi_write(g_write_buffer,1);set_csn();spi_read(1);wait_busy(1000);return ;
}
代码:擦除全片
----------------------------------------
...erase entire chip...
<erase> entire consume time:831 ms
----------------------------------------
小结:全片擦除用了0.8S,比手册提供的2S典型值小。
3.页写入、读功能, 验证数据读写正确性
页读取
写入"读页数据寄存器0x03",后面跟一个24位地址,按照手册要求先发送高位地址,即依次发送addr[23:16],addr[15:8],addr[7:0]。主机还要继续写入提供时钟,写入一个页的数据(0)。读取“读FIFO缓冲区”数据,读取一个页的数据量,得到读取内容。
void page_read(int address, unsigned char * recv, int size)
{int i;set_csn();usleep(10);set_cs0();g_write_buffer[0] = 0x03;g_write_buffer[1] = address >> 16;g_write_buffer[2] = address >> 8;g_write_buffer[3] = address >> 0;spi_write(g_write_buffer,4);spi_read(4);g_write_buffer[0] = 0;memset(g_write_buffer, 0x00, sizeof(g_write_buffer));if (size > 128){spi_write(g_write_buffer,128);spi_read(128);memcpy(recv, g_read_buffer, 128);spi_write(g_write_buffer + 128,size - 128);spi_read(size - 128);memcpy(recv + 128, g_read_buffer, size - 128);}else{spi_write(g_write_buffer, size);spi_read(size);memcpy(recv, g_read_buffer, size);}set_csn();return;
}
代码:页读取
页写入
写入"写页数据寄存器0x02",后面跟一个24位地址,按照手册要求先发送高位地址,即依次发送addr[23:16],addr[15:8],addr[7:0]。主机继续写入一个页的数据,页数据Pattern是一个递增数据。
//Pattern
u8 data[] = {0x00,0x01,0x02....0x0FF};
读取“读FIFO缓冲区”数据,排空无用“读缓冲数据”。
void page_write(int address, unsigned char * data, int size)
{//int i;set_write_enable();set_csn();usleep(10);set_cs0();g_write_buffer[0] = 0x02;g_write_buffer[1] = address >> 16;g_write_buffer[2] = address >> 8;g_write_buffer[3] = address >> 0;spi_write(g_write_buffer,4);spi_read(4);if (size > 128){spi_write(data,128);spi_read(128);spi_write(data + 128,size - 128);spi_read(size - 128);}else{spi_write(data,size);spi_read(size);}wait_busy(1000);set_csn();return;
}
代码:页写入
验证
- 执行“擦除”操作
- 执行“页写入”操作
- 执行“页读取”操作
- 打印读取数据,比较写入数据和读取数据内容,使用memcmp进行比较。
void page_rw_test()
{char send_data[PAGE_SIZE] = {...};//自行填充char recv_data[PAGE_SIZE];erase_entire();page_write(0x00, send_data, PAGE_SIZE);page_read(0x00, recv_data, PAGE_SIZE);r = memcmp(send_data, recv_data, PAGE_SIZE);return r;
}
代码:页读写验证
小结:读写功能正常,数据比较一致。
4.容量测试
方法:每4个字节当作一个单元,每个单元数据递增1。写入再读出作比较。
芯片共有16x16x16个4K个页,往4K个页写入递增数据(0,262143)。打印部分页内容,对比所有写入数据和读取数据,使用memcmp进行比较。
表:地址和容量地址
byte_address | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | .. | .. | .. | .. | 8388604 | 8388605 | 8388606 | 8388607 | |
vol_address | 0 | 1 | ........ | 262143 | |||||||||||||
data | 0 | 1 | ........ | 262143 |
小结:打印内容符合递增预期,数据比较一致。
5.测试读写时间
容量测试加入时间打印,分别记录擦除时间、写入时间和读取时间。
时间计数方式,采用读取CPU计数器计数,转换成时间。
write_data/read_data是page_write/page_read的封装,可以写入/读取任意数据。
int entire_volume_test(const int value_start, int step)
{int blkn, r, value, i,last_value;int escape;XTime start,end;blkn = BLOCK_NUMBER;printf("[ entire volume test ]\r\n");printf("[information]:\r\n");printf("----------------------------------------\r\n");printf(" Capicity( Bit ):%d\r\n", blkn * BLOCK_SIZE * 8);printf(" Capicity(1Byte):%d\r\n", blkn * BLOCK_SIZE);printf(" Capicity(4Byte):%d [*]\r\n", blkn * BLOCK_SIZE/4);printf(" SPI CLK :%d MHZ\r\n", 25);printf("----------------------------------------\r\n");printf("[test parttern] value start:%d,step:%d\n", value_start, step);printf("[test parttern] value range:(%d , %d)\n", value_start, step * (blkn * BLOCK_SIZE/4 - 1));printf("----------------------------------------\r\n");printf("...erase entire chip...\r\n");start = get_sys_count();erase_entire();end = get_sys_count();escape = get_useconds(start, end);printf("<erase> entire consume time:%02d ms \r\n", escape/1000);usleep(200000);last_value = step * (blkn * BLOCK_SIZE/4 - 1);printf(" fill data\r\n");value = value_start;for (i = 0; i < blkn * BLOCK_SIZE/4; i++){g_data[i * 4 + 3 ] = (value >> 24) & 0xFF;g_data[i * 4 + 2 ] = (value >> 16) & 0xFF;g_data[i * 4 + 1 ] = (value >> 8) & 0xFF;g_data[i * 4 + 0 ] = (value >> 0) & 0xFF;value += step;}printf(" write data sequence\r\n");start = get_sys_count();write_data(0, g_data, blkn * BLOCK_SIZE);end = get_sys_count();escape = get_useconds(start, end);printf("<write> data consume time:%02d ms \r\n", escape/1000);printf(" reading.....\r\n");start = get_sys_count();read_data (0, g_recv_buffer,blkn * BLOCK_SIZE);end = get_sys_count();escape = get_useconds(start, end);printf("<read> consume time:%02d ms \r\n", escape/1000);printf(" dump last 2 page \r\n");printf("value will range:(%08d , %08d)\r\n", 1 + last_value - 2 * PAGE_SIZE/ 4, last_value - 1 * PAGE_SIZE/ 4);dec_print(g_recv_buffer + (blkn * BLOCK_SIZE - 2 * PAGE_SIZE) , PAGE_SIZE/4);printf("value will range:(%08d , %08d)\r\n", 1 + last_value - 1 * PAGE_SIZE/ 4, last_value - 0 * PAGE_SIZE/ 4);dec_print(g_recv_buffer + (blkn * BLOCK_SIZE - 1 * PAGE_SIZE) , PAGE_SIZE/4);printf("compare <write data> and <read data> values, compare size:%d Bytes\n", blkn * BLOCK_SIZE);printf("----------------------------------------\r\n");if (memcmp(g_data, g_recv_buffer, blkn * BLOCK_SIZE) == 0){printf(" [*] <pass> volume test !!!\r\n");printf("----------------------------------------\r\n");return 0;}printf("[*] !!<fail> volume test !!!\r\n");printf("----------------------------------------\r\n");return -1;
}
代码:容量测试
<write> page data consume time:1346 us
<read> page data consume time:275 us
...erase entire chip...
<erase> entire consume time:831 ms
fill data
write data sequence
<write> data consume time:5509 ms
reading.....
<read> consume time:1127 ms
----------------------------------------
[*] <pass> volume test !!!
----------------------------------------
记录测试结果到下表
测试项目 | 测试值(ms) | 参考值[典型值,最大值](ms) |
页写入时间 | 1.34 | [0.8, 3] |
页读取时间 | 0.28 | / |
全片擦除时间 | 831 | [2000, 6000] |
全片写入时间 | 5509 | [3276,24576] |
全片读取时间 | 1127 | / |
表:测试结果
总结:擦除速度比datasheet参考值快,其他均正常。
其他相关
Flash读写特性
Flash的特性是,写数据只能将1写为0,0不能写为1。擦除数据是将所有数据都写为1。因此如果想在已经数据的flash上写入新的数据,则必须先擦除。
Flash相关知识学习记录(以W25Q128为例)
芯片地址相关
以WQ25Q80为例,一个地址24位,由块地址、扇地址、页地址、页内偏移组成。
#define ADDRESS(block, sector, page, offset) ((block) << 16 | (sector) << 12 | (page) << 8 | (offset))
代码:使用C语言表示芯片地址
地址项 | 块地址 | 扇区地址 | 页地址 | 页内偏移 |
地址大小(bit) | 4(冗余)+4 | 4 | 4 | 8 |
表:WQ25Q80地址
比如一个地址0x04E3AA,表示块地址0x04,扇区地址0xE,页地址0x03,页内偏移0xAA。
关于CS使用
使用芯片时候需要把CS引脚拉低,在命令写完成后需要把CS引脚拉高。手册里都会有"The instruction is completed by driving /CS high"的说明,这也成为Flash芯片操作的通用操作。
关于PS-SPI软件配置
可以配置CS控制模式、时钟频率,时钟频率通过SPI主频分频得到,分频系数可配置。
int spi_init() {unsigned int config_value;int status;char spi_dev_id = SPI_DEVICE_ID;XSpiPs_Config *spi_config;/** Initialize the SPI device.*/spi_config = XSpiPs_LookupConfig(spi_dev_id);if (NULL == spi_config) {return XST_FAILURE;}status = XSpiPs_CfgInitialize(&g_spi_handle, spi_config, spi_config->BaseAddress);if (status != XST_SUCCESS) {return XST_FAILURE;}/** Perform a self-test to check hardware build.*/status = XSpiPs_SelfTest(&g_spi_handle);if (status != XST_SUCCESS) {return XST_FAILURE;}XSpiPs_ResetHw(spi_config->BaseAddress);printf("%s self test succ\r\n", __func__);status = XSpiPs_SetOptions(&g_spi_handle, XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION);//status = XSpiPs_SetOptions(&g_spi_handle, XSPIPS_MASTER_OPTION);if (status != XST_SUCCESS) {printf("%s XSpiPs_SetOptions fail\n", __func__);return XST_FAILURE;}/** PS SPI CLK DOMAIN 200MHZ* */status = XSpiPs_SetClkPrescaler(&g_spi_handle, XSPIPS_CLK_PRESCALE_8);if (status != XST_SUCCESS) {printf("%s XSpiPs_SetClkPrescaler fail\n", __func__);return XST_FAILURE;}XSpiPs_Enable(&g_spi_handle);printf("spi <%d> config finish\r\n", spi_dev_id);//config_value = XSpiPs_ReadReg(g_spi_handle.Config.BaseAddress,XSPIPS_CR_OFFSET);//printf("config_value :0x%08X\n", config_value);return XST_SUCCESS;}
相关文章:

基于ZYNQ PS-SPI的Flash驱动开发
本文使用PS-SPI实现Flash读写,PS-SPI的基础资料参考Xilinx UG1085的文档说明,其基础使用方法是,配置SPI模式,控制TXFIFO/RXFIFO,ZYNQ的IP自动完成发送TXFIFO数据,接收数据到RXFIFO,FIFO深度为12…...
Linux Shell:local关键字
Linux Shell:local关键字 在 Bash 中,local 是一个用于声明局部变量的关键字。当在函数内部使用 local 声明变量时,该变量只能在函数内部使用,并且不会对函数外部的同名变量产生影响。这样可以确保在函数内部定义的变量不会意外地…...
如何开发python毕业设计
开发Python毕业设计需要以下步骤: 选择项目主题: 选择一个与你的兴趣和专业相关的主题。确保主题具有一定的挑战性,但又不至于过于复杂,以确保你能够在规定时间内完成项目。 制定项目计划: 制定一个清晰的项目计划&…...

D*算法超详解 (D星算法 / Dynamic A*算法/ Dstar算法)(死循环解决--跟其他资料不一样奥)
所需先验知识(没有先验知识可能会有大碍,了解的话会对D*的理解有帮助):A*算法/ Dijkstra算法 何为D*算法 Dijkstra算法是无启发的寻找图中两节点的最短连接路径的算法,A*算法则是在Dijkstra算法的基础上加入了启发函数…...

django学习记录07——订单案例(复选框+ajax请求)
1.订单的数据表 1.1 数据表结构 1.2 数据表的创建 models.py class Order(models.Model):"""订单号"""oid models.CharField(max_length64, verbose_name"订单号")title models.CharField(max_length64, verbose_name"名称&…...

Qt 定时器事件
文章目录 1 定时器事件1.1 界面布局1.2 关联信号槽1.3 重写timerEvent1.4 实现槽函数 启动定时器 2 定时器类 项目完整的源代码 QT中使用定时器,有两种方式: 定时器类:QTimer定时器事件:QEvent::Timer,对应的子类是QTi…...

LLM 推理优化探微 (2) :Transformer 模型 KV 缓存技术详解
编者按:随着 LLM 赋能越来越多需要实时决策和响应的应用场景,以及用户体验不佳、成本过高、资源受限等问题的出现,大模型高效推理已成为一个重要的研究课题。为此,Baihai IDP 推出 Pierre Lienhart 的系列文章,从多个维…...

JavaEE进阶(15)Spring原理:Bean的作用域、Bean的生命周期、Spring Boot自动配置(加载Bean、SpringBoot原理分析)
接上次博客:JavaEE进阶(14)Linux基本使用和程序部署(博客系统部署)-CSDN博客 目录 关于Bean的作用域 概念 Bean的作用域 Bean的生命周期 源码阅读 Spring Boot自动配置 Spring 加载Bean 问题描述 原因分析 …...

ELK-介绍及Elasticsearch集群搭建
ELK是三个开源软件的缩写,分别为Elasticsearch、Logstash、kibana它们都是开源软件。后来新增了一个FileBeat,它是一个轻量及的日志收集处理工具,因为Logstash由java程序开发,比较消耗内存资源,后来将Logstash使用go语…...
保障数据安全,提升性能:探秘Redis AOF持久化机制在在线购物网站的应用
AOF(Append-Only File)日志介绍 Redis使用AOF持久化来保证数据的可靠性。AOF日志是一个追加写文件,记录了所有对Redis数据进行修改的命令。 AOF的常规用途 通常,人们将Redis的AOF用于将后端数据库中的数据存储在内存中…...

魔众智能AI系统v2.1.0版本支持主流大模型(讯飞星火、文心一言、通义千问、腾讯混元、Azure、MiniMax、Gemini)
支持主流大模型(讯飞星火、文心一言、通义千问、腾讯混元、Azure、MiniMax、Gemini) [新功能] 系统全局消息提示 UI 全新优化 [新功能] JS 库增加【ijs】类型字符串,支持默认可执行代码 [新功能] 分类快捷操作工具类 CategoryUtil [新功能…...
抖音视频评论区用户采集工具使用教程
抖音视频评论区用户采集工具是一款用于收集抖音视频评论区用户信息的工具。通过该工具,用户可以提取抖音视频评论区的用户昵称、评论内容、点赞数等信息,并进行数据分析和统计。该工具可以帮助用户了解抖音视频评论区的用户特点和评论趋势,提…...
c 不同类型指针的转换
int 指针与unsigned char类型指针互转 #include <stdio.h> #include <stdlib.h>int main(void){int a(0x1<<24)|(0x2<<16)|(0x3<<8)|0x4; //0x1020304printf("16进制:%x\n",a);u_int8_t *p(u_int8_t *)&a; //int指针转为unsig…...

16 PyTorch 神经网络基础【李沐动手学深度学习v2】
1. 模型构造 在构造自定义块之前,我们先回顾一下多层感知机的代码。 下面的代码生成一个网络,其中包含一个具有256个单元和ReLU激活函数的全连接隐藏层, 然后是一个具有10个隐藏单元且不带激活函数的全连接输出层。 层和块 构造单层神经网咯…...

java数据结构与算法刷题-----LeetCode216. 组合总和 III
java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 文章目录 解题思路 此题是77题的扩展题,仅仅加了一个条件而已&…...
vscode remote ssh 连接 ubuntu/linux报错解决方法
1、问题: WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fin…...

Normalizer(归一化)和MinMaxScaler(最小-最大标准化)的区别详解
1.Normalizer(归一化)(更加推荐使用) 优点:将每个样本向量的欧几里德长度缩放为1,适用于计算样本之间的相似性。 缺点:只对每个样本的特征进行缩放,不保留原始数据的分布形状。 公式…...

覆盖element-ui的el-menu样式记录:背景图片、菜单图标、菜单高亮与鼠标悬浮高亮、调整子菜单等样式
页面中修改el-menu 设置background-color"transparent",menu菜单下的背景图片则能正常显示了 <el-menuclass"el-menu-demo"mode"horizontal"background-color"transparent"><el-menu-item index"1">…...

接口自动化测试从入门到高级实战!
接口测试背景和必要性 接口测试是测试系统组件间接口(API)的一种测试,主要用于检测内部与外部系统、内部子系统之间的交互质量,其测试重点是检查数据交换、传递的准确性,控制和交互管理过程,以及系统间相互…...

【STC8A8K64D4开发板】第2-14讲:I2C总线的应用
第2-14讲:I2C总线的应用 学习目的了解I2C总线的特点。掌握I2C地址的定义,对I2C地址要有深刻的了解,之后再看到I2C接口设备中描述的7位地址或8位地址,不会再有疑惑。掌握STC8A8K64D4系列单片机I2C的特点以及编程方法。掌握通过I2C读…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...