2016-08-26 8 views
2

私は基本的なハンドシェイクをしようとしています。以下は、C8051F120のSMBus(システム管理バス)のISRです。私はそれにI2Cデバイスを実装しようとしています(興味がある人はads1115 7addr 0x48)。これは、主にF120のシリコンラボによって与えられた例であることに注意してください。SMBus(I2C)が意図した余分なACKを送る

void SMBUS_ISR (void) interrupt 7 
{ 
    bit FAIL = 0;      // Used by the ISR to flag failed 
             // transfers 

    static unsigned char sent_byte_counter; 
    static unsigned char rec_byte_counter; 

    // Status code for the SMBus (SMB0STA register) 

    switch (SMB0STA) 
    { 
     // Master Transmitter/Receiver: START condition transmitted. 
     // Load SMB0DAT with slave device address. 
     case SMB_START: //0x08 

     // Master Transmitter/Receiver: repeated START condition transmitted. 
     // Load SMB0DAT with slave device address 
     case SMB_RP_START: //0x10 
     SMB0DAT = TARGET;    // Load address of the slave. 
     SMB0DAT &= 0xFE;    // Clear the LSB of the address for the 
             // R/W bit 
     SMB0DAT |= SMB_RW;   // Load R/W bit 
     STA = 0;      // Manually clear STA bit 

     rec_byte_counter = 1;   // Reset the counter 
     sent_byte_counter = 1;  // Reset the counter 

     break; 

     // Master Transmitter: Slave address + WRITE transmitted. ACK received. 
     // For a READ: N/A 
     // 
     // For a WRITE: Send the first data byte to the slave. 
     case SMB_MTADDACK: //0x18 

     SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1]; 
     sent_byte_counter++; 

     break; 

     // Master Transmitter: Slave address + WRITE transmitted. NACK received. 
     // Restart the transfer. 
     case SMB_MTADDNACK: //0x20 
     STA = 1;      // Restart transfer 
     break; 

     // Master Transmitter: Data byte transmitted. ACK received. 
     // For a READ: N/A 
     // 
     // For a WRITE: Send all data. After the last data byte, send the stop 
     // bit. 
     case SMB_MTDBACK: //0x28 

     if (sent_byte_counter <= NUM_BYTES_WR) 
     { 
      // send data byte 
      SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1]; 
      sent_byte_counter++; 
     } 
     else 
     { 
      STO = 1;     // Set STO to terminate transfer 
      SMB_BUSY = 0;    // And free SMBus interface 
     } 

     break; 

     // Master Transmitter: Data byte transmitted. NACK received. 
     // Restart the transfer. 
     case SMB_MTDBNACK: //0x30 
     STA = 1;      // Restart transfer 

     break; 

     // Master Receiver: Slave address + READ transmitted. ACK received. 
     // For a READ: check if this is a one-byte transfer. if so, set the 
     // NACK after the data byte is received to end the transfer. if not, 
     // set the ACK and receive the other data bytes. 
     // 
     // For a WRITE: N/A 
     case SMB_MRADDACK: //0x40 

     if (rec_byte_counter == NUM_BYTES_RD) 
     { 
      AA = 0;     // Only one byte in this transfer, 
             // send NACK after byte is received 
     } 
     else 
     { 
      AA = 1;     // More than one byte in this transfer, 
             // send ACK after byte is received 
     } 

     break; 

     // Master Receiver: Slave address + READ transmitted. NACK received. 
     // Restart the transfer. 
     case SMB_MRADDNACK: //0x48 
     STA = 1;      // Restart transfer 

     break; 

     // Master Receiver: Data byte received. ACK transmitted. 
     // For a READ: receive each byte from the slave. if this is the last 
     // byte, send a NACK and set the STOP bit. 
     // 
     // For a WRITE: N/A 
     case SMB_MRDBACK: //0x50 

     if (rec_byte_counter < NUM_BYTES_RD) 
     { 
      SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte 
      AA = 1;     // Send ACK to indicate byte received 
      rec_byte_counter++;  // Increment the byte counter 
     } 
     else 
     { 
      AA = 0;     // Send NACK to indicate last byte 
             // of this transfer 
     } 

     break; 

     // Master Receiver: Data byte received. NACK transmitted. 
     // For a READ: Read operation has completed. Read data register and 
     // send STOP. 
     // 
     // For a WRITE: N/A 
     case SMB_MRDBNACK: //0x58 

     SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte 
     STO = 1; 
     SMB_BUSY = 0; 
     AA = 1;      // Set AA for next transfer 

     break; 

     // Master Transmitter: Arbitration lost. 
     case SMB_MTARBLOST: //0x38 

     FAIL = 1;      // Indicate failed transfer 
             // and handle at end of ISR 

     break; 

     // All other status codes invalid. Reset communication. 
     default: 
     FAIL = 1; 

     break; 
    } 

    if (FAIL)       // If the transfer failed, 
    { 
     SMB0CN &= ~0x40;     // Reset communication 
     SMB0CN |= 0x40; 
     STA = 0; 
     STO = 0; 
     AA = 0; 

     SMB_BUSY = 0;     // Free SMBus 

     FAIL = 0; 
    } 

    SI = 0;        // Clear interrupt flag 
} 

//----------------------------------------------------------------------------- 
// Support Functions 
//----------------------------------------------------------------------------- 

//----------------------------------------------------------------------------- 
// SMB_Write 
//----------------------------------------------------------------------------- 
// 
// Return Value : None 
// Parameters : None 
// 
// Writes a single byte to the slave with address specified by the <TARGET> 
// variable. 
// Calling sequence: 
// 1) Write target slave address to the <TARGET> variable 
// 2) Write outgoing data to the <SMB_DATA_OUT> array 
// 3) Call SMB_Write() 
// 
void SMB_Write (void) 
{ 
    char SFRPAGE_SAVE = SFRPAGE;  // Save Current SFR page 

    SFRPAGE = SMB0_PAGE; 

    while (SMB_BUSY);     // Wait for SMBus to be free. 
    SMB_BUSY = 1;      // Claim SMBus (set to busy) 
    SMB_RW = 0;       // Mark this transfer as a WRITE 
    STA = 1;       // Start transfer 

    SFRPAGE = SFRPAGE_SAVE;    // Restore SFR page detector 
} 

//----------------------------------------------------------------------------- 
// SMB_Read 
//----------------------------------------------------------------------------- 
// 
// Return Value : None 
// Parameters : None 
// 
// Reads a single byte from the slave with address specified by the <TARGET> 
// variable. 
// Calling sequence: 
// 1) Write target slave address to the <TARGET> variable 
// 2) Call SMB_Write() 
// 3) Read input data from <SMB_DATA_IN> array 
// 
void SMB_Read (void) 
{ 
    char SFRPAGE_SAVE = SFRPAGE;  // Save Current SFR page 

    SFRPAGE = SMB0_PAGE; 

    while (SMB_BUSY);     // Wait for bus to be free. 
    SMB_BUSY = 1;      // Claim SMBus (set to busy) 
    SMB_RW = 1;       // Mark this transfer as a READ 

    STA = 1;       // Start transfer 

    while (SMB_BUSY);     // Wait for transfer to complete 

    SFRPAGE = SFRPAGE_SAVE;    // Restore SFR page detector 
} 

メインは連続して次のことを行います。3バイトを送信します。最初のバイトはデバイスレジスタポインタです。次に、同じレジスタを読み込みます(ポインタはすでに設定されているため)。それはこれをします。

while (1) 
    { 
     TARGET = SLAVE_ADDR;    // Target the Slave for next SMBus 
             // transfer 
     SMB_DATA_OUT[0] = 0x01;   // Device register 
     SMB_DATA_OUT[1] = 0x0A;   // Register MSByte 
     SMB_DATA_OUT[2] = 0x03;   // Register LSbyte 
     SMB_Write();      // Initiate SMBus write 

     // SMBus Read Sequence 
     TARGET = SLAVE_ADDR;    // Target the Slave for next SMBus 
             // transfer 
     SMB_Read(); 
    } 

そしてここtrace capture of transferです:

マスターが余分なACKを送信して受け取るように私には見えます。だから私の主な焦点は、ケースにされています:

SMB_MRADDACK:// 0x40の

SMB_MRADDNACK:// 0x48

SMB_MRDBACK:// 0x50を

私の主な焦点は、より多くのようにSMB_MRADDNACKです:// 0x48とそれがISR呼び出しの間のif文を通過する回数。私はちょっとしたトラブルを抱えて、正確な失敗点の周りに頭を包んでいます。では、この余分なACKはどこから来ますか?月曜日の午後、ここまでに私がそれを理解しなければ、私はここを振り返ります。

ボーナスに関する質問:何らかの種類の埋め込みスタック交換がありますか?コミュニティで私の目立つものは見当たりませんでした。

+0

ボーナス回答:Cygnal/SiLabsフォーラム(http://community.silabs.com/)に行ったことがありますか?それはSOフォーマットではありませんが、これは非常に有能な人たちによってアクティブになっています。 –

答えて

0
case SMB_MRDBACK: //0x50 

    SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte 
    rec_byte_counter++;  // Increment the byte counter 

    if (rec_byte_counter < NUM_BYTES_RD) 
    { 
     AA = 1;     // Send ACK to indicate byte received 
    } 
    else 
    { 
     AA = 0;     // Send NACK to indicate last byte 
            // of this transfer 
    } 

    break; 
1

あなたのトレースは、送信された3バイトと3バイトの読み取りを示しています。私はあなたが3バイトを書くことを望んでいると仮定していて、と2つのバイトだけを読みます。そうであれば、あなたのマスタは3番目のバイトもクロッキングし続けるので、この問題は偽のACK以上のものです。あなたはSiLabs 1からのサンプルコードと2バイトのみを読むことを望む場合

は、あなたがACKまたは停止するかどうかを決定する代わりにSMB_MRADDACKSMB_MRDBACK状態で使用している値3.付属の2にNUM_BYTES_RDを定義する必要があります。

余分なACKではないかと心配しているため、(0xffの後の)トレースのSDLラインの最終的なドロップに関する質問がある場合は、余分なバイトの代わりにACKについて質問します。心配しないでください。これはSTO(高いSCL中に上昇)であり、送信を終了するマスタにとって正しい動作です。

編集:klambは以下のコメントで正しいですが、SMB_MRDBACK状態にバグがあります。 SMB0DATを保存し、rec_byte_counterをに対してチェックする前に、rec_byte_counterをインクリメントする必要があります。そのようなSiLabsから抜け出した騒動。

+0

私のNUM_BYTES_RDは2と定義されています。私は、i2cを理解してから、別のバイトが来るとACKを出し、バイトを受信したら(そしてクロッキングを止める)NACKします。だから、これは私が意図したよりも余分な時間を肯定して(そして、より多くのクロックパルスを送ることでした)。これは修正プログラムでした: SMB_DATA_IN [rec_byte_counter-1] = SMB0DAT; rec_byte_counter ++; if(rec_byte_counter klamb

関連する問題