第十章 AT32F403A基于V2库串口 弹性dma

目录

概述

硬件

弹性DMA

软件

        流程

        初始化

        初始化代码:

        串口1中断服务函数:

        DMA1通道2设置函数: 

        DMA1通道1发送函数:

测试

最后


概述

         本文主要是使用AT32F403A开发板,基于V2库串口1使用弹性DMA 来传输接收的数据功能,并且配合使用串口空闲中断(IDLE)来接收不定长的数据,主要是在上一章使用dma的基础上来使用弹性DMA来进行数据的搬运。

          串口工具使用的Atlink-ez自带的串口功能。

         工程建立、调试工具配置在前面章节有详细介绍。


硬件

         硬件方面使用的是参考官方AT32F437 SURF板子而设计的一个AT32F403A开发板,板子上的芯片是AT32F403AVGT7的型号,开发板上面还板载了一个atlink-ez的仿真器,atlink-ez除了可以在线仿真和下载之外还有一个串口的功能,硬件上是通过跳线帽接到了MCU的串口1,pa9/10上面。

        如下图是开发板pcb图,以及硬件资源。(左边上角的就是atlink-ez,用usb线接到pc即可):

         如下是实物图:

        本章的主要内容是使用弹性dma对串口1的数据进行搬运,相关原理图部分(jp1的1和2,3和4,分别用跳线帽接起来):


弹性DMA

        如下图默认情况下,外设和DMA的通道是固定,使用的什么外设就需要使用对应的dma通道才能正常进行搬运工作。

        

        从图可以看出,串口1的收发对应的DMA通道是DMA1的通道5和通道4,所以默认情况下就串口1只能使用DMA1的通道4/5来使用,并且收发通道也是固定的,但是在弹性DMA的功能下,就不再有这个局限,外设和通道的对应可以自行设定,比如可以设置串口的接收dma通道为7个通道里面的随意一个通道。这样在多通道使用的时候可以很好的分配通道和外设的对应关系。本文就是使用通道1作为串口1发送通道,通道2作为串口1接收通道。

         弹性DMA需要特别注意,当DMA开启弹性功能后,那么这DMA控制器的其他通道也是失效了,也就是其他通道要使用的时候都需要设置为弹性通道,原本默认的就失去效果。


软件

        流程

        软件上主流程是串口1使用DMA1通道2来搬运串口1收到的数据到接收缓存中,由于dma的搬运的数据长度是必须先设定好的,接收时是不能判定外面有多少数据过来的,使用串口1空闲中断来判定接收完毕,然后把收到的数据复制到发送缓存里面,同时重新设定DMA1通道2的配置,以继续接收下一包数据;收到一帧数据后,通过DMA1 通道1把发送缓存里面的数据通过DMA1 通道1搬运到串口1的发送寄存器,串口1就可以把数据给发送出来了。


        初始化

        初始化部分包括gpio的初始化、串口的初始化、DMA的初始化、设置弹性DMA,绑定外设和通道。串口1使用默认的IO,PA9、PA10,开始使能串口1的DMA接收以及使能DMA1通道2,因为我们的主流程是收到数据后再发送出来的,在需要发送的时候再使能串口1的DMA发送以及使能DMA1通道1。

        DMA的详细介绍和初始化可转上一章浏览,弹性dma主要就是需要开启弹性dam,绑定通道和外设,然后初始化配置通道。

        设置弹性dma函数:

        

        软件上配置串口1的波特率为115200,数据位8位,停止位1位,开启空闲中断(IDLE)。

        串口空闲中断(IDLE)中清中断状态,获取dma搬运的数据也就是收到的数据长度了,再把数据复制到发送缓存中,然后重新设定DMA1通道2,接收下一帧数据。 


        初始化代码:


/*
*串口1  弹性DMA配置函数
*IO:PA9/PA10
*blound: 波特率
*数据位 8,停止位 1,无校验
*DMA1,通道1/2
*/


void usart1_fix_dma_init(u32 bound)
{
	gpio_init_type gpio_init_struct;
	dma_init_type dma_init_struct; 
    
	
	/*Enable the UART Clock*/
	crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);		//开启GPIOA的时钟
	crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);		//开启USART1的时钟
	crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);		//开启DMA1的时钟

	gpio_default_para_init(&gpio_init_struct);
	/* Configure the UART1 TX pin */
	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;	//较大电流推动/吸入能力
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;				//推挽输出
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;								//复用
    gpio_init_struct.gpio_pins = GPIO_PINS_9;								//PA9
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;							//无上下拉
    gpio_init(GPIOA, &gpio_init_struct);

	/* Configure the UART1 RX pin */
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;	//较大电流推动/吸入能力
    gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;				//推挽输出(输入模式,无效)
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;							//输入模式
    gpio_init_struct.gpio_pins = GPIO_PINS_10;								//PA10
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;								//上拉
    gpio_init(GPIOA, &gpio_init_struct);
	
	dma_reset(DMA1_CHANNEL1);
	dma_default_para_init(&dma_init_struct);  
    dma_init_struct.buffer_size = 0;                               				//内存大小
    dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;                   //外设地址为目的地址
    dma_init_struct.memory_base_addr = (uint32_t)0;                             //内存地址
    dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;             //内存数据的宽度
    dma_init_struct.memory_inc_enable = TRUE;                                   //内存地址递增打开
    dma_init_struct.peripheral_base_addr = (uint32_t)&USART1->dt;               //外设地址
    dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;     //外设数据的宽度
    dma_init_struct.peripheral_inc_enable = FALSE;                              //外设地址递增关闭
    dma_init_struct.priority = DMA_PRIORITY_MEDIUM;                             //中等优先级
    dma_init_struct.loop_mode_enable = FALSE;									//不循环
	dma_init(DMA1_CHANNEL1, &dma_init_struct);
	
	dma_reset(DMA1_CHANNEL2);
	dma_init_struct.peripheral_base_addr=(uint32_t)&USART1->dt;                 //外设地址
    dma_init_struct.memory_base_addr=(uint32_t)Muartnum[0].Uartrxbuf;           //内存地址
    dma_init_struct.direction=DMA_DIR_PERIPHERAL_TO_MEMORY;                     //外设地址为源地址
    dma_init_struct.buffer_size=USART_REC_LEN;
	dma_init(DMA1_CHANNEL2, &dma_init_struct);
	
	
	dma_flexible_config(DMA1, FLEX_CHANNEL1, DMA_FLEXIBLE_UART1_TX);            //DMA1的通道1弹性设定为串口1的dma发送通道
    dma_flexible_config(DMA1, FLEX_CHANNEL2, DMA_FLEXIBLE_UART1_RX);			//DMA1的通道2弹性设定为串口1的dma接收通道	
	
	usart_dma_receiver_enable(USART1,TRUE);						//使能串口dma接收
	dma_channel_enable(DMA1_CHANNEL2, TRUE);					//使能通道2
	
	nvic_irq_enable(USART1_IRQn, 0, 0);      						//使能串口1中断,优先级0,次优先级0
	
	/*Configure UART param*/
    usart_init(USART1, bound, USART_DATA_8BITS, USART_STOP_1_BIT);		//波特率,8数据位,1停止位
    usart_hardware_flow_control_set(USART1,USART_HARDWARE_FLOW_NONE);	//无硬件流操作
    usart_parity_selection_config(USART1,USART_PARITY_NONE);			//无校验
    usart_transmitter_enable(USART1, TRUE);								//使能发送
    usart_receiver_enable(USART1, TRUE);								//使能接收
	
	usart_interrupt_enable(USART1, USART_IDLE_INT, TRUE);				//使能串口空闲中断
    usart_enable(USART1, TRUE);											//使能串口
	
}


        串口1中断服务函数:


/*
*串口1中断服务函数
*
*
*/

void USART1_IRQHandler(void)
{	
	uint8_t clear;
	if(usart_flag_get(USART1, USART_IDLEF_FLAG) != RESET)               // USART1总线空闲
	{
		clear=USART1->sts;                                              // USART1清除空闲中断标志位
		clear=USART1->dt;												// USART1清除空闲中断标志位
		clear&=0;
		Muartnum[0].Uartrxsta = 1;										// USART1接收完成标志位
		Muartnum[0].Uartrxcut=USART_REC_LEN-dma_data_number_get(DMA1_CHANNEL2);        //获取DMA通道2收到的数据长度
		Muartnum[0].Uarttxcut=Muartnum[0].Uartrxcut;
		memcpy(Muartnum[0].Uarttxbuf,Muartnum[0].Uartrxbuf,Muartnum[0].Uarttxcut);		//数据复制到txbuf里面
		usartdmarecv(Muartnum[0].Uartrxbuf,USART_REC_LEN);				//重新设定dma,接收下一包数据
	}
		
}

        DMA1通道2设置函数: 

/**
*	串口1的DMA接收设置函数
* 	data:接收数据的buf地址
*	len:设定接收的最大数据长度
*/

void usartdmarecv(u8 *data,u16 len)
{

    dma_flag_clear(DMA1_FDT2_FLAG);                 //清标志  
    dma_channel_enable(DMA1_CHANNEL2, FALSE);       //关闭通道2 
    usart_dma_receiver_enable(USART1,FALSE);        //关闭USART1 DMA 接收
    DMA1_CHANNEL2->dtcnt=len;                       //接收的数据长度
    DMA1_CHANNEL2->maddr=(uint32_t)data;            //存放数据buf地址
    usart_dma_receiver_enable(USART1,TRUE);         //开启USART1 DMA 接收
    dma_channel_enable(DMA1_CHANNEL2, TRUE);        //开启通道2(开始接收)
 
}

  


        DMA1通道1发送函数:

/**
*	串口1的DMA循环发送函数
*   data:要发送的buf地址
*	len:数据长度
*/

void usartdmasend(u8 *data,u16 len)
{
 
    DMA1_CHANNEL1->dtcnt=len;                           //发送的数据长度
    DMA1_CHANNEL1->maddr=(uint32_t)data;                //数据buf地址
    usart_dma_transmitter_enable(USART1,TRUE);          //开启USART1 DMA 发送
    dma_channel_enable(DMA1_CHANNEL1, TRUE);            //开启通道1(开始发送) 
    while(dma_flag_get(DMA1_FDT1_FLAG)==RESET );        //等待传输完成 
    dma_flag_clear(DMA1_FDT1_FLAG);                     //清标志
    dma_channel_enable(DMA1_CHANNEL1, FALSE);           //关闭通道1
    usart_dma_transmitter_enable(USART1,FALSE);         //关闭USART1 DMA 发送
      
}

测试

        通过串口往板子发送数据,正常的话会收到相同的数据,经过测试,功能正常,成功开启了弹性dma功能,使用通道1和通道2传输了串口1的数据。需要注意的是,串口的缓存设置的512的字节长度,DMA1 通道2的长度也设置的512,所以发给板子的最大长度是512字节。

        测试代码:

        测试结果: 


最后

         有问题的可以加QQ群交流,同时相关代码上传到QQ群中。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值