我几天前在Microchip论坛上发布了这篇文章(here),但唯一的反应是蟋蟀。
下面的I2C代码大部分时间都在工作,但偶尔在上电时会发生总线冲突(BCLIF),并且在BCLIF之后I2C模块无法恢复。
I2C线路上拉3.3K欧姆。
使用REALICE和断点我可以看到i2c_write()
重置BCLIF并在设置BCLIF时返回FALSE。
我使用示波器来验证I2C总线是否具有扁平线。
当init_i2c()
返回FALSE时,重新初始化PIC18F25K20 I2C模块(参见下面的i2c_write()
)没有帮助。
PIC18F25K20 I2C连接到单个从器件(MCP4018 I2C数字POT)。
我在之前的PIC18项目中使用了相同的代码而没有出现问题,所以我更换了怀疑是坏部件的MCP4018,但没有看到任何区别。
有没有办法在锁定时复位PIC18F25K20 I2C模块?
void init_i2c(I2C_BAUD_RATE baud_rate, float freq_mhz)
{
UINT32 freq_cycle;
/* Reset i2c */
SSPCON1 = 0;
SSPCON2 = 0;
PIR2bits.BCLIF = 0;
/* Set baud rate */
/* SSPADD = ((Fosc/4) / Fscl) - 1 */
freq_cycle = (UINT32) ((freq_mhz * 1e6) / 4.0);
if (baud_rate == I2C_1_MHZ)
{
SSPADD = (UINT8) ((freq_cycle / 1000000L) - 1);
SSPSTATbits.SMP = 1; /* disable slew rate for 1MHz operation */
}
else if (baud_rate == I2C_400_KHZ)
{
SSPADD = (UINT8) ((freq_cycle / 400000L) - 1);
SSPSTATbits.SMP = 0; /* enable slew rate for 400kHz operation */
}
else /* default to 100 kHz case */
{
SSPADD = (UINT8) ((freq_cycle / 100000L) - 1);
SSPSTATbits.SMP = 1; /* disable slew rate for 1MHz operation */
}
/* Set to Master Mode */
SSPCON1bits.SSPM3 = 1;
SSPCON1bits.SSPM2 = 0;
SSPCON1bits.SSPM1 = 0;
SSPCON1bits.SSPM0 = 0;
/* Enable i2c */
SSPCON1bits.SSPEN = 1;
}
BOOL i2c_write(UINT8 addr, const void *reg, UINT16 reg_size, const void *data, UINT16 data_size)
{
UINT16 i;
const UINT8 *data_ptr, *reg_ptr;
/* convert void ptr to UINT8 ptr */
reg_ptr = (const UINT8 *) reg;
data_ptr = (const UINT8 *) data;
/* check to make sure i2c bus is idle */
while ( ( SSPCON2 & 0x1F ) | ( SSPSTATbits.R_W ) )
;
/* initiate Start condition and wait until it's done */
SSPCON2bits.SEN = 1;
while (SSPCON2bits.SEN)
;
/* check for bus collision */
if (PIR2bits.BCLIF)
{
PIR2bits.BCLIF = 0;
return(FALSE);
}
/* format address with write bit (clear last bit to indicate write) */
addr <<= 1;
addr &= 0xFE;
/* send out address */
if (!write_byte(addr))
return(FALSE);
/* send out register/cmd bytes */
for (i = 0; i < reg_size; i++)
{
if (!write_byte(reg_ptr))
return(FALSE);
}
/* send out data bytes */
for (i = 0; i < data_size; i++)
{
if (!write_byte(data_ptr))
return(FALSE);
}
/* initiate Stop condition and wait until it's done */
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN)
;
/* check for bus collision */
if (PIR2bits.BCLIF)
{
PIR2bits.BCLIF = 0;
return(FALSE);
}
return(TRUE);
}
BOOL write_byte(UINT8 byte)
{
/* send out byte */
SSPBUF = byte;
if (SSPCON1bits.WCOL) /* check for collision */
{
return(FALSE);
}
else
{
while(SSPSTATbits.BF) /* wait for byte to be shifted out */
;
}
/* check to make sure i2c bus is idle before continuing */
while ( ( SSPCON2 & 0x1F ) | ( SSPSTATbits.R_W ) )
;
/* check to make sure received ACK */
if (SSPCON2bits.ACKSTAT)
return(FALSE);
return(TRUE);
}
答案 0 :(得分:8)
This Errata需要添加到PIC18F25K20勘误表中。
PIC18F2455 / 2550/4455/4550 Rev. A3 Silicon Errata
17模块:MSSP
据观察,上电复位后,I2C模式可能没有 只需将SCL和SDA引脚配置为正确初始化即可 输入或输出。这只是在一些独特的系统中看到的 环境。
对具有统计学意义的样本的测试 预生产系统,跨越电压和电流范围 应用程序的电源应指示系统是否易受影响 这个问题。
解决方法
在为I2C配置模块之前 操作:
- 通过清除SCL和SDA引脚将其配置为输出 相应的TRIS位。
- 通过清除相应的LAT位强制SCL和SDA为低电平。
- 在保持LAT位清零的同时,将SCL和SDA配置为输入 通过设置他们的TRIS位。
醇>完成后,使用SSPCON1和 SSPCON2注册以配置正确的I2C模式。
答案 1 :(得分:2)
同样的错误似乎也发生在PIC18F26K20 / SS(版本B3)上,也需要添加到它的勘误表中。
答案 2 :(得分:2)
我不知道你的具体细节,但是一旦微控制器早早出现复位方式(在I2C总线上Vdd稳定之前),我就遇到了问题。因此,uController在目标正常运行之前开始读/写数据,从而导致各种I2C操作问题。
答案 3 :(得分:1)
此勘误表还帮助我将I2C卡在了PIC18F27J53上(但我也需要解锁被SGP30器件阻止的I2C总线,此处的代码示例:https://github.com/esp8266/Arduino/issues/1025#issuecomment-158667929) 我最终决定实现例程,执行在启动条件失败(我的I2C堆栈的代码片段)时调用的restart-unstuck-restart:
eI2Cerr i2c_do_RUR(void) {
//performs restart-unstuck-restart
eI2Cerr eRetVal;
eRetVal = i2c_do_restart();
if (eRetVal == I2C_ERR_TIMEOUT_RSEN_COLLISION) {
i2c_unstuck(true); //true=performs also I2C bus unlock
eRetVal = i2c_do_restart();
}
return(eRetVal);
}
I2C现在似乎稳定并且运行良好。