例如: Mburst 节拍 = 8 (INCR8), MSIZE =“00”(字节)和 PSIZE =“01”(半字),故 DMA_SxNDTR 必须是 (8 × 1/2 = 4) 的倍数。
此模式可用于所有 DMA1 和 DMA2 数据流。
通过将 DMA_SxCR 寄存器中的 DBM 位置 1,即可使能双缓冲区模式。
除了有两个存储器指针之外,双缓冲区数据流的工作方式与常规(单缓冲区)数据流的一样。
使能双缓冲区模式时,将自动使能循环模式( DMA_SxCR 中的 CIRC 位的状态是“无关”),并在每次事务结束时交换存储器指针。
在此模式下, 每次事务结束时, DMA 控制器都从一个存储器目标交换为另一个存储器目标。这样,软件在处理一个存储器区域的同时, DMA 传输还可以填充/使用第二个存储器区域 。
双缓冲区数据流可以双向工作(存储器既可以是源也可以是目标)。
在双缓冲区模式下使能数据流时,可遵循下列条件,实时更新 AHB 存储器的基址( DMA_SxM0AR 或 DMA_SxM1AR):
对于所有其它模式(双缓冲区模式除外),一旦使能数据流,存储器地址寄存器即被写保护。
由于存储器到存储器模式与循环模式不兼容,所以当使能双缓冲区模式时,不允许配置存储器到存储器模式。
以下各种事件均可以结束传输过程,并将 DMA_LISR 或 DMA_HISR 状态寄存器中的 TCIFx位置 1:
可以随时暂停 DMA 传输以供稍后重新开始;也可以在 DMA 传输结束前明确禁止暂停功能。
分为两种情况:
控制要传输的数据数目的实体称为流控制器。此流控制器使用 DMA_SxCR 寄存器中的 PFCTRL 位针对每个数据流独立配置。
配置 DMA 数据流 x(其中 x 是数据流编号)时应遵守下面的顺序:
DMA 控制器可以检测到以下错误:
对于每个 DMA 数据流,可在发生以下事件时产生中断:
- 位 31:28、15:12 保留,必须保持复位值
- 位 27、21、11、5 TCIFx:数据流 x 传输完成中断标志 (Stream x transfer complete interrupt flag) (x=7…4)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
0:数据流 x 上无传输完成事件
1:数据流 x 上发生传输完成事件- 位 26、20、10、4 HTIFx:数据流 x 半传输中断标志 (Stream x half transfer interrupt flag) (x=7…4)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
0:数据流 x 上无半传输事件
1:数据流 x 上发生半传输事件- 位 25、19、9、3 TEIFx:数据流 x 传输错误中断标志 (Stream x transfer error interrupt flag) (x=7…4)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
0:数据流 x 上无传输错误
1:数据流 x 上发生传输错误- 位 24、18、8、2 DMEIFx:数据流 x 直接模式错误中断标志 (Stream x direct mode error interrupt flag) (x=7…4)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
0:数据流 x 上无直接模式错误
1:数据流 x 上发生直接模式错误- 位 23、17、7、1 保留,必须保持复位值
- 位 22、16、6、0 FEIFx:数据流 x FIFO 错误中断标志 (Stream x FIFO error interrupt flag) (x=7…4)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_HIFCR 寄存器的相应位。
0:数据流 x 上无 FIFO 错误事件
1:数据流 x 上发生 FIFO 错误事件
- 位 31:28、15:12 保留,必须保持复位值
- 位 27、21、11、5 TCIFx:数据流 x 传输完成中断标志 (Stream x transfer complete interrupt flag) (x = 3…0)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
0:数据流 x 上无传输完成事件
1:数据流 x 上发生传输完成事件- 位 26、20、10、4 HTIFx:数据流 x 半传输中断标志 (Stream x half transfer interrupt flag) (x=3…0)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
0:数据流 x 上无半传输事件
1:数据流 x 上发生半传输事件- 位 25、19、9、3 TEIFx:数据流 x 传输错误中断标志 (Stream x transfer error interrupt flag) (x=3…0)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
0:数据流 x 上无传输错误
1:数据流 x 上发生传输错误- 位 24、18、8、2 DMEIFx:数据流 x 直接模式错误中断标志 (Stream x direct mode error interrupt flag) (x=3…0)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
0:数据流 x 上无直接模式错误
1:数据流 x 上发生直接模式错误- 位 23、17、7、1 保留,必须保持复位值
- 位 22、16、6、0 FEIFx:数据流 x FIFO 错误中断标志 (Stream x FIFO error interrupt flag) (x=3…0)
此位将由硬件置 1,由软件清零,软件只需将 1 写入 DMA_LIFCR 寄存器的相应位。
0:数据流 x 上无 FIFO 错误事件
1:数据流 x 上发生 FIFO 错误事件
- 位 31:28、15:12 保留,必须保持复位值
- 位 27、21、11、5 CTCIFx:数据流 x 传输完成中断标志清零 (Stream x clear transfer complete interrupt flag) (x = 7.0.4)
将 1 写入此位时,DMA_HISR 寄存器中相应的 TCIFx 标志将清零- 位 26、20、10、4 CHTIFx:数据流 x 半传输中断标志清零 (Stream x clear half transfer interrupt flag) (x = 7.0.4)
将 1 写入此位时,DMA_HISR 寄存器中相应的 HTIFx 标志将清零- 位 25、19、9、3 CTEIFx:数据流 x 传输错误中断标志清零 (Stream x clear transfer error interrupt flag) (x = 7.0.4)
将 1 写入此位时,DMA_HISR 寄存器中相应的 TEIFx 标志将清零- 位 24、18、8、2 CDMEIFx:数据流 x 直接模式错误中断标志清零 (Stream x clear direct mode error interrupt flag) (x = 7.0.4)
将 1 写入此位时,DMA_HISR 寄存器中相应的 DMEIFx 标志将清零- 位 23、17、7、1 保留,必须保持复位值
- 位 22、16、6、0 CFEIFx:数据流 x FIFO 错误中断标志清零 (Stream x clear FIFO error interrupt flag) (x = 7.0.4)
将 1 写入此位时,DMA_HISR 寄存器中相应的 CFEIFx 标志将清零
- 位 31:28、15:12 保留,必须保持复位值
- 位 27、21、11、5 CTCIFx:数据流 x 传输完成中断标志清零 (Stream x clear transfer complete interrupt flag) (x = 3…0)
将 1 写入此位时,DMA_LISR 寄存器中相应的 TCIFx 标志将清零- 位 26、20、10、4 CHTIFx:数据流 x 半传输中断标志清零 (Stream x clear half transfer interrupt flag) (x = 3…0)
将 1 写入此位时,DMA_LISR 寄存器中相应的 HTIFx 标志将清零- 位 25、19、9、3 CTEIFx:数据流 x 传输错误中断标志清零 (Stream x clear transfer error interrupt flag) (x = 3…0)
将 1 写入此位时,DMA_LISR 寄存器中相应的 TEIFx 标志将清零- 位 24、18、8、2 CDMEIFx:数据流 x 直接模式错误中断标志清零 (Stream x clear direct mode error interrupt flag) (x = 3…0)
将 1 写入此位时,DMA_LISR 寄存器中相应的 DMEIFx 标志将清零- 位 23、17、7、1 保留,必须保持复位值
- 位 22、16、6、0 CFEIFx:数据流 x FIFO 错误中断标志清零 (Stream x clear FIFO error interrupt flag) (x = 3…0)
将 1 写入此位时,DMA_LISR 寄存器中相应的 CFEIFx 标志将清零
- 位 31:28 保留,必须保持复位值。
- 位 27:25 CHSEL[2:0]:通道选择 (Channel selection)
这些位将由软件置 1 和清零。
000:选择通道 0
001:选择通道 1
010:选择通道 2
011:选择通道 3
100:选择通道 4
101:选择通道 5
110:选择通道 6
111:选择通道 7
这些位受到保护,只有 EN 为“0”时才可以写入- 位 24:23 MBURST[1:0]:存储器突发传输配置 (Memory burst transfer configuration)
这些位将由软件置 1 和清零。
00:单次传输
01:INCR4(4 个节拍的增量突发传输)
10:INCR8(8 个节拍的增量突发传输)
11:INCR16(16 个节拍的增量突发传输)
这些位受到保护,只有 EN 为“0”时才可以写入
在直接模式中,当位 EN =“1”时,这些位由硬件强制置为 0x0。- 位 22:21 PBURST[1:0]:外设突发传输配置 (Peripheral burst transfer configuration)
这些位将由软件置 1 和清零。
00:单次传输
01:INCR4(4 个节拍的增量突发传输)
10:INCR8(8 个节拍的增量突发传输)
11:INCR16(16 个节拍的增量突发传输)
这些位受到保护,只有 EN 为“0”时才可以写入
在直接模式下,这些位由硬件强制置为 0x0。- 位 20 保留,必须保持复位值
- 位 19 CT:当前目标(仅在双缓冲区模式下)(Current target (only in double buffer mode))
此位由硬件置 1 和清零,也可由软件写入。
0:当前目标存储器为存储器 0(使用 DMA_SxM0AR 指针寻址)
1:当前目标存储器为存储器 1(使用 DMA_SxM1AR 指针寻址)
只有 EN 为“0”时,此位才可以写入,以指示第一次传输的目标存储区。在使能数据流后,此位相当于一个状态标志,用于指示作为当前目标的存储区。- 位 18 DBM:双缓冲区模式 (Double buffer mode)
此位由软件置 1 和清零。
0:传输结束时不切换缓冲区
1:DMA 传输结束时切换目标存储区
此位受到保护,只有 EN 为“0”时才可以写入。- 位 17:16 PL[1:0]:优先级 (Priority level)
这些位将由软件置 1 和清零。
00:低
01:中
10:高
11:非常高
这些位受到保护,只有 EN 为“0”时才可以写入。- 位 15 PINCOS:外设增量偏移量 (Peripheral increment offset size)
此位由软件置 1 和清零
0:用于计算外设地址的偏移量与 PSIZE 相关
1:用于计算外设地址的偏移量固定为 4(32 位对齐)。
如果位 PINC =“0”,则此位没有意义。
此位受到保护,只有 EN 为“0”时才可以写入。
如果选择直接模式或者 PBURST 不等于“00”,则当使能数据流(位 EN =“1”)时,此 位由硬件强制置为低电平。- 位 14:13 MSIZE[1:0]:存储器数据大小 (Memory data size)
这些位将由软件置 1 和清零。
00:字节(8 位)
01:半字(16 位)
10:字(32 位)
11:保留
这些位受到保护,只有 EN 为“0”时才可以写入。
在直接模式下,当位 EN =“1”时,MSIZE 位由硬件强制置为与 PSIZE 相同的值。- 位 12:11 PSIZE[1:0]:外设数据大小 (Peripheral data size)
这些位将由软件置 1 和清零。
00:字节(8 位)
01:半字(16 位)
10:字(32 位)
11:保留
这些位受到保护,只有 EN 为“0”时才可以写入- 位 10 MINC:存储器递增模式 (Memory increment mode)
此位由软件置 1 和清零。
0:存储器地址指针固定
1:每次数据传输后,存储器地址指针递增(增量为 MSIZE 值)
此位受到保护,只有 EN 为“0”时才可以写入。- 位 9 PINC:外设递增模式 (Peripheral increment mode)
此位由软件置 1 和清零。
0:外设地址指针固定
1:每次数据传输后,外设地址指针递增(增量为 PSIZE 值)
此位受到保护,只有 EN 为“0”时才可以写入。- 位 8 CIRC:循环模式 (Circular mode)
此位由软件置 1 和清零,并可由硬件清零。
0:禁止循环模式
1:使能循环模式
如果外设为流控制器(位 PFCTRL=1)且使能数据流(位 EN=1),此位由硬件自动强制 清零。
如果 DBM 位置 1,当使能数据流(位 EN =“1”)时,此位由硬件自动强制置 1。- 位 7:6 DIR[1:0]:数据传输方向 (Data transfer direction)
这些位将由软件置 1 和清零。
00:外设到存储器
01:存储器到外设
10:存储器到存储器
11:保留
这些位受到保护,只有 EN 为“0”时才可以写入。- 位 5 PFCTRL:外设流控制器 (Peripheral flow controller)
此位由软件置 1 和清零。
0:DMA 是流控制器
1:外设是流控制器
此位受到保护,只有 EN 为“0”时才可以写入。
选择存储器到存储器模式(位 DIR[1:0]=10)后,此位由硬件自动强制清零。- 位 4 TCIE:传输完成中断使能 (Transfer complete interrupt enable)
此位由软件置 1 和清零。
0:禁止 TC 中断
1:使能 TC 中断- 位 3 HTIE:半传输中断使能 (Half transfer interrupt enable)
此位由软件置 1 和清零。
0:禁止 HT 中断
1:使能 HT 中断- 位 2 TEIE:传输错误中断使能 (Transfer error interrupt enable)
此位由软件置 1 和清零。
0:禁止 TE 中断
1:使能 TE 中断- 位 1 DMEIE:直接模式错误中断使能 (Direct mode error interrupt enable)
此位由软件置 1 和清零。
0:禁止 DME 中断
1:使能 DME 中断- 位 0 EN:数据流使能/读作低电平时数据流就绪标志 (Stream enable / flag stream ready when read low)
此位由软件置 1 和清零。
0:禁止数据流
1:使能数据流
以下情况下,此位可由硬件清零:
- DMA 传输结束时(准备好配置数据流)
- AHB 主总线出现传输错误时
- 存储器 AHB 端口上的 FIFO 阈值与突发大小不兼容时
此位读作 0 时,软件可以对配置和 FIFO 位寄存器编程。EN 位读作 1 时,禁止向这些寄存 器执行写操作。
将 EN 位置“1”以启动新传输之前,DMA_LISR 或 DMA_HISR 寄存器中与数据流相对应的事件标志必须清零。
- 位 31:16 保留,必须保持复位值。
- 位 15:0 NDT[15:0]:要传输的数据项数目 (Number of data items to transfer)
要传输的数据项数目(0 到 65535)。只有在禁止数据流时,才能向此寄存器执行写操作。
使能数据流后,此寄存器为只读,用于指示要传输的剩余数据项数。每次 DMA 传输后,此 寄存器将递减。
传输完成后,此寄存器保持为零(数据流处于正常模式时),或者在以下情况下自动以先前编程的值重载:
- 以循环模式配置数据流时。
- 通过将 EN 位置“1”来重新使能数据流时
如果该寄存器的值为零,则即使使能数据流,也无法完成任何事务。
- 位 31:0 PAR[31:0]:外设地址 (Peripheral address)
读/写数据的外设数据寄存器的基址。
这些位受到写保护,只有 DMA_SxCR 寄存器中的 EN 为“0”时才可以写入。
- 位 31:0 M0A[31:0]:存储器 0 地址 (Memory 0 address)
读/写数据的存储区 0 的基址。
这些位受到写保护,只有在以下情况下才可以写入:
- 禁止数据流(DMA_SxCR 寄存器中的位 EN=“0”)或
- 使能数据流(DMA_SxCR 寄存器中的 EN=“1”)并且 DMA_SxCR 寄存器中的 位 CT =“1”(在双缓冲区模式下)。
- 位 31:0 M1A[31:0]:存储器 1 地址(用于双缓冲区模式)(Memory 1 address (used in case of Double buffer mode))
读/写数据的存储区 1 的基址。
此寄存器仅用于双缓冲区模式。
这些位受到写保护,只有在以下情况下才可以写入:
- 禁止数据流(DMA_SxCR 寄存器中的位 EN=“0”)或
- 使能数据流(DMA_SxCR 寄存器中的 EN=“1”)并且 DMA_SxCR 寄存器中的位 CT =“0”。
- 位 31:8 保留,必须保持复位值
- 位 7 FEIE:FIFO 错误中断使能 (FIFO error interrupt enable)
此位由软件置 1 和清零。
0:禁止 FE 中断
1:使能 FE 中断- 位 6 保留,必须保持复位值
- 位 5:3 FS[2:0]:FIFO 状态 (FIFO status)
这些位为只读。
000:0 < fifo_level < 1/4
001:1/4 fifo_level < 1/2
010:1/2 fifo_level < 3/4
011:3/4 fifo_level < 满
100:FIFO 为空
101:FIFO 已满
其它:无意义
在直接模式(DMDIS 位为零)下,这些位无意义。- 位 2 DMDIS:直接模式禁止 (Direct mode disable)
此位由软件置 1 和清零。它可由硬件置 1。
0:使能直接模式
1:禁止直接模式
此位受到保护,只有 EN 为“0”时才可以写入。
如果选择存储器到存储器模式(DMA_SxCR 中的 DIR 位为“10”),并且 DMA_SxCR 寄存 器中的 EN 位为“1”,则此位由硬件置 1,因为在存储器到存储器配置不能使用直接模式。- 位 1:0 FTH[1:0]:FIFO 阈值选择 (FIFO threshold selection)
这些位将由软件置 1 和清零。
00:FIFO 容量的 1/4
01:FIFO 容量的 1/2
10:FIFO 容量的 3/4
11:FIFO 完整容量
在直接模式(DMDIS 值为零)下,不使用这些位。
这些位受到保护,只有 EN 为“1”时才可以写入。
我们要用到串口 1 的发送,属于 DMA2 的数据流 7,通道 4。步骤如下:
DMA 的时钟使能是通过 AHB1ENR 寄存器来控制的,这里我们要先使能时钟,才可以配置 DMA
相关寄存器。所以先要使能 DMA2 的时钟。另外,要对配置寄存器( DMA_SxCR)进行设置, 必须先等待其最低位为 0(也就是 DMA 传输禁止了),才可以进行配置 。
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待 DMA 可配置
包括配置通道,外设地址,存储器地址,传输数据量等。
DMA 的某个数据流各种配置参数初始化是通过 DMA_Init 函数实现的:
void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);
typedef struct
{
uint32_t DMA_Channel;
uint32_t DMA_PeripheralBaseAddr;
uint32_t DMA_Memory0BaseAddr;
uint32_t DMA_DIR;
uint32_t DMA_BufferSize;
uint32_t DMA_PeripheralInc;
uint32_t DMA_MemoryInc;
uint32_t DMA_PeripheralDataSize;
uint32_t DMA_MemoryDataSize;
uint32_t DMA_Mode;
uint32_t DMA_Priority;
uint32_t DMA_FIFOMode;
uint32_t DMA_FIFOThreshold;
uint32_t DMA_MemoryBurst;
uint32_t DMA_PeripheralBurst;
}DMA_InitTypeDef;
- DMA_Channel
用来 设置 DMA 数据流对应的通道。 可供每个数据流选择的通道请求多达 8 个,取值范围为: DMA_Channel_0~ DMA_Channel_7。- DMA_PeripheralBaseAddr
用来 设置 DMA 传输的外设基地址,比如要进行串口DMA 传输,那么外设基地址为串口接受发送数据存储器 USART1->DR 的地址,表示方法为&USART1->DR。- DMA_Memory0BaseAddr
为内存基地址,也就是我们 存放 DMA 传输数据的内存地址。- DMA_DIR
设置数据传输方向,决定是从外设读取数据到内存还送从内存读取数据发送到外设,也就是外设是源地还是目的地,这里我们设置为从内存读取数据发送到串口,所以外设自然就是目的地了,所以选择值为 DMA_DIR_PeripheralDST。- DMA_BufferSize
设置一次传输数据量的大小,这个很容易理解。- DMA_PeripheralInc
设置传输数据的时候外设地址是不变还是递增。如果设置为递增,那么下一次传输的时候地址加 1,这里因为我们是一直往固定外设地址&USART1->DR发送数据,所以地址不递增,值为DMA_PeripheralInc_Disable;- DMA_MemoryInc
设置传输数据时候内存地址 是否递增。这个参数 和DMA_PeripheralInc 意思接近,只不过针对的是内存。这里我们的场景是将内存中连续存储单元的数据发送到串口,毫无疑问内存地址是需要递增的,所以值为 DMA_MemoryInc_Enable。- DMA_PeripheralDataSize
设置外设的数据长度是为字节传输(8bits),半字传 输 (16bits) 还 是 字 传 输 (32bits) , 这 里 我 们 是 8 位 字 节 传 输 , 所 以 值 设 置 为
DMA_PeripheralDataSize_Byte。- DMA_MemoryDataSize
设置内存的数据长度,和第八个参数意思接近,这里我们同样设置为字节传输 DMA_MemoryDataSize_Byte。- DMA_Mode
设置 DMA 模式是否循环采集,也就是说,比如我们要从内存中采集 64 个字节发送到串口,如果设置为重复采集,那么它会在 64 个字节采集完成之后继续从内存的第一个地址采集,如此循环。这里我们设置为一次连续采集完成之后不循环。所以设置值为 DMA_Mode_Normal。在我们下面的实验中,如果设置此参数为循环采集,那么你会看到串口不停的打印数据,不会中断。- DMA_Priority
设置 DMA 通道的优先级,有低,中,高,超高三种模式,
这个在前面讲解过,这里我们设置优先级别为中级,所以值为 DMA_Priority_Medium。 优先级可以随便设置,因为我们只有一个数据流被开启了。假设有多个数据流开启(最多 8 个),那么就要设置优先级了, DMA 仲裁器将根据这些优先级的设置来决定先执行那个数据流的 DMA。优先级越高的,越早执行,当优先级相同的时候,根据硬件上的编号来决定哪个先执行(编号越小越优先)。- DMA_FIFOMode
设置是否开启 FIFO 模式。这里我们不开启所以选择 DMA_FIFOMode_Disable。- DMA_FIFOThreshold
选择 FIFO 阈值。根据前面讲解可以为 FIFO 容量的1/4,1/2,3/4 以及 1 倍。这里我们实际并没有开启 FIFO 模式,所以可以不关心。- DMA_MemoryBurst
配置存储器突发传输配置。可以选择为 4 个节拍的增量突发传输 DMA_MemoryBurst_INC4, 8 个节拍的增量突发传输 DMA_MemoryBurst_INC8, 16 个街拍的增量突发传输 DMA_MemoryBurst_INC16 以及单次传输 DMA_MemoryBurst_Single。- DMA_PeripheralBurst
配置外设突发传输配置。跟前面一个参数DMA_MemoryBurst 作用类似,只不过一个针对的是存储器,一个是外设。这里我们选择单次传输 DMA_PeripheralBurst_Single。
/* 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA 外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器 0 地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式
DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化 DMA Stream
进行 DMA 配置之后,我们就要开启串口的 DMA 发送功能,使用的函数是:
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA 发送
如果是要使能串口 DMA 接受,那么第二个参数修改为 USART_DMAReq_Rx 即可。
使能 DMA 数据流的函数为:
void DMA_Cmd(DMA_Stream_TypeDef* DMAy_Streamx, FunctionalState NewState)
使能 DMA2_Stream7,启动传输的方法为:
DMA_Cmd (DMA2_Stream7, ENABLE) ;
通过以上 4 步设置,我们就可以启动一次 USART1 的 DMA 传输了。
在 DMA 传输过程中,我们要查询 DMA 传输通道的状态,使用的函数是:
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
比如我们要查询 DMA 数据流 7 传输是否完成,方法是:
DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7);
这里还有一个比较重要的函数就是获取当前剩余数据量大小的函数:
uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);
比如我们要获取 DMA 数据流 7 还有多少个数据没有传输,方法是:
DMA_GetCurrDataCounter(DMA1_Channel4);
同样,我们也可以设置对应的 DMA 数据流传输的数据量大小,函数为:
void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter);
dma.c
//DMAx 的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8 位数据宽度/存储器增量模式
//DMA_Streamx:DMA 数据流,DMA1_Stream0~7/DMA2_Stream0~7
//chx:DMA 通道选择, @ref DMA_channel DMA_Channel_0~DMA_Channel_7
//par:外设地址 mar:存储器地址 ndtr:数据传输量
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{
DMA_InitTypeDef DMA_InitStructure;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前 stream 是属于 DMA2 还是 DMA1
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能
}
else
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1 时钟使能
}
DMA_DeInit(DMA_Streamx);
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待 DMA 可配置
/* 配置 DMA Stream */
DMA_InitStructure.DMA_Channel = chx; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA 外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器 0 地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式
DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8 位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8 位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化 DMA Stream
}
//开启一次 DMA 传输
//DMA_Streamx:DMA 数据流,DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
DMA_Cmd(DMA_Streamx, DISABLE); //关闭 DMA 传输
while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){} //确保 DMA 可以被设置
DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
DMA_Cmd(DMA_Streamx, ENABLE); //开启 DMA 传输
}
main.c
/*发送数据长度,最好等于 sizeof(TEXT_TO_SEND)+2 的整数倍.*/
#define SEND_BUF_SIZE 8200
u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"};
int main(void)
{
u16 i;
u8 t=0,j,mask=0;
float pro=0;//进度
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为 115200
LED_Init(); //初始化 LED
LCD_Init(); //LCD 初始化
KEY_Init(); //按键初始化
/*DMA2,STEAM7,CH4,外设为串口 1,存储器为 SendBuff,长度为:SEND_BUF_SIZE.*/
MYDMA_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);
LCD_ShowString(30,70,200,16,16,"DMA TEST");
LCD_ShowString(30,130,200,16,16,"KEY0:Start");
POINT_COLOR=BLUE;//设置字体为蓝色
//显示提示信息
j=sizeof(TEXT_TO_SEND);
for(i=0;i<SEND_BUF_SIZE;i++)//填充 ASCII 字符集数据
{
if(t>=j)//加入换行符
{
if(mask)
{
SendBuff[i]=0x0a;t=0;
}
else
{
SendBuff[i]=0x0d;mask++;
}
}
else//复制 TEXT_TO_SEND 语句
{
mask=0;
SendBuff[i]=TEXT_TO_SEND[t];t++;
}
}
POINT_COLOR=BLUE;//设置字体为蓝色
i=0;
while(1)
{
t=KEY_Scan(0);
if(t==KEY0_PRES) //KEY0 按下
{
printf("\r\nDMA DATA:\r\n");
LCD_ShowString(30,150,200,16,16,"Start Transimit....");
LCD_ShowString(30,170,200,16,16," %");//显示百分号
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口 1 的 DMA 发送
MYDMA_Enable(DMA2_Stream7,SEND_BUF_SIZE); //开始一次 DMA 传输!
//等待 DMA 传输完成,此时我们来做另外一些事,点灯
//实际应用中,传输数据期间,可以执行另外的任务
while(1)
{
if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)
//等待 DMA2_Steam7 传输完成
{
DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清传输完成标志
break;
}
pro=DMA_GetCurrDataCounter(DMA2_Stream7);//得到当前剩余数据数
pro=1-pro/SEND_BUF_SIZE;//得到百分比
pro*=100; //扩大 100 倍
LCD_ShowNum(30,170,pro,3,16);
}
LCD_ShowNum(30,170,100,3,16);//显示 100%
LCD_ShowString(30,150,200,16,16,"Transimit Finished!");
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//提示系统正在运行
i=0;
}
}
}