Microcontroller custom protocol FIFO efficient data sending method

Publisher:幸福之路Latest update time:2023-09-12 Source: zhihu Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

This article introduces how to use a serial port with FIFO to reduce the number of receiving interruptions, and provides a frame packaging method through a custom communication protocol format. Then it introduces a special serial port data sending method that can avoid using the serial port to send interrupts. , improve the response speed of the system.


1 Overview

Before that, let’s first list the shortcomings of traditional serial port data transmission and reception:

  • Each time a byte of data is received, a receive interrupt is generated. The serial port hardware FIFO cannot be effectively used to reduce the number of interrupts.

  • The response data adopts the method of waiting to be sent. Since the serial data transmission time is far less than the CPU processing time, waiting for the serial port to finish sending the current byte before sending the next byte will cause a waste of CPU resources and is not conducive to the overall system response (at 1200bps, sending one byte It takes about 10ms. If dozens of bytes of data are sent at a time, the CPU will be waiting for a long time).

  • Response data is sent using interrupts. Adding an interrupt source will increase the number of interrupts in the system, which will affect the overall stability of the system (from a reliability perspective, the fewer interrupt events, the better).

  • To address the above shortcomings, a complete solution will be provided by combining a commonly used custom communication protocol.


2. Serial FIFO

The serial port FIFO can be understood as a dedicated buffer for the serial port, which adopts a first-in-first-out method. The data receiving FIFO and the data sending FIFO are usually two independent pieces of hardware. The data received by the serial port is first put into the receiving FIFO. When the data in the FIFO reaches the trigger value (usually the trigger value is 1, 2, 4, 8, 14 bytes) or the data in the FIFO does not reach the set value but a period of If no data is received within a certain period of time (usually 3.5 character transmission time), the CPU will be notified to generate a receiving interrupt; the data to be sent must first be written into the sending FIFO. As long as the sending FIFO is not empty, the hardware will automatically send the data in the FIFO. The number of bytes written to the transmit FIFO is affected by the maximum depth of the FIFO. Usually, a maximum of 16 bytes is allowed for one write. The data listed above are related to the specific hardware. Different CPU types have different characteristics. You should refer to the corresponding data manual before use.

3. Data receiving and packaging

FIFO can buffer the data received by the serial port, so we can use FIFO to reduce the number of interrupts. Taking NXP's lpc1778 chip as an example, the trigger level of the receive FIFO can be set to 1, 2, 4, 8, and 14 bytes. It is recommended to use 8 bytes or 14 bytes, which is also the default value of the PC serial port receive FIFO. In this way, when a large amount of data is received, an interrupt will be generated every 8 bytes or 14 bytes (except for the last reception). Compared with receiving one byte and generating an interrupt, this method has a much higher number of serial port reception interrupts. reduce. Setting the receive FIFO to 8 or 14 bytes is also very simple. Taking lpc1778 as an example, you only need to set the UART FIFO control register UnFCR. The received data must comply with the communication protocol, and data and protocol are inseparable. Usually we need to package the received data into a frame according to the protocol, and then hand it over to the upper layer for processing. The following introduces a custom protocol frame format and gives a general method of packaging into frames. The custom protocol format is shown in Figure 3-1.


  • Frame header: usually 3~5 0xFF or 0xEE

  • Address number: The address number of the device to be communicated with, 1 byte

  • Command number: corresponding to different functions, 1 byte

  • Length: the number of bytes in the data area, 1 byte

  • Data: related to the specific command number, the length of the data area can be 0, and the length of the entire frame should not exceed 256 bytes

  • Check: XOR sum check (1 byte) or CRC16 check (2 bytes), this example uses CRC16 check

The following describes how to pack the received data into a frame according to the format shown in Figure 3-1. 3.1 Define data structure typedefstruct

{

uint8_t * dst_buf; //Point to the receive buffer

uint8_t sfd; //Frame first flag, 0xFF or 0xEE

uint8_t sfd_flag; //Find the beginning of the frame, usually 3~5 FF or EE

uint8_t sfd_count; //The number of frame headers, usually 3~5

uint8_t received_len; //Number of bytes received

uint8_t find_frame_flag; //After finding the complete frame, set it to 1

uint8_t frame_len; //Total length of data in this frame, this area is optional

}find_frame_struct;3.2 Initialize data structure, usually placed in serial port initialization/**

* @brief Initialize the data structure of the search frame

* @param p_fine_frame: Points to the packed frame data structure variable

* @param dst_buf: points to the frame buffer

* @param sfd: frame first flag, usually 0xFF or 0xEE

*/

voidinit_find_frame_struct(find_frame_struct * p_find_frame,uint8_t *dst_buf,uint8_t sfd)

{

p_find_frame->dst_buf=dst_buf;

p_find_frame->sfd=sfd;

p_find_frame->find_fram_flag=0;

p_find_frame->frame_len=10;

p_find_frame->received_len=0;

p_find_frame->sfd_count=0;

p_find_frame->sfd_flag=0;

} 3.3 Data Packaging Program/**

* @brief Search for a frame of data and return the number of processed data

* @param p_find_frame: Points to the packed frame data structure variable

* @param src_buf: Points to the original data received by the serial port

* @param data_len: src_buf The number of original data received by the serial port this time

* @param sum_len: the maximum length of the frame buffer

* @return The number of data processed this time

*/

uint32_tfind_one_frame(find_frame_struct * p_find_frame,constuint8_t * src_buf,uint32_t data_len,uint32_t sum_len)

{

uint32_t src_len=0;

while(data_len--)

{

if(p_find_frame ->sfd_flag==0)

{ //Start frame not found

if(src_buf[src_len++]==p_find_frame ->sfd)

{

p_find_frame ->dst_buf[p_find_frame ->received_len++]=p_find_frame ->sfd;

if(++p_find_frame ->sfd_count==5)

{

p_find_frame ->sfd_flag=1;

p_find_frame ->sfd_count=0;

p_find_frame ->frame_len=10;

}

}

else

{

p_find_frame ->sfd_count=0;

p_find_frame ->received_len=0;

}

}

else

{ //Is it the "length" byte? Y->Get the data length of this frame

if(7==p_find_frame ->received_len)

{

p_find_frame->frame_len=src_buf[src_len]+5+1+1+1+2; //Frame header+address number+command number+data length+check

if(p_find_frame->frame_len>=sum_len)

{ //The processing method here may not be the same depending on the specific application.

MY_DEBUGF(SLAVE_DEBUG,("Data length exceeds cache!n"));

p_find_frame->frame_len= sum_len;

}

}


p_find_frame ->dst_buf[p_find_frame->received_len++]=src_buf[src_len++];

if(p_find_frame ->received_len==p_find_frame ->frame_len)

{

p_find_frame ->received_len=0; //One frame completed

p_find_frame ->sfd_flag=0;

p_find_frame ->find_fram_flag=1;

return src_len;

}

}

}

p_find_frame ->find_fram_flag=0;

return src_len;

}


Usage example: Define data structure variable: find_frame_struct slave_find_frame_srt; Define receive data buffer: #define SLAVE_REC_DATA_LEN 128

uint8_t slave_rec_buf[SLAVE_REC_DATA_LEN]; Call the structure variable initialization function in the serial port initialization: init_find_frame_struct(&slave_find_frame_srt, slave_rec_buf,0xEE); Call the data packaging function in the serial port reception interrupt: find_one_frame(&slave_find_frame_srt,tmp_rec_buf,data_len,SLAVE_ DATA_LEN);


Among them, rec_buf is the temporary buffer for serial port reception, and data_len is the length of the data received this time.

4. Data sending

As mentioned earlier, the traditional waiting to send method will waste CPU resources, while the interrupt sending method will not cause a waste of CPU resources, but it also adds an interrupt source. In our use, we found that timer interrupts are used by almost every application. We can use timer interrupts and hardware FIFO to send data. After reasonable design, such a sending method will not cause a waste of CPU resources. There will not be more interrupt sources and interrupt events. It should be noted in advance that this method is not suitable for all applications. This method is certainly not supported for applications that do not have timer interrupts enabled. In addition, if the timer interrupt interval is long and the communication baud rate is particularly high, , this method is not suitable either. The communication baud rate currently used by the company is generally relatively small (1200bps, 2400bps). Under these baud rates, the timer interval is 10ms or less (including 10ms). If the timer interval is less than 1ms (including 1ms), 115200bps can be used. The main idea of ​​this method is: after the timer interrupt is triggered, determine whether there is data to be sent. If there is data to be sent and the sending conditions are met, the data is put into the sending FIFO. For lpc1778, up to 16 bytes can be put in at a time. data. The hardware will then automatically start sending without CPU involvement. The following describes how to use a timer to send data. The hardware carrier is RS485. Because sending requires operating the serial port register and the RS485 direction control pin, it needs to be closely related to the hardware. The hardware used in the following code is lpc1778, but the idea is universal. 4.1 Define data structure /*Serial port frame sending structure*/

typedefstruct

{

uint16_t send_sum_len; //The length of the frame data to be sent

uint8_t send_cur_len; //The length of data currently sent

uint8_t send_flag; //Whether to send the flag

uint8_t * send_data; //Point to the data buffer to be sent

}uart_send_struct;

4.2 Timing processing function/**

[1] [2]
Reference address:Microcontroller custom protocol FIFO efficient data sending method

Previous article:Is the main function of the microcontroller executed in the interrupt function?
Next article:Microcontroller port input and output impedance

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号