RR2021 1 8 сентября, 2023 Опубликовано 8 сентября, 2023 (изменено) · Жалоба Вообщем я настроил связку АЦП и 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); } Изменено 8 сентября, 2023 пользователем haker_fox Для оформления кода есть кнопка <>. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
HardEgor 90 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 14 минут назад, RR2021 сказал: ГЛАВНЫЙ Вопрос может ли случится ситуация когда одновременно я считываю значения а DMA их записывает в эту же ячейку , и я считаю какуюнибудь белеберду ?? Наверное для этого надо использовать барьерные инструкции https://hubstub.ru/stm32/222-stm32-barernye-instrukcii.html Для Keil например, надо вызывать __ISB() и т.д. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 14 минут назад, RR2021 сказал: ГЛАВНЫЙ Вопрос может ли случится ситуация когда одновременно я считываю значения а DMA их записывает в эту же ячейку , и я считаю какуюнибудь белеберду ?? Не может. 4 минуты назад, HardEgor сказал: Наверное для этого надо использовать барьерные инструкции Причём тут доступ к памяти и барьерные инструкции? 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dOb 8 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 27 минут назад, RR2021 сказал: ГЛАВНЫЙ Вопрос может ли случится ситуация когда одновременно я считываю значения а DMA их записывает в эту же ячейку Стандартное решение: Используют циркулярный буфер и считывают из той части, куда в данный момент DMA не пишет. Подсматривай регистр: DMA_CHXCNT Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 30 минут назад, RR2021 сказал: Вообщем я настроил связку АЦП и DMA которая работает непрерывно , т.е. АЦП меряет а DMA пишет в двумерный массив НЕПРЕРЫВНО. Я хочу просто когда мне захочется например по каждые 5мс заходить в функцию которая будет просто считывать последние (в моём случае 7) значений по каждому каналу . "Просто" не получится. Открывайте мануал на МК, раздел DMA; читайте что такое "circular transfer mode". В вашем МК он вроде имеется. Используете этот режим и делаете ISR по прерываниям: в конце и в середине передачи. Внутри этого ISR используете данные из буферов. 30 минут назад, RR2021 сказал: я пробовал при входе в функцию чтения значений отключать DMA Так делать нельзя. DMA так не работает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 23 минуты назад, VladislavS сказал: Не может. В принципе, может, что ему запрещает)) Но для этого нужно принудительно прострелить себе ногу, выставив ширины памяти и периферии разными. P.S. Хотя, не помню уж точно, включается ли автоматом FIFO при различных ширинах доступа к шинам источника/назначения DMA, и какова финальная атомарность записи в память. Помнится мне, что при разных ширинах данных DMA осуществляет групповую транзакцию (куда - в свой FIFO или сразу в память?). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 4 минуты назад, Arlleex сказал: В принципе, может, что ему запрещает Шина. Не даст она двум устройствам одновременно доступ. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 4 минуты назад, VladislavS сказал: Шина. Не даст она двум устройствам одновременно доступ. Она тут еще пока не причем... Я к тому, что если ширина периферийного доступа == 32 бита, а ширина доступа к памяти < 32 бит, то (повторюсь, не помню деталей реализации) DMA качнет из периферии одной транзакцией, а вот в память будет писать другой шириной несколько раз. И вот она неатомарность, вне зависимости от приоритетов шин и т.д. CPU может обратиться в ячейку, которая еще недозаписана полностью. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба В данном случае всё наоборот. Память 32 бита, периферийное <32 бита. Всё одной записью обойдётся. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба Только что, VladislavS сказал: В данном случае всё наоборот. Память 32 бита, периферийное <32 бита. Всё одной записью обойдётся. Для периферии автор тоже указал 32 бита (видимо, регистр такой). Ну ок, в данном случае все будет норм. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 20 минут назад, Arlleex сказал: В принципе, может, что ему запрещает)) Но для этого нужно принудительно прострелить себе ногу, выставив ширины памяти и периферии разными. А почему это по-вашему "выстрел в ногу"? В одном текущем проекте я как раз использую разные ширины (памяти и периферии) с разных сторон DMA-канала. И это очень полезная вещь, иначе бы не получилось то, что сделано с помощью неё. 20 минут назад, Arlleex сказал: Помнится мне, что при разных ширинах данных DMA осуществляет групповую транзакцию (куда - в свой FIFO или сразу в память?). Или вы говорите про конкретный МК? PS: Точнее - использую в разных проектах, но в одном это - жизненно необходимо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба Только что, jcxz сказал: А почему это по-вашему "выстрел в ногу"? Ну, оборот мб выбрал не удачный. Просто можно, не до конца скурив мануал, наивно полагать, что подводных камней нет. А они будут. Например, вот из референса на STM32F4 (да, я понимаю, что в обсуждаемом ТС GD32F103 DMA вообще не такой, но все же) Т.е. DMA считает из периферии 32 бита разом (атомарно), а затем отдаст в 32-битную ячейку памяти кусками по 8 бит за 4 DMA-транзакции. Естественно, процессор может вклиниться на чтение этой памяти где-то в середине (ведь мы не о burst-транзакциях DMA говорим) и считает данные неконсистентно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 12 минут назад, Arlleex сказал: Естественно, процессор может вклиниться на чтение этой памяти где-то в середине (ведь мы не о burst-транзакциях DMA говорим) и считает данные неконсистентно. Если работа с циклическим буфером реализована корректно (буфер разбит на (как минимум) 2 части, с прерыванием в конце каждой), то процессор не сможет вклиниться где не надо. Вне зависимости от того - burst или не burst используется. Да и какой смысл в таком режиме пересылки в случае ТС? У него скорее нужно думать в обратную сторону: парные 16-битные чтения с АЦП и однократные 32-битные записи в ОЗУ. Если такая возможность есть у GD. PS: Кста - DMA-контроллер XMC4xxx позволяет программисту включать блокировку шины на время burst-транзакции или даже всей DMA-пересылки. Но сомневаюсь, что такое есть у GD. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 15 минут назад, jcxz сказал: Если работа с циклическим буфером реализована корректно (буфер разбит на (как минимум) 2 части, с прерыванием в конце каждой), то процессор не сможет вклиниться где не надо. Это да. Но ТС как раз имел в виду общий случай, когда DMA просто заполняет (возможно по кругу) некий кусок памяти, а пользовательский код периодически считывает этот буфер с отсчетами, ничего не зная о готовности этих данных 1 час назад, RR2021 сказал: Я хочу просто когда мне захочется например по каждые 5мс заходить в функцию которая будет просто считывать последние (в моём случае 7) значений по каждому канал... 15 минут назад, jcxz сказал: PS: Кста - DMA-контроллер XMC4xxx позволяет программисту включать блокировку шины на время burst-транзакции или даже всей DMA-пересылки. Но сомневаюсь, что такое есть у GD. Так при пакетной передаче шина итак блокируется и другим мастерам недоступна, это и из описания AMBA видно (в пакетных передачах фаза адреса только одна, потом идут данные). Это у всех Cortex-M. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 8 сентября, 2023 Опубликовано 8 сентября, 2023 · Жалоба 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. Получается - возможно прерывание неблокированной передачи. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться