本帖最后由 1455555 于 2023-11-30 14:25 编辑
1、DMA简介 DMA(Direct Memory Access:直接存储器存取)在无须 CPU 干预的情况下,可实现外设与存储器或存储器与存储器之间数据的高速传输,从而节省 CPU 资源来做其他操作。 产品一共有两个 DMA 控制器,共 16 个数据流。每个数据流对应 8 个通道,但每个数据流同一时刻只能使用 1 个通道。每个数据流可设置优先级,仲裁器可根据数据流的优先级协调各个数据流对应的DMA 请求的优先级。
2、比较IDLE和TXC两种收发模式 平时我们发送定长数据,我们通常使用发送完成中断TXC进行数据的发送。这次使用串口IDLE中断利用DMA进行不定长数据发送。 优点: a、使用字节中断的方式去接收,会频繁进入中断,影响系统实时性。使用IDLE中断可以减少进入中断的次数,提高系统的实时性。 b、通过前面对DMA的介绍,可以知道,我们设定好搬运方向,起始地址和目标地址等信息之后,无须CPU的干预。这样就可以减少CPU的负载,让CPU得以空闲处理其他任务。在大批量数据传输时,DMA具有极高的优势。
3、例程用到的知识点补充 A、IDLE中断 这里是为什么选用空闲IDLE中断的呢? 我们处理接收到的数据,需要依靠一定的中断进行处理。DMA每个数据流都有5 种类型的中断事件,分别是半传输、传输完成、传输错误、 FIFO 错误和直接模式错误。通常我们使用半传输,传输完成中断请求,但这两个适用于定长数据传输,也就是说,我们能知道传输多少字长传输完成时,才适用。 怎么在不知道传输长度时,也能知道是否传输完成呢?——串口空闲中断请求。 当一次数据传输完成时,会产生空闲中断。配置好串口和DMA后,等待数据传输完成,IDLE中断产生,DMA实时搬运。下面为用户手册中IDLE中断相关信息。 IDLE标志: 当检测到空闲总线时,由硬件置 1; 由软件清 0:先读取 USART_STS 寄存器,再读 USART_DATA 寄存器完成清0。后续编写代码时,会需要注意到清零有先读STS寄存器再读DATA寄存器的要求。
B、DMA_NDATA寄存器 在代码编写过程中,会多次用到该寄存器中的数据。 在DMA配置时,设定NDATA的初始值,只有禁止数据流时,才允许对此寄存器进行操作,使能数据流后,该寄存器为只读,用来指示要传输的剩余数据项数目。每次 DMA 传输后,该寄存器递减。代码编写中,重新使能数据流,使寄存器自动重载初始值。
4、例程实现 例程实现功能为通过串口IDLE中断与DMA配合,完成串口助手发送数据的回显。 A、代码编写流程图
a、USART_Init
串口配置成波特率115200,收发模式,1位停止位,0位校验位,8位数据位。开启串口空闲中断,同时设置中断优先级。这里只使用到一个中断,所以优先级没有特殊规定。使能串口DMA。
b、USART_DMA_Config DATA_BUF_SIZE设置成32,使用USART1的收发,对应DMA2channel4的stream4和stream7。如下图所示。 USART接收时,数据由USART_DATA寄存器运输到设置的rxDataBuf数组中,因此传输方向为从外设到内存,外设地址无需自增,内存地址需要设置为增加。以字节形式发送。配置完成后,打开DMA接收数据流。接收数据流保持常开,只有在处理接收数据时关闭。 USART发送时,数据由txDataBuf数组运输到USART_DATA寄存器,由串口发送至PC串口助手端。因此传输方向为从内存到外设。其他与接收时配置保持一致。 配置完成后关闭发送数据流,等到数据接收完成后打开。
c、USART1_IRQHandler
在接收完数据后,产生IDLE中断。在服务函数中,我们按照手册要求,通过读取USART_STS寄存器,再读取DATA寄存器,完成IDLE标志的清除。然后将RecvFlag置1。这个参数就是用来传递传输是否完成信息的。 关闭USART_RX对应的DMA数据流,计算得出接收到的数据长度。这里的DMA_ReadDataNumber函数就是读取DMA_NDATA寄存器里的数据。函数具体内容如下图。NDATA寄存器初始值为我们配置DMA时设置的DATA_BUF_SIZE:32;每接收到一个数据,NDATA就减1。因此使用它的初值减去接收完后的值,就等于接收到的数据长度。 不要忘记DMA接收完成标志的清除。处理完数据后,再次打开DMA接收数据流。重新使能后,NDATA的值会被重新加载为预设值32。 (加粗的部分都来源于手册对该寄存器的描述,这里再次贴一下图,我开始对这个寄存器理解有误,导致我的代码写得很乱 )
d、USART_SendArray
在main函数里面,一直在判断RecvFlag判断是否置1,当接收完成标志位置1后,进入到数组发送函数。为了保证函数能够单独使用,设置两个输入参数。第一个为需要发送的数组,第二个为需要传输的数据长度。 先要判断需要传输的数据是否有效:要求len不为0,且不能超过txDataBuf的最大值DATA_BUF_SIZE。当传输数据长度len超过DATA_BUF_SIZE时,只传输DATA_BUF_SIZE长度。如过输数据长度len未超过可传输最大值,传输数据长度就为len。 173行:将需要传输的数组COPY到DMA配置的发送地址处。
e、main
完成发送后,再次等待下一次的接收完成,这部分我是将接收到的数据发送出去,也可以进行其他的数据处理。 程序大概执行过程如流程图所示。
5、实验现象 发送的数据可以正常进行回显。实验成功。
|
你好,请问你使用串口空闲中断,找到如何去设置其空闲中断的触发时间吗,就是接收完一帧数据后多久会触发空闲中断。
想到例程有个问题。。。第一个是在处理数据时,会关闭DMA接收数据流,那如果这个时候来了新的数据,就会造成数据的丢失。