DMA驱动开发---认识DMA

这篇具有很好参考价值的文章主要介绍了DMA驱动开发---认识DMA。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

DMA定义:

DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

DMA传输方式:

DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:

  • 外设到内存
  • 内存到外设
  • 内存到内存
  • 外设到外设

DMA驱动开发---认识DMA

DW_AHB_DMA 主要组件:

  1. DMA Hardware Requeset Interface
  2. AHB Master Interface
  3. AHB Slave Interface
  4. Arbiter(仲裁器)
  5. DMA Channel x
  6. FIFO of DMA Channel x
  7. DMA Controller

DMA驱动开发---认识DMA

DMA的应用

DMA主适用于一些高速、大数据量的I/O设备。对于这类高速I/O设备,如果采用输入输出指令或采用中断的方式传输信息,会占用大量CPU的时间,同时也容易造成数据的丢失。DMA方式能使I/O设备直接和存储器进行成批数据的快速传输。
常见应用如下:

  • FLASH
  • AUDIO
  • VIDEO

DMA相关的基本定义

Source/Destination Peripheral

Source Peripheral :DMAC从AHB层读取数据的设备,然后DMAC将数据存储在通道FIFO中。

Source Peripheral 可以挂在AHB总线或者APB总线上。

Destination Peripheral DMAC 从FIFO中存储的数据的设备(从Source Peripheral读取的)

Destination Peripheral 可以挂在AHB总线或者APB总线上。

Memory/Non-Memory Peripheral

始终“准备就绪”进行DMA传输的Source Peripheral 或者 Destination Peripheral,不需要握手接口即可与DMA进行交互,仅当外围设备插入的等待状态不超过16个时,才应当将其定义为Memory Peripheral。反之,则应当定义为Non-Memory Peripheral并使用握手接口,以便在其准备好接收/发送数据时发出信号通知DMA。

Master/Slave Interface

Master Interface:DW_ahb_dmac是通过AHB总线上的一个master interface,DW_ahb_dmac通过AHB总线从source读取数据并写入数据到destination
Slave Interface:CPU编程DW_ahb_dmac的AHB接口。主要是作为从设备接口部分,供AHB总线上的其他主设备来访问自身控制寄存器。

Handshaking Interface

一组信号或软件寄存器,他们符合DMAC与Source Peripheral/Destination Peripheral之间的握手协议,以控制他们之间的Single/Brust 事务。

该接口用于请求、确认、控制DMAC事务。一般情况DMAC可以通过以下3种方式接收请求:

  • **Hardware handshaking interface: **使用硬件信号来控制DMAC与源或目标外围设备之间的Single/Brust 事务。
  • Software handshaking interface: 使用一组软件寄存器DMAC与源或目标外围设备之间的Single/Brust 事务。
  • Peripheral interrupt handshaking interface: 在这种模式下,来自外围设备的中断线被绑定到硬件握手接口的dma_req输入;忽略其他接口信号。
Channel

通道-在一个配置的AHB层上的源外设和同一或不同AHB层上的目标外设之间通过通道FIFO进行的读/写数据路径

如果源外设不是内存,则将源握手接口分配给通道。

如果目标外设不是内存,则将目标握手接口分配给通道。源和目标握手接口可以通过编程通道寄存器来动态分配

通道可以理解成一个小型的程序,它是一个简单的IO控制程序,只能处理对应外设,mem之间的数据传输,工作之前需要配置DMA里使用哪个通道。

每一个通道都有一个相应的FIFO,该FIFO的缓冲长度可以编程配置

DMA 传输层级结构

Non-Memory Peripherals

DMA驱动开发---认识DMA

Memory

DMA驱动开发---认识DMA

解释:
Block:

Block- DW_ahb_dmac数据的块,其数量即块长,由流量控制器决定。对于DW_ahb_dmac和内存之间的传输,块被直接分解为突发序列和单个传输。对于DW_ahb_dmac和非内存外设之间的传输,块被分解为DW_ahb_dmac事务序列(单个事务和突发事务)。这些又被分解成AHB传输序列。

Transaction:

事务——DW_ahb_dmac传输的基本单元,由硬件或软件握手接口决定

如果外设是非内存设备,则事务仅与DW_ahb_dmac与源外设或目标外设之间的传输相关。

有两种类型的事务:

  • 单个事务—单个事务的长度始终为1,并转换为单个AHB转移。
  • 突发事务—突发事务的长度被编程到DW_ahb_dmac中。突发事务被转换为一系列突发和AHB单传输。突发事务长度由程序控制,通常与dmac和源/目的外设中的FIFO大小有一定关系。
DMA传输方式

软件控制DMAC传输的块数量,在DMA传输完成时DW_AHB_DMAC硬件关闭通道并产生一个DMA传输完毕的中断信号,程序员可以为一个新的DMA传输重编程通道

根据传输块的分布情况可以分为两种

第一种:Single-block DMA transfer

一片连续的组合块

第二种:Multi-block DMA transfer

Multi-block DMA transfer可能由多个DW_ahb_dmac块组成。对多块DMA传输通过以下方式支持:

  • 块链(链表指针) linked list pointer
  • 自动重新加载通道寄存器
  • 连续块

源和目标可以独立地选择要使用的方法。

Scatter-gather DMA方式是与block DMA方式相对应的一种DMA方式。

在DMA传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但是在某些计算机体系中,如IA架构,连续的存储器地址在物理上不一定是连续的,所以DMA传输要分成多次完成。

如果在传输完一块物理上连续的数据后引起一次中断,然后再由主机进行下一块物理上连续的数据传输,那么这种方式就是block DMA方式。

Scatter-gather DMA方式则不同,它使用一个链表描述物理上不连续的存储空间(这个链表),然后把链表首地址告诉DMA master。DMA master在传输完一块物理连续的数据后,不用发起中断,而是根据链表来传输下一块物理上连续的数据,直到传输完毕后再发起一次中断。

感觉对于物理地址不连续离散的使用block dma和sg dma是不一样,sg dma效率更高,可是如果是连续的物理地址,感觉block dma和sg dma效率其实是差不多的。

S-G传输的两种场景:

将一片连续内存数据搬运到一片不连续的的内存空间(且间隔是相等的)

源内存:连续

目的内存:离散

这个时候就可以用到DMA的 Destination Scatter Transfer

DMA驱动开发---认识DMA

场景2:

将一片内存区域中等间隔的多段数据拷贝到一段连续内存中(常见的2D矩形抠图就是这种场景的典型应用)

源内存:分散

目的内存:聚合

这种场景就可以使用DMA的Source Gather transfer

DMA驱动开发---认识DMA


DMA传输相关设置

传输类型
  • Memory to Memory
  • Memory to Peripheral
  • Peripheral to Memory
  • Peripheral to Peripheral
传输控制器 — 流控制器 Flow Controller

流控制器:确定DMA块传输长度并终止它的设备

流控制器一般有以下选择:

  • DMAC
  • Source Peripheral
  • Destination Peripheral

流控制器设置规则:

如果在传输之前知道块大小,则应将DMAC设为流控。
如果在传输之前不知道块大小,则Source Peripheral/Destination Peripheral必须为流控

传输宽度

多数通用DMA是挂在AHB总线上的,即AHB总线的位宽则为DMA的传输宽度的上限。

以STM32单片机为例,DMA的传输宽度 <= 32。则常见有Byte-8bit、HalfWord-16bit、Word-32bit方式

DT53的AHB总线位宽为CPU位宽,同STM32一样都是32位,可以使用Byte-8bit、HalfWord-16bit、Word-32bit方式

传输时地址变化方式

DMA传输过程是没有CPU干预的,DMAC怎么知道下一个数据去哪里搬运,也需要事先告诉DMA地址的变化规律。

即三种地址方式:

  1. 递增
  2. 递减
  3. 不变
通道优先级

对于独立的通用DMA,都会有多条通道,每条通道有独立的FIFO、配置寄存器、中断等硬件。但是DMA的Master Interface仅有一个,故多个通道会竞争该Master Interface资源。

值得注意的是,高优先级通道可以随时提出对主总线接口的请求,但是仅在当前AHB传输(突发或单次)完成后才被授予。因此,当Master Interface正在为底优先级通道传输数据而高优先级通道请求服务时,它会在切换为高优先级通道传输数据之前完成低优先级通道当前的AHB传输(Single 或 Brust)。另外,当优先级相同时(寄存器配置),DMAC一般会根据通道号来选择优先运行的通道。

FIFO的作用

启用DMA的FIFO可以最大程度地避免数据传输过程中的溢出问题,可以减少DMA对内存的访问次数从而减少总线访问竞争

每个DMA stream都有4个字的FIFO可用。它用来暂存来自DMA源端的数据,每当FIFO里存放的数据达到设定的阈值后,数据就会被移走。阈值可以设置为从1个字到4个字的深度

突发模式和循环挪用模式

DMA和CPU共用总线,所以这两种模式是为解决如何共用总线提供了三种方法。

官方文档:

突发模式[编辑]以一个连续的顺序传输整个数据块。
一旦CPU授予DMA控制器对系统总线的访问权限,它就会在释放对系统总线的控制返回给CPU之前传输数据块中的所有字节数据,但会使CPU在相对较长的时间内处于非活动状态。该模式也称为“块传输模式”。它还用于停止不必要的数据。

周期挪用模式[编辑]周期挪用模式用于在突发传输模式所需的时间长度内不应禁用CPU的系统。在周期窃取模式中,DMA控制器以与突发模式相同的方式获得对系统总线的访问,使用BR(总线请求)和BG(总线授权)信号,这两个信号控制CPU和DMA控制器之间的接口。然而,在周期挪用模式下,在一个字节的数据传输之后,系统总线的控制通过BG解除对CPU的断言。然后通过BR不断地再次请求,每次请求传输一个字节的数据,直到整个数据块传输完毕。
通过不断地获得和释放对系统总线的控制,DMA控制器实质上交替的完成了指令和数据传输。CPU处理一条指令,然后DMA控制器传输一个数据值,以此类推。一方面,数据块在周期窃取模式中的传输速度不如在突发模式中那么快,但另一方面,CPU的空闲时间并不像突发模式中的那样长。
周期窃取模式对于实时监控数据的控制器很有用。

突发模式:

当DMA申请总线成功后会连续传送数据,而不给cpu使用总线的机会,直到数据传送完毕。比如stm32设置了4个节拍的突发传输,传输宽度位8位,则一个dma请求会连续传送4个字节,是单次传输的4倍。

Burst操作还是要通过CPU的参与的,与单独的一次读写操作相比,burst只需要提供一个其实地址就行了,以后的地址依次加1,而非burst操作每次都要给出地址,以及需要中间的一些应答、等待状态等等。如果是对地址连续的读取,burst效率高得多,但如果地址是跳跃的,则无法采用burst操作

dma实际上是一次一次的申请总线,把要传的数据总量分成一个一个小的数据块。比如要传64个字节,那么dma内部可能分为2次,一次传64/2=32个字节,这个2(a)次呢,就叫做burst。这个burst是可以设置的。这32个字节又可以分为32位 8或者16位16来传输。

循环挪用模式:

这应该是常用的dma模式,就是一个dma请求就申请一次总线,传输1个字节。

什么时候使用突发模式:

在封装/解封数据的过程中,如果在数据完全封装/解封前中断操作,则有数据损坏的危险。因此,为了确保数据一致性,可将数据流配置成生成突发传输:在这种情况下,属于一个突发的每组传输不可分割。

为什么封装/解封会有数据损坏的危险呢?举个例子,有个外设的一组寄存器是实时联动的,也就是随着时间的变化,这组寄存器会改变。如果我们第一时刻取前一半寄存器的值,后一时刻区取后后一半寄存器的值,将两者封装起来就会损坏数据。所以我们需要在同一时刻取出所有值,这时就要用到突发传输,因为在传输时只有dma用总线,寄存器不会改变。

DMA Relative Register

DW_AHB_DMAC的所有寄存器都是64位对齐的,都是64位宽,但是一般高32位都是Reserved。

DT53一共有两个DMAC,两条物理通道,每个DMAC有3条Channel。

通道配置寄存器

DMA驱动开发---认识DMA

AHB有4个独立的AHB主接口,4个接口可以同时传输,可以在不同的AHB上存在主接口(multi-layer)。

源与目的可以在不同的AHB总线上

每个AHB主接口可配置的数据总线宽度(256bits)

通过APBbridge与APB外设通信

DMA编程示例

DMA传输可以由单个或多个块传输组成。

在多块传输的连续块上,DW_ahb_dmac中的SARx/DARx寄存器使用以下方法之一重新编程:

  1. 使用链表的块链
  2. Auto-reloading
  3. 块之间的连续地址

在多块传输的连续块上,DW_ahb_dmac中的CTLx寄存器使用以下方法之一重新编程:

  1. 使用链表的块链
  2. Auto-reloading

当块链时,使用链表是多块方法的选择。在连续的块上,DW_ahb_dmac中的LLPx寄存器使用带链表的块链重新编程。

块描述符由六个寄存器组成:SARx、DARx、LLPx、CTLx、SSTATx和DSTATx。DW_ahb_dmac使用前四个寄存器和CFGx寄存器来设置和描述块传输。

DMA驱动开发---认识DMA

配置一个通道
  1. 读取ChEnReg通道使能寄存器,寻找当前空闲的通道,然后按照优先级使能该通道

  2. 通过向interrupt clear registers寄存器组中写值去清除上次DMA传输的通道中任何pending interrupts。读取中断原始状态和中断状态寄存器确认所有中断都已清除。

  3. 编程以下通道寄存器

    ​ a. 设置源起始地址,即配置SARx寄存器,x为通道号

    ​ b. 设置目的起始地址,即配置DARx寄存器,x为通道号

    ​ c. 配置CTLx=0和CFGx=1,配置LLPx寄存器为0(DMA传输时不使用链表)(根据表格的ROW1)

    ​ d. 配置CTLx寄存器以为DMA传输设置控制信息

    ​ i. 设置传输类型(memroy or non-memory peripheral for source and destination) 和流控制器设备

    ​ 通过编程CTLx寄存器的TT_FC域

    ​ 参考下表

DMA驱动开发---认识DMA

​ ii. 设置传输特性,例如:

​ ★配置源端传输宽度,SRC_TR_WIDTH域

​ ★配置目的端传输宽度,DST_TR_WIDTH域

DMA驱动开发---认识DMA

​ ★配置source master layer, SMS域

​ ★配置destination master layer,DMS域

​ ★配置源递增/递减/固定地址,SINC域

​ ★配置目的递增/递减/固定地址,DINC域

​ iii. 写入通道配置信息到CFGx寄存器

​ 为源和目标外设指定握手接口类型(硬件或软件);这不是内存所必需的。这一步需要分别编程HS_SEL_SRC/HS_SEL_DST位。

​ 写入0可以激活硬件握手接口来处理源/目标请求。写入1激活软件握手接口来处理源和目标请求。

​ 如果源或目标外设的硬件握手接口被激活,向源和目标外设提供握手接口:这是必需的分别编程SRC_PER和DEST_PER位。

​ f. 如果启用了gather(参数DMAH_CHx_SRC_GAT_EN = True, CTLx. log = True)。SRC_GATHER_EN已启用),为通道x编程SGRx寄存器。

​ g. 如果启用了scatter(参数DMAH CHx DST SCA EN-True和CTLx.DST_SCATTER_EN),则为通道x编程DSRxregister。

  1. 在写入ChEnReg之前,确保DmaCfgReg寄存器的第0位是启用的。
  2. 源和目标请求单个和突发DMA事务,以便传输数据块(假设是非内存外设)。DW_ahb_dmac在块中的每个事务(突发事务和单个事务)完成时确认,并执行块传输。
  3. 传输完成后,硬件设置中断并禁用通道。此时,您可以响应Block Complete或Transfer Complete中断,或者轮询传输完成原始中断状态寄存器(RawTfr[n], n=通道号),直到硬件设置完毕,以便检测传输何时完成。注意,如果使用这种轮询,软件必须确保在通道启用之前,通过写入中断清除寄存器ClearTfr[n]来清除传输完成中断。

需要与设计沟通的地方

每个芯片的DMA设计都不同,如SRC_Master 与DST_Master的选择。它们在代码中以idx的形式给出,设计对每个芯片选择的Master是不同的idx。(0/1/2/3)

handshaking也需要沟通,handshaking是一组信号或软件寄存器,他们符合DMAC与Source Peripheral/Destination Peripheral之间的握手协议,以控制他们之间的Single/Brust 事务。每款芯片的设计,其各个外设,不管是源外设还是目的外设,他们要控制DMAC都可以看作与DMAC的一个连线,这个连线是需要标记出来并设置到寄存器中的,每款芯片的设计不同,这些连线的标记也会不同。比如I2C要作为Source Peripheral去请求DMA,它和DMAC的连线可能在DT56中标记为1,在DT53中的标记为2。开发不同的芯片的DMAC驱动时一定要注意修改这一点。

裸板代码分析

在这里主要分析裸板代码的内存到内存的single传输:

代码4-1

void dw_ahb_dma_mem2mem_single(u32 idx, u32 ch, u32 cfg)
{
	struct xfer_buff *tr_buff = NULL;

	tr_buff = dw_ahb_dmac_get_buff(idx);

	/* 1.init src buffer once */
	if ((idx == 0) && (cfg == DW_AHB_CHANNEL_CFG_0)) {
		for (u32 i = 0; i < tr_buff->buff_size; i++) {
			tr_buff->src[i] = i & 0xff;
		}
	}

	/* 2.clear dest buffer */
	memset(tr_buff->dst, 0x0, tr_buff->buff_size);
	if (dma_cache_on) {
		cleanDCache();
	}

	/* 3.config dmac and start transferring */
	qw_cpu_dmac_cfg_t *cpu_dmac_cfg = &cpu_dmac_config[idx][ch];

	dw_ahb_dmac_handle_t dmac_handle;
	dw_ahb_dmac_priv_t *dmac_priv;

	dmac_handle = dw_ahb_dmac_initialize(idx, dw_ahb_dmac_test_callback);
	dmac_priv = (dw_ahb_dmac_priv_t *)dmac_handle;
	qw_cpu_dmac_reg_t *cpu_dmac = (qw_cpu_dmac_reg_t *)(uintptr_t)(dmac_priv->base);


	cpu_dmac_cfg->src_addr = (u32)tr_buff->src + ADDR_OFFSET;
	cpu_dmac_cfg->dst_addr = (u32)tr_buff->dst + ADDR_OFFSET;

	cpu_dmac_cfg->src_master = 1;//idx;
	cpu_dmac_cfg->dst_master = 1;//idx;
	if (cfg == DW_AHB_CHANNEL_CFG_0) {
		cpu_dmac_cfg->src_bus_width = CPU_DMAC_TR_WIDTH_8BITS;
		cpu_dmac_cfg->dst_bus_width = CPU_DMAC_TR_WIDTH_8BITS;
		cpu_dmac_cfg->src_burst_size = CPU_DMAC_MSIZE_8;
		cpu_dmac_cfg->dst_burst_size = CPU_DMAC_MSIZE_8;
		cpu_dmac_cfg->block_size = BLOCK_SIZE0;
	} else if (cfg == DW_AHB_CHANNEL_CFG_1) {
		cpu_dmac_cfg->src_bus_width = CPU_DMAC_TR_WIDTH_32BITS;
		cpu_dmac_cfg->dst_bus_width = CPU_DMAC_TR_WIDTH_32BITS;
		cpu_dmac_cfg->src_burst_size = CPU_DMAC_MSIZE_4;
		cpu_dmac_cfg->dst_burst_size = CPU_DMAC_MSIZE_4;
		cpu_dmac_cfg->block_size = BLOCK_SIZE1;
	} else if (cfg == DW_AHB_CHANNEL_CFG_2) {
		cpu_dmac_cfg->src_bus_width = CPU_DMAC_TR_WIDTH_32BITS;
		cpu_dmac_cfg->dst_bus_width = CPU_DMAC_TR_WIDTH_16BITS;
		cpu_dmac_cfg->src_burst_size = CPU_DMAC_MSIZE_4;
		cpu_dmac_cfg->dst_burst_size = CPU_DMAC_MSIZE_8;
		cpu_dmac_cfg->block_size = BLOCK_SIZE2;
	}
	dw_ahb_dmac_init_chn_stat(idx, ch,
		COUNT_MAX >> cpu_dmac_cfg->src_bus_width,
		cpu_dmac_cfg->block_size);

	cpu_dmac_cfg->src_addr_change_mode = CPU_DMAC_ADDR_INCREASE;
	cpu_dmac_cfg->dst_addr_change_mode = CPU_DMAC_ADDR_INCREASE;
	cpu_dmac_cfg->trans_mode = CPU_DMAC_CTL_TT_MEM2MEM;
	cpu_dmac_cfg->fifo_mode = CPU_DMAC_FIFO_MODE_EN;

	drv_cpu_dmac_init(cpu_dmac, ch, cpu_dmac_cfg);
	//pr_info("---dma%d DMAC_CTL0 0x%llx\r\n", idx, cpu_dmac->DMAC_CTL0);

	DMA_DBG("---src_bus_width 0x%x, src_burst_size 0x%x\r\n",
			cpu_dmac_cfg->src_bus_width, cpu_dmac_cfg->src_burst_size);
	DMA_DBG("---src_master 0x%x, dst_master 0x%x\r\n",
			cpu_dmac_cfg->src_master, cpu_dmac_cfg->dst_master);

	/* enable selected channel's interrupt */
	drv_cpu_dmac_enable_intr(cpu_dmac, ch, CPU_DMAC_BLOCK_INTR, CPU_DMAC_INTR_EN);

	/* enable dmac */
	drv_cpu_dmac_enable_dmac(cpu_dmac, CPU_DMAC_EN);

	/* enable selected channel */
	drv_cpu_dmac_enable_ch(cpu_dmac, ch, CPU_DMAC_CH_EN);
    
    
}

dw_ahb_dmac_initialize初始化一个DMAC控制器dw_ahb_dmac_priv_t* dmac_priv后,就要利用dmac_priv->base开始配置qw_cpu_dmac_reg_t *cpu_dmac

cpu_dmac是寄存器组,前面定义的qw_cpu_dmac_cfg_t *cpu_dmac_cfg是damc配置信息的对象。

DMA驱动的一大任务就是封装对dmac配置信息设置寄存器的函数。这些函数从dmac配置信息来具体地设置DMA寄存器。

drv_cpu_dmac_init就是做这件事:

代码4-2:

s32 drv_cpu_dmac_init(qw_cpu_dmac_reg_t *addr, u32 channel, qw_cpu_dmac_cfg_t *cpu_dmac_cfg)
{
	if (channel > CPU_DMAC_MAX_CHANNEL)
		return ERROR;

	/* set selected channel's source address */
	drv_cpu_dmac_set_src_addr(addr, channel, cpu_dmac_cfg->src_addr);
	/* set selected channel's destination address */
	drv_cpu_dmac_set_dst_addr(addr, channel, cpu_dmac_cfg->dst_addr);

	/* set selected channel's block size */
	drv_cpu_dmac_set_block_size(addr, channel, cpu_dmac_cfg->block_size);

	drv_cpu_dmac_set_src_master(addr, channel, cpu_dmac_cfg->src_master);
	drv_cpu_dmac_set_dst_master(addr, channel, cpu_dmac_cfg->dst_master);

	/* set selected channel's transfer mode */
	drv_cpu_dmac_set_trans_mode(addr, channel, cpu_dmac_cfg->trans_mode);

	/* set selected channel's source transfer bus width */
	drv_cpu_dmac_set_src_trans_bus_width(addr, channel, cpu_dmac_cfg->src_bus_width);
	/* set selected channel's destination transfer bus width */
	drv_cpu_dmac_set_dst_trans_bus_width(addr, channel, cpu_dmac_cfg->dst_bus_width);

	/* set selected channel's source burst transfer size */
	drv_cpu_dmac_set_src_burst_size(addr, channel, cpu_dmac_cfg->src_burst_size);
	/* set selected channel's destination burst transfer size */
	drv_cpu_dmac_set_dst_burst_size(addr, channel, cpu_dmac_cfg->dst_burst_size);

	/* set selected channel's source address change mode */
	drv_cpu_dmac_set_src_addr_change_mode(addr, channel, cpu_dmac_cfg->src_addr_change_mode);
	/* set selected channel's destination address change mode */
	drv_cpu_dmac_set_dst_addr_change_mode(addr, channel, cpu_dmac_cfg->dst_addr_change_mode);

	/* set selected channel's source handshake mode, soft or hard */
	drv_cpu_dmac_set_src_hs_mode(addr, channel, cpu_dmac_cfg->src_hs_mode);
	/* set selected channel's destination handshake mode, soft or hard */
	drv_cpu_dmac_set_dst_hs_mode(addr, channel, cpu_dmac_cfg->dst_hs_mode);

	/* set selected channel's source hard handshake number */
	drv_cpu_dmac_set_src_hs_num(addr, channel, cpu_dmac_cfg->src_hs_num);
	/* set selected channel's destination hard handshake number */
	drv_cpu_dmac_set_dst_hs_num(addr, channel, cpu_dmac_cfg->dst_hs_num);

	/* set selected channel's block size */
	drv_cpu_dmac_set_fifo_mode(addr, channel, cpu_dmac_cfg->fifo_mode);

	return OK;
}

先不具体看这些设置寄存器的函数,而继续回到代码4-1的dw_ahb_dma_mem2mem_single看它是如何配置cpu_dmac_cfg配置信息对象的。

首先是31-32行 配置源地址与目的地址,其中tr_buff对象是xfer_buff类型,保存了一次传输的基本信息,它由dw_ahb_dmac_get_buff(idx)函数配置,为传输准备源/目的内存空间。

struct xfer_buff {
  u8 *src;
  u8 *dst;
  u32 buff_size;
} g_xfer_buff[DW_AHB_DMAC_MAX];

34-46行 是根据AHB传输通道的不同选择来配置以下两点

  1. SRC_Master 与DST_Master,这里需要与芯片设计沟通,看mem2mem的传输应该具体如何选择。

  2. 设置传输特性,源端/目的端的传输宽度以及burst大小,以及每次传输的block大小

73行 需要使能被选择通道的中断,这里选择的是Block Complete中断

76和79行 前后使能DMAC和Channel

关于AHB传输通道的传输特性,各个参数之间的关系还需要进一步理解:

数据宽度设置为8bit,也就是transfer size=8bits = 1byte(SRC_TR_WIDTH)

一次传8个transfer size,即burst size=8 (SRC_MSIZE = Source Burst Transaction Length:Number of data items each of width CTLx.SRC_TR_WIDTH to be read from the source every time a burst transaferred request is made from either the corresponding hardware or software handshaking interface)

传输块大小设置为4092,也就是Block transfer size=4092(the total number of single transaction to perform for every block transfer

意味着一次传输,实际DMA 将会从源端传输4092个8bit也就是4092bytes

一次burst请求能传送 8bit*8 = 8个字节

4092字节在burst事务会分为4092字节/8字节=511…4 即进行512次burst事务传完一个block

基于FIFO的burst模式的DMA传输时,BURST的大小乘以数据宽度不得超过设置的FIFO阈值大小,否则会出错。

总的来说dw_ahb_dma_mem2mem_single的配置过程和前面DMA编程示例中给出的“配置一个通道”中的示例类似。文章来源地址https://www.toymoban.com/news/detail-420240.html

到了这里,关于DMA驱动开发---认识DMA的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 012 - STM32学习笔记 - DMA_存储器到存储器

    011 - STM32学习笔记 - DMA直接存储器 1、DMA简介 我们知道stm32在实际应用过程中具有很强大的功能,包含数据的采集、处理、逻辑功能的运算等,因此stm32一直在处理大量的事务,但是在实际使用过程中,我们知道有些事情实际上不需要CPU过多参与,比如:数据的复制和存储,当

    2024年02月08日
    浏览(49)
  • STM32外设芯片驱动学习记录 —— (一) BH1750光照传感器驱动开发

    一、芯片介绍 二、Datasheet解读 1.硬件说明 2.寄存器说明 3.通信过程 三、驱动代码编写 1.软件I2C驱动 2. BH1750芯片驱动函数 总结             BH1750是16位数字输出型,环境光强度传感器集成电路,使用I2C接口通信,工作电压:VCC(2.4~3.6V),I2C电平(1.65~VCC),用于各类消费类LCD屏

    2024年02月02日
    浏览(87)
  • STM32外设芯片驱动学习记录 —— (二) PCA9555 IO扩展芯片驱动开发

    一、芯片介绍 二、Datasheet解读 1.硬件说明 2.寄存器说明 3.通信过程 三、驱动代码编写 1.软件I2C驱动 2. PCA9555芯片驱动函数 总结         PCA9555可设置16路输入或输出口,I2C接口,用于IO扩展,3个硬件地址引脚寻址,工作电压:VCC(2.3V 至 5.5V)。 1)框图   INT:中断输出 A0,

    2024年02月11日
    浏览(52)
  • lv15 input子系统框架、外设驱动开发 5

     在我们日常的Linux系统中,存在大量的输入设备,例如按键、鼠标、键盘、触摸屏、摇杆等,他们本身就是字符设备,linux内核将这些字符设备的共同性抽象出来,简化驱动开发建立了一个input子系统。 Linux内核为了两个目的: 简化纯输入类外设 (如:键盘、鼠标、游戏杆

    2024年02月19日
    浏览(42)
  • 驱动开发中的DMA是什么

            DMA是一种无须CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制。 使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来, 从而大大提高系统的吞吐率。 DMA通常与硬件体系结构, 特别是外设的总线技术密切相关。         DMA方式的数据传

    2024年04月17日
    浏览(28)
  • STM32之外设DMA

    DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLAS

    2024年02月09日
    浏览(37)
  • zynq 使用AXI_dma 传输==pl到ps,linux驱动开发,应用层处理DMA数据

    在使用zynq输出处理时,会使用到pl和ps的数据传输,可供使用的方案有多种,由于我们的数据量较大打算,因此使用用以下两种方案处理: 1.使用pl直接写ddr3, 2.使用dma, 本次详细介绍使用axi_dma如何将pl的数据在linux应用层接收数据并处理,以及遇到的问题 fpga工程,我们使用

    2024年02月03日
    浏览(57)
  • Linux 驱动开发基础知识——认识LED驱动程序 (二)

     个人名片: 🦁作者简介:一名喜欢分享和记录学习的在校大学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755@qq.com 🦉个人WeChat:Vir2021GKBS 🐼 本文由妄北y原创,首发CSDN 🎊🎊🎊 🐨座右铭:大多数人想要改造这个世界,但却罕有人想改造自己。 专栏导

    2024年01月21日
    浏览(40)
  • 开源虚拟现实引擎。CARLA是一个基于Unreal Engine 4开发的游戏引擎,支持模拟各种汽车、摩托车、轨道交通信号等车辆的行驶行为,并且提供了一套API接口,供开发者进行调用,用来进行地图构建

    作者:禅与计算机程序设计艺术 目前,市面上存在多个智能汽车解决方案,包括滴滴、理想汽车、百度无人驾驶等,均由大型科技公司或各个公司开发,其中一些公司如理想汽车、百度、字节跳动等已经取得了较为丰硕的成果。随着技术的发展,人们越来越关注智能汽车在未

    2024年02月08日
    浏览(58)
  • STM32外设天造地设的一对:ADC和DMA

    引言:这篇文章主要介绍ADC和DMA配置的注意事项,适合懂得如何配置最基本的ADC和DMA,但是对它们两个的模式不是太理解的朋友们看,本文将重点介绍ADC和DMA模式的注意事项 DMA是CPU的小助手,负责完成数据转运的任务,一般的数据转运可以在主函数完成,但是如果数据量巨大

    2024年02月14日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包