STM32—I2C

news/2024/10/11 6:40:46/文章来源:https://blog.csdn.net/m0_60322134/article/details/142303014

1.I2C

  • I2C总线(Inter l0 BUs)是由Philips公司开发的一种通用数据总线
  • 两根通信线:SCL(Serial Clock)、SDA(Serial Data)
  • 同步,半双工
  • 带数据应答
  • 支持总线挂载多设备(一主多从、多主多从)

MPU6050模块:可以进行姿态测量,使用了12C通信协议

第3个图片是AT24C02,存储器模块

第4个图片是DS3231,实时时钟模块

2.硬件电路

  • 所有I2C设备的SCL连在一起,SDA连在一起
  • 设备的SCL和SDA均要配置成开漏输出模式
  • SCL和SDA各添加一个上拉电阻,阻值一般为4.7KQ左右

这个图就是I2C的一个典型电路模型,这是一个一主多从的模型,左边CPU就是我们的单片机,作为总线的主机,主机的权力很大,包括,对SCL线的完全控制,任何时候,都是主机完全掌控SCL线,另外在空闲状态下,主机可以主动发起对SDA的控制,只有在从机发送数据和从机应答的时候,主机才会转交SDA的控制权给从机,这就是主机的权力

然后看下面,这一系列都是被控IC,也就是,挂载在I2C总线上的从机,这些从机可以是姿态传感器、OLED、存储器、时钟模块等,从机的权利比较小,对于SCL时钟线,在任何时刻都只能被动的读取,从机不允许控制SCL线,对于SDA数据线,从机不允许主动发起对SDA的控制,只有在主机发送读取从机的命令后,或者从机应答的时候,从机才能短暂地取得SDA的控制权,这就是一主多从模型中协议的规定

然后看接线,这里第一条写的是,所有I2C设备的SCL连在一起,SDA连在一起,这就是接线要求,在这个图里可以看到,主机SCL线一条拽出来,所有从机的SCL都接在这上面,主机SDA线也是一样,拽出来,所有从机的SDA接在这上面,这就是SCL和SDA的接线方式

先忽略上面两个电阻,假设我们就这样连接,那如何规定每个设备SCL和SDA的输入输出模式呢,SCL应该好规定,因为现在是一主多从,主机拥有SCL的绝对控制权,所以主机的SCL可以配置成推挽输出,所有从机的SCL都配置成浮空输入或者上拉输入,数据流向是,主机发送,所有从机接收,但是到SDA线这里,就比较麻烦了,因为这是半双工的协议,所以主机的SDA在发送的时候是输出,同样,从机的SDA也会在输入和输出之间反复切换,如果你能协调好输入输出的切换时机,那其实也没问题,但是这样做,如果总线时序没协调好,极有可能发生两个引脚同时处于输出的状态,如果这时又正好是一个输出高电平,一个输出低电平,那这个状态就是电源短路,这个状态是要极力避免的,所以为了避免总线没协调好导致电源短路这个问题,12C的设计是,禁止所有设备输出强上拉的高电平,采用外置弱上拉电阻加开漏输出的电路结构,这两点规定就是上面的这两条,设备的SCL和SDA均要配置成开漏输出模式,SCL和SDA各添加一个上拉电阻,阻值一般为4.7KQ左右

所有的设备,包括CPU和被控IC,它引脚的内部结构都是右图这样的,左边这一块是SCL的结构,这里SCLK就是SCL的意思,右边这一块是SDA的结构,这里DATA就是SDA的意思,首先引脚的信号进来,都可以通过一个数据缓冲器或者是施密特触发器,进行输入,因为输入对电路没有任何影响,所以任何设备在任何时刻都是可以输入的,但是在输出的这部分,采用的是开漏输出的配置,图为推挽输出

开漏输出就是去掉这个强上拉的开关管,输出低电平时,下管导通,是强下拉,输出高电平时,下管断开,但是没有上管了,此时引脚处于浮空的状态,这就是开漏输出

和这里图示是一样的,输出低电平,这个开关管导通,引脚直接接地,是强下拉,输出高电平,这个开关管断开,引脚什么都不接,处于浮空状态,这样的话,所有的设备都只能输出低电平而不能输出高电平,为了避免高电平造成的引脚浮空,这时就需要在总线外面,SCL和SDA各外置一个上拉电阻,这是通过一个电阻拉到高电平的,所以这是一个弱上拉

用我们之前将的弹簧和杆子的模型来解释就是,SCL或SDA就是一根杆子,为了防止有人向上推杆子,有人向下拉杆子,造成冲突,我们就规定,所有的人,不准向上推杆子,只能选择往下拉或者放手,然后,我们再外置一根弹簧向上拉,你要输出低电平,就往下拽,这根弹簧肯定拽不赢你,所以弹簧被拉伸,杆子处于低电平状态,你要输出高电平,就放手,杆子在弹簧的拉力下,回弹到高电平,这就是一个弱上拉的高电平,但是完全不影响数据传输,这样做有什么好处?

  • 第一,完全杜绝了电源短路现象,保证电路的安全,你看所有人无论怎么拉杆子或者放手,杆子都不会处于一个被同时强拉和强推的状态,即使有多个人同时往下拉杆子,也没问题
  • 第二,避免了引脚模式的频繁切换,开漏加弱上拉的模式,同时兼具了输入和输出的功能,你要是想输出,就去拉杆子或放手,操作杆子变化就行了,你要是想输入,就直接放手,然后观察杆子高低就行了,因为开漏模式下,输出高电平就相当于断开引脚,所以在输入之前,可以直接输出高电平,不需要再切换成输入模式了
  • 第三,就是这个模式会有一个“线与”的现象,就是只要有任意一个或多个设备输出了低电平,总线就处于低电平,只有所有设备都输出高电平,总线才处于高电平,12C可以利用这个电路特性,执行多主机模式下的时钟同步和总线仲裁,所以这里SCL虽然在一主多从模式下可以用推挽输出,但是它仍然采用了开漏加上拉输出的模式,因为在多主机模式下会利用到这个特征

3.I2C时序基本单元

起始和终止,都是由主机产生的,从机不允许产生起始和终止,所以在总线空闲状态时,从机必须始终双手放开,不允许主动跳出来去碰总线,如果允许的话,那就是多主机模型了

高电平期间,SDA不允许变化,SCL处于高电平之后,从机需要尽快地读取SDA,一般都是在上升沿这个时刻,从机就已经读取完成了,因为时钟是主机控制的,从机并不知道什么时候就会产生下降沿了,你从机要是磨磨唧唧的,主机可不会等你的,所以从机在上升沿时,就会立刻把数据读走,那主机在放手SCL一段时间后,就可以继续拉低SCL,传输下一位了,主机也需要在SCL下降沿之后尽快把数据放在SDA上,但是主机有时钟的主导权,所以主机并不需要那么着急,只需要在低电平的侄意时刻把数据放在SDA上就行了

另外注意,这里是高位先行,所以第一位是一个字节的最高位B7,然后依次是次高位B6,这个和串口不一样,另外,由于这里有时钟线进行同步,所以如果主机一个字节发送一半,突然进中断了,不操作SCL和SDA了,那时序就会在中断的位置不断拉长,SCL和SDA电平都暂停变化,传输也完全暂停,等中断结束后,主机回来继续操作,传输仍然不会出问题,这就是同步时序的好处

最后就是,由于这整个时序是主机发送一个字节,所以在这个单元里,SCL和SDA全程都由主机掌控,从机只能被动读取,这就是发送一个字节的时序

刚才我们说了,释放SDA其实就相当于切换成输入模式,或者这样来理解,所有设备包括主机都始终处于输入模式,当主机需要发送的时候,就可以主动去拉低SDA,而主机在被动接收的时候,就必须先释放SDA,不要去动它,以免影响别人发送,因为总线是线与的特征,任何一个设备拉低了,总线就是低电平,如果你接收的时候,还拽着SDA不放手,那别人无论发什么数据,总线都始终是低电平,你自己给它拽着不放,还让别人怎么发送呢,是吧,所以主机在接收之前,需要释放SDA

当我们在调用发送一个字节之后,就要紧跟着调用接收应答的时序,用来判断从机有没有收到刚才给它的数据,如果从机收到了,那在应答位这里,主机释放SDA的时候,从机就应该立刻把SDA拉下来,然后在SCL高电平期间,主机读取应答位,如果应答位为0,就说明从机确实收到了,这个场景就是,主机刚发送一个字节,然后说,有没有人收到啊,我现在把SDA放手了哈,如果有人收到的话,你就把SDA拽下来,然后主机高电平读取数据,发现,唉,确实有人给它拽下来了,那就说明有人收到了,如果主机发现,我松手了,结果这个SDA就跟着回弹到高电平了,那就说明没有人回应我,或者它收到了但是没给我回应,这就是发送一个字节,接收应答的流程

同理,在接收一个字节之后,我们也要给从机发送一个应答位,发送应答位的目的是告诉从机,你是不是还要继续发,如果从机发送一个数据后,得到了主机的应答,那从机就还会继续发送,如果从机没得到主机的应答,那从机就会认为,啊,我发送了一个数据,但是主机不理我,可能主机不想要了吧,这时从机就会乖乖地释放SDA,交出SDA的控制权,防止干扰主机之后的操作,这是应答位的执行流程

4.I2C时序

4.1指定地址写

起始条件开始后,主机必须首先调用发送一个字节,来进行从机的寻址和指定读写标志位,如图示的波形,表示本次寻址的目标是1101000的设备,同时,最后一位读写标志为0,表示主机接下来想要发送数据,接着从机发送应答,

在这个时刻,主机要释放SDA,所以如果单看主机的波形,应该是这样,释放SDA之后,引脚电平回弹到高电平,

但是根据协议规定,从机要在这个位拉低SDA,所以单看从机的波形,应该是这样,该应答的时候,从机立刻拽佳SDA,然后应答结束之后,从机再放开SDA

那现在综合两者的波形,结合线与的特性,在主机释放SDA之后,由于SDA也被从机拽住了,所以主机松手后,SDA并没有回弹高电平,这个过程,就代表从机产生了应答,最终高电平期间,主机读取SDA,发现是0,就说明,我进行寻址,有人给我应答了,传输没问题,如果主机读取SDA,发现是1就说明,我进行寻址,应答位期间,我松手了,但是没人拽住官,没人给我应答,那就直接产生停止条件吧,并提示一些信息

然后这个上升沿,就是应答位结束后,从机释放SDA产生的,从机交出了SDA的控制权,因为从机要在低电平尽快变换数据,所以这个上升沿和SCL的下降沿,几乎是同时发生的

由于之前我们读写位给了0,
所以应答结束后,我们要继续发送一个字节,同样的时序再来一把,第二个字节,就可以送到指定设备的内部了,从机设备可以自己定义第二个字节和后续字节的用途,一般第二个字节可以是寄存器地址或者是指令控制字等,比如MPU6050定义的第二个字节就是寄存器地址,比如AD转换器,第二个字节可能就是指令控制字,比如存储器,第二个字节可能就是存储器地址

那图示这里,主机发送这样一个波形,我们--判定,数据为0001 1001,即,主机向从机发送了0x19这个数据,在MPU6050里,就表示我要操作你0x19地址下的寄存器了,接着同样,是从机应答,主机释放SDA,从机拽住SDA,SDA表现为低电平,主机收到应答位为0,表示收到了从机的应答,然后继续,同样的流程再来遍,主机再发送一个字节,这个字节就是主机想要写入到0x19地址下寄存器的内容了,就表示,我要在0x19地址下,写入0xAA,

最后是接收应答位,如果主机不需要继续传输了,就可以产生停止条件(Stop,P),在停止条件之前,先拉低SDA,为后续SDA的上升沿作准备,然后释放SCL,再释放SDA,这样就产生了SCL高电平期间,SDA的上升沿,这样一个完整的数据帧就拼接完成了

这个数据帧的目的就是,对于指定从机地址为1101000的设备,在其内部0x19地址的寄存器中,写入0XAA这个数据,这就是指定地址写的时序

4.2当前地址读

起始条件开始后,主机必须首先调用发送一个字节,来进行从机的寻址和指定读写标志位,

如图示的波形,表示本次寻址的目标是1101000的设备,同时,最后一位读写标志为1,表示主机接下来想要读取数据,紧跟着,发送一个字节之后,接收一下从机应答位,从机应答0,代表从机收到了第一个字节,在从机应答之后,

从这里开始,数据的传输方向就要反过来了,因为刚才主机发出了读的指令,所以这之后,主机就不能继续发送了,要把SDA的控制权交给从机,主机调用接收一个字节的时序,进行接收操作,然后在这一块,从机就得到了主机的允许可以在SCL低电平期间写入SDA,然后主机在SCL高电平期间读取SDA,那最终,主机在SCL高电平期间依次读取8位,就接收到了从机发送的一个字节数据,0000 1111,也就是0x0F

那现在问题就来了,这个0xOF是从机哪个寄存器的数据呢,我们看到,在读的时序中12C协议的规定是,主机进行寻址时,一旦读写标志位给1了,下一个字节就要立马转为读的时序,所以主机还来不及指定,我想要读哪个寄存器,就得开始接收了,所以这里就没有指定地址这个环节,那主机并没有指定寄存器的地址,从机到底该发哪个寄存器的数据呢,这就需要用到我们上面说的当前地址指针了

在从机中,所有的寄存器被分配到了一个线性区域中,并且,会有一个单独的指针变量,指示这其中一个寄存器,这个指针上电默认,一般指向0地址,并且,每写入一个字节和读出一个字节后,这个指针就会自动自增一次,移动到下一个位置,那么在调用当前地址读的时序时,主机没有指定要读哪个地址,从机就会返回当前指针指向的寄存器的值,那假设,我刚刚调用了这个指定地址写的时序,在0x19的位置写入了0xAA,那么指针就会+1,移动到0x1A的位置,我再调用这个当前地址读的时序,返回的就是0x1A地址下的值,如果再调用一次呢,返回的就是0x1B地址下的值,这就是当前地址读时序的操作逻辑,由于当前地址读并不能指定读的地址,所以这个时序用的不是很多

4.3指定地址读

下面的时序在这里分隔一下,前面的部分是指定地址写,但是只指定了地址,还没来得及写,后面的部分是当前地址读,因为我们刚指定了地址,所以再调用当前地址读,两者加在一起,就是指定地址读了

首先,最开始,仍然是启动条件,然后发送一个字节,进行寻址,这里指定从机地址是1101000,读写标志位是0,代表我要进行写的操作,经过从机应答之后,再发送一个字节,第二个字节,用来指定地址,这个数据就写入到了从机的地址指针里了,也就是说,从机接收到这个数据之后,它的寄存器指针就指向了0x19这个位置,之后,我们要写入的数据,不给它发,而是直接再来个起始条件,

这个Sr(Start Repeat)的意思就是重复起始条件,相当于另起一个时序,因为指定读写标志位只能是跟着起始条件的第一个字节,所以如果想切换读写方向,只能再来个起始条件,然后起始条件后,重新寻址并且指定读写标志位,此时读写标志位是1,代表我要开始读了,接着主机接收一个字节,这个字节是不是就是0x19地址下的数据,这就是指定地址读

另外在这里,你也可以再加一个停止条件,这样也行,这样的话,就是两个完整的时序了,先起始,写入地址,停止,因为写入的地址会存在地址指针里面,所以这个地址并不会因为时序的停止而消失,我们就可以再起始,读当前位置,停止,这样两条时序也可以完成任务

但是12C协议官方规定的复合格式是一整个数据帧,就是先起始、再重复起始、再停止,相当于把两条时序拼接成一条了

4.4高级一点

如果你只想写一个字节,那就停止,就行了,如果你想写多个字节,就可以把这最后一部分,多重复几次,写入一次后,地址指针会自动+1,变成0x1A,所以这第二个数据就写入到了0x1A的位置

这样这个时序就进阶为,在指定的位置开始,按顺序连续写入多个字节

然后同理,当前位置读和指定位置读,也可以多次执行这最后一部分时序,由于地址指针在读后也会自增,所以这样就可以连续读出一片区域的寄存器

如果你只想读一个字节就停止的话,在读完一个字节之后,一定要给从机发个非应答(Send Ack, SA),非应答,就是该主机应答的时候,主机不把SDA拉低,从机读到SDA为1,就代表主机没有应答,从机收到非应答之后,就知道主机不想要继续了,从机就会释放总线,把SDA控制权交还给主机,如果主机读完仍然给从机应答了,从机就会认为主机还想要数据,就会继续发送下一个数据,而这时,主机如果想产生停止条件,SDA可能就会因为被从机拽住了,而不能正常弹回高电平

如果主机想连续读取多个字节,就需要在最后一个字节给非应答,而之前的所有字节都要给应答,简单来说就是主机给应答了,从机就会继续发,主机给非应答了,从机就不会再发了,交出SDA的控制权,从机控制SDA发送一个字节的权利,开始于读写标志位为1,结束于主机给应答为1,这就是主机给从机发送应答位的作用

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

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

相关文章

腾讯云升级多个云存储解决方案 以智能化存储助力企业增长

9月6日,在腾讯数字生态大会腾讯云储存专场上,腾讯云升级多个存储解决方案:Data Platform 数据平台解决方案重磅发布,数据加速器 GooseFS、数据处理平台数据万象、日志服务 CLS、高性能并行文件存储 CFS Turbo 等多产品全新升级&am…

【Python篇】深度探索NumPy(下篇):从科学计算到机器学习的高效实战技巧

文章目录 Python NumPy学习指南前言第六部分:NumPy在科学计算中的应用1. 数值积分使用梯形规则进行数值积分使用Simpson规则进行数值积分 2. 求解微分方程通过Euler方法求解一阶常微分方程使用scipy.integrate.solve_ivp求解常微分方程 3. 随机过程模拟模拟布朗运动…

MFC工控项目实例之十六输入信号验证

承接专栏《MFC工控项目实例之十五定时刷新PC6325A模拟量输入》 验证选定的输入信号实时状态 在BoardTest.cpp文件中添加代码 void CBoardTest::OnButton2() {// TODO: Add your control notification handler code hereisThreadBegin true; //运行线程执行pThre…

Qt:饿汉单例(附带单例使用和内存管理)

前言 本文主要写饿汉单例以及单例的释放&#xff0c;网上很多教程只有单例的创建&#xff0c;但是并没有告诉我们单例的内存管理&#xff0c;这就很头疼。 正文 饿汉式单例 // SingletonClass.h #ifndef SINGLETONCLASS_H #define SINGLETONCLASS_H #include <QObject&g…

K-means 算法的介绍与应用

目录 引言 K-means 算法的基本原理 表格总结&#xff1a;K-means 算法的主要步骤 K-means 算法的 MATLAB 实现 优化方法与改进 K-means 算法的应用领域 表格总结&#xff1a;K-means 算法的主要应用领域 结论 引言 K-means 算法是一种经典的基于距离的聚类算法&#xff…

uniapp使用高德地图设置marker标记点,后续根据接口数据改变某个marker标记点,动态更新

最近写的一个功能属实把我难倒了,刚开始我请求一次数据获取所有标记点,然后设置到地图上,然后后面根据socket传来的数据对这些标记点实时更新,改变标记点的图片或者文字, 1:第一个想法是直接全量替换,事实证明这样不行,会很卡顿,有明显闪烁感,如果标记点比较少,就十几个可以用…

828华为云征文 | 使用Flexus云服务器X实例部署GLPI资产管理系统

828华为云征文 | 使用Flexus云服务器X实例部署GLPI资产管理系统 1. 部署环境说明2. 部署基础环境2.1. 操作系统基本配置2.2. 部署Nginx2.3. 部署MySQL2.4. 部署PHP 3. 部署GLPI资产管理系统 1. 部署环境说明 本次环境选择使用华为云Flexus云服务器X实例&#xff0c;因为其具有高…

从Apple Intelligence到IoT Intelligence,端侧生成式AI时代加速到来

9月10日凌晨1点&#xff0c;苹果新品发布会如期举行&#xff0c;全新iPhone16系列成为苹果生态中真正意义上的第一款原生AI手机&#xff0c;在第二代3nm工艺A18和A18 Pro芯片的加持下&#xff0c;iPhone16系列能够容纳并快速运行以Apple Intelligence为中心的生成式AI功能在手机…

唯徳知识产权管理系统 DownloadFileWordTemplate 文件读取漏洞复现

0x01 产品简介 唯徳知识产权管理系统,由深圳市唯德科创信息有限公司精心打造,旨在为企业及代理机构提供全方位、高效、安全的知识产权管理解决方案。该系统集成了专利、商标、版权等知识产权的全面管理功能,并通过云平台实现远程在线办公,提升工作效率。是一款集知识产权申…

mfc140u.dll丢失的解决方法,汇总6款系统dll文件修复工具(2024全新)

许多用户在使用 Windows 电脑时经常会遇到与 mfc140u.dll 相关的问题。当我们试图打开某个游戏或程序时&#xff0c;常常会看到这样的错误提示&#xff1a;“代码执行无法继续&#xff0c;因为找不到 mfc140u.dll”或“程序无法启动&#xff0c;因为计算机上缺少 mfc140u.dll”…

Java外卖小程序管理系统

技术架构&#xff1a; springboot ssm mysql redis 有需要该项目的小伙伴可以添加我Q&#xff1a;598748873&#xff0c;备注&#xff1a;CSDN 功能描述&#xff1a; 商品管理&#xff1a;新增商品、所有商品 菜单管理&#xff1a;菜单管理、菜单分类 订单管理&#x…

在线查看 Android 系统源代码 Git repositories on android

在线查看 Android 系统源代码 Git repositories on android 1. Git repositories on android1.1. Android Make Build System1.2. Android Open Source Project Code Review References 1. Git repositories on android https://android.googlesource.com/ 1.1. Android Make …

成功塑造孩子的人生,这一步很关键!

塑造孩子行为极为重要 思维决定行为&#xff0c;行为决定习惯&#xff0c;这一理念在家庭教育中尤为重要。明智的家长如同经验丰富的指导者&#xff0c;细心关注并引领孩子塑造正确的行为。他们深知每个孩子都是独一无二的&#xff0c;拥有独特的性格与潜力&#xff0c;不可简单…

CSS---序号使用css设置,counter-reset、counter-increment、content配合实现备注文案的序号展示

直接上代码&#xff0c;全代码copy即可使用! <template><div class"reminder"><span class"Bold_12_body" style"line-height: 8vw">温馨提示&#xff1a;</span><br /><div class"rule-container"…

网络安全学习(四)Burpsuite

经过测试&#xff0c;发现BP需要指定的JAVA才能安装。 需要的软件已经放在我的阿里云盘。 &#xff08;一&#xff09;需要下载Java SE 17.0.12(LTS) Java Downloads | Oracle 1.2023版Burp Suite 完美的运行脚本的环境是Java17 2.Java8不支持 看一下是否安装成功&#xff0c…

Leetcode面试经典150题-138.随机链表的复制

题目比较简单&#xff0c;重点是理解思想&#xff0c;random不管&#xff0c;copy一定要放在next 而且里面的遍历过程不能省略 解法都在代码里&#xff0c;不懂就留言或者私信 /* // Definition for a Node. class Node {int val;Node next;Node random;public Node(int val…

JavaEE:文件内容操作(一)

文章目录 文件内容的读写---数据流字节流和字符流打开和关闭文件文件资源泄漏try with resources 文件内容的读写—数据流 文件内容的操作,读文件和写文件,都是操作系统本身提供了API,在Java中也进行了封装. Java中封装了操作文件的这些类,我们给它们起了个名字,叫做"文…

从大脑图谱/ROI中提取BOLD信号

动机 在功能连接&#xff08;Functional Connectivity&#xff0c;FC&#xff09;构建过程中&#xff0c;由于FC中元素数目是节点数目的平方关系&#xff0c;所以在计算FC之前进行数据降维是一个常见的选择。 一般会将体素级/顶点级BOLD信号&#xff08;在2mm的图像分辨率下大脑…

C++从入门到起飞之——继承下篇(万字详解) 全方位剖析!

&#x1f308;个人主页&#xff1a;秋风起&#xff0c;再归来~&#x1f525;系列专栏&#xff1a;C从入门到起飞 &#x1f516;克心守己&#xff0c;律己则安 目录 1、派⽣类的默认成员函数 1.1 四个常⻅默认成员函数 1.2 实现⼀个不能被继承的类 ​编辑 2. 继承与友…

人工智能开发实战matplotlib库应用基础

内容导读 matplotlib简介绘制直方图绘制撒点图 一、matplotlib简介 matplotlib是一个Python 2D绘图库&#xff0c;它以多种硬拷贝格式和跨平台的交互式环境生成高质量的图形。 matplotlib 尝试使容易的事情变得更容易&#xff0c;使困难的事情变得可能。 我们只需几行代码…