This is the 12th day of my participation in Gwen Challenge
When using the serial communication function of single chip microcomputer, the commonly used method of receiving data is to judge whether a frame is sent through a fixed number of bytes, or to indicate that a frame is sent through a fixed end flag bit. But sometimes will have to send the data length is not fixed, also does not have the fixed end, typically for such data every once in a while check whether to change the length of the receiving data, if the specified length of receiving data over a period of time has not changed, is considered to be a frame of data sent to complete. In STM32 MCU serial port provides a better function, is idle interrupt function. That is, when a frame of data is sent, an idle interrupt occurs. This idle interrupt can then be used to determine whether a frame has been received.
The description of serial idle detection can be found in the STM32 reference manual
As can be seen from the figure, when the first group of data Data1, Data2, Data3, Data4 are sent, the bus will be in idle state, and then an idle interrupt will occur.
When each Data1, Data2, Data3, Data4 data arrives, the interrupt generated by the serial port is RXNE: the read data register is not broken in the air. You can view the details in the status register (USART_SR).
That is, the serial port generates an RXNE interrupt for each byte received, and an IDLE interrupt when a frame is sent.
In this way, data can be stored in the serial port after each RXNE interrupt, and then the end of data receiving after the arrival of IDLE interrupt. It’s time to process the received data.
The following code to illustrate how to use serial port idle interrupt to receive variable length data
Initialize the serial port first
void uart2_init( u16 baud ) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // Push pull multiplexing mode gPIo_initstructure. GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Speed = GPIO_Speed_50MHz; // Float input mode gPIo_initstructure. GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); USART_InitStructure.USART_BaudRate = baud; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init( USART2, &USART_InitStructure ); USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART_Cmd(USART2, ENABLE); // Enable serial port 2}Copy the code
The only difference from regular serial port initialization is an extra line of idle interrupt initialization code
USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); // Enable serial port idle interrupt
The data is then processed according to the interrupt type in the interrupt
void USART2_IRQHandler( void ) { u8 tem = 0; if( USART_GetITStatus( USART2, USART_IT_RXNE ) ! = RESET) // Receive interrupt {tem = USART_ReceiveData(USART2); Rec_buff [uart2_rec_cnt++] = tem; } if(USART_GetITStatus(USART2, USART_IT_IDLE)! {tem = USART2->SR; Tem = USART2->DR; // Read DR register (read USART_SR first, then read USART_DR to clear IDLE interrupt flag) copy_data(rec_buff, uart2_rec_cnt); ReceiveOK_flag = 1; Uart2_rec_cnt = 0; // The length of the received data is reset}}Copy the code
When the serial port is RXNE interrupt, the data is directly stored in the array. When IDLE interrupt comes, the received data is stored, and then a frame of data success signal is received. The main program can then process the data when it detects a sign of success.
In this way, it is easy to receive data of variable length through IDLE interrupt. No matter how long the data length is, IDLE interrupt can be detected as long as a frame of data ends.
The complete code is as follows
#ifndef __UART2_H #define __UART2_H #include "sys.h" #define UART2_REC_LEN 20 void uartDMA_Init( void ); void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx ); void uart2_Send( u8 *buf, u16 len ); void copy_data( u8 *buf, u16 len ); #endifCopy the code
#include "uart2.h" u8 rec_buff[UART2_REC_LEN] = {0}; u16 uart2_rec_cnt = 0; U8 data_backup[UART2_REC_LEN] = {0}; U16 dataLen_backup = 0; _Bool receiveOK_flag = 0; // Receive completion flag bit /* What does idle interrupt mean? When the bus receives data, once the data flow is interrupted and the bus is IDLE, IDLE will be set to 1, resulting in IDLE interruption. When data is sent, the IDLE bit is set to 0. Note: it will not automatically clear 0 after setting 1, nor will it always interrupt because the status bit is 1. It will only occur when 0 jumps to 1, which can also be understood as rising edge trigger. Therefore, to ensure that the next idle interrupt will work properly, you need to send arbitrary data in the interrupt service function to clear the flag bit. */ void uart2_init( u16 baud ) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // Push pull multiplexing mode gPIo_initstructure. GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Speed = GPIO_Speed_50MHz; // Float input mode gPIo_initstructure. GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); USART_InitStructure.USART_BaudRate = baud; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init( USART2, &USART_InitStructure ); USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART_Cmd(USART2, ENABLE); // Enable serial port 2} // Send len bytes //buf: start address of the sending area //len: number of bytes sent void uart2_Send(u8 *buf, u16 len) {u16t; for( t = 0; t < len; T++) {while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); USART_SendData( USART2, buf[t] ); } while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET ); } // Back up the received data void copy_data(u8 *buf, u16 len) {u16t; dataLen_backup = len; // Save data length for(t = 0; t < len; t++ ) { data_backup[t] = buf[t]; // Back up the received data to prevent the old data from being overwritten when new data is received during data processing. }} //RXNE interrupts are used to receive serial data of variable length. RXNE interrupts when 1 byte is received, and IDLE interrupts when a frame is received. For example, eight RXNE interrupts and one IDLE interrupt are generated when eight bytes are sent to the microcontroller at a time. void USART2_IRQHandler( void ) { u8 tem = 0; if( USART_GetITStatus( USART2, USART_IT_RXNE ) ! = RESET) // Receive interrupt {tem = USART_ReceiveData(USART2); Rec_buff [uart2_rec_cnt++] = tem; } if(USART_GetITStatus(USART2, USART_IT_IDLE)! {tem = USART2->SR; Tem = USART2->DR; Uart2_Send (rec_buff, uart2_rec_cnt); // Send copy_data(rec_buff, uart2_rec_cnt) to copy_data(rec_buff, uart2_rec_cnt); ReceiveOK_flag = 1; Uart2_rec_cnt = 0; // The length of the received data is reset}}Copy the code
#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "key.h" #include "uart2.h" #include "string.h" extern u8 data_backup[UART2_REC_LEN]; Extern u16 dataLen_backup; Extern _Bool receiveOK_flag; // Extern _Bool receiveOK_flag; Int main(void) {u8 j = 0; NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 ); delay_init(); // the delay function initializes LED_Init(); // Initialize the hardware interface connected to the LED uart2_init(9600); While (1) {if(receiveOK_flag) // Start processing data after receiving a frame of data {receiveOK_flag = 0; uart2_Send( data_backup, dataLen_backup ); Memset (data_backup, 0, sizeof(data_backup)); // empty the backup array} j++; if( j > 50 ) { j = 0; LED = ! LED; } delay_ms( 10 ); }}Copy the code
The test results are as follows
As long as the length of the sent data does not exceed the length of the receive cache, the sent data can be accurately received.