STMCU小助手
发布时间:2022-4-14 10:32
|
项目背景 笔者在进行不少项目开发时,都遇到了需要多通道多次采样的需求。由于STM32片上12位ADC的精度不少很高,通常需要对每个通道多次采样,然后计算平均值作为采样的结果。如果采用常规的读取ADC数值并计算平均值的方法,会占用大量CPU时间,极大地增加了MCU的负担(如6个通道采样20个数据计算平均值,需要采样120次才能刷新一次数据)。而采用DMA传输可以使ADC采样和数据刷新放在“后台”自动进行,可以节省大量的单片机运行时间。 笔者将以STM32F103为例,简单介绍一下采用DMA传输使用ADC的一般方法。 DMA简介 DMA(Direct Memory Access)——直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用 CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,DMA2 有 5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是 DMA2 只存在于大容量的单片机中。下图所示是STM32F103的DMA结构框图:
DMA 控制器独立于内核,属于一个单独的外设,结构比较简单。从应用的角度来说,它主要有以下三点值得注意的地方: DMA请求 如果外设要想通过 DMA 来传输数据,必须先给 DMA控制器发送 DMA请求,DMA收到请求信号之后,控制器会给外设一个应答信号,当外设应答后且 DMA 控制器收到应答信号之后,就会启动 DMA 的传输,直到传输完毕。 DMA 有 DMA1 和 DMA2 两个控制器,DMA1 有 7 个通道,DMA2 有 5 个通道,不同的 DMA 控制器的通道对应着不同的外设请求,这决定了我们在软件编程上该怎么设置。 DMA通道 DMA 具有 12 个独立可编程的通道,其中 DMA1 有 7 个通道,DMA2 有 5 个通道,每 个通道对应不同的外设的 DMA 请求。虽然每个通道可以接收多个外设的请求,但是同一 时间只能接收一个,不能同时接收多个。 下图所示为DMA1请求映射表:
下图所示为DMA2请求映射表:
DMA仲裁 当发生多个 DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器也管理。仲裁器管理 DMA 通道请求分为两个阶段。第一阶段属于软件阶段,可以在DMA_CCRx 寄存器中设置,有 4 个等级:非常高、高、中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的 DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权高,比如通道 0 高于通道 1。在大容量产品和互联型产品中,DMA1 控制器拥有高于 DMA2 控制器的优先级。 DMA配置 使用 DMA,最核心就是配置要传输的数据,包括数据从哪里来,要到哪里去,传输的 数据的单位是什么,要传多少数据,是一次传输还是循环传输等等。 从哪里来到哪里去 DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器。 外设到存储器 当我们使用从外设到存储器传输时,以 ADC 采集为例。DMA 外设寄存器的地址对应的就是 ADC 数据寄存器的地址,DMA 存储器的地址就是我们自定义的变量(用来接收存储 AD 采集的数据)的地址。方向我们设置外设为源地址。 存储器到外设 当我们使用从存储器到外设传输时,以串口向电脑端发送数据为例。DMA 外设寄存器的地址对应的就是串口数据寄存器的地址,DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储通过串口发送到电脑的数据)的地址。方向我们设置外设为目标地址。 存储器到存储器 当我们使用从存储器到存储器传输时,以内部 FLASH 向内部 SRAM 复制数据为例。DMA 外设寄存器的地址对应的就是内部 FLASH(我们这里把内部 FALSH 当作一个外设来看)的地址,DMA 存储器的地址就是我们自定义的变量(相当于一个缓冲区,用来存储来自内部 FLASH 的数据)的地址。方向我们设置外设(即内部 FLASH)为源地址。 要传多少,单位是什么 当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多少,数据的单位是什么。以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由DMA_CNDTR 配置,这是一个 32 位的寄存器,一次最多只能传输 65535 个数据。要想数据传输正确,源和目标地址存储的数据宽度还必须一致,串口数据寄存器是 8 位的,所以我们定义的要发送的数据也必须是 8 位。外设的数据宽度由 DMA_CCRx 的PSIZE[1:0]配置,可以是8/16/32位,存储器的数据宽度由DMA_CCRx的MSIZE[1:0]配置,可以是8/16/32位。 在 DMA 控制器的控制下,数据要想有条不紊的从一个地方搬到另外一个地方,还必须正确设置两边数据指针的增量模式。外设的地址指针由 DMA_CCRx 的 PINC 配置,存储器的地址指针由 MINC 配置。以串口向电脑发送数据为例,要发送的数据很多,每发送完一个,那么存储器的地址指针就应该加 1,而串口数据寄存器只有一个,那么外设的地址指针就固定不变。具体的数据指针的增量模式由实际情况决定。 什么时候传输完成 数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个DMA 通道在 DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该类型的中断后,则会产生中断。有关各个标志位的详细描述请参考 DMA 中断状态寄存器DMA_ISR 的详细描述。 传输完成还分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一次之后就停止,要想再传输的话,必须关断 DMA 使能后再重新配置后才能继续传输。循环传输则是一次传输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的由 DMA_CCRx 寄存器的 CIRC 循环模式位控制。 代码示例 在正确配置后,DMA传输与ADC采样会启动。笔者的代码中使能了6个ADC通道进行采样,存储深度为20组。由于设置了循环传输模式,二维数组 ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM] 中的数据会被不断的刷新。 通过函数uint16_t ADC_GetData_DMA(uint8_t channel)可以读取某个通道的采样数值。 通过函数uint16_t ADC_GetAverageData_DMA(uint8_t channel)可以读取以深度20为单位计算出来的平均采样数值。 配置GPIO
相关变量定义
配置ADC
配置DMA
获取采样值
|
经验分享 | STM32N6 ADC DMA 传输失败案例分享
经验分享 | DMA发送函数只能第一次调用有效?
经验分享 | 使用EXIT0同步触发SPI的DMA发送话题
【开发经验】LAT1500 如何通过DMA配合CRC功能
实战经验 | MC SDK V6.x软件HSO功能ADC采样设计说明
我心中的ST中文论坛
经验分享 | 为什么重启ADC的DMA传输要先停掉ADC?
经验分享 | 使用GPIO+DMA+TIM模拟SPI通信演示
经验分享 | 为什么重启ADC的DMA传输要先停掉ADC?
经验分享 | STM32 ADC过采样话题
微信公众号
手机版