Algunos gadgets de uso común en la comunicación serial

Este artículo resume algunos consejos para la comunicación serial.

1. Datos del puerto serie bajo el analizador lógico

En primer lugar, todavía tenemos que configurar un puerto serie primero. Generalmente, el valor predeterminado es correcto en circunstancias normales.
inserte la descripción de la imagen aquí
El significado de los parámetros anteriores:

  • tasa de baudios 115200
  • Longitud de palabra 8
  • cheque sin cheque
  • 1 bit de parada

Luego echemos un vistazo a cómo se ve la comunicación del puerto serie. Los paquetes de datos del puerto serie se componen de la siguiente manera:
inserte la descripción de la imagen aquí
Echemos un vistazo a los datos enviados por el puerto serie al analizador lógico i. Las instrucciones relevantes se han marcado en el imagen. Ahora,
inserte la descripción de la imagen aquí
estos son los datos enviados por mi puerto serie. Veamos la velocidad en baudios. La velocidad en baudios que elegimos aquí es 115200, lo que significa que se pueden enviar 115200 bytes de datos en 1 segundo. El tiempo de esos datos puede se calculará de la
inserte la descripción de la imagen aquí
siguiente manera: tonterías, ¿no es esto usando un puerto serie de hardware, jajaja ? ) Esto puede ayudarme a entender el puerto serie ¡mas claro!
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

2. Función de número a cadena

En el proceso de comunicación, generalmente se envían más cadenas, porque las cadenas son del tipo char, que pueden coincidir directamente con los datos del puerto serie, lo cual es conveniente para la conversión, por lo que un dispositivo de uso común es convertir números en cadenas.

u32 Pow(u32 num,u8 count)
{
    
    
	u8 i;
	u32 result = 0;
	if(count == 0)
	{
    
    
		num = 1;
		return num;
	}
	else
	{
    
    
		result = num;
		for(i = 0;i<count-1;i++)
		{
    
    
			result *= num;	
		}
	}
	return result;
}

void NumToString(u32 num,u8 numbit,u8 *string)
{
    
    
	u8 i=0,z=0;
	u32 temp = 0;
	u8 changestart = 0;
	u8 j = numbit-1;

	for(i = 0;i<numbit;i++)
	{
    
    
		temp = num/Pow(10,j);

		if(changestart)
		{
    
    
			string[z++] = temp+0x30;
		}
		if((temp>0)&&(changestart == 0))
		{
    
    
			string[z++] = temp+0x30;	
			changestart = 1;
		}

		temp = Pow(10,j);
		num %= temp;
		j--;
	}
	string[z] = 0;
}

Entonces, ¿qué pasa si es un número de coma flotante? Solo puede convertirlo primero en un número entero, es decir, convertirlo primero en un número entero y luego usar esta función para procesarlo.

Una mejor manera también puede usar el método de función oficial, que puede ignorar la diferencia entre números enteros y números de coma flotante. El uso de esta función es el siguiente, porque el último parámetro son datos formateados, luego %d son datos enteros y % f es simple Tipo de punto flotante de precisión también.
inserte la descripción de la imagen aquí

3. Cadena a entero

Este es también un tipo de datos de uso común. En muchos casos, los datos recibidos se envían en forma de cadena. Si queremos obtener los datos enteros, podemos usar este método. La bandera es un delimitador, como un conjunto . de datos 13,23,23 Este es el delimitador !

int32_t str2int(uint8_t * str, uint8_t flag, uint8_t no)
{
    
    
	uint8_t No = 1;
	uint8_t * Str = str;
	uint8_t NumTemp[TempIntLen];
	while(No!=no)
	{
    
    
		if(*Str == flag)
			No++;
		Str++;
	}
	No = 0;
	while(*Str != flag && *Str != '\r' && *Str != '\n' && *Str != '\0' && No < (TempIntLen - 1))
	{
    
    
		NumTemp[No] = *Str;
		Str++;
		No++;
	}
	NumTemp[No] = '\0';
	return atoi(NumTemp);
}

4. Cadena a número de punto flotante

Esto es para obtener los datos de punto flotante, y la bandera es el delimitador

void str2double(uint8_t * str, uint8_t flag, uint8_t no, double * Output)
{
    
    
	uint8_t No = 1;
	uint8_t * Str = str;
	uint8_t NumTemp[TempDoubleLen];
	uint8_t NumTemp_int[TempDoubleLen];
	double OutputNum;
	while(No!=no)
	{
    
    
		if(*Str == flag)
			No++;
		Str++;
	}
	No = 0;
	while(*Str != flag && *Str != '\r' && *Str != '\n' && *Str != '\0' && No < (TempDoubleLen - 1))
	{
    
    
		NumTemp[No] = *Str;
		Str++;
		No++;
	}
	NumTemp[No] = '\0';
	NumTemp[(TempDoubleLen - 1)] = 0;
	No = 0;
	while(NumTemp[NumTemp[(TempDoubleLen - 1)]] != '\0' && NumTemp[(TempDoubleLen - 1)] < (TempDoubleLen - 1))
	{
    
    
		if(NumTemp[NumTemp[(TempDoubleLen - 1)]] == '.')
		{
    
    
			NumTemp[(TempDoubleLen - 1)]++;
			NumTemp_int[(TempDoubleLen - 1)] = NumTemp[(TempDoubleLen - 1)];
		}
		NumTemp_int[No] = NumTemp[NumTemp[(TempDoubleLen - 1)]];
		No++;
		NumTemp[(TempDoubleLen - 1)]++;
	}
	NumTemp_int[No]='\0';
	NumTemp[(TempDoubleLen - 1)] = NumTemp_int[(TempDoubleLen - 1)]++;
	OutputNum = (double)atoi(NumTemp_int);
	while(NumTemp[NumTemp[(TempDoubleLen - 1)]] != '\0')
	{
    
    
		OutputNum /= 10;
		NumTemp[(TempDoubleLen - 1)] ++;
	}
	*Output = OutputNum;
}

5. Método de conversión de prueba

Los cuatro métodos de conversión anteriores se prueban de la siguiente manera:

int num=1253423456;
u8 str[20]= {
    
    0};
char str1[]="112,233,41";
char str2[]="112.32,233.34,343.45";
double dat[3];

int main(void)
{
    
    
	NumToString(num,10,str);
	printf("%s\n",str);
	printf("%d\n%d\n%d\n",str2int(str1,',',1),str2int(str1,',',2),str2int(str1,',',3));
	str2double(str2,',',1,dat);
	str2double(str2,',',2,dat+1);
	str2double(str2,',',3,dat+2);
	printf("%f\n%f\n%f",dat[0],dat[1],dat[2]);
}

El resultado de la impresión se muestra a continuación, como puede ver, ¡el resultado es muy suave!
inserte la descripción de la imagen aquí

6. DMA de interrupción inactiva para recibir datos de longitud variable

Esto se usa más comúnmente cuando se necesita recibir una gran cantidad de datos, como analizar los comandos json emitidos por la nube durante el desarrollo de IoT. Generalmente, los haces de datos son relativamente grandes y no son datos de longitud fija. En este momento, esta cosa se vuelve muy necesario.

Abra el puerto serie y configure los parámetros. Generalmente, se puede configurar de forma predeterminada
inserte la descripción de la imagen aquí
. Configure las interrupciones y DMA
inserte la descripción de la imagen aquí
. Configure los modos de función de envío y recepción de DMA.

inserte la descripción de la imagen aquí
La interrupción inactiva del puerto serie se habilita durante la inicialización y
inserte la descripción de la imagen aquí
la función de interrupción se procesa.
inserte la descripción de la imagen aquí
La función de procesamiento de interrupción inactiva es la siguiente
inserte la descripción de la imagen aquí

El código fuente es el siguiente:

UART_DMA.c

/*
 * UART_DMA.c
 *
 *  Created on: Mar 18, 2022
 *      Author: LX
 */

#include "UART_DMA.h"

#include "string.h"
#include "stdarg.h"
#include "stdio.h"



#define USART_DMA_TX_BUFFER_MAXIMUM			128    // DMA缓冲区大小
#define USART_DMA_RX_BUFFER_MAXIMUM			128    // DMA缓冲区大小

extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;

extern UART_HandleTypeDef huart1; //UART句柄
uint8_t usart1_rx_buffer[USART_DMA_RX_BUFFER_MAXIMUM]; //串口1的DMA接收缓冲区
uint8_t usart1_tx_buffer[USART_DMA_TX_BUFFER_MAXIMUM]; //串口1的DMA发送缓冲区
uint16_t usart1_rx_len; //DMA一次空闲中断接收到的数据长度
uint8_t receive_data[USART_DMA_RX_BUFFER_MAXIMUM];	  //DMA接收数据缓存区

void HAL_UART_ReceiveIdle(UART_HandleTypeDef *huart)
{
    
    
  //当触发了串口空闲中断
  if((__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET))
  {
    
    
    if(huart->Instance == USART1)
    {
    
    
      __HAL_UART_CLEAR_IDLEFLAG(huart);
      HAL_UART_DMAStop(huart);
      usart1_rx_len = USART_DMA_RX_BUFFER_MAXIMUM - (__HAL_DMA_GET_COUNTER(&hdma_usart1_rx));
      memcpy(receive_data, usart1_rx_buffer, USART_DMA_RX_BUFFER_MAXIMUM);
      memset(usart1_rx_buffer,0,128);
      while (HAL_UART_Receive_DMA(&huart1,(uint8_t *)usart1_rx_buffer, USART_DMA_RX_BUFFER_MAXIMUM)!=HAL_OK);

//      Debug_printf("%s",receive_data); // 是否添加回环检测函数
    }
    //下面添加其他串口的处理函数

  }
}
void UART1_TX_DMA_Send(uint8_t *buffer, uint16_t length)
{
    
    
  while(HAL_DMA_GetState(&hdma_usart1_tx) != HAL_DMA_STATE_READY);
  //while(__HAL_DMA_GET_COUNTER(&hdma_usart1_tx));
  __HAL_DMA_DISABLE(&hdma_usart1_tx);
  HAL_UART_Transmit_DMA(&huart1, buffer, length);
}

void Debug_printf(const char *format, ...)
{
    
    
  uint32_t length = 0;
  va_list args;

  va_start(args, format);

  length = vsnprintf((char *)usart1_tx_buffer, sizeof(usart1_tx_buffer), (char *)format, args);
  UART1_TX_DMA_Send(usart1_tx_buffer, length);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    
    
  if (huart->Instance == USART1)
  {
    
    
    __HAL_DMA_CLEAR_FLAG(&hdma_usart1_tx, DMA_FLAG_TC4);
//    HAL_UART_DMAStop(&huart1);
  }
}

UART_DMA.h

#include "main.h"


void HAL_UART_ReceiveIdle(UART_HandleTypeDef *huart);
void UART1_TX_DMA_Send(uint8_t *buffer, uint16_t length);
void Debug_printf(const char *format, ...);

7. Prueba de transceptor de datos de alta frecuencia

Las siguientes pueden ser pruebas de fuerza bruta de recepción de datos:

En el asistente de puerto serie, configure una gran cantidad de datos de alta frecuencia para enviar y configure el retorno de datos en el lado del microcontrolador.Los resultados de la prueba son los siguientes:
inserte la descripción de la imagen aquí
en circunstancias normales, si el
inserte la descripción de la imagen aquí
ciclo de recepción de bucle invertido aumenta, comenzarán los errores Después
inserte la descripción de la imagen aquí
de las pruebas, las conclusiones que obtuve son las siguientes:

  • No configure la frecuencia de envío de datos demasiado alta. El volumen de datos de velocidad en baudios de 115200 no puede soportar la frecuencia de 50 hz
  • Al aumentar la tasa de baudios, los datos se pueden enviar y recibir a una frecuencia más alta. Los 92 datos que usé aquí no son un problema en el caso de 100 khz. Cuando se aumenta a 200 khz, algunos datos están incompletos.
  • Esto es solo una prueba. El microcontrolador solo ejecuta un programa. En situaciones reales, puede haber problemas como la interrupción de otros programas, etc., que afectarán el efecto de recepción.

Supongo que te gusta

Origin blog.csdn.net/m0_51220742/article/details/123568721
Recomendado
Clasificación