STM32F7:ADC DMA 传输只工作一次

Posted

技术标签:

【中文标题】STM32F7:ADC DMA 传输只工作一次【英文标题】:STM32F7: ADC DMA transfer only works once 【发布时间】:2019-08-11 19:12:06 【问题描述】:

我想连续读取 ADC 值并使用 DMA 将它们写入数组。我使用的开发板是带有 STM32F767ZI 的 Nucleo 开发板。

为了让输出数据等操作简单,我将 Arduino IDE 与 STM32 板包一起使用。

我能够让 ADC 在连续模式下工作,但是当我添加 DMA 时它就无法工作了。似乎只转移了一个值。包含要传输的数据量的 NDTR 寄存器保持在我设置为负一的值。

这是小程序:

volatile static bool dma_active = 1;
#define maxSamples 512
int16_t dataPoints[maxSamples];

void setup() 
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // GPIOA clock enable
  GPIOA->MODER |= (0b11 << 6); // PA3 as analog input
  Serial.begin(115200);
  Serial.println("starting");  
  initADC();
  initDMA();


void initADC() 
  RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;     // ADC1 clock enable
  ADC1->CR2 |= ADC_CR2_ADON;              //  ADC on
  ADC1->CR2 |= ADC_CR2_CONT;              //  continuous conversion mode
  ADC1->CR1 |= ADC_CR1_EOCIE;             // EOC interrupt
  ADC1->CR1 &= ~ADC_CR1_DISCEN;           // discontinuous mode disabled

  ADC1->CR1 &= ~ADC_CR1_SCAN;             // scan mode disabled
  ADC1->CR2 |= ADC_CR2_DMA;               // DMA mode
  ADC1->CR2 |= ADC_CR2_DDS;               // continuous DMA request
  ADC1->SQR3 |= 0b11;                     // ADC1_IN3 = PA3
  ADC1->SQR1 &= ~(0b1111 << ADC_SQR1_L);  // 1 conversion
  ADC1->CR2 |= ADC_CR2_SWSTART;           // Start conversion


void initDMA() 
  // DMA2 Stream4 : Channel 0 is ADC1
  RCC->AHB1ENR |= (1 << 22);                        // DMA2 clock enable
  DMA2_Stream4->CR &= ~DMA_SxCR_EN; // Disable
  while (DMA2_Stream4->CR & (1 << 0));
  DMA2_Stream4->CR |= (0b0100 << DMA_SxCR_CHSEL);   // Channel 4
  DMA2_Stream4->CR |= (0b11 << DMA_SxCR_PL);        // Very high priority
  DMA2_Stream4->PAR = (uint32_t)&ADC1->DR;          // Data source register
  DMA2_Stream4->M0AR = uint32_t(&dataPoints);      // Buffer 1
//  DMA2_Stream4->M1AR = uint32_t(&dataPoints1);      // Buffer 2
  DMA2_Stream4->NDTR = maxSamples;                  // Number of transferred data
  DMA2_Stream4->CR |= (0b01 << DMA_SxCR_PSIZE);     // Source data size (00 = byte, 01 = half word, 10 = word)
  DMA2_Stream4->CR |= (0b01 << DMA_SxCR_MSIZE);     // Memory data size (00 = byte, 01 = half word, 10 = word)
  DMA2_Stream4->CR |= DMA_SxCR_TCIE;                // Transfer complete interrupt enable
  DMA2_Stream4->CR |= DMA_SxCR_CIRC;                // circular mode
  DMA2_Stream4->CR &= ~DMA_SxCR_PINC;               // no peripheral increment mode
  DMA2_Stream4->CR |= DMA_SxCR_MINC;                // memory increment mode
//  DMA2_Stream4->CR |= DMA_SxCR_DBM;               // double buffer mode
  DMA2->HIFCR |= 0b111101;                         // clear flags
  NVIC_EnableIRQ(DMA2_Stream4_IRQn);
  delay(20);
  DMA2_Stream4->CR |= DMA_SxCR_EN;                  // Enable


void loop() 
  Serial.print(ADC1->DR);
  Serial.print("  ");
  Serial.print(dataPoints[0]);
  Serial.print("  ");
  Serial.print(dma_active);
  Serial.print("  ");
  Serial.println(DMA2_Stream4->NDTR);
  delay(100);
 

 

void DMA2_Stream4_IRQHandler(void) 
  dma_active = 0;

我在 STM32F3 上成功使用了 ADC+DMA,但我无法让它在这个 F7 上工作。

GPIOA 的时钟启用,PA3 设置为模拟输入。 ADC 的时钟被启用。 ADC 设置为具有 DMA 模式和连续 DMA 请求的连续模式。输入为 PA3。 ADC 转换开始。 DMA 流 4 设置为 ADC1 的正确通道(通道 0)。设置输入和输出地址以及要传输的数据数量,并启用内存增量模式。然后流被启用。

我不确定我在这里缺少什么。

非常感谢您的帮助!

编辑#2

我不小心将通道误认为是流,因此我为 DMA 选择了错误的通道(通道 4 而不是通道 0 用于 DMA2 流 4 中的 ADC1)。这就是为什么它不起作用的主要问题。 现在它在双缓冲模式下工作正常,除了一件事: 当我启用传输完成中断时,程序不再工作。它只是通过 Serial.print 写一个字母,从开头的“s”。不传输任何值。 我做了中断,所以它现在应该只禁用 DMA,但由于某种原因,中断似乎根本不起作用。

volatile static bool dma_active = 1;
#define maxSamples 512
int16_t dataPoints[maxSamples];
int16_t dataPoints2[maxSamples];
void setup() 
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN_Msk; // GPIOA clock enable
  GPIOA->MODER |= (0b11 << GPIO_MODER_MODER3_Pos); // PA3 as analog input
  Serial.begin(115200);
  Serial.println("starting");
   initDMA(); 
 initADC();


void initADC() 
  RCC->APB2ENR |= RCC_APB2ENR_ADC1EN_Msk;     // ADC1 clock enable
  ADC1->CR2 |= ADC_CR2_ADON_Msk;              //  ADC on
  ADC1->CR2 |= ADC_CR2_CONT_Msk;              //  continuous conversion mode
  ADC1->CR1 |= ADC_CR1_EOCIE_Msk;             // EOC interrupt
  ADC1->CR1 &= ~(ADC_CR1_DISCEN_Msk);           // discontinuous mode disabled

  ADC1->CR1 &= ~(ADC_CR1_SCAN_Msk);             // scan mode disabled
  ADC1->CR2 |= ADC_CR2_DMA_Msk;               // DMA mode
  ADC1->CR2 |= ADC_CR2_DDS_Msk;               // continuous DMA request
  ADC1->SQR3 |= 0b11;                     // ADC1_IN3 = PA3
  ADC1->SQR1 &= ~(0b1111 << ADC_SQR1_L_Pos);  // 1 conversion
  ADC1->CR2 |= ADC_CR2_SWSTART_Msk;           // Start conversion


void initDMA() 
  // DMA2 Stream4 : Channel 0 is ADC1
  RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN_Msk;                            // DMA2 clock enable
  DMA2_Stream4->CR &= ~(DMA_SxCR_EN_Msk);               // Disable
  while (DMA2_Stream4->CR & (1 << 0));
  DMA2->HIFCR |= 0b111101;                              // clear flags
  DMA2_Stream4->CR |= (0b11 << DMA_SxCR_PL_Pos);        // Very high priority
  DMA2_Stream4->PAR = (uint32_t)&(ADC1->DR);              // Data source register
  DMA2_Stream4->M0AR = uint32_t(&dataPoints);           // Buffer 1
  DMA2_Stream4->M1AR = uint32_t(&dataPoints2);           // Buffer 2
  DMA2_Stream4->NDTR = maxSamples;                      // Number of transferred data
  DMA2_Stream4->CR &= ~(0b1111 << DMA_SxCR_CHSEL_Pos);   // Channel 4
  DMA2_Stream4->CR |= (0b01 << DMA_SxCR_PSIZE_Pos);     // Source data size (00 = byte, 01 = half word, 10 = word)
  DMA2_Stream4->CR |= (0b01 << DMA_SxCR_MSIZE_Pos);     // Memory data size (00 = byte, 01 = half word, 10 = word)
//  DMA2_Stream4->CR |= DMA_SxCR_TCIE_Msk;                // Transfer complete interrupt enable
//  DMA2_Stream4->CR |= DMA_SxCR_CIRC_Msk;                // circular mode
  DMA2_Stream4->CR |= DMA_SxCR_DBM_Msk;                // double buffer mode
  DMA2_Stream4->CR &= ~(DMA_SxCR_PINC_Msk);             // no peripheral increment mode
  DMA2_Stream4->CR |= DMA_SxCR_MINC_Msk;                // memory increment mode

  NVIC_EnableIRQ(DMA2_Stream4_IRQn);
  DMA2_Stream4->CR |= DMA_SxCR_EN_Msk;                  // Enable


void loop() 
  for (int i = 0; i < maxSamples; i++)
  
    Serial.print(dataPoints[i]);
    Serial.print("  ");
 //   Serial.print(dataPoints2[i]);
    Serial.print("  ");
    Serial.print(dma_active);
    Serial.println("");
  
  delay(2000);


void DMA2_Stream4_IRQHandler(void) 
  if ((DMA2->HISR) & DMA_HISR_TCIF4_Msk)
  
    DMA2_Stream4->CR &= ~DMA_SxCR_EN_Msk;                  // Disable
    dma_active = 0;
    DMA2->HIFCR |= 0b111101;                         // clear flags
  

【问题讨论】:

停止使用幻数什么是 1 不读取 DR 寄存器。零值不是设置通道0吗?许多可疑值 是的,这就是问题所在!我将其设置为频道 4 而不是频道 1!现在它以双缓冲模式连续运行。非常感谢!剩下的只有一个问题:一旦我启用传输完成中断,程序就会再次中断,它只向串行监视器输出一个字符。我期望它做的是进行一次完整的 DMA 传输,然后禁用 DMA 并继续打印相同的值。 您禁用了 DMA,但 ADC 仍在运行,因此发生溢出,这会暂停一切。如果您检查标志,您就会知道 在中断中我尝试清除 SWSTART 和 ADON 位,然后禁用 DMA。程序仍然只是停止并只输出字母“s” 【参考方案1】:

这是由于 D 和 I 缓存。禁用它。

【讨论】:

【参考方案2】:

首先,您没有清除中断标志,并且一直在调用中断。

同样的错误:

ADC1-&gt;SQR1 &amp;= ~(0b1111 &lt;&lt; ADC_SQR1_L); 不清除 SQR1 寄存器中的 L。

应该是ADC1-&gt;SQR1 &amp;= ~(ADC_SQR1_L_Msk &lt;&lt; ADC_SQR1_L_Pos);

到处都是同样的错误:(例如) 0b01 &lt;&lt; DMA_SxCR_PSIZE

在我的 .h 文件中,DMA_SxCR_PSIZE 是 0x00001800 :)

还有更多:)

【讨论】:

感谢您的回答。如果您正在谈论传输完成中断:它甚至不会执行一次。它应该将变量 dma_active 设置为零,但这永远不会发生。数组“datapoints”仅在第一个元素中包含来自 DMA 的值,其余 511 个其余部分均不包含任何值。 如果我清除中断中的中断标志 (DMA2->HIFCR |= 0b111101),它仍然不起作用。它没有到达那个中断,因为出于某种原因,它只进行一次 DMA 传输。 @Pixel 您这里有很多错误 - 请参阅我修改后的答案。 谢谢!看起来我误解了这些定义。我以前用原始位值做过,但即使这样 DMA 也不起作用。现在我认为定义使用正确,但仍然有问题。虽然我遵循了 DMA 手册,但我认为所有必要的寄存器都已为 ADC 设置。

以上是关于STM32F7:ADC DMA 传输只工作一次的主要内容,如果未能解决你的问题,请参考以下文章

STM32F4 ADC模块使用不同DMA模式的区别和对比

带有 DMA 7 通道常规组的 STM32F4 ADC 不工作

带有 STM32F7 的 SPI 从模式和循环 DMA

stm32f4 DMA 在挂起后并不总是启动

STM32 ADC DMA。何时通知 MCU ADC 结束?

STM32 ADC_DMA_UART 数据传输