#天空星DMA串口中断收发

一、实现功能

串口通过DMA通道打印回来我们发送过去的数据

二、DMA通道映射表

我们要用的是DMA2的channel4的stream5和stream7

 三、通道分布

通道4既可以通过stream5也可以通过stream7,而stream5即为USART1_RX的流,stream7即为USART7_TX的流。

四、部分关键函数

四、记录

(1)使能控制必须得在初始化之后。

(2)DMA发送时,方向为内存到外设。DMA接收时,方向相反。

(3)注意优先级

(4)注意外设地址以及内存地址

(5)不使用FIFO

(6)单次burst触发

(7)普通模式

(8)内存地址自增(存入数组)

(9)单位数据单元为一个字节byte

(10)发送数据时需要先获取接收到的数据单元个数,方便设置放松通道的数据单元个数

(11)发送设置完成之后,需要重新设置接收的数据单元空间(免得堆满)

(12)再准备下一次接受之前记得先把接收完成标志位清0(最好一进入中断就清0)

(13)DMA发送通道使能是在需要发送时才调用的,其他时刻处于失能状态。

五、逻辑

串口配置空闲中断,DMA2的stream5和stream7配置中断(stream5可以不配置),主要涉及到一个需要清除标志位,什么时候清除标志位。串口设置了空闲中断,接收完成之后会产生空闲中断,此时就可以将其标志位DMA接收标志位清除。空闲中断之后马上又会DMA发送数据,前面的stream7的中断配置为(传输完成中断),完成之后进入传输完成中断将标志位清0即可,同时失能(等待下一次接收数据完成再使能DMA发送)

六、代码

/*
 * 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
 * 开发板官网:www.lckfb.com
 * 技术支持常驻论坛,任何技术问题欢迎随时交流学习
 * 立创论坛:club.szlcsc.com
 * 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
 * 不靠卖板赚钱,以培养中国工程师为己任
 */
#include "board.h"
#include "bsp_uart.h"
#include "stdio.h"
#include "string.h"
#include "sys.h"

#define USART_MAX_LEN 400

volatile uint16_t usart1_rx_len = 0;    //接收帧数据的长度
volatile uint16_t usart1_tx_len = 0;    //发送帧数据的长度
volatile uint8_t usart1_recv_end_flag = 0;//帧数据接收完成标志
uint8_t DMA_USART1_RX_BUF[USART_MAX_LEN]={0};   //接收数据缓存
uint8_t DMA_USART1_TX_BUF[USART_MAX_LEN]={0};	//DMA发送缓存

void DMA_USART1_Send(u8 *data,u16 size);

int main(void)
{
        
        board_init();
        //1.定义结构体
	      GPIO_InitTypeDef gpioinit;
	     //2.开启gpio时钟 
      	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	     //3.复用配置(注意此处不可以用 | )
        GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
	      GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
	      //4.配置gpio
	      gpioinit.GPIO_Mode=GPIO_Mode_AF; 
	      gpioinit.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
	      gpioinit.GPIO_OType=GPIO_OType_PP;  
	      gpioinit.GPIO_PuPd=GPIO_PuPd_UP;
	      gpioinit.GPIO_Speed=GPIO_Speed_100MHz;
	      GPIO_Init(GPIOA,&gpioinit);
	      //5.定义串口结构体
	      USART_InitTypeDef usart1_init;
				//6.开启串口时钟
				RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); 
	      //7.配置串口
				usart1_init.USART_BaudRate=115200U;
	      usart1_init.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
				usart1_init.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
				usart1_init.USART_Parity=USART_Parity_No;
				usart1_init.USART_StopBits=USART_StopBits_1;
				usart1_init.USART_WordLength=USART_WordLength_8b;
				USART_Init(USART1,&usart1_init);
				//8.初始化成功清除接收置位
				USART_ClearFlag(USART1,USART_FLAG_RXNE);
        //9.使能串口
				USART_Cmd(USART1,ENABLE);
				//10.定义中断结构体
				NVIC_InitTypeDef nvic_init;
				//11.配置串口中断
				nvic_init.NVIC_IRQChannel=USART1_IRQn;
				nvic_init.NVIC_IRQChannelCmd=ENABLE;
				nvic_init.NVIC_IRQChannelPreemptionPriority=0x00;
				nvic_init.NVIC_IRQChannelSubPriority=0x01;
				NVIC_Init(&nvic_init);
				//12.配置DMA接收中断(RX)
        nvic_init.NVIC_IRQChannel=DMA2_Stream5_IRQn;
				nvic_init.NVIC_IRQChannelCmd=ENABLE;
				nvic_init.NVIC_IRQChannelPreemptionPriority=0x01;
				nvic_init.NVIC_IRQChannelSubPriority=0x01;
				NVIC_Init(&nvic_init);				
				//13.配置DMA发送中断(TX)
				nvic_init.NVIC_IRQChannel=DMA2_Stream7_IRQn;
				nvic_init.NVIC_IRQChannelCmd=ENABLE;
				nvic_init.NVIC_IRQChannelPreemptionPriority=0x01;
				nvic_init.NVIC_IRQChannelSubPriority=0x02;
				NVIC_Init(&nvic_init);
			  //14.允许空闲中断,允许DMA接收发送
				USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
				USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
				USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
				//15.配置DMA2串口1接收RX
				DMA_InitTypeDef DMA_InitStucture;
				//16.开DMA2时钟  CHANNEL4   STREAM5
				RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
				DMA_DeInit(DMA2_Stream5);
				DMA_InitStucture.DMA_BufferSize=USART_MAX_LEN;     //设置DMA的最大数据单位个数
				DMA_InitStucture.DMA_Channel=DMA_Channel_4;       //通道4
				DMA_InitStucture.DMA_DIR=DMA_DIR_PeripheralToMemory;  //外设到存储器
				DMA_InitStucture.DMA_FIFOMode=DMA_FIFOMode_Disable;  //禁止FIFO模式
				DMA_InitStucture.DMA_FIFOThreshold=DMA_FIFOThreshold_Full; //fifo阈值
				DMA_InitStucture.DMA_Memory0BaseAddr=(uint32_t)DMA_USART1_RX_BUF;  //DMA存储器基地址
				DMA_InitStucture.DMA_MemoryBurst=DMA_MemoryBurst_Single; //内存地址递增步进
				DMA_InitStucture.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; //内存数据单位:一个字节
				DMA_InitStucture.DMA_MemoryInc=DMA_MemoryInc_Enable;  //允许内存空间地址自增
				DMA_InitStucture.DMA_Mode=DMA_Mode_Normal;            //普通模式
				DMA_InitStucture.DMA_PeripheralBaseAddr=(uint32_t)&USART1->DR;   //外设基地址       
				DMA_InitStucture.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;    //外设突发单次传输
				DMA_InitStucture.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; //外设数据单位: 一个字节
				DMA_InitStucture.DMA_PeripheralInc=DMA_PeripheralInc_Disable;  //不允许外设地址自增
				DMA_InitStucture.DMA_Priority=DMA_Priority_High;    //DMA高优先级
				DMA_Init(DMA2_Stream5,&DMA_InitStucture);
				DMA_Cmd(DMA2_Stream5,ENABLE);   //使能SMA2的Stream5
				
				
				DMA_DeInit(DMA2_Stream7);    //初始化DMA Stream
        while(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);//等待DMA可配置
		
        //17.配置DMA2串口1发送TX
				DMA_InitStucture.DMA_BufferSize=USART_MAX_LEN;
				DMA_InitStucture.DMA_Channel=DMA_Channel_4;           //通道4
				DMA_InitStucture.DMA_DIR=DMA_DIR_MemoryToPeripheral;  //存储器到外设
				DMA_InitStucture.DMA_FIFOMode=DMA_FIFOMode_Disable;  //禁止FIFO模式
				DMA_InitStucture.DMA_FIFOThreshold=DMA_FIFOThreshold_1QuarterFull; //fifo阈值
				DMA_InitStucture.DMA_Memory0BaseAddr=(uint32_t)DMA_USART1_TX_BUF;  //DMA存储器基地址
				DMA_InitStucture.DMA_MemoryBurst=DMA_MemoryBurst_Single; //内存地址递增步进
				DMA_InitStucture.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte; //内存数据单位:一个字节
				DMA_InitStucture.DMA_MemoryInc=DMA_MemoryInc_Enable;  //允许内存空间地址自增
				DMA_InitStucture.DMA_Mode=DMA_Mode_Normal;            //普通模式
				DMA_InitStucture.DMA_PeripheralBaseAddr=(uint32_t)&USART1->DR;   //外设基地址       
				DMA_InitStucture.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;    //外设突发单次传输
				DMA_InitStucture.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; //外设数据单位: 一个字节
				DMA_InitStucture.DMA_PeripheralInc=DMA_PeripheralInc_Disable;  //不允许外设地址自增
				DMA_InitStucture.DMA_Priority=DMA_Priority_Medium;    //DMA中优先级
				DMA_Init(DMA2_Stream7,&DMA_InitStucture);
				DMA_Cmd(DMA2_Stream7,DISABLE);        //先失能DMA发送,在需要使用时再使能
				//18.配置DMA发送完成后中断
        DMA_ITConfig(DMA2_Stream7,DMA_IT_TC,ENABLE);  //DMA发送完成中断				
				
        DMA_USART1_Send((uint8_t *)"hello world!!\r\n",13);
        while(1)
        {       
          
        }
        

}

//串口1中断服务函数
void USART1_IRQHandler(void)
{  
	//19.1 DMA接收完成,进入空闲
	 if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)
	 {
  //19.2 失能DMA接收	
		 DMA_Cmd(DMA2_Stream5,DISABLE);
	//19.3 获得收到的数据的长度
		 usart1_rx_len=USART_MAX_LEN-DMA_GetCurrDataCounter(DMA2_Stream5); //DMA_GetCurrDataCounter函数是获取剩余空间
  //19.4 清除DMA接收完成标志位	
		 DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);
	 /*
     19.5到19.7可以替换为DMA_USART1_Send(DMA_USART1_RX_BUF,usart1_rx_len);
	 */	
	//19.5 往发送buff里面转入接收到的数据
	   memcpy(DMA_USART1_TX_BUF,DMA_USART1_RX_BUF,usart1_rx_len);
     while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);	//确保DMA可以被设置		 
  //19.6 设置DMA发送的buff的长度
     DMA_SetCurrDataCounter(DMA2_Stream7,usart1_rx_len);
	//19.7 使能DMA发送(这是DMA开始自动发送)
     DMA_Cmd(DMA2_Stream7,ENABLE);

  //19.8 重新设置接收DMA的长度
     DMA_SetCurrDataCounter(DMA2_Stream5,USART_MAX_LEN);
  //19.9 重新使能DMA接收
     DMA_Cmd(DMA2_Stream5,ENABLE);
  //19.10 清除空闲中断
     USART_ReceiveData(USART1); //清除空闲中断标志位(接收函数有清标志位的作用)		 
	 }
}

//发送完成中断
void DMA2_Stream7_IRQHandler(void)
{
	//20.1 DMA发送完毕
    if(DMA_GetITStatus(DMA2_Stream7,DMA_IT_TCIF7))
		{
	//20.2 清除DMA发送完成标志位
			DMA_ClearITPendingBit(DMA2_Stream7,DMA_IT_TCIF7);
	//20.3 失能DMA发送	
			DMA_Cmd(DMA2_Stream7,DISABLE);   //失能DMA发送
		}
}

//DMA发送函数
void DMA_USART1_Send(u8 *data,u16 size)
{
	memcpy(DMA_USART1_TX_BUF,data,size);				//复制数据到DMA发送缓存区
	while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);	//确保DMA可以被设置
	DMA_SetCurrDataCounter(DMA2_Stream7,size);			//设置数据传输长度
	DMA_Cmd(DMA2_Stream7,ENABLE);						//打开DMA数据流,开始发送
	
}



  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值