Skocz do zawartości

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


Pomocna odpowiedź

Napisano
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.

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ę?

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.

  • 2 miesiące później...
  • 1 rok później...

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
  • 7 miesiące później...

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

 

(edytowany)

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
  • 5 miesiące później...

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

 

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...