Skocz do zawartości

Kurs STM32 - #8 - DMA, czyli bezpośredni dostęp do pamięci


Pomocna odpowiedź

Chcę móc jak najszybciej odczytać np. 256 (lub więcej) kolejnych wartości z jednego wejścia analogowego i za pomocą DMA (by nie opóźniać kolejnych odczytów) zapisać je do tablicy.

radek04, tematyka FFT nie dotyczy kursu, więc załóż, proszę, nowy wątek. Podpowiem tylko, że to co potrzebujesz to nie tyle szybkie zapisywanie wyników z ADC, ale raczej pobieranie danych w stałych odstępach czasu. Częstotliwość próbkowania to ważny parametr, który musi być stały. Natomiast jego wartość to już decyzja projektowa i "szybko" nie zawsze jest dobrą wartością.

  • Lubię! 1
  • 2 lat(a) później...

Chciałem wykonać zadanie 8.3 z tej części kursu, podłączając 2 potencjometry oraz 1 fotorezystor, z wykorzystaniem DMA. Napisałem poniższy kod, jednak wartości z kanału fotorezystora (ADC_Channel_2), który podłączyłem do Pinu_0 GPIOC nie zmieniają się. Nie mogę namierzyć błędu. 

#include <stdio.h>
#include <stdint.h>
#include "stm32f10x.h"

#define ADC_CHANNELS    3

volatile uint32_t timer_ms = 0;

uint16_t     adc_value[ADC_CHANNELS]; // bufor na odebrane wyniki

void SysTick_Handler()
{
    if (timer_ms)
        timer_ms--;
}

void delay_ms(int time)
{
    timer_ms = time;
    while (timer_ms);
}

void send_char(char c)
{
    while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
    USART_SendData(USART2, c);
}

int __io_putchar(int c)
{
    if (c=='\n')
        send_char('\r');
    send_char(c);
    return c;
}

int main(void)
{
    int i;
    GPIO_InitTypeDef gpio;
    USART_InitTypeDef uart;
    DMA_InitTypeDef dma;
    ADC_InitTypeDef adc;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    RCC_ADCCLKConfig(RCC_PCLK2_Div6); // należy zmniejszyc częstotliwośc pracy zegara
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    GPIO_StructInit(&gpio);
    gpio.GPIO_Pin = GPIO_Pin_5;
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &gpio);

    gpio.GPIO_Pin = GPIO_Pin_2;
    gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &gpio);

    gpio.GPIO_Pin = GPIO_Pin_3;
    gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &gpio);

    USART_StructInit(&uart);
    uart.USART_BaudRate = 9600;
    USART_Init(USART2, &uart);
    USART_Cmd(USART2, ENABLE);

    gpio.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
    gpio.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &gpio);

    gpio.GPIO_Pin = GPIO_Pin_0;
    gpio.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOC, &gpio);

    DMA_StructInit(&dma);
    dma.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; 
    dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
    dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 
    dma.DMA_MemoryBaseAddr = (uint32_t)adc_value;
    dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    dma.DMA_DIR = DMA_DIR_PeripheralSRC;
    dma.DMA_BufferSize = ADC_CHANNELS; 
    dma.DMA_Mode = DMA_Mode_Circular; 
    DMA_Init(DMA1_Channel1, &dma);
    DMA_Cmd(DMA1_Channel1, ENABLE); 

    ADC_StructInit(&adc);
    adc.ADC_ScanConvMode = ENABLE; 
    adc.ADC_ContinuousConvMode = ENABLE; 
    adc.ADC_NbrOfChannel = ADC_CHANNELS; 
    adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // konwersja jest programowa dlatego można wyłączyc
    ADC_Init(ADC1, &adc);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_71Cycles5);

    ADC_DMACmd(ADC1, ENABLE); 
    ADC_Cmd(ADC1, ENABLE);

    // niżej dodatkowa kalibracja i sprawdzenie całgeo przetwornika
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));

    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    SysTick_Config(SystemCoreClock / 1000);

    while (1) {
        for (i = 0; i < ADC_CHANNELS; i++){
            printf("ADC%d = %d   ", i, adc_value);
            if( i == 2) printf("\n");

        }

        GPIO_SetBits(GPIOA, GPIO_Pin_5);
        delay_ms(100);
        GPIO_ResetBits(GPIOA, GPIO_Pin_5);
        delay_ms(400);
    }
}


forbot_1.thumb.png.a39babb0c62a42bc6739f0c40f998d82.png

@Micak zapytam tylko najpierw dla pewności - masz świadomość, że korzystasz ze starej wersji kursu i jest już dostępna nowa (bazująca na bibliotece HAL)? Zestaw elementów nie uległ zmianie, ale pojawił się zupełnie nowy kurs 🙂

@Treker Tak, zdaję sobie z tego sprawę. Ten chciałbym skończyć chyba czystą siłą rozpędu, przy początku nauki obsługi STM'ów błędnie oceniłem i uznałem, tę wersję jako "podstawową" i łatwiejszą. Rozumiem, że nie do końca poprawnie.

Moim celem obecnie jest wykonanie prostej konstrukcji robota typu LineFollow'er. Polecasz zatem, żeby nadrobić zaległości z HAL'a i potem zająć się konstrukcją?

 

20 godzin temu, Micak napisał:

przy początku nauki obsługi STM'ów błędnie oceniłem i uznałem, tę wersję jako "podstawową" i łatwiejszą. Rozumiem, że nie do końca poprawnie.

@Micak kiedyś był taki "trend", że sporo osób zaczynało od metody opisanej w tej kursie, a później przechodziła do HAL-a. Aktualnie HAL to jedyna sensowna opcja (oprócz LL i rejestrów), więc na spokojnie możesz sobie odpuścić ten materiał. Lepiej zająć się tamtym kursem - szczególnie, że sprzęt już masz 🙂

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...