tag 标签: I2C

相关帖子
相关博文
  • 热度 1
    2024-9-15 07:13
    161 次阅读|
    0 个评论
    SPI、I2C和I3C是三种常见的串行通信协议,广泛应用于嵌入式系统和微控制器之间的数据交换。每种协议都有其独特的特点和适用场景,以下是对这三种协议的性能比较: 1. SPI (Serial Peripheral Interface) 速度:SPI是一种高速的同步串行通信协议,通常比I2C更快,因为它没有起始位和停止位的概念,数据传输是全双工的(即可以同时发送和接收数据)。 引脚数:SPI需要至少4个引脚(MOSI, MISO, SCLK, SS/CS),这可能在某些应用中是一个限制因素。 灵活性:由于SPI是全双工通信,它可以在主机和从机之间实现真正的并行数据传输,这对于需要高吞吐量的应用非常有用。 配置:SPI支持多种模式,包括时钟极性和相位,这提供了额外的灵活性。 2. I2C (Inter-Integrated Circuit) 速度:I2C的速度通常低于SPI,但它足以满足许多低至中等速度的应用需求。 引脚数:I2C只需要两根线(SDA和SCL),这使得它非常适合于空间受限的设计。 可扩展性:I2C支持多主设备和多从设备,允许在同一总线上连接多个设备,这是其最大的优势之一。 地址分配:每个I2C设备都有一个唯一的地址,这使得总线上的设备可以轻松地被识别和寻址。 3. I3C (Improved Inter-Integrated Circuit) 速度:I3C是I2C的改进版,旨在提供更高的速度和更低的功耗。它通过引入新的功能来提高性能,如动态地址分配和更高效的电源管理。 兼容性:I3C保持了与I2C的向后兼容性,这意味着现有的I2C设备可以直接与I3C总线交互,无需修改。 引脚数:I3C通常使用与I2C相同的两根线,但在需要时可以通过增加额外的信号线来实现更多的功能。 新特性:I3C引入了一些新特性,如快速模式切换和增强的错误检测机制,这些特性使其在某些应用中比传统的I2C更具吸引力。 总结来说,SPI适合需要高速数据传输且不介意多引脚的应用;I2C适用于需要多设备通信且空间受限的设计;而I3C则结合了两者的优点,提供了更高的速度和新的功能,同时保持了与I2C的兼容性。选择哪种协议取决于具体的应用需求和设计约束。
  • 热度 3
    2023-6-8 12:09
    2690 次阅读|
    0 个评论
    近期有点全身心投入到了嵌入式驱动的开发意思了,起早贪黑的学习。不过也是,人生的路都是在不断地学习中度过的。对于干了几年的硬件工程师而言,不说硬件是不是很牛了,就是想换换脑子,整天三极管、电阻、电容的,确实让人乏味。思来想去,硬件是软件的基座,驱动是软件沟通硬件的桥梁。倒不如自己整点知识,也方便自己以后调试硬件不是,再说了从软件角度去理解硬件思维,会有很多不同的收获不是。 奋战了一个月,倒是把驱动的基本框架了解七七八八了,兴致使然,图像采集感觉还不错,公司有产品当开发板,也是省下了大部分的学习成本。 硬件基本结构就是:SOC平台为瑞芯微,视频桥接芯片是LT6911UXC,千兆网络接口和基本的电源电路,还有的最小核心板组成就不多说了。 总归是要初始化和调试LT6911UXC的,那么最基础的当然是通过固定的总线去访问和配置其寄存器了,而大多这类芯片都是用的I2C,LT6911UXC也不例外。于是重点看了下I2C总线的驱动实现框架。那么就在已有的基本驱动框架下实验下了 一、基本的驱动框架 #include #include static int lt6911_driver_init(void) { return 0; } static void lt6911_driver_exit(void) { } module_init(lt6911_driver_init); module_exit(lt6911_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("LY"); MODULE_VERSION("V1.0"); 二、 增加I2C的框架 1. 添加一个I2C设备 这一步是通过i2c_add_driver(driver)这个API函数实现的,那么就在驱动加载的时候使用这个函数 static int lt6911_driver_init(void) { int ret; this is lt6911_driver_init\n"); ret= i2c_add_driver(<6911_driver); if(ret<0){ lt6911 i2c driver add error\n"); } return 0; } 那么要按照以上的实现方式,必须要先实现一个I2C设备,这个设备是通过i2c_driver这个结构体实现的 struct i2c_driver lt6911_driver={ .probe=lt6911_driver_probe, .remove=lt6911_driver_remove, .driver={ .owner=THIS_MODULE, .name="lt6911uxc", //没有设备树使用的匹配名 .of_match_table=lt6911_id //使用设备树匹配的设备列表 }, .id_table=lt6911_id_table //无论使不使用设备树,这里必须实现 }; 2. 实现两个函数 从上一步的i2c_driver设备结构体可以看出,需要实现probe和remove函数。probe函数是当I2C设备正确挂载后所执行的函数,remove函数是I2C设备卸载时所执行的函数。 probe函数 int lt6911_driver_probe(struct i2c_client *client, const struct i2c_device_id *id) { this is lt6911_driver_probe\n"); return 0; } remove函数 int lt6911_driver_remove(struct i2c_client *client) { this is lt6911_driver_remove\n"); return 0; } 3. 在设备树中对应的I2C下添加此设备信息 上面两步完成后,编译驱动为KO文件,通过insmod是可以加载此驱动的,但是会发现加载后只会执行到init这一步。那是因为我们没有在设备树中添加相应设备信息。我的板卡是挂在了I2C2上的,于是就进行下面操作 &i2c2{ status = "okay"; clock-frequency = ; lt6911uxc:lt6911uxc@56{ compatible = "lt6911uxc"; status = "okay"; reg = ; //设备的芯片地址,手册都会说明 interrupt-parent = ; interrupts = ; rst-gpio = ; pinctrl-names="default"; pinctrl-0=< ; }; }; 然后重新编译内核,烧录开发板。再此进行加载KO文件,发现可以打印probe函数中设置的打印语句了 image-20230608105838962 4. 实现最简单的读取Chip ID 框架都搭建完成,接下来当然是与芯片交流一下了,阅读了下LT6911UXC相关手册,要想读取寄存器的数据还得改变它的I2C工作模式和切换bank.原因是其内部集成了MCU,而这个MCU也是通过这个I2C在内部已经连接了LT6911UXC处理核心。 那么就要实现i2c的write和read函数了。驱动程序中I2C的读写都是以包的形式发送和接收的,所以我们先封包。封包使用的结构体是struct i2c_msg,最终的读写函数实现如下 static void lt6911_i2c_write( u16 reg, u8 *values, u32 n) { struct i2c_msg msgs ; int err, i; u8 data ; 8; u8 reg_addr = reg & 0xFF; u8 buf = {0xFF, bank}; data = reg_addr; for (i = 0; i < n; i++) data = values ; /* write bank */ addr; msgs .flags = 0; msgs .len = 2; msgs .buf = buf; /* write reg data */ addr; msgs .flags = 0; msgs .len = 1 + n; msgs .buf = data; adapter, msgs, ARRAY_SIZE(msgs)); if(err < 0){ transfer error %d\n",err); } } static int lt6911_i2c_read(u16 reg,u8 *values, u32 n) { int ret; 8; u8 reg_addr = reg & 0xFF; u8 bank_buff ={0xff,bank}; struct i2c_msg msgs ={ addr, .flags=0, .len=2, .buf=bank_buff, }, ={ addr, .flags=0, .len=sizeof(reg_addr), .buf=®_addr, }, ={ addr, .flags=1, .len=sizeof(values), .buf=values, } }; adapter, msgs, ARRAY_SIZE(msgs)); if(ret < 0){ transfer error %d\n",ret); return ret; } return 0; } 读写函数实现没问题了,那么就在init函数中添加调用就可以了 lt6911_i2c_write( 0x80ee, &i2c_enable, 1); lt6911_i2c_read(0x8100,&rdata,1); lt6911_id is %#x\n",rdata); 编译后,再次加载KO文件,发现在写函数中i2c_transfer函数返回值为-6,意思是NO ACK。怎么回事呢,经过询问最近比较火热的Chatgpt,它告诉我了个答案 也就是我们给了设备地址,但是这个函数会将设备地址左移后然后增加读写位,才是真正的发送的地址。而通过开发板命令行中使用I2C工具(命令:i2cdump -y -f 2 0x56)来读取设备寄存器,通过逻辑分析仪抓取后得到 0x56左移一位再加上写标志位,确实是0xAC啊,经过资料的一番查找,对于I2C设备地址,都是七位。而资料给的发送格式0x56是带有读写位的。那么去掉读写位,也就是将0x56右移一位,在最高位加一个零,就得到了0x2B,再次使用I2C工具试下 就这样成功了,翻阅了大量资料。对于一个初学者而言都是在不断地怀疑和比较中找到了答案。还是挺兴奋的。所以我们就要把设备树中的配置更改下 &i2c2{ status = "okay"; clock-frequency = ; lt6911uxc:lt6911uxc@56{ compatible = "lt6911uxc"; status = "okay"; reg = ; //设备的芯片地址,手册都会说明 interrupt-parent = ; interrupts = ; rst-gpio = ; pinctrl-names="default"; pinctrl-0=< ; }; }; 编译内核,烧录。读chipID成功 总结 i2c的读写最关键的就是设备地址了,驱动的框架是固定的。 学习就应该在怀疑中调试,在调试中比较,在比较中得到答案。我们都是站在巨人的肩膀上的,当自己出现问题时,最好是看看巨人都是怎么做的。 原文链接
  • 热度 4
    2022-5-24 08:32
    6517 次阅读|
    0 个评论
    如何用一条I2C总线驱动多个I2C设备?
    应用设计中,用一条I2C总线驱动多个I2C设备师很常见,实施也很简单,本项目将连接以下I2C设备: 1x16×2 I2C LCD显示屏,地址0x27 1x 128×32 I2C OLED显示屏,地址0x3C 2x PCF8574 I2C Io扩展器,地址0x20、0x21 上述组件可使用以下库文件,通过Arduino Uno控制: LiquidCrystal_I2C.h,控制LCD屏; Wire.h and PCF8574.h,控制I2C IO扩展器; Adafruit_GFX, Adafruit_SSD1306.h 和 SPI.h,控制SSD1306 128×32 OLED显示屏。 由于所有元件相距不远,没必要I2C总线使用上拉电阻,因为这些杜邦线就是组件的一部分。 本项目电路很直观,不同的是,Uno板子在顶部靠近USB适配器附近增加了一个I2C口,我们把它和A4、A5一起使用。 首先,将所有I2C组件的SDA引脚以串行方式连接在一起,再连接于Arduino SDA引脚(一般为A4)。 其次,将所有I2C组件的SCL引脚串联起来,再连接到Arduino SCL引脚(一般为A5) 第三,将所有5v(Vcc)引线连接到Arduino开发板的5v引脚,所有接地引线(GND)连接到Arduino板子的GND引脚。 第四,通过阻值在640-1000欧姆范围内的电阻,将4个LEDs的一个引脚连接到两个PCF8574 IO扩展器P0、P1引脚,另一个引脚接地。 本项目采用外接的5v电源供电,因为根据Uno的实际需求,没必要从稳压器获得太大的电流。 连接完成后,检查所有接线,再打开Arduino IDE中的i2c扫描工具,就会再示例中看到库 Wire.h 文件。 启动电源,将库文件上传到Uno,再打开Serial Monitor,就会看到检测到了4个I2C设备。 如果没有检测到4个设备,检查接线和设备地址,看看问题出在哪里,很有可能是接线问题,或者设备的地址错了。 如果一切无误,将以下代码拷入Arduino IDE,相关解释参见中间的注释。 /* Multiple devices on the I2C bus Maker and Iot Ideas, MakerIoT2020 */ // Include the libraries that we will need #include // needed for OLED display. #include // PCF8574 #include // Generic I2C library #include // for OLED display #include // for OLED display #include // For I2C LCD display // we need to define the size of the OLED screen #define OLED_WIDTH 128 #define OLED_HEIGHT 32 // mine does not have an onboard reset pin. If yours do, specify the // pin that it is connected to on the Arduino here. To use the // Arduino reset pin, specify -1 as below #define OLED_RESET -1 // Define the OLED display, width,hight protocol and reset pin Adafruit_SSD1306 oled(OLED_WIDTH,OLED_HEIGHT, &Wire, OLED_RESET); // Define the I2C LCD screen address and pin configuration LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3,POSITIVE); // Define the PCF8574 devices ( you can have up to 8 on a bus ) // but in this case, my LCD uses address 0x27, so I will have a // conflicting address if I were to use 8 of them together with the // LCD PCF8574 Remote_1(0x20); PCF8574 Remote_2(0x21); // Note the I2C addresses. You can obtain them from the i2c_scanner void setup() { // serial debugging if needed Serial.begin(115200); // Start OLED Display Init if (!oled.begin(SSD1306_SWITCHCAPVCC,0x3C)) { // Init the OLED Serial.println(F("OLED INIT FAILED")); for(;;); // Dont proceed ... loop forever } oled.display(); delay(2000); // This delay is required to give display time to // initialise properly oled.clearDisplay(); oled.setTextSize(0); oled.setTextColor(SSD1306_WHITE); oled.setCursor(0,0); oled.println("TEST SCREEN"); oled.display(); delay(2000); oled.clearDisplay(); oled.setCursor(1,0); oled.println("OLED SCREEN ON"); oled.display(); // Start the LCD lcd.begin(16,2); // Set the initial state of the pins on the PCF8574 devices // I found that the PCF8574 library sometimes does funny things // This is also an example of how to use native i2c to set the // status of the pins Wire.begin(); Wire.beginTransmission(0x20); // device 1 Wire.write(0x00); // all ports off Wire.endTransmission(); Wire.begin(); Wire.beginTransmission(0x21); // device 2 Wire.write(0x00); // all ports off Wire.endTransmission(); // Set pinModes for PCF8574 devices // Note that there are two of them Remote_1.pinMode(P0,OUTPUT); Remote_1.pinMode(P1,OUTPUT); Remote_2.pinMode(P0,OUTPUT); Remote_2.pinMode(P1,OUTPUT); // Start both IO extenders Remote_1.begin(); Remote_2.begin(); // and set ports to low on both // you may find that if you ommit this step, they come up in an // unstable state. Remote_1.digitalWrite(P0,LOW); Remote_1.digitalWrite(P1,LOW); Remote_2.digitalWrite(P0,LOW); Remote_2.digitalWrite(P1,LOW); } void loop() { // Draw a character map on the OLED display. // This function is borrowed from the Adafruit library testdrawchar(); // Write to the IO extenders Remote_1.digitalWrite(P0,HIGH); Remote_1.digitalWrite(P1,LOW); Remote_2.digitalWrite(P0,HIGH); Remote_2.digitalWrite(P1,LOW); // Display their status on the LCD lcd.setCursor(0,0); lcd.print(" R1 P0=1 P1=0"); lcd.setCursor(0,1); lcd.print(" R2 P0=1 P1=0"); delay(500); // Change status Remote_1.digitalWrite(P1,HIGH); Remote_1.digitalWrite(P0,LOW); Remote_2.digitalWrite(P1,HIGH); Remote_2.digitalWrite(P0,LOW); // Update LCD lcd.setCursor(0,0); lcd.print(" R1 P0=0 P1=1"); lcd.setCursor(0,1); lcd.print(" R2 P0=0 P1=1"); delay(500); // Do some graphics on the OLED display // Function borrowed from Adafruit testdrawrect(); oled.clearDisplay(); delay(500); // repeat indefinitely } void testdrawrect(void) { oled.clearDisplay(); for(int16_t i=0; i oled.drawRect(i, i, oled.width()-2*i, oled.height()-2*i, SSD1306_WHITE); oled.display(); // Update screen with each newly-drawn rectangle delay(1); } delay(500); } void testdrawchar(void) { oled.clearDisplay(); oled.setTextSize(1); // Normal 1:1 pixel scale oled.setTextColor(SSD1306_WHITE); // Draw white text oled.setCursor(0, 0); // Start at top-left corner oled.cp437(true); // Use full 256 char 'Code Page 437' font // Not all the characters will fit on the display. This is normal. // Library will draw what it can and the rest will be clipped. for(int16_t i=0; i<256; i++) { if(i == '\n') oled.write(' '); else oled.write(i); } oled.display(); delay(500); }
  • 热度 10
    2021-6-15 10:42
    1729 次阅读|
    0 个评论
    参考https://www.eet-china.com/mp/a21533.html I2C通讯只传输两种类型的帧:地址帧和数据帧 实现逻辑:开始信号、应答信号、数据信号、停止信号、重复开始信号 约定:主机提供时钟信号,时钟上升沿发数据 开始信号: 主:(初始)SDA=1;SCL=1;①SDA=0;②地址帧:7bit从机地址+1bit读写 从:③ACK(SDA=0)规定时间9个时钟周期内回复 n个数据信号: 主:④SDA=0;⑤数据帧8bit 从:⑥ACK或NACK 停止信号: 主:⑦SCL=0;⑧SDA=1; 重新开始信号: 主:①SCL=0,SDA=1;②SCL=1
  • 热度 20
    2020-7-9 15:12
    5669 次阅读|
    0 个评论
    噪音滤波器 I2C模块包含模拟噪音滤波器和数字噪音滤波器,其中模拟噪音滤波器可以抑制尖峰宽度高达50ns(满足快速模式协议规格),用户可以选择关闭这一特性;数字滤波器抑制尖峰噪音的长度可以通过软件配置,范围为1~15(DNF)个I2CCLK周期,当这一特性开启后,SCL/SDA线的电平只有在DNF*I2CCLK周期内持续保持稳定时才会改变 I2C时序 当检测到SCL下降沿时,再一段延迟等待,SDA数据才会被送出。这个等待延迟由Tsdadel定义,时序寄存器中SDADEL位用于该延时配置。Tsdadel = SDADEL * Tpresc + Ti2cclk,其中Tpresc=(PRESC+1)*Ti2cclk。总的SDA输出延时等于Tsync1+{ * Ti2cclk} 数据传输(通过发送寄存器、接收寄存器和移位寄存器实现) 接收:SDA输入信号填入移位寄存器,第8个SCL脉冲后,当RXNE=0时,移位寄存器的内容会复制进接收寄存器;当RXNE=1时,意味着前一个数据还没有被读出,此时SCL线会被拉低(STRETCHED),直到前一个数据被读出,SCL延伸信号会被插入在第8与第9个SCL脉冲之间(即ACK信号前) 发送:发送寄存器非空时(TXE=0),该数据会在第9个SCL脉冲后被复制到移位寄存器,然后移位寄存器的内容会陆续发送到SDA线上。如果TXE=1,意味着数据寄存器中没有数据,此时SCL线会被拉低(STRETCHED),直到新的数据被写入。SCL延伸信号会在第9个SCL脉冲后插入。 硬件传输管理 I2C内嵌一个字节计数器,用于管理字节传输以及不同模式下执行关闭通讯操作,如主模式下的NACK/STOP/RESTART,从模式下的ACK控制,SMBUS特性时的PEC产生以及校验 字节计数器在主模式时会被打开。而从模式下默认会被关闭,但可以通过软件开启(CR2:SBC位) 要传输的字节个数在CR2:NBYTES 中定义(如果字节个数超过255或者需要在每次收到数据后都发送ACK,则需要将CR2:RELOAD位置1),如果定义的字节数传输完成后,TCR标志会被置1,SCL会被拉伸直到TCR标志被清除(NBYTES写入非0值时会清除TCR标志) 主模式下,当RELOAD=0且AUTOEND=1时,一旦定义的字节数传输完毕,STOP条件会被自动发出 主模式下,当RELOAD=0但AUTOEND=0时,定义的字节数传输完毕后,TC标志会被置位,SCL信号会被拉伸直到TC标志被清除(当发送START或STOP条件时,TC标志会被清除) RELOAD=1时,AUTOEND位无效 I2C从机初始化 I2C模块提供了2个从机地址寄存器,地址1可以被配置为7位模式或10位模式,地址2为7位地址但可以配合屏蔽选项以选择使用不同的地址 默认情况下,时钟拉伸功能会被启用,如果I2C主机不支持该功能,则需要设置NOSTRETCH=1以关闭该功能 收到ADDR匹配中断后,需要读取ISR:ADDCODE 以确认匹配的地址,以及通过DIR标志来识别传输的方向 当NOSTRETCH=0时,收到ADDR匹配后,SCL时钟线会被拉伸直到ADDR标志被清除;在数据发送过程中,如果前一个数据传输已经完成,新的数据还没有被写入数据发送寄存器,或者在ADDR标志被清除时,第一个传输数据还没有被写入到数据发送寄存器(TXE=1),这两种情况下,SCL信号都会被拉伸,直到新的数据被写入至数据发送寄存器;在接收过程中,当数据接收寄存器的数据还没有被读出而新的数据又收到的时候,SCL时钟信号会被拉伸,直到数据接收寄存器中的数据被读出后才会释放;在从机字节控制模式下,当设定的字节数被传输完成后(TCR=1),重加载模式(SBC=1,RELOAD=1)意味着最后一个字节数据已经被传输完毕,SCL时钟会被拉伸直至一个非0数据被写入至NBYTES 位中 当NOSTRETCH=1时,I2C从机将不会执行SCL信号拉伸操作。数据发送过程中,在第一个SCL脉冲发生时,如果数据发送寄存器的内容为空,则溢出(UNDERRUN)标记OVR会被置位;在首个数据发送开始时,STOPF位如若仍处于置位状态,OVR标志也会被置位;在接收过程中,用户必须在下个数据的第9个SCL脉冲(ACK脉冲)前把数据接收寄存器的内容读出,否则溢出(OVERRUN)标记OVR会被置位 从机字节控制模式(NOSTRETCH必须为0) 为兼容SMBUS标准,在从机接收模式下,为了允许字节ACK控制,从字节控制模式必须被打开,在此情况下,RELOAD标志必须被置1。为了对每个数据字节进行控制,在ADDR匹配中断处理程序中,NBYTES必须被初始化为1。当数据字节收到后,TCR标志置1,SCL信号在第8到第9个脉冲之间被拉伸,用后可以读取数据接收寄存器,并决定回复ACK还是NACK信号。 从机发送 数据发送寄存器为空时,TXIS传输中断状态位会被置1,当写入新的数据至数据发送寄存器时,TXIS位会被清除。当收到NACK确认信息时,NACKF标志位会被置1,从机自动释放SCL和SDA信号线以使主机可以发送STOP或RESTART条件。当STOP信息收到时,STOPF标志位会被置1,。当收到从机地址时(ADDR=1),用户可以选择发送数据寄存器的内容或者置位TXE位清除数据发送寄存器的内容,以便可以写入新的数据供下次发送。 当NOSTRETCH=1时,SCL时钟信号不会被拉伸,所以用户不能在ADDR接收中断程序中写数据发送寄存器,所以即将发送的数据必须被提前写入 主机发送 TXIS标志在每次字节传输完成后置1(ACK收到后),TXIS事件数取决于NBYTES的值,如果传输字节总数大于255,RELOAD标志位需要被置1,此时若数据传输数到达255时,TCR标志会被置1,同时SCL线会被拉伸,直到NBYTES位被写入新的字节长度值。如果NACK收到后,TXIS标志不会被置1。 当RELOAD=0且NBYTES数据量被传输完成后:STOP条件被自动送出(AUTOEND=1),或者TC标志被置1且SCL线被拉伸(AUTOEND=0),直到RERSTART或STOP条件被发出 主机接收 收到一个字节后,RXNE标志会被置1 当RELOAD=0且NBYTES数据量已经被接收时,NACK和STOP条件会被自动送出(AUTOEND=1),或者NACK被自动送出(AUTOEND=0),TC标志被置1且SCL线被拉伸,直到RERSTART或STOP条件被发出 I2C错误 总线错误BERR:在没有收到ACK/NACK时(未到第9个时钟脉冲时),检测到START或STOP条件即产生总线错误。只有参与通讯的主设备或已经寻址的的从设备才会产生总线错误。 仲裁丢失ARLO:当一个高电平发送至SDA数据线上,当在SCL上升沿时检测到SDA为低,此时产生仲裁丢失错误。在主模式,仲裁丢失会在寻址阶段、数据阶段以及数据确认阶段进行探测。如果发生该错误,START控制位会被清除,SCL/SDA数据线会被释放,主设备自动切换至从设备。在从模式时,仲裁丢失仅在数据阶段和数据确认阶段探测,发生该错误后,传输终止,SCL/SDA信号线会被释放 溢出错误OVR:溢出错误仅发生在从模式下且NOSTRETCH=1时。在接收数据时,新的数据到来但旧数据还没有被读出,此时新的数据会被丢弃,NACK会被自动发出。在发送数据时,当数据字节需要被送出时,发送寄存器的内容还是为空(TXE=1),此时0xFF会被送出 包错误校验错误PECERR:SMBUS相关,略 超时错误TIMEOUT:SMBUS相关,略
相关资源