STM32 USART 使用DMA 详解

发布者:静静思索最新更新时间:2019-05-24 来源: eefocus关键字:STM32  USART  DMA 手机看文章 扫描二维码
随时随地手机看文章

前言(绕开吧):

       这段时间由于我们的项目Manibus板卡需要融入 WIFI, BLT, 网口,CAN,串口的多位一体通讯,互不干扰,而且可以相互调用彼此进行数据通讯,这里为了节省MCU资源,所以就使用DMA的方式来进行串口 和 ESP8266的通讯,接下来就介绍一下具体的操作内容!

        DMA具体的不介绍,总的来说,他就是一个中转站,数据给DMA,他帮你传递或接受,你只要读就行了!!

接下来看代码!

        

void localUsartDMAConfig(void){

 

    DMA_InitTypeDef DMA_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

  DMA_DeInit(DMA1_Channel4);

   

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);

    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Send_Branch_Buffer;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

  DMA_InitStructure.DMA_BufferSize = USART1_BUR_MAX;

  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_High;

  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

  DMA_Init(DMA1_Channel4,&DMA_InitStructure);

  

  DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);

  DMA_ITConfig(DMA1_Channel4,DMA_IT_TE,ENABLE);

USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);

DMA_Cmd(DMA1_Channel4,DISABLE);

  DMA_DeInit(DMA1_Channel5); 

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_Rev_Branch_Buffer;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

DMA_InitStructure.DMA_BufferSize = USART1_BUR_MAX;

    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_High;

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

DMA_Init(DMA1_Channel5,&DMA_InitStructure);

DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);

  DMA_ITConfig(DMA1_Channel5,DMA_IT_TE,ENABLE);

    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);

DMA_Cmd(DMA1_Channel5,ENABLE);

}  

这里我们使用的是 USART1, 其对应的DMA 是 TX ->DMA1_Channel4,RX0->DMA1_Channel5, 这里我们还是使用DMA中断,就是DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);

  DMA_ITConfig(DMA1_Channel4,DMA_IT_TE,ENABLE);这样的好处就是,当数据接收满 或者发送完成后,他能自动重新的让其不使能,或者重新更新!



static void NVIC_Configuration(void)

{

NVIC_InitTypeDef NVIC_InitStructure;

 

//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

 

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

 

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

}

这里我们配置NVIC的中断,这里有让DMA有更高的权限,这是为了让他能及时更新!


void localUsartConfig(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

 

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_InitStructure.USART_BaudRate = 115200;

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(USART1, &USART_InitStructure);

 

USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

USART_Cmd(USART1, ENABLE);

USART_ClearFlag(USART1,USART_FLAG_TC);

 

localUsartDMAConfig();    //Set DMA 

    NVIC_Configuration();     //Set NVIC

 

USART1_BASE_HAS_BEEN_INIT_FLAG =1;

}

这里配置 USART1,都是常规操作,这里我们使用的是IDLE空闲中断,就是发送一段字节后,串口空闲 没接受下一个数据了就会中断,也就是一个数据包,(以后都为废话)这样也适合我们的项目要求有关,分总线的通讯方式(分线向执行总线传递数据包)这里就要求不能分线数据交错!


void Usart_ReadArray_(uint8_t *array,u16 length)

{

   if(length){  

length =  length > USART1_BUR_MAX ? 1024: length; 

}else{  

return;

}  

     while(USART1_SendFlag_End ==0);

  

   if(array)memcpy(USART1_Send_Branch_Buffer,array,length);

 

DMA_ClearITPendingBit(DMA1_IT_TC4);

   DMA_Cmd(DMA1_Channel4, DISABLE);

   DMA1_Channel4->CNDTR= length;

DMA_Cmd(DMA1_Channel4, ENABLE);

USART1_SendFlag_End = 0;  

 

}

这里不要被函数名误解,功能就是一个串口通过DMA发送字符串的函数! 接下来就到重点了,在配置DMA发送函数的时候,我们设置DMA1_Channel4 的使能为DISABLE,是为了不让他一直发送,配置了ENABLE,你会发现它一直发送 00 00.。。。。这也是很好理解的,所以我们在需要的时候让他使能发送! 函数中有个while是为了让他把已有的数据发送完成,再进行下一个数据包的发送!


void USART1_IRQHandler(void){

 

u16 data_len;

 

 if(USART_GetITStatus(USART1,USART_IT_IDLE)!= RESET){

data_len  =USART1->SR;

data_len  =USART1->DR;

 

DMA_Cmd(DMA1_Channel5,DISABLE);

data_len = USART1_BUR_MAX - DMA_GetCurrDataCounter(DMA1_Channel5);

 

Usart_ReadArray_(USART1_Rev_Branch_Buffer,data_len);

 

DMA_ClearFlag(DMA1_FLAG_GL5 | DMA1_FLAG_TC5 | DMA1_FLAG_TE5 | DMA1_FLAG_HT5); 

DMA1_Channel5->CNDTR = USART1_BUR_MAX;

  

    DMA_Cmd(DMA1_Channel5,ENABLE);

 

  USART_ClearITPendingBit(USART1, USART_IT_IDLE);

}

}

这里判断时候产生空闲中断!  然后我们需要USART_ReceiveData 这里和先USART1->SR;后USART1->DR; 一样;然后读取数据段长度;这里我进行读取在发送进行验证! 后面清除各种标志位;然后DMA重装Size, 使能接收DMA,让其继续接收;最后清除空闲中断的标志位!



void DMA1_Channel4_IRQHandler(void){

 

    DMA_ClearITPendingBit(DMA1_IT_TC4);

  DMA_ClearITPendingBit(DMA1_IT_TE4);

    DMA_Cmd(DMA1_Channel4, DISABLE);

  USART1_SendFlag_End = 1; 

 

}

 

void DMA1_Channel5_IRQHandler(void){

    

  DMA_ClearITPendingBit(DMA1_IT_TC5);

  DMA_ClearITPendingBit(DMA1_IT_TE5);

    DMA_Cmd(DMA1_Channel5, DISABLE); 

    DMA1_Channel5->CNDTR = USART1_BUR_MAX;

  DMA_Cmd(DMA1_Channel5, ENABLE);

}

这里为DMA中断,发送中断是在发送完成后产生的,重新跟新标志位证明发送完成!  接收中断是在接收字节满了后产生的,重装Size!

以上基本结构就配置完成了,有误希望指正!


关键字:STM32  USART  DMA 引用地址:STM32 USART 使用DMA 详解

上一篇:对STM32F10xxx中UART通信的一些理解
下一篇:STM32F10X SysTick小教程

推荐阅读最新更新时间:2024-10-31 23:14

STM32的位操作和跑马灯实验
位操作代码在 sys.h 文件中,实现对 STM32 各个 IO 口的位操作,包括读入和输出。当然在这些函数调用之前,必须先进行 IO 口时钟的使能和 IO 口功能定义。 一。位带操作的原理 把一个位膨胀为一个32位的地址,如果要写这个位为1,只需要往这个地址写1. 二。哪些区域支持位带操作? 例如一个SRAM的区域 0x20000000上有32位,每一位都可以映射成一个地址,如果想往哪一位写1,只需要往这一位映射的地址写1.从而达到操作位的目的。 三。位带操作的优越性 不用位带操作的话,要把bit2置1,要先读取0x20000000的值,然后把bit2置1,然后再把寄存器的值写回0x2000000
[单片机]
<font color='red'>STM32</font>的位操作和跑马灯实验
STM32学习总结之时钟
学习内容: 这个图说明了STM32的时钟走向,从图的左边开始,从时钟源一步步分配到外设时钟。从时钟频率来说,又分为高速时钟和低速时钟,高速时钟是提供给芯片主体的主时钟,而低速时钟只是提供给芯片中的RTC(实时时钟)及独立看门狗使用。 从芯片角度来说,时钟源分为内部时钟与外部时钟源 ,内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。而外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号。 所以,STM32有以下4个时钟源: 高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,我们一般采用8M
[单片机]
<font color='red'>STM32</font>学习总结之时钟
stm32 F407串口通信出现乱码
一直用正点原子的例程,后面开始在官网上下载库,完成自己写,到串口通信这里,发现写的东西跟例程一样,输出一直是乱码; 折腾半天是因为 晶振的问题。 我的板子HSE晶振为8M,分频是8;官网提供的25M,分频为25;(为什么分频后相同不能用?是因为板子晶振不对?) (为什么官网的晶振频率不可更改 25 ?);现在只是打印字符,还没有进行通信 修改后正确; 修改后输出正确结果,之前都是乱码
[单片机]
<font color='red'>stm32</font> F407串口通信出现乱码
STM32问题记录:这回Keil编译器背锅
最近写了个用环形缓冲区发送数据的STM32串口程序:使用头指针(front)指向下一个要发送数据,使用尾指针(rear)指向新数据存储的地方。中断里面会判断front和rear是否相等,如果相等则表示缓冲区为空,发送已经完成,关闭中断;反过来说,front和rear相等表示缓冲区还有数据要发送,那么就在中断里面把数据一个一个地发送出去。 那么问题来了,我在存数据的时候写了这么一行代码: USART1_SendQueue = data; 也就是说,把数据存入缓冲区后,尾指针自+1。这看起来没毛病,但编译器给出汇编代码是这样的: 0x0800213C LDR r0, ;取出USART1_SendRear的地址
[单片机]
STM32 PWM输出
a) 目的:基础PWM输出,以及中断配合应用。输出选用PB1,配置为TIM3_CH4,是目标板的LED6控制脚。 b) 对于简单的PWM输出应用,暂时无需考虑TIM1的高级功能之区别。 c) 初始化函数定义: void TIM_Configuration(void); //定义TIM初始化函数 d) 初始化函数调用: TIM_Configuration(); //TIM初始化函数调用 e) 初始化函数,不同于前面模块,TIM的初始化分为两部分 基本初始化和通道初始化: void TIM_Configuration(void)//TIM初始化函数
[单片机]
STM32 GPIO的8中种配置模式
STM32的GPIO引脚可以配置成8中模式: (1)GPIO_Mode_AIN 模拟输入 (2)GPIO_Mode_IN_FLOATING 悬空输入 (3)GPIO_Mode_IPD 下拉输入 (4)GPIO_Mode_IPU 上拉输入 (5)GPIO_Mode_Out_OD 开漏输出 (6)GPIO_Mode_Out_PP 推挽输出 (7)GPIO_Mode_AF_OD 复用开漏输出 (8)GPIO_Mode_AF_PP 复用推挽输出 这8中模式可以软件编程,IO端口位的基本机构如下图所示: IO口的这8中模式
[单片机]
<font color='red'>STM32</font> GPIO的8中种配置模式
STM32DMA配置
DMA可以认为连接两个“地址”数据通道。DMA共享系统总线,不占用CPU,所以可以实现快速数据传输。 这里以DMA连接存储器(数组)和串口(USART1- DR)为例。 1 void DMA_init(void) 2 { 4 RCC- AHBENR|=1 0;//时能DMA1时钟 5 7 DMA1_Channel4- CPAR=(u32)&USART1- DR;//读外设串口数据寄存器 8 DMA1_Channel4- CMAR=(u32)tbuff;//存储器地址为temp地址 9 DMA1_Channel4- CNDTR=10;//一次接收字节数DMA_BUFF_SIZE 11 //DNA- CCR配置 12
[单片机]
STM32 MCU的技术特点和应用前景
引言: 随着物联网和智能家居的快速发展,微控制器单元(Microcontroller Unit,MCU)在各种电子产品和系统中扮演着重要角色。作为一种高度集成的芯片,MCU结合了中央处理器(Central Processing Unit,CPU)、随机存取存储器(Random Access Memory,RAM)、Flash存储器以及其他周边设备。本文将介绍STM32 MCU的技术特点和应用前景。 一、STM32 MCU概述: STM32系列微控制器是由意法半导体(STMicroelectronics)公司开发的。该系列MCU基于ARM Cortex-M内核,具有高性能、低功耗、高集成度和易用性等特点。它们广泛应用于汽车、医疗
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved