Как регулировать передачу UART с прерывистым приводом PIC24H? - PullRequest
8 голосов
/ 07 июля 2011

Я передаю данные с моего микроконтроллера PIC24H по UART со скоростью 460 кбод на радиомодуль bluetooth. В большинстве случаев этот поток работает просто отлично, и модуль Bluetooth использует линии CTS и RTS для управления контролем потока, когда его внутренние буферы данных заполнены. Однако в модуле bluetooth есть какая-то ошибка, которая сбрасывает его, когда данные непрерывно отправляются на него без каких-либо перерывов, что происходит, если мои данные копируются в другое узкое место.

Было бы неплохо, если бы модуль работал правильно, но это вне моего контроля. Так что, похоже, мой единственный вариант - сделать некоторые данные на своем конце, чтобы убедиться, что я не превышаю предельные значения пропускной способности данных (которые я знаю примерно экспериментально).

Мой вопрос, как реализовать регулирование скорости передачи данных?

Моя текущая реализация UART - это кольцевой буфер FIFO в ОЗУ длиной 1024 байта, в который основной цикл записывает данные. Периферийное прерывание запускается PIC, когда последний байт был отправлен оборудованием UART, и мой ISR считывает следующий байт из буфера и отправляет его на оборудование UART.

Вот идея исходного кода:

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. Сохранение текущего количества байтов, переданных за определенный период времени, и, если максимальное число байтов превышено для этого промежутка времени, создайте немного большую задержку перед продолжением передачи.

Теоретически, любой из этих вариантов работает, его реализация мне интересна.

Ответы [ 4 ]

2 голосов
/ 12 июля 2011

Возможно, вам нужен квотный подход.Используя периодическое прерывание соответствующей временной шкалы, добавьте квоту «байтов для передачи» в глобальную переменную до такой степени, что вы не пройдете уровень, скорректированный для соответствующего потока.Затем просто проверьте, есть ли квота, прежде чем вы пришли, чтобы отправить байт.При новой передаче будет начальный поток, но позже квота ограничит скорость передачи.

~~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 голосов
/ 07 июля 2011

Если у вас есть бесплатный таймер или вы можете использовать уже существующий, вы можете выполнить своего рода «дебат» отправленных байтов.

Представьте, что у вас есть этот глобальный var 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 голос
/ 14 июля 2011

Таймер, который добавляет задержку к Tx в определенное время:

  • Настройка свободного таймера с соответствующей периодической скоростью.
  • В таймере ISR, переключить бит в глобальной переменной состояния (delayBit)
  • В UART ISR, если delayBit ​​имеет высокое значение, а delayPostedBit имеет низкое значение, выйдите из TX ISR без очистки флага прерывания TX и установите бит в глобальной переменной состояния (delayPostedBit). Если delayBit ​​низок, тогда сбросьте delayPostedBit. Результатом является задержка, равная одной задержке расписания ISR, поскольку ISR будет введен снова. Это не задержка ожидания, поэтому она не повлияет на время работы остальной системы.
  • Настройте период таймера для добавления задержки через соответствующие интервалы.
1 голос
/ 11 июля 2011

Во-первых, обычно используются два типа управления последовательным потоком.

Вы говорите, что CTS включен, но вы можете посмотреть, можно ли каким-то образом включить XON / XOFF.

Другой подход, если вы можете настроить его, это просто использовать более низкую скорость передачи данных.Очевидно, это зависит от того, что вы можете настроить на другом конце канала, но обычно это самый простой способ устранения проблем, когда устройства не могут справиться с более высокой скоростью передачи.

...