gpt4 book ai didi

c++ - ATMega328P 和 ESP8266ex 之间的 I2c 通信只能发送 8 个字节,bug?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:06:57 25 4
gpt4 key购买 nike

我正在开发一个 I2C 桥接器,它使我能够以“智能”方式虚拟扩展 ESP8266ex 上可用的引脚。 ESP8266ex 不错,但可用引脚较少。

看起来扩展引脚集成到 ESP8266ex 本身。实际上这不是因为桥在后台通过 i2c 通信来访问 ATMega328P 上的引脚,但是我可以使用标准方法和函数,例如 pinModedigitalRead/write模拟读/写。因此,ESP8266ex 完全可以控制 ATMega328P 上的引脚。


 I2C MASTER/SLAVE CONFIG            GND
o
------------------------ | ------------------
| MASTER GND o |------------|---------------------- | o GND SLAVE |
| D2 | <- i2c bus -> | A4 |
| ESP8266(ex) SDA o |--------------------|-------------- | o SDA A E |
| SCL o |------------|-----/ | /------------ | o SCL T G |
| D1 | | | | A5 M A |
| +5V o |-| [ ] [ ] --- | o +5V |
------------------------ | 4.7K [ ] [ ] 4.7K | -----------------
| [ ] [ ] |
| | | |
|----------|-------|-----------|
D2 = GPIO 4 (SDA) |
D1 = GPIO 5 (SCL) o +5V

例如,要修改 ATMega328P 上的引脚,我可以执行以下操作(引脚当然会重新映射):


  pinMode( D22, OUTPUT );     // Sets pin D13 = ONBOARD_LED on ATMega328P 
digitalWrite( D22, HIGH ); // Turns the onboard LED ON on ATMega328P
delay(2000); // Wait two seconds
digitalWrite( D22, LOW ); // Turns the onboard LED OFF on ATMega328P

这是非常出色的,非常直接的功能和直接的结果,但是,我还扩展/链接内部 EEPROM 与外部 EEPROM 以“加倍”大小。前 1K 属于 ESP8266ex,接下来的 1K 属于 ATMega328P。


我为此开发了一系列函数,并产生了一堆易于使用的函数:

bool setStorage( uint16_t iAddress, uint8_t* buffer, <parameters> );
bool setStorage( uint16_t iAddress, char* buffer, <parameters> );
bool getStorage( uint16_t iAddress, char* buffer, <parameters> );
.....
char* getStorage( uint16_t iAddress, char* psReturnDefault ); // Returns pointer buffered char array
....

例如,我可以这样做:

setStorage( 2000UL, "Hello world" ); // Set 11 chars in EEPROM on ATMega328P on pos 977
delay(1000);
// Read it back
Serial.println( "String is: " );
Serial.println( (char*)getStorage( 2000UL, "" ) );

问题

我验证了正确写入和读取的数据,但是当从设备 (ATMega328P) 发送超过 8 个字节(使用 Wire.write())时,主设备 (ESP8266ex) 只读取了一堆0xFF(使用 Wire.read())。所以 I2C 通信之间有问题。

检查/验证所有内容,缓冲区大小(32 字节,对于本例来说足够了),检查缓冲区的内容,一切都很好。试图立即发送缓冲,似乎没有任何帮助。

这是 Wire 库中的错误吗?有可用的解决方法吗?



我的 MASTER 库的一部分(无法全部发布,对于 StackOverflow 来说太大了),让您了解我在做什么:

......

#define IPMB_HDR_DATASIZE 0x03
#define IPMB_MAX_DATACOUNT (BUFFER_LENGTH-IPMB_HDR_DATASIZE)

......

typedef struct rIpmbRequestDataStruc
{
uint8_t cmd; // Request command, take a look at IPMB_CMD_* above
uint8_t version; // Software version of request, must match
uint8_t dataType;
uint8_t data[ IPMB_MAX_DATACOUNT ]; // Data/parameters to be send
};


.........


bool i2cBridgeRequest( uint8_t iCmd, // Request command
uint8_t* puResult, // Pointer to result var
uint16_t iParam1, // First parameter
uint16_t iParam2 = 0, // Second parameter, data or length
uint8_t* pParam3 = 0 // Byte data when stream or string
)
{
bool bSuccess = i2cBridgeAvailable();

uint8_t iErrorCode = 0;
uint8_t iDataType = 0;
uint16_t iBytes = 0;


if( bSuccess )
{


rIpmbRequestDataStruc dataStruc;
memset( (uint8_t*)&dataStruc, 0, sizeof( dataStruc ));

dataStruc.cmd = iCmd;
dataStruc.version = IPMB_DSI_VERSION;
dataStruc.dataType = IPMB_DAT_TYPE_UINT16;

uint16_t i = 0;
uint16_t iMax = IPMB_MAX_DATACOUNT+IPMB_HDR_DATASIZE;
uint8_t* pParam = 0;

if( iCmd == IPMB_CMD_EEPROMREAD || iCmd == IPMB_CMD_DIGITALWRITE
|| iCmd == IPMB_CMD_ANALOGWRITE || iCmd == IPMB_CMD_EEPROMWRITE )
{
// First parameter must be 16 bits
pParam = (uint8_t*)&iParam1;
dataStruc.data[i++] = *pParam++;
dataStruc.data[i++] = *pParam;
}
else {
dataStruc.dataType = IPMB_DAT_TYPE_UINT8;
dataStruc.data[i++] = iParam1;
}

if( iCmd == IPMB_CMD_DIGITALWRITE || iCmd == IPMB_CMD_ANALOGWRITE
|| (iCmd == IPMB_CMD_CONFIG && iParam1 == IPMB_CFG_PWMCLOCK )
|| iCmd == IPMB_CMD_EEPROMREAD || pParam3 )
{
// Second parameter must be 16 bits
pParam = (uint8_t*)&iParam2;
dataStruc.data[i++] = *pParam++;
dataStruc.data[i++] = *pParam;
}
else { dataStruc.data[i++] = iParam2; }

// When pParam3 is specified, we expect iParam2 is the length
if( pParam3 )
{
if( iParam2 > 1 )
{ dataStruc.dataType = IPMB_DAT_TYPE_STREAM; }

iParam2+=IPMB_HDR_DATASIZE+1;
while( i < iParam2 && i < iMax )
{ dataStruc.data[i++]=*pParam3++; }
}
else if( iCmd == IPMB_CMD_EEPROMREAD && iParam2 >= 1 )
{ dataStruc.dataType = IPMB_DAT_TYPE_STREAM;
Serial.println( "Data length = " );
Serial.println( iParam2 );
}

// Start transmission and send command and data
Wire.beginTransmission( IPMB_I2C_ADDRESS );
Wire.write( (uint8_t*)&dataStruc, IPMB_HDR_DATASIZE + i );
bSuccess = ( Wire.endTransmission() == 0 );

//Serial.println( bSuccess );

// When data successfully send, perform command and data and ask result by request
if( bSuccess )
{
//Wire.requestFrom( IPMB_I2C_ADDRESS, 3 + ( iCmd == IPMB_CMD_ANALOGREAD) );
Wire.requestFrom( IPMB_I2C_ADDRESS, IPMB_HDR_DATASIZE+IPMB_MAX_DATACOUNT );
//Serial.println( Wire.available() );
if( Wire.available() > 2 )
{
iErrorCode = Wire.read();
if( !(iErrorCode >= IPMB_ECMD_MIN && iErrorCode <= IPMB_ECMD_MAX ))
{ iErrorCode = IPMB_ECMD_INVALID_RESPONSE;

// Debug read, reads only 0xFF's when received more than 8 bytes
while( Wire.available() )
{ Serial.println( Wire.read(), HEX ); }

}
}
else { iErrorCode = IPMB_ECMD_INVALID_RESPONSE; }

bSuccess = ( iErrorCode == IPMB_ECMD_OK );
}

Serial.println( "ErrorCode:" );
Serial.println( iErrorCode, HEX );

if( bSuccess )
{
iDataType = Wire.read();
Serial.println( iDataType, HEX );

if( iDataType != IPMB_DAT_TYPE_NONE )
{
uint8_t* pFuncResult = puResult?puResult:(uint8_t*)&dataStruc.data[0];
uint16_t iMaxBytes = i2cBridgeGetDataSize( iDataType );

Serial.println( "Result is: " );
Serial.println( (char*)pFuncResult );

if( puResult )
{ memset( &pFuncResult[0], 0, sizeof( dataStruc.data )); }

while( Wire.available() && iBytes < iMaxBytes )
{ pFuncResult[iBytes++] = Wire.read(); }

if( iMaxBytes <= 4 )
{ bSuccess = ( iBytes == iMaxBytes ); }
else { bSuccess = ( iBytes > 0 ); }
}
}
else {
if( puResult )
{ *puResult = iErrorCode; }
}

// Eat all left bytes if any
while( Wire.available() )
{ Wire.read(); }


}

return bSuccess;
}

我的 SLAVE 库的一部分(无法全部发布,对于 StackOverflow 来说太大了),让您了解我在做什么:

.........

typedef struct rIpmbResultDataStruc
{
uint8_t errorCode;
uint8_t dataType;
uint8_t data[ IPMB_MAX_DATACOUNT ];
};

........

void eventHandleRequestReplyHandler() // #2 Finish request,

implement received data
{
/*
Serial.print( "Bytes: " );
Serial.println( __iIpmbDataByteCount );
Serial.print( "Command: " );
Serial.println( __rIpmbDataStruc.cmd );
Serial.print( "Version: " );
Serial.println( __rIpmbDataStruc.version, HEX );
Serial.print( "DataType: " );
Serial.println( __rIpmbDataStruc.dataType, HEX );
*/
resetSendBuffer();

uint16_t i = 0;
uint16_t iLength = 0;
uint8_t iValue = 0;
uint16_t iAddress = 0;

// When reboot and sleep mode is previously requested,
// don't allow other commands
if( __bIpmbDoDeviceReset || __bIpmbDoDeviceSleep || isRebootSleepModeRequested() )
{
Wire.write( IPMB_ECMD_BUSY );
Wire.write( IPMB_DAT_TYPE_NONE );
Wire.write(0);
return;
}

if( isValidCommand( __rIpmbDataStruc.cmd ) // Valid command received?
&& isValidVersion( __rIpmbDataStruc.version ) // Version the same?
&& isValidDataType( __rIpmbDataStruc.dataType ) ) // Valid dataType specified?
{
if( __rIpmbDataStruc.cmd == IPMB_CMD_DIGITALWRITE )
{
digitalWrite( getBuffDataUint16(0), getBuffDataUint16(1) );
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_NONE );
Wire.write(0);
return;
}

if( __rIpmbDataStruc.cmd == IPMB_CMD_ANALOGWRITE )
{
analogWrite( getBuffDataUint16(0), getBuffDataUint16(1) );
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_NONE );
Wire.write(0);
return;
}

if( __rIpmbDataStruc.cmd == IPMB_CMD_DIGITALREAD )
{
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_UINT8 );
Wire.write( digitalRead( getBuffDataUint8(0) ));
return;
}

if( __rIpmbDataStruc.cmd == IPMB_CMD_ANALOGREAD )
{
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_UINT16 );
uint16_t iResult = analogRead( getBuffDataUint8(0) );
uint8_t* pResult = (uint8_t*)&iResult;
Wire.write( *pResult++ );
Wire.write( *pResult );
return;
}

if( __rIpmbDataStruc.cmd == IPMB_CMD_PINMODE )
{
pinMode( getBuffDataUint8(0), getBuffDataUint8(1) );
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_NONE );
Wire.write(0);
return;
}


if( __rIpmbDataStruc.cmd == IPMB_CMD_EEPROMREAD )
{
Serial.println( "EEPROM READ");
//Serial.println( getBuffDataUint16(0) );

iAddress = IPMB_ADR_CUSTOM_DATA + getBuffDataUint16(0);
iLength = getBuffDataUint16(1);

if( iLength > IPMB_MAX_DATACOUNT )
{ iLength = IPMB_MAX_DATACOUNT; }



if( __rIpmbDataStruc.dataType == IPMB_DAT_TYPE_STREAM && iLength > 0 )
{
//Wire.write( IPMB_DAT_TYPE_STREAM );
__rIpmbResultStruc.errorCode = IPMB_ECMD_OK;
__rIpmbResultStruc.dataType = IPMB_DAT_TYPE_STREAM;

while( i < iLength )
{
__rIpmbResultStruc.data[i++] = readStorage( iAddress++ );
}

//Serial.println( (char*)&__pIpmbResultByteBuff[0] );
Wire.write( (uint8_t*)&__pIpmbResultByteBuff[0], 2+i );
}
else {
Wire.write( IPMB_DAT_TYPE_UINT8 );
Wire.write( readStorage( iAddress,
getBuffDataUint8(1)
)
);
}
return;
}


if( __rIpmbDataStruc.cmd == IPMB_CMD_EEPROMWRITE )
{
Serial.println( "EEPROM WRITE");
Serial.println( getBuffDataUint16(0) );
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_UINT8 );

iAddress = IPMB_ADR_CUSTOM_DATA + getBuffDataUint16(0);

if( __rIpmbDataStruc.dataType == IPMB_DAT_TYPE_STREAM )
{
iLength = getBuffDataUint16(1);
Serial.println( iLength ); delay(100);

while( i < iLength )
{
iValue = getBuffDataUint8(4+i);
Serial.println( (char)iValue ); delay(100);
if( writeStorage( iAddress++, iValue ) != iValue )
{
Wire.write(0);
return;
}

++i;
}

Wire.write( IPMB_ECMD_OK );
Serial.println( "Done" ); delay(100);
}
else {
Wire.write( writeStorage( iAddress,
getBuffDataUint8(2),
getBuffDataUint8(3),
getBuffDataUint8(4)
)
);
}
return;
}

if( __rIpmbDataStruc.cmd == IPMB_CMD_RESET )
{
//Serial.println( "SoftReset!" );
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_NONE );
Wire.write(0);
__bIpmbDoDeviceReset = true;
return;
}

if( __rIpmbDataStruc.cmd == IPMB_CMD_CONFIG )
{
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_UINT8 );

uint8_t iCfg = getBuffDataUint8(0);

if( iCfg == IPMB_CFG_WIPE || iCfg == IPMB_CFG_WIPE_EEPROM )
{
wipeStorage( iCfg == IPMB_CFG_WIPE_EEPROM );
Wire.write( IPMB_ECMD_OK );

// Always reset
__bIpmbDoDeviceReset = true;
}
else
if( iCfg == IPMB_CFG_MCUCLOCK )
{
Wire.write(
setMcuClock( getBuffDataUint8(1),
__iIpmbConfigAutoSave
)
);
}
else
if( iCfg == IPMB_CFG_PWMCLOCK )
{
Wire.write(
setPwmClock( getBuffDataUint8(1),
getBuffDataUint8(2),
__iIpmbConfigAutoSave
)
);
}
else { Wire.write(0); }

// Set reboot flag if required
if( __iIpmbRebootAtConfig && __iIpmbConfigAutoSave )
{ __bIpmbDoDeviceReset = true; }

return;
}

if( __rIpmbDataStruc.cmd == IPMB_CMD_SLEEP )
{
//Serial.println( "Sleep" );
Wire.write( IPMB_ECMD_OK );
Wire.write( IPMB_DAT_TYPE_UINT8 );
if( getBuffDataUint8(0) )
{ __bIpmbDoDeviceSleep = true; }
Wire.write( (uint8_t)__bIpmbDoDeviceSleep );
return;
}
}

if( isValidCommand( __rIpmbDataStruc.cmd ) && !isValidVersion( __rIpmbDataStruc.version ))
{ Wire.write( IPMB_ECMD_INVALID_VERSION ); }
else { Wire.write( IPMB_ECMD_INVALID_REQUEST ); }

Wire.write( IPMB_DAT_TYPE_NONE );
Wire.write(0);

resetDataBuffer();
}

最佳答案

终于找到bug和解决办法了,玩了几个小时ESP的库代码,是ESP twi库的问题。我将其发布为答案,也许它可以帮助其他人。原因:超时范围太小,因此,提前调用超时,依赖它的函数将失败。这就是读取 0xFF 而不是实际接收到的数据(它实际上在那里)的原因。

我从以前的项目中了解到,Esp8266 对延迟非常挑剔,处理过程由于某种原因花费的时间太长,可能会导致崩溃或设备开始出现故障,但是,实际上,这个超时时间太短了,尤其是当你使用更多的时候phiperials 例如显示器或想要通过总线发送更多字节。

这是 ESP8266 twi 库中的一个错误,位于 twi_init 函数内。它与 I2C 总线读取超时有关,不能通过类函数更改(您可以更改总线速度但不能更改此),值在该函数中硬编码。

函数在packages/esp8266/2.4.0/cores/esp8266/core_esp8266_si2c.c:

void twi_init(unsigned char sda, unsigned char scl){
twi_sda = sda;
twi_scl = scl;
pinMode(twi_sda, INPUT_PULLUP);
pinMode(twi_scl, INPUT_PULLUP);
twi_setClock(100000);
twi_setClockStretchLimit(230); // default value is 230 uS
}

twi_setClockStretchLimit() 指令中,“ClockStretch”(不管是什么意思)设置为 230 uS,这太低或太窄了。

要修复它,您需要将此值增加到 600 或更多,并且必须在初始化 Wire 库之后执行此操作,例如:

Wire.begin();
// Give it some time
delay( 500 );
// default value is set to 230 uS, we change it here
twi_setClockStretchLimit(600);
.....
.....

现在我可以接收完整的 32 个字节(默认缓冲区限制)。因此,当我向 ATMega 从机询问 EEPROM 中的字符串(流)时,我将收到 28 个字节的数据和 4 个字节的数据信息(错误代码(字节)、数据类型(字节)、长度(2 字节))。

玩得开心;-)

关于c++ - ATMega328P 和 ESP8266ex 之间的 I2c 通信只能发送 8 个字节,bug?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48350250/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com