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

目录

     

  #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)

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值