STM32F303: АЦП с DMA работает только несколько раз

В настоящее время я работаю с платой Nucleo-64 с чипом STM32F303RE. Для программирования я использую Arduino IDE с пакетами STM32. Я хочу пока избегать HAL, потому что я думаю, что это несколько сбивает с толку, когда вам приходится изучать регистры и функции библиотеки одновременно.

Я хочу иметь возможность сэмплировать 4 входных сигнала параллельно с частотой 5,1 Мбит / с (максимум F303). Мой план состоял в том, чтобы АЦП работал. Затем, когда я хочу взять сэмплы, я сбрасываю флаги DMA и устанавливаю счетчик (CNDTR-Register) равным количеству сэмплов, которые я хочу захватить.

Следующая часть показывает мою попытку достичь этого. Это в основном почти работает, но только ограниченное количество раз. Как часто это работает, кажется, зависит от случайных значений сна, которые я ввожу в определенных частях программы. Например: если я введу задержку в 10 мсек после функции takeSamples(), программа будет работать в течение 41 цикла основного цикла - тогда она застревает.

Когда он застревает, он делает следующее: Регистр DMA-CNDTR уменьшается только на одно значение, затем он просто остается там. Таким образом, программа ожидает, пока значение регистра не станет равным нулю, но этого никогда не происходит. АЦП выполняет выборку все время, я могу читать регистры данных АЦП просто отлично.

У кого-нибудь есть идея, что может привести к тому, что DMA прекратит передачу данных через определенное количество раз?

Вот соответствующие части программы:

void setup() {
  Serial.begin(57600);

  // Enable clocks
  RCC->AHBENR |= (1 << 17); // GPIOA
  RCC->AHBENR |= (1 << 18); // GPIOB

  // Set ADC pins to analog input
  GPIOA->MODER |= (0b11 << 0); // PA0 for ADC1
  GPIOA->MODER |= (0b11 << 8); // PA4 for ADC2
  GPIOB->MODER |= (0b11 << 2); // PB1 for ADC3
  GPIOB->MODER |= (0b11 << 24); // PB1 for ADC4

  initClock();
  DMA_init();
  ADC_init();

  // Start conversion
  ADC1->CR |= (1 << 2);
  ADC3->CR |= (1 << 2);
}

void initClock()
{
  FLASH->ACR |= (0b10 << 0); // add two wait states

  RCC->CR |= (1 << 18); // Bypass HSE, use external clock signal from STLink instead

  RCC->CR &= ~(1 << 24); // turn off PLL
  delay(100);
  RCC->CFGR |= (0b0000 << 4); // Do not divide system clock
  RCC->CFGR |= (0b0111 << 18);  // PLL multiply = 9
  RCC->CFGR |= (0b10 << 15); // use HSE as PLL source
  RCC->CFGR |= (1 << 10);  // not divided
  delay(100);
  RCC->CR |= (1 << 24); // turn on PLL
  delay(100);
}

void ADC_init(void) {

  RCC->CFGR2 |= (0b10000 << 4); // Prescaler
  RCC->CFGR2 |= (0b10000 << 9); // Prescaler
  RCC->AHBENR |= (1 << 28); // turn on ADC12 clock
  RCC->AHBENR |= (1 << 29); // turn on ADC34 clock

  // Set ADC clock
  ADC12_COMMON->CCR |= (0b01 << 16); // 0b01
  ADC34_COMMON->CCR |= (0b01 << 16); // 0b01


  // disable the ADC
  ADC1->CR &= ~(1 << 0);
  ADC2->CR &= ~(1 << 0);
  ADC3->CR &= ~(1 << 0);
  ADC4->CR &= ~(1 << 0);

  // enable the ADC voltage regulator
  ADC1->CR &= ~(1 << 29);
  ADC2->CR &= ~(1 << 29);
  ADC3->CR &= ~(1 << 29);
  ADC4->CR &= ~(1 << 29);

  ADC1->CR |= (1 << 28);
  ADC2->CR |= (1 << 28);
  ADC3->CR |= (1 << 28);
  ADC4->CR |= (1 << 28);

  // start ADC calibration cycle
  ADC1->CR |= (1 << 31);
  // wait for calibration to complete
  while (ADC1->CR & (1 << 31));

  // start ADC calibration cycle
  ADC2->CR |= (1 << 31);
  // wait for calibration to complete
  while (ADC2->CR & (1 << 31));

  // start ADC calibration cycle
  ADC3->CR |= (1 << 31);
  // wait for calibration to complete
  while (ADC3->CR & (1 << 31));

  // start ADC calibration cycle
  ADC4->CR |= (1 << 31);
  // wait for calibration to complete
  while (ADC4->CR & (1 << 31));

  // enable the ADC
  ADC1->CR |= (1 << 0);
  ADC2->CR |= (1 << 0);
  ADC3->CR |= (1 << 0);
  ADC4->CR |= (1 << 0);

  while (!(ADC1->ISR & (1 << 0)));
  while (!(ADC2->ISR & (1 << 0)));
  while (!(ADC3->ISR & (1 << 0)));
  while (!(ADC4->ISR & (1 << 0)));

  // Select ADC Channels
  ADC1->SQR1 = (1 << 6);
  ADC2->SQR1 = (1 << 6);
  ADC3->SQR1 = (1 << 6);
  ADC4->SQR1 = (3 << 6);

  // Set sampling time for regular group 1
  ADC1->SMPR1 |= (0b000 << 3); // 0b000 -> 1.5 clock cycles, shortest available sampling time
  ADC2->SMPR1 |= (0b000 << 3);
  ADC3->SMPR1 |= (0b000 << 3);
  ADC4->SMPR1 |= (0b000 << 3);

  // Regular sequence settings
  ADC1->SQR1 |= (0b0000 << 0); // One conversion in the regular sequence
  ADC2->SQR1 |= (0b0000 << 0);
  ADC3->SQR1 |= (0b0000 << 0);
  ADC4->SQR1 |= (0b0000 << 0);

  // Enable continuous conversion mode
  ADC1->CFGR |= (1 << 13); // Master ADC1 + ADC2
  ADC3->CFGR |= (1 << 13); // Master ADC3 + ADC4

  ADC12_COMMON->CCR |= (0b00110 << 0);
  ADC34_COMMON->CCR |= (0b00110 << 0);

  // DMA mode
  ADC12_COMMON->CCR |= (0 << 13); // 0 -> One Shot; 1 -> Circular
  ADC34_COMMON->CCR |= (0 << 13);

  // DMA mode for 12-bit resolution
  ADC12_COMMON->CCR |= (0b10 << 14);
  ADC34_COMMON->CCR |= (0b10 << 14);
}

void DMA_init(void) {

  // Enable clocks
  RCC->AHBENR |= (1 << 0); // DMA1
  RCC->AHBENR |= (1 << 1); // DMA2

  // Transfer complete interrupt enable
  DMA1_Channel1->CCR |= (1 << 1);
  DMA2_Channel5->CCR |= (1 << 1);

  // Memory increment mode
  DMA1_Channel1->CCR |= (1 << 7);
  DMA2_Channel5->CCR |= (1 << 7);

  // Peripheral size
  DMA1_Channel1->CCR |= (0b11 << 8);
  DMA2_Channel5->CCR |= (0b11 << 8);

  // Memory size
  DMA1_Channel1->CCR |= (0b11 << 10);
  DMA2_Channel5->CCR |= (0b11 << 10);

  // Number of data to transfer
  DMA1_Channel1->CNDTR = uint32_t(maxSamples);
  DMA2_Channel5->CNDTR = uint32_t(maxSamples);

  // Peripheral address register
  DMA1_Channel1->CPAR |= (uint32_t)&ADC12_COMMON->CDR;
  DMA2_Channel5->CPAR |= (uint32_t)&ADC34_COMMON->CDR;

  // Memory address register
  DMA1_Channel1->CMAR |= uint32_t(&dataPoints1232);
  DMA2_Channel5->CMAR |= uint32_t(&dataPoints3432);

  // Reset flags
  DMA1->IFCR |= 0xFF;
  DMA2->IFCR |= 0xFF;
}

void takeSamples(void) {

  // Reset flags
  DMA1->IFCR |= (0b1111111111111111111111111111111 << 0);
  DMA2->IFCR |= (0b1111111111111111111111111111111 << 0);

  // Number of data to transfer
  DMA1_Channel1->CNDTR = uint32_t(maxSamples);
  DMA2_Channel5->CNDTR = uint32_t(maxSamples);

  delay(10); // does not work without this random delay

  elapsedTime = micros();
  // Enable DMA
  DMA1_Channel1->CCR |= (1 << 0);
  DMA2_Channel5->CCR |= (1 << 0);

  while ((DMA1_Channel1->CNDTR > 0) || (DMA2_Channel5->CNDTR > 0))
  }

  elapsedTime = micros() - elapsedTime;

  // Reset flags
  DMA1->IFCR |= (0b1111111111111111111111111111111 << 0);
  DMA2->IFCR |= (0b1111111111111111111111111111111 << 0);;

  DMA1_Channel1->CCR &= ~(1 << 0);
  DMA2_Channel5->CCR &= ~(1 << 0);

  // ADC stop conversion
  ADC1->CR |= (1 << 4);
  ADC3->CR |= (1 << 4);

  while ((ADC1->CR & (1 << 2)) || (ADC3->CR & (1 << 2)));

  ADC12_COMMON->CCR &= ~(0b10 << 14);
  ADC34_COMMON->CCR &= ~(0b10 << 14);

  ADC12_COMMON->CCR |= (0b10 << 14);
  ADC34_COMMON->CCR |= (0b10 << 14);

  // ADC start conversion
  ADC1->CR |= (1 << 2);
  ADC3->CR |= (1 << 2);
}

void loop() {
  takeSamples();
  Serial.print("Elapsed time: ");
  Serial.println(elapsedTime);
}

Буду очень признателен за любые советы или подсказки по этому вопросу!

Привет Бенни

РЕДАКТИРОВАТЬ: У меня также была та же проблема с ядро-64 с чипом STM32F401. С другой стороны, STM32F4 Discovery работал просто отлично. Не было такой проблемы с моей F103, а также с контроллером полета.

1 ответ

В качестве примера что-то простое с таймером запускает преобразования.

void ReadChannels(int channel, size_t nsamples, uint8_t *obuff) 
 {
     TIM1 -> CR1 = 0;
     TIM1 -> CR2 = 0;
     TIM1 -> PSC = PSC;
     TIM1 -> ARR = ARR;
     TIM1 -> EGR |= TIM_EGR_UG;

     DMA1_Channel1 -> CPAR = (uint32_t)&(ADC1 -> DR);
     DMA1_Channel1 -> CMAR = (uint32_t)obuff;
     DMA1_Channel1 -> CNDTR = nsamples;
     DMA1_Channel1 -> CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_EN;

     ADC1 -> CFGR = ADC_CFGR_DMAEN | (0b10 << ADC_CFGR_RES_Pos) | (9 << ADC_CFGR_EXTSEL_Pos) | (0b01 << ADC_CFGR_EXTEN_Pos);
     ADC1 -> SMPR1 = 0;
     ADC1 -> SMPR2 = 0;

     ADC1 -> SQR1 &= ~(ADC_SQR1_L_Msk);
     ADC1 -> SQR1 &= ~(ADC_SQR1_SQ1_Msk);

     ADC1 -> SQR1 |= channel << ADC_SQR1_SQ1_Pos);
     ADC1 -> CR |= ADC_CR_ADSTART;

     TIM1 -> CR2 |= TIM_CR2_MMS_1;
     TIM1 -> CR1 |= TIM_CR1_CEN;

     DMA1_Channel1 -> CCR = 0;
     TIM1 -> CR1 = 0;
 }
Другие вопросы по тегам