读取时I2C异常延迟问题
我一直在努力让我的态度85位的爆炸I2C(读/写)。我有以下配置:读取时I2C异常延迟问题,c,avr,i2c,C,Avr,I2c,我一直在努力让我的态度85位的爆炸I2C(读/写)。我有以下配置: PB0 = SDA PB1 = LED PB2 = SCL 我可以毫无问题地写,但只有在读循环中有我的'delay()'函数的情况下,才可以读,到目前为止还不错: char i2c_read(void) { uint8_t B = 0; DDRB &= 0b11111110; // switch PB0 to input for ( int bit = 0; bit < 0x08; bi
PB0 = SDA
PB1 = LED
PB2 = SCL
我可以毫无问题地写,但只有在读循环中有我的'delay()'函数的情况下,才可以读,到目前为止还不错:
char i2c_read(void)
{
uint8_t B = 0;
DDRB &= 0b11111110; // switch PB0 to input
for ( int bit = 0; bit < 0x08; bit++ )
{
delay(); // <--!!!!!!!!! the root of all evil
SIGNAL_HIGH( PORT_SCL );
B <<= 1;
if( PINB & (1 << PB0 ) )
{
B |= 1;
}
else
{
B |= 0;
}
SIGNAL_LOW( PORT_SCL );
}
DDRB |= 0b00000001; // switch PB0 as output
i2c_nack();
return B;
}
我怀疑我可能“锁定”了一个完美的延迟,该延迟产生了另一个设备所期望的适当信号长度,因此我尝试使用for循环和示波器进行相同的延迟:
void delay()
{
for( int i=0; i<20; i++){ }
}
奇怪的是I2C又停止工作了!只有当我将PB1设置为高/低时,它才起作用。我仍然无法理解,我是否恰好确定了所需的完美延迟,只是碰巧打开PB1比打开PB3花费的时间更少,或者它与电路本身有关,LED在I2C上执行某种上拉/下拉功能(请原谅我的无知,我是初学者),但是PB1也没有连接到I2C线路
有人能解释一下为什么只有在我打开/关闭PB1时它才起作用,而不是真正的延迟吗?谢谢
全文来源:
#define PORT_SDA PB0
#define PORT_SCL PB2
#define SIGNAL_HIGH(PORT) PORTB |= ( 1 << PORT )
#define SIGNAL_LOW(PORT) PORTB &= ~( 1 << PORT )
void delay();
void LED_ON(void);
void LED_OFF(void);
void i2c_init(void);
void i2c_start(void);
char i2c_read(void);
void i2c_stop(void);
void i2c_nack(void);
void i2c_ack(void);
void i2c_ack_slave(void);
void i2c_write(uint8_t byte);
void i2c_init()
{
DDRB = 0b00000010; // TODO: should be removed once the weird delay issue is solved
DDRB |= ( 1 << PORT_SDA );
DDRB |= ( 1 << PORT_SCL );
}
void i2c_start( void )
{
SIGNAL_LOW( PORT_SCL );
SIGNAL_HIGH( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SDA );
SIGNAL_LOW( PORT_SCL );
}
void i2c_stop( void )
{
SIGNAL_LOW( PORT_SCL );
SIGNAL_LOW( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_HIGH( PORT_SDA );
}
void i2c_ack(void)
{
SIGNAL_LOW( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
SIGNAL_HIGH( PORT_SDA );
}
void i2c_nack(void)
{
SIGNAL_HIGH( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
}
void i2c_ack_slave(void)
{
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
}
void i2c_write(uint8_t byte)
{
uint8_t bit;
for ( bit = 0; bit < 0x08; bit++ )
{
if( ( byte << bit ) & 0x80 )
SIGNAL_HIGH( PORT_SDA );
else
SIGNAL_LOW( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
}
// Clear both lines (needed?)
SIGNAL_LOW( PORT_SCL );
SIGNAL_LOW( PORT_SDA );
i2c_ack();
}
char i2c_read(void)
{
uint8_t B = 0;
DDRB &= 0b11111110; // switch PB0 to input
for ( int bit = 0; bit < 0x08; bit++ )
{
delay(); // <-- the root of all evil
SIGNAL_HIGH( PORT_SCL );
B <<= 1;
if( PINB & (1 << PB0 ) )
{
B |= 1;
}
else
{
B |= 0;
}
SIGNAL_LOW( PORT_SCL );
}
DDRB |= 0b00000001; // switch PB0 as output
i2c_nack();
return B;
}
void delay()
{
LED_ON();
LED_OFF();
}
void LED_ON( void )
{
PORTB |= 0b00000010;
}
void LED_OFF( void )
{
PORTB &= 0b11111101;
}
#定义端口SDA PB0
#定义端口\u SCL PB2
#定义信号_HIGH(PORT)PORTB |=(1I2c定义了信号的一些最小计时-这里重要的是SCL的高时间和低时间-在允许下一次转换到相反状态之前,SCL应该稳定的时间量。
这些计时为典型的~5µs,精确数字应取自数据表
read
循环结束时的循环大约需要2到3条指令,这取决于编译器的工作。AVR指令大约需要200纳秒,取决于您的时钟频率,因此(没有延迟)SCL低约600纳秒,给定或获取-这太短了,至少对于您的特定应用程序来说显然是如此“另一端设备”
当您在被调用函数中插入函数调用和端口访问时,您插入了足够多的指令,使SCL保持在低水平足够长的时间,以便正常工作
在您的代码中,高时间并不是问题所在,因为您让AVR在SCL高的时候执行更多的指令-显然,足够长的时间来保持SCL高
您在延迟功能中切换端口引脚的事实与此无关-唯一的相关性是您需要在SCL较低时花费一些时间。显然,您目前所做的只是浪费端口引脚等待一段时间-使用delay\u us
,试验延迟,而不是延迟。但请检查“另一端的“数据表”对于所需的精确定时,4-5µs应该可以
为什么你的延迟循环不起作用?很可能是被编译器优化掉了,因为编译器意识到你在这个空循环中没有做任何相关的事情
理想情况下,您应该尝试在大约SCL高相位的中间读取SDA-使用8位的未滚动循环和一些delay\u us
扩展,这样应该可以很好地工作。您实际上是在读取SCL线上升沿上的SDA线;也许您的设备还没有准备好?您是否尝试在更近的位置读取SDA而是下降沿?即,信号高(端口SCL);\u延迟(1);B=(B
void delay()
{
LED_ON();
LED_OFF();
}
void LED_ON( void )
{
PORTB |= 0b00001000; // PB3
}
void LED_OFF( void )
{
PORTB &= 0b11110111; // PB3
}
#define PORT_SDA PB0
#define PORT_SCL PB2
#define SIGNAL_HIGH(PORT) PORTB |= ( 1 << PORT )
#define SIGNAL_LOW(PORT) PORTB &= ~( 1 << PORT )
void delay();
void LED_ON(void);
void LED_OFF(void);
void i2c_init(void);
void i2c_start(void);
char i2c_read(void);
void i2c_stop(void);
void i2c_nack(void);
void i2c_ack(void);
void i2c_ack_slave(void);
void i2c_write(uint8_t byte);
void i2c_init()
{
DDRB = 0b00000010; // TODO: should be removed once the weird delay issue is solved
DDRB |= ( 1 << PORT_SDA );
DDRB |= ( 1 << PORT_SCL );
}
void i2c_start( void )
{
SIGNAL_LOW( PORT_SCL );
SIGNAL_HIGH( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SDA );
SIGNAL_LOW( PORT_SCL );
}
void i2c_stop( void )
{
SIGNAL_LOW( PORT_SCL );
SIGNAL_LOW( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_HIGH( PORT_SDA );
}
void i2c_ack(void)
{
SIGNAL_LOW( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
SIGNAL_HIGH( PORT_SDA );
}
void i2c_nack(void)
{
SIGNAL_HIGH( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
}
void i2c_ack_slave(void)
{
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
}
void i2c_write(uint8_t byte)
{
uint8_t bit;
for ( bit = 0; bit < 0x08; bit++ )
{
if( ( byte << bit ) & 0x80 )
SIGNAL_HIGH( PORT_SDA );
else
SIGNAL_LOW( PORT_SDA );
SIGNAL_HIGH( PORT_SCL );
SIGNAL_LOW( PORT_SCL );
}
// Clear both lines (needed?)
SIGNAL_LOW( PORT_SCL );
SIGNAL_LOW( PORT_SDA );
i2c_ack();
}
char i2c_read(void)
{
uint8_t B = 0;
DDRB &= 0b11111110; // switch PB0 to input
for ( int bit = 0; bit < 0x08; bit++ )
{
delay(); // <-- the root of all evil
SIGNAL_HIGH( PORT_SCL );
B <<= 1;
if( PINB & (1 << PB0 ) )
{
B |= 1;
}
else
{
B |= 0;
}
SIGNAL_LOW( PORT_SCL );
}
DDRB |= 0b00000001; // switch PB0 as output
i2c_nack();
return B;
}
void delay()
{
LED_ON();
LED_OFF();
}
void LED_ON( void )
{
PORTB |= 0b00000010;
}
void LED_OFF( void )
{
PORTB &= 0b11111101;
}