①定义发送和接送的缓冲区
#define UART_BUFFSIZE 200 // 定义缓冲区的大小
typedef struct
{
u16 Uart_SendLens; //待发送数据长度
u16 Uart_RecvLens; //接收到的数据长度
u16 RecvQue_Head; //新接收数据环形队列头指针
u16 RecvQue_Tail; //新接收数据环形队列尾指针
u8 Uart_SentBuff[UART_BUFFSIZE];
u8 Uart_RecvBuff[UART_BUFFSIZE];
}UART_STR;
②串口初始化配置修改
UART_STR Uart1_Str,Uart2_Str,Uart3_Str,Uart4_Str,Uart5_Str; // 定义串口发送接收缓冲区
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
//以下为自己添加的中断配置
/* USER CODE BEGIN USART1_Init 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 开启空闲中断
// 接收DMA通道关联缓冲区
HAL_UART_Receive_DMA(&huart1, Uart1_Str.Uart_RecvBuff, UART_BUFFSIZE);
// 以下这两个中断最好关掉,不然debug的时候会莫名其妙进中断,DMA发送不了
__HAL_UART_DISABLE_IT(&huart1, UART_IT_ERR);
__HAL_UART_DISABLE_IT(&huart1, UART_IT_PE);
/* USER CODE END USART1_Init 2 */
}
③发送函数
/*
函数功能:串口1DMA数据发送
函数形参:Sendbuff :缓冲数据
Bufflens :数据长度
函数返回值:数据长度
备注:无
*/
u16 Uart1_DMA_Sent(u8 * Sendbuff, u16 Bufflens)
{
u16 l_val = Bufflens > UART_BUFFSIZE ? UART_BUFFSIZE : Bufflens;
if(Bufflens <= 0)
{
return 0;
}
while(__HAL_DMA_GET_COUNTER(&hdma_usart1_tx));//检测DMA发送通道内还有没有数据
if(Sendbuff)
{
memcpy(Uart1_Str.Uart_SentBuff, Sendbuff, l_val);
}
HAL_UART_Transmit_DMA(&huart1, Uart1_Str.Uart_SentBuff, l_val);
return l_val;
}
④接收中断处理
/*
*函数功能:串口1接收中断函数
*/
void USART1_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)
{
u16 l_val;
l_val = UART_BUFFSIZE - DMA1_Channel5->CNDTR; // 通过DMA接收指针计算接收的字节数
if(l_val > Uart1_Str.RecvQue_Tail)
{
Uart1_Str.Uart_RecvLens += l_val - Uart1_Str.RecvQue_Tail;
}
else
{
Uart1_Str.Uart_RecvLens += UART_BUFFSIZE - Uart1_Str.RecvQue_Tail + l_val;
}
Uart1_Str.RecvQue_Tail = l_val;
Uart1_Str.Uart_RecvLens %= UART_BUFFSIZE;
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
}
HAL_UART_IRQHandler(&huart1); // 这一条保留就行了
}
⑤接收函数
/*
函数功能:接收数据函数
函数形参:* Uart_Str : 串口数据缓冲结构地址
RcvBuff :接收数据缓冲区
RevLen :接收缓冲区长度
函数返回值:接收数据长度
备注:无
*/
static u16 Uart_Receive_Data(UART_STR * Uart_Str, u8 * RcvBuff, u16 RevLen)
{
u16 l_val = 0; // 局部变量 此次能拷贝的数据个数
if(!RevLen || !Uart_Str->Uart_RecvLens)
{
return 0;
}
l_val = Uart_Str->Uart_RecvLens;
if(l_val > RevLen) // 缓冲区的数据数量比传进来的缓冲区容量大
{
l_val = RevLen;
}
if(Uart_Str->RecvQue_Tail > Uart_Str->RecvQue_Head)
{
memcpy(RcvBuff, Uart_Str->Uart_RecvBuff + Uart_Str->RecvQue_Head, l_val);
}
else
{
if((UART_BUFFSIZE - Uart_Str->RecvQue_Head) >= l_val)
{
memcpy(RcvBuff, Uart_Str->Uart_RecvBuff + Uart_Str->RecvQue_Head, l_val);
}
else
{
memcpy(RcvBuff, Uart_Str->Uart_RecvBuff + Uart_Str->RecvQue_Head, UART_BUFFSIZE - Uart_Str->RecvQue_Head);
memcpy(RcvBuff + (UART_BUFFSIZE - Uart_Str->RecvQue_Head), Uart_Str->Uart_RecvBuff, l_val - (UART_BUFFSIZE - Uart_Str->RecvQue_Head));
}
}
Uart_Str->Uart_RecvLens -= l_val;
Uart_Str->RecvQue_Head += l_val;
Uart_Str->RecvQue_Head %= UART_BUFFSIZE;
return l_val;
}
/*
函数功能:从串口获取数据
函数形参:* Uartx :串口地址
RcvBuff :接收缓冲指针
RevLen :接收缓冲区大小
函数返回值:接收数据长度
备注:无
*/
u16 Get_Uart_Data(USART_TypeDef* Uartx,u8 * RcvBuff, u16 RevLen)
{
if(Uartx == USART1)
{
return(Uart_Receive_Data(&Uart1_Str, RcvBuff, RevLen));
}
else if(Uartx == USART2)
{
return(Uart_Receive_Data(&Uart2_Str, RcvBuff, RevLen));
}
else if(Uartx == USART3)
{
return(Uart_Receive_Data(&Uart3_Str, RcvBuff, RevLen));
}
else if(Uartx == UART4)
{
return(Uart_Receive_Data(&Uart4_Str, RcvBuff, RevLen));
}
else if(Uartx == UART5)
{
return(Uart_Receive_Data(&Uart5_Str, RcvBuff, RevLen));
}
return 0;
}
总结:发送采用单次的DMA发送,而接收则采用DMA循环接收,只要串口有收到数据DMA会自动帮你搬到内存中,地址会自动增加,当接收的数据个数到达设置的最大值时,DMA的接收指针又会重新指向接收buf的头指针。调用Get_Uart_Data()函数,会将最近接收的数据拷贝返回,拷贝的数据个数取决于接收的buf还有串口接收到的数据。这里只写出一个串口,除了串口5(没有DMA),其他的三个串口都可以参照上面写。