Air-duino / Arduino-AirMCU

Arduino core for the Air MCU.
https://arduino.luatos.com/
MIT License
69 stars 21 forks source link

AIR001的I2C作从机,接收主机用Wire.write发送过来的数据异常 #67

Open luckycheng2017 opened 1 year ago

luckycheng2017 commented 1 year ago

描述一下这个bug / Describe the bug

两块板子,一块是ESP32C3,另一块是AIR001,用I2C连接,有接上拉电阻。ESP32C3作主机,AIR001作从机。ESP32C3主机用Wire.write方法发送操作多个字节的数据时,反馈结果是2,AIR001从机那边没有收到数据。之前 #34 这个bug有提过,但是没彻底解决。

复现步骤 / To Reproduce

从机先复位,然后主机复位,主机能找到从机的地址,从机没有收到主机发送过来的数据。

如果正常,应该是什么样 / Expected behavior

正常AIR001从机能够读取到ESP32主机发过来的数据。

截图 / Screenshots

ESP32C3主机代码: `

include

define I2C_slave_ADDR 0x08

uint8_t hello[] = {1, 2, 3, 4, 5, 6}; uint8_t aTxBuffer[] = "你好"; uint8_t myBuffer[10];

void setup() { Wire.setClock(100000); Wire.begin(4, 5);

Serial.begin(9600);

byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); }
} if (nDevices == 0) { Serial.println("No I2C devices found\n"); } else { Serial.println("done\n"); } }

void loop() { Wire.beginTransmission(I2C_slave_ADDR); Wire.write((uint8_t *) hello, sizeof(hello)); Serial.print("发送:"); Serial.println(Wire.endTransmission()); delay(2000); }

`

AIR001从机代码 `

include

define I2C_ADDR 0x08

uint8_t hello[] = {1, 2, 3}; uint8_t aTxBuffer[] = "你好";

// function that executes whenever data is received from master // this function is registered as an event, see setup() void receiveEvent(int howMany) { while(1 < Wire.available()) // loop through all but the last { int c = Wire.read(); // receive byte as a character Serial.println(c); // print the character } int x = Wire.read(); // receive byte as an integer Serial.println(x); // print the integer }

// function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() {
int res = Wire.write((uint8_t *) hello, sizeof(hello)); Serial.print("发送数据, res = "); Serial.println(res); }

void setup() { Serial.begin(9600); // start serial for output

Wire.setSDA(PF_0); Wire.setSCL(PF_1); Wire.setClock(100000); Wire.begin((int) I2C_ADDR, false, true); // join i2c bus with address #4 Wire.onReceive(receiveEvent); // register event Wire.onRequest(requestEvent); // register event }

void loop() { //empty loop } `

日志 / Logs

主机日志: 990ff76fdbc68116e70e62105a953fa

从机日志: e955f0e6a6af9b15ba693432f75f7dc

系统 / System

Win10

PACK包版本 / Version

0.4.5

验证

luckycheng2017 commented 1 year ago

补充说明:差不多同一份代码,修改一下引脚,两个ESP32C3的板子之间I2C通信是正常的。 ESP32C3主机代码: `

include

define I2C_slave_ADDR 0x08

uint8_t hello[] = {1, 2, 3, 4, 5, 6}; uint8_t aTxBuffer[] = "你好"; uint8_t myBuffer[10];

void setup() { Wire.setClock(100000); Wire.begin(4, 5);

Serial.begin(9600);

byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); }
} if (nDevices == 0) { Serial.println("No I2C devices found\n"); } else { Serial.println("done\n"); } }

void loop() { Wire.beginTransmission(I2C_slave_ADDR); Wire.write((uint8_t *) hello, sizeof(hello)); Serial.print("发送:"); Serial.println(Wire.endTransmission()); delay(2000); }

`

ESP32C3从机代码: `

include

define I2C_slave_ADDR 0x08

uint8_t hello[] = {1, 2, 3, 4, 5, 6}; uint8_t aTxBuffer[] = "你好";

void receiveEvent(int len) { while (Wire.available() > 1) { int c = Wire.read(); Serial.print(c); } int x = Wire.read(); Serial.println(x); }

void requestEvent() { Wire.write(hello, sizeof(hello)); Wire.write(0); }

void setup() { Wire.begin(I2C_slave_ADDR, 4, 5, 100000); Wire.onRequest(requestEvent); Wire.onReceive(receiveEvent);

Serial.begin(9600);

}

void loop() { }

`

ESP32C3主机日志: c0d130be40f0ebe2617ab3f8c73e4b0

ESP32C3从机日志: c782883c4b45e16b1e24272588633f7

luckycheng2017 commented 1 year ago

补充说明,ESP32C3主机用Wire.write发送单个字节的数据,AIR001才能收到,但是发送下个字节前,要操作Wire.beginTransmission(I2C_slave_ADDR); Wire.endTransmission()才不会漏发数据。

ESP32C3主机代码: `

include

define I2C_slave_ADDR 0x08

uint8_t hello[] = {1, 2, 3, 4, 5, 6}; uint8_t aTxBuffer[] = "你好"; uint8_t myBuffer[10];

void setup() { Wire.setClock(100000); Wire.begin(4, 5);

Serial.begin(9600);

byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); }
} if (nDevices == 0) { Serial.println("No I2C devices found\n"); } else { Serial.println("done\n"); } }

int i = 0;

void loop() { i++; if (i > 100) { i = 0; } Wire.beginTransmission(I2C_slave_ADDR); Wire.write(i); Serial.print("发送1:"); Serial.println(Wire.endTransmission()); Wire.beginTransmission(I2C_slave_ADDR); Serial.print("发送2:"); Serial.println(Wire.endTransmission()); delay(2000); } `

AIR001从机代码: `

include

define I2C_ADDR 0x08

uint8_t hello[] = {1, 2, 3}; uint8_t aTxBuffer[] = "你好";

// function that executes whenever data is received from master // this function is registered as an event, see setup() void receiveEvent(int howMany) { while(1 < Wire.available()) // loop through all but the last { int c = Wire.read(); // receive byte as a character Serial.println(c); // print the character } int x = Wire.read(); // receive byte as an integer Serial.println(x); // print the integer }

// function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() {
int res = Wire.write((uint8_t *) hello, sizeof(hello)); Serial.print("发送数据, res = "); Serial.println(res); }

void setup() { Serial.begin(9600); // start serial for output

Wire.setSDA(PF_0); Wire.setSCL(PF_1); Wire.setClock(100000); Wire.begin((int) I2C_ADDR, false, true); // join i2c bus with address #4 Wire.onReceive(receiveEvent); // register event Wire.onRequest(requestEvent); // register event }

void loop() { //empty loop } `

ESP32C3主机日志: 9718d8757580ee45118550416d51bcd

AIR001从机日志: 45de914ae47eb751ed47442601f4d68

luckycheng2017 commented 1 year ago

AIR001作I2C从机时,比较不稳定,容易返回2,望解决这个问题。

jyzhkj commented 1 year ago

用逻辑分析仪看数据, 可以发现,应该是ev中断的处理判断 问题。 主机发数据后,下个周期再发,air从机才会触 发中断。 hal库的HAL_I2C_Slave_Receive_IT 函数,是有字节长度判断 的。

jyzhkj commented 1 year ago

在主机端,暂时用这个方式来解决: 速度400k(从机不需要指定速度) ` u8_t rtv;

Wire.beginTransmission(I2c_base);       // transmit to device #8
Wire.write((uint8_t *)&au16data[1], 2); // sends 2 byte
Wire.write((uint8_t *)&au16data[2], 2); // sends 2 byte
rtv = Wire.endTransmission();       // stop transmitting
bitWrite(au16data[24], 0, rtv); // i2c 状态  出错置0
//delayMicroseconds(10);
Wire.beginTransmission(I2c_base);
Wire.endTransmission();
return true;`

/ 未处理前的 时序图, 分别是scl,sda,接收中断中的串口输出

1 2 3

处理后:

4 5

jyzhkj commented 1 year ago

TWI.C中的 HAL_I2C_ListenCpltCallback 回调函数不能有效触发, 上面增加了 Wire.beginTransmission(I2c_base); Wire.endTransmission(); 也就是激活触发这个回调函数。

也可在HAL_I2C_SlaveRxCpltCallback 函数中 增加判断接收的字节来实现回调(前提是你I2C固定通讯长度)