梳理STM32F429之通信传输部分---NO.10 串口空闲IDEL中断+DMA(可接受任意长度的字符)

目录

 

前言:代码包下载

一、串口与DMA部分知识讲解

二、串口空闲IDEL中断

三、串口配置、串口空闲中断配置、DMA配置、中断服务函数

1、串口的GPIO配置

2、串口配置

3、串口空闲中断配置

4、DMA直接存储器访问配置

5、使能空闲中断

6、stm32f4xx_it.c 的配置

7、main.c

8、注意:要清空数组!

9、效果展示


前言:代码包下载

下载方式一:CSDN下载:https://download.csdn.net/download/qq_38351824/12130954

下载方式二:微信公众号:暂不提供

一、串口与DMA部分知识讲解

如果大家有什么问题,欢迎在下面评论交流!

串口部分的详解梳理STM32F429之通信传输部分---NO.1 串口通讯

DMA直接存储器访问:梳理STM32F429之通信传输部分---NO.2 DMA—直接存储区访问

这里补充一下:串口空闲IDEL中断的一些注意事项:

二、串口空闲IDEL中断

1、串口空闲IDEL中断的作用:

       现在有很多数据处理都要用到不定长数据,而单片机串口的RXNE中断一次只能接收一个字节的数据,没有缓冲区,无法接收一帧多个数据,利用串口IDLE空闲中断的方式接收一帧数据,方法如下:

       实现思路:采用STM32F429的串口1,并配置成空闲中断IDLE模式且使能DMA接收,并同时设置接收缓冲区和初始化DMA。那么初始化完成之后,当外部给单片机发送数据的时候,假设这帧数据长度是200个字节,那么在单片机接收到一个字节的时候并不会产生串口中断,而是DMA在后台把数据默默地搬运到你指定的缓冲区里面。当整帧数据发送完毕之后串口才会产生一次中断,此时可以利用DMA_GetCurrDataCounter();函数计算出本次的数据接受长度,从而进行数据处理

2、注意事项:避免一直进入空闲中断!

       空闲中断是在检测到在数据收受后,总线上在一个字节的时间内没有再接收到数据时发生。即串口的RXNE位被置位之后才开始检测,检测到空闲之后,串口的CR1寄存器的IDLE位被硬件置1,必须采用软件将IDLE位清零才能避免反复进入空闲中断。具体的做法是先读取USART_SR,再读取USART_DR。需要注意的是,不能采用库函数USART_ClearFlag()或者USART_ClearItPending()来清除IDEL标志位。

USART_ReceiveData(USART1);    清除标志位 

三、串口配置、串口空闲中断配置、DMA配置、中断服务函数

1、串口的GPIO配置

  GPIO_InitTypeDef  GPIO_InitStructure;	
	
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
  /* 使能 GPIOC 时钟 */

  /* GPIO初始化 */	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//输出推挽
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHZ	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	 
    GPIO_Init(GPIOC, &GPIO_InitStructure);	

2、串口配置


  USART_InitTypeDef USART_InitStructure;


 /* 连接 PXx 到 USARTx_Tx*/
  GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_USART6);
 /* 连接 PXx 到 USARTx_Rx*/
  GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_USART6);	
	
	
	USART_InitStructure.USART_BaudRate = 115200;      //串口波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;   /* 硬件流控制:不使用硬件流 */
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx ;  /* USART模式控制:同时使能接收和发送 */
	USART_InitStructure.USART_Parity = USART_Parity_No; /* 校验位选择:不使用校验 */
	USART_InitStructure.USART_StopBits = USART_StopBits_1; /* 停止位:1个停止位 */
	USART_InitStructure.USART_WordLength = USART_WordLength_8b; /* 字长(数据位+校验位):8 */
	
	USART_Init(USART6, &USART_InitStructure);

3、串口空闲中断配置

	NVIC_InitTypeDef  NVIC_InitStructure;

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);     /* 嵌套向量中断控制器组选择 */
	NVIC_InitStructure.NVIC_IRQChannel = USART6_IRQn;   /* 配置USART为中断源 */
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;               /* 使能中断 */
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    /* 抢断优先级为1 */
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;           /* 子优先级为1 */

	NVIC_Init(&NVIC_InitStructure);

4、DMA直接存储器访问配置

(1)开启DMA时钟、复位DMA(根据下图选择DMA?、数据流和通道)

(2)DMA的结构体配置

说明:

  • (uint32_t) 是强制转换的意思,因为DMA结构体需要的是32位无符号类型数据;
  • &USART6->DR:串口数据寄存器地址(此处需要填数据传来的地址即下图的寄存器,USART_DR寄存器相对于USART6偏移了0x04)

(3)清除DMA所有标志
  DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF5);       DMA2 通道5 数据流1
  DMA_ITConfig(DMA2_Stream1, DMA_IT_TE, ENABLE);   DMA2 数据流1 传输错误使能中断


(4)开启串口DMA接收
    USART_DMACmd(USART6, USART_DMAReq_Rx,     ENABLE); 
(5)使能DMA
  DMA_Cmd (DMA2_Stream1,ENABLE);

  DMA_InitTypeDef   DMA_InitStructure;

 // 开启DMA时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
  // DMA复位
  DMA_DeInit(DMA2_Stream1);  
  // 设置DMA通道
	
  DMA_InitStructure.DMA_Channel = DMA_Channel_5;  
  /*设置DMA源:串口数据寄存器地址*/
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART6->DR);
  // 内存地址(要传输的变量的指针)
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RevBuff;
  // 方向:从外设到内存	
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  // 传输大小	
  DMA_InitStructure.DMA_BufferSize = (uint32_t)5000;
  // 外设地址不增	    
  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_VeryHigh; 
  // 禁止内存到内存的传输
  /*禁用FIFO*/
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;        
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    
  /*存储器突发传输 1个节拍*/
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;    
  /*外设突发传输 1个节拍*/
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; 


   
  /*配置DMA2的数据流7*/		   
  DMA_Init(DMA2_Stream1, &DMA_InitStructure);
	

  // 清除DMA所有标志
  DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF2);
  DMA_ITConfig(DMA2_Stream1, DMA_IT_TE, ENABLE);
   // 开启串口DMA接收
	USART_DMACmd(USART6, USART_DMAReq_Rx,     ENABLE); 
	// 使能DMA
  DMA_Cmd (DMA2_Stream1,ENABLE);

5、使能空闲中断

USART_IT_IDLEUSART_IT_RXNE区别 
当接收到1个字节,会产生USART_IT_RXNE中断 
当接收到一帧数据,就会产生USART_IT_IDLE中断

	// 开启 串口空闲 IDEL 中断
  USART_ITConfig(USART6, USART_IT_IDLE, ENABLE);	 
	// 使能串口 
  USART_Cmd(USART6, ENABLE);

6、stm32f4xx_it.c 的配置

#include "stm32f4xx_it.h"
#include "bsp_usart_dma.h" 
int Flag=0;

void USART6_IRQHandler(void)
{
	if(USART_GetITStatus(USART6,USART_IT_IDLE)!=RESET)
	{	
		USART_ReceiveData(USART6); /* 清除标志位 */
		Uart_DMA_Rx_Data();  
    Flag=1;		
	}
}

7、main.c

/**
  ******************************************************************************
  * @file    main.c
  * @author  Sumjes
  * @version V1.0
  * @date    2020-02-02
  * @brief   串口接发测试,串口接收到数据后马上回传。
  ******************************************************************************
  * @attention
  *
  * 实验平台: STM32 F429 开发板
  * 博客    : https://blog.csdn.net/qq_38351824
  * 公众号  : Tech云
  *
  ******************************************************************************
  */
  
#include "stm32f4xx.h"
#include "bsp_usart_dma.h" 

/* 标准库头文件 */
#include <string.h>

extern int Flag;
extern char RevBuff[5000];


int main(void)
{	
  /*初始化USART6 配置模式为 115200 8-N-1,中断接收*/
  usart6_init();
	
	/* 发送一个字符串 */
	printf("这是一个串口DMA空闲中断接收回显实验\r\n");
	
  while(1)
	{	
		if(Flag)
		{
			printf("接收到数据,回发==|%s|==\r\n",RevBuff);
			memset(RevBuff,0,5000);/* 清零 */			
			Flag=0;			
		}
	}	
}



/*********************************************END OF FILE***************************************/

8、注意:要清空数组!

memset(RevBuff,0,5000);/* 清零 */	

9、效果展示

发布了357 篇原创文章 · 获赞 693 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/qq_38351824/article/details/104143631