Skocz do zawartości

Kurs STM32 F1 HAL - #8 - bezpośredni dostęp do pamięci


Pomocna odpowiedź

html_mig_img
Podczas 6 części kursu poznaliśmy możliwości przetwornika ADC. Uruchamiane przykłady były jednak pod pewnym względem niedoskonałe.Dziś poznamy nową, efektywną metodę. Zamiast aktywnie czekać na odczyt, wykorzystamy moduł DMA, który będzie robił to w tle.

UWAGA, to tylko wstęp! Dalsza część artykułu dostępna jest na blogu.

Przeczytaj całość »

Poniżej znajdują się komentarze powiązane z tym wpisem.

Link do komentarza
Share on other sites

Dzień dobry

1) Z jaką częstotliwością pracuje DMA tzn jak szybko jest odświeżana zawartość pamięci w przypadku kopiowania z ADC do pamięci?

2)Jak rozwiązano problem jednoczesnego dostępu do komórek pamięci przez DMA i procedurę?

Link do komentarza
Share on other sites

To jak często są wykonywane odczyty z przetwornika ustala konfiguracja ADC - samo DMA ma tutaj niewiele do powiedzenia. Po pobraniu kolejnej próbki moduł generuje zdarzenie, w wyniku którego DMA odczytuje dane z rejestru i zapisuje w pamięci. Częstotliwość próbkowania można obliczyć znając częstotliwość zegara ADC, czas samplowania oraz przetwarzania danych.

Natomiast jak chodzi o jednoczesny dostęp to opisywany przykład jest faktycznie mocno uproszczony. Typowe zastosowania DMA wykorzystują raczej dwa bufory - jeden do zbierania danych z przetwornika oraz drugi, w którym są dane gotowe dla CPU. Chcieliśmy jednak uprościć opisywane przykłady, więc po prostu CPU odczytuje dane zapisywane przez DMA.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

W zadaniu gdzie kopiowane są dane z pamięci do pamięci można jeszcze przyspieszyć cały proces 4-krotnie przez kopiowanie bloków po 4 bajty jednocześnie (DMA_PDATAALIGN_WORD zamiast DMA_PDATAALIGN_BYTE). Trzeba tylko pamiętać żeby zmieną BUFFER_SIZE podzielić przez 4 (przesunąć logicznie o 2 bity w prawo).

Fragmenty kodu przed zmianą:

void copy_dma()
{
    HAL_DMA_Start(&dma, (uint32_t)src_buffer, (uint32_t)dst_buffer, BUFFER_SIZE);
    HAL_DMA_PollForTransfer(&dma, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
}

    dma.Instance = DMA1_Channel1;
    dma.Init.Direction = DMA_MEMORY_TO_MEMORY;
    dma.Init.PeriphInc = DMA_PINC_ENABLE;
    dma.Init.MemInc = DMA_MINC_ENABLE;
    dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    dma.Init.MemDataAlignment = DMA_PDATAALIGN_BYTE;
    dma.Init.Mode = DMA_NORMAL;
    dma.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&dma);

Fragmenty kodu po zmianie:

void copy_DMA(void) {
	HAL_DMA_Start(&dma, (uint32_t)src_buffer, (uint32_t)dst_buffer, (BUFFER_SIZE >> 2));
	HAL_DMA_PollForTransfer(&dma, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);
}

	dma.Instance = DMA1_Channel1;
	dma.Init.Mode = DMA_NORMAL;
	dma.Init.Direction = DMA_MEMORY_TO_MEMORY;
	dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	dma.Init.MemInc = DMA_MINC_ENABLE;
	dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	dma.Init.PeriphInc = DMA_PINC_ENABLE;
	dma.Init.Priority = DMA_PRIORITY_HIGH;
	HAL_DMA_Init(&dma);

Mam nadzieję że komuś się ta informacja przyda.

Pozdrawiam

  • Lubię! 1
Link do komentarza
Share on other sites

Witam,

mam pytanie odnośnie zadania 8.2. Nie jestem w stanie stwierdzić co robię źle, dodatkowe dwa wejścia na przetwornik nie działają, chociaż wydaje mi się, że zostały skonfigurowane poprawnie. Mógłby mnie ktoś naprowadzić na błąd? Wstawiam kod z komentarzami co dodałem

#include "stm32f1xx.h"
#define ADC_CHANNELS 4 //jasna sprawa, bo teraz chcemy mieć 4 kanały
uint16_t adc_value[ADC_CHANNELS];

UART_HandleTypeDef uart;
ADC_HandleTypeDef adc;
DMA_HandleTypeDef dma;

void send_char(char c)
{
    HAL_UART_Transmit(&uart, (uint8_t*)&c, 1, 1000);
}

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

int main(void)
{
    SystemCoreClock = 8000000;
    HAL_Init();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();
    __HAL_RCC_ADC1_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
   
    GPIO_InitTypeDef gpio;
    gpio.Mode = GPIO_MODE_AF_PP;
    gpio.Pin = GPIO_PIN_2;
    gpio.Pull = GPIO_NOPULL;
    gpio.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &gpio);

    gpio.Mode = GPIO_MODE_AF_INPUT;
    gpio.Pin = GPIO_PIN_3;
    HAL_GPIO_Init(GPIOA, &gpio);

    gpio.Mode = GPIO_MODE_OUTPUT_PP;
    gpio.Pin = GPIO_PIN_5;
    HAL_GPIO_Init(GPIOA, &gpio);

    gpio.Mode = GPIO_MODE_ANALOG;
    gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4; //pin PA4 jako wejście przetwornika (A2)
    HAL_GPIO_Init(GPIOA, &gpio);

    gpio.Mode = GPIO_MODE_ANALOG;
	gpio.Pin = GPIO_PIN_0;
	HAL_GPIO_Init(GPIOB, &gpio); //pin PB0 jako wejście przetwornika (A3)

    uart.Instance = USART2;
    uart.Init.BaudRate = 115200;
    uart.Init.WordLength = UART_WORDLENGTH_8B;
    uart.Init.Parity = UART_PARITY_NONE;
    uart.Init.StopBits = UART_STOPBITS_1;
    uart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    uart.Init.OverSampling = UART_OVERSAMPLING_16;
    uart.Init.Mode = UART_MODE_TX_RX;
    HAL_UART_Init(&uart);

    RCC_PeriphCLKInitTypeDef adc_clk;
    adc_clk.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    adc_clk.AdcClockSelection = RCC_ADCPCLK2_DIV2;
    HAL_RCCEx_PeriphCLKConfig(&adc_clk);

    adc.Instance = ADC1;
    adc.Init.ContinuousConvMode = ENABLE;
    adc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    adc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    adc.Init.ScanConvMode = ADC_SCAN_ENABLE;
    adc.Init.NbrOfConversion = ADC_CHANNELS;
    adc.Init.DiscontinuousConvMode = DISABLE;
    adc.Init.NbrOfDiscConversion = 1;
    HAL_ADC_Init(&adc);

    ADC_ChannelConfTypeDef adc_ch;
    adc_ch.Channel = ADC_CHANNEL_0;
    adc_ch.Rank = ADC_REGULAR_RANK_1;
    adc_ch.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
    HAL_ADC_ConfigChannel(&adc, &adc_ch);

    adc_ch.Channel = ADC_CHANNEL_1;
    adc_ch.Rank = ADC_REGULAR_RANK_2;
    HAL_ADC_ConfigChannel(&adc, &adc_ch);
	//dodajemy jeszcze dwa kanały
    adc_ch.Channel = ADC_CHANNEL_2; 
    adc_ch.Rank = ADC_REGULAR_RANK_3;
    HAL_ADC_ConfigChannel(&adc, &adc_ch);

    adc_ch.Channel = ADC_CHANNEL_3;
    adc_ch.Rank = ADC_REGULAR_RANK_4;
    HAL_ADC_ConfigChannel(&adc, &adc_ch);
    HAL_ADCEx_Calibration_Start(&adc);

    dma.Instance = DMA1_Channel1;
    dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
    dma.Init.PeriphInc = DMA_PINC_DISABLE;
    dma.Init.MemInc = DMA_MINC_ENABLE;
    dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    dma.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    dma.Init.Mode = DMA_CIRCULAR;
    dma.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&dma);
    __HAL_LINKDMA(&adc, DMA_Handle, dma);

    HAL_ADC_Start_DMA(&adc, (uint32_t*)adc_value, ADC_CHANNELS);

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

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_Delay(100);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
        HAL_Delay(400);
    }
}

 

Link do komentarza
Share on other sites

Do posta wyżej,

aby nasze przetworniki działały poprawnie na odpowiednich pinach w moim przypadku A0,A1,A2,A3 należy ustawić odpowiednio kanały.

W naszym przypadku A0 jest na kanale 0, A1 na kanale 1 i mogłoby się wydawać, że dalej jest podobnie. Otóż nic bardziej mylnego! Do pinu A2 i A3 przypisane są kanały odpowiednio 4 i 8.

W powyższym kodzie należy poprawić przypisanie do kanału i szafa gra ! 🙂

Do błędu doszedłem po zainstalowaniu CubeMX 😄 

Edytowano przez Merfinius
  • Lubię! 1
Link do komentarza
Share on other sites

Pytania do zadania 8.1.
1. Jest jakaś gotowa funkcja w C do porównywania tablic?

2. Da się to zrobić szybciej niż porównywanie kolejnych pozycji? (patrz kod)
 

char compare_buffer( uint8_t a[], uint8_t b[], int size)
{
	int i;
	for (i=0; i<size; i++)
	{
		if( a[i] != b[i] ) return 0;
	}
	return 1;
}

 

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.