Directorio de artículos
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.
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:
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,
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
siguiente manera: tonterías, ¿no es esto usando un puerto serie de hardware, jajaja ? ) Esto puede ayudarme a entender el puerto serie ¡mas claro!
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.
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!
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
. Configure las interrupciones y DMA
. Configure los modos de función de envío y recepción de DMA.
La interrupción inactiva del puerto serie se habilita durante la inicialización y
la función de interrupción se procesa.
La función de procesamiento de interrupción inactiva es la siguiente
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:
en circunstancias normales, si el
ciclo de recepción de bucle invertido aumenta, comenzarán los errores Después
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.