关于ADC_DMAAccessMode,DMA_BufferSize的续谈

2 篇文章 0 订阅
1 篇文章 0 订阅

1.续谈前提:

我们在上一个文章中,

了解到DMA_BufferSize的基本概念,

且前提是:

 注:这里我们假设的ADC配置为:

单次触发/不连续转换,扫描模式,单adc模式,两个通道,且数据对齐

且:我们先不用给DMA_BufferSize或者其他下定义,我们先来看看咋么回事!

2.疑惑——》加深对于配置间的理解

但当我们碰到 ,如连续转换,多通道的双/三adc模式,数据不对齐的情况,以及ADC_DMAAccessMode 配置不同的情况,我们ADC转换的数据和我们需要显示的数据情况的不同(这与数据对齐与否有关),等等等等的问题/需求时,或许我们的理解也需要加深了

3.我们分情况来

情况1:

我们需要多通道,且由外部触发来进行ADC转换,并用单ADC模式,数据对齐,扫描模式,单次触发(这里的外部触发,先暂时指:TIM定时器更新事件触发)

这里的希望或许是:

我们需要在一定的时间段中,来检测多个需要检测的电压目标,并显示出。

(这里的重点在于:外部触发的一定时间间隔,来查看多个待检测目标的变化情况)

这里我们使用DMA的目的是: 防止数据覆盖

原因是:

使用规则组的多通道进行目标检测时,数据存储在DR寄存器中,会造成数据覆盖,如此我们就查看不到这段时间的测量变化情况,若不使用DMA进行数据搬运,则我们每次读取到的数据只是最近的通道转换的数据。

因此,我们这里使用DMA数据搬运,解决/缓解了两个问题:

1.检测到的数据情况,我们能够根据外部触发的时间间隔来,间歇地查看这段时间中的检测变化情况,以便做出下一步的处理工作

2.我们能够查看多个规则组通道的数据,而不至于我们每次读取到的数据只是最近的通道转换的数据。

我们回到讨论中,在这里 ,我们DMA_BufferSize这一配置可以是:M*N

为什么突然给出个这个结论呢?  听我慢慢道来

1.回顾:  DMA 事务由给定数目的数据传输序列组成。

2.这里的M,我给的定义是:进行多少轮这样的数据传输序列转换过程

3.这里配置的为不连续转换/单次外部触发,因此这里的M:1

(这里的M也就是侧重体现了手册DMA事务中的数据传输序列)

4.接着,N我给的定义是:每一轮需要传输的数据数目个数

5.这里配置的为7个通道,且数据对齐,且单adc模式,因此这里的N:7

(这里的N也就是侧重体现了手册DMA事务中的给定数目的数据)

6.因此,这里的DMA_BufferSize:1*7

注意:这里的DMA_BufferSize并不等同于DMA请求,有关这个我们在上一篇已讨论了一部分

7.执行过程:

当我们外设能够被外部触发的更新事件触发转换后,而使能了DMA请求,那么每一轮的DMA请求,都会使DMA_BufferSize中的7减1,直到7——》0,本次的DMA请求结束,外设释放DMA控制总线。

(这里还不需要讨论外设的DMA请求的优先级问题)

情况2:

我们需要多通道,且由外部触发来进行ADC转换,并用单ADC模式,数据对齐,扫描模式,单次触发,但连续转换(这里的外部触发,先暂时指:TIM定时器更新事件触发)

(这里的一般情况是:外部触发一次,而我们来配置  DMA 事务 进而来改变M(情况1中的)

  当然根据不同情况,我们也可以改变N,这涉及到许多情况,另论)

我们或许希望的是: 

在TIM定时器外部触发的某一次中,我们需要检测的目标,在连续的极小时间中是否电压稳定

因此这里的DMA作用为:搬运在某次TIM定时器外部触发的给定数目的数据传输序列的数据量,并显示出,从而知道在这段极小的时间内,会不会出现电压的波动如何,是否稳定

因此,我们这里的DMA_BufferSize依旧是:M*N

(不过这里的N,我们不会常常变化)

情况3:

我们需要多通道,且由软件触发来进行ADC转换,并用双/三ADC模式,规则组同步,数据对齐,扫描模式,(可连续转换/不)

(我们先忽然上述的16 通道的规则同时模式:双重 ADC 模式的种种细节)

我们先再对标题的情况进行设置上的补充说明:

ADC1,转换4个通道,通道为1 ,2,3,4,顺序为1 ,2,3,4。

ADC2,转换4个通道,通道为1 ,2,3,4,顺序为1 ,2,3,4。

因此,转换的执行情况参考上述图 44. 16 通道的规则同时模式:双重 ADC 模式

我们首先得知道当多重adc转换时,数据是存储在

适用于双重和三重模式的 ADC 通用规则数据寄存器(ADC_CDR)中

也即;我们可以理解为32 = 16 +16

结合上述三个图片来说:

当这个设置为ADC_DMAAccessMode2的时候,ADC转换完成后发送一个32位的DMA请求,假设我们的内存(存储搬运的数据)的大小为32位,也即数据对齐。

一次ADC_CDR满的执行过程:

当ADC1转换了一个通道时,同时ADC2也转换了一个通道,两种的数据分别存放在ADC_CDR寄存器的高低16位,而后发送DMA请求。(DDS会的配置会先发送DMA请求,而不会再转换而覆盖ADC_CDR)

这里我们回到  

DMA_BufferSize是:M*N,这一预先的定义——尽管它是鲁莽的!

那么我们这里的DMA 事务由给定数目的数据传输序列组成 ,该如何理解呢?

我认为,还是

M :进行多少轮这样的数据传输序列转换过程    这里为1

而N  之前 我给的定义是:每一轮需要传输的数据数目个数

在这里我们可以对其有了更深的理解,

对之前的补充:

其数据数目指的是:每个通道进行adc转换后的数据存储到DR寄存器中,这样的数据有多少个

那么这里也继续扩展:

其数据数目指的是:每个通道进行adc转换后的数据存储到CDR寄存器中,这样的数据有多少个

(也就是说,我们这里的数据已经进行了一次打包,将ADC1 ADC2一次规则组同时转换不同通道的数据打包在一个寄存器CDR中,分别是高16位,和低16位)

因此在DMA_BufferSize是:M*N,这一预先的定义中,我们似乎能够补充/遗漏了一个参数

L:一个数据包的大小,一般整合了两个数据(16位的)

(16位为一个单位,以后或许有不同,如8位,再说)

N:变成了 N/L

因此这里的DMA_BufferSize是:M*L*N =M*L*(N/L)

//或许结果一样,但逻辑执行过程和细节部分就已大不同了

注:这里的和上述的情况1,均为数据对齐(为了讨论方便起见,一步一步来),但不同 

就是说,情况1中的数据存储到SRAM的大小为16位,情况3的数据存储到SRAM的大小为32位

扩展再论!当这个设置为ADC_DMAAccessMode1的时候,ADC转换完成后发送一个32位的DMA请求,假设我们的内存(存储搬运的数据)的大小为32位,也即数据对齐

ADC_DMAAccessMode1时,就是说我们是ADC1或者ADC2转换后,发送一个16位的DMA请求,即一个一个的传输数据,数据大小位16位,这类应用其实比较少,但还需要讨论

注:我们这里探讨其出现的问题即可

(这里的情况和我们有些不同,因为这里是7路的通道,且涉及到数据大小对齐的问题)

我们以这篇文章为例

http://t.csdnimg.cn/tFKMj

我先谈文章中数据错位的问题,并尝试扩展说说

在我看来,造成数据错位的原因:

1.一次软件触发的连续转换问题

2.且SRAM的数据大小为16位,总的数据不对齐

                                                                                  1,2同时满足,而造成数据错位

这里我们谈谈解决方法及可能的应用和问题:

1.不用连续转换

则按照,我们之前谈及的DMA_BufferSize为 1*1*7

(一次触发转换,16位数据大小(SRAM),7个通道)

(其实这里和情况1很类似,区别在于是双/多ADC模式,且数据存储在CDR寄存器中)

但话又说回来,若是这样,那么我们使用双/多ADC模式且交替/同时模式转换的应用意义就不大,甚至于没有,效率自然就没有提高。

2.使一次的DMA事务/DMA_BufferSize为8

(稍等一下,我们写着写着又有疑惑了:若按照这样的配置,则DMA_SxNDTR 寄存器似乎达不到0,即无法通过这一条件来终止本次外设请求传输,就是说,我们没办法空出来的那位16位数据大小,应该是会被再次转换到的ADC数据填上)

(上述,待定:需要阅读和理解:对于本次外设请求传输终止的条件,以及双ADC模式且同时转换模式)

这样就与下一轮的转换数据分开了,因为相当于空出了一个16位大小来标识,就不会发生错位的情况

但1的问题依旧存在,且造成了16位的存储空间浪费(每一轮转换)

补充上述紫色说明:

1.我们在下图找到了双重ADC且规则同时模式的终止条件和标志位

2.我们在DMA的外设到存储器中找到了DMA请求传输终止的条件,下图,如果那句

因此,我们上述紫色背景的地方,是我们的猜想错误!!!

情况4:

我们需要多通道,且由软件触发来进行ADC转换,并用双/多ADC模式,规则组同步,不数据对齐,扫描模式,(连续转换/不),且 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;

1.引到简要 我们上述

情况3 中探讨的得出的结果DMA_BufferSize是:M*L*N =M*L*(N/L)

2.当但我们做了实验发现,这里我们还是忽略了一个关于DMA关于9.3.10 可编程数据宽度、封装/解封、字节序的相关介绍细节。

先看下列图

上述我们汇总地说:

我们配置的NDT/DMA_SxNDTR 寄存器的数据传输数目,等于外设需要传输的数目(这里先指一次转换)

这是什么意思呢?

我结合之前提到的DMA_BufferSize并用通俗的话说:

就是我们之前数据打包的单元L:一个数据包的大小,一般整合了两个数据(16位的),之前我们是将其和通道数联系在一起说的。

而这里,我们需要联系外设SRAM的一个数据大小单元来说: 

即是说,我们的数据大小(CDR寄存器)为32位,而外设SRAM的一个数据大小单元为16位

(为什么如此设置,和传输数据呢?)

1.由于我们是双/多ADC模式,数据存储在CDR中,当配置为ADC_DMAAccessMode_2时,即CDR两个数据项满了,触发DMA请求,这样效率高

2.但由于1的存储方式,我们无法判断或者显示出,哪些为ADC1转换的或者其他,因此一般我们在DMA搬运到SRAM中,以两个数据项分开来显示

(在多ADC模式下,第一次的是ADC1和ADC2的数据项,而后面的就不同了,这要更为精妙的程序或者设计来解决,我们这里先不探讨,先提出)

如下图

我们接着上面说:

我们只需要考虑一次转换要传输的数据单元L,当数据不对齐的情况下,要在以上的基础上用考虑一个传输问题,即:外设的一次传输的DMA搬运数据单元L,到SRAM中需要多少个单元(用S来表示)来存储(侧重S<L),或者能够存储多少外设数据单元L(侧重S>L)

并配置下列参数

  //  外设数据大小
    DMA_InitStructure.DMA_PeripheralDataSize = 
  //    存储器数据大小
    DMA_InitStructure.DMA_MemoryDataSize =    

那么,我们又回到了DMA_BufferSize 

其实,我们上述 情况3 DMA_BufferSize是:M*L*N =M*L*(N/L)

其中L是用16位,为一个单元/单位的,不太严谨,在以后我们会碰到8位的,或者其他

(当然这一点再说,可以,但要注意)(这里就以16位单元吧,和之前一样)

我们拓展情况3中的DMA_BufferSize

我们这里也把传输的数据单元LSRAM中需要多少个单元(用S来表示),换算为16位为基本单元

因此在情况4的讨论中

DMA_BufferSize是:M*L*N =M*L*(N/L)*(S/L)

简要说明:

以后对于情况N的讨论还会继续,如若是一个ADC为外部触发(多种情况的触发源),另一个为软件触发或者其他等等等等,这或许会引到其他参数和逻辑执行关系的探讨,

附录和更新补充:

(对于情况3的扩展再论!)

由那个7路通道的文章细想引出


1.若是数据对齐的情况下,双adc模式下,如果是交替转换,数据存储在CDR寄存器中,且触发DMA请求为一个数据项(16位)
那么就不存在,数据错位的情况,因为当最后一个通道转换完成后(一次转换),触发DMA请求,将其搬运到SRAM的数据单元中


2.如果上述改为是触发DMA请求为两个数据项,且还是交替模式,其他不变,
那么我们需要8个的SRAM数据存储单元来存储吗?
这里其实很不对劲,因为最后一个通道转换后,是无法触发DMA请求的,因为才一个数据项
3.最后的计算公式是对的
即:buffer_size为  M*L*(N/L)*(S/L)
4.当DMA将DR或者CDR的数据传输走后,那么对应的DR和CDR位为0吗,还是多少?
5.当通道7的buffer_size为4时,且SRAM数据存储单元为8个,触发DMA请求为两个数据项,那么这不是还是一样有这2的问题存在吗  
6.当通道7的buffer_size为4时,且SRAM数据存储单元为7个,触发DMA请求为两个数据项,就算能够搬运,那这样会出现什么问题呢
(等于说32位的外设数据单元,本来是要搬运到SRAM数据存储单元中的两个,尽管其中一个没有数据——回到4)
7.若为同时转换模式,那就不存在触发DMA请求为一个数据项的情况,除非是最后一个通道转换
那这里还是那个问题——回到2
跟数据对齐好像没有关系了
8.如果为是连续转换,交替转换,触发DMA请求为一个数据项,且我们才存在数据对齐的错位的问题,因为要区分每一轮的数据,因此buffer_size为8
(这里我们需要思考一下,并不能简单的按照计算公式来)
9.若是在8的基础上,改为同时转换,那么我们如何在存储两个数据前,就抢先触发DMA请求
这是如何可能的?

当有一个数据项时,就触发DMA请求

但同时转换模式下,不是直接就生成两个数据项到CDR中了吗

这两者如何可能,如何协同工作呢?

(上述这些问题,待补充和探讨)

  • 43
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现STM32F407的ADC多通道DMA采集需要以下步骤: 1. 配置ADC模块:首先需要配置ADC模块,设置ADC的采样时间、分辨率、转换模式等参数。 2. 配置DMA:开启DMA时钟,并配置DMA通道、数据传输方向、数据长度、传输完成中断等参数。 3. 配置NVIC中断:开启DMA中断,并设置优先级。 4. 启动ADCDMA:使能ADCDMA。 以下是一个示例代码,可以参考实现: ```C #include "stm32f4xx.h" #define ADC1_DR_ADDRESS ((uint32_t)0x4001204C) uint16_t adc_value[3]; void ADC_DMA_Init(void) { ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* ADC1, DMA2 and GPIOA clock enable */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); /* Configure ADC1 Channel7,8,9 pins as analog input -------------------------*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); /* DMA2 Stream0 channel0 configuration ----------------------------------------------*/ DMA_InitStructure.DMA_Channel = DMA_Channel_0; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_ADDRESS; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adc_value; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = 3; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure); DMA_Cmd(DMA2_Stream0, ENABLE); /* ADC Common Init **********************************************************/ ADC_CommonInitTypeDef ADC_CommonInitStructure; ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); /* ADC1 Init ****************************************************************/ ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 3; ADC_Init(ADC1, &ADC_InitStructure); /* ADC1 regular channel7,8,9 configuration **************************************/ ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_84Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 2, ADC_SampleTime_84Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 3, ADC_SampleTime_84Cycles); /* Enable DMA request after last transfer (Single-ADC mode) */ ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); /* Enable ADC1 DMA */ ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); /* Enable DMA Stream Half / Transfer Complete interrupt */ NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* Enable DMA Stream Half / Transfer Complete interrupt */ DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE); /* Start ADC1 Software Conversion */ ADC_SoftwareStartConv(ADC1); } void DMA2_Stream0_IRQHandler(void) { if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); /* do something with the adc_value here */ } if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0)) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0); /* do something with the adc_value here */ } } int main(void) { ADC_DMA_Init(); while(1); } ``` 在这个示例中,我们使用了ADC1和DMA2,并采集了ADC1的7、8、9三个通道的数据。DMA2的Stream0被配置为循环模式,传输完成中断和半传输完成中断都被开启。当传输完成中断或半传输完成中断发生时,程序会调用DMA2_Stream0_IRQHandler函数进行处理。你可以根据自己的需求修改这个示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值