我一直試圖讓我的ATTINY85處於bit-bang I2C(讀/寫)狀態。我有以下配置:讀取I2C時奇怪的延遲問題
PB0 = SDA
PB1 = LED
PB2 = SCL
我能夠毫無問題地寫,但只有在閱讀作品,如果我有我的「延遲()」讀循環中的功能,到目前爲止好:
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;
}
如果我刪除延遲(),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看看它是延遲相關還是pin /電路相關:
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;
}
您實質上是在SCL線的上升沿讀取SDA線;也許你的設備還沒有準備好呢?您是否嘗試過讀取SDA以接近下降沿呢?也就是說,'SIGNAL_HIGH(PORT_SCL); _delay_us(1); B =(B << 1)| !!(PINB&(1 << PB0)); SIGNAL_LOW(PORT_SCL); _delay_us(1);'(重複八次)? –