Перейти к содержанию
    

Контроллер GD32F103RCT6 связка DMA и АЦП, можно ли читать результат не прерывая работу DMA

Вообщем я настроил связку АЦП и DMA которая работает непрерывно , т.е. АЦП меряет а DMA пишет в двумерный массив НЕПРЕРЫВНО. Я хочу просто когда мне захочется например по каждые 5мс заходить в функцию которая будет просто считывать последние (в моём случае 7) значений по каждому каналу .  

ГЛАВНЫЙ Вопрос может ли случится ситуация когда одновременно я считываю значения а DMA их записывает в эту же ячейку , и я считаю какуюнибудь белеберду ?? 

 

Код инициализации привёл ниже для понимания 

всё это пишу в массив uint32_t ADC0_Buffer[ADC0_CHANNELS][ADC0_NUMBER] = {0};

где ADC0_CHANNELS  и ADC0_NUMBER равны количеству каналов , тогда в каждом  ADC0_Buffer[ADC0_CHANNELS]  лежат по порядку все каналы 

 

я пробовал при входе в функцию чтения значений отключать  DMA   (  adc_dma_mode_disable(ADC0);    adc_dma_mode_enable(ADC0);) но тогда  начинают "съезжать "  позиции в буфере ADC0_Buffer т.е. условно тот канал который должен ложится в ADC0_Buffer [X] [0]; начинает ложится то в  ADC0_Buffer [X] [4] то в  ADC0_Buffer [X] [5];

Spoiler
void init_adc (void){
    

/* ADC0   */

 
     // GPIO clock enable
        rcu_periph_clock_enable(RCU_GPIOA);
        rcu_periph_clock_enable(RCU_GPIOC);
    // adc clock enable     
        rcu_periph_clock_enable(RCU_ADC0);
 
 
// adc clock 8 points frequency, maximum 14MHz
        rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);

        rcu_periph_clock_enable(RCU_AF);
        rcu_periph_clock_enable(RCU_DMA0);



//#define ADC2_PORT                        GPIOA


/*  ADC I/O */
    gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, PA6_ADC0_IN6_PA_CURR | PA7_ADC0_IN7_PA_VCC );
    
//    gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, PA6_ADC0_IN6_PA_CURR | PA7_ADC0_IN7_PA_VCC );  


    gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, PC0_ADC0_IN10_DAC_OUT_3_MPC472 | PC1_ADC0_IN11_V_RX |
    PC2_ADC0_IN11_TRX_VCC | PC3_ADC0_IN13_V_DSC | PC5_ADC0_IN12_V_TX);  
 
    dma_parameter_struct dma_init_ADC0;
  
    /* deinitialize DMA channel */
    dma_deinit(DMA0, DMA_CH0);
 
 
        /* initialize DMA0 channeln(ADC0) */                            
    dma_init_ADC0.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_ADC0.memory_addr = (uint32_t)(&ADC0_Buffer);
    dma_init_ADC0.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_ADC0.memory_width = DMA_MEMORY_WIDTH_32BIT;
    dma_init_ADC0.number = (uint32_t)ADC0_CHANNELS*ADC0_NUMBER;
    dma_init_ADC0.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
    dma_init_ADC0.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_ADC0.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
    dma_init_ADC0.priority = DMA_PRIORITY_LOW;
    dma_init(DMA0, DMA_CH0, &dma_init_ADC0);    
    dma_circulation_enable(DMA0, DMA_CH0);    
     
    /* enable all DMA channels you need */
    dma_channel_enable(DMA0,DMA_CH0);
 
 
/* reset ADC */
    adc_deinit(ADC0);
 
    /* ADC scan mode function enable */
    adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
    adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
 
    /* ADC trigger config */
    adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
  
    
    /* ADC data alignment config */
    adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
 
    /* configure the ADC sync mode */
    adc_mode_config(ADC_MODE_FREE);      //   ADC_DAUL_REGULAL_PARALLEL  ADC_MODE_FREE  /////ОСТАНОВИЛСЯ ЗДЕСЬ 

 /* ADC channel length config */
    adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 7);
 
    /* ADC regular channel config */
    adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_6,   ADC_SAMPLETIME_1POINT5  );
    adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_7,      ADC_SAMPLETIME_1POINT5  );
    adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_10,  ADC_SAMPLETIME_1POINT5  );
    adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_11,  ADC_SAMPLETIME_1POINT5  );
    adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_12,  ADC_SAMPLETIME_1POINT5  );
    adc_regular_channel_config(ADC0, 5, ADC_CHANNEL_13,  ADC_SAMPLETIME_1POINT5  );
    adc_regular_channel_config(ADC0, 6, ADC_CHANNEL_15,  ADC_SAMPLETIME_1POINT5  );
 
/* ADC external trigger enable */
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
 
     
/* enable ADC interface */
    adc_enable(ADC0);
    delay_ms(10);    
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC0);
 
    /* ADC DMA function enable */
    adc_dma_mode_enable(ADC0);
  
        
    adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
 
}

 

  
    

Изменено пользователем haker_fox
Для оформления кода есть кнопка <>.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 минут назад, RR2021 сказал:

ГЛАВНЫЙ Вопрос может ли случится ситуация когда одновременно я считываю значения а DMA их записывает в эту же ячейку , и я считаю какуюнибудь белеберду ?? 

Наверное  для этого  надо использовать барьерные инструкции https://hubstub.ru/stm32/222-stm32-barernye-instrukcii.html

Для Keil например, надо вызывать __ISB() и т.д.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 минут назад, RR2021 сказал:

ГЛАВНЫЙ Вопрос может ли случится ситуация когда одновременно я считываю значения а DMA их записывает в эту же ячейку , и я считаю какуюнибудь белеберду ?? 

Не может.

4 минуты назад, HardEgor сказал:

Наверное  для этого  надо использовать барьерные инструкции

Причём тут доступ к памяти и барьерные инструкции?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

27 минут назад, RR2021 сказал:

ГЛАВНЫЙ Вопрос может ли случится ситуация когда одновременно я считываю значения а DMA их записывает в эту же ячейку

Стандартное решение: Используют циркулярный буфер и считывают из той части, куда в данный момент DMA  не пишет.

Подсматривай регистр: DMA_CHXCNT

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

30 минут назад, RR2021 сказал:

Вообщем я настроил связку АЦП и DMA которая работает непрерывно , т.е. АЦП меряет а DMA пишет в двумерный массив НЕПРЕРЫВНО. Я хочу просто когда мне захочется например по каждые 5мс заходить в функцию которая будет просто считывать последние (в моём случае 7) значений по каждому каналу .  

"Просто" не получится. Открывайте мануал на МК, раздел DMA; читайте что такое "circular transfer mode". В вашем МК он вроде имеется. Используете этот режим и делаете ISR по прерываниям: в конце и в середине передачи. Внутри этого ISR используете данные из буферов.

30 минут назад, RR2021 сказал:

я пробовал при входе в функцию чтения значений отключать  DMA

Так делать нельзя. DMA так не работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 минуты назад, VladislavS сказал:

Не может.

В принципе, может, что ему запрещает)) Но для этого нужно принудительно прострелить себе ногу, выставив ширины памяти и периферии разными.

P.S. Хотя, не помню уж точно, включается ли автоматом FIFO при различных ширинах доступа к шинам источника/назначения DMA, и какова финальная атомарность записи в память.

Помнится мне, что при разных ширинах данных DMA осуществляет групповую транзакцию (куда - в свой FIFO или сразу в память?).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 минуты назад, Arlleex сказал:

В принципе, может, что ему запрещает

Шина. Не даст она двум устройствам одновременно доступ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 минуты назад, VladislavS сказал:

Шина. Не даст она двум устройствам одновременно доступ.

Она тут еще пока не причем... Я к тому, что если ширина периферийного доступа == 32 бита, а ширина доступа к памяти < 32 бит, то (повторюсь, не помню деталей реализации) DMA качнет из периферии одной транзакцией, а вот в память будет писать другой шириной несколько раз. И вот она неатомарность, вне зависимости от приоритетов шин и т.д. CPU может обратиться в ячейку, которая еще недозаписана полностью.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В данном случае всё наоборот. Память 32 бита, периферийное <32 бита. Всё одной записью обойдётся.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только что, VladislavS сказал:

В данном случае всё наоборот. Память 32 бита, периферийное <32 бита. Всё одной записью обойдётся.

Для периферии автор тоже указал 32 бита (видимо, регистр такой). Ну ок, в данном случае все будет норм.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 минут назад, Arlleex сказал:

В принципе, может, что ему запрещает)) Но для этого нужно принудительно прострелить себе ногу, выставив ширины памяти и периферии разными.

А почему это по-вашему "выстрел в ногу"?

В одном текущем проекте я как раз использую разные ширины (памяти и периферии) с разных сторон DMA-канала. И это очень полезная вещь, иначе бы не получилось то, что сделано с помощью неё.

20 минут назад, Arlleex сказал:

Помнится мне, что при разных ширинах данных DMA осуществляет групповую транзакцию (куда - в свой FIFO или сразу в память?).

Или вы говорите про конкретный МК?

 

PS: Точнее - использую в разных проектах, но в одном это - жизненно необходимо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только что, jcxz сказал:

А почему это по-вашему "выстрел в ногу"?

Ну, оборот мб выбрал не удачный. Просто можно, не до конца скурив мануал, наивно полагать, что подводных камней нет. А они будут.

Например, вот из референса на STM32F4 (да, я понимаю, что в обсуждаемом ТС GD32F103 DMA вообще не такой, но все же)

image.thumb.png.f65fd8d921dae441212c0b1bb859ddce.png


Т.е. DMA считает из периферии 32 бита разом (атомарно), а затем отдаст в 32-битную ячейку памяти кусками по 8 бит за 4 DMA-транзакции.

Естественно, процессор может вклиниться на чтение этой памяти где-то в середине (ведь мы не о burst-транзакциях DMA говорим) и считает данные неконсистентно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 минут назад, Arlleex сказал:

Естественно, процессор может вклиниться на чтение этой памяти где-то в середине (ведь мы не о burst-транзакциях DMA говорим) и считает данные неконсистентно.

Если работа с циклическим буфером реализована корректно (буфер разбит на (как минимум) 2 части, с прерыванием в конце каждой), то процессор не сможет вклиниться где не надо. Вне зависимости от того - burst или не burst используется.

Да и какой смысл в таком режиме пересылки в случае ТС? У него скорее нужно думать в обратную сторону: парные 16-битные чтения с АЦП и однократные 32-битные записи в ОЗУ. Если такая возможность есть у GD.

PS: Кста - DMA-контроллер XMC4xxx позволяет программисту включать блокировку шины на время burst-транзакции или даже всей DMA-пересылки. Но сомневаюсь, что такое есть у GD. :wink:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 минут назад, jcxz сказал:

Если работа с циклическим буфером реализована корректно (буфер разбит на (как минимум) 2 части, с прерыванием в конце каждой), то процессор не сможет вклиниться где не надо.

Это да. Но ТС как раз имел в виду общий случай, когда DMA просто заполняет (возможно по кругу) некий кусок памяти, а пользовательский код периодически считывает этот буфер с отсчетами, ничего не зная о готовности этих данных

1 час назад, RR2021 сказал:

Я хочу просто когда мне захочется например по каждые 5мс заходить в функцию которая будет просто считывать последние (в моём случае 7) значений по каждому канал...

15 минут назад, jcxz сказал:

PS: Кста - DMA-контроллер XMC4xxx позволяет программисту включать блокировку шины на время burst-транзакции или даже всей DMA-пересылки. Но сомневаюсь, что такое есть у GD. :wink:

Так при пакетной передаче шина итак блокируется и другим мастерам недоступна, это и из описания AMBA видно (в пакетных передачах фаза адреса только одна, потом идут данные). Это у всех Cortex-M.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Arlleex сказал:

Так при пакетной передаче шина итак блокируется и другим мастерам недоступна, это и из описания AMBA видно (в пакетных передачах фаза адреса только одна, потом идут данные). Это у всех Cortex-M.

Зачем тогда в Infineon её делали? Явно не зря. И оно там работает, проверял.

То, что burst-передача начата, не говорит о том, что она не может быть прервана более приоритетным запросом. Lock возможно выключает возможность этой приостановки. Я не знаю как там устроено внутри, но имхо - при приходе более приоритетного DMA-запроса от другого bus-master, если нет флажка Lock в DMA, AHB-arbiter может:

1) сохранить контекст текущей burst (в своей памяти);

2) выставить текущему 1-му bus-master-у HLOCK (блокируя передачу остальных слов пакета);

3) выполнить приоритетную транзакцию 2-го bus-master-а;

4) восстановить прерванный контекст burst;

5) снять HLOCK, возобновляя работу 1-го bus-master.

Как вариант. Ну или контекст должен сохранять останавливаемый bus-master, без разницы.

 

Блокировка на время передачи есть не только у Infineon. Также есть она у LPC17xx. Только там гораздо проще. И в описании там сказано:

Цитата

The lock control may set the lock bit by writing a 1 to bit 16 of the DMACCxConfig Register. When a burst occurs, the AHB arbiter will not de-grant the master during the burst until the lock is de-asserted. The DMA Controller can be locked for a a single burst such as a long source fetch burst or a long destination drain burst. The DMA Controller does not usually assert the lock continuously for a source fetch burst followed by a destination drain burst. There are situations when the DMA Controller asserts the lock for source transfers followed by destination transfers. This is possible when internal conditions in the DMA Controller permit it to perform a source fetch followed by a destination drain back-to-back.

Получается - возможно прерывание неблокированной передачи.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...