我的项目有一个通过UART连接到Bluegiga蓝牙模块的MSP430。MCU必须能够从BG模块接收可变长度的消息。在当前架构中,每个接收到的字节都会产生一个通用异步收发器中断,以允许进行消息处理,并且功率限制会对MSP430的时钟速度施加限制。这使得MSP430很难跟上高于9600bps的任何通用异步收发器的速度。结果是通信接口很慢。加快数据速率会导致溢出错误、字节丢失和通信中断。
在这种情况下,如何在不牺牲数据完整性的情况下提高通信速度?
发布于 2020-02-02 22:38:53
通过在MSP430上使用3个可用DMA通道中的2个来填充可由CPU处理的环形缓冲区,我能够实现12倍的速度提升。这有点棘手,因为只有当大小寄存器达到零时才会产生MSP430 DMA中断,所以我不能直接填充环形缓冲区,因为消息大小是可变的。
使用一个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.
}
}https://stackoverflow.com/questions/60022971
复制相似问题