三、STM32入门学习 之 DMA
这里我们以STM32F10x系列芯片为例直接存储器存取(DMA):在外设和存储器之间或者存储器和存储器之间实现高速数据传输的一种技术,无需CPU参与就能传递数据。两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。一个仲裁器来协调各个DMA请求的优先权。
·
1、DMA简介
这里我们以STM32F10x系列芯片为例
-
直接存储器存取(DMA):在外设和存储器之间 或者 存储器和存储器 之间实现高速数据传输的一种技术,无需CPU参与就能传递数据。
-
两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。
-
一个仲裁器来协调各个DMA请求的优先权。
2、DMA硬件电路

其中,DMA2仅存在于大容量产品和互联型产品。
3、DMA请求
- DMA1控制器

- 从外设
(TIMx[x=1, 2, 3, 4]、ADC1、SPI1、SPI/I2S2、I2Cx[x=1, 2]和USARTx[x=1, 2, 3])产生的7个请求,通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。 - 外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位,被独立地开启或关闭。
- DMA2控制器
存在于DMA2仅存在于大容量产品和互联型产品。
- 从外设
(TIMx[5, 6, 7, 8]、ADC3、SPI/I2S3、UART4、DAC通道1、2和SDIO)产生的5个请求,经逻辑或输入到DMA2控制器,这意味着同时只能有一个请求有效。 - 外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位,被独立地开启或关闭。
4、常用寄存器及标志位
1) 常用寄存器
- DMA 控制寄存器(CCR)
- 方向(DIR):设置数据传输方向(外设→内存 或 内存→外设)。
- 循环模式(CIRC):使能循环传输模式。
- 优先级(PL):设置 DMA 通道优先级(高 / 中 / 低)。
- 数据宽度(PSIZE、MSIZE):外设和内存的数据宽度(字节、半字、字)。
- 增量模式(PINC、MINC):使能外设或内存地址自动递增。
- DMA 外设地址寄存器(CPAR):存储外设数据传输的起始地址(如 USART_DR、ADC_DR 等)。
- DMA 内存地址寄存器(CMAR):存储内存缓冲区的起始地址。
- DMA 数据计数器寄存器(CNDTR):设置要传输的数据量(最大值为 65535)。
- DMA 中断使能寄存器(DIER):使能 / 禁用中断(传输完成、半传输完成、错误中断等)。
- DMA 状态寄存器(ISR):包含传输状态标志位(如传输完成、半传输完成、错误标志)。
2) 常用标志位(位于 ISR 寄存器)
- 传输完成标志(TCIFx):当 DMA 传输完成时置 1,需软件清除。
- 半传输完成标志(HTIFx):当传输数据量达到总数据量的一半时置 1。
- 传输错误标志(TEIFx):传输过程中出现错误时置 1。
- 直接模式错误标志(DMEIFx):直接模式下出现错误时置 1(仅特定型号支持)。
5、基本DMA配置流程
- 初始化 DMA 通道 / 数据流:
- 选择 DMA 通道和外设。
- 配置 CPAR、CMAR、CNDTR。
- 配置 CCR 寄存器:
- 设置传输方向、数据宽度、增量模式等。
- 使能中断(可选):
- 在 DIER 中使能对应的中断类型。
- 启动 DMA 传输:
- 通过 CCR 的 EN 位使能 DMA 通道。
- 处理中断(可选):
- 在中断服务函数中检查 ISR 标志位并清除。
6、代码
- 使用DMA进行内存与串口之间的数据发送
#include "stm32f10x.h" // Device header
#define BUFFER_SIZE 14
uint8_t TxBuffer[BUFFER_SIZE] = "Hello World!\r\n";
void DMA1_Configuration(void);
void USART1_Configuration(void);
int main(void)
{
DMA1_Configuration();
USART1_Configuration();
while(1)
{
}
}
void DMA1_Configuration(void)
{
//开启DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//配置DMA通道
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE; //传输数据的大小
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; //传输方向
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //这是一个布尔型参数,用于启用或禁用内存到内存的传输模式
DMA_InitStruct.DMA_MemoryBaseAddr =(uint32_t)TxBuffer; //DMA 传输数据的源或目标内存地址,具体取决于 DMA_DIR 的设置
DMA_InitStruct.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //它定义了内存中数据项单位,字节,半字,字
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //这是一个布尔型参数,用于决定在每次传输后内存地址是否自动递增
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //设置 DMA 的工作模式
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; //外设的基地址,也就是 DMA 传输数据的源或目标外设地址,具体取决于 DMA_DIR 的设置。
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据项单位,字节,半字,字
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //这是一个布尔型参数,用于决定在每次传输后外设地址是否自动递增。
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //DMA通道的优先级。
DMA_Init(DMA1_Channel4, &DMA_InitStruct);
//开启DMA中断
DMA_ITConfig(DMA1_Channel4,DMA_IT_TC|DMA_IT_HT|DMA_IT_TE,ENABLE);
//配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStruct);
DMA_Cmd(DMA1_Channel4,ENABLE);
}
void USART1_Configuration(void)
{
//开启USART1总线的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 初始化 GPIOA Pin_9作为 USART1 的 TX 引脚
GPIO_InitTypeDef GPIO_InitStructure;
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);
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = 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);
//开启USART_DMA的硬件触发功能
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //当 USART1 的数据寄存器DR为空时,会自动触发 DMA 将内存中的数据传输到USART1外设。
//开启USART1
USART_Cmd(USART1,ENABLE);
}
void DMA1_Channel4_IRQHandler(void)
{
if (DMA_GetITStatus(DMA1_IT_TC4))
DMA_ClearITPendingBit(DMA1_IT_TC4);
}
更多推荐



所有评论(0)