stc32单片机实现串口2M波特率满带宽传输
我需要实现已极高的速度用串口往上位机发送数据, 并且还不能占用mcu资源, 使用的单片机位stc32g8K64
我的方法是串口接收采用中断接收, 发送采用dma自动发送, 预先初始化16个64字节的缓冲区, 每次通过串口发送时, 先找到当前的空闲缓冲区, 然后往缓冲区里填充数据, 在dma传输完成一帧数据时释放缓冲区, 在主循环里检测是否有需要发送的数据, 然后调用dma自动发送
高速USB转串口采用AI8H2K12U实现的USB转串口, 以前采用ch340, ch343USB转串口, 在1M及以上波特率下通信会导致电脑偶尔蓝屏, 尤其是ch340, 1个小时左右电脑有很大的概率蓝屏, 我实锤就是USB转串口引起的, 用AI8H2K12U后就没出现过蓝屏的问题了
//========================================================================
// ---------------------- 串口发送接收部分 --------------------------------
//========================================================================
#define Uart1_ReceiveSize 128
#define Uart1_SendBufferSize 255
#define Uart1_SendDMABufferSize 64
#define Uart1_SendDMAGroupCount 16
#define Uart1_CommandBufferSize 255
uint8 Uart1_receiveBuffer[Uart1_ReceiveSize] = {0};
uint8 Uart1_sendGroupState[Uart1_SendDMAGroupCount] = {0}; // DMA空闲状态 0:空闲 1:等待发送 2:发送中
uint8 Uart1_sendGroupSize[Uart1_SendDMAGroupCount] = {0}; // DMA每组数据长度的数据长度, 最长255
uint8 Uart1_sendGroupOrder[Uart1_SendDMAGroupCount] = {0};
uint8 xdata Uart1_sendBuffer[Uart1_SendBufferSize] = {0}; // DMA按组发送顺序
uint8 xdata Uart1_sendDMABuffer[Uart1_SendDMAGroupCount][Uart1_SendDMABufferSize] = {{0}}; // DMA发送缓冲区
uint8 Uart1_commandBuffer[Uart1_CommandBufferSize] = {0};
uint8 UART1_isSend = 0; // 是否正在发送数据
uint8 UART1_isDMASend = 0; // 是否使用DMA发送
uint8 UART1_enable = 1; // 是否启用串口
int8 Uart1_sendCurrentGroupIndex = -1; // 当前发送的组
uint32 UART1_sendCount = 0; // 发送字节计数void UART1_sendData();// FIFO环形缓冲队列
typedef struct FIFOBuffer
{unsigned int headPos; // 缓冲区头部位置unsigned int tailPos; // 缓冲区尾部位置unsigned int bufferSize; // 缓冲区长度unsigned char *buffer; // 缓冲区数组
};
struct FIFOBuffer uartReceiveFIFO;
struct FIFOBuffer uartSendFIFO;
struct FIFOBuffer uartSendGroupFIFO;unsigned char FIFOBuffer_available(struct FIFOBuffer *fifo_buffer)
{return fifo_buffer->headPos != fifo_buffer->tailPos;
}void FIFOBuffer_flush(struct FIFOBuffer *fifo_buffer)
{fifo_buffer->headPos = 0;fifo_buffer->tailPos = 0;
}unsigned char FIFOBuffer_read(struct FIFOBuffer *fifo_buffer)
{unsigned char buf = 0;// 如果头尾接触表示缓冲区为空if (fifo_buffer->headPos != fifo_buffer->tailPos){buf = fifo_buffer->buffer[fifo_buffer->headPos];if (++fifo_buffer->headPos >= fifo_buffer->bufferSize){fifo_buffer->headPos = 0;}}return buf;
}#define FIFOBUFFER_PUSH(fifo, buf) \do \{ \(fifo).buffer[(fifo).tailPos] = (buf); \if (++(fifo).tailPos >= (fifo).bufferSize) \{ \(fifo).tailPos = 0; \} \if ((fifo).tailPos == (fifo).headPos) \{ \if (++(fifo).headPos >= (fifo).bufferSize) \{ \(fifo).headPos = 0; \} \} \} while (0)void FIFOBuffer_push(struct FIFOBuffer *fifo_buffer, unsigned char buf)
{fifo_buffer->buffer[fifo_buffer->tailPos] = buf; // 从尾部追加if (++fifo_buffer->tailPos >= fifo_buffer->bufferSize){ // 尾节点偏移fifo_buffer->tailPos = 0;}if (fifo_buffer->tailPos == fifo_buffer->headPos){if (++fifo_buffer->headPos >= fifo_buffer->bufferSize){fifo_buffer->headPos = 0;}}
}// 自定义通信协议, 检测命令格式 {Start1,Start2,data...,dataLength,End1,End2}
struct MyCommand
{unsigned char Start1;unsigned char Start2;unsigned char End1;unsigned char End2;unsigned char isStart;unsigned char count;unsigned char bufferSize;unsigned char *buffer;// 检测命令格式 {Start1,Start2,data...,dataLength,End1,End2}void (*resolveCommandCallback)(unsigned char *buffer, unsigned char length);
};
struct MyCommand uartCommand;unsigned char getCheckSum(unsigned char *buffer, unsigned int start, unsigned int end)
{unsigned int i = 0;unsigned char sum = 0;for (i = start; i < end; i++){sum += buffer[i];}return sum;
}
void MyCommand_addData(struct MyCommand *command, unsigned char tempData)
{if (!command->isStart){if (command->count == 0 && tempData != command->Start1){return;}command->count++;if (command->count == 2){if (command->Start2 == tempData){command->isStart = true;command->count = 0;}else{command->count = 0;if (tempData == command->Start1){command->count++;}}}if (command->count > 2){command->count = 0;command->isStart = false;}return;}if (command->count >= command->bufferSize){command->count = 0;command->isStart = false;}command->buffer[command->count] = tempData;command->count++;if (command->isStart && command->count >= 4){// 检测结束if (tempData == command->End2 &&command->buffer[command->count - 2] == command->End1){// 长度位if (command->buffer[command->count - 3] == command->count - 3){if (command->resolveCommandCallback){command->resolveCommandCallback(command->buffer, command->count - 3);}command->isStart = false;command->count = 0;}}}
}
void UART1_Isr(void) interrupt 4
{if (TI){TI = 0;// 队列中还有数据, 继续发送if (uartSendFIFO.headPos != uartSendFIFO.tailPos){SBUF = FIFOBuffer_read(&uartSendFIFO);}else{UART1_isSend = 0;}}if (RI){RI = 0;// 接收到数据, 放入队列// FIFOBuffer_push(&uartReceiveFIFO, SBUF);FIFOBUFFER_PUSH(uartReceiveFIFO, SBUF);}
}
void UART1_initCommand(void (*commandCallback)(uint8 *buffer, uint8 length))
{ES = 1; // 允许串行口中断uartReceiveFIFO.headPos = 0;uartReceiveFIFO.tailPos = 0;uartReceiveFIFO.bufferSize = Uart1_ReceiveSize;uartReceiveFIFO.buffer = Uart1_receiveBuffer;uartSendFIFO.headPos = 0;uartSendFIFO.tailPos = 0;uartSendFIFO.bufferSize = Uart1_SendBufferSize;uartSendFIFO.buffer = Uart1_sendBuffer;// DMA按组发送uartSendGroupFIFO.headPos = 0;uartSendGroupFIFO.tailPos = 0;uartSendGroupFIFO.bufferSize = Uart1_SendDMAGroupCount;uartSendGroupFIFO.buffer = Uart1_sendGroupOrder;uartCommand.Start1 = 0xf2;uartCommand.Start2 = 0xf3;uartCommand.End1 = 0xe2;uartCommand.End2 = 0xe3;uartCommand.isStart = 0;uartCommand.count = 0;uartCommand.bufferSize = Uart1_CommandBufferSize;uartCommand.buffer = Uart1_commandBuffer;uartCommand.resolveCommandCallback = commandCallback;
}
uint8 UART1_getIdleGroupIndex()
{uint8 i = 0;for (i = 0; i < Uart1_SendDMAGroupCount; i++){if (Uart1_sendGroupState[i] == 0){return i;}}return -1;
}
void UART1_DMA_Config(uint8 groupIndex)
{UART1_sendCount++;DMA_UR1T_CFG = 0x80; // bit7 1:Enable InterruptDMA_UR1T_STA = 0x00;DMA_UR1T_AMT = Uart1_sendGroupSize[groupIndex] - 1; // 设置传输总字节数(低8位):n+1DMA_UR1T_AMTH = 0x00; // 设置传输总字节数(高8位):n+1DMA_UR1T_TXAH = (uint8)((uint16)&Uart1_sendDMABuffer[groupIndex] >> 8); // 缓冲区地址DMA_UR1T_TXAL = (uint8)((uint16)&Uart1_sendDMABuffer[groupIndex]);DMA_UR1T_CR = 0xc0; // bit7 1:使能 UART1_DMA, bit6 1:开始 UART1_DMA 自动发送
}
void UART1_writeBuffer(uint8 *buffer, uint8 length)
{uint8 i = 0;if (UART1_enable == 0){return;}for (i = 0; i < length; i++){FIFOBuffer_push(&uartSendFIFO, buffer[i]);}if (UART1_isSend == 0 && UART1_isDMASend == 0){UART1_isSend = 1;SBUF = FIFOBuffer_read(&uartSendFIFO);}
}
void UART1_writeBufferByDMA(uint8 groupIndex, uint8 length)
{if (UART1_enable == 0){return;}Uart1_sendGroupSize[groupIndex] = length;if (UART1_isSend == 0 && UART1_isDMASend == 0){UART1_isDMASend = 1;Uart1_sendCurrentGroupIndex = groupIndex;Uart1_sendGroupState[groupIndex] = 2;UART1_DMA_Config(groupIndex);}else{Uart1_sendGroupState[groupIndex] = 1;FIFOBuffer_push(&uartSendGroupFIFO, groupIndex);}
}
void UART1_DMA_Interrupt(void) interrupt 50
{if (DMA_UR1T_STA & 0x01) // 发送完成{DMA_UR1T_STA &= ~0x01;if (Uart1_sendCurrentGroupIndex > -1){Uart1_sendGroupState[Uart1_sendCurrentGroupIndex] = 0; // 发送完成, 设置空闲}UART1_isDMASend = 0;Uart1_sendCurrentGroupIndex = -1;// 继续发送下一帧数据if ((uartSendGroupFIFO.headPos != uartSendGroupFIFO.tailPos)){Uart1_sendCurrentGroupIndex = FIFOBuffer_read(&uartSendGroupFIFO);if (Uart1_sendGroupSize[Uart1_sendCurrentGroupIndex] > 0){UART1_isDMASend = 1;Uart1_sendGroupState[Uart1_sendCurrentGroupIndex] = 2;UART1_DMA_Config(Uart1_sendCurrentGroupIndex);}else{Uart1_sendGroupState[Uart1_sendCurrentGroupIndex] = 0;Uart1_sendCurrentGroupIndex = -1;UART1_isDMASend = 0;}}}if (DMA_UR1T_STA & 0x04) // 数据覆盖{DMA_UR1T_STA &= ~0x04;}
}
void UART1_sendData()
{if (UART1_enable == 0){return;}// 中断发送数据if (UART1_isSend == 0 && UART1_isDMASend == 0 && uartSendFIFO.headPos != uartSendFIFO.tailPos){UART1_isSend = 1;SBUF = FIFOBuffer_read(&uartSendFIFO);return;}// DMA发送数据while (UART1_isSend == 0 && UART1_isDMASend == 0 && (uartSendGroupFIFO.headPos != uartSendGroupFIFO.tailPos)){Uart1_sendCurrentGroupIndex = FIFOBuffer_read(&uartSendGroupFIFO);if (Uart1_sendGroupSize[Uart1_sendCurrentGroupIndex] > 0){UART1_isDMASend = 1;Uart1_sendGroupState[Uart1_sendCurrentGroupIndex] = 2;UART1_DMA_Config(Uart1_sendCurrentGroupIndex);break;}else{Uart1_sendGroupState[Uart1_sendCurrentGroupIndex] = 0;Uart1_sendCurrentGroupIndex = -1;UART1_isDMASend = 0;}}
}
//========================================================================
// ---------------------- 串口发送接收部分 结束----------------------------
//========================================================================
使用伪代码:
// 发送ADC数据
void sendADCSampleSimple()
{uint8 i = 0;uint8 j = 0;uint8 size = adcCurrentIndex;uint8 *buffer = NULL; // DMA缓存int8 dmaBufferIndex = -1; // DMA缓存的索引dmaBufferIndex = UART1_getIdleGroupIndex();if (dmaBufferIndex == -1){return;}buffer = &Uart1_sendDMABuffer[dmaBufferIndex][0];i = 0;buffer[i++] = uartCommand.Start1;buffer[i++] = uartCommand.Start2;buffer[i++] = 0x20;buffer[i++] = size; // 数据个数buffer[i++] = compareValue >> 8; // 触发值buffer[i++] = compareValue;// 拼接各个通道的数据for (j = 0; j < size; j++){buffer[i++] = adc1Array[j] >> 8;buffer[i++] = adc1Array[j] & 0x00ff;}buffer[i++] = getCheckSum(buffer, 2, i); // 校验和buffer[i++] = i - 2;buffer[i++] = uartCommand.End1;buffer[i++] = uartCommand.End2;UART1_writeBufferByDMA(dmaBufferIndex, i);
}void main(void)
{UART1_initCommand(receiveUartDataCallback);while (1){// uart1接收数据while (uartReceiveFIFO.headPos != uartReceiveFIFO.tailPos){MyCommand_addData(&uartCommand, FIFOBuffer_read(&uartReceiveFIFO));}// 发送数据UART1_sendData();// 发送ADC数据, 20个数据一起发送if (adcCurrentIndex >= 20){sendADCSampleSimple();adcCurrentIndex = 0; // 重置}}
}