首页 > 其他分享 >DMA学习笔记(天空星stmf4开发板)

DMA学习笔记(天空星stmf4开发板)

时间:2024-06-22 17:59:30浏览次数:28  
标签:DMA USART stmf4 开发板 DMA2 InitStruct GPIO USART1

目录

     

  #DMA介绍

#相关数据传输方法介绍

#DMA相关数据手册框图介绍

#DMA相关涉及概念介绍

   #软件实现DMA传输数据


     

  #DMA介绍

#相关数据传输方法介绍

       Direct Memory Access  即 DMA ,芯片开发板,片上外设的一种,相关作用:进行传输数据不需要,消耗CPU的资源,也就是说使用DMA的这个过程,不需要CPU去干预

        三种常见的数据传输方法分析

        中断法传输数据:将数据传输的指令,写在中断响应函数里面,当需要传输数据,触发中断,如果传输大量数据的时候,会出现一个问题,单片机中断,是一瞬间的事情,在响应中断的时候,CPU会只执行中断响应函数,while()循环里面的函数就不行执行,这就会造成,正常功能响应延迟这个问题

        轮询法传输数据:在main()函数的while()循环里面,执行检测相关标志位,的语句判断是否符合条件,满足条件进行传输数据,如果没有上rtos操作系统,又传输大量数据,这个时候,也会出现 响应延迟,因为mcu 在没有操作系统的情况下,while()里面语句是一条一条 向下去执行的,这一条语句执行的时间长,那么就意味着,下一条语句没有办法去执行,相应的功能也就没有,也就是所为的延迟响应。

        DMA传输数据:DMA片上外设的一种,传输过程不需要CPU的参与,可以进行高速数据进行传输,用于在外设与存储器之间,以及存储器与存储器之间,存储器到外设之间。

#DMA相关数据手册框图介绍

        在STMF4两个DMA,每个DMA有8个通道,具体通道见下图

        两个DMA通道的外设请求映射分别如下图所示。

#DMA相关涉及概念介绍

        DMA挂在AHB时钟总线上面,在STMF4里面两个DMA有8个通道,两个内置的总线仲裁器用来处理DMA请求的优先级问题

        FIFO概念

        FIFO(First-In-First-Out):FIFO是一种先进先出(First In, First Out)的数据结构或队列。在这种队列中,插入数据(入队)总是发生在队列的一端,而删除数据(出队)也是从同一端开始,按照数据到达的顺序。FIFO在很多场景下都很常见,比如任务调度、消息传递系统以及网络数据包的处理,因为它们保证了数据处理的顺序,而在DMA中也使用了这种方式进行数据传,DMA控制器的每个通道都有一个4字深度的FIFO用于缓冲数据,从源地址读取的数据会暂时保存在FIFO中,再传输到目的地址。

   

        每个数据流有单独的四级32位的先进先出存储缓冲区(FIFO),可用于FIFO模式或直接模式:

FIFO模式:可通过软件将阈值级别选取为FIFO大小的1/4 1/2 或3/4

直接模式:每个DMA请求会立即启动对存储区的传输,当在直接模式(禁止FIFO)下将DMA请求配置为以存储器到外设模式传输数据时,DMA仅会将一个数据从存储器预加载到内部的FIFO,从而确保一旦外设触发DMA请求时立即传输数据。

        种裁器,这个东西就像 NVIC(中断向量控制器),用来干嘛的就是管理 DMA 数据流传输优先级的,为了防止多个DMA传输数据时,优先级发生冲突,就需要使用这个工能,具体优先级排列见下图数据手册图片,手册连接放在文章末尾。

#软件实现DMA传输数据

        

        这里要特别注意一点的是,如果开启时钟错误,或者结构体变量,赋值的时候,将两个结构体变量的值赋值相反,那么KEIL5是不会进行报错的,需要注意细节。

#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 Dam_GPIO_Init(void)
{

GPIO_InitTypeDef  GPIO_InitStruct;
	
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_9;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_100MHz;
	
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
	
GPIO_Init(GPIOA,&GPIO_InitStruct);

}

        串口引脚初始化

void Dam_Usart_Init(void)
{
	USART_InitTypeDef USART_InitStruct;//结构体初始化
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//配置时钟
	
	USART_InitStruct.USART_BaudRate = 9600;//配置结构体变量
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_WordLength =USART_WordLength_8b;

	USART_Init(USART1,&USART_InitStruct);//调用初始化函数 Init USART


}

        配置串口

void Dam_Init(void)
{
DMA_InitTypeDef DMA_InitStruct; //初始化定义结构体
														
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);		//开启时钟

	//DMA通道本身配置
DMA_InitStruct.DMA_BufferSize = DMA_Channel_4;
DMA_InitStruct.DMA_Channel = DMA_Channel_4;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	//DMA 存储器配置
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)DMA_USART1_RX_BUF;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; 
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;     
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; 
	//DMA  外设配置 
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStruct.DMA_PeripheralBurst  = DMA_PeripheralBurst_Single; 
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh; 

DMA_DeInit(DMA2_Stream7);    //初始化DMA Stream
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);//等待DMA可配置
    
/* 配置DMA2 Stream7,USART1发送 */
DMA_InitStruct.DMA_Channel            = DMA_Channel_4;               //通道选择
DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)&USART1->DR;            //DMA外设地址
DMA_InitStruct.DMA_Memory0BaseAddr    = (u32)DMA_USART1_TX_BUF;      //DMA 存储器0地址
DMA_InitStruct.DMA_DIR                = DMA_DIR_MemoryToPeripheral;  //存储器到外设模式
DMA_InitStruct.DMA_BufferSize         = USART_MAX_LEN;               //数据传输量
DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;   //外设非增量模式
DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Enable;        //存储器增量模式
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位
DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;     //存储器数据长度:8位
DMA_InitStruct.DMA_Mode               = DMA_Mode_Normal;             //使用普通模式
DMA_InitStruct.DMA_Priority           = DMA_Priority_Medium;         //中等优先级
DMA_InitStruct.DMA_FIFOMode           = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold      = DMA_FIFOThreshold_1QuarterFull;
DMA_InitStruct.DMA_MemoryBurst        = DMA_MemoryBurst_Single;      //存储器突发单次传输
DMA_InitStruct.DMA_PeripheralBurst    = DMA_PeripheralBurst_Single;  //外设突发单次传输
DMA_Init(DMA2_Stream7, &DMA_InitStruct);                             //初始化DMA Stream7

DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);  //DMA2传输完成中断
DMA_Cmd(DMA2_Stream7, DISABLE);  //不使能
    
USART_Cmd(USART1, ENABLE);  //使能串口1


}

        DMA初始化

void DMA2_Stream7_IRQHandler(void)
{
    //清除标志
    if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
    {
            DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7); //清除DMA2_Steam7传输完成标志
               DMA_Cmd(DMA2_Stream7,DISABLE);      //关闭使能
        USART_ITConfig(USART1,USART_IT_TC,ENABLE);  //打开串口发送完成中断
    }
}
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数据流,开始发送
}
void USART1_IRQHandler(void)  //串口1中断服务程序
{
    if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET)         //空闲中断触发
    {
        usart1_recv_end_flag = 1;          // 接受完成标志位置1
    
        DMA_Cmd(DMA2_Stream5, DISABLE);    /* 暂时关闭dma,数据尚未处理 */
        usart1_rx_len = USART_MAX_LEN - DMA_GetCurrDataCounter(DMA2_Stream5);/* 获取接收到的数据长度 单位为字节*/
        DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);  /* 清DMA标志位 */
            
            DMA_USART1_Send(DMA_USART1_RX_BUF, usart1_rx_len); // 将接收的数据回显
            
        DMA_SetCurrDataCounter(DMA2_Stream5,USART_MAX_LEN);  /* 重新赋值计数值,必须大于等于最大可能接收到的数据帧数目 */
        DMA_Cmd(DMA2_Stream5, ENABLE); /*打开DMA*/
            
        USART_ReceiveData(USART1);  //清除空闲中断标志位(接收函数有清标志位的作用)
    }
    
    if(USART_GetFlagStatus(USART1,USART_IT_TXE)==RESET)        //串口发送完成
    {
        USART_ITConfig(USART1,USART_IT_TC,DISABLE);
        usart1_rx_len = 0;
     }
}

        两个中断响应函数的初始化,然后扔到while()里面去调用。

【立创·天空星STM32F407VET6】入门手册 - 飞书云文档 (feishu.cn)

标签:DMA,USART,stmf4,开发板,DMA2,InitStruct,GPIO,USART1
From: https://blog.csdn.net/weixin_62292999/article/details/139865734

相关文章

  • 用ADAU1466开发板教你做音频开发,有手就行(二十二):按键控制音量+-和静音(IO的应用)
    作者的话本章开始正式进入ADAU1466的开发教程,什么叫有手就行,看下去就明白了。特别注意因为ADAU1452和ADAU1466是P2P完全兼容的,管脚兼容,硬件设计兼容,软件程序配置全部都兼容,差别在于ADAU1466的内存更大。我的文章里所用到的程序都是基于ADAU1452的,程序也是基于ADAU1452的,A......
  • 3dmax怎么渲染又快又清晰?
    在3dsMax中,追求快速且清晰的渲染效果是每个设计师的目标。云渲染技术的出现,为这一目标提供了强大的支持。通过云渲染,设计师能够利用远程服务器的强大计算能力,实现快速渲染,同时保持图像的高清晰度。一、3dmax怎么渲染清晰第一步:打开渲染设置点击公用部分,输出图片大小,网上......
  • 一文带你了解STM32F4中断的概念,串口的概念,DMA的转运,以及如何运用在串口的收发上,串口收
    本篇主要实现的是用UART的接收中断接收数据,用DMA接收不定长的数据并发送回给电脑,接收信息控制LED灯的亮灭,成为点灯大师。什么是中断(EXIT)EXIT 外部中断/事件控制器,管理了控制器的20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下......
  • 学习笔记STMF4 TIMER定时器(使用开发板立创天空星STMF4)
    目录                                                #定时器的介绍             #怎么去理解定时器的预分频系数                                        ......
  • 3dmax怎么渲染又快又清晰?
    在3dsMax中,追求快速且清晰的渲染效果是每个设计师的目标。云渲染技术的出现,为这一目标提供了强大的支持。通过云渲染,设计师能够利用远程服务器的强大计算能力,实现快速渲染,同时保持图像的高清晰度。一、3dmax怎么渲染清晰第一步:打开渲染设置点击公用部分,输出图片大小,网上用的3......
  • 使用芯片为ZYNQ—7020,基于野火FPGA ZYNQ开发板
    使用芯片为ZYNQ—7020,基于野火FPGAZYNQ开发板肤色模型简介YCrCb也称为YUV,主要用于优化彩色视频信号的传输。与RGB视频信号传输相比,它最大的优点在于只需占用极少的频宽(RGB要求三个独立的视频信号同时传输)。其中“Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V”......
  • 51单片机有开发板A和开发板B,通过串口,实现开发板A的按键,控制开发板B的LED亮灭。
    1、功能描述有开发板A和开发板B,通过串口,实现开发板A的按键,控制开发板B的LED亮灭。2、实验原理1.串口通信(UART)串口通信是一种异步通信方式,它允许单片机通过两条线(发送线TX和接收线RX)进行数据传输。在51单片机中,串口通信是通过两个主要寄存器(SCON、SMO/SM1)和中断(RI、TI)......
  • QT210开发板学习(2): 通过DNW点亮LED灯
    查找开发板原理图,可知可用的LED有4个,引脚为EINT0/1/2/3,对应的IO口则是GPH0_0/1/2/3,寄存器有GPH0CON,GPH0DAT,GPH0PUD,GPH0DRVGPH0CON用来设置IO模式(地址为0xE0200C00),GPH0DAT是电平状态(地址为0xE0200C04)我们需要设置为Output模式,并且将状态设置为高电平新......
  • QT210开发板学习(1):SEC S5PC110 Test B/D驱动安装
    把开发板的开关拨到USBBOOT,通过USB线连接到开发板的OTG口,打开板上总电源,会提示驱动安装失败我们需要下载驱动(win7-64-DNW-USB)https://github.com/joyjohn131/QT210/tree/main/1打开dseo13b.exe,依次点击Next,Yes选择"EnableTestMode","Next",提示完成开启选择"SignaS......
  • Podman
    Podmanhttps://podman.io/Thebestfree&opensourcecontainertoolsManagecontainers,pods,andimageswithPodman.SeamlesslyworkwithcontainersandKubernetesfromyourlocalenvironment. advantagehttps://www.redhat.com/zh/topics/container......