2

我正在用一个间歇性的虫子把我的头发拉出来。我正在异步接收和发送字节(在 PIC16F77 上),并实现了一个用于接收和发送的循环软件 FIFO 缓冲区,并结合了一个在可以发送或已接收字节时触发的中断服务程序。

问题是有时要传输的字节顺序错误

我将不胜感激

  1. 关于调试它的建议,或者
  2. 帮助发现代码中的问题

目前进展:

  • 似乎只有在收到一些字节时才会发生这种情况 - 但是我没有成功地进一步缩小范围。AFAIK,我没有得到任何欠载或超支。
  • 当我更改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 缓冲区发送一个字节,调整索引并返回(请注意,之后可能仍有一些字节在 sw 缓冲区中排队)。

每当您发送一个字节时,您都会在 HW fifo 中查找空间并将该字节直接放在那里,如果没有,则将其排队到 SW 缓冲区中。

在我看来,问题是您希望中断例程在返回到 send_char() 之前耗尽软件缓冲区,这不一定是这种情况。从中断返回后,将完全执行下一条指令(一条指令中间没有中断)。如果下一条指令是 send_char() 中的 di(),则不会发生此中断,并且 sw 缓冲区中仍有字节只能稍后发送(为时已晚)。

我宁愿将字节从 send_char() 排入 sw 缓冲区,而不是直接从 send_char() 写入 fifo,或者在直接访问 hw fifo 之前另外检查 sw 缓冲区是否为空。

于 2014-03-16T17:26:22.530 回答