Jump to content
    

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

}

 

Share this post


Link to post
Share on other sites

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

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

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

 

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

Share this post


Link to post
Share on other sites

Just now, jcxz said:

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

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

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

316052473_.thumb.png.13a6567ebad50f0774a484aa90163a0a.png

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

27 minutes ago, jcxz said:

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

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

27 minutes ago, jcxz said:

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Цитата

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


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

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

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

 

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

Share this post


Link to post
Share on other sites

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

 

Share this post


Link to post
Share on other sites

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] = индекс будущей записи в буфер. Последняя запись - предыдущая.

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

Цитата

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

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

Цитата

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

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

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

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

 

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

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

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

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

 

 

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

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

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

Share this post


Link to post
Share on other sites

Спасибо всем отписавшимся 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 такта на чтение из регистра АЦП, сохранения в кольцевой буфер, инкремент указателя, проверка пары условий с чтением из буфера и при совпадении остановка АЦП. 

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...