当前位置: 首页 > news >正文

Linux DMA(一)

SPI OLED DMA 驱动程序 - 学习DMA概念
目录结构

spi_oled_dma/
├── spi_oled_dma.c # 主驱动程序
├── spi_oled_dma.h # 头文件
├── spi_oled_fb.c # Framebuffer接口
├── Makefile # 编译文件
├── dts/
│ └── spi_oled_overlay.dts # 设备树
└── test/
└── test_oled.c # 用户空间测试程序

  1. 头文件 (spi_oled_dma.h)
/** spi_oled_dma.h - SPI OLED DMA驱动头文件** 用于学习Linux内核DMA概念的教学驱动* 支持SSD1306 OLED显示屏 (128x64)*/#ifndef _SPI_OLED_DMA_H_
#define _SPI_OLED_DMA_H_#include <linux/types.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/gpio/consumer.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>/* ============================================================* 📚 DMA基础概念说明* ============================================================** DMA (Direct Memory Access) 直接内存访问:** 传统方式 (PIO - Programmed I/O):*   CPU从内存读数据 -> CPU写到SPI控制器 -> 重复...*   CPU全程参与,无法做其他事情** DMA方式:*   CPU告诉DMA控制器: "从这个地址开始,搬这么多数据到SPI控制器"*   DMA控制器自动搬运数据,CPU可以去做别的事情*   搬运完成后,DMA控制器通过中断通知CPU** 关键概念:*   1. DMA缓冲区  - 一块特殊分配的内存,CPU和DMA控制器都能访问*   2. 总线地址   - DMA控制器看到的内存地址(可能与CPU看到的不同)*   3. DMA通道    - DMA控制器中的一条数据通路*   4. DMA描述符  - 描述一次DMA传输的参数(源地址、目的地址、长度等)*   5. scatter-gather - 将多个不连续的内存块在一次DMA操作中传输** 内存地址类型:*   虚拟地址 (Virtual Address)  - CPU使用的地址*   物理地址 (Physical Address) - 实际内存芯片上的地址*   总线地址 (Bus/DMA Address)  - DMA控制器使用的地址*   在简单系统中,物理地址 == 总线地址*   在有IOMMU的系统中,它们可能不同** DMA映射类型:*   一致性映射 (Coherent/Consistent) - 硬件保证CPU和设备看到相同数据*   流式映射   (Streaming)           - 需要显式同步操作*//* SSD1306 OLED 显示参数 */
#define OLED_WIDTH              128
#define OLED_HEIGHT             64
#define OLED_PAGES              (OLED_HEIGHT / 8)  /* 8页,每页8行像素 */
#define OLED_BUF_SIZE           (OLED_WIDTH * OLED_PAGES)  /* 1024字节 *//* SSD1306 命令定义 */
#define SSD1306_SET_LOW_COLUMN          0x00
#define SSD1306_SET_HIGH_COLUMN         0x10
#define SSD1306_SET_MEMORY_MODE         0x20
#define SSD1306_SET_COL_ADDR            0x21
#define SSD1306_SET_PAGE_ADDR           0x22
#define SSD1306_SET_START_LINE          0x40
#define SSD1306_SET_CONTRAST            0x81
#define SSD1306_CHARGE_PUMP             0x8D
#define SSD1306_SET_SEG_REMAP           0xA1
#define SSD1306_DISPLAY_ALL_ON_RESUME   0xA4
#define SSD1306_SET_NORMAL_DISPLAY      0xA6
#define SSD1306_SET_INVERSE_DISPLAY     0xA7
#define SSD1306_SET_MUX_RATIO           0xA8
#define SSD1306_DISPLAY_OFF             0xAE
#define SSD1306_DISPLAY_ON              0xAF
#define SSD1306_SET_COM_SCAN_DEC        0xC8
#define SSD1306_SET_DISPLAY_OFFSET      0xD3
#define SSD1306_SET_DISPLAY_CLOCK_DIV   0xD5
#define SSD1306_SET_PRECHARGE           0xD9
#define SSD1306_SET_COM_PINS            0xDA
#define SSD1306_SET_VCOM_DETECT         0xDB/* DMA传输模式 */
enum oled_dma_mode {DMA_MODE_COHERENT,      /* 一致性DMA映射 */DMA_MODE_STREAMING,     /* 流式DMA映射 */DMA_MODE_SG,            /* Scatter-Gather DMA */DMA_MODE_PIO,           /* 无DMA,CPU直接传输(对比用) */
};/** ============================================================* 📚 DMA缓冲区结构说明* ============================================================** 一致性DMA缓冲区 (Coherent DMA Buffer):*   分配: dma_alloc_coherent()*   释放: dma_free_coherent()*   特点: *     - CPU和设备始终看到一致的数据(硬件保证cache一致性)*     - 不需要手动同步*     - 可能比较慢(通常映射为non-cacheable)*     - 适用于频繁被CPU和设备同时访问的缓冲区*   返回:*     - 虚拟地址(CPU使用)*     - DMA地址(设备使用)** 流式DMA映射 (Streaming DMA Mapping):*   映射: dma_map_single()*   取消: dma_unmap_single()*   同步: dma_sync_single_for_device() / dma_sync_single_for_cpu()*   特点:*     - 可以映射普通kmalloc内存*     - 需要指定方向: DMA_TO_DEVICE / DMA_FROM_DEVICE / DMA_BIDIRECTIONAL*     - 需要手动同步(确保cache一致性)*     - 性能通常更好(内存可以是cacheable的)*     - 适用于一次性或间歇性的DMA传输*//* DMA缓冲区信息 */
struct oled_dma_buf {void *vaddr;            /* CPU虚拟地址 */dma_addr_t dma_addr;    /* DMA总线地址 */size_t size;            /* 缓冲区大小 */bool is_coherent;       /* 是否为一致性映射 */
};/* Scatter-Gather表项 */
struct oled_sg_entry {struct scatterlist sg;  /* 内核sg结构 */void *vaddr;            /* 该段的虚拟地址 */size_t size;            /* 该段的大小 */
};/* * DMA统计信息 - 用于学习和调试 * 通过sysfs导出,方便观察DMA行为*/
struct oled_dma_stats {u64 dma_transfers;          /* DMA传输总次数 */u64 dma_bytes;              /* DMA传输总字节数 */u64 dma_errors;             /* DMA错误次数 */u64 pio_transfers;          /* PIO传输次数(对比) */u64 avg_transfer_time_ns;   /* 平均传输时间(纳秒) */ktime_t last_transfer_start;/* 上次传输开始时间 */ktime_t last_transfer_end;  /* 上次传输结束时间 */
};/* 主设备结构体 */
struct spi_oled_device {/* SPI相关 */struct spi_device *spi;struct device *dev;/* GPIO引脚 */struct gpio_desc *dc_gpio;      /* 数据/命令选择引脚: 0=命令, 1=数据 */struct gpio_desc *reset_gpio;   /* 复位引脚 *//* Framebuffer相关 */struct fb_info *fb_info;u8 *fb_vmem;                    /* framebuffer显存(CPU格式: 每像素1bit) */size_t fb_vmem_size;/* ====== DMA相关 ====== *//* * 一致性DMA缓冲区* 用于存放要发送到OLED的显示数据* CPU写入数据后,DMA控制器可以直接读取,无需额外同步*/struct oled_dma_buf coherent_buf;/** 流式DMA缓冲区* 普通内存 + DMA映射* 需要在传输前后进行同步操作*/struct oled_dma_buf streaming_buf;/** Scatter-Gather DMA* 将framebuffer分成多个不连续的块传输*/struct sg_table sg_table;int sg_nents;                   /* SG表项数量 *//** DMA通道* SPI控制器通常有自己的DMA通道* 这里我们也可以请求独立的DMA通道来学习*/struct dma_chan *dma_chan_tx;    /* 发送DMA通道 *//* 当前DMA模式 */enum oled_dma_mode dma_mode;/* DMA完成等待 */struct completion dma_completion;/* DMA统计 */struct oled_dma_stats stats;/* 同步保护 */struct mutex buf_lock;          /* 缓冲区访问锁 */spinlock_t stats_lock;          /* 统计信息锁 *//* 工作队列 - 用于延迟刷新 */struct delayed_work refresh_work;bool refresh_pending;/* 设备状态 */bool powered_on;u8 contrast;
};/* 函数声明 *//* 驱动核心函数 */
int oled_hw_init(struct spi_oled_device *oled);
void oled_hw_reset(struct spi_oled_device *oled);
int oled_write_cmd(struct spi_oled_device *oled, u8 cmd);
int oled_write_cmd_seq(struct spi_oled_device *oled, const u8 *cmds, size_t len);/* DMA相关函数 */
int oled_dma_init(struct spi_oled_device *oled);
void oled_dma_cleanup(struct spi_oled_device *oled);
int oled_dma_transfer_coherent(struct spi_oled_device *oled);
int oled_dma_transfer_streaming(struct spi_oled_device *oled);
int oled_dma_transfer_sg(struct spi_oled_device *oled);
int oled_pio_transfer(struct spi_oled_device *oled);/* 显示刷新 */
int oled_refresh_display(struct spi_oled_device *oled);
void oled_convert_fb_to_oled(struct spi_oled_device *oled, u8 *dst);/* Framebuffer接口 */
int oled_fb_register(struct spi_oled_device *oled);
void oled_fb_unregister(struct spi_oled_device *oled);#endif /* _SPI_OLED_DMA_H_ */
http://www.jsqmd.com/news/414658/

相关文章:

  • 2026年工业溶气气浮机厂家推荐:平流/浅层/沉淀式气浮设备供应商精选 - 品牌推荐官
  • 2026工业压力变送器优质厂家推荐指南:智能电磁流量计、检测密度计、水位液位计、浮子流量计、涡轮流量计选择指南 - 优质品牌商家
  • 2026靠谱水果店加盟品牌推荐 - 优质品牌商家
  • OpenClaw 在严肃场景下的实践:迁移 Ingress NGINX
  • 2026北京留学中介排名:主流机构服务实力对比分析 - 品牌排行榜
  • 天猫超市卡回收靠谱平台推荐 - 京顺回收
  • 2026年酒店易耗品价格排名,邦亿客宾馆易耗品价格合理吗 - 工业品网
  • 2026上海留学中介排名及服务能力深度解析 - 品牌排行榜
  • 双头车床、管螺纹车床、龙门铣床采购全指南:行业趋势 + 三大平台对比 + 精准选购建议 - 品牌推荐大师1
  • 西南靠谱水果店加盟品牌推荐:一站式水果加盟扶持、中高端水果店加盟、低风险水果实体店加盟、全国水果加盟品牌哪家好选择指南 - 优质品牌商家
  • 北京海关纳税争议解决律师怎么选,口碑好的在这里 - 工业推荐榜
  • 探讨质成新材料的产品效果,各地区靠谱企业怎么收费 - mypinpai
  • 解读2026年驻马店定制衣柜优质生产商,哪家排名靠前 - 工业设备
  • 2026年比较可靠的留学机构有哪些?这份详细指南请收好 - 品牌排行榜
  • 2026年校园招聘岗位哪个性价比高,为你揭晓答案 - 工业品牌热点
  • 2026最新PVC发泡板十一大厂家实力排行榜:聚焦全屋健康,基于环保性能与市场口碑的权威推荐榜单 - 十大品牌榜
  • 2026年天津家庭全屋墙面用乳胶漆刷新品牌推荐,哪家性价比高 - 工业品牌热点
  • 金三银四春招来袭,三天吃透Java面试八股文(2026最新整理)
  • 细聊2026年廊坊靠谱的整屋全包专业公司,哪家性价比高 - myqiye
  • 2026深圳留学机构推荐:聚焦服务与资源的选择指南 - 品牌排行榜
  • 2026老年智慧食堂厂家推荐榜,带你了解优质厂商,可视验收秤/智慧食堂系统/食堂称重系统,智慧食堂工厂联系方式 - 品牌推荐师
  • Hugging Face 直跑3万+ LoRA模型的技术实现
  • 盘点昆明假期补课服务,滇云教育性价比高,推荐假期初中补课班 - myqiye
  • 2026年九江可靠的兵器类国防特色专业学校,分享优质院校 - 工业推荐榜
  • Prompt Engineering(提示词工程)
  • 2026年汽车零部件清洗机公司权威推荐:通过式超声波清洗机、高压清洗机、高压清洗机、五金模具清洗机选择指南 - 优质品牌商家
  • 基于国密GB35114协议的国标GB28181平台EasyGBS视频联网安全能力解析
  • SNH48新企划《11》,一场关于初心的价值回归 - 博客万
  • Spring AI + JManus 从入门到实战:构建企业级AI应用
  • 2026年碳氢超声波清洗机厂家最新推荐:汽车零部件清洗机/通过式清洗机/通过式清洗机/通过式超声波清洗机/选择指南 - 优质品牌商家