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

STM32F1 АЦП+DMA Указатель на текущий адрес элемента в буфере

Здравствуйте форумчане!

МК - 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)
{  

}

 

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


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

7 минут назад, ToR_TDA сказал:

я хочу иметь возможность в любой момент времени узнать адрес или номер элемента массива adc_buf в который произойдет очередное сохранение (или последний куда сохранение было сделано).

Откройте "Reference manual" на свой МК и прочитайте про регистр DMA.CNDTR.

 

PS: А вообще, с DMA такое как правило не нужно. Или у вас какие-то проблемы в общем алгоритме работы....

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


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

Just now, jcxz said:

 Или у вас какие-то проблемы в общем алгоритме работы....

Нет, это не проблема, просто я хочу по некоторому событию в структуре данных буфера АЦП делать остановку DMA и дальше работать со статичным массивом, но одна из вещей которая мне нужна это знание на каком элементе ДМА было остановлено.

Если я правильно понял в этом регистре будет храниться число оставшихся данных для передачи. И в circular mode, для моего случает когда буфер дойдет до 0, произойдет перезагрузка на 255? Этот регистр декрементируется да?

316052473_.thumb.png.13a6567ebad50f0774a484aa90163a0a.png

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


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

7 минут назад, ToR_TDA сказал:

И в circular mode, для моего случает когда буфер дойдет до 0, произойдет перезагрузка на 255?

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

8 минут назад, ToR_TDA сказал:

но одна из вещей которая мне нужна это знание на каком элементе ДМА было остановлено

Насколько я помню, чтение CNTDR должно дать вам ответ.

33 минуты назад, ToR_TDA сказал:

некоторой переменной в прерывании по готовности передачи DMA

Прерывние происходит по полупередачи и по полной передачи. Т.е. вы всё равно не сможете узнать с точностью до элемента сколько было передано данных. А если бы и могли, то какой смысл от такого прерывания?

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


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

17 минут назад, MrBearManul сказал:

Прерывние происходит по полупередачи и по полной передачи. Т.е. вы всё равно не сможете узнать с точностью до элемента сколько было передано данных. А если бы и могли, то какой смысл от такого прерывания?

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

Остановить наверное можно, но не DMA, а источник триггер-событий для него (таймер?). И нужно знать - что это за событие, которое должно останавливать процесс DMA? Насколько точно нужно останавливать (каков допустимый временной лаг от возникновения стоп-события до собственно останова)?

А может и вообще не нужно останавливать, а нужно только получить текущую позицию DMA-потока в момент некоего события (опять-же - какого?)

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


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

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

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

У меня тоже такое ощущение. Т.к. я использовал связку АЦП+ПДП, причём ПДП был в кольцевом режиме. Это был STM32. Я запускал и останавливал преобразования АЦП. Естественно, что и ПДП останавливался. Но знать текущую позицию мне как-то было ненужно.

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


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

27 minutes ago, jcxz said:

Остановить наверное можно, но не DMA, а источник триггер-событий для него (таймер?).

АЦП в режиме continuous mode сам себя перезапускает, по готовности АЦП отрабатывает ПДП. Да, я останавливаю работу АЦП и тем останавливается ПДП, вопрос только на какой был последний переданный элемент массива adc_buf[256];

27 minutes ago, jcxz said:

текущую позицию DMA-потока

Да, как это получать максимально просто и правильно?

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


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

3 минуты назад, ToR_TDA сказал:

Да, как это получать максимально просто и правильно?

Я уже писал: это зависит от максимально допустимого временного лага. Чем больше допустимый лаг - тем проще.

И от того - по какому событию нужно останаливать это всё?

Вы ничего так и не ответили на это. А ясновидящих тут нет.  :unknw:

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


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

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

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


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

Цитата

Пример события:


  while (1)
{  
 if (adc_buf[128] > 1000 && adc_buf[127] < 1000)
 {
   HAL_ADC_Stop_DMA(&hadc1);
 }
}

Для такой остановки "глобальная переменная, обновляющаяся раз в 1uS" бесполезна. Потому как точность остановки вашего цикла имеет временной лаг намного больше (может даже - в разы больше).

А вообще - есть МК, которые могут генерить события по результату преобразования АЦП. И такое событие будет уже как раз с точностью до сэмпла. Не знаю - умеет ли такое ваш МК...

 

PS: И вообще - реакция на события с разрешением ~1мкс обычно реализуются с помощью спец.возможностей периферии. А никак не программно, да ещё калокодом.  :unknw:

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


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

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; // перезапуск АЦП 
}

 

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


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

33 минуты назад, ToR_TDA сказал:

Где adc_buf_pos это суть вопроса

Неправильно наверху написал, скорее так:

Из примеров кода совсем непонятна задумка ТС (код непросекаемый :smile:),
а из объяснений вырисовывается след. задача:
1. АЦП с частотой 1МГц цифрует входной сигнал и через ПДП складывает результаты в циклический буфер на 256 значений.
2. Одновременно ТС хочет анализировать сигнал на перепад (фронт), после чего останавливать АЦП и неспешно анализировать данные.

Если я правильно прованговал задумку, то два варианта решения:
1. Для анализа перепада входного сигнала использовать (11.3.7 Analog watchdog).
2. Периодически читать последние результаты АЦП и ловить перепад сигнала (но если перепад короткий, его можно и пропустить).

Для определения местоположения последних результатов АЦП использовать, как уже сказали, регистр DMA->CNDTR.
[ADC_BUF_LEN - DMA->CNDTR + 1] = индекс будущей записи в буфер. Последняя запись - предыдущая.

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


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

2 часа назад, ToR_TDA сказал:

Почему? 1uS это не так и мало это целых 56 тактов при 56МГц. Остановка АЦП может быть и через CMSIS, это не столь важно, мой вопрос от этого не меняется.

Целых?... :crazy:

А ISR-ов у вас - ни одного нет? И длительность их выполнения какая? (Особенно если внутри них такие же кал-вызовы)

А какова скорость выборки из флеша (код то наверное из флеша выполняется)?

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

Цитата

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

Вы же сами привели выдержку из мануала - там всё написано. :unknw: А если что-то не ясно, то нетрудно под отладчиком всё проверить по шагам.

Цитата

Да, есть событие по окончанию преобразования группы каналов АЦП, можно вызвать по нему прерывание, но я лишь хочу знать текущее положение потока DMA в буфере, например чтобы сделать такое условие:

Может вам просто работать по прерыванию, без DMA? А то Вы вроде сделали DMA, а теперь пытаетесь убить все его преимущества и привести к алгоритму функционирования "по прерываниям".

Зачем вам DMA если стараетесь с ним бороться???  :wacko2:

Просто опрашивайте АЦП поллингом или по прерыванию - так будет лучше и проще.

 

PS: А то как в анекдоте про мужика купишего автомобиль, а потом спрашивающего - "а куда тут кобылу запрягать-то?"

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


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

15 часов назад, ToR_TDA сказал:

обновляющуюся раз в 1uS

Вряд ли это возможно. Только в прерывании. Но таким прерыванием вы положите производительность системы, скорее всего.

15 часов назад, ToR_TDA сказал:

Пример события:

Гм... У вас поиск события никак не привязан к данным в буфере, т.к. выполняется асинхронно сбору. И неатомарности будут присутствовать: пока обращаетесь к паре ячеек, они уже раз двадцать переобновятся.

14 часов назад, ToR_TDA сказал:

целых 56 тактов при 56МГц

Вычтете такты на вход в прерывание (для CM3 примерно 12,  точнее надо смотреть документацию), на выход (тоже примерно 12) и на сам код прерывания тоже. Останется не так уж и много.

 

 

14 часов назад, ToR_TDA сказал:

например чтобы сделать такое условие:

Я бы не использовал ПДП. Настроил бы прерывание по завершению преобразования всех нужных вам каналов (EOC), и в прерывании бы анализировал данные, и заполнял бы кольцевой буфер. Возможно, что такое прерывание придётся писать на ассемблере. Дать ему наивысший приоритет в NVIC. Не блокировать прерывания в основном коде (критические секции и т.д. и т.п.).

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


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

Спасибо всем отписавшимся jcxzMrBearManulBaser мне понадобиться время что бы все это обдумать.

Да, похоже получается обработка в прерывании будет лучше. На тот момент я думал, что через ПДП это можно сделать быстрее, но все верно, получается асинхронный счет сэмплов, а это глупость.

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 если стараетесь с ним бороться???  :wacko2:

Просто опрашивайте АЦП поллингом или по прерыванию - так будет лучше и проще.

PS: А то как в анекдоте про мужика купишего автомобиль, а потом спрашивающего - "а куда тут кобылу запрягать-то?"

Да, похоже что так.

5 hours ago, MrBearManul said:

Гм... У вас поиск события никак не привязан к данным в буфере, т.к. выполняется асинхронно сбору. И неатомарности будут присутствовать: пока обращаетесь к паре ячеек, они уже раз двадцать переобновятся.

Я понял, согласен.

5 hours ago, MrBearManul said:

Вычтете такты на вход в прерывание (для CM3 примерно 12,  точнее надо смотреть документацию), на выход (тоже примерно 12) и на сам код прерывания тоже. Останется не так уж и много.

32 такта на чтение из регистра АЦП, сохранения в кольцевой буфер, инкремент указателя, проверка пары условий с чтением из буфера и при совпадении остановка АЦП. 

Думаете не заморачиваться?

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


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

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

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

Гость
Ответить в этой теме...

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

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

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

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

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

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