IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> stm32f103串口接收队列,DMA循环模式+空闲中断(2) -> 正文阅读

[嵌入式]stm32f103串口接收队列,DMA循环模式+空闲中断(2)

之前有过一篇文章,不过那个只支持1个串口,而stm32f103大容量有5个串口,写这一篇的目的正是为了支持所有串口,可通过宏定义USE_USARTx进行设置使用串口的情况。关于基础部分,主要是原理的讲解可参考:https://blog.csdn.net/qq_40831436/article/details/115071656?spm=1001.2014.3001.5501

串口初始化讲解

初始化说明:5个串口并非所有串口都支持DMA传输,由下表所知,只有串口1-4具备DMA,而串口5无,所以,本程序中,串口1-4接收数据使用DMA传输,而串口5使用接收字节中断。
在这里插入图片描述
在这里插入图片描述
以下以串口1初始化为例:通过宏定义USE_USART1条件编译串口1初始化

#if  USE_USART1
	/*串口x使用到的全局变量初始化*/
	USARTx_Var_Init(&U1RxBuff,&g_U1RxFra);
	/**打开串口时钟**/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	//配置接收发送引脚
	USARTx_AFPP_GPIO_Config(USART1TX_GPIO_CLK,USART1TX_GPIO_PORT,USART1TX_GPIO_Pin);
	USARTx_FloatIN_GPIO_Config(USART1RX_GPIO_CLK,USART1RX_GPIO_PORT,USART1RX_GPIO_Pin);
	//串口工作模式配置
	USARTx_WorkMode_Config(USART1,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
	//串口中断配置
	USARTx_NVIC_Config(USART1_IRQn,1,1);
	// 配置串口空闲中断
	USART_ClearITPendingBit(USART1,USART_IT_IDLE);
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);	
	// 使能串口
	USART_Cmd(USART1, ENABLE);
	//串口接收DMA配置
	USARTx_RxDMA_Config(USART1,DMA1_Channel5,RCC_AHBPeriph_DMA1,(USART1_BASE+0x04),(uint32_t)U1RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
	#endif

通过初始化之后,串口1接收缓冲区为:U1RxBuff.rxarr,即通过USARTx_RxDMA_Config函数进行配置。
而U1RxBuff定义如下:

#if USE_USART1
_USARTxRXBUFF U1RxBuff;  //定义串口接收缓冲区
_FRAMEATTRI   g_U1RxFra;   //定义帧记录指针缓冲区,非真指针
#endif

同样是通过条件编译,每个串口具备独立的串口接收缓冲区,互不干涉。

串口接收讲解

接收具备两种情况,一种是USART1-4,DMA接收,不用CPU干预,只需等待接收完成,进入空闲中断。而在空闲中断中的处理如下:
1、得到当前帧写入的结束地址 pRxBuff->wp;
2、将当前帧的起始地址 rp 赋值给帧记录队列 rpx进行记录
3、将当前帧的结束地址 wp 赋值给帧记录队列 wpx 进行记录
4、帧记录队列+1,等待前台处理数据
5、将 wp 赋值给 rp,一边记录下一个帧

/**************************************
*函 数 名: USARTxIDLE_IRQ
*功    能: 串口x空闲中断调用函数;仅串口1-4
*入口参数: pUSARTx--USARTx
            DMAy_Channelx--串口通道
            pRxBuff--串口接收缓冲区
            pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
**************************************/
static void USARTx_IDLE_IRQ(USART_TypeDef * pUSARTx,DMA_Channel_TypeDef* DMAy_Channelx,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
	uint16_t trnum=0;
	//清除空闲中断标志位
	pUSARTx->SR;
	pUSARTx->DR;
	//手册虽然说这个寄存器在DMA循环模式的时候,清0之后会自动恢复为最大接收缓冲区,但加入这一步以防万一
	if(DMAy_Channelx->CNDTR == 0) 
	{
		trnum = UxRXBUFFSIZE;
	}
	else
	{
		trnum = DMAy_Channelx->CNDTR&0xffff;
	}
	pRxBuff->wp = UxRXBUFFSIZE-trnum;  //得到最新帧的结束地址
	pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp;  //最新帧的起始地址
	pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp;  //最新帧的结束地址
	pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
	pRxBuff->rp = pRxBuff->wp;  //最新帧的起始与结束地址记录完,等待下一次记录
}

之后再空闲中断中调用 USARTxIDLE_IRQ()–调用–>USARTx_IDLE_IRQ()

/**************************************
*函 数 名: USARTxIDLE_IRQ
*功    能: 串口x空闲中断调用函数;串口1-5
*入口参数: Com_USARTx--定义好的串口通道
*返 回 值: 无
*说    明:串口5除了这个之外还要调用串口接收字节处理
**************************************/
void USARTxIDLE_IRQ(uint8_t Com_USARTx)
{
	switch(Com_USARTx)
	{
		#if  USE_USART1
		case Com_USART1:
			USARTx_IDLE_IRQ(USART1,USART1_RxDMA_CHANNEL,&U1RxBuff,&g_U1RxFra);
		break;
		#endif
		#if  USE_USART2
		case Com_USART2:
			USARTx_IDLE_IRQ(USART2,USART2_RxDMA_CHANNEL,&U2RxBuff,&g_U2RxFra);
		break;
		#endif
		#if  USE_USART3
		case Com_USART3:
			USARTx_IDLE_IRQ(USART3,USART3_RxDMA_CHANNEL,&U3RxBuff,&g_U3RxFra);
		break;
		#endif
		#if  USE_USART4
		case Com_USART4:
			USARTx_IDLE_IRQ(UART4,USART4_RxDMA_CHANNEL,&U4RxBuff,&g_U4RxFra);
		break;
		#endif
		#if  USE_USART5
		case Com_USART5:
			UART5_IDLE_IRQ(&U5RxBuff,&g_U5RxFra);
		break;
		#endif
		default:
			
		break;
	}
}

接收的第二种情况是无DMA接收,需要CPU进行干预,则通过利用串口接收中断:
串口接收字节数据处理如下:

/**************************************
*函 数 名: USART5_RXNE_IRQ
*功    能: 串口5接收一个字节
*入口参数: pRxBuff--串口接收缓冲区
*返 回 值: 无
*说    明: 
**************************************/
static void USART5_RXNE_IRQ(_USARTxRXBUFF *pRxBuff)
{
	pRxBuff->rxarr[pRxBuff->wp] = USART_ReceiveData(UART5);
	pRxBuff->wp = (pRxBuff->wp+1)%UxRXBUFFSIZE; //pRxBuff->wp的值被限制再0,1....(UxRXBUFFSIZE-1)
}

在串口接收中断中通过调用 USART5RXNE_IRQ()–调用–>USART5_RXNE_IRQ()接收数据。

/**************************************
*函 数 名: USART5RXNE_IRQ
*功    能: 串口5接收字节中断调用函数;串口5专用
*入口参数: 无
*返 回 值: 无
*说    明: 在接收字节中断服务函数中调用
**************************************/
void USART5RXNE_IRQ(void)
{
	USART_ClearITPendingBit(UART5,USART_IT_RXNE);
	USART5_RXNE_IRQ(&U5RxBuff);
}

对于无DMA的空闲中断中调用的函数处理如下:

/**************************************
*函 数 名: UART5_IDLE_IRQ
*功    能: 串口5空闲中断处理
*入口参数: pRxBuff--串口接收缓冲区
            pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
*说    明: 
**************************************/
static void UART5_IDLE_IRQ(_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
	UART5->SR;
	UART5->DR;
	pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp;  //最新帧的起始地址
	pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp;  //最新帧的结束地址
	pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
	pRxBuff->rp = pRxBuff->wp;  //最新帧的起始与结束地址记录完,等待下一次记录
}

在中断中调用情况如下:

// 串口中断服务函数
#if USE_USART1
void USART1_IRQHandler(void)
{
	
	if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART1);
	}	 
}
#endif
// 串口中断服务函数
#if USE_USART2
void USART2_IRQHandler(void)
{
	
	if(USART_GetITStatus(USART2,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART2);
	}	 
}
#endif

// 串口中断服务函数
#if USE_USART3
void USART3_IRQHandler(void)
{
	
	if(USART_GetITStatus(USART3,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART3);
	}	 
}
#endif
// 串口中断服务函数
#if USE_USART4
void UART4_IRQHandler(void)
{
	
	if(USART_GetITStatus(UART4,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART4);
	}	 
}
#endif
#if USE_USART5
// 串口中断服务函数
void UART5_IRQHandler(void)
{
	if(USART_GetITStatus(UART5,USART_IT_RXNE)!=RESET)
	{		
		
		USART5RXNE_IRQ();
	}	
	if(USART_GetITStatus(UART5,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART5);
	}	 
}
#endif

数据读取讲解

数据读取可参考先前文章,先判断是否有新的数据帧产生,即通过 currfra与 nextfra两个变量进行记录,其中nextfra变量在串口空闲中断中会+1,而当当前帧数据被应用层取出时:currfra+1。当currfra == nextfra时,说明所有的数据帧都已读取完毕。
从串口接收缓冲区中读取当前处理帧数据主要分为两种情况
一种是rpx<wpx,直接读取即可;
一种是wpx<rpx,说明接收时发生了接收缓冲区队列翻转情况,需要先读取:第一部分为rpx-(UxRXBUFFSIZE-1)的数据,第二部分为0-wpx的数据。

/**************************************
*函 数 名: GetAFraFromUxRxBuff
*功    能: 获取一帧数据
*入口参数: pbuff--获取一帧数据的数组
            psize--获取的数目
            pRxBuff--串口x缓冲区
            pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说    明: 本函数没有进行地址超出判断,一定要确保你的数据帧的空间小于你定下的 pbuff 的空间
**************************************/
static uint8_t GetAFraFromUxRxBuff(uint8_t *pbuff,uint8_t *psize,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxFra)
{
	uint8_t rtflg=0;  //返回值
	uint16_t fralen=0;  //帧长度
	if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
	{
		/*根据每帧的帧属性(起始与结束地址)在串口接收缓冲区主体中获取一帧数据*/
		if(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
		{
			//本帧写入的起始地址小于写入的结束地址,直接使用写入结束地址-写入起始地址,计算出本帧的长度
			fralen = pUxFra->fraddr[pUxFra->currfra].wpx-pUxFra->fraddr[pUxFra->currfra].rpx;
			for((*psize)=0;(*psize)<fralen;(*psize)++)
			{
				//从串口接收缓冲区主体中取出本帧的数据,索引为:pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)
				pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)];
			}
			//数据取出之后,本帧的.rpx(本帧起始的写入地址)=本帧的.wpx(本帧写入的结束地址)
			pUxFra->fraddr[pUxFra->currfra].rpx=pUxFra->fraddr[pUxFra->currfra].wpx;
		}
		else
		{
			//本帧写入的起始地址大于写入的结束地址,说明数据在缓冲区中进行了溢出翻转,分两部分读取
			//第一部分为rpx-(UxRXBUFFSIZE-1)的数据,第二部分为0-wpx的数据
			for((*psize)=0;pUxFra->fraddr[pUxFra->currfra].rpx<UxRXBUFFSIZE;pUxFra->fraddr[pUxFra->currfra].rpx++)
			{
				//读取第一部分数据
				pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
				(*psize)++;
			}
			pUxFra->fraddr[pUxFra->currfra].rpx = 0;
			//读取第二部分数据
			while(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
			{
				pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
				(*psize)++;
				pUxFra->fraddr[pUxFra->currfra].rpx++;
			}
			
		}
		//当前帧数据读取结束,currfra(当前处理的帧+1)----0..(FRADDRMAX-1)
		pUxFra->currfra = (pUxFra->currfra+1)%FRADDRMAX;
		//读取到数据,返回值赋1
		rtflg = 1; 
	}
	return rtflg;
}

而应用层实际调用函数为:GetAFraFromComx()–调用–>GetAFraFromUxRxBuff()

/**************************************
*函 数 名: GetAFraFromComx
*功    能: 从COM口获取一帧数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
            pbuff--获取一帧数据的数组
            psize--获取的数目
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说    明:
**************************************/
uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize)
{
	uint8_t rtflg=0;  //返回值
	switch(Com_USARTx)
	{
		#if  USE_USART1
		case Com_USART1:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U1RxBuff,&g_U1RxFra);
		break;
		#endif
		#if  USE_USART2
		case Com_USART2:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U2RxBuff,&g_U2RxFra);
		break;
		#endif
		#if  USE_USART3
		case Com_USART3:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U3RxBuff,&g_U3RxFra);
		break;
		#endif
		#if  USE_USART4
		case Com_USART4:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U4RxBuff,&g_U4RxFra);
		break;
		#endif
		#if  USE_USART5
		case Com_USART5:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U5RxBuff,&g_U5RxFra);
		break;
		#endif
		default:
			
		break;
	}
	return rtflg;
}

而判断是否有新的未处理的数据帧可通过以下函数,(一般在OS中才使用本函数),裸机可直接通过调用GetAFraFromComx()函数判断。

/**************************************
*函 数 名: JudgeUxFrameDataNum
*功    能: 判断是否有未处理的数据帧
*入口参数: pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说    明:
**************************************/
static uint8_t JudgeUxFrameDataNum(_FRAMEATTRI *pUxFra)
{
	uint8_t rtflg=0;
	if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
	{
		rtflg = 1;
	}
	else
	{
		rtflg = 0;
	}
	return rtflg;
}
/**************************************
*函 数 名: JudgeUxFraIsNull
*功    能: 判断当前COM口是否还有未处理的数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说    明:
**************************************/
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx)
{
	uint8_t rtflg=0;  //返回值
	switch(Com_USARTx)
	{
		#if  USE_USART1
		case Com_USART1:
			rtflg = JudgeUxFrameDataNum(&g_U1RxFra);
		break;
		#endif
		#if  USE_USART2
		case Com_USART2:
			rtflg = JudgeUxFrameDataNum(&g_U2RxFra);
		break;
		#endif
		#if  USE_USART3
		case Com_USART3:
			rtflg = JudgeUxFrameDataNum(&g_U3RxFra);
		break;
		#endif
		#if  USE_USART4
		case Com_USART4:
			rtflg = JudgeUxFrameDataNum(&g_U4RxFra);
		break;
		#endif
		#if  USE_USART5
		case Com_USART5:
			rtflg = JudgeUxFrameDataNum(&g_U5RxFra);
		break;
		#endif
		default:
			
		break;
	}
	return rtflg;
}

使用说明

简单使用本库只需知道一下宏定义与调用以下几个函数即可。
一、宏定义:
想要使用哪个串口只需将 USE_USARTx 宏定义为1即可
而要是使用中断优先级分组则将 NeedSet_NVICprio 宏定义为1,默认分组2

/*串口使用情况,置1代表使用此串口*/
#define  USE_USART1    1
#define  USE_USART2    1
#define  USE_USART3    1
#define  USE_USART4    1
#define  USE_USART5    1

#define  NeedSet_NVICprio    1       //1:需要设置中断优先级分组,默认分组2  0:不需要设置中断优先级分组

二、使用函数说明:

void USARTx_Config(void);  //串口初始化配置

void USARTxIDLE_IRQ(uint8_t Com_USARTx);  //串口空闲中断处理函数,空闲中断调用
void USART5RXNE_IRQ(void);  //串口5接收字节中断处理,串口5专用,串口5接收字节中断调用
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);  //串口发送函数

uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize);  //从COM1..5口获取一帧数据
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx);  //判断COM1..5口是否还有未处理数据

完整代码

bsp_usart_dma.h

/*******************************************************
*设计:陈文德
*版本:V1.0
*版本:V1.1版本:5串口全部支持
*说明:通过设置USE_USART1..5,可以选择使用哪些串口
       通过设置NeedSet_NVICprio可以选择是否进行中断优先级分组,默认分组2
*******************************************************/
#ifndef __USARTDMA_H
#define	__USARTDMA_H


#include "stm32f10x.h"
#include <stdio.h>
/*串口使用情况,置1代表使用此串口*/
#define  USE_USART1    0
#define  USE_USART2    1
#define  USE_USART3    0
#define  USE_USART4    0
#define  USE_USART5    0

#define  NeedSet_NVICprio    1       //1:需要设置中断优先级分组,默认分组2  0:不需要设置中断优先级分组


/*给5个串口编号*/
#define  Com_USART1    1
#define  Com_USART2    2
#define  Com_USART3    3
#define  Com_USART4    4
#define  Com_USART5    5

#ifdef   USE_USART1
/*串口1发送引脚定义*/
#define  USART1TX_GPIO_CLK     RCC_APB2Periph_GPIOA
#define  USART1TX_GPIO_PORT    GPIOA
#define  USART1TX_GPIO_Pin     GPIO_Pin_9
/*串口1接收引脚定义*/
#define  USART1RX_GPIO_CLK     RCC_APB2Periph_GPIOA
#define  USART1RX_GPIO_PORT    GPIOA
#define  USART1RX_GPIO_Pin     GPIO_Pin_10

#define  USART1_RxDMA_CHANNEL     DMA1_Channel5
#endif

#ifdef   USE_USART2
/*串口2发送引脚定义*/
#define  USART2TX_GPIO_CLK     RCC_APB2Periph_GPIOA
#define  USART2TX_GPIO_PORT    GPIOA
#define  USART2TX_GPIO_Pin     GPIO_Pin_2
/*串口2接收引脚定义*/
#define  USART2RX_GPIO_CLK     RCC_APB2Periph_GPIOA
#define  USART2RX_GPIO_PORT    GPIOA
#define  USART2RX_GPIO_Pin     GPIO_Pin_3

#define  USART2_RxDMA_CHANNEL     DMA1_Channel6
#endif

#ifdef   USE_USART3
/*串口3发送引脚定义*/
#define  USART3TX_GPIO_CLK     RCC_APB2Periph_GPIOB
#define  USART3TX_GPIO_PORT    GPIOB
#define  USART3TX_GPIO_Pin     GPIO_Pin_10
/*串口3接收引脚定义*/
#define  USART3RX_GPIO_CLK     RCC_APB2Periph_GPIOB
#define  USART3RX_GPIO_PORT    GPIOB
#define  USART3RX_GPIO_Pin     GPIO_Pin_11

#define  USART3_RxDMA_CHANNEL     DMA1_Channel3
#endif

#ifdef   USE_USART4
/*串口4发送引脚定义*/
#define  USART4TX_GPIO_CLK     RCC_APB2Periph_GPIOC
#define  USART4TX_GPIO_PORT    GPIOC
#define  USART4TX_GPIO_Pin     GPIO_Pin_10
/*串口4接收引脚定义*/
#define  USART4RX_GPIO_CLK     RCC_APB2Periph_GPIOC
#define  USART4RX_GPIO_PORT    GPIOC
#define  USART4RX_GPIO_Pin     GPIO_Pin_11

#define  USART4_RxDMA_CHANNEL     DMA2_Channel3
#endif

#ifdef   USE_USART5
/*串口5发送引脚定义*/
#define  USART5TX_GPIO_CLK     RCC_APB2Periph_GPIOC
#define  USART5TX_GPIO_PORT    GPIOC
#define  USART5TX_GPIO_Pin     GPIO_Pin_12
/*串口5接收引脚定义*/
#define  USART5RX_GPIO_CLK     RCC_APB2Periph_GPIOD
#define  USART5RX_GPIO_PORT    GPIOD
#define  USART5RX_GPIO_Pin     GPIO_Pin_2
#endif
 

#define  UxRXBUFFSIZE       512    //接收缓冲区的大小
/**串口接收缓冲区**/
typedef struct __USARTxRXBUFF
{
	uint16_t wp;  //当前接收帧在接收缓冲区所处的写地址
	uint16_t rp;  //当前接收帧在接收缓冲区所处的读地址
	uint8_t  rxarr[UxRXBUFFSIZE];  //接收缓冲区实体
}_USARTxRXBUFF;

/**帧地址结构体**/
typedef struct __FRAMEADDR
{
	uint16_t wpx;  //本帧写地址的索引
	uint16_t rpx;  //本帧读地址的索引
}_FRAMEADDR;

#define  FRADDRMAX  10  //最多能记录的帧
/**帧属性结构体**/
typedef struct __FRAMEATTRI
{
	_FRAMEADDR fraddr[FRADDRMAX];  //每帧的地址,即通过wpx记录wp, rpx记录rp。队列主体
	uint8_t currfra;  //当前处理帧。0-(FRADDRMAX-1)
	uint8_t nextfra;  //下一个帧。0-(FRADDRMAX-1)
}_FRAMEATTRI;



void USARTx_Config(void);  //串口初始化配置

void USARTxIDLE_IRQ(uint8_t Com_USARTx);  //串口空闲中断处理函数,空闲中断调用
void USART5RXNE_IRQ(void);  //串口5接收字节中断处理,串口5专用,串口5接收字节中断调用
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);  //串口发送函数

uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize);  //从COM1..5口获取一帧数据
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx);  //判断COM1..5口是否还有未处理数据
#endif /* __USARTDMA_H */

bsp_usart_dma.c

/*******************************************************
*设计:陈文德
*版本:V1.0
*版本:V1.1版本:增加USART1..5
*******************************************************/
#include "bsp_usart_dma.h"
#if USE_USART1
_USARTxRXBUFF U1RxBuff;  //定义串口接收缓冲区
_FRAMEATTRI   g_U1RxFra;   //定义帧记录指针缓冲区,非真指针
#endif

#if USE_USART2
_USARTxRXBUFF U2RxBuff;  //定义串口接收缓冲区
_FRAMEATTRI   g_U2RxFra;   //定义帧记录指针缓冲区,非真指针
#endif

#if USE_USART3
_USARTxRXBUFF U3RxBuff;  //定义串口接收缓冲区
_FRAMEATTRI   g_U3RxFra;   //定义帧记录指针缓冲区,非真指针
#endif

#if USE_USART4
_USARTxRXBUFF U4RxBuff;  //定义串口接收缓冲区
_FRAMEATTRI   g_U4RxFra;   //定义帧记录指针缓冲区,非真指针
#endif

#if USE_USART5
_USARTxRXBUFF U5RxBuff;  //定义串口接收缓冲区
_FRAMEATTRI   g_U5RxFra;   //定义帧记录指针缓冲区,非真指针
#endif


/**************************************
*函 数 名: USARTx_Var_Init
*功    能: 串口x相关全局变量初始化
*入口参数: pRxBuff--接收缓冲区
            pUxFra--帧记录缓冲区
*返 回 值: 无
**************************************/
static void USARTx_Var_Init(_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
	pRxBuff->rp = 0;
	pRxBuff->wp = 0;
	pUxRxFra->currfra = 0;
	pUxRxFra->nextfra = 0;
}

/**************************************
*函 数 名: USART_NVIC_Config
*功    能: 串口中断配置
*入口参数: usartx_irqn--中断号
            preeprio--抢占优先级
            subprio--子优先级
*返 回 值: 无
**************************************/
static void USARTx_NVIC_Config(uint8_t usartx_irqn,uint8_t preeprio,uint8_t subprio)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	/* 配置USART为中断源 */
	NVIC_InitStructure.NVIC_IRQChannel = usartx_irqn;
	/* 抢断优先级*/
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = preeprio;
	/* 子优先级 */
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = subprio;
	/* 使能中断 */
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	/* 初始化配置NVIC */
	NVIC_Init(&NVIC_InitStructure);
}
/**************************************
*函 数 名: USARTx_AFPP_GPIO_Config
*功    能: 复用推挽输出引脚配置
*入口参数: RCC_APB2Periph--APB2时钟
            GPIOx--端口
					  GPIO_Pinx--引脚
*返 回 值: 无
**************************************/
static void USARTx_AFPP_GPIO_Config(uint32_t RCC_APB2Periph,GPIO_TypeDef* GPIOx,uint16_t GPIO_Pinx)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pinx;
	GPIO_Init(GPIOx, &GPIO_InitStructure);	
}
/**************************************
*函 数 名: USARTx_FloatIN_GPIO_Config
*功    能: 浮空输入引脚配置
*入口参数: RCC_APB2Periph--APB2时钟
            GPIOx--端口
					  GPIO_Pinx--引脚
*返 回 值: 无
**************************************/
static void USARTx_FloatIN_GPIO_Config(uint32_t RCC_APB2Periph,GPIO_TypeDef* GPIOx,uint16_t GPIO_Pinx)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pinx;
	GPIO_Init(GPIOx, &GPIO_InitStructure);	
}
/**************************************
*函 数 名: USARTx_WorkMode_Config
*功    能: 串口工作模式配置
*入口参数: USARTx--串口
            BaudRate--波特率
			WordLength--字长
            StopBits--停止位
            Parity--优先级
*返 回 值: 无
**************************************/
static void USARTx_WorkMode_Config(USART_TypeDef* USARTx,uint32_t BaudRate,uint16_t WordLength,uint16_t StopBits,uint16_t Parity)
{
	USART_InitTypeDef USART_InitStructure;
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = BaudRate;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = WordLength;
	// 配置停止位
	USART_InitStructure.USART_StopBits = StopBits;
	// 配置校验位
	USART_InitStructure.USART_Parity = Parity ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(USARTx, &USART_InitStructure);
}
/**************************************
*函 数 名: USARTx_RxDMA_Config
*功    能: 串口x接收DMA配置
*入口参数: USARTx--串口
            DMAy_Channelx--DMA通道
			RCC_DMAx--时钟
            PeriBsAddr--外设地址
            MemBsAddr--存储器地址
            buffsize--缓冲区大小
            prio--DMA优先级
*返 回 值: 无
**************************************/
static void USARTx_RxDMA_Config(USART_TypeDef* USARTx,DMA_Channel_TypeDef* DMAy_Channelx,uint32_t RCC_DMAx,\
	uint32_t PeriBsAddr,uint32_t MemBsAddr,uint32_t buffsize,uint32_t prio)
{
	DMA_InitTypeDef DMA_InitStructure;
	// 开启DMA时钟
	RCC_AHBPeriphClockCmd(RCC_DMAx, ENABLE);
	DMA_InitStructure.DMA_PeripheralBaseAddr = PeriBsAddr;
	DMA_InitStructure.DMA_MemoryBaseAddr = MemBsAddr;  //串口接收基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = buffsize;  //接收缓冲区的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = 
	DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	 
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;  //循环模式
	DMA_InitStructure.DMA_Priority = prio; 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMAy_Channelx, &DMA_InitStructure);	
	// 使能DMA
	DMA_Cmd (DMAy_Channelx,ENABLE);
	USART_DMACmd(USARTx,USART_DMAReq_Rx,ENABLE);
}
/**************************************
*函 数 名: USARTx_Config
*功    能: 串口初始化
*入口参数: 无
*返 回 值: 无
*说    明: 根据USE_USARTx定义初始化串口
**************************************/
void USARTx_Config(void)
{
	#if  NeedSet_NVICprio
	NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2);
	#endif
	#if  USE_USART1
	/*串口x使用到的全局变量初始化*/
	USARTx_Var_Init(&U1RxBuff,&g_U1RxFra);
	/**打开串口时钟**/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	//配置接收发送引脚
	USARTx_AFPP_GPIO_Config(USART1TX_GPIO_CLK,USART1TX_GPIO_PORT,USART1TX_GPIO_Pin);
	USARTx_FloatIN_GPIO_Config(USART1RX_GPIO_CLK,USART1RX_GPIO_PORT,USART1RX_GPIO_Pin);
	//串口工作模式配置
	USARTx_WorkMode_Config(USART1,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
	//串口中断配置
	USARTx_NVIC_Config(USART1_IRQn,1,1);
	// 配置串口空闲中断
	USART_ClearITPendingBit(USART1,USART_IT_IDLE);
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);	
	// 使能串口
	USART_Cmd(USART1, ENABLE);
	//串口接收DMA配置
	USARTx_RxDMA_Config(USART1,DMA1_Channel5,RCC_AHBPeriph_DMA1,(USART1_BASE+0x04),(uint32_t)U1RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
	#endif
	
	#if  USE_USART2
	/*串口x使用到的全局变量初始化*/
	USARTx_Var_Init(&U2RxBuff,&g_U2RxFra);
	/**打开串口时钟**/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	//配置接收发送引脚
	USARTx_AFPP_GPIO_Config(USART2TX_GPIO_CLK,USART2TX_GPIO_PORT,USART2TX_GPIO_Pin);
	USARTx_FloatIN_GPIO_Config(USART2RX_GPIO_CLK,USART2RX_GPIO_PORT,USART2RX_GPIO_Pin);
	//串口工作模式配置
	USARTx_WorkMode_Config(USART2,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
	//串口中断配置
	USARTx_NVIC_Config(USART2_IRQn,1,1);
	// 配置串口空闲中断
	USART_ClearITPendingBit(USART2,USART_IT_IDLE);
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);	
	// 使能串口
	USART_Cmd(USART2, ENABLE);
	//串口接收DMA配置
	USARTx_RxDMA_Config(USART2,DMA1_Channel6,RCC_AHBPeriph_DMA1,(USART2_BASE+0x04),(uint32_t)U2RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
	#endif
	
	#if  USE_USART3
	/*串口x使用到的全局变量初始化*/
	USARTx_Var_Init(&U3RxBuff,&g_U3RxFra);
	/**打开串口时钟**/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	//配置接收发送引脚
	USARTx_AFPP_GPIO_Config(USART3TX_GPIO_CLK,USART3TX_GPIO_PORT,USART3TX_GPIO_Pin);
	USARTx_FloatIN_GPIO_Config(USART3RX_GPIO_CLK,USART3RX_GPIO_PORT,USART3RX_GPIO_Pin);
	//串口工作模式配置
	USARTx_WorkMode_Config(USART3,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
	//串口中断配置
	USARTx_NVIC_Config(USART3_IRQn,1,1);
	// 配置串口空闲中断
	USART_ClearITPendingBit(USART3,USART_IT_IDLE);
	USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);	
	// 使能串口
	USART_Cmd(USART3, ENABLE);
	//串口接收DMA配置
	USARTx_RxDMA_Config(USART3,DMA1_Channel3,RCC_AHBPeriph_DMA1,(USART3_BASE+0x04),(uint32_t)U3RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
	#endif
	
	#if  USE_USART4
	/*串口x使用到的全局变量初始化*/
	USARTx_Var_Init(&U4RxBuff,&g_U4RxFra);
	/**打开串口时钟**/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);
	//配置接收发送引脚
	USARTx_AFPP_GPIO_Config(USART4TX_GPIO_CLK,USART4TX_GPIO_PORT,USART4TX_GPIO_Pin);
	USARTx_FloatIN_GPIO_Config(USART4RX_GPIO_CLK,USART4RX_GPIO_PORT,USART4RX_GPIO_Pin);
	//串口工作模式配置
	USARTx_WorkMode_Config(UART4,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
	//串口中断配置
	USARTx_NVIC_Config(UART4_IRQn,1,1);
	// 配置串口空闲中断
	USART_ClearITPendingBit(UART4,USART_IT_IDLE);
	USART_ITConfig(UART4, USART_IT_IDLE, ENABLE);	
	// 使能串口
	USART_Cmd(UART4, ENABLE);
	//串口接收DMA配置
	USARTx_RxDMA_Config(UART4,DMA2_Channel3,RCC_AHBPeriph_DMA2,(UART4_BASE+0x04),(uint32_t)U4RxBuff.rxarr,UxRXBUFFSIZE,DMA_Priority_Low);
	#endif
	
	#if  USE_USART5
	/*串口x使用到的全局变量初始化*/
	USARTx_Var_Init(&U5RxBuff,&g_U5RxFra);
	/**打开串口时钟**/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5,ENABLE);
	//配置接收发送引脚
	USARTx_AFPP_GPIO_Config(USART5TX_GPIO_CLK,USART5TX_GPIO_PORT,USART5TX_GPIO_Pin);
	USARTx_FloatIN_GPIO_Config(USART5RX_GPIO_CLK,USART5RX_GPIO_PORT,USART5RX_GPIO_Pin);
	//串口工作模式配置
	USARTx_WorkMode_Config(UART5,115200,USART_WordLength_8b,USART_StopBits_1,USART_Parity_No);
	//串口中断配置
	USARTx_NVIC_Config(UART5_IRQn,1,1);
	// 配置串口接收中断
	USART_ClearITPendingBit(UART5,USART_IT_RXNE);
	USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);	
	//配置串口空闲中断
	USART_ClearITPendingBit(UART5,USART_IT_IDLE);
	USART_ITConfig(UART5, USART_IT_IDLE, ENABLE);	
	// 使能串口
	USART_Cmd(UART5, ENABLE);
	//串口5无DMA传输
	#endif
}

/**************************************
*函 数 名: Usart_SendByte
*功    能: 串口发送一个字节
*入口参数: pUSARTx--USART1..3,UART4/5
            ch--要发送的字节
*返 回 值: 无
*说    明: 
**************************************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/**************************************
*函 数 名: Usart_SendArray
*功    能: 串口发送8位数组
*入口参数: pUSARTx--USART1..3,UART4/5
            array--要发送的数组指针
            num--要发送的数目
*返 回 值: 无
*说    明: 
**************************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i;
	
	for(i=0; i<num; i++)
  {
	    /* 发送一个字节数据到USART */
	    Usart_SendByte(pUSARTx,array[i]);	
  
  }
	/* 等待发送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

/**************************************
*函 数 名: Usart_SendString
*功    能: 串口发送字符串
*入口参数: pUSARTx--USART1..3,UART4/5
            str--要发送的字符串指针
*返 回 值: 无
*说    明: 
**************************************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(USART1, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(USART1);
}

/**************************************
*函 数 名: USARTxIDLE_IRQ
*功    能: 串口x空闲中断调用函数;仅串口1-4
*入口参数: pUSARTx--USARTx
            DMAy_Channelx--串口通道
            pRxBuff--串口接收缓冲区
            pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
**************************************/
static void USARTx_IDLE_IRQ(USART_TypeDef * pUSARTx,DMA_Channel_TypeDef* DMAy_Channelx,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
	uint16_t trnum=0;
	//清除空闲中断标志位
	pUSARTx->SR;
	pUSARTx->DR;
	//手册虽然说这个寄存器在DMA循环模式的时候,清0之后会自动恢复为最大接收缓冲区,但加入这一步以防万一
	if(DMAy_Channelx->CNDTR == 0) 
	{
		trnum = UxRXBUFFSIZE;
	}
	else
	{
		trnum = DMAy_Channelx->CNDTR&0xffff;
	}
	pRxBuff->wp = UxRXBUFFSIZE-trnum;  //得到最新帧的结束地址
	pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp;  //最新帧的起始地址
	pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp;  //最新帧的结束地址
	pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
	pRxBuff->rp = pRxBuff->wp;  //最新帧的起始与结束地址记录完,等待下一次记录
}
#if  USE_USART5
/**************************************
*函 数 名: USART5_RXNE_IRQ
*功    能: 串口5接收一个字节
*入口参数: pRxBuff--串口接收缓冲区
*返 回 值: 无
*说    明: 
**************************************/
static void USART5_RXNE_IRQ(_USARTxRXBUFF *pRxBuff)
{
	pRxBuff->rxarr[pRxBuff->wp] = USART_ReceiveData(UART5);
	pRxBuff->wp = (pRxBuff->wp+1)%UxRXBUFFSIZE; //pRxBuff->wp的值被限制再0,1....(UxRXBUFFSIZE-1)
}
/**************************************
*函 数 名: UART5_IDLE_IRQ
*功    能: 串口5空闲中断处理
*入口参数: pRxBuff--串口接收缓冲区
            pUxFra--串口接收帧记录缓冲区
*返 回 值: 无
*说    明: 
**************************************/
static void UART5_IDLE_IRQ(_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxRxFra)
{
	UART5->SR;
	UART5->DR;
	pUxRxFra->fraddr[pUxRxFra->nextfra].rpx = pRxBuff->rp;  //最新帧的起始地址
	pUxRxFra->fraddr[pUxRxFra->nextfra].wpx = pRxBuff->wp;  //最新帧的结束地址
	pUxRxFra->nextfra = (pUxRxFra->nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
	pRxBuff->rp = pRxBuff->wp;  //最新帧的起始与结束地址记录完,等待下一次记录
}
/**************************************
*函 数 名: USART5RXNE_IRQ
*功    能: 串口5接收字节中断调用函数;串口5专用
*入口参数: 无
*返 回 值: 无
*说    明: 在接收字节中断服务函数中调用
**************************************/
void USART5RXNE_IRQ(void)
{
	USART_ClearITPendingBit(UART5,USART_IT_RXNE);
	USART5_RXNE_IRQ(&U5RxBuff);
}
#endif
/**************************************
*函 数 名: USARTxIDLE_IRQ
*功    能: 串口x空闲中断调用函数;串口1-5
*入口参数: Com_USARTx--定义好的串口通道
*返 回 值: 无
*说    明:串口5除了这个之外还要调用串口接收字节处理
**************************************/
void USARTxIDLE_IRQ(uint8_t Com_USARTx)
{
	switch(Com_USARTx)
	{
		#if  USE_USART1
		case Com_USART1:
			USARTx_IDLE_IRQ(USART1,USART1_RxDMA_CHANNEL,&U1RxBuff,&g_U1RxFra);
		break;
		#endif
		#if  USE_USART2
		case Com_USART2:
			USARTx_IDLE_IRQ(USART2,USART2_RxDMA_CHANNEL,&U2RxBuff,&g_U2RxFra);
		break;
		#endif
		#if  USE_USART3
		case Com_USART3:
			USARTx_IDLE_IRQ(USART3,USART3_RxDMA_CHANNEL,&U3RxBuff,&g_U3RxFra);
		break;
		#endif
		#if  USE_USART4
		case Com_USART4:
			USARTx_IDLE_IRQ(UART4,USART4_RxDMA_CHANNEL,&U4RxBuff,&g_U4RxFra);
		break;
		#endif
		#if  USE_USART5
		case Com_USART5:
			UART5_IDLE_IRQ(&U5RxBuff,&g_U5RxFra);
		break;
		#endif
		default:
			
		break;
	}
}

/**************************************
*函 数 名: GetAFraFromUxRxBuff
*功    能: 获取一帧数据
*入口参数: pbuff--获取一帧数据的数组
            psize--获取的数目
            pRxBuff--串口x缓冲区
            pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说    明: 本函数没有进行地址超出判断,一定要确保你的数据帧的空间小于你定下的 pbuff 的空间
**************************************/
static uint8_t GetAFraFromUxRxBuff(uint8_t *pbuff,uint8_t *psize,_USARTxRXBUFF *pRxBuff,_FRAMEATTRI *pUxFra)
{
	uint8_t rtflg=0;  //返回值
	uint16_t fralen=0;  //帧长度
	if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
	{
		/*根据每帧的帧属性(起始与结束地址)在串口接收缓冲区主体中获取一帧数据*/
		if(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
		{
			//本帧写入的起始地址小于写入的结束地址,直接使用写入结束地址-写入起始地址,计算出本帧的长度
			fralen = pUxFra->fraddr[pUxFra->currfra].wpx-pUxFra->fraddr[pUxFra->currfra].rpx;
			for((*psize)=0;(*psize)<fralen;(*psize)++)
			{
				//从串口接收缓冲区主体中取出本帧的数据,索引为:pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)
				pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx+(*psize)];
			}
			//数据取出之后,本帧的.rpx(本帧起始的写入地址)=本帧的.wpx(本帧写入的结束地址)
			pUxFra->fraddr[pUxFra->currfra].rpx=pUxFra->fraddr[pUxFra->currfra].wpx;
		}
		else
		{
			//本帧写入的起始地址大于写入的结束地址,说明数据在缓冲区中进行了溢出翻转,分两部分读取
			//第一部分为rpx-(UxRXBUFFSIZE-1)的数据,第二部分为0-wpx的数据
			for((*psize)=0;pUxFra->fraddr[pUxFra->currfra].rpx<UxRXBUFFSIZE;pUxFra->fraddr[pUxFra->currfra].rpx++)
			{
				//读取第一部分数据
				pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
				(*psize)++;
			}
			pUxFra->fraddr[pUxFra->currfra].rpx = 0;
			//读取第二部分数据
			while(pUxFra->fraddr[pUxFra->currfra].rpx<pUxFra->fraddr[pUxFra->currfra].wpx)
			{
				pbuff[(*psize)] = pRxBuff->rxarr[pUxFra->fraddr[pUxFra->currfra].rpx];
				(*psize)++;
				pUxFra->fraddr[pUxFra->currfra].rpx++;
			}
			
		}
		//当前帧数据读取结束,currfra(当前处理的帧+1)----0..(FRADDRMAX-1)
		pUxFra->currfra = (pUxFra->currfra+1)%FRADDRMAX;
		//读取到数据,返回值赋1
		rtflg = 1; 
	}
	return rtflg;
}


/**************************************
*函 数 名: JudgeUxFrameDataNum
*功    能: 判断是否有未处理的数据帧
*入口参数: pUxFra--串口x帧记录
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说    明:
**************************************/
static uint8_t JudgeUxFrameDataNum(_FRAMEATTRI *pUxFra)
{
	uint8_t rtflg=0;
	if(pUxFra->currfra != pUxFra->nextfra) //如果为真,说明有未处理的帧
	{
		rtflg = 1;
	}
	else
	{
		rtflg = 0;
	}
	return rtflg;
}
/**************************************
*函 数 名: GetAFraFromComx
*功    能: 从COM口获取一帧数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
            pbuff--获取一帧数据的数组
            psize--获取的数目
*返 回 值: rtflg--0代表没有获取数据,1代表获取到数据
*说    明:
**************************************/
uint8_t GetAFraFromComx(uint8_t Com_USARTx,uint8_t *pbuff,uint8_t *psize)
{
	uint8_t rtflg=0;  //返回值
	switch(Com_USARTx)
	{
		#if  USE_USART1
		case Com_USART1:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U1RxBuff,&g_U1RxFra);
		break;
		#endif
		#if  USE_USART2
		case Com_USART2:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U2RxBuff,&g_U2RxFra);
		break;
		#endif
		#if  USE_USART3
		case Com_USART3:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U3RxBuff,&g_U3RxFra);
		break;
		#endif
		#if  USE_USART4
		case Com_USART4:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U4RxBuff,&g_U4RxFra);
		break;
		#endif
		#if  USE_USART5
		case Com_USART5:
			rtflg = GetAFraFromUxRxBuff(pbuff,psize,&U5RxBuff,&g_U5RxFra);
		break;
		#endif
		default:
			
		break;
	}
	return rtflg;
}
/**************************************
*函 数 名: JudgeUxFraIsNull
*功    能: 判断当前COM口是否还有未处理的数据
*入口参数: Com_USARTx--定义好的COM口,Com_USART1..5
*返 回 值: rtflg--0代表没有数据,1代表有数据需要处理
*说    明:
**************************************/
uint8_t JudgeUxFraIsNull(uint8_t Com_USARTx)
{
	uint8_t rtflg=0;  //返回值
	switch(Com_USARTx)
	{
		#if  USE_USART1
		case Com_USART1:
			rtflg = JudgeUxFrameDataNum(&g_U1RxFra);
		break;
		#endif
		#if  USE_USART2
		case Com_USART2:
			rtflg = JudgeUxFrameDataNum(&g_U2RxFra);
		break;
		#endif
		#if  USE_USART3
		case Com_USART3:
			rtflg = JudgeUxFrameDataNum(&g_U3RxFra);
		break;
		#endif
		#if  USE_USART4
		case Com_USART4:
			rtflg = JudgeUxFrameDataNum(&g_U4RxFra);
		break;
		#endif
		#if  USE_USART5
		case Com_USART5:
			rtflg = JudgeUxFrameDataNum(&g_U5RxFra);
		break;
		#endif
		default:
			
		break;
	}
	return rtflg;
}

中断服务函数中:

// 串口中断服务函数
#if USE_USART1
void USART1_IRQHandler(void)
{
	
	if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART1);
	}	 
}
#endif
// 串口中断服务函数
#if USE_USART2
void USART2_IRQHandler(void)
{
	
	if(USART_GetITStatus(USART2,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART2);
	}	 
}
#endif

// 串口中断服务函数
#if USE_USART3
void USART3_IRQHandler(void)
{
	
	if(USART_GetITStatus(USART3,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART3);
	}	 
}
#endif
// 串口中断服务函数
#if USE_USART4
void UART4_IRQHandler(void)
{
	
	if(USART_GetITStatus(UART4,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART4);
	}	 
}
#endif
#if USE_USART5
// 串口中断服务函数
void UART5_IRQHandler(void)
{
	if(USART_GetITStatus(UART5,USART_IT_RXNE)!=RESET)
	{		
		
		USART5RXNE_IRQ();
	}	
	if(USART_GetITStatus(UART5,USART_IT_IDLE)!=RESET)
	{		
		
		 USARTxIDLE_IRQ(Com_USART5);
	}	 
}
#endif

main测试函数:

uint8_t parr[100],psize;
int main(void)
{
	BSP_Init();
	SysTick_Config(SystemCoreClock / 1000);
	while(1)
	{	
		if(GetAFraFromComx(Com_USART1,parr,&psize))
		{
			if(psize != 0)
			{
				Usart_SendArray(USART1,parr,psize);
			}
		}
		if(GetAFraFromComx(Com_USART2,parr,&psize))
		{
			if(psize != 0)
			{
				Usart_SendArray(USART2,parr,psize);
			}
		}
		if(GetAFraFromComx(Com_USART3,parr,&psize))
		{
			if(psize != 0)
			{
				Usart_SendArray(USART3,parr,psize);
			}
		}
		if(GetAFraFromComx(Com_USART4,parr,&psize))
		{
			if(psize != 0)
			{
				Usart_SendArray(UART4,parr,psize);
			}
		}
		if(GetAFraFromComx(Com_USART5,parr,&psize))
		{
			if(psize != 0)
			{
				Usart_SendArray(UART5,parr,psize);
			}
		}
	}
}

兴起再写DMA发送队列

题外话

以上的都是会产生空闲中断的数据帧格式,如果数据帧无空闲,而是通过帧头帧尾判断的话,以下代码为串口接收处理参考。
通过帧头帧尾判断数据帧,无需配置空闲中断,而在串口接收中断中调用:

//通过帧头帧尾判断数据帧
void USART1RXNE_IRQ(void)
{
	static uint8_t afracnt=0; //单帧数据计数
	uint8_t ch;
	USART_ClearITPendingBit(USART1,USART_IT_RXNE);
	afracnt++;
	ch = USART_ReceiveData(DEBUG_USARTx)&0xff;
	RxBuff.rxarr[RxBuff.wp] = ch;
	RxBuff.wp=(RxBuff.wp+1)%RXBUFFSIZE;
	if((ch == 0xf5)||(afracnt==50))  //到达本帧帧尾或者这帧数据超过50个字节
	{
		afracnt = 0;
		g_Fra.fraddr[g_Fra.nextfra].rpx = RxBuff.rp;  //最新帧的起始地址
		g_Fra.fraddr[g_Fra.nextfra].wpx = RxBuff.wp;  //最新帧的结束地址
		g_Fra.nextfra = (g_Fra.nextfra+1)%FRADDRMAX; //g_Fra.nextfra的值被限制再0,1....(FRADDRMAX-1)
		RxBuff.rp = RxBuff.wp;  //最新帧的起始与结束地址记录完,等待下一次记录
	}
}

中断中为:

/****************************************************
函数名称 : USART1_IRQHandler
功    能 : 判断通过串口接收到的数据,执行相应的命令
参    数 : 无
返 回 值 : 无
****************************************************/
void USART1_IRQHandler(void)    //串口接收中断服务函数  
{
	if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
	{
		USART1RXNE_IRQ();
	}
	if ( USART_GetITStatus( USART1, USART_IT_IDLE ) != RESET )                                         //数据帧接收完毕
	{
//		USART1IDLE_IRQ();
	}	
}

其余与上述一致。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-11 12:35:13  更:2021-08-11 12:36:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/12 17:13:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码