Some commonly used gadgets in serial communication

This article summarizes some tips for serial communication

1. Serial port data under the logic analyzer

First of all, we still need to configure a serial port first. Generally, the default is OK under normal circumstances.
insert image description here
The meaning of the above parameters:

  • baud rate 115200
  • Word length 8
  • check no check
  • 1 stop bit

Then let's take a look at what the serial port communication looks like. The serial port data packets are composed as follows:
insert image description here
Let's take a look at the data sent by the serial port to the logic i analyzer. The relevant instructions have been marked in the picture. Now ,
insert image description here
this is the data sent by my serial port. Let's look at the baud rate. The baud rate we choose here is 115200, which means that 115200 bytes of data can be sent in 1 second. The time of that data can be calculated as
insert image description here
follows:
insert image description here
We can measure the accuracy of this time in the logic analyzer, and we can see that it is very accurate ( nonsense, isn't this using a hardware serial port, hahaha )
insert image description here
This can help me understand the serial port more clearly!

2. Number to string function

In the communication process, more strings are generally sent, because the strings are of the char type, which can directly match the data of the serial port, which is convenient for conversion, so a commonly used gadget is to convert numbers to strings!

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;
}

So what if it is a floating-point number, you can only convert it to an integer first, that is, convert it to an integer first, and then use this function to process it!

A better way can also use the official function method, which can ignore the difference between integers and floating-point numbers. The usage of this function is as follows, because the last parameter is formatted data, then %d is integer data, and %f is single. Precision floating point type too.
insert image description here

3. String to integer

This is also a commonly used data type. In many cases, the received data is sent in the form of a string. If we want to obtain the integer data, we can use this method. The flag is a delimiter, such as a set of data. 13,23,23 This is the delimiter !

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. String to floating point number

This is to get the floating-point data in it, and flag is the delimiter

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. Test conversion method

The above four conversion methods are tested as follows:

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]);
}

The print result is shown below, as you can see, the result is very smooth!
insert image description here

6. Idle interrupt DMA to receive variable length data

This is more commonly used when a large amount of data needs to be received, such as parsing json commands issued by the cloud during IoT development. Generally, the data beams are relatively large and not fixed-length data. At this time, this thing becomes very necessary. span

Open the serial port and set the parameters. Generally, it can be configured by default
insert image description here
. Configure interrupts and DMA
insert image description here
. Configure DMA send and receive function modes.

insert image description here
The serial port idle interrupt is enabled during initialization, and
insert image description here
the interrupt function is processed.
insert image description here
The idle interrupt processing function is as follows
insert image description here

The source code is as follows:

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. High frequency data transceiver test

The following can be brute force testing of data reception:

In the serial port assistant, set a large amount of high-frequency data to send, and set the data return on the microcontroller side. The test results are as follows:
insert image description here
under normal circumstances, if the loopback receiving
insert image description here
cycle is increased, errors will begin to occur.
insert image description here
After testing, the conclusions I got are as follows:

  • Do not set the data sending frequency too high. The baud rate data volume of 115200 can't stand the frequency of 50hz
  • By increasing the baud rate, data can be sent and received at a higher frequency. The 92 data I used here is no problem in the case of 100khz. When it is increased to 200khz, some data are incomplete.
  • This is just a test. The microcontroller just runs a program. In actual situations, problems such as interruption of other programs may occur, which will affect the receiving effect.

Guess you like

Origin blog.csdn.net/m0_51220742/article/details/123568721