精华内容
下载资源
问答
  • 也就是说,在循环模式下,当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--);
    }
    
    展开全文
  • STM32高速串口通信DMA收发实现 测试平台 平台 晶振 BSP库 串口 STM32F030C8T6 12MHz 标准库 UART1、UART2 STM32F103ZET6 8MHz 标准库 UART1、UART2 ... 实现功能 收/发环形缓冲区 不定长度接收处理 高速(1.5Mbps)...
  • 另一种是循环模式,使用一次发送会一直发送。 这里发送我选择普通模式,接收选择循环模式。 省略之前串口相关设置,想了解的可以看我之前串口的相关文章 生成代码, 定义相关变量 #define RXBUFFERSIZE 256 //最大...

    stm32串口使用DMA模式接收发送数据

    在cubeMX中调整DMA相关设置
    在DMA Setting里点击Add添加USART1_TX,Mode有两种模式,
    一种是普通模式,使用一次发送语句就发一次;
    另一种是循环模式,使用一次发送会一直发送。
    这里发送我选择普通模式,接收选择循环模式。
    在这里插入图片描述
    在这里插入图片描述
    省略之前串口相关设置,想了解的可以看我之前串口的相关文章

    生成代码,

    定义相关变量

    #define RXBUFFERSIZE  256     //最大接收字节数
    char RxBuffer[RXBUFFERSIZE];   //接收数据
    uint8_t aRxBuffer;			//接收中断缓冲
    uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
    

    在main函数中打开串口DMA循环接收

    /* USER CODE BEGIN 2 */
    HAL_UART_Receive_DMA(&huart1, (uint8_t *)&aRxBuffer, 1);
    /* USER CODE END 2 */
    

    while循环中添加发送函数

    HAL_Delay(1000);
    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)"test\n", 5); 	
    

    重写接收中断回调函数,功能是把收到的数据再发送出去

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      /* Prevent unused argument(s) compilation warning */
      UNUSED(huart);
      /* NOTE: This function Should not be modified, when the callback is needed,
               the HAL_UART_TxCpltCallback could be implemented in the user file
       */
     
    	if(Uart1_Rx_Cnt >= 255)  //溢出判断
    	{
    		Uart1_Rx_Cnt = 0;
    		memset(RxBuffer,0x00,sizeof(RxBuffer));
    		HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF); 	
            
    	}
    	else
    	{
    		RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
    	
    		if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
    		{
    			HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
          		while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
    			Uart1_Rx_Cnt = 0;
    			memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
    		}
    	}
    	
    	//HAL_UART_Receive_DMA(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
    }
    

    这里为什么不用

    HAL_UART_Transmit_DMA()
    

    这个函数呢?

    实际测试过程中发现在回调函数中使用DMA方式发送数据会有丢包现象,在main函数中使用是没有问题的,暂时没有找到解决办法。

    这里主要测试了DMA接收数据是正常的,使用DMA串口收发的主要目的也是防止大量接收串口数据时产生大量中断,占用大量cpu时间,导致程序运行卡顿。

    运行结果
    在这里插入图片描述

    展开全文
  • //DMA接收定义为循环模式,只能接受定长数据 //串口DMA接收中断调用的回调函数和串口接收中断是同一个 uint8_t rxch[5]; uint8_t trch[]="transmit dma:\r\n"; int main(void) { HAL_UART_Transmit_DMA(&huart1,...
    //DMA接收定义为循环模式,只能接受定长数据
    //串口DMA中断调用的回调函数和串口接收中断是同一个
    //中断进入void DMA1_Channel5_IRQHandler(void)
    uint8_t rxch[5];
    uint8_t trch[]="transmit dma:\r\n";
    int main(void)
    {
    HAL_UART_Transmit_DMA(&huart1,trch,15);
    HAL_UART_Receive_DMA(&huart1,rxch,5);
      while (1)
      {
      }
    
    }
    //DMA接收完成回调函数和这是同一个
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      if(huart->Instance==USART1)
      {
          printf("%s\r\n",rxch);
          HAL_UART_Transmit_DMA(&huart1,rxch,5);
      }
    
    }
    
    //DMA传输完成回调函数和这是同一个
    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
    {
    	uint8_t tx_str[] = "Data Transfer completed\r\n";
    	HAL_UART_Transmit(&huart1,tx_str,sizeof(tx_str),200);
    }
    
    
    展开全文
  • STM32 USART串口DMA接收和发送模式

    万次阅读 2016-05-18 16:33:00
    STM32 USART串口DMA接收和发送模式 温馨提示:需要下载资料的请到原文下载 串口DMA发送: 发送数据的流程: 前台程序中有数据要发送,则需要做如下几件事 1. 在数据发送缓冲区内放好要发送的数据,...

    STM32 USART串口DMA接收和发送模式


    温馨提示:需要下载资料的请到原文下载

    串口DMA发送:
    发送数据的流程:
    前台程序中有数据要发送,则需要做如下几件事
    1.      在数据发送缓冲区内放好要发送的数据,说明:此数据缓冲区的首地址必须要在DMA初始化的时候写入到DMA配置中去。
    2.      将数据缓冲区内要发送的数据字节数赋值给发送DMA通道,(串口发送DMA和串口接收DAM不是同一个DMA通道)
    3.      开启DMA,一旦开启,则DMA开始发送数据,说明一下:在KEIL调试好的时候,DMA和调试是不同步的,即不管Keil 是什么状态,DMA总是发送数据。
    4.      等待发送完成标志位,即下面的终端服务函数中的第3点设置的标志位。或者根据自己的实际情况来定,是否要一直等待这个标志位,也可以通过状态机的方式来循环查询也可以。或者其他方式。
    判断数据发送完成:
    启动DMA并发送完后,产生DMA发送完成中断,在中断函数中做如下几件事:
    1. DMA发送完成中断标志位
    2. 关闭串口发送DMA通道
    3. 给前台程序设置一个软件标志位,说明数据已经发送完毕
    串口DMA接收:
    接收数据的流程:
    串口接收DMA在初始化的时候就处于开启状态,一直等待数据的到来,在软件上无需做任何事情,只要在初始化配置的时候设置好配置就可以了。
    判断数据数据接收完成:
           这里判断接收完成是通过串口空闲中断的方式实现,即当串口数据流停止后,就会产生IDLE中断。这个中断里面做如下几件事:
    1.      关闭串口接收DMA通道,2点原因:1.防止后面又有数据接收到,产生干扰。2.便于DMA的重新配置赋值,下面第4点。
    2.      清除DMA 所有标志位
    3.      从DMA寄存器中获取接收到的数据字节数
    4.      重新设置DMA下次要接收的数据字节数,注意,这里是给DMA寄存器重新设置接收的计数值,这个数量只能大于或者等于可能接收的字节数,否则当DMA接收计数器递减到0的时候,又会重载这个计数值,重新循环递减计数,所以接收缓冲区的数据则会被覆盖丢失。
    5. 开启DMA通道,等待下一次的数据接收,注意,对DMA的相关寄存器配置写入,如第4条的写入计数值,必须要在关闭DMA的条件进行,否则操作无效。
    说明一下,STM32IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。

    /**********************************************************************************************纯属搬运***********************************************************************************************/

    上面文字性的分析已经写的很好了,我就不累赘了。原作者给了个例子,我移植到了战舰上,就帖个工程吧。
    再附送一个F207上的例子(同理F4),这个跟F1系列的差异还是蛮大的。
    /*******************************************************************************
    * 函数名        : dma_uart2_init
    * 描述          : 初始化DMA串口2
    *             DMA1
    *             DMA_Channel_4 通道4
    *             DMA2_Stream4  数据流6
    * 参数          :  
    * 返回值        : 无
    *******************************************************************************/
    void dma_uart2_init(void)
    {
        DMA_InitTypeDef DMA_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
         
        /* DMA clock enable */
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // 开启DMA2时钟
       
    //=DMA_Configuration==============================================================================//
    //    DMA_Cmd(DMA2_Stream7, DISABLE);                                         // 关DMA通道
         
        DMA_DeInit(DMA1_Stream6); // 恢复缺省值
        while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){}//等待DMA可配置

        DMA_InitStructure.DMA_Channel = DMA_Channel_4;                          //通道4
        DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DR);          // 设置串口发送数据寄存器
        DMA_InitStructure.DMA_Memory0BaseAddr = (u32)DMA_UART2_SendBuf;         // 设置发送缓冲区首地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                 // 设置外设位目标,内存缓冲区 -> 外设寄存器
        DMA_InitStructure.DMA_BufferSize = 0;                                   // 需要发送的字节数,这里其实可以设置为0,因为在实际要发送的时候,会重新设置次值
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式,不循环
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置
            DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;//DMA_FIFOMode_Disable      
            DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
            DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
            DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
        DMA_Init(DMA1_Stream6, &DMA_InitStructure);               // 写入配置
         
        /* Enable the DMA Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn;   // 发送DMA通道的中断配置
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9;  // 优先级设置
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
         
        DMA_ITConfig(DMA1_Stream6, DMA_IT_TC, ENABLE);// 开启DMA通道传输完成中断
        USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);// 开启串口DMA发送
    }

    /*******************************************************************************
    * 函数名        : uart2_init
    * 描述          : 初始化串口2
    * 参数          : bound:波特率
    * 返回值        : 无
    *******************************************************************************/
    void uart2_init(u32 bound)//初始化串口2
    {
        //GPIO端口设置
            NVIC_InitTypeDef NVIC_InitStructure;
            GPIO_InitTypeDef GPIO_InitStructure;
            USART_InitTypeDef USART_InitStructure;

        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOb时钟
            RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//USART2
         
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);//重映射,TX
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);//重映射,RX
             
            GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //翻转速度
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //输入输出设置,输入/输出/复用/模拟
            GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //输出模式,开漏/推挽
            GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //输入模式,浮空/上拉/下拉
            GPIO_Init(GPIOA, &GPIO_InitStructure);

       //USART 初始化设置
            USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
            USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
            USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
            USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
            USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
            USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
         
            USART_Init(USART2, &USART_InitStructure);//初始化串口  
         
        //Usart1 NVIC 配置         
            NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
            NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级2
            NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;       //子优先级3,子优先级不能为0???
            NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //IRQ通道使能
            NVIC_Init(&NVIC_InitStructure);//根据指定的参数初始化VIC寄存器

            USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断
            USART_Cmd(USART2, ENABLE);//使能串口
         
        dma_uart2_init();//初始化串口DMA发送
    }

    /*******************************************************************************
    * 函数名        : USART2_IRQHandler
    * 描述          : 串口2中断服务函数,接收采集板数据
    * 参数          : 无
    * 返回值        : 无
    *******************************************************************************/
    void USART2_IRQHandler(void)//串口2中断服务程序
    {         
            u8 c;

            if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)//接收中断
            {
            USART_ClearITPendingBit(USART2, USART_IT_RXNE);
             
            LED2 = !LED2;
                    c = USART_ReceiveData(USART2);//读取接收寄存器,读数据会清除中断
                    write_loop_2_buf(c);
            }
    }

    //DMA 发送应用源码
    void DMA1_Stream6_IRQHandler(void)
    {
        static portBASE_TYPE xHigherPriorityTaskWoken;
         
        xHigherPriorityTaskWoken = pdFALSE;
         
        if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6))
        {
    //        printf("DMA1中断\r\n");
            DMA_ClearFlag(DMA1_Stream6, DMA_IT_TCIF6);// 清除标志
            DMA_Cmd(DMA1_Stream6, DISABLE);           // 关闭DMA通道
             
            //发送信号量
            xSemaphoreGiveFromISR(xSemaphoreHandle_DMA_USART2_SendFlag, &xHigherPriorityTaskWoken);//发送完成标志
             
            if(xHigherPriorityTaskWoken == pdTRUE) //给出信号量使任务解除阻塞,如果解除阻塞的任务的优先级高于当前任务的优先级——强制进行一次任务切换
            {
                portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
            }
        }
    }

    void DMA_USART2_SendData(u8 *buf, u16 size)//DMA串口2发送数据
    {
        memcpy(DMA_UART2_SendBuf, buf, size);//拷贝到DMA缓冲区
         
        DMA1_Stream6->NDTR = (u16)size; // 设置要发送的字节数目
    //    printf("发送字节数=%d\r\n",size);
        DMA_Cmd(DMA1_Stream6, ENABLE);  //开始DMA发送
         
        xSemaphoreTake(xSemaphoreHandle_DMA_USART2_SendFlag, portMAX_DELAY);//使用信号量等待发送完成,无超时

    串口DMA接收对于全双工模式挺适合,半双工模式实用性就一般了,不如中断加FIFIO操作性好。DMA+FIFO没有研究明白是否可行。

    对于485有一个不能忽视的问题:

    当DMA完成中断触发的时候,大约有2个字节还没有发送,一般延时2ms就差不多了。为了保险和适应不同波特率,我写成了查询+阻塞(OS延时)。


    展开全文
  • 串口工作在DMA模式下有时接收异常

    千次阅读 2016-11-25 16:56:05
    1 前言客户反馈在使用STM32F205的串口工作在DMA模式时,有时能够接收数据,有时完全没有数据,但如果换成中断模式来接收又能100%正常收到数据。2 复现现象2.1 问题背景与客户沟通,客户使用的是STM32F2标准库V1.1.0...
  • SPI接口ADC; 代码功能描述:有xyz3路数据,每路1个双通道adc采集,从x路依次数据并用串口发送出去,14字节,2250000bps; 方案1:按照顺序执行,依次采集各路数据,然后uart发送。这样也行,但是我不喜欢,这样会...
  • 前言: 本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片: STM32F407ZET6/ STM32F103ZET6 ...DMA工作原理 STM32CubeMX创建...
  • STM32之串口DMA接收不定长数据

    万次阅读 多人点赞 2018-09-17 15:49:25
    STM32之串口DMA接收不定长数据 本文为杰杰原创,如需转载请说明出处 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? ...
  • 串口模式实现有三种 1.普通模式:在主函数中接收函数 2.中断模式:产生的不影响主程序运行 3. DMA模式:与主函数互不影响,独立运行 本文记录三种DMA模式
  • 串口波特率115200
  • 串口通讯(DMA模式

    2018-01-16 13:25:00
    STM32 串口接收数据时,HAL API 要求指定数据长度。但无论轮询、中断或是DMA方式,都必须完整地接收到这么多字节,程序流程才继续。如何接收变长消息,我想不到特别好的实现方式。一种方式是,轮询加超时。另一种...
  • 本文描述串口DMA收/发机制设计,及STM32串口DMA收/发实现。
  • 串口DMA方式发送&接收

    万次阅读 多人点赞 2019-01-16 16:38:05
    串口DMA方式收发  笔者使用的是STM32F407VET6,共包含6路串口,页尾处程序已将全部串口DMA收发配置完成,本文仅以串口1为例进行讲解。(查看代码可直接跳至第二节或页尾处下载) 1 STM32F4 DMA 简介  DMA,...
  • STM32定时器触发DMA循环完成数据搬运

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

    千次阅读 2020-03-31 09:41:29
    STM32F4 串口DMA接收 STM32F4 串口DMA接收需要注意的点: DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //存储器到外设模式 RX TX不同这里,外设到地址或者地址到外设 这句代码跟发射的不一样。 下面...
  • STM32串口DMA容易忽视的问题

    万次阅读 2016-05-20 16:28:22
    博主昨天晚上在STM32串口DMA的问题上纠结了好长时间,所以今天上午写篇博客来谈谈我对串口DMA发送的理解→_→今天主要讨论三个问题:1、什么叫串口DMA 请求;2、串口简要复习;3、串口DMA发送流程。 1、什么叫串口...
  • USART 串口 DMA 发送和接收

    千次阅读 2015-11-07 15:24:32
    串口DMA发送: 发送数据的流程: 前台程序中有数据要发送,则需要做如下几件事 1. 在数据发送缓冲区内放好要发送的数据,说明:此数据缓冲区的首地址必须要在DMA初始化的时候写入到DMA配置中去。 2. 将数据...
  • STM32串口DMA不定长数据收发   DMA简介   直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。...
  • 最近在项目中给串口的接收添加DMA,遇到的问题: 1、“配置好”DMA后,但是DMA不工作 初始化串口1为接收、DMA1的通道3,并使能相应的外设,外设的时钟也全部打开,但是通过调试发现,DMA就是不传输数据。 问题点...
  • STM32 串口DMA收发(一)

    万次阅读 2018-07-28 11:16:31
    STM32 串口DMA收发数据 一、STM32 DMA简介与功能说明 1、STM32F4 DMA简介 DMA(Direct memory access),即直接存储器访问。用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输的一种方式。它...
  • DMA接收的硬件双缓冲区切换功能目前只能做到定长接收,如果不够检测到接收数据长度不够,芯片会攒够设定的接收长度数据之后才会触发中断,而且使用这种模式,DMA是一定要设置为循环模式而非单次触发模式的,切记。...
  • STM32 串口DMA收发(二)

    千次阅读 2018-08-23 16:32:52
    STM32 串口DMA收发数据 一、STM32 DMA简介与功能说明 1、STM32F4 DMA简介 DMA(Direct memory access),即直接存储器访问。用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输的一种方式。它...
  • 串口DMA的配置和使用。。。1、串口DMA请求映像;2、资源说明;3、DMA初始化配置;4、串口配置。。。 串口DMA DMA利用好无疑会让串口使用起来更加高效,同时CPU还能处理自己的事情,但是DMA的使用却让代码变得更加...
  • 2. RT-Thread 串口框架 2.1. DMA 发送 2.2. DMA接收 2.3. 其他吐槽 3. 我的方案 3.1. DMA 发送 3.2. DMA 接收 4. POSIX 实现简单描述 4.1. write 阻塞 4.2. read 阻塞 4.3. select 中 fpoll 回调的实现 ...
  • 我做这个串口数据接收 dma+空闲中断 加fifo 实现串口的高效收发 ,主要是串口接收的数据长度不定长,时间超时也不好做,还要串口收发的效率要高,采用串口数据的接收 dma+空闲中断+fifo的方式 速度快和效率高,不...
  • 2、循环模式------是否循环操作 3、外设地址增量 4、内存地址增量 5、外设数据宽度 8位,16位还是32位 6、内存地址宽度 7、通道优先级 8、是否是 存储器到存储器------复位后为0,表示外设与存储器通信,所以HAL这位...
  • 串口DMA与空闲中断原理三. CubeMx配置工程四. 驱动代码与应用程序五. 几点勘误 一. 功能与环境 这两天好好整理了一下STM32的串口通信,主要测试DMA方式发送与接收,以及配合串口空闲中断接收不定长数据。前后在F103...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,861
精华内容 1,944
关键字:

串口dma循环模式