Cómo recibir de forma estable a alta velocidad de transmisión del puerto serie

Ya sea Bluetooth, WiFi o 4G, 5G u otros módulos, todos admiten comando AT + modo de transmisión transparente.

En el modo de comando AT, ejecute el comando de consulta y el comando de operación (configuración).

La velocidad de respuesta es rápida, la interacción lógica es clara y no se requiere un código de procesamiento complejo.

Por ejemplo, consulte los comandos de información MAC, configure los comandos de velocidad de transmisión, etc., puede simplemente enviarlos y luego esperar la interrupción para procesar los datos. En el medio, solo necesita una variable global para transferir el estado, y un Puntero de búfer para almacenar en caché los resultados y luego liberarlos después de su uso.

Interacción entre el control principal y el módulo

En escenarios de trabajo reales, a menudo es necesario ingresar al modo de transmisión transparente.

Los datos recibidos por el módulo se pasan directamente a la MCU; cuando la MCU necesita enviar datos, envía directamente los metadatos al módulo.

En el modo de transmisión transparente, generalmente hay dos puntos a considerar:

Uno es el formato de protocolo de transmisión transparente.

Los datos originales         se pueden pasar directamente entre sí , o se puede agregar un paquete fijo en la cabeza o la cola (o la cabeza y la cola) . Esto depende principalmente de si la comunicación entre las dos partes es confiable y de la complejidad de la comunicación.

El segundo es la transferencia del estado del enlace.

        Para ingresar al modo de transmisión transparente, la premisa debe ser que la conexión se establezca con éxito. Si la conexión se desconecta, la retroalimentación debe realizarse a tiempo y el extremo del par debe realizar un procesamiento lógico a tiempo.

        Entre ellos, bajo 1 a 1, la mejor manera de transferir el estado es detectar el estado IO (conexión de hardware). A juzgar por el software, siempre hay errores inevitables.

Procesamiento de datos en el lado maestro

El terminal de control principal es el envío de datos y la recepción de datos.

El envío generalmente no es un problema.

Principalmente, ¿cómo recibir datos de manera eficiente?

Tome la transferencia de archivos bluetooth como ejemplo, utilizando el módulo BR2x51e(-s), velocidad de transmisión de comunicación del puerto serie 921600, envío de archivos desde una tableta o teléfono móvil, un paquete de 10K de tamaño de datos a la vez, el tamaño máximo total del archivo es de aproximadamente 20M.

problemas encontrados

1. Hay un fenómeno de pérdida de datos al recibir datos en modo DMA Normal/circular

A una velocidad de transmisión de 115200 baudios de 256 KB o incluso 1 K, básicamente solo se activará una interrupción inactiva;

Incluso un paquete de datos de 256 KB a una velocidad de transmisión de 921600 activará múltiples interrupciones inactivas del puerto serie;

Es decir, cuanto mayor sea la velocidad en baudios, mayor será la probabilidad de que se active la interrupción inactiva del puerto serie debido a la asincronía del reloj oa la sincronización inexacta del módulo.

2. El modo de ciclo DMA + la interrupción de finalización de la transferencia DMA se produce cuando se sobrescriben los datos

A diferencia del anterior que provocaba la pérdida de datos por interrupción del hardware, esta vez no se pudieron procesar los datos del subproceso, lo que provocó que se sobrescribieran.

Resuelto después de agregar el mecanismo de búfer secundario.

Resumido de la siguiente manera

2 métodos de recepción de puerto serie de uso común

1. Modo normal DMA + recepción de interrupción inactiva

Este método es aplicable al modo de transmisión simple, es decir, la interacción en forma de uno que recibe y otro que envía.

Porque en el modo Normal,

        1. Cuando los datos recibidos se desbordan, los datos originales se sobrescribirán desde el principio y existe el riesgo de pérdida de datos;

        2. En este modo, se requiere que el tamaño del buf de recepción de DMA sea mayor que la longitud máxima de los datos posibles, lo que desperdicia memoria;

Pero es muy adecuado para la interacción de datos en el modo de comando AT, y el área buf solo necesita una pequeña longitud.

//典型代码

uint8_t g_logRxBuf[LOG_RX_BUF_NUM_MAX];//定义DMA接收缓冲区


void LogInit(void)
{
    __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能空闲中断
    HAL_UART_Receive_DMA(&huart2, g_logRxBuf, LOG_RX_BUF_NUM_MAX);//配置DMA接收地址和长度
}


__weak void ConsoleRxCallback(uint8_t *pData,uint16_t unDataLen) { }//回调函数


void LogRxIdleCallBack(void)
{
  if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE)!=RESET)
  {
    __HAL_UART_CLEAR_IDLEFLAG(&huart2);//清中断标志位

    HAL_UART_DMAStop(&huart2);//关闭DMA传输
    __HAL_UNLOCK(&huart2);

    ConsoleRxCallback(g_logRxBuf,LOG_RX_BUF_NUM_MAX - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx));//数据处理

    HAL_UART_Receive_DMA(&huart2, g_logRxBuf, LOG_RX_BUF_NUM_MAX);//重新配置DMA传输
  }
}

2. Modo de ciclo DMA + interrupción de finalización de DMA de puerto serie/interrupción semicompleta de DMA de puerto serie/interrupción inactiva de puerto serie

En este modo, es equivalente a usar tres fuentes de interrupción para activar el procesamiento de datos.

De esta manera, utilizando la interrupción semicompleta y la interrupción completa de la transmisión DMA, solo un pequeño buf puede procesar una gran cantidad de datos. Aquí, no hay problema en usar un buf de 1K para recibir datos de 16K . Al procesar esta recepción de datos de 16K , Siempre ingrese la interrupción a medio terminar para procesar primero la primera mitad de los datos, y luego ingrese la interrupción completa para procesar la segunda mitad de los datos, para que la recepción de datos no se vea afectada al procesar los datos.

No hay pérdida de datos a nivel de hardware.

static uint8_t g_bt_dma_buf[BT_BUF_SIZE];//DMA接收buf,1K大小
static volatile uint16_t unBtDmaReadOffset;//接收偏移量(volatile防止被优化)

void BlueToothInit(void)
{
	//BlueToothConfig(921600);
	__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart3,g_bt_dma_buf,BT_BUF_SIZE);
}

__weak void BTUartRxCallBack(uint8_t *pData,uint16_t unDataLen) {}

void BtUartRxIdleCallback(void)
{
	uint32_t ulLen;
	
    if((__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) != RESET))
    {
        __HAL_UART_CLEAR_IDLEFLAG(&huart3);
       
		ulLen = BT_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx) - unBtDmaReadOffset;
		
		BTUartRxCallBack(&g_bt_dma_buf[unBtDmaReadOffset], ulLen);

		//空闲中断:累加DMA接收偏移量
		unBtDmaReadOffset += ulLen;
    }
}

void BtUartDmaRxHalfCpltCallback(void)
{
    uint16_t unLen = 0;
    //半完成中断:DMA接收偏移量为buf的一半大小
    unLen = BT_BUF_SIZE/2 - unBtDmaReadOffset;
    BTUartRxCallBack(&g_bt_dma_buf[unBtDmaReadOffset], unLen);
    unBtDmaReadOffset = BT_BUF_SIZE/2;
}

void BtUartDmaRxCpltCallback(void)
{
    uint16_t unLen = 0;
    //完成中断:DMA接收偏移量就等于buf的大小,即0
    unLen = BT_BUF_SIZE - unBtDmaReadOffset;
    BTUartRxCallBack(&g_bt_dma_buf[unBtDmaReadOffset], unLen);
    unBtDmaReadOffset = 0;
}

búfer secundario

Cuando se usa DMA para completar la transmisión de interrupción de datos, el problema de pérdida de datos causado por la interrupción del hardware se puede resolver. Sin embargo, cuando los datos se transmiten continuamente a alta velocidad, la capa de aplicación no podrá manejarlos. En este momento , se puede agregar otra capa de búfer para resolver el problema.

static uint8_t pBtRxDataBuff[BT_UART_RX_BUFF_MAX_LEN];//接收缓冲区,4K大小
static volatile uint16_t unBtRxBuffDealOffset = 0;//缓冲区读取偏移量
static volatile uint16_t unBtRxBuffRxOffset = 0;//缓冲区写入偏移量
static volatile uint16_t unBtRxBuffNeedDealLen = 0;//缓冲区写入总长度

void BTUartRxCallBack(uint8_t *pData,uint16_t unDataLen)
{
	WriteBlueToothRxData(pData, unDataLen);
    OSSemPost(UpperRxSem);
}

//将数据写入“循环”缓冲区
void WriteBlueToothRxData(uint8_t *pData,uint16_t unDataLen)
{
	uint16_t unWriteLen = 0,unWriteOffset = 0;
	
	while(1)
	{
		if(unWriteOffset == unDataLen)
			break;
	
		unWriteLen = unDataLen - unWriteOffset;
	
        //当写入长度大于剩余长度时,1、先写入剩余长度
        //2、再将多余长度从buf首地址开始写入
		if(unWriteLen >= (BT_UART_RX_BUFF_MAX_LEN - unBtRxBuffRxOffset))
			unWriteLen = BT_UART_RX_BUFF_MAX_LEN - unBtRxBuffRxOffset;
	    
        //拷贝数据
		memcpy(&pBtRxDataBuff[unBtRxBuffRxOffset],pData + unWriteOffset,unWriteLen);
		unWriteOffset += unWriteLen;
		unBtRxBuffNeedDealLen += unWriteLen;
		unBtRxBuffRxOffset += unWriteLen;

        //写满时将写入位置拉到buf首地址
		if(unBtRxBuffRxOffset >= BT_UART_RX_BUFF_MAX_LEN)
			unBtRxBuffRxOffset = 0;
	}
}

//获取缓冲区数据长度
//unBtRxBuffDealOffset buf读取的偏移量,用来追接收偏移量
uint16_t ReadBlueToothRxDataLen(void)
{
	if(unBtRxBuffDealOffset != unBtRxBuffRxOffset)
	{
		if(unBtRxBuffRxOffset > unBtRxBuffDealOffset)	
			return unBtRxBuffRxOffset - unBtRxBuffDealOffset;
		else
			return unBtRxBuffRxOffset + BT_UART_RX_BUFF_MAX_LEN - unBtRxBuffDealOffset;
	}
	else
		return 0;
}


//读取缓冲区数据
uint8_t ReadBlueToothRxData(void)
{
    uint8_t byData;

	unBtRxBuffNeedDealLen--;

	byData = pBtRxDataBuff[unBtRxBuffDealOffset++];
	if(unBtRxBuffDealOffset >= BT_UART_RX_BUFF_MAX_LEN)
		unBtRxBuffDealOffset = 0;

    return byData;
}

void ReadBlueToothRxDataStr(uint8_t *pBuf,uint16_t len)
{
	uint16_t i;

	for(i=0; i<len; i++)
		pBuf[i] = ReadBlueToothRxData();

    return;
}

Supongo que te gusta

Origin blog.csdn.net/weixin_38743772/article/details/126356323
Recomendado
Clasificación