2017-06-28 22 views
0

私は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; 
} 
+0

。おそらくあなたのデバイスはまだ準備ができていませんか?代わりにSDAを立ち下がりに近づけて試しましたか?つまり、 'SIGNAL_HIGH(PORT_SCL); _delay_us(1); B =(B << 1)| !!(PINB&(1 << PB0)); SIGNAL_LOW(PORT_SCL); _delay_us(1); '(8回繰り返し)? –

答えて

1

I2Cは、信号の最小タイミングの数規定する - ここで重要なのSCLのHIGHとLOW時間 - SCLが隣の遷移の前に安定でなければならない時間の量反対の状態が許される。 これらのタイミングは標準で約5μsです。正確な数値はデータシートから取得する必要があります。

readループの最後のループアラウンドには、コンパイラの機能に応じて2〜3命令が多少かかります。あなたのクロックレートに応じて、AVR命令はおよそ200nsかかりますので、(遅れなく)SCLは約600nsの間、低いですか、あまりにも短いですが、特定の「他端デバイス"

呼び出された関数に関数呼び出しとポートアクセスを挿入したときに、十分に長い間SCLを長く保つための十分な指示を挿入しました。

あなたのコードでは、SCLが高い間にAVRにより多くの命令を実行させるので、HIGH時間はあまり問題になりません.-十分に長い時間SCLをHIGHに保つのに十分な時間がかかります。

あなたが遅延機能でポートピンをトグルしているという事実は、ここでは関係ありません - 唯一の関連性は、あなたがSCLがLowの間、いくつかの時間を費やす必要があるということです。使用delay_us、代わりにその遅れで実験 - もちろん、あなたが現在やっていることは、単にいくつかの時間待ちを過ごすためにポート端子の無駄です。しかし、必要な正確なタイミングのために「もう一方の端の」データシートを確認し、4-5μsは問題ないはずです。

あなたの遅延ループが機能しなかったのはなぜ?それはおそらくあなたがその空のループに関連する何かをしていなかった認識コンパイラによって離れて最適化しました。

理想的には、SCLのHIGHフェーズのほぼ中間でSDAを読みとる必要があります.8ビットのアンロールループと完全に機能するdelay_usがあります。あなたは、本質的にSCLラインの立ち上がりでSDAラインを読んでいる

関連する問題