#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
// 定义常量
#define MEM_SIZE 256 // 模拟内存大小
#define MAX_TRANSFER_SIZE 64 // 最大传输字节数
// 模拟内存数组
uint8_t source_memory[MEM_SIZE];
uint8_t dest_memory[MEM_SIZE];
// DMA 控制寄存器结构体
typedef struct {
uint32_t source_addr; // 源地址
uint32_t dest_addr; // 目标地址
uint16_t transfer_size; // 传输字节数
bool enable; // DMA 使能位
bool interrupt_enable; // 中断使能位
bool done; // 传输完成标志
} DMA_Control;
// DMA 模块结构体
typedef struct {
DMA_Control control; // 控制寄存器
bool interrupt_pending; // 中断状态
} DMA_Module;
// 全局 DMA 模块实例
DMA_Module dma;
// 初始化 DMA 模块
void dma_init(void) {
memset(&dma, 0, sizeof(DMA_Module));
dma.control.enable = false;
dma.control.interrupt_enable = false;
dma.control.done = false;
dma.interrupt_pending = false;
}
// 配置 DMA 传输
bool dma_configure(uint32_t src, uint32_t dst, uint16_t size) {
// 校验参数
if (size > MAX_TRANSFER_SIZE || size == 0) {
printf("错误:无效的传输大小\n");
return false;
}
if (src + size > MEM_SIZE || dst + size > MEM_SIZE) {
printf("错误:超出内存范围\n");
return false;
}
// 设置控制寄存器
dma.control.source_addr = src;
dma.control.dest_addr = dst;
dma.control.transfer_size = size;
dma.control.done = false;
dma.interrupt_pending = false;
return true;
}
// 启动 DMA 传输
void dma_start(void) {
if (!dma.control.enable) {
dma.control.enable = true;
// 模拟突发传输
memcpy(&dest_memory[dma.control.dest_addr],
&source_memory[dma.control.source_addr],
dma.control.transfer_size);
// 标记传输完成
dma.control.done = true;
dma.control.enable = false;
// 如果使能中断则设置中断
if (dma.control.interrupt_enable) {
dma.interrupt_pending = true;
}
}
}
// 检查 DMA 传输是否完成
bool dma_is_done(void) {
return dma.control.done;
}
// 检查并清除中断
bool dma_check_interrupt(void) {
bool pending = dma.interrupt_pending;
dma.interrupt_pending = false;
return pending;
}
// 使能/禁止中断
void dma_set_interrupt(bool enable) {
dma.control.interrupt_enable = enable;
}
// 用测试数据初始化内存
void init_memory(void) {
for (int i = 0; i < MEM_SIZE; i++) {
source_memory[i] = (uint8_t)i; // 源内存填充递增值
dest_memory[i] = 0; // 目标内存清零
}
}
// 打印内存内容
void print_memory(uint32_t start, uint32_t size) {
printf("内存内容:\n");
for (uint32_t i = start; i < start + size; i++) {
printf("地址 %u: 源 = %u, 目标 = %u\n",
i, source_memory[i], dest_memory[i]);
}
}
// 测试程序
int main(void) {
// 初始化 DMA 和内存
dma_init();
init_memory();
// 配置 DMA 传输
uint32_t src_addr = 0;
uint32_t dst_addr = 100;
uint16_t size = 16;
printf("配置 DMA: 源=%u, 目标=%u, 大小=%u\n",
src_addr, dst_addr, size);
if (!dma_configure(src_addr, dst_addr, size)) {
printf("DMA 配置失败\n");
return 1;
}
// 使能中断并启动传输
dma_set_interrupt(true);
dma_start();
// 等待传输完成
while (!dma_is_done()) {
// 模拟轮询
}
// 检查中断
if (dma_check_interrupt()) {
printf("DMA 传输完成(中断)\n");
}
// 校验结果
print_memory(dst_addr, size);
// 检查数据是否正确传输
bool success = true;
for (uint32_t i = 0; i < size; i++) {
if (dest_memory[dst_addr + i] != source_memory[src_addr + i]) {
success = false;
break;
}
}
printf("传输%s\n", success ? "成功" : "失败");
return 0;
}
DMA 模块设计文档
1. 功能描述
本 DMA(直接内存访问)模块是一个用于教学目的的简化 C 语言仿真。它模拟了 DMA 控制器的基本功能,实现了在无需 CPU 干预的情况下,在两个内存区域之间进行数据传输。主要特性包括:
- 突发模式传输:一次性传输一块数据(最多 64 字节)。
- 可配置参数:支持用户自定义源地址、目标地址和传输大小。
- 中断仿真:传输完成后可选择产生中断信号。
- 控制/状态接口:使用结构体模拟硬件寄存器进行配置和状态监控。
- 内存模型:用 C 数组模拟源内存和目标内存。
该模块设计简洁,避免了复杂的硬件细节(如时序或总线协议),适合初学者学习嵌入式系统相关概念。
2. 主要函数与数据结构
数据结构
DMA_Control
结构体:source_addr
(uint32_t):源内存地址dest_addr
(uint32_t):目标内存地址transfer_size
(uint16_t):传输字节数enable
(bool):DMA 使能/禁止interrupt_enable
(bool):中断使能/禁止done
(bool):传输完成标志
DMA_Module
结构体:control
(DMA_Control):控制与状态寄存器interrupt_pending
(bool):中断状态标志
- 内存数组:
source_memory
(uint8_t[256]):模拟源内存dest_memory
(uint8_t[256]):模拟目标内存
函数
dma_init()
:初始化 DMA 模块,重置所有寄存器dma_configure(src, dst, size)
:配置 DMA 的源、目标和大小,并校验输入dma_start()
:启动突发传输,使用memcpy
模拟数据移动dma_is_done()
:检查传输是否完成dma_check_interrupt()
:检查并清除中断状态dma_set_interrupt(enable)
:使能/禁止中断init_memory()
:用测试数据初始化源内存并清零目标内存print_memory(start, size)
:显示内存内容以便验证
3. DMA 工作流程
- 初始化:
- 调用
dma_init()
重置 DMA 模块 - 用
init_memory()
初始化内存以便测试
- 调用
- 配置:
- 调用
dma_configure(src, dst, size)
设置传输参数 - 校验地址和大小,确保在合法范围内
- 调用
- 传输:
- 如需中断,先用
dma_set_interrupt(true)
使能 - 调用
dma_start()
执行突发传输(用memcpy
仿真) - 设置
done
标志,如使能中断则触发中断
- 如需中断,先用
- 完成检查:
- 轮询
dma_is_done()
或用dma_check_interrupt()
检查完成状态
- 轮询
- 结果验证:
- 用
print_memory()
检查结果 - 比较源数据和目标数据,验证传输正确性
- 用
4. 测试用例
提供的 main()
函数演示了完整的 DMA 传输流程:
- 初始化:初始化 DMA 和内存,源内存填充递增值(0, 1, 2, ...)
- 配置:配置从地址 0 到地址 100 的 16 字节传输
- 执行:使能中断,启动传输,等待完成
- 验证:打印目标内存内容,并检查数据是否与源一致
- 预期输出:
- 配置信息(源、目标、大小)
- 传输完成时的中断提示
- 显示已传输的数据内容(如 dest_memory[100]=0, dest_memory[101]=1, ...)
- 若数据一致则输出“传输成功”