私はATTINY85をビットバン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;
}
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
}
私は、私はおそらく、他のデバイスによって期待される適切な信号長を作成し、完璧な遅延「を釘付け」ことを疑わ私はループとオシロスコープのために使用した同じ遅延を作ってみました:
void delay()
{
for(int i=0; i<20; i++){ }
}
ませ運、I2Cの読み取りが動作を停止しません。..
その後、私は別のPINにLEDを切り替えて、完全に一人で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を回す時間よりも短くなるか、または回路自体と何かがあり、 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を立ち下がりに近づけて試しましたか?つまり、 'SIGNAL_HIGH(PORT_SCL); _delay_us(1); B =(B << 1)| !!(PINB&(1 << PB0)); SIGNAL_LOW(PORT_SCL); _delay_us(1); '(8回繰り返し)? –