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