Article directory
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.
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:
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 ,
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
follows:
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 )
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.
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!
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
. Configure interrupts and DMA
. Configure DMA send and receive function modes.
The serial port idle interrupt is enabled during initialization, and
the interrupt function is processed.
The idle interrupt processing function is as follows
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:
under normal circumstances, if the loopback receiving
cycle is increased, errors will begin to occur.
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.