关于ADC_DMAAccessMode,DMA_BufferSize

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

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

DMA_BufferSize

  /*------------------------- DMAy Streamx NDTR Configuration ----------------*/
  /* Write to DMAy Streamx NDTR register */
  DMAy_Streamx->NDTR = DMA_InitStruct->DMA_BufferSize;

 关于这个参数的理解,我们还需要阅读参考手册的对于章节

 1.

9.3.2 DMA 事务
DMA 事务由给定数目的数据传输序列组成。要传输的数据项的数目及其宽度(8 位、16 位
或 32 位)可用软件编程。
每个 DMA 传输包含三项操作:
● 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,从外设数据寄存器或存储器单元中
加载数据。
● 通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,将加载的数据存储到外设数据寄存
器或存储器单元。
● DMA_SxNDTR 计数器在数据存储结束后递减,该计数器中包含仍需执行的事务数。

在产生事件后,外设会向 DMA 控制器发送请求信号。DMA 控制器根据通道优先级处理该请
求。只要 DMA 控制器访问外设,DMA 控制器就会向外设发送确认信号。外设获得 DMA 控
制器的确认信号后,便会立即释放其请求。一旦外设使请求失效,DMA 控制器就会释放确
认信号。如果有更多请求,外设可以启动下一个事务。

我们主要关注其手册的红色说明

2.

如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或
DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。

3.

上述三个是关于DMA_BufferSize的具体在使用中的说明

我们会发现:什么叫做事务数?DMA请求搬运和这个又有什么关系呢?

DMA 事务由给定数目的数据传输序列组成。

而这个与DMA请求搬运有什么关系呢?

我们先举个例子,

  // 外设数据大小为半字,即两个字节 
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  //同外设数据大小一样
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

上述的配置,为需要搬运的数据和搬运到的每一个存储空间大小一样的前提下

需求/逻辑关系1:

当ADC的规则组通道数为一个时,若是由外部触发来使ADC开始转换,而后数据存储到DR寄存器中(规则组的寄存器有且只有一个),我们需要DMA来搬运每次由外部触发得到的ADC转换的值,并显示.(大致是这个意思,具体配置先不用管)

而这里DMA的请求,目的是防止每次外部触发的ADC转换的值,被覆盖

这里每次的DMA请求为一次,DMA搬运的总工作量为多少呢?

答案是:1

而我们这里的DMA 事务指的就是每次的DMA请求为一次,DMA搬运的总工作量

这里还不能很好的体现出DMA事务和DMA搬运的关系

需求/逻辑关系2:

我们在以上  需求/逻辑关系1  的基础追加一个规则通道,其他不变

这里也需要高效的DMA来协助ADC转换工作,防止数据被覆盖

也即:

通道1,转换完成后,数据存储到DR寄存器中,我们理应需要一次DMA请求

通道2,转换完成后,数据存储到DR寄存器中,我们理应也需要一次DMA请求

这里,我们先回顾一下:前面的需求/逻辑关系1

得知:我们是外部触发ADC转换完,而ADC才触发DMA请求

那么,视乎和上述我们需求/逻辑关系2 中的需要不符合

我们应该是

通道1——》DMA请求

通道2——》DMA请求

难道我们是需要通过标志位来检测每一个通道的转换情况,而进行DMA请求吗,

这在功能实现方面可能吗——是可能的

不过这ADC转换的速度是极高的,且每次的DMA请求又是效率极低的方式

 因此:引出,我们上述的DMA 事务指的就是每次的DMA请求为一次,DMA搬运的总工作量

这里ADC请求的情况是 外设发送DMA请求,事先配置好的 DMA_SxNDTR,DMA开始搬运工作,当一次DMA搬运数据结束后, DMA_SxNDTR递减1,如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止

这里的需求就是:DMA_BufferSize配置为2,一次DMA请求,事务为2,即进行两次的DMA搬运工作。

而后,在写这个的过程中,我们又有了一个顾虑:

就是外部触发ADC开始转换,而后扫描通道2,转换完成才进行DMA搬运,那数据不是早就覆盖了吗,即使上述的事务数为2,但同时实验现象又是对的,这是为什么?

我们从下述找到了依据:

/**
  * @brief  Enables or disables the ADC DMA request after last transfer (Single-ADC mode)  
  * @param  ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
  * @param  NewState: new state of the selected ADC DMA request after last transfer.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void ADC_DMARequestAfterLastTransferCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_ADC_ALL_PERIPH(ADCx));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    /* Enable the selected ADC DMA request after last transfer */
    ADCx->CR2 |= (uint32_t)ADC_CR2_DDS;
  }
  else
  {
    /* Disable the selected ADC DMA request after last transfer */
    ADCx->CR2 &= (uint32_t)(~ADC_CR2_DDS);
  }
}

查看参考手册:

以上,我们知道了我们使能了DMA请求,只要配置了事务数,而配置了DDS这个位

则会来进行不断地检测工作,只要发生数据转换且 DMA = 1,便会发出 DAM 请求

这里,我们也就是说实现了

通道1——》DMA请求

通道2——》DMA请求

但不是我们手动去实现的,是通过DDS位来实现的

那么结合上述的,我们尝试这描述一下上述的过程:

DMA_BufferSize为 DMA 事务由给定数目的数据传输序列组成,也即某一个外设通过通道优先级获得了对于DMA总线请求的占用(DMA也是一个总线结构,有点类似I2C硬件结构),而执行DMA事务,每进行一次ADC通道的DMA传输数据后,DMA事务/DMA_BufferSize/DMA_SxNDTR 计数器递减,若有更多的请求(DMA_SxNDTR 计数器不为0)外设可以启动下一个事务,也即下一次的DMA控制器的请求信号,直至DMA_SxNDTR 计数器为0,该外设释放DMA控制总线。

  ADC_DMAAccessMode

ADC_DMAAccessMode和DMA_BufferSize 还有数据大小的关系有关,下期再说

  • 44
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值