STM32H7 拥有多个DMA,此处只讲解最常用的DMA1,DMA2;与之前的STM32不同的是,STM32H7的DMA支持不同的触发源,不用像以前一样,每个通道与对应的硬件触发源固定死,通过DMAMUX进项选择
使用上会更加灵活。
在使用DMA过程中,需要注意,STM32H743的内存分区很多,ITCM与DTCM是无法通过DMA1,DMA2进行访问的,使用的时候需要注意内存位置,同时使用了DMA后会存在一致性问题,
就是DMA传输的数据通常都在cache中更新,通过CPU去访问会发现数据并没有同步到实际内存区域。
直接存储器访问 (DMA) 用于在外设与存储器之间以及存储器与存储器之间提供高速数据传
输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可
供其它操作使用。
DMA 控制器基于复杂的总线矩阵架构,将功能强大的双 AHB 主总线架构与独立的 FIFO 结
合在一起,优化了系统带宽。
两个 DMA 控制器总共有 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用于管理
一个或多个外设的存储器访问请求。每个数据流总共可以有多达 8 个通道(或称请求)。每
个通道都有一个仲裁器,用于处理 DMA 请求间的优先级。
DMA 主要特性
DMA 主要特性是:
双 AHB 主总线架构,一个用于存储器访问,另一个用于外设访问
仅支持 32 位访问的 AHB 从编程接口D
每个 DMA 控制器有 8 个数据流,每个数据流有多达 115 个通道(或称请求)
每个数据流有四个字深的 32 位先进先出存储器缓冲区 (FIFO),可用于 FIFO 模式或直接
模式:
– FIFO 模式:可通过软件将阈值级别选取为 FIFO 大小的 1/4、 1/2 或 3/4
– 直接模式
每个 DMA 请求会立即启动对存储器的传输。当在直接模式(禁止 FIFO)下将
DMA 请求配置为以存储器到外设模式传输数据时, DMA 仅会将一个数据从存储器
预加载到内部 FIFO,从而确保一旦外设触发 DMA 请求时则立即传输数据。
通过硬件可以将每个数据流配置为:
– 支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道
– 在存储器端支持双缓冲的双缓冲区通道
8 个数据流中的每一个都连接到专用硬件 DMA 通道(请求)
DMA 数据流请求之间的优先级可用软件编程(4 个级别:非常高、高、中、低),在软
件优先级相同的情况下可以通过硬件决定优先级(例如,请求 0 的优先级高于请求 1)
每个数据流还支持通过软件触发的存储器间的传输
可通过 DMAMux1 在多达 115 个可能的通道请求中选择每个数据流请求。此选择可由软
件配置,允许多个外设发起 DMA 请求
要传输的数据项的数目可以由 DMA 控制器或外设管理:
– DMA 流控制器:要传输的数据项的数目可用软件编程,从 1 至 65535
– 外设流控制器:要传输的数据项的数目未知并由源或目标外设控制,这些外设通过
硬件发出传输结束的信号
独立的源和目标传输宽度(字节、半字、 字):源和目标的数据宽度不相等时, DMA
自动封装/解封必要的传输数据来优化带宽。这个特性仅在 FIFO 模式下可用
对源和目标的增量或非增量寻址
支持 4 个、 8 个和 16 个节拍的增量突发传输。突发增量的大小可由软件配置,通常等
于外设 FIFO 大小的一
//DMA驱动代码
/*************************************************************************************************************
* 文件名 : dma.c
* 功能 : STM32H7 DMA驱动
* 作者 : www.scj-water.com
* 创建时间 : 2025-03-07
* 最后修改时间 : 2025-03-11
* 详细: 需要提前使能DMA1 DMA2时钟
* DMA12无法访问ITCM与DTCM内存
*************************************************************************************************************/
#include "dma.h"
#include "system.h"
//DMA外设结构
static const DMA_TypeDef * const sg_DMA_TYPE_BUFF[2] = {DMA1, DMA2};
static const DMA_Stream_TypeDef * sg_DMA1_Stream[8] = {DMA1_Stream0, DMA1_Stream1, DMA1_Stream2, DMA1_Stream3, DMA1_Stream4, DMA1_Stream5, DMA1_Stream6, DMA1_Stream7};
static const DMA_Stream_TypeDef *sg_DMA2_Stream[8] = {DMA2_Stream0, DMA2_Stream1, DMA2_Stream2, DMA2_Stream3, DMA2_Stream4, DMA2_Stream5, DMA2_Stream6, DMA2_Stream7};
static const DMAMUX_Channel_TypeDef* sg_DMAMUX_Channel[16] = {
DMAMUX1_Channel0, DMAMUX1_Channel1, DMAMUX1_Channel2, DMAMUX1_Channel3, DMAMUX1_Channel4, DMAMUX1_Channel5, DMAMUX1_Channel6, DMAMUX1_Channel7,
DMAMUX1_Channel8, DMAMUX1_Channel9, DMAMUX1_Channel10, DMAMUX1_Channel11, DMAMUX1_Channel12, DMAMUX1_Channel13, DMAMUX1_Channel14, DMAMUX1_Channel15
};
#define DMA12_MUX1_REQ_MAX 115 //请求号最大值
/*************************************************************************************************************************
* 函数 : void DMA_Config(DMAxSx_CH_TYPE DMAxSx_Ch, DAM_CONFIG *pConfig)
* 功能 : DMA配置
* 参数 : DMAxSx_Ch:DMA通道选择;pConfig:配置,见DAM_CONFIG
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-11
* 最后修改时间 : 2025-03-11
* 说明 :
*************************************************************************************************************************/
void DMA_Config(DMAxSx_CH_TYPE DMAxSx_Ch, DAM_CONFIG *pConfig)
{
u8 Stream,Req;
DMA_Stream_TypeDef *DMA_Stream;
DMAMUX_Channel_TypeDef* DMAMUX_Channel;
DMA_TypeDef *DMAx;
DMAx = (DMA_TypeDef *)sg_DMA_TYPE_BUFF[(DMAxSx_Ch >> 8)&0x01]; //获取DMA
Stream = (DMAxSx_Ch)&0xff; //获取流通道
Req = DMAxSx_Ch&0xff;
if((Req > DMA12_MUX1_REQ_MAX) || (Stream > 7)) //通道或流超出了范围
{
return;
}
if((DMAxSx_Ch >> 8)&0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
DMAMUX_Channel = (DMAMUX_Channel_TypeDef*)sg_DMAMUX_Channel[8+ Stream];
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
DMAMUX_Channel = (DMAMUX_Channel_TypeDef*)sg_DMAMUX_Channel[Stream];
}
//开始配置
DMA_Stream->CR = 0; //禁止数据流,才能进行后面的设置
DMAMUX_Channel->CCR = pConfig->DMA_Req; //设置DMA请求,同时禁止同步模式,禁止中断,禁止事件
DMA_Stream->CR |= (pConfig->Mem_BurstSize & 0x03)<<23; //存储器突发传输配置
DMA_Stream->CR |= (pConfig->Per_BurstSize & 0x03)<<21; //外设突发传输配置
if(pConfig->isDoubleBuffer) //使能双缓冲模式
{
DMA_Stream->CR |= 1<<18;
}
DMA_Stream->CR |= (pConfig->PrioLevel & 0x03)<<16; //优先级设置
if(!pConfig->isPerIncPsizeAuto) //外设地址的偏移量与 PSIZE 相关
{
DMA_Stream->CR |= 1<<15; //用于计算外设地址的偏移量固定为 4(32 位对齐)。
}
DMA_Stream->CR |= (pConfig->Mem_Size & 0x03)<<13; //存储器数据大小设置
DMA_Stream->CR |= (pConfig->Pre_Size & 0x03)<<11; //外设数据大小设置
if(pConfig->isMemInc)
{
DMA_Stream->CR |= 1<<10; //存储器地址递增模式
}
if(pConfig->isPerInc)
{
DMA_Stream->CR |= 1<<9; //外设地址递增模式
}
if(pConfig->isCircMode)
{
DMA_Stream->CR |= 1<<8; //循环模式
}
DMA_Stream->CR |= (pConfig->DataTranDir & 0x03)<<6; //数据传输方向
if(pConfig->isPreFlowCtrl)
{
DMA_Stream->CR |= 1<<5; //外设是流控设备
}
if(pConfig->isEnableTranComplInt)
{
DMA_Stream->CR |= 1<<4; //使能完成中断
}
DMA_Stream->NDTR = pConfig->TranDataCount; //传输的数据项数-外设到存储器是流控无需设置长度
DMA_Stream->PAR = pConfig->PerAddr; //外设地址
DMA_Stream->M0AR = pConfig->Mem0Addr; //存储器0地址
DMA_Stream->M1AR = pConfig->Mem0Addr; //存储器1地址
DMA_Stream->FCR = 0;
if(!pConfig->isDirectMode)
{
DMA_Stream->FCR |= 1 << 2; //禁止直接模式
}
DMA_Stream->FCR |= (pConfig->FIFO_Thres & 0x03)<<0; //FIFO阈值选择
//必须清除中断标记,否则DMA可能会停止工作
DMA_ClearIntStatus(DMAxSx_Ch, DMA_INT_ALL);
if(pConfig->isEnableStream)
{
DMA_Stream->CR |= 1<<0; //使能流,开始传输
}
//INFO_S("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
//INFO_S("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
//INFO_S("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
}
/*************************************************************************************************************************
* 函数 : void DMA_StartTrans(DMAxSx_CH_TYPE DMAxSx_Ch, u32 Mem0Addr, u32 Mem1Addr, u16 TranDataCount)
* 功能 : 启动DMA 传输
* 参数 : DMAxSx_Ch:DMA通道选择;Mem0Addr:内存地址0,Mem1Addr:内存地址1(双缓冲模式下有效);TranDataCount:传输数据数量
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-11
* 最后修改时间 : 2025-03-11
* 说明 : 启动传输,请先调用DMA_Config对当前通道进行配置
*************************************************************************************************************************/
void DMA_StartTrans(DMAxSx_CH_TYPE DMAxSx_Ch, u32 Mem0Addr, u32 Mem1Addr, u16 TranDataCount)
{
u8 Stream;
DMA_Stream_TypeDef *DMA_Stream;
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
if (Stream > 7) //通道或流超出了范围
{
return;
}
if((DMAxSx_Ch >> 8)&0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
}
DMA_Stream->CR &= ~BIT0; //关闭数据流
DMA_Stream->NDTR = TranDataCount; //传输的数据项数
DMA_Stream->M0AR = Mem0Addr; //存储器0地址
DMA_Stream->M1AR = Mem1Addr; //存储器1地址
//必须清除中断标记,否则DMA可能会停止工作
DMA_ClearIntStatus(DMAxSx_Ch, DMA_INT_ALL);
DMA_Stream->CR |= 1<<0; //使能流,开始传输
}
/*************************************************************************************************************************
* 函数 : void DMA_StartTrans_Mem(DMAxSx_CH_TYPE DMAxSx_Ch, u32 DestAddr, u32 SourceAddr, u16 TranDataCount)
* 功能 : 启动DMA 传输(内存到内存传输)
* 参数 : DMAxSx_Ch:DMA通道选择;DestAddr:目标内存地址,SourceAddr:源内存地址;TranDataCount:传输数据数量
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-11
* 最后修改时间 : 2025-03-11
* 说明 : 启动传输,请先调用DMA_Config对当前通道进行配置
*************************************************************************************************************************/
void DMA_StartTrans_Mem(DMAxSx_CH_TYPE DMAxSx_Ch, u32 DestAddr, u32 SourceAddr, u16 TranDataCount)
{
u8 Stream;
DMA_Stream_TypeDef* DMA_Stream;
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
if (Stream > 7) //通道或流超出了范围
{
return;
}
if((DMAxSx_Ch >> 8)&0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
}
DMA_Stream->CR &= ~BIT0; //关闭数据流
DMA_Stream->NDTR = TranDataCount; //传输的数据项数
DMA_Stream->M0AR = DestAddr; //存储器0地址-目标地址
DMA_Stream->PAR = SourceAddr; //外设-源地址
//必须清除中断标记,否则DMA可能会停止工作
DMA_ClearIntStatus(DMAxSx_Ch, DMA_INT_ALL);
DMA_Stream->CR |= 1<<0; //使能流,开始传输
}
/*************************************************************************************************************************
* 函数 : void DMA_StopTrans(DMAxSx_CH_TYPE DMAxSx_Ch)
* 功能 : 关闭 DMA 传输
* 参数 : DMAxSx_Ch:DMA通道选择
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-11
* 最后修改时间 : 2025-03-11
* 说明 :
*************************************************************************************************************************/
void DMA_StopTrans(DMAxSx_CH_TYPE DMAxSx_Ch)
{
u8 Stream;
DMA_Stream_TypeDef *DMA_Stream;
u32 retry = 100000;
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
if (Stream > 7) //通道或流超出了范围
{
return;
}
if((DMAxSx_Ch >> 8)&0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
}
DMA_Stream->CR &= ~BIT0; //关闭数据流
while((DMA_Stream->CR & BIT0) && retry) //等待数据流关闭成功
{
retry --;
Delay_US(1);
}
if (0 == retry)
{
ERROR_S("DMA CH%d停止超时\r\n", DMAxSx_Ch);
}
//必须清除中断标记,否则DMA可能会停止工作
DMA_ClearIntStatus(DMAxSx_Ch, DMA_INT_ALL);
}
/*************************************************************************************************************************
* 函数 : u32 DMA_GetIntStatus(DMAxSx_CH_TYPE DMAxSx_Ch)
* 功能 : 获取DMA中断状态
* 参数 : DMAxSx_Ch:DMA通道选择
* 返回 : 中断状态,见DMA中断定义
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-11
* 最后修改时间 : 2025-03-11
* 说明 :
*************************************************************************************************************************/
u32 DMA_GetIntStatus(DMAxSx_CH_TYPE DMAxSx_Ch)
{
u8 Stream;
DMA_TypeDef *DMAx;
u32 temp = 0;
DMAx = (DMA_TypeDef *)sg_DMA_TYPE_BUFF[(DMAxSx_Ch >> 8)&0x01]; //获取DMA
Stream = (DMAxSx_Ch)&0xff; //获取流通道
if (Stream > 7) //通道或流超出了范围
{
return 0;
}
switch(Stream)
{
case 0: //Stream 0
{
temp = (DMAx->LISR >> 0)&0x3F;
}break;
case 1: //Stream 1
{
temp = (DMAx->LISR >> 6)&0x3F;
}break;
case 2: //Stream 2
{
temp = (DMAx->LISR >> (16+0))&0x3F;
}break;
case 3: //Stream 3
{
temp = (DMAx->LISR >> (16+6))&0x3F;
}break;
case 4: //Stream 4
{
temp = (DMAx->HISR >> 0)&0x3F;
}break;
case 5: //Stream 5
{
temp = (DMAx->HISR >> 6)&0x3F;
}break;
case 6: //Stream 6
{
temp = (DMAx->HISR >> (16+0))&0x3F;
}break;
case 7: //Stream 7
{
temp = (DMAx->HISR >> (16+6))&0x3F;
}break;
default:
{
temp = 0;
}break;
}
return temp;
}
/*************************************************************************************************************************
* 函数 : void DMA_ClearIntStatus(DMAxSx_CH_TYPE DMAxSx_Ch, u32 IntStatus)
* 功能 : 清除DMA中断状态
* 参数 : DMAxSx_Ch:DMA通道选择;IntStatus:要清除的中断,见DMA中断定义
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-11
* 最后修改时间 : 2025-03-11
* 说明 :
*************************************************************************************************************************/
void DMA_ClearIntStatus(DMAxSx_CH_TYPE DMAxSx_Ch, u32 IntStatus)
{
u8 Stream;
DMA_TypeDef *DMAx;
u32 temp;
DMAx = (DMA_TypeDef *)sg_DMA_TYPE_BUFF[(DMAxSx_Ch >> 8)&0x01]; //获取DMA
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
if (Stream > 7) //通道或流超出了范围
{
return;
}
switch(Stream)
{
case 0: //Stream 0
{
temp = IntStatus & 0x3F;
DMAx->LIFCR |= temp << 0;
}break;
case 1: //Stream 1
{
temp = IntStatus & 0x3F;
DMAx->LIFCR |= temp << 6;
}break;
case 2: //Stream 2
{
temp = IntStatus & 0x3F;
DMAx->LIFCR |= temp << (16 + 0);
}break;
case 3: //Stream 3
{
temp = IntStatus & 0x3F;
DMAx->LIFCR |= temp << (16 + 6);
}break;
case 4: //Stream 4
{
temp = IntStatus & 0x3F;
DMAx->HIFCR |= temp << 0;
}break;
case 5: //Stream 5
{
temp = IntStatus & 0x3F;
DMAx->HIFCR |= temp << 6;
}break;
case 6: //Stream 6
{
temp = IntStatus & 0x3F;
DMAx->HIFCR |= temp << (16 + 0);
}break;
case 7: //Stream 7
{
temp = IntStatus & 0x3F;
DMAx->HIFCR |= temp << (16 + 6);
}break;
default : break;
}
}
/*************************************************************************************************************************
* 函数 : u8 DMA_GetCurrentTargetBuffIndex(DMAxSx_CH_TYPE DMAxSx_Ch)
* 功能 : 获取双缓冲模式下当前使用的存储器缓冲区索引(0,1)
* 参数 : DMAxSx_Ch:DMA通道选择;
* 返回 : 0:存储器0正在被使用;1:存储器1正在被使用
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-11
* 最后修改时间 : 2025-03-11
* 说明 : 仅在双缓冲模式下有效
*************************************************************************************************************************/
u8 DMA_GetCurrentTargetBuffIndex(DMAxSx_CH_TYPE DMAxSx_Ch)
{
u8 Stream;
DMA_Stream_TypeDef *DMA_Stream;
// DMA_TypeDef *DMAx;
// u32 temp;
// DMAx = (DMA_TypeDef *)DMA_TYPE_BUFF[(DMAxSx_Ch >> 16)&0x01]; //获取DMA
Stream = (DMAxSx_Ch)&0xff; //获取流通道
if(Stream > 7) return 0;
if((DMAxSx_Ch >> 8)&0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef *)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
}
if(DMA_Stream->CR & BIT19) return 1;
else return 0;
}
/*************************************************************************************************************************
* 函数 : u16 DMA_GetCompleteResidualCnt(DAM1Sx_CH_TYPE DMA1Sx_Ch)
* 功能 : 获取DMA传输的剩余数据量
* 参数 : DMAxSx_Ch:DMA通道选择;
* 返回 : 剩余数据数量
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-23
* 最后修改时间 : 2025-03-23
* 说明 :
*************************************************************************************************************************/
u16 DMA_GetCompleteResidualCnt(DMAxSx_CH_TYPE DMAxSx_Ch)
{
u8 Stream;
DMA_Stream_TypeDef* DMA_Stream;
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
if (Stream > 7) return 0;
if ((DMAxSx_Ch >> 8) & 0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
}
return (u16)(DMA_Stream->NDTR); //获取传输的剩余数据量
}
/*************************************************************************************************************************
* 函数 : bool DMA_WaitComplete(DMAxSx_CH_TYPE DMAxSx_Ch, u16 TimeOutMs)
* 功能 : 等待DMA传输完成
* 参数 : DMAxSx_Ch:DMA通道选择;TimeOutMs:等待超时时间,单位ms
* 返回 : TRUE:传输完成,FALSE:传输超时
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-23
* 最后修改时间 : 2025-03-23
* 说明 : 无
*************************************************************************************************************************/
bool DMA_WaitComplete(DMAxSx_CH_TYPE DMAxSx_Ch, u16 TimeOutMs)
{
u32 DelayCount = TimeOutMs + 1;
u8 Stream;
DMA_Stream_TypeDef* DMA_Stream;
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
if (Stream > 7) return 0;
if ((DMAxSx_Ch >> 8) & 0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
}
while (DelayCount)
{
if ((DMA_Stream->NDTR) == 0) return TRUE;
SYS_DelayMS(1);
DelayCount--;
}
return FALSE;
}
/*************************************************************************************************************************
* 函数 : void DMA_MemoryToPeripheralConfig(DMAxSx_CH_TYPE DMAxSx_Ch, DMA12_MUX1_REQ DMA_Req, u32 PeriAddr, DMA_SIZE_TYPE SIZE_xbit, bool isMemoryInc, bool isCircularMode)
* 功能 : DMA存储器到外设传输配置(快捷设置)-不会启动传输
* 参数 : DMAxSx_Ch:DMA通道选择,DMA_Req:DMA请求选择,,PeriAddr:外设地址;SIZE_xbit:传输位宽;isMemoryInc:内存地址自增;isCircularMode:循环模式
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-23
* 最后修改时间 : 2025-03-23
* 说明 : 不会启动传输
*************************************************************************************************************************/
void DMA_MemoryToPeripheralConfig(DMAxSx_CH_TYPE DMAxSx_Ch, DMA12_MUX1_REQ DMA_Req,u32 PeriAddr, DMA_SIZE_TYPE SIZE_xbit, bool isMemoryInc, bool isCircularMode)
{
u8 Stream, Req;
DMA_Stream_TypeDef* DMA_Stream;
DMAMUX_Channel_TypeDef* DMAMUX_Channel;
DMA_TypeDef* DMAx;
DMAx = (DMA_TypeDef*)sg_DMA_TYPE_BUFF[(DMAxSx_Ch >> 8) & 0x01]; //获取DMA
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
Req = DMAxSx_Ch & 0xff;
if ((Req > DMA12_MUX1_REQ_MAX) || (Stream > 7)) //通道或流超出了范围
{
return;
}
if ((DMAxSx_Ch >> 8) & 0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
DMAMUX_Channel = (DMAMUX_Channel_TypeDef*)sg_DMAMUX_Channel[8 + Stream];
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
DMAMUX_Channel = (DMAMUX_Channel_TypeDef*)sg_DMAMUX_Channel[Stream];
}
//开始配置
DMA_Stream->CR = 0; //禁止数据流,才能进行后面的设置
DMAMUX_Channel->CCR = DMA_Req; //设置DMA请求,同时禁止同步模式,禁止中断,禁止事件
DMA_Stream->CR |= 1 << 16; //优先级中等
DMA_Stream->CR |= SIZE_xbit << 13; //存储器数据大小设置
DMA_Stream->CR |= SIZE_xbit << 11; //外设数据大小设置
if (isMemoryInc) DMA_Stream->CR |= 1 << 10; //存储器地址递增模式
//DMA_Stream->CR |= 1<<9; //外设地址递增模式
if (isCircularMode) DMA_Stream->CR |= 1 << 8; //循环模式开启
DMA_Stream->CR |= 1 << 6; //数据传输方向->存储器到外设
//DMA_Stream->CR |= 1<<5; //外设是流控设备
DMA_Stream->FCR = 0; //使能直接模式
DMA_Stream->PAR = PeriAddr; //外设地址
//DMA_Stream->M0AR = MemAddr; //存储器0地址
//DMA_Stream->NDTR = DataCnt; //传输的数据项数
//DMA_Stream->CR |= 1 << 0; //使能流,开始传输
//uart_printf("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
//uart_printf("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
//uart_printf("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
}
/*************************************************************************************************************************
* 函数 : void DMA_PeripheralToMemoryConfig(DMAxSx_CH_TYPE DMAxSx_Ch, DMA12_MUX1_REQ DMA_Req, u32 MemAddr, u32 PeriAddr, DMA_SIZE_TYPE SIZE_xbit, u16 DataCnt, bool isMemoryInc)
* 功能 : DMA外设到存储器传输配置(快捷设置)
* 参数 : DMAxSx_Ch:DMA通道选择,DMA_Req:DMA请求选择,MemAddr:存储器地址;PeriAddr:外设地址;SIZE_xbit:传输位宽;DataCnt:传输数量;isMemoryInc:内存地址自增
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-23
* 最后修改时间 : 2025-03-23
* 说明 : 不会启动传输
*************************************************************************************************************************/
void DMA_PeripheralToMemoryConfig(DMAxSx_CH_TYPE DMAxSx_Ch, DMA12_MUX1_REQ DMA_Req, u32 MemAddr, u32 PeriAddr, DMA_SIZE_TYPE SIZE_xbit, u16 DataCnt, bool isMemoryInc)
{
u8 Stream, Req;
DMA_Stream_TypeDef* DMA_Stream;
DMAMUX_Channel_TypeDef* DMAMUX_Channel;
DMA_TypeDef* DMAx;
DMAx = (DMA_TypeDef*)sg_DMA_TYPE_BUFF[(DMAxSx_Ch >> 8) & 0x01]; //获取DMA
Stream = (DMAxSx_Ch) & 0xff; //获取流通道
Req = DMAxSx_Ch & 0xff;
if ((Req > DMA12_MUX1_REQ_MAX) || (Stream > 7)) //通道或流超出了范围
{
return;
}
if ((DMAxSx_Ch >> 8) & 0x01) //DMA2
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA2_Stream[Stream]; //获取数据流 x 配置寄存器
DMAMUX_Channel = (DMAMUX_Channel_TypeDef*)sg_DMAMUX_Channel[8 + Stream];
}
else //DMA1
{
DMA_Stream = (DMA_Stream_TypeDef*)sg_DMA1_Stream[Stream]; //获取数据流 x 配置寄存器
DMAMUX_Channel = (DMAMUX_Channel_TypeDef*)sg_DMAMUX_Channel[Stream];
}
//开始配置
DMA_Stream->CR = 0; //禁止数据流,才能进行后面的设置
DMAMUX_Channel->CCR = DMA_Req; //设置DMA请求,同时禁止同步模式,禁止中断,禁止事件
DMA_Stream->CR |= 3 << 16; //优先高
DMA_Stream->CR |= SIZE_xbit << 13; //存储器数据大小设置
DMA_Stream->CR |= SIZE_xbit << 11; //外设数据大小设置
if (isMemoryInc) DMA_Stream->CR |= 1 << 10; //存储器地址递增模式
//DMA_Stream->CR |= 1<<9; //外设地址递增模式
DMA_Stream->CR |= 0 << 6; //数据传输方向->外设到存储器
//DMA_Stream->CR |= 1<<5; //外设是流控设备
DMA_Stream->FCR = 0; //使能直接模式
DMA_Stream->PAR = PeriAddr; //外设地址
DMA_Stream->M0AR = MemAddr; //存储器地址
//必须清除中断标记,否则DMA可能会停止工作
if (Stream < 4)
{
DMA2->LIFCR = DMA2->LISR; //写1清除中断标志
}
else
{
DMA2->HIFCR = DMA2->HISR; //写1清除中断标志
}
DMA_Stream->NDTR = DataCnt; //传输的数据项数
//uart_printf("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
//uart_printf("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
//uart_printf("DMA_Stream->NDTR:%X\r\n",DMA_Stream->NDTR);
}
//==动态申请DMA相关==
static u16 sg_DMA_ChannelStatus = 0; //DMA通道占用状态 DAM1的8个通道灵活申请
/*************************************************************************************************************************
* 函数 : int DMA_GetIdleChannel(void)
* 功能 : 获取一个DMA空闲通道(用完后需要释放)
* 参数 : 无
* 返回 : -1:无效;0-7:DMA1的通道1-通道8
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-30
* 最后修改时间 : 2025-03-30
* 说明 : 只能动态获取DMA1的8个通道
*************************************************************************************************************************/
int DMA_GetIdleChannel(void)
{
OS_CRITICAL_SR_VAL;
u8 i;
int ch = -1;
OS_EnterCriticalSection(); //关闭系统中断,进入临界状态
for (i = 0; i < 8; i++)
{
if ((sg_DMA_ChannelStatus & (1 << i)) == 0) //当前通道空闲
{
ch = i;
sg_DMA_ChannelStatus |= 1 << i; //记录当前通道,并进行占用
break;
}
}
OS_LeaveCriticalSection(); //退出临界状态
return ch;
}
/*************************************************************************************************************************
* 函数 : void DMA_ReleaseChannel(int ch)
* 功能 : 释放当前使用的通道
* 参数 : ch:0-7:DMA1的通道1-通道8
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : www.scj-water.com
* 时间 : 2025-03-30
* 最后修改时间 : 2025-03-30
* 说明 : 只能动态获取DMA1的7个通道
*************************************************************************************************************************/
void DMA_ReleaseChannel(int ch)
{
OS_CRITICAL_SR_VAL;
if (ch < 0 || ch > 7) return;
OS_EnterCriticalSection(); //关闭系统中断,进入临界状态
sg_DMA_ChannelStatus &= ~(1 << ch); //释放占用状态
OS_LeaveCriticalSection(); //退出临界状态
DMA_StopTrans((DMAxSx_CH_TYPE)ch); //关闭DMA
}
//DMA.h
/*************************************************************************************************************
* 文件名 : dma.h
* 功能 : STM32H7 DMA驱动
* 作者 : www.scj-water.com
* 创建时间 : 2025-03-07
* 最后修改时间 : 2025-03-07
* 详细:
*************************************************************************************************************/
#ifndef __DMA_H_
#define __DMA_H_
#include "system.h"
//传输数据位宽定义
typedef enum
{
DMA_SIZE_8BIT = 0,
DMA_SIZE_16BIT = 1,
DMA_SIZE_32BIT = 2,
}DMA_SIZE_TYPE;
//DMA1,2请求映射(使用的DMAMUX1通道)
typedef enum
{
dmamux1_req_gen0 =1,
dmamux1_req_gen1 =2,
dmamux1_req_gen2 =3,
dmamux1_req_gen3 =4,
dmamux1_req_gen4 =5,
dmamux1_req_gen5 =6,
dmamux1_req_gen6 =7,
dmamux1_req_gen7 =8,
DMA12_ADC1 =9,
DMA12_ADC2 =10,
DMA12_TIM1_CH1 =11,
DMA12_TIM1_CH2 =12,
DMA12_TIM1_CH3 =13,
DMA12_TIM1_CH4 =14,
DMA12_TIM1_UP =15,
DMA12_TIM1_TRIG =16,
DMA12_TIM1_COM =17,
DMA12_TIM2_CH1 =18,
DMA12_TIM2_CH2 =19,
DMA12_TIM2_CH3 =20,
DMA12_TIM2_CH4 =21,
DMA12_TIM2_UP =22,
DMA12_TIM3_CH1 =23,
DMA12_TIM3_CH2 =24,
DMA12_TIM3_CH3 =25,
DMA12_TIM3_CH4 =26,
DMA12_TIM3_UP =27,
DMA12_TIM3_TRIG =28,
DMA12_TIM4_CH1 =29,
DMA12_TIM4_CH2 =30,
DMA12_TIM4_CH3 =31,
DMA12_TIM4_UP =32,
DMA12_I2C1_RX =33,
DMA12_I2C1_TX =34,
DMA12_I2C2_RX =35,
DMA12_I2C2_TX =36,
DMA12_SPI1_RX =37,
DMA12_SPI1_TX =38,
DMA12_SPI2_RX =39,
DMA12_SPI2_TX =40,
DMA12_USART1_RX =41,
DMA12_USART1_TX =42,
DMA12_USART2_RX =43,
DMA12_USART2_TX =44,
DMA12_USART3_RX =45,
DMA12_USART3_TX =46,
DMA12_TIM8_CH1 =47,
DMA12_TIM8_CH2 =48,
DMA12_TIM8_CH3 =49,
DMA12_TIM8_CH4 =50,
DMA12_TIM8_UP =51,
DMA12_TIM8_TRIG =52,
DMA12_TIM8_COM =53,
//DMA12_保留 =54,
DMA12_TIM5_CH1 =55,
DMA12_TIM5_CH2 =56,
DMA12_TIM5_CH3 =57,
DMA12_TIM5_CH4 =58,
DMA12_TIM5_UP =59,
DMA12_TIM5_TRIG =60,
DMA12_SPI3_RX =61,
DMA12_SPI3_TX =62,
DMA12_UART4_RX =63,
DMA12_UART4_TX =64,
DMA12_UART5_RX =65,
DMA12_UART5_TX =66,
DMA12_DAC1 =67,
DMA12_DAC2 =68,
DMA12_TIM6_UP =69,
DMA12_TIM7_UP =70,
DMA12_USART6_RX =71,
DMA12_USART6_TX =72,
DMA12_I2C3_RX =73,
DMA12_I2C3_TX =74,
DMA12_DCMI =75,
DMA12_CRYP_IN =76,
DMA12_CRYP_OUT =77,
DMA12_HASH_IN =78,
DMA12_UART7_RX =79,
DMA12_UART7_TX =80,
DMA12_UART8_RX =81,
DMA12_UART8_TX =82,
DMA12_SPI4_RX =83,
DMA12_SPI4_TX =84,
DMA12_SPI5_RX =85,
DMA12_SPI5_TX =86,
DMA12_SAI1_A =87,
DMA12_SAI1_B =88,
DMA12_SAI2_A =89,
DMA12_SAI2_B =90,
DMA12_SWPMI_RX =91,
DMA12_SWPMI_TX =92,
DMA12_SPDIFRX_DT =93,
DMA12_SPDIFRX_CS =94,
DMA12_HR_REQ_1 =95,
DMA12_HR_REQ_2 =96,
DMA12_HR_REQ_3 =97,
DMA12_HR_REQ_4 =98,
DMA12_HR_REQ_5 =99,
DMA12_HR_REQ_6 =100,
dfsdm1_dma0 =101,
dfsdm1_dma1 =102,
dfsdm1_dma2 =103,
dfsdm1_dma3 =104,
DMA12_TIM15_CH1 =105,
DMA12_TIM15_UP =106,
DMA12_TIM15_TRIG =107,
DMA12_TIM15_COM =108,
DMA12_TIM16_CH1 =109,
DMA12_TIM16_UP =110,
DMA12_TIM17_CH1 =111,
DMA12_TIM17_UP =112,
DMA12_SAI3_A =113,
DMA12_SAI3_B =114,
DMA12_ADC3 =115
}DMA12_MUX1_REQ;
//DMA通道选择
typedef enum
{
DMA1_CH0 = 0x0000,
DMA1_CH1 = 0x0001,
DMA1_CH2 = 0x0002,
DMA1_CH3 = 0x0003,
DMA1_CH4 = 0x0004,
DMA1_CH5 = 0x0005,
DMA1_CH6 = 0x0006,
DMA1_CH7 = 0x0007,
DMA2_CH0 = 0x0100,
DMA2_CH1 = 0x0101,
DMA2_CH2 = 0x0102,
DMA2_CH3 = 0x0103,
DMA2_CH4 = 0x0104,
DMA2_CH5 = 0x0105,
DMA2_CH6 = 0x0106,
DMA2_CH7 = 0x0107,
}DMAxSx_CH_TYPE;
//存储器到存储器通道定义
typedef enum
{
//内存到内存通道,使用一些不常用的通道用内存到内存的通道
DAM2S0_MEM = 0x010001,
DAM2S1_MEM = 0x010103,
DAM2S2_MEM = 0x010202,
DAM2S3_MEM = 0x010302,
DAM2S4_MEM = 0x010404,
DAM2S5_MEM = 0x010505,
DAM2S6_MEM = 0x010606,
DAM2S7_MEM = 0x010706,
}DMA_MEM_CH;
//突发传输长度配置(在直接模式中,当位EN =1时这些位由硬件强制置为 0x0。)
typedef enum
{
DMA_BURST_INCR1 = 0, //单次传输
DMA_BURST_INCR4 = 1, //4个节拍传输
DMA_BURST_INCR8 = 2, //8个节拍传输
DMA_BURST_INCR16 = 3, //16个节拍传输
}DMA_BURST_TRAN_SIZE;
//优先级
typedef enum
{
DAM_PRIO_LEVEL_0 = 0, //优先级低
DAM_PRIO_LEVEL_1 = 1, //优先级中
DAM_PRIO_LEVEL_2 = 3, //优先级高
DAM_PRIO_LEVEL_3 = 4, //优先级最高
}DAM_PRIO_LEVEL;
//数据传输方向定义
typedef enum
{
DMA_PRE_TO_MEM = 0, //外设到存储器
DMA_MEM_TO_PRE = 1, //存储器到外设
DMA_MEM_TO_MEM = 2, //存储器到存储器
}DMA_TRAN_DIR;
//FIFO阈值选择
typedef enum
{
DMA_FIFO_THRES_1_4 = 0, //FIFO阈值为1/4
DMA_FIFO_THRES_1_2 = 1, //FIFO阈值为1/2
DMA_FIFO_THRES_3_4 = 2, //FIFO阈值为3/4
DMA_FIFO_THRES_FULL = 3, //FIFO阈值为完整容量
}DMA_FIFO_THRES;
//DMA配置
typedef struct
{
DMA12_MUX1_REQ DMA_Req; //DMA请求定义
DMA_BURST_TRAN_SIZE Mem_BurstSize; //存储器的突发传输长度配置
DMA_BURST_TRAN_SIZE Per_BurstSize; //外设的突发传输长度配置
DAM_PRIO_LEVEL PrioLevel; //传输优先级
DMA_SIZE_TYPE Mem_Size; //存储器数据大小
DMA_SIZE_TYPE Pre_Size; //外设数据大小
DMA_TRAN_DIR DataTranDir; //数据传输方向
u32 PerAddr; //外设地址
u32 Mem0Addr; //存储器0地址
u32 Mem1Addr; //存储器1地址
DMA_FIFO_THRES FIFO_Thres; //非直接模式下,FIFO阈值容量选择
u16 TranDataCount; //传输数据长度-外设到存储器是流控无需设置长度
bool isDoubleBuffer; //使能双缓冲模式
bool isPerIncPsizeAuto; //外设偏移量自动根据PSIZE设置(TRUE:偏移量与 PSIZE 相关;否则4字节对齐,如果位 PINC =“0”,则此位没有意义)
bool isMemInc; //存储器地址自增
bool isPerInc; //外设地址自增
bool isCircMode; //循环模式
bool isPreFlowCtrl; //外设是流控制设备
bool isEnableTranComplInt; //传输完成中断使能
bool isDirectMode; //直接模式
bool isEnableStream; //是否使能数据流-开始传输
}DAM_CONFIG;
//DMA中断定义
#define DAM_INT_FIFO_ERROR BIT0 //数据流FIFO错误中断
#define DMA_INT_DIRECT_ERROR BIT2 //直接模式错误中断
#define DMA_INT_TRANS_ERROR BIT3 //传输错误
#define DMA_INT_TRANS_HALF BIT4 //传输过半
#define DMA_INT_TRANS_COMPL BIT5 //传输完成中断
#define DMA_INT_ALL (0x3D) //所有中断
void DMA_Config(DMAxSx_CH_TYPE DMAxSx_Ch, DAM_CONFIG *pConfig); //DMA配置
void DMA_StartTrans(DMAxSx_CH_TYPE DMAxSx_Ch, u32 Mem0Addr, u32 Mem1Addr, u16 TranDataCount); //启动DMA 传输
void DMA_StartTrans_Mem(DMAxSx_CH_TYPE DMAxSx_Ch, u32 DestAddr, u32 SourceAddr, u16 TranDataCount); //启动DMA 传输(内存到内存传输)
void DMA_StopTrans(DMAxSx_CH_TYPE DMAxSx_Ch); //关闭 DMA 传输
u32 DMA_GetIntStatus(DMAxSx_CH_TYPE DMAxSx_Ch); //获取DMA中断状态
void DMA_ClearIntStatus(DMAxSx_CH_TYPE DMAxSx_Ch, u32 IntStatus); //清除DMA中断状态
u8 DMA_GetCurrentTargetBuffIndex(DMAxSx_CH_TYPE DMAxSx_Ch); //获取双缓冲模式下当前使用的存储器缓冲区索引(0,1)
u16 DMA_GetCompleteResidualCnt(DMAxSx_CH_TYPE DMAxSx_Ch); //获取DMA传输的剩余数据量
bool DMA_WaitComplete(DMAxSx_CH_TYPE DMAxSx_Ch, u16 TimeOutMs); //等待DMA传输完成
//============快捷配置接口============
//DMA存储器到外设传输配置(快捷设置)-不会启动传输依赖DMA_StartTrans()开始传输
void DMA_MemoryToPeripheralConfig(DMAxSx_CH_TYPE DMAxSx_Ch, DMA12_MUX1_REQ DMA_Req, u32 PeriAddr, DMA_SIZE_TYPE SIZE_xbit, bool isMemoryInc, bool isCircularMode);
//DMA外设到存储器传输配置(快捷设置)-不会启动传输依赖DMA_StartTrans()开始传输
void DMA_PeripheralToMemoryConfig(DMAxSx_CH_TYPE DMAxSx_Ch, DMA12_MUX1_REQ DMA_Req, u32 MemAddr, u32 PeriAddr, DMA_SIZE_TYPE SIZE_xbit, u16 DataCnt, bool isMemoryInc);
int DMA_GetIdleChannel(void); //获取一个DMA空闲通道(用完后需要释放)
void DMA_ReleaseChannel(int ch); //释放当前使用的通道
#endif //__DMA_H_
//测试
/*************************************************************************************************************
* 文件名: dma_test.c
* 功能: DMA测试
* 作者: www.scj-water.com
* 创建时间: 2025-03-14
* 最后修改时间: 2025-03-14
* 详细:
*************************************************************************************************************/
#include "system.h"
#include "test.h"
#include "dma.h"
#include "string.h"
char g_buff1[33];
static char g_buff2[33];
//DMA 内存到内存的传输测试
void DMA_MemToMem_Test(void)
{
DAM_CONFIG mConfig;
u16 cnt;
u16 i;
INFO_S("DMA 内存到内存拷贝测试\r\n");
mConfig.DMA_Req = 0; //DMA请求定义-内存到内存,软件触发
mConfig.Mem_BurstSize = DMA_BURST_INCR4; //存储器的突发传输长度配置
mConfig.Per_BurstSize = DMA_BURST_INCR4; //外设的突发传输长度配置
mConfig.PrioLevel = DAM_PRIO_LEVEL_1; //传输优先级
mConfig.Mem_Size = DMA_SIZE_8BIT; //存储器数据大小
mConfig.Pre_Size = DMA_SIZE_8BIT; //外设数据大小
mConfig.DataTranDir = DMA_MEM_TO_MEM; //数据传输方向
mConfig.PerAddr = (u32)g_buff2; //外设地址
mConfig.Mem0Addr = (u32)g_buff1; //存储器0地址
mConfig.Mem1Addr = NULL; //存储器1地址
mConfig.FIFO_Thres = DMA_FIFO_THRES_1_2; //非直接模式下,FIFO阈值容量选择
mConfig.TranDataCount = sizeof(g_buff1); //传输数据长度-外设到存储器是流控无需设置长度
mConfig.isDoubleBuffer = FALSE; //使能双缓冲模式
mConfig.isPerIncPsizeAuto = TRUE; //外设偏移量自动根据PSIZE设置(TRUE:偏移量与 PSIZE 相关;否则4字节对齐,如果位 PINC =“0”,则此位没有意义)
mConfig.isMemInc = TRUE; //存储器地址自增
mConfig.isPerInc = TRUE; //外设地址自增
mConfig.isCircMode = FALSE; //循环模式
mConfig.isPreFlowCtrl = FALSE; //外设是流控制设备
mConfig.isEnableTranComplInt = FALSE; //传输完成中断使能
mConfig.isDirectMode = TRUE; //直接模式
mConfig.isEnableStream = TRUE; //是否使能数据流-开始传输
memset(g_buff1, 0, sizeof(g_buff1));
for (i = 0; i < sizeof(g_buff1); i++)
{
g_buff2[i] = '0' + i;
}
DMA_Config(DMA1_CH1, &mConfig);
SYS_DelayMS(2);
cnt = 0;
g_buff1[sizeof(g_buff1) - 1] = 0;
uart_printf("buff1:%s\r\n", g_buff1);
g_buff2[sizeof(g_buff2) - 1] = 0;
uart_printf("buff2:%s\r\n", g_buff2);
while(1)
{
SYS_DelayMS(1000);
}
}
如果您有任何问题,请跟我们联系!
欢迎来电:18571629282
Copyright © 2024 武汉水测家科技有限公司 版权所有 鄂ICP备2022002065号-1 --WTRExpert English website--