2

断続的なバグで髪を引っ張っています。(PIC16F77 で) バイトを非同期的に送受信しており、送受信用の循環ソフトウェア FIFO バッファを実装し、バイトを送信できるか受信したときにトリガーされる割り込みサービス ルーチンと組み合わせました。

問題は、送信されるバイトが間違った順序で送信されることがあるということです。

次のいずれかをいただければ幸いです。

  1. デバッグに関するアドバイス、または
  2. コードの問題を見つける支援

これまでの進捗状況:

  • いくつかのバイトが受信されている場合にのみ発生するようですが、さらに絞り込むことに失敗しました。私の知る限り、アンダーランやオーバーランは発生していません。
  • 次のいずれかに変更send_char()すると、問題なく動作します: 1. ハードウェア バッファーのスペースを待って、バイトを直接入れることによってソフトウェア バッファーをバイパスするか、2. ハードウェアにスペースがある場合でも、バイトをソフトウェア バッファーに入れます。バッファ。

コード: (ハードウェア変数の説明については、質問の下部を参照してください)

unsigned volatile char volatile rc_buff[16];
unsigned char volatile rc_begin = 0;
unsigned char volatile rc_next_free = 0;
unsigned char volatile rc_count = 0;

unsigned volatile char volatile tx_buff[16];
unsigned char volatile tx_begin = 0;
unsigned char volatile tx_next_free = 0;
unsigned char volatile tx_count = 0;

__interrupt isr(){
    // If a character has arrived in the hardware buffer
    if (RCIF){
        // Put it in the software buffer
        if (rc_count >= 16) die(ERROR_RC_OVERFLOW);
        rc_buff[rc_next_free] = RCREG;
        rc_next_free = (rc_next_free + 1) % 16;
        rc_count++;
    }
    // If there is space in hardware FIFO, and interrupt
    // has been enabled because stuff in software FIFO needs to be sent.
    if (TXIE && TXIF){
        // Put a byte from s/w fifo to h/w fifo.
        // (Here, tx_count is always > 0 (in theory))
        TXREG = tx_buff[tx_begin];
        tx_count--;
        tx_begin = (tx_begin + 1) % 16;
        // If this was the last byte in the s/w FIFO,
        // disable the interrupt: we don't care
        // when it has finished sending.
        if(tx_count==0) TXIE = 0;
    }
}
void send_char(char c){
    // disable interrupts to avoid bad things happening
    di();
    // if the hardware buffer is empty,
    if (TXIF){
        // put a byte directly into the hardware FIFO
        TXREG = c;
    } else {
        // cannot send byte directly so put in the software FIFO
        if (tx_count >= 16) die(ERROR_TX_OVERFLOW);
        tx_buff[tx_next_free] = c;
        tx_next_free = (tx_next_free + 1) % 16;
        tx_count++;
        // Enable TX interrupt since it now has something
        // it needs to transfer from the s/w FIFO to the h/w FIFO
        TXIE = 1;
    }
    ei();
}
char get_char(){
    // wait for a byte to appear in the s/w buffer
    while (!rc_count) {
        // If the h/w buffer overflowed, die with error
        if (OERR) die(ERROR_RC_HW_OVERFLOW)
    }
    // disable interrupts to avoid bad things happening
    di();
    unsigned char c = rc_buff[rc_begin];
    rc_count--;
    rc_begin = (rc_begin + 1) % 16;
    ei();
    return c;
}
void send_str(const unsigned char * str){
    unsigned char char_idx = 0;
    // until we reach the end-of-string null character,
    while (str[char_idx]){
        // queue a character for sending
        send_char(str[char_idx++]);
    }
}

ハードウェア変数の説明:

参考までに、ハードウェア レジスタとフラグにマップされる (揮発性) 変数を次に示します。

RCIF // Read-only receive flag:  True == byte(s) are waiting in hardware receive FIFO
TXIF // Read-only transmit flag: True == there is space in the hardware transmit FIFO
RCREG // Read only: Holds the next byte from the hardware FIFO that has been received
TXREG // Write-only: Assigning a byte to this transfers the byte to the hardware transmit FIFO
TXIE // Read/Write: Enable transmit interrupt: True == trigger ISR when TX h/w FIFO has space
RCIE // Read/Write: Enable receive interrupt:  True == trigger ISR when RC h/w FIFO has a byte to be read

また、以下は、複数のグループ化された操作をアトミックに保つために割り込みを一時停止/再開する特別なインライン関数です。(ISR は、他の割り込みを含め、何によっても中断できません)

di() // suspend interrupts
ei() // re-enable interrupts
4

1 に答える 1

2

うーん。あなたのプログラムにはいくつかのロジックが欠けていると思います (レシーバー部分が機能しているように見えるので、送信部分のみをカバーしますか?):

送信ハードウェア FIFO にスペースがある場合、割り込みルーチンがトリガーされます。次に、sw バッファーから 1 バイトを送信し、インデックスを調整して戻ります (その後、sw バッファー内にまだバイトがキューに入れられている可能性があることに注意してください)。

バイトを送信するときはいつでも、HW fifo でスペースを探し、そのバイトをそこに直接置きます。そうでない場合は、SW バッファーにキューに入れます。

問題は、必ずしもそうではない send_char() に戻る前に、割り込みルーチンがソフトウェア バッファを空にすることを期待しているように思えます。割り込みから戻った後、次の命令が完全に実行されます (命令の途中で割り込みはありません)。この次の命令が send_char() の di() である場合、この割り込みは発生せず、後でしか送信できない (遅すぎる) バイトが sw バッファーにまだ存在します。

send_char() から fifo に直接書き込むのではなく、send_char() から sw バッファーにバイトをエンキューするか、ハードウェア fifo に直接アクセスする前に sw バッファーが空であることを確認します。

于 2014-03-16T17:26:22.530 に答える