Как получить высокоскоростные данные UART на низкоскоростном MSP430? - PullRequest
1 голос
/ 02 февраля 2020

В моем проекте MSP430 подключен через UART к Bluetooth-модулю Bluegiga. MCU должен иметь возможность получать сообщения переменной длины от модуля BG. В текущей архитектуре каждый полученный байт генерирует прерывание UART, чтобы разрешить обработку сообщений, а ограничения мощности накладывают ограничение на тактовую частоту MSP430. Это мешает MSP430 поддерживать любую скорость UART, превышающую 9600 бит / с. Результатом является медленный интерфейс связи. Ускорение скорости передачи данных приводит к ошибкам переполнения, потерям байтов и нарушению связи.

Есть идеи о том, как увеличить скорость передачи данных, не жертвуя целостностью данных в этой ситуации?

1 Ответ

1 голос
/ 02 февраля 2020

Я смог добиться sh повышения скорости в 12 раз, используя 2 из 3 доступных каналов DMA на MSP430 для заполнения кольцевого буфера, который затем мог быть обработан ЦП. Это было немного сложно, потому что прерывания DMA MSP430 генерируются только тогда, когда регистр размера достигает нуля, поэтому я не мог просто заполнить кольцевой буфер напрямую, потому что размер сообщения является переменным.

Использование одного канала DMA в качестве однобайтовый буфер, который запускается на каждом байте, полученном UART, который затем запускает второй канал DMA, который заполняет кольцевой буфер, делает свое дело.

Ниже приведен пример C кода, который иллюстрирует метод. Обратите внимание, что он включает ссылки из библиотек MSP430.

#include "dma.h"

#define BLUETOOTH_RXQUEUE_SIZE <size_of_ring_buffer>

static int headIndex = 0;
static int tailIndex = 0;

static char uartRecvByte;
static char bluetoothRXQueue[BLUETOOTH_RXQUEUE_SIZE];

/*!********************************************************************************
  * \brief Initialize DMA hardware
  *
  * \param none
  *
  * \return none
  *
  ******************************************************************************/
void init(void)
{
    // This is the primary buffer.
    //  It will be triggered by UART Rx and generate an interrupt.
    //  It's purpose is to service every byte recieved by the UART while
    //    also waking up the CPU to let it know something happened.
    //  It uses a single address of RAM to store the data, so it needs to be
    //    serviced before the next byte is received from the UART.
    //  This was done so that any byte received triggers processing of the data.
    DMA_initParam dmaSettings;
    dmaSettings.channelSelect = DMA_CHANNEL_2;
    dmaSettings.transferModeSelect = DMA_TRANSFER_REPEATED_SINGLE;
    dmaSettings.transferSize = 1;
    dmaSettings.transferUnitSelect = DMA_SIZE_SRCBYTE_DSTBYTE;
    dmaSettings.triggerSourceSelect = DMA_TRIGGERSOURCE_20; // USCA1RXIFG, or any UART recieve trigger
    dmaSettings.triggerTypeSelect = DMA_TRIGGER_RISINGEDGE;
    DMA_init(&dmaSettings);
    DMA_setSrcAddress(DMA_CHANNEL_2, (UINT32)&UCA1RXBUF, DMA_DIRECTION_UNCHANGED);
    DMA_setDstAddress(DMA_CHANNEL_2, (UINT32)&uartRecvByte, DMA_DIRECTION_UNCHANGED);

    // This is the secondary buffer.
    //  It will be triggered when DMA_CHANNEL_2 copies a byte and will store bytes into a ring buffer.
    //  It's purpose is to pull data from DMA_CHANNEL_2 as quickly as possible
    //    and add it to the ring buffer.
    dmaSettings.channelSelect = DMA_CHANNEL_0;
    dmaSettings.transferModeSelect = DMA_TRANSFER_REPEATED_SINGLE;
    dmaSettings.transferSize = BLUETOOTH_RXQUEUE_SIZE;
    dmaSettings.transferUnitSelect = DMA_SIZE_SRCBYTE_DSTBYTE;
    dmaSettings.triggerSourceSelect = DMA_TRIGGERSOURCE_30; // DMA2IFG
    dmaSettings.triggerTypeSelect = DMA_TRIGGER_RISINGEDGE;
    DMA_init(&dmaSettings);
    DMA_setSrcAddress(DMA_CHANNEL_0, (UINT32)&uartRecvByte, DMA_DIRECTION_UNCHANGED);
    DMA_setDstAddress(DMA_CHANNEL_0, (UINT32)&bluetoothRXQueue, DMA_DIRECTION_INCREMENT);

    DMA_enableTransfers(DMA_CHANNEL_2);
    DMA_enableTransfers(DMA_CHANNEL_0);
    DMA_enableInterrupt(DMA_CHANNEL_2);
}

/*!********************************************************************************
  * \brief DMA Interrupt for receipt of data from the Bluegiga module
  *
  * \param none
  *
  * \return none
  *
  * \par Further Detail
  * \n Dependencies:   N/A
  * \n Processing:     Clear the interrupt and update the circular buffer head
  * \n Error Handling: N/A
  * \n Tests:          N/A
  * \n Special Considerations: N/A
  *
  ******************************************************************************/
void DMA_Interrupt(void)
{
    DMA_clearInterrupt(DMA_CHANNEL_2);
    headIndex = BLUETOOTH_RXQUEUE_SIZE - DMA_getTransferSize(DMA_CHANNEL_0);

    if (headIndex == tailIndex)
    {
        // This indicates ring buffer overflow.
    }
    else
    {
        // Perform processing on the current state of the ring buffer here.
        // If only partial data has been received, just leave.  Either set a flag
        // or generate an event to process the message outside of the interrupt.
        // Once the message is processed, move the tailIndex.
    }
}
...