2017-09-18 11 views
2

Ich arbeite derzeit mit einer Nucleo-64-Platine, die den STM32F303RE-Chip hat. Für die Programmierung verwende ich die Arduino IDE mit den STM32-Paketen. Ich möchte HAL für jetzt vermeiden, weil ich denke, dass es ziemlich verwirrend ist, wenn Sie die Register und die Bibliotheksfunktionen gleichzeitig lernen müssen.STM32F303: ADC mit DMA funktioniert nur ein paar Mal

Ich möchte 4 Eingangssignale parallel mit 5,1 Msps (max der F303) probieren können. Mein Plan war, den ADC am Laufen zu halten. Dann, wenn ich die Samples nehmen möchte, setze ich die DMA-Flags zurück und setze den Zähler (CNDTR-Register) auf die Anzahl der Samples, die ich erfassen möchte.

Der folgende Teil zeigt meinen Versuch, dies zu erreichen. Es funktioniert im Prinzip fast, aber nur in begrenztem Umfang. Wie oft es funktioniert, scheint von zufälligen Schlafwerten abhängig zu sein, die ich an bestimmten Stellen im Programm betrete. Zum Beispiel: Wenn ich eine Verzögerung von 10ms nach der takeSamples() - Funktion einstelle, arbeitet das Programm für 41 Zyklen der Hauptschleife - dann bleibt es stecken.

Wenn es klemmt, macht es Folgendes: Das DMA-CNDTR-Register wird nur um einen Wert reduziert, dann bleibt es nur dort. Das Programm wartet also darauf, dass der Registerwert gleich Null wird, aber das passiert nie. Der ADC tastet die ganze Zeit ab, ich kann die ADC-Datenregister gut lesen.

Hat jemand eine Idee, was könnte dazu führen, dass der DMA die Übertragung von Daten nach einer bestimmten Anzahl von Zeiten zu stoppen?

Hier sind die relevanten Teile des Programms:

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

Ich würde für alle Tipps oder Hinweise zu diesem Thema wirklich dankbar sein!

Grüße Benny

EDIT: Ich hatte auch das gleiche Problem mit der Nucleo-64 mit dem STM32F401 Chip. Die STM32F4 Discovery funktionierte dagegen gut. So etwas gab es auch mit meinem F103 FlightController-Board nicht.

+0

1. Beginnen Sie zuerst, die vom Menschen lesbaren Werte zu verwenden. –

+0

2. Geben Sie die arduino IDE auf und starten Sie etwas Anständiges - zum Beispiel eclipse mit installiertem openSTM32. –

+0

3. Verwende CMSIS Definitionen –

Antwort

2

Als Beispiel etwas einfaches mit dem Timer trigered Konvertierungen.

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

Vielen Dank für deine Antwort! Die Verwendung der Definitionen würde das Programm viel lesbarer machen. Vielleicht kann das Problem von dem Code selbst in eine andere Frage getrennt werden: Was könnte den ADC + DMA für eine Weile arbeiten lassen, aber dann, nach einer bestimmten Anzahl von vollen DMA-Zyklen, stoppt der DMA die Datenübertragung? Die Ammount ist irgendwie abhängig von Schlafintervallen, die ich an verschiedenen Stellen des Programms einfüge. Vielleicht ein Synchronisationsproblem mit anderen Uhren? – Pixel

+0

Die Frage ist komplex und kann nicht allgemein beantwortet werden. Sie müssen Ihr Programm debuggen, wenn das Problem auftritt, und die Werte der DMA-, ADC- und TIM-Register prüfen, um den tatsächlichen Status und die Fehler zu sehen. –

Verwandte Themen