基于ZYNQ PS-SPI的Flash驱动开发

news2024/11/27 6:14:44

       


        本文使用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语言)。

 调试和测试流程

  1. 读取芯片ID,验证SPI通路
  2. 验证全片擦除、页写入、读功能
  3. 页写入、读功能, 验证数据读写正确性
  4. 容量测试
  5. 测试读写时间 

 调试手段

  • 发送数据:写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 = 1
    set_csn();
    usleep(10);
    //CS = 0
    set_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_address01234567........8388604838860583886068388607
vol_address01........262143
data01........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(冗余)+4448

表: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;

}

 


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

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

相关文章

整合力-整合思维模型和领导力

整合力和领导力是组织成功的两大关键因素。在当今复杂多变的商业环境中&#xff0c;整合力和领导力的结合对于推动组织发展至关重要。本文将探讨整合力和领导力的概念、重要性以及如何有效整合二者以促进组织的成功发展。 ### 整合力的重要性 整合力指的是组织内部各个部门、…

CSS字体样式的使用,前端开发手册

零基础学web前端开发要怎么去学? 首先要学习的就是基础知识&#xff1a;html、css和JavaScript。HTML是内容&#xff0c;CSS是表现&#xff0c;JavaScript是行为。前端开发的门槛其实非常低&#xff0c;与服务器端语言先慢后快的学习曲线相比&#xff0c;前端开发的学习曲线是…

java: Compilation failed: internal java compiler error或者构建时内存溢出解决办法

启动项目的时候出现了java: Compilation failed: internal java compiler error问题 解决办法 统一JDK版本 一、查看项目的jdk&#xff08;CtrlAltshiftS&#xff09;&#xff1a;File ->Project Structure ->Project Settings ->Project 二、查看工程的jdk&…

硬盘删除的文件如何恢复?分享硬盘数据恢复方法

随着信息时代的飞速发展&#xff0c;硬盘作为我们储存数据的主要场所其重要性日益凸显。但硬盘数据的丢失或误删也成为了许多用户头疼的问题。当您发现重要的文件从硬盘中消失时不必过于焦虑。本文将为您介绍五种高效且原创的数据恢复策略&#xff0c;帮助您找回那些珍贵的文件…

爬虫学习笔记-requests爬取NBA得分榜

1.导入requests库,用于请求获取URL位置的资源 import requests 2.导入lxml库,解析及生成xml和html文件 from lxml import etree 3.定义发送请求的地址 url https://nba.hupu.com/stats/players 4.定义请求头 headers {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64…

CSS如何清除默认样式,想学web开发

一面 css 如何实现垂直居中&#xff1f;position 有哪几种&#xff1f;分别说一下对应的效果和用法css 选择器优先级js 原型链&#xff0c;问了js 如何创建对象页面渲染和响应 式MVVM 和 MVC的区别Vue 生命周期&#xff0c;在哪个阶段可以获取页面 dom 信息Vue 的组件通信 &am…

CSS的三种定位,响应式web开发项目教程

标准文档流 文档流&#xff1a;指的是元素排版布局过程中 戳这里领取完整开源项目&#xff1a;【一线大厂前端面试题解析核心总结学习笔记Web真实项目实战最新讲解视频】 &#xff0c;元素会默认自动从左往右&#xff0c;从上往下的流式排列方式。并最终窗体自上而下分成一行行…

Spring框架的优点

Spring框架是一个开放源代码的J2EE应用程序框架&#xff0c;是针对bean的生命周期进行管理的轻量级容器。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题&#xff0c;提供了功能强大的IOC、AOP及Web MVC等功能。 轻量级&#xff1a;相对于EJB框架而言。 Spring 体系结…

力扣543. 二叉树的直径

Problem: 543. 二叉树的直径 文章目录 题目描述思路复杂度Code 题目描述 思路 1.最大直径 左子树的最大深度 右子树的最大深度&#xff1b; 2.定义一个变量maxDiameter记录最大直径&#xff0c;并编写一个递归函数maxDepth&#xff0c;利用树的后序遍历每次递归求取leftMax&a…

智慧医疗新篇章:Java+SpringBoot技术探索

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

CSS如何清除默认样式,前端开发实训

前端的现状 提到现状&#xff0c;必须先提到一个概念 大前端。由于近几年互联网的发展&#xff0c;尤其是移动互联网的发展&#xff0c;有的大前端概念将 Native 归入前端的范畴&#xff0c;有的大前端概念将 Node 甚至只渲染页面的 PHP 归入前端范畴&#xff0c;但不管怎么说…

一文帮助快速入门Django

文章目录 创建django项目应用app配置pycharm虚拟环境打包依赖 路由传统路由include路由分发namenamespace 视图中间件orm关系对象映射操作表数据库配置model常见字段及参数orm基本操作 cookie和sessiondemo 创建django项目 指定版本安装django&#xff1a;pip install django3.…

部署DNS解析服务

一、安装软件&#xff0c;关闭防火墙&#xff0c;启动服务 1.yum install -y bind bind-utils bind-chroot 2.systemctl stop firewalld && setenforce 0 3.systemctl start named 二、工作目录 /var/named/chroot/etc #存放主配置文件 /var/named/chroot/var/n…

实施 ASPM 面临哪些挑战?

在保护组织的应用程序时&#xff0c;您可能听说过有关应用程序安全状态管理 (ASPM) 的热议。根据研究称&#xff0c;到 2026 年&#xff0c;超过 40% 的开发专有应用程序的组织将采用应用程序安全态势管理。您意识到它有可能彻底改变您的应用程序安全性。但你也明白&#xff0c…

html标签元素类型,web开发工具

面试题 HTML 1&#xff0c;html5有哪些新特性&#xff1f; 2&#xff0c;html5移除了那些元素&#xff1f; 3&#xff0c;如何处理HTML5新标签的浏览器兼容问题&#xff1f; 4&#xff0c;如何区分 HTML 和 HTML5&#xff1f; CSS 1&#xff0c;CSS 选择符有哪些&#xf…

解决前端性能问题:如何优化大量数据渲染和复杂交互?

✨✨祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、分页加载数据 二、虚拟滚动 三、懒加载 四、数据缓存 五、减少重绘和回流 …

docker 安装rabbitmq并配置hyperf使用

这里我想完成的是 制作消息&#xff08;多个协程制造&#xff09;——》推送到rabbitmq——》订阅消息队列——》消费消息&#xff08;ws协程客户端【一次消费多条】/ws前端&#xff09; 利用 WebSocket 协议让客户端和服务器端保持有状态的长链接&#xff0c;保存链接上来的客…

Doris——纵腾集团流批一体数仓架构

目录 前言 一、早期架构 二、架构选型 三、新数据架构 3.1 数据中台 3.2 数仓建模 3.3 数据导入 四、实践经验 4.1 准备阶段 4.2 验证阶段 4.3 压测阶段 4.4 上线阶段 4.5 宣导阶段 4.6 运行阶段 4.6.1 Tablet规范问题 4.6.2 集群读写优化 五、总结收益 六…

Scala 之舞:林浩然与杨凌芸的 IDEA 冒险

Scala 之舞&#xff1a;林浩然与杨凌芸的 IDEA 冒险 The Dance of Scala: The IDEA Adventure of Lin Haoran and Yang Lingyun 在那个阳光明媚的日子里&#xff0c;林浩然如同一位英勇的探险家&#xff0c;踏入了 Scala 的 IntelliJ IDEA 开发环境的奇妙领域&#xff0c;他带着…

返回两个数组中每个对应位置的两个元素的最小值np.fmin()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 返回两个数组中 每个对应位置的 两个元素的最小值 np.fmin() 选择题 以下代码输出的结果是&#xff1f; import numpy as np a1 [1,np.nan,3] a2 [3,2,1] print("【显示】a1",a1)…