如何限制中断驱动的UART传输PIC24H?

时间:2011-07-06 21:02:41

标签: c embedded microcontroller interrupt

我通过460Kbaud UART将PIC24H单片机的数据传输到蓝牙无线电模块。在大多数情况下,此流程工作正常,蓝牙模块使用CTS和RTS线路来管理其内部数据缓冲区已满时的流量控制。但是,蓝牙模块中存在某种类型的错误,当数据连续发送到它时没有任何中断时会重置它,如果我的数据在另一个瓶颈中备份就会发生这种情况。

如果模块正常工作会很好,但这是我无法控制的。因此,似乎我唯一的选择是在我的端点上进行一些数据限制,以确保我不会超过数据吞吐量限制(我通过实验大致知道)。

我的问题是如何实施数据速率限制?

我当前的UART实现是一个RAM循环FIFO缓冲区,长度为1024字节,主循环将数据写入。当最后一个字节由UART硬件发出并且我的ISR从缓冲区读取下一个字节并将其发送到UART硬件时,PIC会触发外设中断。

以下是源代码的概念:

uart_isr.c

//*************** Interrupt Service routine for UART2 Transmission  
void __attribute__ ((interrupt,no_auto_psv)) _U2TXInterrupt(void)
{       
//the UART2 Tx Buffer is empty (!UART_TX_BUF_FULL()), fill it
//Only if data exists in data buffer (!isTxBufEmpty())
while(!isTxBufEmpty()&& !UART_TX_BUF_FULL())    {
    if(BT_CONNECTED)
    {   //Transmit next byte of data
        U2TXREG = 0xFF & (unsigned int)txbuf[txReadPtr];
        txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;
    }else{
        break;
    }
}
IFS1bits.U2TXIF = 0;
}

uart_methods.c

//return false if buffer overrun
BOOL writeStrUART(WORD length, BYTE* writePtr)
{
    BOOL overrun = TRUE;
    while(length)
    {
        txbuf[txWritePtr] = *(writePtr);
        //increment writePtr
        txWritePtr = (txWritePtr + 1) % TX_BUFFER_SIZE;
        if(txWritePtr == txReadPtr)
        {
            //write pointer has caught up to read, increment read ptr
            txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;
            //Set overrun flag to FALSE
            overrun = FALSE;
        }

        writePtr++;
        length--;
    }

    //Make sure that Data is being transmitted
    ensureTxCycleStarted();

    return overrun;
}


void ensureTxCycleStarted()
{
    WORD oldPtr = 0;
    if(IS_UART_TX_IDLE() && !isTxBufEmpty())
    {
        //write one byte to start UART transmit cycle
        oldPtr = txReadPtr;
        txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;//Preincrement pointer
        //Note: if pointer is incremented after U2TXREG write,
        //      the interrupt will trigger before the increment
        //      and the first piece of data will be retransmitted.
        U2TXREG = 0xFF & (unsigned int)txbuf[oldPtr];
    }
}

修改
我认为有两种方法可以实现限制:

  1. 在要写入的UART字节之间执行时间延迟,这会对数据吞吐量设置上限。

  2. 保持在特定时间范围内传输的字节运行记录,如果超过该时间跨度的最大字节数,则在继续传输之前会产生稍长的延迟。

  3. 这两个选项在理论上都可行,它是我想知道的实现。

4 个答案:

答案 0 :(得分:2)

如果您有空闲计时器,或者您可以使用现有计时器,则可以对发送的字节进行某种“去抖动”。

想象一下,你有这个全局变量byte_interval并且你每微秒都有一个定时器溢出(并触发ISR)。然后它看起来像这样:

timer_usec_isr() {
    // other stuff
    if (byte_interval)
        byte_interval--;
}

然后在“putchar”函数中,你可以得到类似的东西:

uart_send_byte(unsigned char b) {
    if (!byte_interval) { // this could be a while too, 
                          // depends on how you want to structure the code
        //code to send the byte
        byte_interval = AMOUNT_OF_USECS;
    }

}

我很遗憾没有仔细研究你的代码,所以我可以更具体一点。 这只是一个想法,我不知道它是否适合你。

答案 1 :(得分:2)

也许配额方法就是你想要的。 使用相关时间刻度的周期性中断,将“要传输的字节”的配额添加到全局变量中,使其不会超过为相关的洪水调整的某个级别。 然后在发送字节之前检查是否有配额。在新的传输中会有一个初始的洪水,但后来配额将限制传输速率。

~~some periodic interrupt
if(bytes_to_send < MAX_LEVEL){
  bytes_to_send = bytes_to_send + BYTES_PER_PERIOD;
  }
~~in uart_send_byte
if(bytes_to_send){
  bytes_to_send = bytes_to_send - 1;
  //then send the byte

答案 2 :(得分:1)

首先,常用的串行流量控制有两种类型。

你说CTS已开启,但你可能想知道是否可以某种方式启用XON / XOFF。

如果您可以配置它,另一种方法就是使用较低的波特率。这显然取决于你可以在链接的另一端配置什么,但它通常是当设备无法应对更高速度传输时解决问题的最简单方法。

答案 3 :(得分:1)

定时器方法,在特定时间为Tx增加延迟:

  • 以适当的定期速率配置自由运行的计时器。
  • 在计时器ISR中,在全局状态变量(delayBit)中切换一位
  • 在UART ISR中,如果delayBit为高电平且delayPostedBit为低电平,则退出TX ISR而不清除TX中断标志,并在全局状态变量(delayPostedBit)中设置一个位。如果delayBit为低,则清除delayPostedBit。结果是导致延迟等于一个ISR调度延迟,因为将再次输入ISR。这不是忙等待延迟,因此不会影响系统其余部分的时间。
  • 调整计时器的周期以适当的间隔添加延迟。