当前位置: 首页 > news >正文

ATPCS:ARM-Thumb程序调用的基本规则

        为了使单独编译的c文件和汇编文件之间能够互相调用,需要制定一系列的规则,AAPCS就是ARM程序和Thumb程序中子程序调用的基本规则。

1、ATPCS概述

        ATPCS规定了子程序调用过程中寄存器的使用规程、数据站的使用规则、参数的传递规则。为了适应一些特殊的需求,对这些规则进行改动可以得到几种不同的子程序调用规则,具体包括:

  • 支持数据栈限制检查的ATPCS

  • 支持只读段位置无关(ROPI)的ATPCS

  • 支持可读写段位置无关(RWPI)的ATPCS

  • 支持ARM程序和Thumb程序混合使用的ATPCS

  • 处理浮点运算的ATPCS

        相关调用程序必须遵守同一种ATPCS,编译器/汇编器在ELF格式的目标文件中设置相应的属性,标识用户选定 ATPSC类型。不同类型的ATPCS规则,有对应的C语言库,连接器根据用户的指定ATPCS类型链接相应的c库。

        C语言的编译器编译的C子程序能够满足用户指定的ATPCS类型。而对于汇编语言,需要用户来满足子程序间的ATPCS类型。汇编子程序必须满足如下三个条件:

  • 子程序编写时必须遵守相应的ATPCS规则

  • 数据栈的使用要遵守相应的ATPCS规则

  • 汇编器中使用-apcs选项

    下面是keil中,C语言配置的选项。

下面是汇编器的配置。

2、基本ATPCS

基本的ATPCS规则包括下面三个方面内容:

  • 各寄存器的使用规则及其相应的名称

  • 数据栈的使用规则

  • 参数传递的规则

满足基本类型的ATPCS程序运行速度更快,占用内存更少,但不支持以下功能:

  • ARM程序和Thumb程序的互相调用(注:在Cortex-M3中使用的是Thumb2指令,已经不区分ARM、Thumb指令了,《Cortex-M3权威手册》对此有论述。因此,该条存疑,本文参考的资料也较老,新版可能会更改,待验证。。。。)

  • 数据以及代码的位置无关的支持

  • 子程序的可重入性

  • 数据栈检查的支持

        派生的几种ATPCS规则是在基本的ATPCS基础上添加额外规则形成的,目的就是提供上述功能。

2.1 寄存器的使用规则

   寄存器的使用必须满足如下规则:

  • 子程序间通过R0--R3传递参数,被调用的子程序在返回前无需恢复寄存器R0--R3的内容。

  • 在子程序中,使用R4--R11保存局部变量,如果在子程序中使用到了寄存器R4--R11中的某些寄存器,子程序进入前需要保存这些寄存器的值,在返回时需要恢复。未用到,不处理。在Thumb程序中,通常只能使用寄存器R4--R7来保存局部变量。

  • 寄存器R12用作子程序间scratch寄存器,记作ip。在子程序间的连接代码段中常用这种使用规则。

  • 寄存器R13用作数据栈指针,记作sp。在子程序中sp不能用作其它用途,进入子程序时的sp值和退出子程序时的sp值必须相等。

  • R14称为连接寄存器,记作lr。用于保存子程序的返回地址,如果在子程序中保存了返回地址,R14可以用作其它的用途。

  • R15是程序计数器,记作pc,不能用于其它的用途。

ATPCS中各寄存器的使用规则及其名称

寄存器    别名特殊名称使用规则
R15Pc程序计数器
R14Lr连接寄存器
R13Sp数据栈指针
R12Ip子程序内部调用的scratch寄存器
R11V8ARM状态局部变量寄存器8
R10V7sl

ARM状态局部变量寄存器7

在支持数据栈检查的ATPCS中为数据栈限制指针

R9V6sb

ARM状态局部变量寄存器6

在支持RWPI的ATPCS中为静态基址寄存器

R8V5ARM状态局部变量寄存器5
R7V4wr

局部变量寄存器4

Thumb状态工作寄存器

R6V3局部变量寄存器3
R5V2局部变量寄存器2
R4V1局部变量寄存器1
R3A4参数/结果/scratch 寄存器4
R2A3参数/结果/scratch 寄存器3
R1A2参数/结果/scratch 寄存器2
R0A1参数/结果/scratch 寄存器1

2.2 数据栈的使用规则

        栈指针通常可以指向不同的位置。当栈指针指向栈顶元素(即最后一个入栈的数据元素)时,称为FULL栈;当栈指针指向与栈顶指针(即最后一个入栈的数据元素)相邻的一个可用数据单元时,称为EMPTY栈。

        栈的增长方式也可以不同,向内存地址减小的方向增长时,称为DESCENDING栈,向地址增长的方向增长时,称为ASCENDING栈。故,有四种栈:

  • FD FULL DESCENDING
  • ED EMPTY DESCENDING
  • FA FULL ASCENDING
  • EA EMPTY ASCENDING

        ATPCS规定数据栈为FD栈,并且数据栈的操作时8 Byte对齐。下面时数据栈的示例和名词解释。

  • 数据栈栈指针(stack pointer)是指最后一个写入栈的数据的内存地址。
  • 数据栈的基地址(stack base)是指数据栈的最高地址。由于ATPCS中数据栈式FD类型,实际上数据栈中最早入栈的数据占据的内存单元式基地址的下一个内存单元。
  • 数据栈界限(stack limit)是指数据栈可以使用的最低的内存单元的地址。
  • 已占用的数据栈(used stack)是指数据栈的基地址和数据栈栈指针之间的区域。其中包括数据栈栈指针对应的内存单元,但不包括数据栈的基地址对应的内存单元。
  • 未占用的数据栈(unused stack)是指数据栈栈指针和数据栈界限之间的区域,其中包括数据栈界限对应的内存单元,但不包括数据栈栈指针对应的内存单元。
  • 数据栈中的数据帧(stack frames)是指在数据栈中,为子程序分配的用来保存寄存器和局部变量的区域。

        异常中断的处理程序可以使用被中断程序的数据栈,这时用户要保证中断的程序的数据栈足够大。

数据栈的示意图

        在ARMv5RE(很老的版本了,cortex-m3式ARMv7版本)中,批量传输指令LDRD/STRD要求的数据栈是8 Byte对齐,以提高数据的传输速度。用ADS编译器产生的目标文件中,外部接口的数据栈都是8 Byte对齐的,并且编译器告诉连接器:本目标文件的数据栈是8字节对齐的,对于汇编程序来说,如果目标文件中包含了外部调用,则必须满足下列条件:

  • 外部接口的数据栈必须是8字节对齐,保证在进入该汇编代码后,知道该汇编代码调用外部程序之间,数据栈的栈指针变化的是偶数个字(如栈指针加2个字,而非3个字)。
  • 汇编程序中使用PRESERVE8位操作告诉连接器,本汇编程序数据栈是8字节对齐的。

2.3 参数传递规则

        根据参数个数是否固定可以将子程序分为参数个数固定的子程序和参数个数可变的子程序,二者的参数传递规则不同。

2.3.1 参数个数可变的子程序参数传递规则(少见,对应C中的可变参数)

        当参数不超过4个时,可以使用寄存器R0--R3来传递参数,超过4个,还可以使用数据栈来传递参数。

        参数传递时,将所有参数看作时存放在连续的内存字单元中的字数据,然后,依次将各字数据传送到寄存器R0--R3中,如果大于4个,将剩余的字数据传送到数据栈中,入栈顺序与参数顺序相反。

        按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,另一半通过数据栈传递。

2.3.2 参数不可变的子程序传递规则(常见,大部分C语言的情况)

        如果系统包含了浮点运算的硬件部件,浮点参数将按照下面的规则传递:

  • 各个浮点参数按顺序处理
  • 为每个浮点参数分配FP寄存器
  • 分配的方法:满足该浮点参数需要的且编号最小的一组连续FP寄存器

        第一个整数参数,通过R0--R3传输,其余的参数按照数据栈传输。

2.3.3 子程序结果返回规则

子程序中结果返回的规则如下:

  • 结果为一个32位整数时,可以通过R0返回.
  • 结果为一个64位整数时,可以通过R0、R1返回,以此类推。
  • 结果位一个浮点数时,可以通过浮点运算部件的寄存器f0、d0或者s0返回
  • 结果为复合型的浮点数(如复数)时,可以通过寄存器f0--fN或者d0--dN来返回。
  • 对于位数更多的结果,需要通过内存来传递。

3 几种特定的ATPCS

        几种特定的ATPCS是在基本的ATPCS基础上增加一些规则形成的:

  • 支持数据栈限制检查的ATPCS

  • 支持只读段位置无关(ROPI)的ATPCS

  • 支持可读写段位置无关(RWPI)的ATPCS

  • 支持ARM程序和Thumb程序混合使用的ATPCS

  • 处理浮点运算的ATPCS

3.1 支持数据栈限制检查的ATPCS

 3.1.1 支持数据栈限制检查的ATPCS基本原理

        如果在程序设计期间能够准确计算程序用到的内存大小,就不需要进行数据栈检查,但这在一般情况下很难做到,这时就需要进行数据栈的检查。

        在进行数据栈的检查时,使用R10(又记作sl)作为数据栈限制指针。用户在程序中不能使用该寄存器。支持数据栈限制检查的ATPCS要满足下面的规则:

  • 在已占用的栈的最低地址和sl之间必须有256字节的空间。即sl所指的内存地址必须比已经占用的栈的最低地址低256字节。当中断处理程序可以使用用户的数据栈时,在已经占用的栈的最低地址和sl之间除了必须保留的256字节的内存单元之外,还必须为中断处理预留足够的内存空间。
  • 用户程序不能修改sl的值
  • 数据栈栈指针sp的值必须不小于sl的值

与支持数据栈限制检查的ATPCS相关的编译/汇编选项有下面几种:

  • 选项/swst(software  stack limit checking)指示编译器生成的代码遵守支持数据栈限制检查的ATPCS。用户在程序设计期间不能准确计算出程序所需的所有数据栈大小时,需要指定该选项。
  • 选项/noswst(no software  stack limit checking)指示编译器生成的代码不支持数据栈限制检查功能。用户在程序设计期间能准确计算出程序所有的所有数据栈大小时,可以指定该选项,默认该选项。
  • 选项/swstna(software  stack limit checking not  applicable)如果汇编程序对于是否进行数据栈检查无所谓,而与该汇编程序连续的其它程序指定了选项/swst或选项/noswst,这时该汇编程序使用选项/swstna

 3.1.2 编写遵守支持数据栈限制检查的ATPCS的汇编语言程序

        对于C或者C++来说,如果在编译时指定swst选项,生成的目标代码将遵守支持数据栈限制检查的ATPCS。

        对于汇编程序来说,如果要遵守支持数据栈限制检查的ATPCS,用户在编写程序时,必须满足支持数据栈限制检查的ATPCS所要求的规则。然后在汇编时指定选项swst。

3.2 支持只读段位置无关(ROPI)的ATPCS

3.2.1 支持只读段位置无关的ATPCS的应用场合

        位置无关的只读段可能为位置无关的代码段,也可能是只读数据段。使用 支持只读段位置无关的ATPCS可以避免必须将程序存放到特定的位置。常用于:

  • 程序在运行期间动态加载到内存中。
  • 程序在不同的场合,与不同的程序组合后加载到内存中。
  • 在运行期间映射到不同的地址。在一些嵌入式系统中,将程序放到ROM中,运行时再加载到RAM中的不同地址(Linux常用这种方式,uboot加载Linux内核镜像文件到RAM中)。

3.2.2 支持只读段位置无关的ATPCS的程序设计

如果程序遵守支持只读段位置无关的ATPCS,需要满足如下规则:

  • 当ROPI段中的代码引用同一个ROPI段中的符号时,必须是基于PC的。
  • 当ROPI段中的代码引用另一个ROPI段中的符号时,必须是基于PC的。并且两个ROPI段的位置关系必须固定。
  • 其它被ROPI段中的代码引用的必须是绝对地址,或基于sb的可写数据。
  • ROPI段移动后,对ROPI中符号的引用要作相应的调整。

3.3 支持可读写段位置无关(RWPI)的ATPCS

        如果一个程序中所有的可读写段都是位置无关,则称该程序遵守支持可读写段位置无关(RWPI)的ATPCS。使用支持可读写段位置无关的ATPCS可以避免必须将程序放到特定的位置。这时,R9通常用作静态基址寄存器,记作sb。可重入的子程序可以再内存中同时有多个实例,各个实例拥有独立的可读写段。在生成一个新的实例时,sb指向该实例的可读写段。RWPI段中的符号的计算方法为:连接器首先计算出该符号相对于RWPI段中某一特定位置的偏移量,通常该特定位置选为RWPI段的第一个字节处:在程序运行时,将该偏移量加上sb即可生成该符号的地址。

3.4 支持ARM程序和Thumb程序混合使用的ATPCS

        据《Cortex-M3权威手册》中所述,Cortex-M3已经不再区分ARM指令和Thumb指令,M3中使用的时Thumb2指令集,Thumb2指令集兼容32位和16位指令,因此,该部分在目前已经用不到,就不再叙述。

3.5 处理浮点运算的ATPCS

        ATPCS支持VFP体系和FPA体系两种不同的浮点硬件体系和指令集,两种体系对应的代码不兼容。

        相应的,ADS的编译器和汇编器有下面6种与浮点数相关的选项。

  • -fpu  VFP
  • -fpu  FPA
  • -fpu  softVFP
  • -fpu  softVFP + VFP
  • -fpu  softFPA
  • -fpu  none

        当系统种包含浮点运算部件时,可以选择上述选项。在M3内核种不包含浮点数运算单元,但M4内核中包含一个可选的浮点数运算单元(FPU),FPU符合IEEE 754标准(未实现完整的内容)。FPU为单精度浮点单元,浮点数据和计算基于IEEE Std 754-2008,IEEE二进制浮点运算标准,是VFP体系。

4 说明

        本篇文章参考了《ARM体系结构与编程》(杜春雷  著)和《Cortex-M3权威指南》,属于学习笔记性质。参考资料链接:https://gitee.com/zichuanning520/htq_library

相关文章:

ATPCS:ARM-Thumb程序调用的基本规则

为了使单独编译的c文件和汇编文件之间能够互相调用,需要制定一系列的规则,AAPCS就是ARM程序和Thumb程序中子程序调用的基本规则。 1、ATPCS概述 ATPCS规定了子程序调用过程中寄存器的使用规程、数据站的使用规则、参数的传递规则。为了适应一些特殊的需…...

Swift 判断 A B 两个时间是不是同一天,A 是不是 B 的昨天

1. 今天要做这个效果(在时间旁边显示今天,昨天) 2. Preview 3. Code: // 添加 今天 昨天 func show_today_yesterday(d: Date Date()) -> String {let calendar Calendar.currentlet today: Date Date()if calendar.isDate(today, inS…...

华为OD 高效的任务规划(200分)【java】A卷+B卷

华为OD统一考试A卷+B卷 新题库说明 你收到的链接上面会标注A卷还是B卷。目前大部分收到的都是B卷。 B卷对应20022部分考题以及新出的题目,A卷对应的是新出的题目。 我将持续更新最新题目 获取更多免费题目可前往夸克网盘下载,请点击以下链接进入: 我用夸克网盘分享了「华为O…...

使用VGG框架实现从二分类到多分类

一.数据集的准备 与之前的不同,这一次我们不使用开源数据集,而是自己来制作数据集。重点需要解决的问题是对数据进行预处理,如每一个图片的大小均不同,需要进行resize,还需要对每一张图片打标签等操作。 数据集文件 …...

Ubuntu服务器配置qq邮箱发送信息

效果: 此处设置的是自己给自己发送,配合linux的cron实现定时触发发送事件的效果 实现过程: 安装邮箱客户端Postfix sudo apt-get install postfix配置Postfix:编辑Postfix的主要配置文件 /etc/postfix/main.cf,并在…...

HTML读书笔记

HTML的读书笔记 概述 Jack 2023.10.23 参考网站: w3school 在线教程 HTML 头部 | 菜鸟教程 本教程已教你如何使用 HTML 创建站点。 HTML 是一种在 Web 上使用的通用标记语言(并不是类似Python一样的编程语言)。HTML 允许你格式化文本&…...

初识Java

一、Java语言概述 1.1 Java是什么 Java是一种优秀的程序设计语言,它具有令人赏心悦目的语法和易于理解的语义 不仅如此,Java还是一个有一系列计算机软件和规范形成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境&a…...

bootstrap.properties中配置Nacos

bootstrap.properties用于在Spring Boot应用程序启动阶段加载外部配置 优先级高:在应用程序启动时首先加载,用于配置应用程序的基础设置,如配置数据源、日志、配置服务器 外部配置:加载外部配置源(如远程配置服务器&a…...

【CVPR 2023】Diffusion Models高分辨率长视频生成 Align your Latents

Diffusion Models专栏文章汇总:入门与实战 前言:CVPR 2023年的工作《Align your Latents: High-Resolution Video Synthesis with Latent Diffusion Models》实现了高帧率高分辨率长视频生成,并在保持时间一致性上做了很多工作。这篇博客详细解读一下背后的原理,并总结一下…...

[Linux 基础] make、Makefile自动化构建代码工具

文章目录 1、make与Makefile是什么2、为什么要有make与Makefile3、怎么实现一个Makefile文件3.1 如何编写Makefile文件3.1.1 依赖关系3.1.2 依赖方法 3.2 如何清理项目3.2.1 如何编写3.2.2 clean详解 3.3 make的使用3.4 原理3.4.1 查看文件修改时间 1、make与Makefile是什么 m…...

vue3结合Cesium加载倾斜摄影3dtiles

这篇文章主要是为了记录加载3dtiles时模型与地形有时候存在一些高度上的差异,为此将解决方法做一个记录,便于其他读者使用。 加载倾斜摄影3dtitle //加载倾斜摄影图像 function init3Dtiles() {const tileSet new Cesium3DTileset({url: "http://1…...

面对DDoS和APT攻击,我们该如何有效防御?

关于DDoS(Distributed Denial of Service)分布式拒绝服务攻击,是指攻击者通过技术手段,在很短的时间内对目标攻击网站发出大量请求,极大地消耗相关网站的主机资源,导致其无法正常服务。 打个比方来说&#…...

【前端学习】—Vue生命周期(十七)

【前端学习】—Vue生命周期(十七) 一、Vue生命周期 二、Vue父子组件生命周期调用顺序 三、Vue中在哪个生命周期内调用异步请求...

ant的Path-like结构

ant可以使用path和classpath结构指明路径。path和classpath可以包含内嵌的元素&#xff0c;类似下面的通用形式&#xff1a; <classpath><pathelement path"${classpath}"/><pathelement location"lib/helper.jar"/> </classpath>…...

Java设计模式之组合模式

比如在实现一个文件管理系统时&#xff0c;对于客户端来说&#xff0c;如果需要区分文件与文件夹的使用&#xff0c;会比较麻烦&#xff0c;使用组合模式可以在使用不同对象时使用方法保持一致性。 定义 又名部分整体模式&#xff0c;是用于把一组相似的对象当作一个单一的对…...

划词搜索IP插件

插件背景 浏览器插件可以让用户根据个人工作及日常需求来定制浏览器的功能和界面。当用户在网页上看到一些IP地址时&#xff0c;或许会好奇它们的来源和归属。传统的做法是&#xff0c;用户需要复制这个IP地址&#xff0c;然后跳转到埃文科技旗下的http://IPUU.net网站进行查询…...

LeetCode刷题---简单组(一)

文章目录 &#x1f352;题目一 507. 完美数&#x1f352;解法一 &#x1f352;题目二 2678. 老人的数目&#x1f352;解法一 &#x1f352;题目三 520. 检测大写字母&#x1f352;解法一&#x1f352;解法二 &#x1f352;题目一 507. 完美数 对于一个 正整数&#xff0c;如果它…...

如何能通过表面挖掘到深层次的底层思维?

如何能通过表面挖掘到深层次的底层思维&#xff1f; 挖掘深层次的底层思维是一项复杂且细致的工作&#xff0c;通常涉及到个人反思、观察、沟通和分析等多个方面。下面是一些可能会对你有帮助的策略和方法&#xff1a; 1. 提问的艺术 五次为什么&#xff1a;这是一种简单而有…...

【单片机学习笔记】Windows+Vscode+STM32F4+freeRTOS+FatFs gcc环境搭建

为摒弃在接受keil邮件&#xff0c;研究了下gun编译&#xff0c;以STM32F407为例&#xff0c;简单记录 1. 软件包准备 Git 选择对应版本直接安装即可https://git-scm.com/download/winmakegcc ​ 1&#xff09;将上述软件包放置于C盘根目录 2&#xff09;添加环境变量 3&am…...

Oracle 控制文件的作用与控制文件创建

1、控制文件存储的数据信息 1) 数据库名称和数据库唯一标识符 (DBID)&#xff0c;通过 select name,dbid from v$database; 查 询 DBID 和数据库名称 2) 创建数据库的时间戳 3) 有关数据文件、联机重做日志文件、归档重做日志文件的信息 4) 表空间信息 5) 检查点信息 6) 日志序…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...