ToR_TDA 0 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба Здравствуйте форумчане! МК - STM32F103. Сохраняю данные с АЦП в циклический буфер с передачей по DMA, все работает ок, ниже примерный код с HAL, инициализация не показана, но это не суть, вопрос такой: я хочу иметь возможность в любой момент времени узнать адрес или номер элемента массива adc_buf в который произойдет очередное сохранение (или последний куда сохранение было сделано). То есть указатель бегающий вместе со DMA. Один из вариантов, который я продумывал было инкремент некоторой переменной в прерывании по готовности передачи DMA, но есть ли нормальный (лучший) способ это делать, ведь этот указатель уже должен быть частью DMA, мне лишь надо его читать. #define ADC_BUF_LEN 256 ADC_HandleTypeDef hadc1; uint16_t adc_buf[ADC_BUF_LEN]; ADC_HandleTypeDef hadc1; HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, ADC_BUF_LEN); while (1) { } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 191 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 7 минут назад, ToR_TDA сказал: я хочу иметь возможность в любой момент времени узнать адрес или номер элемента массива adc_buf в который произойдет очередное сохранение (или последний куда сохранение было сделано). Откройте "Reference manual" на свой МК и прочитайте про регистр DMA.CNDTR. PS: А вообще, с DMA такое как правило не нужно. Или у вас какие-то проблемы в общем алгоритме работы.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ToR_TDA 0 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба Just now, jcxz said: Или у вас какие-то проблемы в общем алгоритме работы.... Нет, это не проблема, просто я хочу по некоторому событию в структуре данных буфера АЦП делать остановку DMA и дальше работать со статичным массивом, но одна из вещей которая мне нужна это знание на каком элементе ДМА было остановлено. Если я правильно понял в этом регистре будет храниться число оставшихся данных для передачи. И в circular mode, для моего случает когда буфер дойдет до 0, произойдет перезагрузка на 255? Этот регистр декрементируется да? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrBearManul 0 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 7 минут назад, ToR_TDA сказал: И в circular mode, для моего случает когда буфер дойдет до 0, произойдет перезагрузка на 255? Нет. В него будет записано значение, изначально вами запрограммированное. А иначе какой смысл в кольцевом режиме?? 8 минут назад, ToR_TDA сказал: но одна из вещей которая мне нужна это знание на каком элементе ДМА было остановлено Насколько я помню, чтение CNTDR должно дать вам ответ. 33 минуты назад, ToR_TDA сказал: некоторой переменной в прерывании по готовности передачи DMA Прерывние происходит по полупередачи и по полной передачи. Т.е. вы всё равно не сможете узнать с точностью до элемента сколько было передано данных. А если бы и могли, то какой смысл от такого прерывания? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 191 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 17 минут назад, MrBearManul сказал: Прерывние происходит по полупередачи и по полной передачи. Т.е. вы всё равно не сможете узнать с точностью до элемента сколько было передано данных. А если бы и могли, то какой смысл от такого прерывания? Как я уже писал - проблема видимо в выбранном алгоритме работы или выбранной его реализации. Остановить наверное можно, но не DMA, а источник триггер-событий для него (таймер?). И нужно знать - что это за событие, которое должно останавливать процесс DMA? Насколько точно нужно останавливать (каков допустимый временной лаг от возникновения стоп-события до собственно останова)? А может и вообще не нужно останавливать, а нужно только получить текущую позицию DMA-потока в момент некоего события (опять-же - какого?) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrBearManul 0 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 23 минуты назад, jcxz сказал: Как я уже писал - проблема видимо в выбранном алгоритме работы или выбранной его реализации. У меня тоже такое ощущение. Т.к. я использовал связку АЦП+ПДП, причём ПДП был в кольцевом режиме. Это был STM32. Я запускал и останавливал преобразования АЦП. Естественно, что и ПДП останавливался. Но знать текущую позицию мне как-то было ненужно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ToR_TDA 0 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 27 minutes ago, jcxz said: Остановить наверное можно, но не DMA, а источник триггер-событий для него (таймер?). АЦП в режиме continuous mode сам себя перезапускает, по готовности АЦП отрабатывает ПДП. Да, я останавливаю работу АЦП и тем останавливается ПДП, вопрос только на какой был последний переданный элемент массива adc_buf[256]; 27 minutes ago, jcxz said: текущую позицию DMA-потока Да, как это получать максимально просто и правильно? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 191 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 3 минуты назад, ToR_TDA сказал: Да, как это получать максимально просто и правильно? Я уже писал: это зависит от максимально допустимого временного лага. Чем больше допустимый лаг - тем проще. И от того - по какому событию нужно останаливать это всё? Вы ничего так и не ответили на это. А ясновидящих тут нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ToR_TDA 0 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 1 minute ago, jcxz said: Я уже писал: это зависит от максимально допустимого временного лага. Чем больше допустимый лаг - тем проще. АЦП создает новое данное раз в 1uS, мне бы хотелось иметь глобальную переменную, обновляющуюся раз в 1uS и несущую в себе информацию о адресе в буфере adc_buf[256] по которому была произведена последняя передача ПДП. 1 minute ago, jcxz said: И от того - по какому событию нужно останаливать это всё? Пример события: while (1) { if (adc_buf[128] > 1000 && adc_buf[127] < 1000) { HAL_ADC_Stop_DMA(&hadc1); } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 191 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба Цитата Пример события: while (1) { if (adc_buf[128] > 1000 && adc_buf[127] < 1000) { HAL_ADC_Stop_DMA(&hadc1); } } Для такой остановки "глобальная переменная, обновляющаяся раз в 1uS" бесполезна. Потому как точность остановки вашего цикла имеет временной лаг намного больше (может даже - в разы больше). А вообще - есть МК, которые могут генерить события по результату преобразования АЦП. И такое событие будет уже как раз с точностью до сэмпла. Не знаю - умеет ли такое ваш МК... PS: И вообще - реакция на события с разрешением ~1мкс обычно реализуются с помощью спец.возможностей периферии. А никак не программно, да ещё калокодом. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ToR_TDA 0 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 30 minutes ago, jcxz said: Для такой остановки "глобальная переменная, обновляющаяся раз в 1uS" бесполезна. Потому как точность остановки вашего цикла имеет временной лаг намного больше (может даже - в разы больше). Почему? 1uS это не так и мало это целых 56 тактов при 56МГц. Остановка АЦП может быть и через CMSIS, это не столь важно, мой вопрос от этого не меняется. Я уже понял, что нужно разобраться с регистром DMA_CNDTR1, но пока я не понял, какие именно данные мне в нем ожидать при циклическом заполнении буфера длиной 256 слов. У меня МК 32F103С8T6, но я думаю эти принципы должны распространятся по крайней мере на все 100-е МК. Да, есть событие по окончанию преобразования группы каналов АЦП, можно вызвать по нему прерывание, но я лишь хочу знать текущее положение потока DMA в буфере, например чтобы сделать такое условие: ADC1->CR2 |= ADC_CR2_SWSTART; uint8_t adc_buf_pos; if (adc_buf[adc_buf_pos-127] > 1000 && adc_buf[adc_buf_pos -128] < 1000 && adc_buf_pos == 255) { ADC1->CR2 &= ~ADC_CR2_SWSTART; } Где adc_buf_pos это суть вопроса Неправильно наверху написал, скорее так: ADC1->CR2 |= ADC_CR2_SWSTART; // старт АЦП uint8_t adc_buf_pos; while(1) { if (adc_buf[128] > 1000 && adc_buf[127] < 1000) { while(adc_buf_pos<255) { adc_buf_pos = ???; } ADC1->CR2 &= ~ADC_CR2_SWSTART; // Стоп АЦП // обработка статического массива } ADC1->CR2 |= ADC_CR2_SWSTART; // перезапуск АЦП } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Baser 5 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 33 минуты назад, ToR_TDA сказал: Где adc_buf_pos это суть вопроса Неправильно наверху написал, скорее так: Из примеров кода совсем непонятна задумка ТС (код непросекаемый ), а из объяснений вырисовывается след. задача: 1. АЦП с частотой 1МГц цифрует входной сигнал и через ПДП складывает результаты в циклический буфер на 256 значений. 2. Одновременно ТС хочет анализировать сигнал на перепад (фронт), после чего останавливать АЦП и неспешно анализировать данные. Если я правильно прованговал задумку, то два варианта решения: 1. Для анализа перепада входного сигнала использовать (11.3.7 Analog watchdog). 2. Периодически читать последние результаты АЦП и ловить перепад сигнала (но если перепад короткий, его можно и пропустить). Для определения местоположения последних результатов АЦП использовать, как уже сказали, регистр DMA->CNDTR. [ADC_BUF_LEN - DMA->CNDTR + 1] = индекс будущей записи в буфер. Последняя запись - предыдущая. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 191 12 февраля, 2021 Опубликовано 12 февраля, 2021 · Жалоба 2 часа назад, ToR_TDA сказал: Почему? 1uS это не так и мало это целых 56 тактов при 56МГц. Остановка АЦП может быть и через CMSIS, это не столь важно, мой вопрос от этого не меняется. Целых?... А ISR-ов у вас - ни одного нет? И длительность их выполнения какая? (Особенно если внутри них такие же кал-вызовы) А какова скорость выборки из флеша (код то наверное из флеша выполняется)? Если запретить все прерывания и работать через регистры, то тогда наверное можно получить 1 мкс... Цитата Я уже понял, что нужно разобраться с регистром DMA_CNDTR1, но пока я не понял, какие именно данные мне в нем ожидать при циклическом заполнении буфера длиной 256 слов. Вы же сами привели выдержку из мануала - там всё написано. А если что-то не ясно, то нетрудно под отладчиком всё проверить по шагам. Цитата Да, есть событие по окончанию преобразования группы каналов АЦП, можно вызвать по нему прерывание, но я лишь хочу знать текущее положение потока DMA в буфере, например чтобы сделать такое условие: Может вам просто работать по прерыванию, без DMA? А то Вы вроде сделали DMA, а теперь пытаетесь убить все его преимущества и привести к алгоритму функционирования "по прерываниям". Зачем вам DMA если стараетесь с ним бороться??? Просто опрашивайте АЦП поллингом или по прерыванию - так будет лучше и проще. PS: А то как в анекдоте про мужика купишего автомобиль, а потом спрашивающего - "а куда тут кобылу запрягать-то?" Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrBearManul 0 13 февраля, 2021 Опубликовано 13 февраля, 2021 · Жалоба 15 часов назад, ToR_TDA сказал: обновляющуюся раз в 1uS Вряд ли это возможно. Только в прерывании. Но таким прерыванием вы положите производительность системы, скорее всего. 15 часов назад, ToR_TDA сказал: Пример события: Гм... У вас поиск события никак не привязан к данным в буфере, т.к. выполняется асинхронно сбору. И неатомарности будут присутствовать: пока обращаетесь к паре ячеек, они уже раз двадцать переобновятся. 14 часов назад, ToR_TDA сказал: целых 56 тактов при 56МГц Вычтете такты на вход в прерывание (для CM3 примерно 12, точнее надо смотреть документацию), на выход (тоже примерно 12) и на сам код прерывания тоже. Останется не так уж и много. 14 часов назад, ToR_TDA сказал: например чтобы сделать такое условие: Я бы не использовал ПДП. Настроил бы прерывание по завершению преобразования всех нужных вам каналов (EOC), и в прерывании бы анализировал данные, и заполнял бы кольцевой буфер. Возможно, что такое прерывание придётся писать на ассемблере. Дать ему наивысший приоритет в NVIC. Не блокировать прерывания в основном коде (критические секции и т.д. и т.п.). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ToR_TDA 0 13 февраля, 2021 Опубликовано 13 февраля, 2021 · Жалоба Спасибо всем отписавшимся jcxz, MrBearManul, Baser мне понадобиться время что бы все это обдумать. Да, похоже получается обработка в прерывании будет лучше. На тот момент я думал, что через ПДП это можно сделать быстрее, но все верно, получается асинхронный счет сэмплов, а это глупость. 19 hours ago, Baser said: 1. АЦП с частотой 1МГц цифрует входной сигнал и через ПДП складывает результаты в циклический буфер на 256 значений. Абсолютно верно. 19 hours ago, Baser said: 2. Одновременно ТС хочет анализировать сигнал на перепад (фронт), после чего останавливать АЦП и неспешно анализировать данные. Не обязательно фронт, в идеале любой настраиваемый триггер. Про неспешный анализ после остановки АЦП совершенно верно. 19 hours ago, Baser said: 1. Для анализа перепада входного сигнала использовать (11.3.7 Analog watchdog). Хотел произвольный триггер, не только фронт/спад. 19 hours ago, Baser said: 2. Периодически читать последние результаты АЦП и ловить перепад сигнала (но если перепад короткий, его можно и пропустить). Да, читать данные данные в циклическом буфере для поиска определенных состояний. Да, регистр DMA1_Channel1->CNDTR, действительно содержит информацию о оставшемся количестве слов до заполнения циклического буфера (если я ничего не попутал), но получается придется читать его асинхронно, а это плохо. 18 hours ago, jcxz said: А ISR-ов у вас - ни одного нет? И длительность их выполнения какая? (Особенно если внутри них такие же кал-вызовы) А какова скорость выборки из флеша (код то наверное из флеша выполняется)? Если запретить все прерывания и работать через регистры, то тогда наверное можно получить 1 мкс... ISR от DMA. скорость выборки из флеша 1 такт, не? Да, ничего лишнего только АЦП, кольцевой буфер, блок триггера, и команда на остановку АЦП. В общем, задача очень похожа на то как работают цифровые осциллографы. То есть поиск события в буфере, после события дополняем буфер на еще N отсчетов, а далее стоп и медленно обрабатываем. В идеале я хотел бы получать буфер на 256 точек, в котором посередине было бы мое событие. 18 hours ago, jcxz said: Может вам просто работать по прерыванию, без DMA? А то Вы вроде сделали DMA, а теперь пытаетесь убить все его преимущества и привести к алгоритму функционирования "по прерываниям". Зачем вам DMA если стараетесь с ним бороться??? Просто опрашивайте АЦП поллингом или по прерыванию - так будет лучше и проще. PS: А то как в анекдоте про мужика купишего автомобиль, а потом спрашивающего - "а куда тут кобылу запрягать-то?" Да, похоже что так. 5 hours ago, MrBearManul said: Гм... У вас поиск события никак не привязан к данным в буфере, т.к. выполняется асинхронно сбору. И неатомарности будут присутствовать: пока обращаетесь к паре ячеек, они уже раз двадцать переобновятся. Я понял, согласен. 5 hours ago, MrBearManul said: Вычтете такты на вход в прерывание (для CM3 примерно 12, точнее надо смотреть документацию), на выход (тоже примерно 12) и на сам код прерывания тоже. Останется не так уж и много. 32 такта на чтение из регистра АЦП, сохранения в кольцевой буфер, инкремент указателя, проверка пары условий с чтением из буфера и при совпадении остановка АЦП. Думаете не заморачиваться? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться