0

I want to continuously read ADC values and write them into an array using the DMA. The board I am using is a Nucleo board with the STM32F767ZI.

To keep stuff like outputting data simple I am using the Arduino IDE with the STM32 board package.

I was able to get the ADC to work in continuous mode, but when I add the DMA it will not work. Only one single value seems to be transferred. The NDTR-register containing the amount of data to be transferred stays at the value I set it to minus one.

Here is the little program:

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;
}

I used ADC+DMA on STM32F3's successfully, but I cannot get it to work on this F7.

The clock for GPIOA gets enabled, and PA3 is set to analog input. The clock for the ADC gets enabled. The ADC is set to continuous mode with DMA mode and continuous DMA requests. The input is PA3. The ADC conversion is started. The DMA stream 4 is set to the correct channel for ADC1 (channel 0). The input and output addresses are set as well as the number of data to transfer and the memory increment mode gets enabled. Then the stream gets enabled.

I am not sure what I step I am missing here.

I would really appreciate your help!

EDIT #2

I accidently mistook channel for stream and so I had the wrong channel selected for the DMA (channel 4 instead of channel 0 for ADC1 in DMA2 Stream 4). That was the main issue why it did not work. Now it is working fine in double buffer mode, except for one thing: When I enable the transfer complete interrupt, the program is no longer working. It is only writing one letter via Serial.print, the "s" from starting. No values are transmitted. I made the interrupt so that it should just disable the DMA for now, but for some reason the interrupt seems to not work at all.

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
  }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Pixel
  • 25
  • 2
  • 8
  • Stop using magic numbers what is 1<<22? – 0___________ Mar 22 '19 at 08:41
  • 1
    Do not read DR register. Is not zero val setting channel 0? Many suspicious values – 0___________ Mar 22 '19 at 08:45
  • Yes, that was the problem! I had it set to channel 4 instead of channel 1! Now it is running continuously in double buffer mode. Thanks alot! There is just one problem remaining: As soon as I enable the transfer complete interrupt, the program is broken again and it only outputs a single character to the serial monitor. What I expect it to do is to do one full DMA transfer and then to disable the DMA and keep printing the same values. – Pixel Mar 22 '19 at 13:30
  • You disabled the DMA but ADC is still running so the overrun happen which suspends everything.. If you check the flags you will know – 0___________ Mar 22 '19 at 19:18
  • In the interrupt I tried to clear the SWSTART and ADON bits, and then disabling the DMA. The program still just stops and outputs nothing more than the letter "s" – Pixel Mar 23 '19 at 21:00

3 Answers3

1

First of all you do not clear the interrupt flag and the interrupt is being called all the time.

Same errors:

ADC1->SQR1 &= ~(0b1111 << ADC_SQR1_L); does not cleat the L in the SQR1 register.

It should be ADC1->SQR1 &= ~(ADC_SQR1_L_Msk << ADC_SQR1_L_Pos);

Same error everywhere: (for example) 0b01 << DMA_SxCR_PSIZE

in my .h file DMA_SxCR_PSIZE is 0x00001800 :)

And many many more :)

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Thank you for the answer. If you are talking about the transfer complete interrupt: It does not get executed even once. It should set the variable dma_active to zero, but that never happens. The array "datapoints" only contains a value from the DMA in the first element, all the other 511 remaining parts contain no value. – Pixel Mar 21 '19 at 12:01
  • If I clear the interrupt flags (DMA2->HIFCR |= 0b111101) in the interrupt it still doesn't work. It does not reach that interrupt because does not do more than one single DMA transfer for some reason. – Pixel Mar 21 '19 at 17:39
  • 1
    @Pixel You have a lots of errors here - see my amended answer. – 0___________ Mar 21 '19 at 19:12
  • Thank you! Looks like I misunderstood these defines. I previously did it with the raw bit values, but even then the DMA did not work. Now I think that the defines are correctly used, but something is still wrong. Although I followed the manual for the DMA and I think all the necessary registers are set for the ADC. – Pixel Mar 22 '19 at 01:49
1

It's due to D and I Cache. Disable it.

Er.Mukesh
  • 54
  • 5
0

Maybe you can put DMA initialization before ADC initialization

emmm
  • 1