精华内容
下载资源
问答
  • 也就是说,在循环模式下,当DMA传输了一个数据,DMA_CNDTRx传输数量寄存器相应减一,当DMA_CNDTRx传输数量寄存器减为0时,DMA_CNDTRx寄存器将恢复为相应的初始值,不用软件干预,那么,这不就是循环队列的方式吗?

    串口通讯最主要的就是要不丢数据,不丢帧,基本设想就是建立一个大的串口缓冲区,串口接收到的数据使用循环队列的方式全部往这个缓冲区放,不过这种方式需要把串口缓冲区弄大一点,防止数据覆盖。在stm32中,利用DMA+空闲中断很容易做到这一点。只需要将DMA设置为循环模式,如下图:
    在这里插入图片描述
    也就是说,在循环模式下,当DMA传输了一个数据,DMA_CNDTRx传输数量寄存器相应减一,当DMA_CNDTRx传输数量寄存器减为0时,DMA_CNDTRx寄存器将恢复为相应的初始值,不用软件干预,那么,这不就是循环队列的方式吗?
    于是,只需将DMA传输方式配置为循环模式,串口接收循环队列就此完成。串口初始化代码如下:

    _USART1RXBUFF RxBuff;  //定义串口接收缓冲区
     /**
      * @brief  配置嵌套向量中断控制器NVIC
      * @param  无
      * @retval 无
      */
    static void NVIC_Configuration(void)
    {
      NVIC_InitTypeDef NVIC_InitStructure;
      
      /* 嵌套向量中断控制器组选择 */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      
      /* 配置USART为中断源 */
      NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
      /* 抢断优先级*/
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      /* 子优先级 */
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      /* 使能中断 */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      /* 初始化配置NVIC */
      NVIC_Init(&NVIC_InitStructure);
    }
    
     /**
      * @brief  USART GPIO 配置,工作参数配置
      * @param  无
      * @retval 无
      */
    void USART_Config(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    
    	// 打开串口GPIO的时钟
    	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
    	
    	// 打开串口外设的时钟
    	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
    
    	// 将USART Tx的GPIO配置为推挽复用模式
    	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
    
      // 将USART Rx的GPIO配置为浮空输入模式
    	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
    	
    	// 配置串口的工作参数
    	// 配置波特率
    	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
    	// 配置 针数据字长
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    	// 配置停止位
    	USART_InitStructure.USART_StopBits = USART_StopBits_1;
    	// 配置校验位
    	USART_InitStructure.USART_Parity = USART_Parity_No ;
    	// 配置硬件流控制
    	USART_InitStructure.USART_HardwareFlowControl = 
    	USART_HardwareFlowControl_None;
    	// 配置工作模式,收发一起
    	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    	// 完成串口的初始化配置
    	USART_Init(DEBUG_USARTx, &USART_InitStructure);
    	
    	// 串口中断优先级配置
    	NVIC_Configuration();
    	
    	// 使能串口接收中断
    	USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE);	
    	
    	// 使能串口
    	USART_Cmd(DEBUG_USARTx, ENABLE);	    
    }
    /**
      * @brief  USARTx DMA 配置
      * @param  无
      * @retval 无
      */
    void USARTx_DMA_Config(void)
    {
    		DMA_InitTypeDef DMA_InitStructure;
    	
    		// 开启DMA时钟
    		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    //		// 设置DMA源地址:串口数据寄存器地址*/
    //		DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
    //		// 内存地址(要传输的变量的指针)
    //		DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
    //		// 方向:从内存到外设	
    //		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    //		// 传输大小	
    //		DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
    //		// 外设地址不增	    
    //		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模式,一次或者循环模式
    //		DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
    //		//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
    //		// 优先级:中	
    //		DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
    //		// 禁止内存到内存的传输
    //		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    //		// 配置DMA通道		   
    //		DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);	
    		
    		DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
    		DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuff.rxarr;  //串口接收基地址
    		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    		DMA_InitStructure.DMA_BufferSize = RXBUFFSIZE;  //接收缓冲区的大小
    		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 = DMA_Priority_Medium; 
    		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    		DMA_Init(USART1RX_DMA_CHANNEL, &DMA_InitStructure);	
    		// 使能DMA
    //		DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
    		DMA_Cmd (USART1RX_DMA_CHANNEL,ENABLE);
    		USART_DMACmd(DEBUG_USARTx,USART_DMAReq_Rx,ENABLE);
    }
    

    初始化没什么好说的,定义了串口接收缓冲区,然后DMA配置为循环模式,接收缓冲区的结构体如下:

    #define  RXBUFFSIZE       100    //接收缓冲区的大小
    /**串口接收缓冲区**/
    typedef struct __USART1RXBUFF
    {
    	uint16_t wp;  //接收缓冲区写地址
    	uint16_t rp;  //接收缓冲区的读地址
    	uint8_t  rxarr[RXBUFFSIZE];  //接收缓冲区实体
    }_USART1RXBUFF;
    
    /**帧地址结构体**/
    typedef struct __FRAMEADDR
    {
    	uint16_t wpx;  //本帧写地址的索引
    	uint16_t rpx;  //本帧读地址的索引
    }_FRAMEADDR;
    
    #define  FRADDRMAX  10  //最多能记录的帧
    /**帧属性结构体**/
    typedef struct __FRAMEATTRI
    {
    	_FRAMEADDR fraddr[FRADDRMAX];  //每帧的地址
    	uint8_t currfra;  //当前处理帧
    	uint8_t nextfra;  //下一个帧
    }_FRAMEATTRI;
    

    总共有三个结构体,结构体详细介绍如下:
    首先是第一个结构体,串口接收缓冲区结构体,串口接收缓冲区RxBuff的定义就是这个结构体属性。

    #define  RXBUFFSIZE       100    //接收缓冲区的大小
    /**串口接收缓冲区**/
    typedef struct __USART1RXBUFF
    {
    	uint16_t wp;  //接收缓冲区写地址
    	uint16_t rp;  //接收缓冲区的读地址
    	uint8_t  rxarr[RXBUFFSIZE];  //接收缓冲区实体
    }_USART1RXBUFF;
    
    

    其中 wp 记录接收缓冲区当前写到的地址, rp 记录接收缓冲区当前读到的地址,而 rxarr 则是接收缓冲区的主体,所有从串口接收到的数据都会放入这个数组中,大小为 RXBUFFSIZE 设置,这个值需要设置大一点,防止数据覆盖。图解如下:
    在这里插入图片描述
    刚开始时,rp、wp都指向0,之后来了第一帧数据之后如下:
    在这里插入图片描述
    之后帧1填充至串口缓冲区,rp记录帧1的起始地址,wp记录帧1的结束地址,这两个值的记录处理如下:

    /*在串口空闲中断中调用*/
    void USART1IDLE_IRQ(void)
    {
    	uint16_t trnum=0;
    	USART1->SR;
    	USART1->DR;
    	//手册虽然说这个寄存器在DMA循环模式的时候,清0之后会自动恢复为最大接收缓冲区,但加入这一步以防万一
    	if(USART1RX_DMA_CHANNEL->CNDTR == 0) 
    	{
    		trnum = RXBUFFSIZE;
    	}
    	else
    	{
    		trnum = USART1RX_DMA_CHANNEL->CNDTR&0xffff;
    	}
    	
    	RxBuff.wp = RXBUFFSIZE-trnum;  //指向接收缓冲区帧1的写地址末尾
    }
    

    知道了帧1在串口缓冲区的起始地址与结束地址,那么在主函数中就可以将数据从串口接收缓冲区取出来。等待帧1数据读取完之后,让RxBuff.rp=RxBuff.wp。如下:

     /**
      * @brief  JustAFra获取一帧数据
      * @param  pbuff--获取一帧数据的数组,psize--获取的数目
      * @retval rtflg--0代表没有获取数据,1代表获取到数据
      */
    uint8_t JustAFra(uint8_t *pbuff,uint8_t *psize)
    {
    	uint8_t rtflg=0;
    	if(RxBuff.rp != RxBuff.wp)
    	{
    		rtflg = 1;
    		printf("RxBuff.rp=%d,RxBuff.wp=%d\r\n",RxBuff.rp,RxBuff.wp);
    		
    		if(RxBuff.rp<RxBuff.wp)
    		{
    			for(*psize=0;*psize<(RxBuff.wp-RxBuff.rp);(*psize)++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[RxBuff.rp+(*psize)];
    			}
    			RxBuff.rp = RxBuff.wp;
    		}
    		else
    		{
    			for((*psize)=0;RxBuff.rp<RXBUFFSIZE;RxBuff.rp++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[RxBuff.rp];
    				(*psize)++;
    			}
    			RxBuff.rp = 0;
    			
    			while(RxBuff.rp<RxBuff.wp)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[RxBuff.rp];
    				(*psize)++;
    				RxBuff.rp++;
    			}
    		}
    	}
    	return rtflg;
    }
    

    但是,如果仅仅这样的话,设想一下,如果帧1处理的时间比较长,此时rp=wp(数据读取的时间很短,不考虑在读取的时候又接收到下一帧这种情况),这时候后面帧2、帧3、帧4来了,而你帧1还没有处理完,如此一来,rp还是指向帧1的末尾(即帧2的起始),但wp指向的却是帧4的末尾,等下一次帧处理的时候,帧2/帧3/帧4被当成一帧了。如下图:
    在这里插入图片描述
    这时候还使用上面那一种方式读取的话,帧2/3/4被当做一帧。
    如何避免这种情况的发生?如果我们记录每一个到达帧的起始地址(rp)和结束地址(wp),再具备一个记录当前正在处理帧(currfra)与一个最新到达帧(nextfra),也就是说建立第二个帧属性的队列,在这个队列中记录了每个帧的属性。
    在这里插入图片描述

    这样的话,来了一个新的帧,即便我帧1还没有处理完,但是我可以记录帧2的属性(在串口接收缓冲区的起始地址rp与结束地址wp)、帧3、帧4;之后等帧1处理完之后,可以根据currfra的索引处理帧2、帧3、帧4,一直等待执行到currfra == nextfra,则说明所有的帧都处理完成,这种方法需要要求串口接收缓冲区足够大,不会使后面到达的帧破坏前面帧的数据。一般能同时存十个帧的大小就够用,自己估计下。
    所以就使用到了后面的两个结构体:

    /**帧地址结构体**/
    typedef struct __FRAMEADDR
    {
    	uint16_t wpx;  //本帧写地址的索引
    	uint16_t rpx;  //本帧读地址的索引
    }_FRAMEADDR;
    
    #define  FRADDRMAX  10  //最多能记录的帧
    /**帧属性结构体**/
    typedef struct __FRAMEATTRI
    {
    	_FRAMEADDR fraddr[FRADDRMAX];  //每帧的地址,队列主体
    	uint8_t currfra;  //当前处理帧
    	uint8_t nextfra;  //下一个帧
    }_FRAMEATTRI;
    

    定义帧属性队列:

    _FRAMEATTRI g_Fra;
    

    全新串口空闲中断调用函数修改如下:

    /*在串口空闲中断中调用*/
    void USART1IDLE_IRQ(void)
    {
    	uint16_t trnum=0;
    	USART1->SR;
    	USART1->DR;
    	//手册虽然说这个寄存器在DMA循环模式的时候,清0之后会自动恢复为最大接收缓冲区,但加入这一步以防万一
    	if(USART1RX_DMA_CHANNEL->CNDTR == 0) 
    	{
    		trnum = RXBUFFSIZE;
    	}
    	else
    	{
    		trnum = USART1RX_DMA_CHANNEL->CNDTR&0xffff;
    	}
    	
    	RxBuff.wp = RXBUFFSIZE-trnum;  //得到最新帧的结束地址
    	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;  //最新帧的起始与结束地址记录完,等待下一次记录
    }
    

    在空闲中断中,在帧属性队列主体中记录最新一帧的起始,结束地址;
    获取一帧数据函数如下:

     /**
      * @brief  GetAFra--获取一帧数据
      * @param  pbuff--获取一帧数据的数组,psize--获取的数目
      * @retval rtflg--0代表没有获取数据,1代表获取到数据
      */
    uint8_t GetAFra(uint8_t *pbuff,uint8_t *psize)
    {
    	uint8_t rtflg=0;  //返回值
    	uint16_t fralen=0;  //帧长度
    	if(g_Fra.currfra != g_Fra.nextfra) //如果为真,说明有未处理的帧
    	{
    //		printf("RxBuff.rp=%d,RxBuff.wp=%d\r\n",RxBuff.rp,RxBuff.wp);
    //		printf("currfra=%d,nextfra=%d\r\n",g_Fra.currfra,g_Fra.nextfra);
    		/*根据每帧的帧属性(起始与结束地址)在串口接收缓冲区主体中获取一帧数据*/
    		if(g_Fra.fraddr[g_Fra.currfra].rpx<g_Fra.fraddr[g_Fra.currfra].wpx)
    		{
    			fralen = g_Fra.fraddr[g_Fra.currfra].wpx-g_Fra.fraddr[g_Fra.currfra].rpx;
    			for((*psize)=0;(*psize)<fralen;(*psize)++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[g_Fra.fraddr[g_Fra.currfra].rpx+(*psize)];
    			}
    			g_Fra.fraddr[g_Fra.currfra].rpx=g_Fra.fraddr[g_Fra.currfra].wpx;
    		}
    		else
    		{
    			for((*psize)=0;g_Fra.fraddr[g_Fra.currfra].rpx<RXBUFFSIZE;g_Fra.fraddr[g_Fra.currfra].rpx++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[g_Fra.fraddr[g_Fra.currfra].rpx];
    				(*psize)++;
    			}
    			g_Fra.fraddr[g_Fra.currfra].rpx = 0;
    			
    			while(g_Fra.fraddr[g_Fra.currfra].rpx<g_Fra.fraddr[g_Fra.currfra].wpx)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[g_Fra.fraddr[g_Fra.currfra].rpx];
    				(*psize)++;
    				g_Fra.fraddr[g_Fra.currfra].rpx++;
    			}
    			
    		}
    		g_Fra.currfra = (g_Fra.currfra+1)%FRADDRMAX;
    //		printf("currfra=%d,nextfra=%d\r\n",g_Fra.currfra,g_Fra.nextfra);
    		rtflg = 1;
    	}
    	return rtflg;
    }
    

    获取数据时有两种情况,一种是rp<wp,如下图所示
    在这里插入图片描述

    说明帧数据就在rp 与wp的地址中间,另外一种就是wp<rp,如下图所示:
    在这里插入图片描述
    数据需要分两段提取。

    测试函数如下:

    flg = GetAFra(arr,&getsize);
    	if(flg!=0)
    	{
    		if(test==8)
    		{
    			test = 0;
    		}
    		if(test == 0)
    		{
    			Delay(0x1ffffff);
    		}
    		test++;
    		printf("getsize=%d\r\n",getsize);
    		for(temp=0;temp<getsize;temp++)
    		{
    			printf("%d ",arr[temp]);
    		}
    		printf("\r\n");
    		getsize = 0;
    	}
    

    就是将获取到的数据打印出来。

    在这里插入图片描述
    如图中所示:当我们获取了第一帧之后,因为加入了一个延时,模拟第一帧处理时间过长,这时候来了帧2,3,4,5,但我们能依次获取后面的4个帧。

    大爷的,本来想上传工程赚几个积分,一直传不上,这就很烦。源码如下:
    bsp_usart_dma.c

    /*******************************************************
    *设计:陈文德
    *版本:V1.0
    *******************************************************/
    #include "bsp_usart_dma.h"
    
    _USART1RXBUFF RxBuff;  //定义串口接收缓冲区
    _FRAMEATTRI   g_Fra;
     /**
      * @brief  配置嵌套向量中断控制器NVIC
      * @param  无
      * @retval 无
      */
    static void NVIC_Configuration(void)
    {
      NVIC_InitTypeDef NVIC_InitStructure;
      
      /* 嵌套向量中断控制器组选择 */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      
      /* 配置USART为中断源 */
      NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
      /* 抢断优先级*/
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      /* 子优先级 */
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      /* 使能中断 */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      /* 初始化配置NVIC */
      NVIC_Init(&NVIC_InitStructure);
    }
    
     /**
      * @brief  USART GPIO 配置,工作参数配置
      * @param  无
      * @retval 无
      */
    void USART_Config(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    
    	// 打开串口GPIO的时钟
    	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
    	
    	// 打开串口外设的时钟
    	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
    
    	// 将USART Tx的GPIO配置为推挽复用模式
    	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
    
      // 将USART Rx的GPIO配置为浮空输入模式
    	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
    	
    	// 配置串口的工作参数
    	// 配置波特率
    	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
    	// 配置 针数据字长
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    	// 配置停止位
    	USART_InitStructure.USART_StopBits = USART_StopBits_1;
    	// 配置校验位
    	USART_InitStructure.USART_Parity = USART_Parity_No ;
    	// 配置硬件流控制
    	USART_InitStructure.USART_HardwareFlowControl = 
    	USART_HardwareFlowControl_None;
    	// 配置工作模式,收发一起
    	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    	// 完成串口的初始化配置
    	USART_Init(DEBUG_USARTx, &USART_InitStructure);
    	
    	// 串口中断优先级配置
    	NVIC_Configuration();
    	
    	// 使能串口接收中断
    	USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE);	
    	
    	// 使能串口
    	USART_Cmd(DEBUG_USARTx, ENABLE);	    
    }
    /**
      * @brief  USARTx DMA 配置
      * @param  无
      * @retval 无
      */
    void USART1_DMA_Config(void)
    {
    		DMA_InitTypeDef DMA_InitStructure;
    	
    		// 开启DMA时钟
    		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    //		// 设置DMA源地址:串口数据寄存器地址*/
    //		DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
    //		// 内存地址(要传输的变量的指针)
    //		DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
    //		// 方向:从内存到外设	
    //		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    //		// 传输大小	
    //		DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
    //		// 外设地址不增	    
    //		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模式,一次或者循环模式
    //		DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
    //		//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
    //		// 优先级:中	
    //		DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
    //		// 禁止内存到内存的传输
    //		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    //		// 配置DMA通道		   
    //		DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);	
    		
    		DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
    		DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuff.rxarr;  //串口接收基地址
    		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    		DMA_InitStructure.DMA_BufferSize = RXBUFFSIZE;  //接收缓冲区的大小
    		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 = DMA_Priority_Medium; 
    		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    		DMA_Init(USART1RX_DMA_CHANNEL, &DMA_InitStructure);	
    		// 使能DMA
    //		DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
    		DMA_Cmd (USART1RX_DMA_CHANNEL,ENABLE);
    		USART_DMACmd(DEBUG_USARTx,USART_DMAReq_Rx,ENABLE);
    }
    void USART1Var_Init(void)
    {
    	RxBuff.rp = 0;
    	RxBuff.wp = 0;
    	g_Fra.currfra = 0;
    	g_Fra.nextfra = 0;
    }
    
    void USART1_Init(void)
    {
    	USART1Var_Init();
    	USART_Config();
    	USART1_DMA_Config();
    }
    
    
    /*****************  发送一个字节 **********************/
    void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
    {
    	/* 发送一个字节数据到USART */
    	USART_SendData(pUSARTx,ch);
    		
    	/* 等待发送数据寄存器为空 */
    	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
    }
    
    /****************** 发送8位的数组 ************************/
    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);
    }
    
    /*****************  发送字符串 **********************/
    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(DEBUG_USARTx, (uint8_t) ch);
    		
    		/* 等待发送完毕 */
    		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
    	
    		return (ch);
    }
    
    ///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
    int fgetc(FILE *f)
    {
    		/* 等待串口输入数据 */
    		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
    
    		return (int)USART_ReceiveData(DEBUG_USARTx);
    }
    
    
    
    
    
     /**
      * @brief  USART1IDLE_IRQ--在空闲中断中调用
      * @param  无
      * @retval 无
      */
    void USART1IDLE_IRQ(void)
    {
    	uint16_t trnum=0;
    	USART1->SR;
    	USART1->DR;
    	//手册虽然说这个寄存器在DMA循环模式的时候,清0之后会自动恢复为最大接收缓冲区,但加入这一步以防万一
    	if(USART1RX_DMA_CHANNEL->CNDTR == 0) 
    	{
    		trnum = RXBUFFSIZE;
    	}
    	else
    	{
    		trnum = USART1RX_DMA_CHANNEL->CNDTR&0xffff;
    	}
    	
    	RxBuff.wp = RXBUFFSIZE-trnum;  //得到最新帧的结束地址
    	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;  //最新帧的起始与结束地址记录完,等待下一次记录
    }
    
     /**
      * @brief  GetAFra--获取一帧数据
      * @param  pbuff--获取一帧数据的数组,psize--获取的数目
      * @retval rtflg--0代表没有获取数据,1代表获取到数据
      */
    uint8_t GetAFra(uint8_t *pbuff,uint8_t *psize)
    {
    	uint8_t rtflg=0;  //返回值
    	uint16_t fralen=0;  //帧长度
    	if(g_Fra.currfra != g_Fra.nextfra) //如果为真,说明有未处理的帧
    	{
    		printf("RxBuff.rp=%d,RxBuff.wp=%d\r\n",RxBuff.rp,RxBuff.wp);
    		printf("currfra=%d,nextfra=%d\r\n",g_Fra.currfra,g_Fra.nextfra);
    		/*根据每帧的帧属性(起始与结束地址)在串口接收缓冲区主体中获取一帧数据*/
    		if(g_Fra.fraddr[g_Fra.currfra].rpx<g_Fra.fraddr[g_Fra.currfra].wpx)
    		{
    			fralen = g_Fra.fraddr[g_Fra.currfra].wpx-g_Fra.fraddr[g_Fra.currfra].rpx;
    			for((*psize)=0;(*psize)<fralen;(*psize)++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[g_Fra.fraddr[g_Fra.currfra].rpx+(*psize)];
    			}
    			g_Fra.fraddr[g_Fra.currfra].rpx=g_Fra.fraddr[g_Fra.currfra].wpx;
    		}
    		else
    		{
    			for((*psize)=0;g_Fra.fraddr[g_Fra.currfra].rpx<RXBUFFSIZE;g_Fra.fraddr[g_Fra.currfra].rpx++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[g_Fra.fraddr[g_Fra.currfra].rpx];
    				(*psize)++;
    			}
    			g_Fra.fraddr[g_Fra.currfra].rpx = 0;
    			
    			while(g_Fra.fraddr[g_Fra.currfra].rpx<g_Fra.fraddr[g_Fra.currfra].wpx)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[g_Fra.fraddr[g_Fra.currfra].rpx];
    				(*psize)++;
    				g_Fra.fraddr[g_Fra.currfra].rpx++;
    			}
    			
    		}
    		g_Fra.currfra = (g_Fra.currfra+1)%FRADDRMAX;
    		printf("currfra=%d,nextfra=%d\r\n",g_Fra.currfra,g_Fra.nextfra);
    		rtflg = 1;
    	}
    	return rtflg;
    }
    
     /**
      * @brief  JustAFra获取一帧数据
      * @param  pbuff--获取一帧数据的数组,psize--获取的数目
      * @retval rtflg--0代表没有获取数据,1代表获取到数据
      */
    uint8_t JustAFra(uint8_t *pbuff,uint8_t *psize)
    {
    	uint8_t rtflg=0;
    	if(RxBuff.rp != RxBuff.wp)
    	{
    		rtflg = 1;
    		printf("RxBuff.rp=%d,RxBuff.wp=%d\r\n",RxBuff.rp,RxBuff.wp);
    		
    		if(RxBuff.rp<RxBuff.wp)
    		{
    			for(*psize=0;*psize<(RxBuff.wp-RxBuff.rp);(*psize)++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[RxBuff.rp+(*psize)];
    			}
    			RxBuff.rp = RxBuff.wp;
    		}
    		else
    		{
    			for((*psize)=0;RxBuff.rp<RXBUFFSIZE;RxBuff.rp++)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[RxBuff.rp];
    				(*psize)++;
    			}
    			RxBuff.rp = 0;
    			
    			while(RxBuff.rp<RxBuff.wp)
    			{
    				pbuff[(*psize)] = RxBuff.rxarr[RxBuff.rp];
    				(*psize)++;
    				RxBuff.rp++;
    			}
    		}
    	}
    	return rtflg;
    }
    
    
    
    

    bsp_usart_dma.h:

    /*******************************************************
    *设计:陈文德
    *版本:V1.0
    *******************************************************/
    #ifndef __USARTDMA_H
    #define	__USARTDMA_H
    
    
    #include "stm32f10x.h"
    #include <stdio.h>
    
    
    // 串口工作参数宏定义
    #define  DEBUG_USARTx                   USART1
    #define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
    #define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
    #define  DEBUG_USART_BAUDRATE           115200
    
    // USART GPIO 引脚宏定义
    #define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
    #define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
        
    #define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
    #define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
    #define  DEBUG_USART_RX_GPIO_PORT       GPIOA
    #define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10
    
    // 串口对应的DMA请求通道
    #define  USART_TX_DMA_CHANNEL     DMA1_Channel4
    
    #define  USART1RX_DMA_CHANNEL     DMA1_Channel5
    // 外设寄存器地址
    #define  USART_DR_ADDRESS        (USART1_BASE+0x04)
    // 一次发送的数据量
    #define  SENDBUFF_SIZE            500
    #define  RXBUFFSIZE       50    //接收缓冲区的大小
    /**串口接收缓冲区**/
    typedef struct __USART1RXBUFF
    {
    	uint16_t wp;  //接收缓冲区写地址
    	uint16_t rp;  //接收缓冲区的读地址
    	uint8_t  rxarr[RXBUFFSIZE];  //接收缓冲区实体
    }_USART1RXBUFF;
    
    /**帧地址结构体**/
    typedef struct __FRAMEADDR
    {
    	uint16_t wpx;  //本帧写地址的索引
    	uint16_t rpx;  //本帧读地址的索引
    }_FRAMEADDR;
    
    #define  FRADDRMAX  10  //最多能记录的帧
    /**帧属性结构体**/
    typedef struct __FRAMEATTRI
    {
    	_FRAMEADDR fraddr[FRADDRMAX];  //每帧的地址,队列主体
    	uint8_t currfra;  //当前处理帧
    	uint8_t nextfra;  //下一个帧
    }_FRAMEATTRI;
    
    
    
    void USART1_Init(void);
    void USART1IDLE_IRQ(void);
    
    uint8_t GetAFra(uint8_t *pbuff,uint8_t *psize);
    uint8_t JustAFra(uint8_t *pbuff,uint8_t *psize);
    #endif /* __USARTDMA_H */
    
    
    
    

    main.c

    #include "stm32f10x.h"
    #include "bsp_usart_dma.h"
    static void Delay(__IO u32 nCount); 
    
    /**
      * @brief  主函数
      * @param  无
      * @retval 无
      */
    int main(void)
    {
    	static uint8_t arr[10];
    	static uint8_t getsize=0;
    	static uint8_t test = 0;
    	uint16_t temp;
    	uint8_t flg;
    	/* 初始化USART */
    	USART1_Init();
    	while(1)
    	{
    
    		flg = GetAFra(arr,&getsize);
    		if(flg!=0)
    		{
    			if(test==8)
    			{
    				test = 0;
    			}
    			if(test == 0)
    			{
    				Delay(0x2ffffff);
    			}
    			test++;
    			printf("getsize=%d\r\n",getsize);
    			for(temp=0;temp<getsize;temp++)
    			{
    				printf("%d ",arr[temp]);
    			}
    			printf("\r\n");
    			getsize = 0;
    		}
    	}
    }
    
    static void Delay(__IO uint32_t nCount)	 //简单的延时函数
    {
    	for(; nCount != 0; nCount--);
    }
    
    展开全文
  • 之前有过一篇文章,不过那个只支持1个串口,而stm32f103大容量有5个串口,写这一篇的目的正是为了...初始化说明:5个串口并非所有串口都支持DMA传输,由下表所知,只有串口1-4具备DMA,而串口5无,所以,本程序中,串

    之前有过一篇文章,不过那个只支持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();
    	}	
    }
    

    其余与上述一致。

    展开全文
  • 最近在项目中给串口的接收添加DMA,遇到的问题: 1、“配置好”DMA后,但是DMA不工作 初始化串口1为接收、DMA1的通道3,并使能相应的外设,外设的时钟也全部打开,但是通过调试发现,DMA就是不传输数据。 问题点...

    最近在项目中给串口的接收添加DMA,遇到的问题:

    1、“配置好”DMA后,但是DMA不工作

    初始化串口1为接收、DMA1的通道3,并使能相应的外设,外设的时钟也全部打开,但是通过调试发现,DMA就是不传输数据。

    问题点:忽视了DMA的请求映像

    每个DMA控制器都分管着不同的外设DMA请求,通过查表发现串口1的接收只能映射到DMA1的通道5.因此DMA的通道选择错误。现粘贴串口一的中断处理程序如下:(以下程序并没有采用循环队列的方式接受数据)

    static unsigned char APP_ComBuf[100];
    unsigned char Receive_Message[100] = {0};
    static int APP_iConter = 0;
    void USART1_IRQHandler(void)                	
    {
    	u8 Res,i;
    	OS_ERR err;
    #ifdef SYSTEM_SUPPORT_OS	 	
    	OSIntEnter();    
    #endif
    	if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)   
      {
    		   
           DMA_Cmd(DMA1_Channel5,DISABLE);    
           APP_iConter=USART1->DR;//
           APP_iConter=USART1->SR;
           APP_iConter=100-DMA_GetCurrDataCounter(DMA1_Channel5);//  
           APP_ComBuf[APP_iConter]=0;//
           DMA1_Channel5->CNDTR=100;//重新设置传输量,这一步是必须的。
    			 if(APP_iConter>=6)
    			 {
    					for(i=0;i<APP_ComBuf[2];i++)
    					{
    						Receive_Message[i] = APP_ComBuf[i];
    					}
    				  memset(APP_ComBuf,0,sizeof(APP_ComBuf));					
    				  OSTaskQPost((OS_TCB*	)&APPTCB,	
    								     (void*		)Receive_Message,
    								     (OS_MSG_SIZE)Receive_Message[2],
    								     (OS_OPT		)OS_OPT_POST_FIFO,
    				             (OS_ERR*	)&err);
    			 }
           DMA_Cmd(DMA1_Channel5,ENABLE);  
      }
    	 NVIC_Clear(USART1);
    
    #ifdef SYSTEM_SUPPORT_OS	 
    	OSIntExit();  											 
    #endif
    } 	

    在设置好DMA的通道后,每收到数据后,使能DMA后,DMA会自动将串口接收到的数据搬运到存储区App_ComBuff中,如果没有DMA1_Channel5->CNDTR=100;这一步,或者该步配置错误,那么每当串口接收到的数据都会向存储区App_ComBuff中存储,App_ComBuff的地址递增。于此同时APP_iConter也在累积,APP_ComBuf[2]也不能被解析到,因为数据随着APP_ComBuf地址的递增,数据都往后存储,帧长度并未存储到APP_ComBuf[2]中,因此自己认为的APP_ComBuf[2]存储的是帧长度,但是APP_ComBuf[2]始终是0。再次理解一下 DMA1_Channel5->CNDTR=100;这句话的必要性,串口采用的是空闲中断接收数据,并非采用的是接收中断,因此当串口处于空闲中断时,这时候已经传输完成了一帧数据。因此,将 DMA1_Channel5->CNDTR=100;便可以在下次空闲中断时,将串口接收数据寄存器中的数据搬运到存储器中的首地址。这样for(i=0;i<APP_ComBuf[2];i++)这个for循环也才可以得以执行。另:DMA通道x的传输数量寄存器DMA_CNDTRx,当这个寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输。以上是本人通过DMA接收数据的运用的肤浅理解。

    关于采用队列的方式接收串口数据,后面继续更新,谢谢。

    展开全文
  • 设备打开函数plx9054_open完成以下操作:设备私有数据结构内存空间分配及初始化,DMA连续物理内存空间申请,为天气雷达最大距离探测采集数据的大小;调用pci_map_single将分配的DMA内存空间映射为用于DMA传输地址;...

    设备打开函数plx9054_open完成以下操作:设备私有数据结构内存空间分配及初始化,DMA连续物理内存空间申请,为天气雷达最大距离探测采集数据的大小;调用pci_map_single将分配的DMA内存空间映射为用于DMA传输地址;DMA数据缓冲区分配(容量为2 MB),缓冲区保存每次DMA传输中获取的数据;调用request_irq对中断函数p9054_interrupt进行中断号申请;对天气雷达工作参数进行缺省配置,启动雷达工作。设备释放函数PLX9054_close完成的操作与设备打开函数与PLX9054_open相反。IO操作函数PLX9054_ioctl通过sys_ioctl系统调用,完成用户与设备的信息查询、雷达工作参数配置与查询,如驱动软件版本号、雷达当前工作各参数配置和查询,主要用于对FPGA中RAM内存空间的雷达工作参数数据访问。由于这部分数据对访问速度要求不高,使用IO操作能很好地满足设计要求。读设备函数PLX9054_read将驱动DMA数据缓冲区中的数据实时读取到用户空间缓冲区内,完成采集数据从设备到用户的读取操作。

    2.1.3 中断处理及DMA传输

    中断处理及DMA传输是驱动的核心部分,它的设计质量直接影响到雷达采集数据能否正确、实时地从采集系统板传送到设备驱动缓冲区,并实时被用户获取。中断处理工作流程如图2所示。

    a57dacafe1427db44c6f6946fc66d511.gif

    由于DMA数据缓冲区远大于每次DMA数据传输大小,这对于雷达数据采集中因用户读取数据延迟而导致的缓冲区溢出有较好的抑制作用,而相对于现有计算机平台,2 MB内存容量已足够。DMA数据缓冲区设计为循环缓冲区,使用读、写指针维护,在中断处理中更新其写指针,在PLX9054_read中更新其读指针,采用溢出则覆盖的机制,既方便管理,节省资源,同时又可对溢出进行计数,很方便进行实时性能的测试。

    2.2 数据采集处理程序实现

    数据采集处理程序是雷达高速数据采集系统的应用处理程序,完成雷达高速数据采集及对采集数据的数字信号处理、输出天气雷达生成一次产品。其主要流程如图3所示。

    1f08a403047e9160b75d84e705f86669.gif

    在采集I/Q数据时,I/Q两路数据采用交叉采集方式存入FIFO缓冲中,每路数据有效位为23,最高位为符号位。因此,在采集数据处理中需对读取的数据进行I/Q数据分离,即将采集数据恢复为原始I、Q两路数据,同时将24 bit数据转换为32 bit数据方式存储,以满足后面数字信号处理要求。

    在软件设计中,不同数字信号处理功能采用不同数字信号处理函数实现,在采集数据获取前进行选择配置,数字信号处理功能增加可通过增加相关的数字信号处理函数来满足,以方便功能的完善和移植。通过对I/Q数据分离后获得的数据进行相关数字信号处理即可获得天气雷达中的一次产品,如功率和反射率等。

    本文基于Linux2.6平台,设计实现了天气雷达高速数据采集系统的驱动和数据采集及对采集数据的数字信号处理,并在现有天气雷达上完成了调试验证。目前,本设计已成功地应用在现有的天气雷达中。

    展开全文
  • STM32 DMA 循环模式DMA_Mode_Circular详解

    千次阅读 2021-08-09 18:19:10
    目前在网上还没看到详细讲解DMA循环模式的文章,参考手册中只有简单的一段话。仅靠这么一段话,不一定对DMA的模式有深入的了解。本文详细阐述了DMA普通模式与循环模式,相信可以加深你对DMA的理解。
  • 串口DMA传输模式

    2021-10-28 16:09:53
    DMA传输模式前言一、DMA简介(一)DMA系统框图(二)DMA传输通道(三)DMA传输模式1.外设与存储器之间2.存储器与存储器之间3.小结二、使用DMA配置1.打开USART1及DMA模式2.读入数据总结 前言 DMA(Direct Memory ...
  • STM32CubeMX生成的HAL库中,提供了三类串口数据收发的接口,分别为阻塞模式,非阻塞模式和DMA模式,文本主要对DMA模式进行了分析并依据提供的接口提出了更加实用的串口数据收发方案。通过对网上资料的查找和分析,...
  • ZYNQ基础系列(六) DMA基本用法

    万次阅读 多人点赞 2018-03-30 11:00:42
    涉及到高速数据传输时,DMA就显得非常重要了,本文的DMA主要是对PL侧的AXI DMA核进行介绍(不涉及PS侧的DMA控制器)。AXI DMA的用法基本是:PS通过AXI-lite向AXI DMA发送指令,AXI DMA通过HP通路和DDR交换数据,PL...
  • 【STM32】 DMA原理,步骤超细详解,一文看懂DMA

    万次阅读 多人点赞 2020-03-19 21:50:24
    DMA的基本介绍 什么是DMA (DMA的基本定义) DMA,全称Direct Memory Access,即直接存储器访问。 DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。 ...
  • ADC-dma循环问题

    2021-01-26 12:18:06
    初始化dma时候,里面会先使能dma,然后打开相应的中断,然后结果会导致main函数中卡死 卡在箭头处,也就是打不开adc和dma 解决方案 全部注释,只留第一个配置dma的函数
  • STM32定时器触发DMA循环完成数据搬运

    千次阅读 2019-10-11 11:41:47
    通过TIM8的事件触发DMA,从内存中的地址搬运数据到外设的寄存器,例子中的中断部分可以关闭,与功能无关,仅为测试时观察方便。 定时器每产生一次事件(本文以UPDATE为例,CC等其他事件也可实现),DMA被启动一次,...
  • STM32 HAL CubeMX 串口IDLE接收空闲中断+DMA

    万次阅读 多人点赞 2020-03-22 11:01:56
    关于DMA原理部分讲解,及CubeMx配置部分,请参考该文章 【STM32】HAL库 STM32CubeMX教程十一—DMA (串口DMA发送接收) 本篇文章我们仅针对例程进行详解剖析 本篇文章提供两种方法: 一种是 :IDLE 接收空闲中断+DMA ...
  • STM32 AD多通道循环采样后DMA保存数据

    千次阅读 2018-02-08 16:56:28
    STM32 AD多通道循环采样后DMA保存数据:描述:用ADC连续采集8路模拟信号,并由DMA传输到内存。ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ。在每次转换结束后,由DMA循环将转换的数据传输到内存中。程序...
  • HAL_ADC_Start_DMA(&hadc1,(uint32_t *)AD_Value,20); 上面配置如果是word,则更改一下定义就好了,cube生成的代码会随着变: uint32_t AD_Value[20]; //main.c文件 HAL_ADC_Start_DMA(&hadc1,(uint32_t *)...
  • 如需转载请注明地址:https://blog.csdn.net/as480133937/article/details/104927922 DMA的基本介绍 什么是DMA (DMA的基本定义) DMA,全称Direct Memory Access,即直接存储器访问。 DM...
  • 一般的来说数据接收可以按下面方式处理: 在STM32单片机中我一般使用 DMA循环接收 + UART空闲中断 的方式来处理这块业务: 声明一个静态数组作为数据缓存,DMA循环将收到的数据放到该缓存中; 然后启用UART的空闲...
  • 本文的主要内容是使用STM32F407的ADC1来采集8路通道的传感器数据,因为要定时采集,所以使用了定时器功能,另外需要开启DMA功能。首先说一下程序的思路:使用ADC的DMA功能,将ADC数据寄存器的值通过DMA传到内存的...
  • 目录DMA概念STM32F103C8通过DMA连续发送总结参考 DMA概念 DMA是在专门的硬件( DMA...在main.c中while循环中添加: 之后烧录并调试: 总结 本次了解了DMA的相关概念原理,通过DMA传输,提高了数据传输的效率。 参考
  • 选择合适的DMA通道 ADC0使用ch0 /*! \brief configure the DMA peripheral \param[in] none \param[out] none \retval none */ void dma_config(void) { /* ADC_DMA_channel configuration */ dma_parameter...
  • DMA环路测试

    2021-02-02 13:19:15
    利用HP接口实现DDR—DMA—FIFO—DMA—DDR 的数据传输过程(DMA是simple模式,中断实现) DMA IP核三种模式:更多实例可参考项目system.mss中的demo。 simple模式:支持轮询和中断两种方式。 SG模式:支持多通道同时...
  • DMA原理介绍

    千次阅读 2020-12-24 09:34:40
    DMA的基本定义DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。当CPU初始化这个传输动作,传输...
  • STM32 | DMA配置和使用如此简单(超详细)

    万次阅读 多人点赞 2020-04-26 09:42:01
    DMA学习很难吗?其实DMA配置和使用如此简单!本文让你无论在DMA认识和理解上(抽象不抽象都有)还是在DMA配置和使用上有个彻彻底底的认识。文章包括寄存器和库函数讲解,省去你自行翻阅使用手册和固件库手册,点到点...
  • 前言: 本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片: STM32F407ZET6/ STM32F103ZET6 ...DMA工作原理 STM32CubeMX创建...
  • DMA

    千次阅读 2018-09-25 19:23:06
    DMA控制器包含了DMA1和DMA2,其中DMA1有7个通道,DMA2有5个通道,可以理解为传输数据的一种管道。要注意的是,DMA2只存在于大容量单片机中。 一、DMA框图解析 DMA控制器独立于内核,属于一个单独外设,结构结合下图...
  • stm32之DMA彻底研究

    千次阅读 2017-11-06 20:46:48
    在做实验之前,首先必须明白什么是DMADMA的作用又体现在哪里。 DMA,即直接内存存储,在一些数据的...要配置的有DMA传输通道选择,传输的成员和方向、普通模式还是循环模式等等。 void DMA_Configuration(void
  • 宏定义 /*******************************************************/ //DMA发送 #define DEBUG_USART_DR_BASE (USART1_BASE+0x04) #define SEND_BUFF_SIZE ...#define DEBUG_USART_DMA_Tx_CLK RCC_AHB1Periph_D.
  • DMA传输涉及到的概念 关于DMA传输中的burst、transfer、wrap等概念,请参考上一篇: https://blog.csdn.net/booksyhay/article/details/87790497 DMA流程总览 这里截取DSP器件的技术参考手册中的状态图。高清...
  • 承接上一篇,接着讲ADC_DMA功能配置,直接存储器访问(DMA,Direct Memory Access)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。在配置后,可以在无需任何CPU操作的情况下通过DMA快速移动数据,...
  • STM32DMA实验

    千次阅读 2018-09-18 22:34:02
    1.1STM32F4 DMA简介 DMA,全称Direct Memory Access ,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送的同类,能使...
  • DMA的基本概念

    2020-12-31 09:43:30
    DMA允许外围设备和主内存之间直接传输 I/O 数据, DMA 依赖于系统。每一种体系结构DMA传输不同,编程接口也不同。数据传输可以以两种方式触发:一种软件请求数据,另一种由硬件异步传输。在第一种情况下,调用的步骤...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,359
精华内容 8,543
关键字:

循环dma