C:UART,ISR,循环FIFO缓冲区:有时以错误的顺序发送字节

时间:2014-03-16 16:17:19

标签: c fifo uart isr

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

问题有时要传输的字节是在错误的顺序中完成的。

我非常感谢

  1. 有关调试的建议,或
  2. 协助发现代码中的问题
  3. 到目前为止的进展:

    • 似乎只有在收到一些字节时才会发生 - 但是我没有成功将其缩小范围。我没有得到任何未完成或超支,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++]);
        }
    }
    

    硬件变量说明:

    作为参考,以下是映射到硬件寄存器和标志的(volatile)变量:

    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
    

1 个答案:

答案 0 :(得分:2)

嗯。我认为你的程序中缺少一些逻辑(我只会覆盖发送部分,因为接收器部分似乎正在工作?):

如果发送hw FIFO中有空间,则会触发中断例程。然后从sw缓冲区发出一个字节,调整索引并返回(注意在此之后可能仍有一些字节在sw缓冲区内排队)。

每当你发送一个字节时,你在HW fifo中查找空格并直接将字节放在那里,如果没有,你将它排在SW缓冲区中。

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

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