gpt4 book ai didi

c - I2C读取时出现奇怪的延迟问题

转载 作者:行者123 更新时间:2023-11-30 14:56:18 25 4
gpt4 key购买 nike

我一直在尝试让我的 ATTINY85 进行 bit-bang 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; 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;
}

如果我删除delay(),I2C将不再工作,并且我无法从设备读取数据(设备不响应​​)。似乎合乎逻辑,但我想删除delay()的原因是因为它实际上并不是“真正的”延迟,它只是打开和关闭位于不同引脚(PB1)上的LED,I2C线路位于PB0和PB2上.

_delay_ms 太慢了,所以我只是打开和关闭 PB1 引脚以产生微小的延迟,这是它工作的唯一方法。这是我的延迟函数的内容,如果我像这样保留它,一切都会很好:

void delay()
{
LED_ON();
LED_OFF();
}

void LED_ON( void )
{
PORTB |= 0b00000010; // PB1
}

void LED_OFF( void )
{
PORTB &= 0b11111101; // PB1
}

我怀疑我可能“钉住”了一个完美的延迟,从而创建了其他设备所需的适当信号长度,因此我尝试使用 for 循环和示波器来实现相同的延迟:

void delay()
{
for( int i=0; i<20; i++){ }
}

不走运,I2C 读取停止工作..

然后我决定将 LED 切换到另一个 PIN,并完全保留 PB1,看看它是否与延迟相关或与引脚/电路相关:

void delay()
{
LED_ON();
LED_OFF();
}

void LED_ON( void )
{
PORTB |= 0b00001000; // PB3
}


void LED_OFF( void )
{
PORTB &= 0b11110111; // PB3
}

奇怪的是,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;
}

最佳答案

I2c 定义了许多信号的最小时序 - 这里重要的是 SCL 的高电平和低电平时间 - 在允许下一次转换到相反状态之前 SCL 应保持稳定的时间量。这些时序通常约为 5μs,准确的数字应从数据表中获取。

read 循环末尾的循环需要 2 到 3 条指令,具体取决于编译器的操作。一条 AVR 指令,根据您的时钟速率,大约需要大约 200ns,因此(没有延迟)SCL 处于低电平大约 600ns,给予或采取 - 这太短了,至少对于您的特定“其他终端设备来说显然如此” ”。

当您在被调用函数中插入函数调用和端口访问时,您插入了足够的指令以使 SCL 保持低电平足够长的时间以正常工作。

在您的代码中,高电平时间并不是什么大问题,因为您让 AVR 在 SCL 为高电平时执行更多指令 - 显然,有足够的时间让 SCL 保持高电平足够长的时间。

您在延迟函数中切换端口引脚的事实与此无关 - 唯一相关的是您需要在 SCL 为低电平时花费一些时间。显然,您当前所做的只是花费一些时间等待,这是浪费端口引脚 - 使用延迟而不是延迟进行实验。但请检查“另一端”数据表以了解所需的确切时间,4-5μs 应该没问题。

为什么你的延迟循环不起作用?它很可能被编译器优化掉,编译器认识到您没有在该空循环中执行相关操作。

理想情况下,您应该尝试在 SCL 的高电平阶段中间读取 SDA - 带有 8 位的展开循环和一些 delay_us 传播,这样应该可以完美工作。

关于c - I2C读取时出现奇怪的延迟问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44806066/

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