Full set of cubemx+HAL+UART+DMA+IDLE configuration

This article mainly records the steps to prepare the serial port code generated by cubemx, transplant the buffer fifo and create the interface function. The creation of the nanny level is divided into the following steps.

    • 1. cubemx configure uart + interrupt
      • Step One-New Construction
      • Step 2 – Configure DMA
      • Step Three – Configure Interrupts
      • Step 4 – Generate
    • 2. Migrate the buffer fifo file
      • Step 1 – Add BSP and APP folders
      • Step 2 – Prepare cfifo.c fifo.h
      • Step 3 – Prepare cfifo_usart_dma.c cfifo_usart_dma.h
    • 3. Configure the specific location and steps of the buffer fifo code file.
      • The first step – modify fifo.h
      • Step 1 – Modify cfifo_usart_dma.c
      • Step 3 – Modify cfifo_usart_dma.h
    • 5. Project configuration binding use
      • Step 1 – Modify rewrite_it.c
      • Step 2 – Modify rewrite_it.h
      • Step 3 – Modify the stm32f4xx_it.c file
    • 4. Usage examples

1. cubemx configuration uart + interrupt

Step one-new project

1.Select MCU-stm32f407vgt6

Step 2 – Configure DMA

2. After configuring the clock and other necessary functions, open the DMA configuration of the serial port as follows

Step 3 – Configure Interrupts

3. After configuring the clock and other necessary functions, open the interrupt configuration of the serial port as follows (6 serial ports are opened here, all with the same configuration)

Step 4-Generate

4. Click GENERATE CODE to generate directly

2. Migrate the buffer fifo file

Step 1-Add BSP and APP folders

4. Add the folders BSP and APP to the generated folder, and create source include in their subdirectories. The two directories are as follows:

Step 2 – Prepare cfifo.c fifo.h

cfifo.c

#include "cfifo.h"
 
 
/*Ring CFIFO initialization*/
void CfifoBuff_Init(CfifoBuff * Cfifo_pointer)
{<!-- -->
   //Initialize related information
   Cfifo_pointer->Head = 0;
   Cfifo_pointer->Tail = 0;
   Cfifo_pointer->Lenght = 0;
   memset(Cfifo_pointer->BUFF,'\0',CFIFO_SIZE); //Ring CFIFO buffer initialization
}
 
/*Ring CFIFO data clear*/
void CfifoBuff_Clear(CfifoBuff * Cfifo_pointer)
{<!-- -->
   //Clear related information
   Cfifo_pointer->Head = 0;
   Cfifo_pointer->Tail = 0;
   Cfifo_pointer->Lenght = 0;
   memset(Cfifo_pointer->BUFF,'\0',CFIFO_SIZE); //Clear the ring CFIFO buffer area
}
 
 
 
/*Ring CFIFO data writing*/
/*
*Parameter Description:
* Cfifo_pointer----Ring CFIFO structure
* User_buff----Data to be written
* num----Written data length
*
*Return value description: The length of data correctly written to the FIFO buffer area
*
*Function description: Write the data in User_buff to the circular CFIFO buffer area
*
*/
int16_t CfifoBuff_Write(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num)
{<!-- -->
int16_t i , wrint_num;
\t
    if(Cfifo_pointer->Lenght >= CFIFO_SIZE) //Determine whether the buffer area is full
    {<!-- -->
write_num=-1;
\t\t 
         return wrint_num; //data overflow
    }
\t\t
    if(Cfifo_pointer->Lenght + num<CFIFO_SIZE) //Determine whether the length of the written data exceeds the current maximum value that can be written
{<!-- -->
write_num=num;
}
else
{<!-- -->
wrint_num=CFIFO_SIZE-Cfifo_pointer->Lenght;
}
\t\t
for(i=0;i<wrint_num;i + + )
{<!-- -->
Cfifo_pointer->BUFF[Cfifo_pointer->Tail]=*(User_buff + i);
\t\t\t
Cfifo_pointer->Tail = (Cfifo_pointer->Tail + 1)?IFO_SIZE;//Prevent cross-border illegal access
}
\t\t
    Cfifo_pointer->Lenght + =wrint_num;
\t\t
    return wrint_num; //Return the correctly written data length
}
 
 
/*Ring CFIFO reading*/
/*
*Parameter Description:
* Cfifo_pointer----Ring CFIFO structure
* User_buff----Read the data storage location
* num----read data length
*
*Return value description: Correctly read the data length of User_buff
*
*Function description: Read the data in the ring CFIFO buffer area to User_buff
*
*/
int16_t CfifoBuff_Read(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num)
{<!-- -->
int16_t i , read_num;
\t
    if(Cfifo_pointer->Lenght == 0) //Determine if it is not empty
    {<!-- -->
        read_num=-1;
\t\t 
        return read_num; //No data
    }
\t
   if(Cfifo_pointer->Lenght-num>=0) //Determine whether the read data length exceeds the current maximum readable value
   {<!-- -->
read_num=num;
   }
   else
   {<!-- -->
    read_num=Cfifo_pointer->Lenght;
   }
\t\t
   for(i=0;i<read_num;i + + )
   {<!-- -->
*(User_buff + i)=Cfifo_pointer->BUFF[Cfifo_pointer->Head];
\t\t\t
Cfifo_pointer->Head = (Cfifo_pointer->Head + 1)?IFO_SIZE;//Prevent cross-border illegal access
   }
\t\t
    Cfifo_pointer->Lenght-=read_num;
\t\t
    return read_num; //Return the correctly written data length
\t\t
}

cfifo.c

#ifndef _CFIFO_H_
#define _CFIFO_H_
 
#ifdef __cplusplus
extern "C" {<!-- -->
#endif
 
/* Includes ----------------------------------------------- ------------------*/
#include "main.h"
 
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
/* USER CODE END Includes */
 
/* USER CODE BEGIN Private defines */
 
#define CFIFO_SIZE 1024 //Ring queue CFIFO size
 
 
/*Ring CFIFO structure*/
typedef struct
{<!-- -->
uint16_t Head; //Ring CFIFO queue head
uint16_t Tail; //Tail of ring CFIFO queue
    uint16_t Lenght; //Ring CFIFO data length
uint8_t BUFF[CFIFO_SIZE]; //Ring CFIFO buffer area
}CfifoBuff;
 
 
/* USER CODE END Private defines */
 
 
/* USER CODE BEGIN Prototypes */
void CfifoBuff_Init(CfifoBuff * Cfifo_pointer); //CFIFO initialization
void CfifoBuff_Clear(CfifoBuff * Cfifo_pointer); //CFIFO data clear
int16_t CfifoBuff_Write(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num); //CFIFO data writing
int16_t CfifoBuff_Read(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num); //CFIFO data reading
/* USER CODE END Prototypes */
 
#ifdef __cplusplus
}
#endif
 
#endif /* _CFIFO_H_ */
 
 

Step 3 – Prepare cfifo_usart_dma.c cfifo_usart_dma.h

cfifo_usart_dma.c

#include "cfifo_usart_dma.h"




static uint8_t DbgTxDma[MW_UART_DMA_LEN] = {<!-- -->0}; //DMA send buffer area
static uint8_t DbgRxDma[MW_UART_DMA_LEN] = {<!-- -->0}; //DMA receive buffer area

static uint8_t E2KTxDma[MW_UART_DMA_LEN] = {<!-- -->0}; //DMA send buffer area
static uint8_t E2KRxDma[MW_UART_DMA_LEN] = {<!-- -->0}; //DMA receive buffer area

static uint8_t FPGATxDma[MW_UART_DMA_LEN] = {<!-- -->0}; //DMA send buffer area
static uint8_t FPGARxDma[MW_UART_DMA_LEN] = {<!-- -->0}; //DMA receive buffer area

MW_UART_ATTR E2K_UartAttr;
MW_UART_ATTR FPGA_UartAttr;
MW_UART_ATTR Dbg_UartAttr;


/**
  * @brief Initialize the circular queue maintained by the serial port
  * @param sUartAttr maintains the structure of the serial port circular queue, including the send and receive buffer fifo, dma offset, sending status, and dma buffer size
  * @param huart maintains the serial port number huartx(x = 1.2.3) defined in usart.c
  * @param RxDma DMA receive data buffer
  * @param TxDma DMA receive data buffer
  * @retval Initialization success status Success: MW_FAIL Failure: MW_SUCCESS
  */
int8_t MW_UART_Init(MW_UART_ATTR *sUartAttr, UART_HandleTypeDef *huart, uint8_t *RxDma, uint8_t *TxDma)
{<!-- -->
MW_UART_ATTR *pUartAttr = NULL;
\t\t
/*Attach initial values to attribute parameters */
pUartAttr = sUartAttr;
pUartAttr->handle = huart;
pUartAttr->DamOffset = 0;
pUartAttr->TransFlag = MW_TRANS_IDLE;
pUartAttr->DmaSize = MW_UART_DMA_LEN;
pUartAttr->pReadDma = RxDma;
pUartAttr->pWriteDma = TxDma;
CfifoBuff_Init( & amp;pUartAttr->AcceptCFifo); // used for data acceptance
CfifoBuff_Init( & amp;pUartAttr->SendCFifo); // used for data sending
\t\t
/*Configure DMA parameters and enable interrupts*/
if(HAL_OK != HAL_UART_Receive_DMA(pUartAttr->handle, pUartAttr->pReadDma, MW_UART_DMA_LEN))
{<!-- -->
return MW_FAIL;
}
/*Enable serial port idle interrupt*/
__HAL_UART_ENABLE_IT(pUartAttr->handle, UART_IT_IDLE);
\t\t
return MW_SUCCESS;
}
/**
  * @brief Initializes the circular queue maintained by all serial ports
  * @param none
  * @retval Initialization success status Success: MW_FAIL Failure: MW_SUCCESS
  */
int8_t MW_UART_Init_All()
{<!-- -->
if(MW_UART_Init( & amp;E2K_UartAttr, & amp;huart3, E2KRxDma, E2KTxDma) & amp; & amp; \
MW_UART_Init( & amp;FPGA_UartAttr, & amp;huart2, FPGARxDma, FPGATxDma) & amp; & amp; \
MW_UART_Init( & amp;Dbg_UartAttr, & amp;huart1, DbgRxDma, DbgTxDma))
{<!-- -->
return MW_SUCCESS;
}

return MW_FAIL;
}


/**
  * @brief Send data using a circular queue of instructions.
  * @param sUartAttr maintains the structure of the serial port circular queue, including the send and receive buffer fifo, dma offset, sending status, and dma buffer size
  * @param buffer send data buffer address
  * @param len sending length size
  * @retval Successful sending length size
  */
int32_t MW_UART_Transmit(MW_UART_ATTR *sUartAttr, uint8_t* buffer,int32_t len)
{<!-- -->
int32_t TransNum = 0;
int32_t TransLen = 0;
MW_UART_ATTR *pUartAttr = sUartAttr;
/*Write the data to be sent into the circular buffer first*/
TransNum = CfifoBuff_Write( & amp;pUartAttr->SendCFifo, (char *) buffer, len);
\t\t
/*If the sending DMA is not sending, enable sending*/
if(pUartAttr->TransFlag == MW_TRANS_IDLE)
{<!-- -->
TransLen = CfifoBuff_Read( & amp;pUartAttr->SendCFifo,(char *)(pUartAttr->pWriteDma),pUartAttr->DmaSize);
if(TransLen > 0)
{<!-- -->
pUartAttr->TransFlag = MW_TRANS_BUSY;
if(HAL_OK != HAL_UART_Transmit_DMA(pUartAttr->handle,pUartAttr->pWriteDma,TransLen))
{<!-- -->
printf("Uart Trans_DMA failed\r\
");
}
}
}
return TransNum;
}
 

cfifo_usart_dma.c

#ifndef _CFIFO_USART_DMA_H_
#define _CFIFO_USART_DMA_H_
 
#ifdef __cplusplus
extern "C" {<!-- -->
#endif
 
/* Includes ----------------------------------------------- ------------------*/
#include "main.h"
 
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "cfifo.h"
#include "usart.h"
/* USER CODE END Includes */
 
/* USER CODE BEGIN Private defines */
 
#defineMW_FAIL 0
#defineMW_SUCCESS 1

#define MW_TRANS_IDLE 0
#define MW_TRANS_BUSY 1

#define MW_UART_DMA_LEN 256
 
/* USER CODE END Private defines */
typedef struct
{<!-- -->
UART_HandleTypeDef *handle; /*Serial port handle provided by HAL library*/
int16_t TransFlag; /*data transmission flag*/
int32_t DmaSize; /*size of DMA buffer*/
int32_t DamOffset; /*Get the offset of the data in the DMA buffer*/
uint8_t *pReadDma; /*Points to the first address of the receive DMA buffer*/
uint8_t *pWriteDma; /*Points to the first address of the sending DMA buffer*/
CfifoBuff AcceptCFifo; /*Circular buffer that accepts data*/
CfifoBuff SendCFifo; /*Circular buffer for sending data*/
}MW_UART_ATTR;
 

extern MW_UART_ATTR E2K_UartAttr;
extern MW_UART_ATTR FPGA_UartAttr;
extern MW_UART_ATTR Dbg_UartAttr;

 
/* USER CODE BEGIN Prototypes */
/**
  * @brief Initializes the circular queue maintained by all serial ports
  * @param none
  * @retval Initialization success status Success: MW_FAIL Failure: MW_SUCCESS
  */
int8_t MW_UART_Init_All(void);
/**
  * @brief Initialize the circular queue maintained by the serial port
  * @param sUartAttr maintains the structure of the serial port circular queue, including the send and receive buffer fifo, dma offset, sending status, and dma buffer size
  * @param huart maintains the serial port number huartx(x = 1.2.3) defined in usart.c
  * @param RxDma DMA receive data buffer
  * @param TxDma DMA receive data buffer
  * @retval Initialization success status Success: MW_FAIL Failure: MW_SUCCESS
  */
int8_t MW_UART_Init(MW_UART_ATTR *sUartAttr, UART_HandleTypeDef *huart, uint8_t *RxDma, uint8_t *TxDma);


/**
  * @brief Send data using a circular queue of instructions.
  * @param sUartAttr maintains the structure of the serial port circular queue, including the send and receive buffer fifo, dma offset, sending status, and dma buffer size
  * @param buffer send data buffer address
  * @param len sending length size
  * @retval Successful sending length size
  */
int32_t MW_UART_Transmit(MW_UART_ATTR *sUartAttr, uint8_t* buffer,int32_t len);


void MW_UART_IRQHandler(UART_HandleTypeDef *huart); //Called during serial port interrupt

/* USER CODE END Prototypes */
 
#ifdef __cplusplus
}
#endif
#endif /* _CFIFO_USART_DMA_H_ */
 

3. Configure the specific location and steps of the buffer fifo code file.

Step one-modify fifo.h

In cfifo.h, the most changed thing is to modify the buffer size of line 18, and other changes are not big

Step one-modify cfifo_usart_dma.c

In cfifo_usart_dma., you need to initialize it according to the number of serial ports you use. Change the places as follows:

1. According to the number of serial ports, enable the DMA buffer for each serial port’s sending and receiving. It is actually a static global array, and you can add or reduce it by yourself (lines 6~13), as shown below

2. According to the number of serial ports, define a maintained structure for each serial port, such as MW_UART_ATTR Dbg_UartAttr; Add and reduce by yourself (lines 15~17), as shown below

3. According to the number of serial ports, perform initial binding and add and reduce by yourself (lines 60~63), as shown below

Step 3-Modify cfifo_usart_dma.h

1. Expand extern to other files according to the number of maintenance serial ports defined in cfifo_usart_dma.c. Add and reduce cfifo_usart_dma.h (lines 42~44) by yourself, as shown below

This ends the configuration of fifo. The next step is to call and use it in the entire project

5. Project configuration binding usage

Step one-modify rewrite_it.c

#include "rewrite_it.h"
#include "usart.h"



MW_UART_ATTR *CaseUsart(UART_HandleTypeDef *huart)
{<!-- -->
MW_UART_ATTR *ret = NULL;
\t
if(huart->Instance == huart1.Instance)
{<!-- -->
ret = &UART0_UartAttr;
}
else if(huart->Instance == huart2.Instance)
{<!-- -->
ret = &UART2_UartAttr;
}
else if(huart->Instance == huart4.Instance)
{<!-- -->
ret = &FPGA_UartAttr;
}
else if(huart->Instance == huart6.Instance)
{<!-- -->
ret = &Dbg_UartAttr;
}
else
{<!-- -->
;
}
 
return ret;
}
/**
  * @brief Serial port transmission completion callback function
  * @param huart: Serial port handle.
* @retval None
*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{<!-- -->
int32_t TransNum = 0;
MW_UART_ATTR *pUartAttr = NULL;
\t\t
\t\t
pUartAttr = CaseUsart(huart);
\t

/*Get data from the send circular buffer*/
TransNum = CfifoBuff_Read( & amp;pUartAttr->SendCFifo,(char *)(pUartAttr->pWriteDma),pUartAttr->DmaSize);
if(TransNum > 0)
{<!-- -->
if(HAL_OK != HAL_UART_Transmit_DMA(pUartAttr->handle,pUartAttr->pWriteDma,TransNum))
{<!-- -->
printf("Uart Trans_DMA failed\r\
");
}
}
else
{<!-- -->
pUartAttr->TransFlag = MW_TRANS_IDLE;
}
 }

/**
  * @brief The serial port reception completion callback function resets the DMA offset. This function will be called when the DMA reception is completed.
  * @param huart: Serial port handle.
  * @retval None
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{<!-- -->
int32_t DmaLen = 0;
int32_t WriteNum = 0;
\t\t
MW_UART_ATTR *pUartAttr = NULL;
\t\t
\t\t
pUartAttr = CaseUsart(huart);
\t\t
/*Calculate the length of data to be obtained*/
DmaLen = pUartAttr->DmaSize - pUartAttr->DamOffset;
/*Store the obtained data into the data buffer*/
WriteNum = CfifoBuff_Write( & amp;pUartAttr->AcceptCFifo,(char *)(pUartAttr->pReadDma + pUartAttr->DamOffset),DmaLen);
\t\t
if(WriteNum != DmaLen)
{<!-- -->
printf("Uart ReadFifo is not enough\r\
");
}
/*Reset DMA offset*/
pUartAttr->DamOffset = 0;
}

/**
  * @brief The serial port interrupt call function handles idle interrupt data, writes the data to the buffer, and maintains the circular queue size and head and tail pointers.
  * @param huart: Serial port handle.
  * @retval None
*/
void MW_UART_IRQHandler(UART_HandleTypeDef *huart)
{<!-- -->
int32_t RecvNum = 0;
int32_t WriteNum = 0;
int32_t DmaIdleNum = 0;
\t\t
MW_UART_ATTR *pUartAttr = NULL;
\t\t
pUartAttr = CaseUsart(huart);
\t\t 
if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
{<!-- -->
/*Clear the idle interrupt flag bit and re-accept the serial port idle interrupt*/
__HAL_UART_CLEAR_IDLEFLAG(huart);
\t\t\t\t
/*Calculate the length of data to be obtained in the DMA buffer*/
DmaIdleNum = __HAL_DMA_GET_COUNTER(huart->hdmarx);
RecvNum = pUartAttr->DmaSize - DmaIdleNum - pUartAttr->DamOffset;
/*Put the obtained data into the data receiving buffer*/
WriteNum = CfifoBuff_Write( & amp;pUartAttr->AcceptCFifo,(char *)(pUartAttr->pReadDma + pUartAttr->DamOffset),RecvNum);
\t\t\t\t
if(WriteNum != RecvNum)
{<!-- -->
printf("Uart ReadFifo is not enough\r\
");
}
/*Calculate the offset of the data location*/
pUartAttr->DamOffset + = RecvNum;
//
// printf("%s",pUartAttr.AcceptCFifo.BUFF);
//
// CfifoBuff_Clear( & amp;pUartAttr.AcceptCFifo);
}
}
 

*For the rewrite_it file, the main thing that needs to be modified is the MW_UART_ATTR *CaseUsart(UART_HandleTypeDef huart) function. Select the maintenance serial port according to the serial port situation, and modify the function as needed. The function is as shown below:

Step 2 – Modify rewrite_it.h

#ifndef _REWRITE_IT_
#define _REWRITE_IT_
 
/**
  * @brief Serial port transmission completion callback function
  * @param huart: Serial port handle.
* @retval None
*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
/**
  * @brief Serial port reception completion callback function
  * @param huart: Serial port handle.
  * @retval None
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
/**
  * @brief The serial port interrupt call function handles idle interrupt data, writes the data to the buffer, and maintains the circular queue size and head and tail pointers.
  * @param huart: Serial port handle.
  * @retval None
*/
void MW_UART_IRQHandler(UART_HandleTypeDef *huart);

#endif

Step 3 – Modify the stm32f4xx_it.c file

Add MW_UART_IRQHandler( & amp;huartx); to the interrupt function UARTx_IRQHandler or USARTx_IRQHandler function

At this point, all added configurations have been completed.

4. Usage examples

{<!-- -->
char sbuf[128];
char sbuf[128];
MW_UART_Transmit( & amp;UART2_UartAttr, (uint8_t *)sbuf, len);
}