硬件环境:AC7801x 通用开发板 ATC-LINK
软件环境:keil 5.23
前几周我们使用了uart和adc模块,但实现上比较简陋,今天打算把dma用起来,实现一个uart dma的打印输出以及adc 多通道采样数据通过dma搬运的功能。
一、DMA模块注意事项
DMA模块可以支持memsize 8位,16位,32位,在选择的时候需要注意一点,那就是传输地址需要和选择的size对齐,如果选择32位的,则需要四字节对齐。
二、可编程数据宽度
我从参考手册DMA章节中摘录了一部分可编程数据宽度表格,用于解释下列配置的意义,方便大家理解我们芯片DMA的实现。
源宽度指的就是memory的访问size,是8位,16位还是32位。目标宽度指的就是外设寄存器的访问size。传输方向TX就是memory到外设。RX就是外设到memory。
注;这里提一嘴,DMA传输当然不只是memory到外设和外设到memory,对于外设到外设,只需要把其中一个外设寄存器当作memory也是一样。而memory到memory则是纯软件触发。
Byte模式指的是源,也就是memory的传输模式,即分几次传输,如第一条为0,则32位memory就分一次传输。而外设size只有8位,所以最后只有低八位的memory数据写入了外设,高24位都被丢弃了。
而这个传输数目,则可以理解为传输次数,像第一条Byte模式为0,那么一个32位memory就传输一次。如果Byte模式为1,则一个32位memory分两次传输,那么传输数据为4其实指使用了2个32位memory。
源端宽度
(MSIZE) | 目标宽度
(PSIZE) | 传输数目
(Length) | Byte
模式 | 源:地址/数据
(MSIZE) | 传输方向
(Dir) | 传输操作 | 目标: 地址/数据
(PSIZE) | 32 | 8 | 4 | 00 | 0x00/03020100
0x04/07060504
0x08/0B0A0908
0x0C/0F0E0D0C | 1(TX) | 1. 在 0x00 读 03020100,
往 0x00 写入 00
2. 在 0x04 读 07060504,
往 0x01 写入 04
3.在 0x08 读 0B0A0908,
往 0x02 写入 08
4.在0x0C读0F0E0D0C,往0x03写入0C | 0x00/00
0x01/04
0x02/08
0x03/0C | 32 | 16 | 4 | 00 | 0x00/03020100
0x04/07060504
0x08/0B0A0908
0x0C/0F0E0D0C | 1(TX) | 1.在 0x00 读 03020100,
往 0x00 写入 0100
2.在 0x04 读 07060504,
往 0x02 写入 0504
3.在 0x08 读 0B0A0908,
往 0x04 写入 0908
4.在 0x0C 读 0F0E0D0C,
往 0x06 写入 0D0C | 0x00/0100
0x02/0504
0x04/0908
0x06/0D0C | 32 | 32 | 4 | 00 | 0x00/03020100
0x04/07060504
0x08/0B0A0908
0x0C/0F0E0D0C | 1(TX) | 1.在 0x00 读 03020100,
往 0x00 写入 03020100
2.在 0x04 读 07060504,
往 0x04 写入 07060504。
3.在 0x08 读 0B0A0908,
往 0x08 写入 0B0A0908
4.在 0x0C 读 0F0E0D0C,
往 0x0C 写入 0F0E0D0C | 0x00/03020100
0x04/07060504
0x08/0B0A0908
0x0C/0F0E0D0C | 32 | 8 | 4 | 01 | 0x00/03020100
0x04/07060504 | 1(TX) | 1.在 0x00 读 03020100,
再拆分成 0302 和 0100
往 0x00 写入 00
往 0x01 写入 02
2.在 0x04 读 07060504,
再拆分成 0706 和 0504
往 0x02 写入 04
往 0x03 写入 06 | 0x00/00
0x01/02
0x02/04
0x03/06 | 32 | 16 | 4 | 01 | 0x00/03020100
0x04/07060504 | 1(TX) | 1.在 0x00 读 03020100,
再拆分成 0302 和 0100
往 0x00 写入 0100
往 0x02 写入 0302
2.在 0x04 读 07060504,
再拆分成 0706 和 0504
往 0x04 写入 0504
往 0x06 写入 0706 | 0x00/0100
0x02/0302
0x04/0504
0x06/0706 | 32 | 32 | 4 | 01 | 0x00/03020100
0x04/07060504 | 1(TX) | 1.在 0x00 读 03020100,
再拆分成 0302 和 0100
往 0x00 写入 00000100
往 0x04 写入 00000302
2.在 0x04 读 07060504,
再拆分成 0706 和 0504
往 0x08 写入 00000504
往 0x0C 写入 00000706 | 0x00/00000100
0x04/00000302
0x08/00000504
0x0C/00000706 | 在文档表格的最后面,我们可以看到如下注解,即作为发送源,只能设置为32,那么我们在使用的时候就要根据接收源的大小来设置Byte mode了。
当 dir=1(TX)时,MSIZE 只有 32, 表示 DMA 从 MEM 恒定读 32bit 数据。
当 dir=0(RX)时, PSIZE 只有 32, 表示 DMA 从 PERIPH 恒定读 32bit 数据,即使外设寄存器有效位只有 8bit。
例如:我当前UART DMA发送,是从mem->periph,那么我的msize只能为32(此处同时要确保memory起始地址四字节对齐),但我的UART是8bit发送,所以我需要设置外设size为8bit,为了保证32bit memory都能发送出去,需要设置Byte mode为3,即分割为四次传输。
再例如:我的ADC模块采用DMA接收,是从periph->mem,那么外设size只能是32,但我的ADC寄存器里面只有前16bit是有效的,所以我只想使用一个uint16的数组来接收每一个采样值,那么我的memsize会设置为16,Byte mode为0,即不分割,这样DMA搬运的时候就会自动舍弃高16bit数据,只将低16bit数据填入我的数组中。
dma.rar
(584.06 KB)
|