2011-07-06 2 views
8

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

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

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

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

Любой вариант теоретически будет работать, его реализация мне интересна.

+0

Опишите, какие «перерывы» требуется модулю для работы ... –

+0

@Ben Jackson было бы здорово, если бы я знал. Это ошибка, а не функция, поэтому она не документирована. В основном он сбрасывается примерно через 3 секунды с постоянной пропускной способностью, даже если он контролируется потоком. В среднем я могу обойтись со скоростью 25 кбит/с. – CodeFusionMobile

ответ

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 
+0

Мне нравится этот подход. Все еще отправляет куски данных, но позволяет управлять потоком без задержки каждого байта. – CodeFusionMobile

2

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

Представьте, что у вас есть этот глобальный 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; 
    } 

} 

Мне жаль не смотреть много в код, так что я может быть более конкретным. Это всего лишь идея, я не знаю, подходит ли она вам.

+0

Я не хочу устанавливать задержку между каждым байтом, поскольку для этого потребуется частый перезапуск цикла tx. Есть ли способ, которым я мог бы адаптировать это, чтобы определить, когда х число (слишком много) байтов было передано в последние y usecs и запускает задержку таймера? Это немного улучшило бы мою структуру кода. – CodeFusionMobile

+0

Можете ли вы получить UART, чтобы установить txdone даже в отсутствие реального персонажа? Если вы можете, то он должен посылать стоповые биты, если он не передает реальные данные. Затем вы можете просто отпустить 2 или 3 txints каждые 1024 (скажем) символов и забрать снова после того, как те два или три символа будут проходить. Если бы вы могли это сделать, я бы попытался это сделать первым. –

+0

@Pete Wilson Я не могу установить txdone без инициирования цикла передачи, который вытолкнул бы пустые данные. Я должен был бы сделать задержку таймера или что-то, чтобы вызвать прерывание. – CodeFusionMobile

1

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

  • CTS/RTS handshaking ('аппаратное управление потоком')
  • XON/XOFF ('программное управление потоком')

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

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

+0

Аппаратное управление потоком включено и управляется модулем Bluetooth. Я попытался установить более низкую скорость передачи в бодах, но это вызвало слишком много других проблем (хотя это действительно устранило эту конкретную проблему). +1 за хороший ответ с данными. – CodeFusionMobile

+0

В качестве побочного примечания другие проблемы возникли из-за того, что у меня есть две скорости передачи данных, которые настроены во время работы, и для переключения скоростей передачи данных мне нужно настроить модуль BT, а затем сбросить его, всего 2-секундный процесс, плюс снова подключиться. Таким образом, снижение скорости передачи данных просто не работает с конкретной точки приложения. – CodeFusionMobile

1

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

  • Конфигурирование свободного хода таймера при соответствующей скорости периодического.
  • В ISR таймера, переключения немного в глобальной переменной состояния (delayBit)
  • В UART ISR, если delayBit ​​высока и delayPostedBit является низким, а затем выйти из TX ISR без очистки флага прерывания TX и установить бит в глобальной переменной состояния (delayPostedBit). Если delayBit ​​низкий, то clear delayPostedBit. В результате вы получите задержку, равную одной задержке в расписании ISR, так как ISR будет введен снова. Это не ожидание задержки ожидания, поэтому это не повлияет на время остальной части системы.
  • Отрегулируйте период таймера, чтобы добавить задержку с соответствующими интервалами.
+0

Выход из TX ISR без очистки флага прерывания приводит к тому, что ISR блокирует CPU с постоянными отзывами, тем самым блокируя основной цикл, что неприемлемо для моего приложения , – CodeFusionMobile

+0

Он должен выйти из ISR на 2-й итерации через ISR. Это намерение (добавить задержку, равную 1 ISR-латентности), что дало бы наилучший выбор времени. Я предположил, что желаемая задержка находится в микросекундах, а не в миллисекундах. –

Смежные вопросы