目录
#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