Nuru_Banmian
Nuru_Banmian
Published on 2025-07-16 / 48 Visits
0
0

DMA-ROM传输RAM-寄存器&HAL库实现

需求描述

使用寄存器操作把ROM中的数据通过DMA传输到RAM,然后把数据通过printf发送到串口验证是否正确。

DMA传输不涉及外设,所以通道随便选。我们选DMA1的1通道。

一般设置流程

时钟使能

代码

/* 1. 初始化DMA1时钟。DMA是挂在AHB总线上 */
RCC->AHBENR |= RCC_AHBENR_DMA1EN;

寄存器

DMA-ROM传输RAM-时钟使能-1.png

DMA-ROM传输RAM-时钟使能-2.png

设置储存器方向

代码

/* 2. 设置传输方向和存储器到存储器模式  0:从外设读 1:从内存读*/
DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;
DMA1_Channel1->CCR |= DMA_CCR1_MEM2MEM;

寄存器

这里设置解释虽然说的从外设读,实际上是将传出数据的设备称为外设,所以我们这里虽然ROM和RAM都是存储器,但是这里将ROM称为外设。

DMA-ROM传输RAM-设置储存器方向-1.png

DMA-ROM传输RAM-设置储存器方向-2.png

DMA-ROM传输RAM-设置储存器方向-3.png

设置外设和存储器宽度

代码

/* 4. 设置外设和存储器数据宽度 00:8位  01:16位  10:32位*/
DMA1_Channel1->CCR &= ~DMA_CCR1_MSIZE;
DMA1_Channel1->CCR &= ~DMA_CCR1_PSIZE;

寄存器

设置要一致,虽然我并不明白这里开了分开设置的通道的理由是什么,为什么不合并在一起?

DMA-ROM传输RAM-设置外设和存储器宽度-1.png

DMA-ROM传输RAM-设置外设和存储器宽度-2.png

设置外设和存储器地址自增

代码

/* 5. 外设和存储器地址自增 0:不自增 1:自增*/
DMA1_Channel1->CCR |= DMA_CCR1_MINC;
DMA1_Channel1->CCR |= DMA_CCR1_PINC;

寄存器

顾名思义,当我们传入大量数据的时候,开启自增让我们无需手动重新定位存储器来存储数据。

DMA-ROM传输RAM-设置外设和存储器地址自增-1.png

DMA-ROM传输RAM-设置外设和存储器地址自增-2.png

传输中断使能

代码

/* 6. 开启传输完成中断使能 */
DMA1_Channel1->CCR |= DMA_CCR1_TCIE;

寄存器

DMA-ROM传输RAM-传输中断使能-1.png

DMA-ROM传输RAM-传输中断使能-2.png

配置源和目的地地址

代码

/* 配置外设地址 */
DMA1_Channel1->CPAR = srcAddr;

/* 配置内存地址 */
DMA1_Channel1->CMAR = desAddr;

寄存器

DMA-ROM传输RAM-配置源和目的地地址-1.png

DMA-ROM传输RAM-配置源和目的地地址-2.png

DMA-ROM传输RAM-配置源和目的地地址-3.png

数据传输长度

代码

/* 配置要传输的数据长度 */
DMA1_Channel1->CNDTR = dataLength;

寄存器

DMA-ROM传输RAM-数据传输长度.png

通道传输使能

代码

/* 开启数据传输 */
DMA1_Channel1->CCR |= DMA_CCR1_EN;

寄存器

DMA-ROM传输RAM-通道传输使能-1.png

DMA-ROM传输RAM-通道传输使能-2.png

寄存器实现

dma.h

#ifndef __DMA_H
#define __DMA_H

#include "stm32f10x.h"

extern uint8_t isFinished;

// 初始化
void DMA1_Init(void);

// 数据传输
void DMA1_Transmit(uint32_t srcAddr, uint32_t destAddr, uint16_t dataLen);

#endif

dma.c

#include "dma.h"

// 初始化
void DMA1_Init(void)
{
    // 1. 开启时钟
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    // 2. DMA相关配置
    // 2.1 数据传输方向: 存储器到存储器,从外设读
    DMA1_Channel1->CCR |= DMA_CCR1_MEM2MEM;
    DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;

    // 2.2 数据宽度: 8位 - 00
    DMA1_Channel1->CCR &= ~DMA_CCR1_PSIZE;
    DMA1_Channel1->CCR &= ~DMA_CCR1_MSIZE;

    // 2.3 地址自增:开启自增
    DMA1_Channel1->CCR |= DMA_CCR1_PINC;
    DMA1_Channel1->CCR |= DMA_CCR1_MINC;

    // 2.4 开启数据传输完成中断标志
    DMA1_Channel1->CCR |= DMA_CCR1_TCIE;

    // 3. NVIC配置
    NVIC_SetPriorityGrouping(3);
    NVIC_SetPriority(DMA1_Channel1_IRQn, 2);
    NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

// 数据传输
void DMA1_Transmit(uint32_t srcAddr, uint32_t destAddr, uint16_t dataLen)
{
    // 1. 设置外设地址
    DMA1_Channel1->CPAR = destAddr;

    // 2. 设置存储器地址
    DMA1_Channel1->CMAR = srcAddr;

    // 3. 设置传输的数据量
    DMA1_Channel1->CNDTR = dataLen;

    // 4. 开启通道,开始传输数据
    DMA1_Channel1->CCR |= DMA_CCR1_EN;
}

// 中断服务程序
void DMA1_Channel1_IRQHandler(void)
{
    // 判断中断标志位
    if (DMA1->ISR & DMA_ISR_TCIF1)
    {
        // 清除中断标志位
        DMA1->IFCR |= DMA_IFCR_CTCIF1;

        // 关闭DMA通道
        DMA1_Channel1->CCR &= ~DMA_CCR1_EN;

        isFinished = 1;
    }
}

main.c

#include "usart.h"
#include "dma.h"

// 定义全局变量,表示数据传输完成
uint8_t isFinished = 0;

// 定义全局常量,放置在ROM中,作为数据源
const uint8_t src[] = {10,20,30,40};

// 定义变量数组,放置在RAM中,用来存储接收到的数据
uint8_t dest[4] = {0};

int main(void)
{
	// 初始化
	USART_Init();
	DMA1_Init();

	printf("Hello, world!\n");

	// 打印变量和常量地址
	printf("src = %p, dest = %p\n", src, dest);

	// 开启DMA通道进行传输
	DMA1_Transmit((uint32_t)src, (uint32_t)dest, 4);

	while(1)
	{
		if (isFinished)
		{
			// 打印输出验证
			for (uint8_t i = 0; i < 4; i++)
			{
				printf("%d\t", dest[i]);
			}
			isFinished = 0;
		}	
	}
}

HAL库实现

HAL库配置

DMA-ROM传输RAM-HAL库设置-1.png

DMA-ROM传输RAM-HAL库设置-2.png

DMA-ROM传输RAM-HAL库设置-3.png

DMA-ROM传输RAM-HAL库设置-4.png

DMA初始化代码

void MX_DMA_Init(void)
{
    /* DMA controller clock enable */
    __HAL_RCC_DMA1_CLK_ENABLE();

    /* Configure DMA request hdma_memtomem_dma1_channel1 on DMA1_Channel1 */
    hdma_memtomem_dma1_channel1.Instance = DMA1_Channel1;
    hdma_memtomem_dma1_channel1.Init.Direction = DMA_MEMORY_TO_MEMORY;
    hdma_memtomem_dma1_channel1.Init.PeriphInc = DMA_PINC_ENABLE;
    hdma_memtomem_dma1_channel1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_memtomem_dma1_channel1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_memtomem_dma1_channel1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_memtomem_dma1_channel1.Init.Mode = DMA_NORMAL;
    hdma_memtomem_dma1_channel1.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_memtomem_dma1_channel1) != HAL_OK)
    {
        Error_Handler();
    }
}

具体实现代码/main.c

int main(void)
{
   
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART1_UART_Init();
    /* 注册中断回调函数 */
    HAL_DMA_RegisterCallback(&hdma_memtomem_dma1_channel1,
                             HAL_DMA_XFER_CPLT_CB_ID,
                             dmaCompleteCallBack);

    HAL_DMA_Start_IT(&hdma_memtomem_dma1_channel1, (uint32_t)src, (uint32_t)des, 4);
    while (isFinished == 0)
        ;
    printf("%s\r\n", des);

    while (1)
    {
    }
}


Comment