使用STM32F103标准库和DMA技术实现高效的串口数据收发

本文分为两个部分,第一部分为串口进行简单的数据收发;第二部分为串口使用DMA进行数据收发。熟悉第一部分的小伙伴们可以直接参考第二部分内容,文档内容仅供参考。

一、使用串口进行简单的数据收发

1、使能串口以及对应GPIO时钟

	//使能GPIOA 以及串口1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE); 

2、初始化对应IO

//初始化对应IO
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

3、配置USART结构体

	//配置USART参数
	USART_InitStructure.USART_BaudRate=115200;
	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_HardwareFlowControl=USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
    USART_Init(USART1,&USART_InitStructure);

4、配置串口中断

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//配制中断源
	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);

5、使能串口接收中断以及串口

	//使能串口接收中断
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
    //使能串口1
	USART_Cmd(USART1,ENABLE);	

6、编写串口中断函数

void USART1_IRQHandler(void)                               
{   
	
	u8 temp=0;
	if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)
	{
		temp=USART_ReceiveData(USART1);
		USART_SendData(USART1,temp);
	}
}

7、编写数据收发验证函数(验证使用软件为正点原子提供的XCOM串口调试助手)

//通过串口1发送单个数据 
USART_SendData(USART1,0xA0);  	
//检查指定的串口标志位是否设置
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);

二、串口+DMA进行数据收发

1、将串口接收中断使能修改为空闲总线中断(可以直接参考代码;也可以查看注释修改位置)

void Uart_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	//使能GPIOA 以及串口1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE); 
	//初始化对应IO
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	//配置USART参数
	USART_DeInit(USART1);
	USART_InitStructure.USART_BaudRate=115200;
	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_HardwareFlowControl=USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
    USART_Init(USART1,&USART_InitStructure);
    //配置中断控制器
	//配置中断控制器组选择
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	//配制中断源
	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);

    //使能串口接收中断修改为串口总线空闲中断
	//USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
    //使能串口1
	USART_Cmd(USART1,ENABLE);	
	
}

2、编写DMA数据输出函数

void DMA_SendConfig(DMA_Channel_TypeDef* DMAy_Channelx,u32 cpar,u32 cmar,u16 buf)
{
	
	DMA_InitTypeDef DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
    //使能DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	//配置DMA参数
	
	//配置内存地址
	DMA_InitStructure.DMA_MemoryBaseAddr=cmar;
	//配置外设地址
	DMA_InitStructure.DMA_PeripheralBaseAddr=cpar; 
	//配置数据传输方向 外设作为数据传输目的地(外设作为数据源)
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
	//配置数据传输大小
	DMA_InitStructure.DMA_BufferSize=buf;
	//禁止外设地址递增
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	//允许内存地址是否递增
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
	//配置外设数据单位 8位字节传输
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
	//配置内存数据单位
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	//配置DMA模式 单次传输(循环传输)
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
	//配置DMA优先级
	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;
	//配置是否使能从内存到内存传输
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
	//配置DMA通道初始化
	DMA_Init(DMAy_Channelx,&DMA_InitStructure);
	DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
	
		//配置串口接收中断源
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	//使能DMA
//	DMA_Cmd(DMAy_Channelx,ENABLE);
}

3、编写DMA数据接收函数

void DMA_ReceiveConfig(DMA_Channel_TypeDef* DMAy_Channelx,u32 cpar,u32 cmar,u16 buf)
{
	
	DMA_InitTypeDef DMA_InitStructure;
    //使能DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	//配置DMA参数
	//配置内存地址
	DMA_InitStructure.DMA_MemoryBaseAddr=cmar;
	//配置外设地址
	DMA_InitStructure.DMA_PeripheralBaseAddr=cpar; 
	//配置数据传输方向 外设作为数据源
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;
	//配置数据传输大小
	DMA_InitStructure.DMA_BufferSize=buf;
	//禁止外设地址递增
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	//允许内存地址是否递增
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;
	//配置外设数据单位 8位字节传输
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
	//配置内存数据单位
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	//配置DMA模式 单次传输(循环传输)
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
	//配置DMA优先级
	DMA_InitStructure.DMA_Priority=DMA_Priority_High;
	//配置是否使能从内存到内存传输
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
	//配置DMA通道初始化
	DMA_Init(DMAy_Channelx,&DMA_InitStructure);
	//使能DMA
	DMA_Cmd(DMAy_Channelx,ENABLE);
	
}

4、修改USART1中断函数

u16 Data_LEN=100; //数据长度
u8 SendBuf[100];  
u8 ReceBuf[100];

void USART1_IRQHandler(void)                               
{   
	
	uint32_t temp = 0;
	uint16_t i = 0;
	
	if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {
    //USART_ClearFlag(USART3,USART_IT_IDLE);
    //temp = USART1->SR;  //串口状态寄存器
    temp = USART1->DR; // 串口数据寄存器 (取消后会有数据丢位现象 原因待查)
    DMA_Cmd(DMA1_Channel5,DISABLE); //禁用DMA接收通道
	temp = Data_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); //获取DMA1通道5剩余数据
	for (i = 0;i < temp;i++)
	 {
	   Data_Receive_Usart = ReceBuf[i]; //变量接收DMA通道5的数据	
	   SendBuf[i]=Data_Receive_Usart;	 //将接收到的数据传到SendBuf中
       DMA1_Channel4->CNDTR = (uint16_t)temp; //设置需要传输的数据量
       DMA_Cmd(DMA1_Channel4, ENABLE);   		
	 }
	//设置传输数据长度
	DMA_SetCurrDataCounter(DMA1_Channel5,Data_LEN);  //设置当前DMA1通道5中的数据量
    //打开DMA
	DMA_Cmd(DMA1_Channel5,ENABLE);
    } 
} 	

5、编写DMA1_Channel4中断函数

void DMA1_Channel4_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_FLAG_TC4)==SET)  //传输完成中断
    {
            DMA_ClearFlag(DMA1_FLAG_GL4);    //清楚通道4全局标志位    
            DMA_Cmd(DMA1_Channel4, DISABLE); //关闭数据输出通道
    }
}

6、编写串口DMA收发数据测试函数

		DMA_ReceiveConfig(DMA1_Channel5,(u32)&USART1->DR,(u32)ReceBuf,100);
	    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
		DMA_SendConfig(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuf,100);
		USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32F103标准库和DMA技术实现高效的串口数据收发

发表评论