STM32CubeMX serial port idle interrupt plus DMA to realize multi serial port indefinite length data transmission without affecting the transmission

Keywords: Linker

Theme:

Receiving: configure DMA receiving of serial port, open idle interrupt of serial port, but the length of DMA receiving must be appropriate, and it is easy to overflow if it is small. Then in the idle interrupt of the serial port, turn off the receiving of DMA, take out DMA data, transfer or directly process. After that, configure DMA receive again, and point the address to the beginning of DMA receive array.

Send: when DMA is sent, set the mark and clear the zero mark in the interrupt after sending to prevent calling DMA to send continuously, resulting in not sending last time. This time, the pointer of DMA sending is directly changed. Causes incomplete sending.

I. CubeMX configuration

  1. Configure serial port. Multiple serial ports can be configured in the same way
  2. After configuration, generate code according to usage habits

2. Rewrite code

  1. Open MDK, modify void MX? DMA? Init (void) in DMA, no DMA receive interrupt is needed, note out DMA receive interrupt, note out channel number, according to chip
    void MX_DMA_Init(void) 
    {
      /* DMA controller clock enable */
      __HAL_RCC_DMA2_CLK_ENABLE();
     
      /* DMA interrupt init */
      /* DMA2_Stream0_IRQn interrupt configuration */
      HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 2, 0);
      HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
      /* DMA2_Stream2_IRQn interrupt configuration */
    //  HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
    //  HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
      /* DMA2_Stream7_IRQn interrupt configuration */
      HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 1, 0);
      HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
     
    }

     

  2. Define the serial port receiving data type in the usart.h file as follows (usart.h)

/* USER CODE BEGIN Private defines */
#define RECEIVELEN 1024 / / the length is customized according to the application
#Define USART? DMA? Sending 1 / / sending incomplete
#Define USART? DMA? Sendover 0 / / sending completed
typedef struct
{
uint8_t receive_flag:1;//Idle receive flag
uint8_t dmaSend_flag:1;//Send completion flag
uint16_t rx_len;//Receiving length
uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA receive cache
}USART_RECEIVETYPE;
 
extern USART_RECEIVETYPE UsartType1;
 
 
/* USER CODE END Private defines */

3. Then we need to add idle interrupt processing function and DMA sending function in USART. C. (usart.c)

1) first define the data type:

/* USER CODE BEGIN 0 */
 
USART_RECEIVETYPE UsartType1;
 
/* USER CODE END 0 */

2) then define the Idle interrupt processing function and DMA sending function. The serial port Idle interrupt function name needs to be declared in the uart.h file for the convenience of calling in the interrupt file function

/* USER CODE BEGIN 1 */
#ifdef __GNUC__
 
  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
 set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
 
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
	
PUTCHAR_PROTOTYPE
{
	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
 
//DMA send function
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
{
	while(UsartType1.dmaSend_flag == USART_DMA_SENDING);
	UsartType1.dmaSend_flag = USART_DMA_SENDING;
	HAL_UART_Transmit_DMA(&huart1, pdata, Length);
}
 
//DMA send completion interrupt callback function
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	 __HAL_DMA_DISABLE(huart->hdmatx);
	UsartType1.dmaSend_flag = USART_DMA_SENDOVER;
}
 
//Serial port receives idle interrupt
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
	uint32_t temp;
 
	if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
	{ 
//                Hal ﹣ UART ﹣ dmastop (& huart1); / / Stop is not needed here, or the sending will be stopped together
                HAL_UART_DMA_StopRX(huart);//Use StopRX instead, so DMA transmission will not be affected
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);	
		temp = huart1.hdmarx->Instance->NDTR;
		UsartType1.rx_len =  RECEIVELEN - temp; 
		UsartType1.receive_flag=1;
		HAL_UART_Receive_DMA(&huart1,UsartType1.usartDMA_rxBuf,RECEIVELEN);
	}
}
 
/* USER CODE END 1 */

4. Add in the interrupt file (of course, the above idle interrupt processing function needs to be declared) (stm32fxxxit.c)

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	UsartReceive_IDLE(&huart1);
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
 
  /* USER CODE END USART1_IRQn 1 */
}

5. In the main function, turn on idle interrupt and initialize DMA reception

/* USER CODE BEGIN 2 */
	HAL_UART_Receive_DMA(&huart1, UsartType1.usartDMA_rxBuf, RECEIVELEN);
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
  /* USER CODE END 2 */

6. Then you can process the data in while(1)

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
	
  while (1)
  {
		if(UsartType1.receive_flag)//If an idle interrupt occurs
		{
			UsartType1.receive_flag=0;//Zero mark
			Usart1SendData_DMA(UsartType1.usartDMA_rxBuf,UsartType1.rx_len);//The serial port prints the received data.
		}
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
		
  }
  /* USER CODE END 3 */

This is the modified code. However, the HAL library version changes, and the function name may be different.

Posted by madhukar_garg on Thu, 07 Nov 2019 20:56:27 -0800