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->SQR1 &= ~(0b1111 << ADC_SQR1_L);
不清除 SQR1 寄存器中的 L。
应该是ADC1->SQR1 &= ~(ADC_SQR1_L_Msk << ADC_SQR1_L_Pos);
到处都是同样的错误:(例如)
0b01 << DMA_SxCR_PSIZE
在我的 .h 文件中,DMA_SxCR_PSIZE 是 0x00001800 :)
还有更多:)
【讨论】:
感谢您的回答。如果您正在谈论传输完成中断:它甚至不会执行一次。它应该将变量 dma_active 设置为零,但这永远不会发生。数组“datapoints”仅在第一个元素中包含来自 DMA 的值,其余 511 个其余部分均不包含任何值。 如果我清除中断中的中断标志 (DMA2->HIFCR |= 0b111101),它仍然不起作用。它没有到达那个中断,因为出于某种原因,它只进行一次 DMA 传输。 @Pixel 您这里有很多错误 - 请参阅我修改后的答案。 谢谢!看起来我误解了这些定义。我以前用原始位值做过,但即使这样 DMA 也不起作用。现在我认为定义使用正确,但仍然有问题。虽然我遵循了 DMA 手册,但我认为所有必要的寄存器都已为 ADC 设置。以上是关于STM32F7:ADC DMA 传输只工作一次的主要内容,如果未能解决你的问题,请参考以下文章
带有 DMA 7 通道常规组的 STM32F4 ADC 不工作