- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在开发一个 I2C 桥接器,它使我能够以“智能”方式虚拟扩展 ESP8266ex 上可用的引脚。 ESP8266ex 不错,但可用引脚较少。
看起来扩展引脚集成到 ESP8266ex 本身。实际上这不是因为桥在后台通过 i2c 通信来访问 ATMega328P 上的引脚,但是我可以使用标准方法和函数,例如 pinMode
、digitalRead/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/
有一条(相对)众所周知的 Perl 公理:“只有 Perl 可以解析 Perl”。我想知道 Perl 6 是否仍然如此? 扩大讨论...考虑到 PyPy 最近的更新,我想到了这个问题。 Perl 独特
这是设置。在上一个问题中,我发现我可以通过子组件中的状态传递对象属性,然后使用 componentDidUpdate 获取该对象属性。在这种情况下,状态和属性都称为到达。 这是基本代码... expo
我运行的是 10.5.2 社区版。我已经标记了 源/主要/资源 作为源目录。我可以右键单击并“编译”某些文件,据我所知,这意味着 IDE 将文件复制到与发送类文件的“com.mydomain.pack
我是一名优秀的程序员,十分优秀!