Синхронизация вывода и обработки

Юрий48
Offline
Зарегистрирован: 19.06.2018

nik182 пишет:

Верить main.c .Рапорт более ранней версии. Да и тики таймера с PWM2 я ещё не проверил.

Помнится в Keil-е я мог без железа сэмулировать работу программы. Более того, подписав небольшую служебную программку, читающую значения с выхода и передающая их на вход АЦП, посмотреть, что же натворено на интересующем выходе. Здесь такое возможно?

Юрий48
Offline
Зарегистрирован: 19.06.2018

Взяв за основу варианты nik182 и mixail844 родил такую структуру. Её удобство в том, что, пользуясь всего двумя параметрами ППо и n, можно играться с частотой опроса и усреднением меняя их всего в двух местах программы.

Теперь её надо правильно вписать в Куб. Думается, что это не намного труднее, чем в квадрат. Хотя для меня всё едино. Львиную долю nik182 уже сделал. А мне всё ещё точить и точить саблю. Теперь вплотную надо разбираться с Atollic. Да и Си, можно сказать, с нуля.

nik182
Онлайн
Зарегистрирован: 04.05.2015

Непонятна конечная цель. Если АЦП пишет каждые 2 мкс, да если ещё после каждой записи как то пытаться обрабатывать полученные значения, то только вход и выход из прерывания АЦП, где собственно в режиме реального времени возможно получить текущие данные АЦП, займет 0.5 мкс. На обработку и анализ остается совсем мало времени.  Буквально 100 циклов процессора. Учитывайте это при составлении тех. задания на устройство.

Юрий48
Offline
Зарегистрирован: 19.06.2018

nik182 пишет:

Непонятна конечная цель. Если АЦП пишет каждые 2 мкс, да если ещё после каждой записи как то пытаться обрабатывать полученные значения, то только вход и выход из прерывания АЦП, где собственно в режиме реального времени возможно получить текущие данные АЦП, займет 0.5 мкс. На обработку и анализ остается совсем мало времени.  Буквально 100 циклов процессора. Учитывайте это при составлении тех. задания на устройство.

Перечитал несколько раз, но не смог уловить, что вас настораживает. То, что вы предложили ранее, как мне кажется, говорит о понимании. Как то я особо и не собирался делать какую либо обработку с темпом опроса АЦП, вернее мысль такая была, но вы уже мне на неё ответили и я принял ваше решение проводить обработку по оканчании всего цикла записи измерений в буфер, а потом всё по новой. Единственное, что пришло на ум так это, что не надо останавливать процесс генерации, чтобы при новом запуске избежать переходных процессов непосредственно в датчике. То есть, генератор и АЦП пусть работают постоянно, а запуск записи выборки в буфер (работа по ПДП) переодически после окончания обработки. Единственно надо засинхронизировать с  каким ни будь фронтом генератора. А что касается 0,5 мкс, так это что бы цикл преобразования АЦП не разрезался фронтом генератора, то есть быть уверенным, что точка попала не на фронт.

Теперь о структуре. Всё же структура, представленная ниже более красива. В ней использовалось предложение mixail844, пост 44.

Что мне пока не ясно, так это поведение тригера. Тригер есть тригер - на значение ему наплевать, для него важна команда изменить его. В каком состоянии он может оказаться при запуске таймера? Это всегда одно и то же значение или оно может и поменяться. Вот, если его можно предварительно установить в заданное состояние, то такая схема приобретает дополнительную функциональность. То есть таким образом можно задавать фазу квадратурного сигнала для всех четырёх квадрантов. Сдаётся мне, что это можно сделать простой установкой соответствующего выхода в нужное состояние. Выскажитесь, пожалуйста, по поводу того, как можно установить тригер в нужное состояние.

nik182
Онлайн
Зарегистрирован: 04.05.2015

Можно по окончании одного цикла делать deinit - init (см. datasheet по HAL) таймеров. Обещали, что после такой процедуры все внутренние тригеры обнулятся и жинь начнётся сначала. 

Юрий48
Offline
Зарегистрирован: 19.06.2018

Вот ещё препятствие. Для Output Compare есть варианты:

Frozen (used for Timing base)
Active Level on match
Inactive Level on match
Toggle on match
Forced Active
Forced Inactive

Toggle on match понятно это тригерный выход. Остальное только догадываюсь. Forced Active это положительный импульс, а Forced Inactive это отрицательный импульс, правда какой длительности не понятно. А что касается Active Level on match и Inactive Level on match, вообще не могу представить, что это может быть. Достойного описания на таймеры нет. Переводчик даёт какую то ерунду. Даа, чувствую, что совсем отстал от жизни. И ещё меня ввёл в ступор такой момент. Для одного таймера для разных каналов Куб допускает задание ШИМ с разными модами - выравниванием по фронту и по центру. Ну как это может быть? Я что то не понимаю или Куб не блокирует такие ситуации? Начинаю чувствовать какую то неловкость за столько вопросов, может, для кого то совсем тривиальных.

mixail844
Онлайн
Зарегистрирован: 30.04.2012

Юрий48 пишет:

Вот ещё препятствие. Для Output Compare есть варианты:

Frozen (used for Timing base)
Active Level on match
Inactive Level on match
Toggle on match
Forced Active
Forced Inactive

Toggle on match понятно это тригерный выход. Остальное только догадываюсь. Forced Active это положительный импульс, а Forced Inactive это отрицательный импульс, правда какой длительности не понятно. А что касается Active Level on match и Inactive Level on match, вообще не могу представить, что это может быть. Достойного описания на таймеры нет. Переводчик даёт какую то ерунду. Даа, чувствую, что совсем отстал от жизни.

не волнуйстесь ,у STM хоть и хорошая документация ,но не все и всегда ясно сразу.если не понимаете как все работает в General-purpose timer cookbook (AN4776) или STM32 cross-series timer overview(AN4013),то всегда есть Referenca Manual ...хотя там тоже иногда черт ногу сломит.и еще надо "перводить " как оно должно быть в STM32CubeMx

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

mixail844
Онлайн
Зарегистрирован: 30.04.2012

Юрий48 пишет:

Для одного таймера для разных каналов Куб допускает задание ШИМ с разными модами - выравниванием по фронту и по центру. Ну как это может быть? Я что то не понимаю или Куб не блокирует такие ситуации? Начинаю чувствовать какую то неловкость за столько вопросов, может, для кого то совсем тривиальных.

Куб далеко далеко не идеален и генерирует много оверхеда,что бы оптимизировать все надо либо писать на чистых регистрах(с обертками) либо использовать генерацию не в HAL а в LL(Low Level) drivers.

а почему должна быть блокировка ? я с таймерами  в OutputCompare режиме прям плотно не работал,но кмк,это не противоречит..счетчик у таймера один а OutputCompareRegister у каждого канала свой ,могу ошибаться и тут реально баг Куба.

a5021
Offline
Зарегистрирован: 07.07.2013

Юрий48 пишет:
Для одного таймера для разных каналов Куб допускает задание ШИМ с разными модами - выравниванием по фронту и по центру. Ну как это может быть?

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

mixail844 пишет:
но кмк,это не противоречит..счетчик у таймера один а OutputCompareRegister у каждого канала свой ,могу ошибаться и тут реально баг Куба.

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

b707
Offline
Зарегистрирован: 26.05.2017

Юрий48 пишет:

Вот ещё препятствие. Для Output Compare есть варианты:

Frozen (used for Timing base)
Active Level on match
Inactive Level on match
Toggle on match
Forced Active
Forced Inactive

Toggle on match понятно это тригерный выход. Остальное только догадываюсь. Forced Active это положительный импульс, а Forced Inactive это отрицательный импульс, правда какой длительности не понятно. А что касается Active Level on match и Inactive Level on match, вообще не могу представить, что это может быть.

да тут все просто -

Active/Inactive Level on match - при совпадении счетчика с регистром выход канала становится HIGH или LOW, соответсвенно

Toggle on match - при совпадении логический уровень инвертируется

Forced Active- Inactive - состояние выхода насильно установлено HIGH илиLOW и от таймера вообще не зависит

b707
Offline
Зарегистрирован: 26.05.2017

a5021 пишет:

Юрий48 пишет:
Для одного таймера для разных каналов Куб допускает задание ШИМ с разными модами - выравниванием по фронту и по центру. Ну как это может быть?

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

я думаю ТС путает выравнивание и PWM1 PWM2 mode

Юрий48
Offline
Зарегистрирован: 19.06.2018

b707 пишет:

я думаю ТС путает выравнивание и PWM1 PWM2 mode

Думаю, что это сообщение относится ко мне, правда заинтересовало, что такое ТС. Уверен, что вы правы, поскольку до конца не понял, что такое PWM1 PWM2 mode. Буду разбираться, но, если не сложно, растолкуйте в чём разница и тогда где и чем задаётся параметр выравнивания.

Правильно ли я понял, что блок Ouput control в режиме Toggle on match по приходу импульса со счётчика просто берёт текущее состояние выхода и его инвертирует? Тогда моё предположение в конце поста 54 верно и можно  таким образом инициализировать начальную фазу меандра на этом выходе. Если же у него есть внутренняя память, которую он инвертрует и уж потом подаёт на выход, то тогда начальная установка возможно только одним значением, определяемым общим сбросом -0. А что же на самом деле?

b707
Offline
Зарегистрирован: 26.05.2017

Юрий48 пишет:

Думаю, что это сообщение относится ко мне, правда заинтересовало, что такое ТС. Уверен, что вы правы, поскольку до конца не понял, что такое PWM1 PWM2 mode. Буду разбираться, но, если не сложно, растолкуйте в чём разница

у меня сейчас под рукой мануала нет, но в принципе PWM1 и 2 практически одно и то же, отличаются только состоянием выхода. В одном из режимов выход имеет низкий уровень, когда значение счетчика меньше регистра сравнения и высокий, когда счетчик больше регистра. А во втором режиме наоборот. Вот и вся разница.

добавка - ТС = топик-стартер, то есть тот, кто начал тему, в данном случае Вы

 

a5021
Offline
Зарегистрирован: 07.07.2013

Юрий48 пишет:
Если же у него есть внутренняя память, которую он инвертрует и уж потом подаёт на выход, то тогда начальная установка возможно только одним значением, определяемым общим сбросом -0. А что же на самом деле?

А на самом деле это железный автомат, который щелкает синхронно с тактовой частотой. Если сказано, что Toggle on match, то это самое Toggle произойдет ровно по тому же фронту тактовой частоты, как и match. Все растактовки есть в референсе в картинках. Надо только не лениться разглядывать.

Юрий48
Offline
Зарегистрирован: 19.06.2018

a5021 пишет:
А на самом деле это железный автомат, который щелкает синхронно с тактовой частотой. Если сказано, что Toggle on match, то это самое Toggle произойдет ровно по тому же фронту тактовой частоты, как и match. Все растактовки есть в референсе в картинках. Надо только не лениться разглядывать.

Чего чего а картинок то уж понаразглядывался. Это самое то для меня - поизучать именно картинкки и, наверно, обратили внимание, что и сам я не ленюсь их порисовать. Другое дело, что набор их не полный. Спасибо вот mixail844 дал наводку того что у меня не было AN4776, очень полезно.

То, что всё железно и происходит ровно по фронту и у меня не вызывает сомнения. Только этим для режима Toggle on match нельзя  однозначно сказать - какое же значение будет на выходе при приходе фронта, но однозначно можно сказать, что оно изменит своё состояние. Меня то всё беспокоит вопрос можно ли и как сделать так, что бы в этом режиме при старте генератора на выходе были значения, заданные мной , а не  по умолчению. И что бы эти значения были не сами по себе, а являлись начальными для работы режима Toggle on match.

Юрий48
Offline
Зарегистрирован: 19.06.2018

И ещё вопрос, прямо не относящийся к теме, но всё же. Дайте, пожалуйста, ссылочки какими справочными данными пользуетесь при программировании. В первую очередь интересуют сжатые материалы.

b707
Offline
Зарегистрирован: 26.05.2017

Юрий48 пишет:

Меня то всё беспокоит вопрос можно ли и как сделать так, что бы в этом режиме при старте генератора на выходе были значения, заданные мной , а не  по умолчению. И что бы эти значения были не сами по себе, а являлись начальными для работы режима Toggle on match.

Вместо того что сидеть и рассуждать, как это вас беспокоит :) - что мешает взять и попробовать? Выходы каналов таймера - это обычные GPIO Думаю, что если задать начальное значение выхода так же, как вы хажигаете светодиод :) - это будет именно то, что вы хотите.

a5021
Offline
Зарегистрирован: 07.07.2013

Юрий48 пишет:
Меня то всё беспокоит вопрос можно ли и как сделать так, что бы в этом режиме при старте генератора на выходе были значения, заданные мной , а не  по умолчению. И что бы эти значения были не сами по себе, а являлись начальными для работы режима Toggle on match.

То, какие состояния изначально, зависит от PWM Mode. Для первого режима активное состояние до совпадения, неактивное после. Для второго режима наоборот. Кроме Toggle, реакцией на совпадение может быть "001: Set channel  to active level on match" и "010: Set channel to inactive level on match". Тобишь, по совпадению можно переключаться из любого режима в любой, причем, несколькими способами.
 

Юрий48
Offline
Зарегистрирован: 19.06.2018

b707 пишет:

Вместо того что сидеть и рассуждать, как это вас беспокоит :) - что мешает взять и попробовать? Выходы каналов таймера - это обычные GPIO Думаю, что если задать начальное значение выхода так же, как вы хажигаете светодиод :) - это будет именно то, что вы хотите.

Да  у меня самого давно руки чешуться поиграться, но, видимо, такой характер. Долго запрягаю, но быстро езжу. Да ещё накладывается то, что тропинка то ещё не протоптана. Сейчас буду учиться таким тривиальным вещам,  как, например, лучше оформить отображение программы, скрыть или показать всю эту лабуду с инициализацией, разбираться в синтаксисе, заливка программы, отладка и т.д. К стате о заливке и отладке. Буду пользоваться ST-Link. Наверняка что то надо будут настроить в Кубе. Что?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Юрий48 пишет:

 Меня то всё беспокоит вопрос можно ли и как сделать так, что бы в этом режиме при старте генератора на выходе были значения, заданные мной , а не  по умолчению. И что бы эти значения были не сами по себе, а являлись начальными для работы режима Toggle on match.

Можно задать любые значения для кадого вывода. Вот чуть модифицировал тот же пример, что я вам уже давал. Тут  поднимаются обе аппаратные ноги таймера (14 строка), и остаются поднятыми до момента старта генерации (16 строка).

 void setup() {
pinMode(PA8,PWM); 
pinMode(PA9,PWM); 
RCC_BASE->APB2RSTR |= 1<<11; //reset timer 1
RCC_BASE->APB2RSTR&= ~(1<<11); 
TIMER1_BASE->CCER=(1<<4)|(1<<0);//cc1e/cc2e enable 
TIMER1_BASE->CCMR1=(1<<13)|(1<<12)|(1<<5)|(1<<4);//toogle mode 
TIMER1_BASE->PSC=0;
TIMER1_BASE->ARR=2879;
TIMER1_BASE->CCR1=0 ;
TIMER1_BASE->CCR2=1439 ;
TIMER1_BASE->BDTR= 1<<14; //Automatic output enable
TIMER1_BASE->CCMR1=(1<<13)|(1<<12)|(1<<5)|(1<<4);//toogle mode 
TIMER1_BASE->CR2= 1<<10 | 1<<8 ; // SET output idle state OC1, OC2 = HIGH
delay(5000);
TIMER1_BASE->CR1=1;
}

Захватил осциллом момент старта генерации:

Юрий48
Offline
Зарегистрирован: 19.06.2018

a5021 пишет:
То, какие состояния изначально, зависит от PWM Mode. Для первого режима активное состояние до совпадения, неактивное после. Для второго режима наоборот. Кроме Toggle, реакцией на совпадение может быть "001: Set channel  to active level on match" и "010: Set channel to inactive level on match". Тобишь, по совпадению можно переключаться из любого режима в любой, причем, несколькими способами.
 

a5021, спасибо. Когда я говорил о своём беспокойстве, не имел ввиду режим PWM - понял, что он мне уже не нужен. А говорил о режиме обычного компарирования.

mixail844
Онлайн
Зарегистрирован: 30.04.2012

Юрий48 пишет:

Буду пользоваться ST-Link. Наверняка что то надо будут настроить в Кубе. Что?

вкладка SYS->Debug:Serial Wire (Serial Wire вроде как всеми ST-Link'ами потдерживаються и китайскими клонами и оригинальными)

со-но в Atollic выбрать в Configure Debug -> debugger-> Debug Probe :ST-Link

 

Юрий48
Offline
Зарегистрирован: 19.06.2018

dimax, здорово. Смотришь и душа радуется - всего 17 строчек и такая красота. Ну, вот никак не хочет банда Ардуино отпускать из своих сетей в мафию Куба и иже с ним. Опять я сильно заколебался. Если вы мне ещё покажете как засинхронизировать со сдвигом опрос АЦП (DMA) по отношению к фронтам генератора и запуск выборки по отношению, опять же, к какому из фронтов, то точно останусь у вас. Большое спасибо, что показали и утвердили в правильности моих предположений о  работе Toggle on match.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Юрий48,  c АЦП мне кажется вам стоит пересмотреть свои изначальные хотелки. Одним "взмахом пера", то бишь желанием ненароком не попасть на фронт меандра вы многократно усложнили себе  задачу. А надо ли оно вам? Я бы просто программно "выбросил" подозрительный семпл, и делов-то.  Пусть АЦП-шка строчит со своей комфортной скоростью и без привязки к таймерам. Потом разбирайте её буфер по какому-нибудь медианному алгоритму и усредняйте если надо. Думаю, что результат получится не хуже, чем тот, на который вы изначально рассчитывали..

a5021
Offline
Зарегистрирован: 07.07.2013

И не забываем, что блоков АЦП там два. Они умеют работать совместно и полностью автономно каждый.

Юрий48
Offline
Зарегистрирован: 19.06.2018

dimax пишет:

Юрий48,  c АЦП мне кажется вам стоит пересмотреть свои изначальные хотелки. Одним "взмахом пера", то бишь желанием ненароком не попасть на фронт меандра вы многократно усложнили себе  задачу. А надо ли оно вам? Я бы просто программно "выбросил" подозрительный семпл, и делов-то.  Пусть АЦП-шка строчит со своей комфортной скоростью и без привязки к таймерам. Потом разбирайте её буфер по какому-нибудь медианному алгоритму и усредняйте если надо. Думаю, что результат получится не хуже, чем тот, на который вы изначально рассчитывали..

Тут возникает много вопросов:

1. как понять, что точка попала на фронт?

2. А если все семплы окажутся подозрительными?

3. Если выкидывать только подозрительную точку, то тогда надо будет для каждой четверти подсчитывать количество правильных. Это, конечно, возможно но не лучший вариант.

4. Возможен и такой вариант. Выкидывать без объяснений первую и последнюю точки из четверти. Но это уж из за полной безысходности.

Да, и ещё. Поскольку я пока не представляю (по физике), что в конечном итоге получится, то очень желательно, чтобы в каждой четверти по всей выборке их было одинаковое количество и они были одинаково расставленны.

Что то упустил самое главное. Ведь обработка должна вестись по четвертям полностью аналогично синхронному фильтру. А если АЦП и генератор будут асинхронны, то тогда нужно каким то образом параллельно формировать массив импульсов синхронизации. Согласитесь, что это ....

Юрий48
Offline
Зарегистрирован: 19.06.2018

b707 пишет:

Active/Inactive Level on match - при совпадении счетчика с регистром выход канала становится HIGH или LOW, соответсвенно

Ну, выставился он в соответствующее состояние. А для нового цикла в исходное состояние как или чем он возвращается? Речь идёт не о режиме PWM, а о режиме простого сравнения.

Юрий48
Offline
Зарегистрирован: 19.06.2018

Вот ещё вопрос. Что то не могу понять какие значения надо задавать для предделителя, автоперезагрузки, компарирования. Имеется ввиду, что их надо или не надо уменьшать на единицу. Просто натыкался на противоречивые варианты.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Юрий48 пишет:

Ну, выставился он в соответствующее состояние. А для нового цикла в исходное состояние как или чем он возвращается?

Что есть новый цикл? Если таймер перевести в режим idle, то ноги тотчас займут то положение,  которое определено для этого статуса.

Юрий48 пишет:

Вот ещё вопрос. Что то не могу понять какие значения надо задавать для предделителя, автоперезагрузки, компарирования. Имеется ввиду, что их надо или не надо уменьшать на единицу. Просто натыкался на противоречивые варианты.

В регистрах счёт с ноля. Поэтому если нужно досчитать до 1000 то записываем 999.

Юрий48
Offline
Зарегистрирован: 19.06.2018

dimax пишет:

Юрий48 пишет:

Ну, выставился он в соответствующее состояние. А для нового цикла в исходное состояние как или чем он возвращается?

Что есть новый цикл? Если таймер перевести в режим idle, то ноги тотчас займут то положение,  которое определено для этого статуса.

Скажем, режим компарирования. Счётчик дошёл до уровня компарирования, выход выставился, скажем, в состояние 1. дальше счётчик досчитал то уровня перезагрузки и пошёл считать по новой до уровня компарирования. Что, всё это время выход так и находится в 1?

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Юрий48, да,  а какие тут могут быть  варианты? Переключение в рабочем порядке происходит только при совпадении регистров.

Юрий48
Offline
Зарегистрирован: 19.06.2018

dimax пишет:

Юрий48, да,  а какие тут могут быть  варианты? Переключение в рабочем порядке происходит только при совпадении регистров.

Ну да. На следующем совпадении - опять в единицу и т.д. И на данный момент в моём представлении получается, что на выходе будет всегда единица. Но это же не так. Между совпадениям должно происходить нечто, что выход опять выставляет в 0. Или это должен делать я программно?

mixail844
Онлайн
Зарегистрирован: 30.04.2012

Юрий48 ,

eще вариант , можно подсчитать сколько времени берет одна конверсия АЦП (зависит от частоты его работы , от киличества сэмплов на конверсию и от выбраной резолюции .как то считал,получалось от долей микросекунд до десятков микросекунд) сохранять конверсии при помощи DMA(ПДП)в (кольцевой) буффер(можно даже double buffering организовать) и потом подсчитать(исходя из времени концерсии) номер какой конверсии совпадал с фронтом таймера .double buffering для того что бы с одним работать а в другой при помощи DMA конвертировать

Юрий48
Offline
Зарегистрирован: 19.06.2018

mixail844 пишет:

Юрий48 ,

eще вариант , можно подсчитать сколько времени берет одна конверсия АЦП (зависит от частоты его работы , от киличества сэмплов на конверсию и от выбраной резолюции .как то считал,получалось от долей микросекунд до десятков микросекунд) сохранять конверсии при помощи DMA(ПДП)в (кольцевой) буффер(можно даже double buffering организовать) и потом подсчитать(исходя из времени концерсии) номер какой конверсии совпадал с фронтом таймера .double buffering для того что бы с одним работать а в другой при помощи DMA конвертировать

Double buffering хороший вариант и я о нём тоже думал, но не актуален. Может, со временем он и будет воплощён. Хотя, если о нём будет полное понимание, то почему же и нет. На счёт подсчёта номера совпадения конверсии - не прочувствовал.  Я то планирую сделать так. Генератор и АЦП молотят постоянно. А запись в ПДП-буфер с перерывами на обработку и индикацию. При чём возобновление записи в ПДП строго засинхронизировать с одним из фронтов генератора. Длину буфера брать равной требуемому усреднению помноженному на количество точек, приходящихся на период генератора. В соответствии с моей картинкой это У*4*n, где У - количество усреднений, а n - количество точек (по вашему конверсия) на четверть периода. Тогда обработка становится совсем прозрачной и значительно упрощается.

mixail844
Онлайн
Зарегистрирован: 30.04.2012

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

Юрий48
Offline
Зарегистрирован: 19.06.2018

mixail844 пишет:

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

Это понятно и это есть хорошо.

mixail844 пишет:
и подобрать время конверсии так что бы последующие ни совпадали с фронтом другого таймера ? тогда конверсия начнется вместе с одним из таймеров и продлиться какое то время,таймер уже переключит вывод.

А с этим не понтно. Чего подбирать время конверсии? Задать его изначально, меньше времени опроса (точками) АЦП. А сам опрос тоже постоянный, такой, что бы на период генератора априходилось 4*n точек.

a5021
Offline
Зарегистрирован: 07.07.2013

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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Юрий48 пишет:

Ну да. На следующем совпадении - опять в единицу и т.д. И на данный момент в моём представлении получается, что на выходе будет всегда единица. Но это же не так. Между совпадениям должно происходить нечто, что выход опять выставляет в 0. Или это должен делать я программно?

Мне кажется мы всё время говорили о режиме "Toggle on compare match" Разве само название не говорит о том, что при каждом совпадении выход переключиться на противоположное значение? Юрий, скажу прямо -сомневаюсь что  у вас что-то выйдет. Вы уже строите планы по увязке всей периферии в некую систему, но о принципах работы и особянностях программирования этой периферии не знаете ничего. Как же можно что-то планировать без знания "железа", с которым будете работать?

Юрий48
Offline
Зарегистрирован: 19.06.2018

dimax пишет:
Мне кажется мы всё время говорили о режиме "Toggle on compare match" Разве само название не говорит о том, что при каждом совпадении выход переключиться на противоположное значение?

Да ладно, поскольку я так и не понял до конца этот режим, то и не буду его использовать. То же самое могу получить, используя ШИМ.

dimax пишет:

Юрий, скажу прямо -сомневаюсь что  у вас что-то выйдет. Вы уже строите планы по увязке всей периферии в некую систему, но о принципах работы и особянностях программирования этой периферии не знаете ничего. Как же можно что-то планировать без знания "железа", с которым будете работать?

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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Юрий48 пишет:

Да ладно, поскольку я так и не понял до конца этот режим, то и не буду его использовать. То же самое могу получить, используя ШИМ.

И это печально, потому что режим PWM тут совершеннно ни к чему. Дело ваше конечно, но мне кажется имея рабочий пример  гораздо проще начать писать свою программу, чем не имея такого примера. А пример у вас есть, вам нужно всего лишь разобрать что делает каждая из 17 строк.

Юрий48 пишет:

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

Можно, особенно если вы не будете себе усложнять задачу. С теми планами что у вас сейчас у вас могут уйти многие месяцы на написание и отладку этой программы.

Юрий48
Offline
Зарегистрирован: 19.06.2018

dimax пишет:

И это печально, потому что режим PWM тут совершеннно ни к чему. Дело ваше конечно, но мне кажется имея рабочий пример  гораздо проще начать писать свою программу, чем не имея такого примера. А пример у вас есть, вам нужно всего лишь разобрать что делает каждая из 17 строк.

Я тоже считаю, что печально, и можно обойтись без режима PWM. Но уверяю вас, что, если бы я увидел, что этот режим наносит ущерб чему ни будь, например, быстродействию,  то я бы добил бы до конца вариант с компарированием. А мне печально то, что я не смог донести, что мне не понятно и слил продолжение дискуссии по этому вопросу. С примерами как раз никаких проблем нет, Да, работает, но ответа на мой вопрос не даёт.

dimax пишет:

Можно, особенно если вы не будете себе усложнять задачу. С теми планами что у вас сейчас у вас могут уйти многие месяцы на написание и отладку этой программы.

Вот и хорошо, что можно.

nik182
Онлайн
Зарегистрирован: 04.05.2015

Собственно вторая версия. Оказалось что HAL не имеет возможности одновременного запуска таймеров. Команда поднятия канала таймера этот таймер и запускает. Пришлось использовать LL драйвера куба для таймера. И ещё засада оказалась с тем что центрированный PWM требует на один тик больше. Результат порадовал. LL драйвера это практически CMSIS обёрнутый в буквы. Местами даже удобнее STL. При этом генерится кубом.

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"
#include "usb_device.h"

/* USER CODE BEGIN Includes */
#include "stdbool.h"

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
#define ADCCONVERTEDVALUES_BUFFER_SIZE ((uint32_t)  2000)
__IO uint16_t   aADCConvertedValues[ADCCONVERTEDVALUES_BUFFER_SIZE];

__IO uint32_t flRd;

extern PCD_HandleTypeDef hpcd_USB_FS;

#define APP_TX_DATA_SIZE  1000
extern uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];


/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_TIM1_Init(void);
static void MX_TIM2_Init(void);
static void MX_TIM3_Init(void);
static void MX_TIM4_Init(void);
static void MX_ADC1_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */



/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  *
  * @retval None
  */
int main(void)
{
  /* USER CODE BEGIN 1 */



  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  MX_ADC1_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */
  
//  USBD_CDC_SetTxBuffer(&hpcd_USB_FS, UserTxBuffer, 0);
//  USBD_CDC_SetRxBuffer(&hpcd_USB_FS, UserRxBuffer);

  if (HAL_ADC_Start_DMA(&hadc1,
                                   (uint32_t *)aADCConvertedValues,
                                    ADCCONVERTEDVALUES_BUFFER_SIZE
                                  ) != HAL_OK)
  {
    /* Start Error */
    Error_Handler();
  };
  
   LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
   LL_TIM_CC_EnableChannel(TIM3, LL_TIM_CHANNEL_CH1);
   LL_TIM_CC_EnableChannel(TIM4, LL_TIM_CHANNEL_CH4);
   LL_TIM_EnableCounter(TIM1);


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (flRd) 
    {
      flRd=false;
      for (uint32_t i=0;i<2000;i++)
      {
  //       sprintf(UserTxBufferFS,"%4d ,%4d\r\n\0",aADCConvertedValues[i][0],aADCConvertedValues[i][1]);
  //       USBD_CDC_SetTxBuffer(&hpcd_USB_FS, UserTxBuffer, 12);
  //       USBD_CDC_TransmitPacket(&hpcd_USB_FS);
      };
 
    };
    
    
    
    
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInit;

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_USB;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* ADC1 init function */
static void MX_ADC1_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

    /**Common config 
    */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T4_CC4;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Regular Channel 
    */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

/* TIM1 init function */
static void MX_TIM1_Init(void)
{

  LL_TIM_InitTypeDef TIM_InitStruct;

  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);

  TIM_InitStruct.Prescaler = 7199;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 9999;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  TIM_InitStruct.RepetitionCounter = 0;
  LL_TIM_Init(TIM1, &TIM_InitStruct);

  LL_TIM_DisableARRPreload(TIM1);

  LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);

  LL_TIM_SetOnePulseMode(TIM1, LL_TIM_ONEPULSEMODE_SINGLE);

  LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_UPDATE);

  LL_TIM_EnableMasterSlaveMode(TIM1);

}

/* TIM2 init function */
static void MX_TIM2_Init(void)
{

  LL_TIM_InitTypeDef TIM_InitStruct;
  LL_TIM_OC_InitTypeDef TIM_OC_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);

  TIM_InitStruct.Prescaler = 71;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 79;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM2, &TIM_InitStruct);

  LL_TIM_EnableARRPreload(TIM2);

  LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);

  LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH1);

  TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
  TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
  TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
  TIM_OC_InitStruct.CompareValue = 40;
  TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
  LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);

  LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH1);

  LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_ITR0);

  LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_TRIGGER);

  LL_TIM_DisableIT_TRIG(TIM2);

  LL_TIM_DisableDMAReq_TRIG(TIM2);

  LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);

  LL_TIM_DisableMasterSlaveMode(TIM2);

  /**TIM2 GPIO Configuration  
  PA0-WKUP   ------> TIM2_CH1 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

/* TIM3 init function */
static void MX_TIM3_Init(void)
{

  LL_TIM_InitTypeDef TIM_InitStruct;
  LL_TIM_OC_InitTypeDef TIM_OC_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);

  TIM_InitStruct.Prescaler = 35;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_CENTER_UP_DOWN;
  TIM_InitStruct.Autoreload = 80;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM3, &TIM_InitStruct);

  LL_TIM_EnableARRPreload(TIM3);

  LL_TIM_SetClockSource(TIM3, LL_TIM_CLOCKSOURCE_INTERNAL);

  LL_TIM_OC_EnablePreload(TIM3, LL_TIM_CHANNEL_CH1);

  TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
  TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
  TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
  TIM_OC_InitStruct.CompareValue = 40;
  TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
  LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);

  LL_TIM_OC_DisableFast(TIM3, LL_TIM_CHANNEL_CH1);

  LL_TIM_SetTriggerInput(TIM3, LL_TIM_TS_ITR0);

  LL_TIM_SetSlaveMode(TIM3, LL_TIM_SLAVEMODE_TRIGGER);

  LL_TIM_DisableIT_TRIG(TIM3);

  LL_TIM_DisableDMAReq_TRIG(TIM3);

  LL_TIM_SetTriggerOutput(TIM3, LL_TIM_TRGO_UPDATE);

  LL_TIM_DisableMasterSlaveMode(TIM3);

  /**TIM3 GPIO Configuration  
  PA6   ------> TIM3_CH1 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

/* TIM4 init function */
static void MX_TIM4_Init(void)
{

  LL_TIM_InitTypeDef TIM_InitStruct;
  LL_TIM_OC_InitTypeDef TIM_OC_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM4);

  TIM_InitStruct.Prescaler = 17;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = 7;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM4, &TIM_InitStruct);

  LL_TIM_EnableARRPreload(TIM4);

  LL_TIM_SetClockSource(TIM4, LL_TIM_CLOCKSOURCE_INTERNAL);

  LL_TIM_OC_EnablePreload(TIM4, LL_TIM_CHANNEL_CH4);

  TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM2;
  TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
  TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
  TIM_OC_InitStruct.CompareValue = 4;
  TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
  LL_TIM_OC_Init(TIM4, LL_TIM_CHANNEL_CH4, &TIM_OC_InitStruct);

  LL_TIM_OC_DisableFast(TIM4, LL_TIM_CHANNEL_CH4);

  LL_TIM_SetTriggerInput(TIM4, LL_TIM_TS_ITR0);

  LL_TIM_SetSlaveMode(TIM4, LL_TIM_SLAVEMODE_TRIGGER);

  LL_TIM_DisableIT_TRIG(TIM4);

  LL_TIM_DisableDMAReq_TRIG(TIM4);

  LL_TIM_SetTriggerOutput(TIM4, LL_TIM_TRGO_RESET);

  LL_TIM_DisableMasterSlaveMode(TIM4);

  /**TIM4 GPIO Configuration  
  PB9   ------> TIM4_CH4 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *AdcHandle)
{

 
 flRd=true;


}

И результат работы. Осцилограф двухлучевой, поэтому сначала два канала со сдвигом 90 градусов, потом соотношение старта АЦП - по восходящим фронтам - к фронту канала. На третьей фото видно что цифрует точно за 1мкс до и 1 мкс после фронта.   

Юрий48
Offline
Зарегистрирован: 19.06.2018

nik182, Спасибо огромное. Со второй картинкой не понятно. На вид это просто растянутая первая картинка и по этому не соответствует тому, что сказано в тексте. Третья картинка заставила меня задуматься. Я, когда в начале описывал задачу, представлял, что на фронт генератора не должен попадать цикл преобразования АЦП. Поэтому думал, что для гарантии запуск АЦП должен быть сдвинут не просто на 1 мкс, а на 0,5 мкс с тем что бы фронт генератора пришёлся не на отрицательный фронт запуска АЦП, а на середину низкого уровня. Если цикл преобразования АЦП больше 1 мкс, то замер этой точки исказится. Если равно, то тут трудно сказать что либо определённое, может, и не страшно. Сейчас как раз разбираюсь с АЦП и подсчётом времени преобразования. Ваше решение беру за основу, буду работать с ним и, если хватит терпения, то попробую перейти к варианту из поста №54, который мне очень по душе. Код программы вы выложили, за что ещё раз огромное спасибо. И, если вам не надоели "спасибо", то скинте ещё, пожалуйста, файл для Куба.

 

 

nik182
Онлайн
Зарегистрирован: 04.05.2015

АЦП сейчас работае 1.5 цикла или 0.1 мкс. Это не та цифра из за которой нужно переживать.

Юрий48
Offline
Зарегистрирован: 19.06.2018

nik182 пишет:
АЦП сейчас работае 1.5 цикла или 0.1 мкс. Это не та цифра из за которой нужно переживать.

Понятно. Я то думал, что время пробразования 1 мкс. А как же так, везде встречал, что мнимальное время преобразования для этого АЦП 1 мкс, а тут 0,1. Как это понимать?

Юрий48
Offline
Зарегистрирован: 19.06.2018

nik182 пишет:
АЦП сейчас работае 1.5 цикла или 0.1 мкс. Это не та цифра из за которой нужно переживать.

Подразобрался. Время преобразования составляет 12,5 циклов тактирования, а минимальное время выборки, выраженное в тактах, определяется как сумма  времени преобразования (12,5) и заданной паузой, в данном случае 1,5. 12,5+1,5 =14 тактов. То есть, даже, если принять во внимание только преобразование (12,5) без паузы, то его время при частоте тактирования 12 мГц будет чуть больше 1 мкс, что не есть хорошо.

nik182
Онлайн
Зарегистрирован: 04.05.2015

Файл timers.ioc 

#MicroXplorer Configuration settings - do not modify
ADC1.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_1
ADC1.ContinuousConvMode=DISABLE
ADC1.DataAlign=ADC_DATAALIGN_RIGHT
ADC1.DiscontinuousConvMode=DISABLE
ADC1.EnableAnalogWatchDog=false
ADC1.EnableRegularConversion=ENABLE
ADC1.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T4_CC4
ADC1.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,NbrOfConversionFlag,ExternalTrigConv,NbrOfConversion,master,DataAlign,ScanConvMode,ContinuousConvMode,DiscontinuousConvMode,EnableRegularConversion,InjNumberOfConversion,EnableAnalogWatchDog
ADC1.InjNumberOfConversion=0
ADC1.NbrOfConversion=1
ADC1.NbrOfConversionFlag=1
ADC1.Rank-0\#ChannelRegularConversion=1
ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_1CYCLE_5
ADC1.ScanConvMode=ADC_SCAN_DISABLE
ADC1.master=1
Dma.ADC1.0.Direction=DMA_PERIPH_TO_MEMORY
Dma.ADC1.0.Instance=DMA1_Channel1
Dma.ADC1.0.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.ADC1.0.MemInc=DMA_MINC_ENABLE
Dma.ADC1.0.Mode=DMA_CIRCULAR
Dma.ADC1.0.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.ADC1.0.PeriphInc=DMA_PINC_DISABLE
Dma.ADC1.0.Priority=DMA_PRIORITY_LOW
Dma.ADC1.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
Dma.Request0=ADC1
Dma.RequestsNb=1
File.Version=6
KeepUserPlacement=true
Mcu.Family=STM32F1
Mcu.IP0=ADC1
Mcu.IP1=DMA
Mcu.IP10=USB_DEVICE
Mcu.IP2=NVIC
Mcu.IP3=RCC
Mcu.IP4=SYS
Mcu.IP5=TIM1
Mcu.IP6=TIM2
Mcu.IP7=TIM3
Mcu.IP8=TIM4
Mcu.IP9=USB
Mcu.IPNb=11
Mcu.Name=STM32F103C(8-B)Tx
Mcu.Package=LQFP48
Mcu.Pin0=PD0-OSC_IN
Mcu.Pin1=PD1-OSC_OUT
Mcu.Pin10=VP_SYS_VS_Systick
Mcu.Pin11=VP_TIM1_VS_ClockSourceINT
Mcu.Pin12=VP_TIM1_VS_OPM
Mcu.Pin13=VP_TIM2_VS_ControllerModeTrigger
Mcu.Pin14=VP_TIM2_VS_ClockSourceINT
Mcu.Pin15=VP_TIM2_VS_ClockSourceITR
Mcu.Pin16=VP_TIM3_VS_ControllerModeTrigger
Mcu.Pin17=VP_TIM3_VS_ClockSourceINT
Mcu.Pin18=VP_TIM3_VS_ClockSourceITR
Mcu.Pin19=VP_TIM4_VS_ControllerModeTrigger
Mcu.Pin2=PA0-WKUP
Mcu.Pin20=VP_TIM4_VS_ClockSourceINT
Mcu.Pin21=VP_TIM4_VS_ClockSourceITR
Mcu.Pin22=VP_USB_DEVICE_VS_USB_DEVICE_CDC_FS
Mcu.Pin3=PA1
Mcu.Pin4=PA6
Mcu.Pin5=PA11
Mcu.Pin6=PA12
Mcu.Pin7=PA13
Mcu.Pin8=PA14
Mcu.Pin9=PB9
Mcu.PinsNb=23
Mcu.ThirdPartyNb=0
Mcu.UserConstants=
Mcu.UserName=STM32F103C8Tx
MxCube.Version=4.27.0
MxDb.Version=DB.4.0.270
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.DMA1_Channel1_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.SysTick_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.USB_LP_CAN1_RX0_IRQn=true\:0\:0\:false\:false\:true\:false
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false
PA0-WKUP.GPIOParameters=GPIO_Speed
PA0-WKUP.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PA0-WKUP.Signal=S_TIM2_CH1_ETR
PA1.Signal=ADCx_IN1
PA11.Mode=Device
PA11.Signal=USB_DM
PA12.Mode=Device
PA12.Signal=USB_DP
PA13.Mode=Serial_Wire
PA13.Signal=SYS_JTMS-SWDIO
PA14.Mode=Serial_Wire
PA14.Signal=SYS_JTCK-SWCLK
PA6.GPIOParameters=GPIO_Speed
PA6.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PA6.Signal=S_TIM3_CH1
PB9.GPIOParameters=GPIO_Speed
PB9.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PB9.Signal=S_TIM4_CH4
PCC.Checker=false
PCC.Line=STM32F103
PCC.MCU=STM32F103C(8-B)Tx
PCC.PartNumber=STM32F103C8Tx
PCC.Seq0=0
PCC.Series=STM32F1
PCC.Temperature=25
PCC.Vdd=3.3
PD0-OSC_IN.Mode=HSE-External-Oscillator
PD0-OSC_IN.Signal=RCC_OSC_IN
PD1-OSC_OUT.Mode=HSE-External-Oscillator
PD1-OSC_OUT.Signal=RCC_OSC_OUT
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
ProjectManager.CompilerOptimize=6
ProjectManager.ComputerToolchain=false
ProjectManager.CoupleFile=false
ProjectManager.CustomerFirmwarePackage=
ProjectManager.DefaultFWLocation=true
ProjectManager.DeletePrevious=true
ProjectManager.DeviceId=STM32F103C8Tx
ProjectManager.FirmwarePackage=STM32Cube FW_F1 V1.6.1
ProjectManager.FreePins=false
ProjectManager.HalAssertFull=false
ProjectManager.HeapSize=0x200
ProjectManager.KeepUserCode=true
ProjectManager.LastFirmware=true
ProjectManager.LibraryCopy=1
ProjectManager.MainLocation=Src
ProjectManager.NoMain=false
ProjectManager.PreviousToolchain=TrueSTUDIO
ProjectManager.ProjectBuild=false
ProjectManager.ProjectFileName=timers.ioc
ProjectManager.ProjectName=timers
ProjectManager.StackSize=0x400
ProjectManager.TargetToolchain=TrueSTUDIO
ProjectManager.ToolChainLocation=
ProjectManager.UnderRoot=true
ProjectManager.functionlistsort=1-MX_GPIO_Init-GPIO-false-HAL-true,2-MX_DMA_Init-DMA-false-HAL-true,3-SystemClock_Config-RCC-false-HAL-false,4-MX_TIM1_Init-TIM1-false-LL-true,5-MX_TIM2_Init-TIM2-false-LL-true,6-MX_TIM3_Init-TIM3-false-LL-true,7-MX_TIM4_Init-TIM4-false-LL-true,8-MX_ADC1_Init-ADC1-false-HAL-true,9-MX_USB_DEVICE_Init-USB_DEVICE-false-HAL-true
RCC.ADCFreqValue=12000000
RCC.ADCPresc=RCC_ADCPCLK2_DIV6
RCC.AHBFreq_Value=72000000
RCC.APB1CLKDivider=RCC_HCLK_DIV2
RCC.APB1Freq_Value=36000000
RCC.APB1TimFreq_Value=72000000
RCC.APB2Freq_Value=72000000
RCC.APB2TimFreq_Value=72000000
RCC.FCLKCortexFreq_Value=72000000
RCC.FamilyName=M
RCC.HCLKFreq_Value=72000000
RCC.IPParameters=ADCFreqValue,ADCPresc,AHBFreq_Value,APB1CLKDivider,APB1Freq_Value,APB1TimFreq_Value,APB2Freq_Value,APB2TimFreq_Value,FCLKCortexFreq_Value,FamilyName,HCLKFreq_Value,MCOFreq_Value,PLLCLKFreq_Value,PLLMCOFreq_Value,PLLMUL,PLLSourceVirtual,SYSCLKFreq_VALUE,SYSCLKSource,TimSysFreq_Value,USBFreq_Value,USBPrescaler,VCOOutput2Freq_Value
RCC.MCOFreq_Value=72000000
RCC.PLLCLKFreq_Value=72000000
RCC.PLLMCOFreq_Value=36000000
RCC.PLLMUL=RCC_PLL_MUL9
RCC.PLLSourceVirtual=RCC_PLLSOURCE_HSE
RCC.SYSCLKFreq_VALUE=72000000
RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.TimSysFreq_Value=72000000
RCC.USBFreq_Value=48000000
RCC.USBPrescaler=RCC_USBCLKSOURCE_PLL_DIV1_5
RCC.VCOOutput2Freq_Value=8000000
SH.ADCx_IN1.0=ADC1_IN1,IN1
SH.ADCx_IN1.ConfNb=1
SH.S_TIM2_CH1_ETR.0=TIM2_CH1,PWM Generation1 CH1
SH.S_TIM2_CH1_ETR.ConfNb=1
SH.S_TIM3_CH1.0=TIM3_CH1,PWM Generation1 CH1
SH.S_TIM3_CH1.ConfNb=1
SH.S_TIM4_CH4.0=TIM4_CH4,PWM Generation4 CH4
SH.S_TIM4_CH4.ConfNb=1
TIM1.IPParameters=TIM_MasterSlaveMode,TIM_MasterOutputTrigger,Prescaler,Period
TIM1.Period=9999
TIM1.Prescaler=7199
TIM1.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
TIM1.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_ENABLE
TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
TIM2.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
TIM2.IPParameters=TIM_MasterSlaveMode,Prescaler,Period,Channel-PWM Generation1 CH1,Pulse-PWM Generation1 CH1,AutoReloadPreload
TIM2.Period=79
TIM2.Prescaler=71
TIM2.Pulse-PWM\ Generation1\ CH1=40
TIM2.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE
TIM3.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
TIM3.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
TIM3.CounterMode=TIM_COUNTERMODE_CENTERALIGNED3
TIM3.IPParameters=Prescaler,Period,OCPolarity_1,CounterMode,Channel-PWM Generation1 CH1,TIM_MasterOutputTrigger,Pulse-PWM Generation1 CH1,AutoReloadPreload
TIM3.OCPolarity_1=TIM_OCPOLARITY_HIGH
TIM3.Period=80
TIM3.Prescaler=35
TIM3.Pulse-PWM\ Generation1\ CH1=40
TIM3.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
TIM4.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_ENABLE
TIM4.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
TIM4.CounterMode=TIM_COUNTERMODE_UP
TIM4.IPParameters=Prescaler,Period,Channel-PWM Generation4 CH4,Pulse-PWM Generation4 CH4,CounterMode,AutoReloadPreload,OCMode_PWM-PWM Generation4 CH4
TIM4.OCMode_PWM-PWM\ Generation4\ CH4=TIM_OCMODE_PWM2
TIM4.Period=7
TIM4.Prescaler=17
TIM4.Pulse-PWM\ Generation4\ CH4=4
USB_DEVICE.CLASS_NAME_FS=CDC
USB_DEVICE.IPParameters=VirtualMode,VirtualModeFS,CLASS_NAME_FS
USB_DEVICE.VirtualMode=Cdc
USB_DEVICE.VirtualModeFS=Cdc_FS
VP_SYS_VS_Systick.Mode=SysTick
VP_SYS_VS_Systick.Signal=SYS_VS_Systick
VP_TIM1_VS_ClockSourceINT.Mode=Internal
VP_TIM1_VS_ClockSourceINT.Signal=TIM1_VS_ClockSourceINT
VP_TIM1_VS_OPM.Mode=OPM_bit
VP_TIM1_VS_OPM.Signal=TIM1_VS_OPM
VP_TIM2_VS_ClockSourceINT.Mode=Internal
VP_TIM2_VS_ClockSourceINT.Signal=TIM2_VS_ClockSourceINT
VP_TIM2_VS_ClockSourceITR.Mode=TriggerSource_ITR0
VP_TIM2_VS_ClockSourceITR.Signal=TIM2_VS_ClockSourceITR
VP_TIM2_VS_ControllerModeTrigger.Mode=Trigger Mode
VP_TIM2_VS_ControllerModeTrigger.Signal=TIM2_VS_ControllerModeTrigger
VP_TIM3_VS_ClockSourceINT.Mode=Internal
VP_TIM3_VS_ClockSourceINT.Signal=TIM3_VS_ClockSourceINT
VP_TIM3_VS_ClockSourceITR.Mode=TriggerSource_ITR0
VP_TIM3_VS_ClockSourceITR.Signal=TIM3_VS_ClockSourceITR
VP_TIM3_VS_ControllerModeTrigger.Mode=Trigger Mode
VP_TIM3_VS_ControllerModeTrigger.Signal=TIM3_VS_ControllerModeTrigger
VP_TIM4_VS_ClockSourceINT.Mode=Internal
VP_TIM4_VS_ClockSourceINT.Signal=TIM4_VS_ClockSourceINT
VP_TIM4_VS_ClockSourceITR.Mode=TriggerSource_ITR0
VP_TIM4_VS_ClockSourceITR.Signal=TIM4_VS_ClockSourceITR
VP_TIM4_VS_ControllerModeTrigger.Mode=Trigger Mode
VP_TIM4_VS_ControllerModeTrigger.Signal=TIM4_VS_ControllerModeTrigger
VP_USB_DEVICE_VS_USB_DEVICE_CDC_FS.Mode=CDC_FS
VP_USB_DEVICE_VS_USB_DEVICE_CDC_FS.Signal=USB_DEVICE_VS_USB_DEVICE_CDC_FS
board=custom

В кубе при настройке АЦП можно выбрать количество циклов на преобразование. Минимальное 1.5 , максимальное 239.5 - при цикле 12МГц или 0.08333мкс. Множим и получаем что преобразование может быть до 20 мкс. Зато выходное сопротивление источника сигнала при этом может быть большим. Кроме того после преобразования полученный результат нужно передать в выходной регистр, а цепи АЦП подготовить к следующему преобразованию. На это тоже уходит время и полный цикл от одного преобразования до другого на может быть меньше 1 мкс. Больше может. Всё это описано в datasheet.  

a5021
Offline
Зарегистрирован: 07.07.2013

nik182 пишет:

В кубе при настройке АЦП можно выбрать количество циклов на преобразование. Минимальное 1.5 , максимальное 239.5 - при цикле 12МГц или 0.08333мкс. Множим и получаем что преобразование может быть до 20 мкс.

Указанное время -- это не время преобразования, а время семплирования -- время, которое отводится на заряд емкости УВХ. Для расчета полного времени преобразования к времени семплирования надо добавить 12.5 тактов цифрования.

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

Первый раз слышу.

Цитата:
Больше может. Всё это описано в datasheet.

Знать у нас разные даташиты.

nik182
Онлайн
Зарегистрирован: 04.05.2015

Юрий48 пишет:

То есть, даже, если принять во внимание только преобразование (12,5) без паузы, то его время при частоте тактирования 12 мГц будет чуть больше 1 мкс, что не есть хорошо.

Вот именно. Вообще в datasheet указывается что максимальная частота внешнего тактирования не более 823кГц при самплинге АЦП 14 МГц. У нас 12МГц -  на 1 мкс для одного АЦП это ни как не тянет. И то эти цифры идут под грифом: параметры дизайна, в реальных устройствах не гарантируется. 

Юрий48
Offline
Зарегистрирован: 19.06.2018

nik182 пишет:

Вот именно. Вообще в datasheet указывается что максимальная частота внешнего тактирования не более 823кГц при самплинге АЦП 14 МГц. У нас 12МГц -  на 1 мкс для одного АЦП это ни как не тянет. И то эти цифры идут под грифом: параметры дизайна, в реальных устройствах не гарантируется. 

Эти моменты я чувствую и держу под вниманием и для реальных условий их скорректирую. Главное что бы база была правильной. В свзи с этим проверте пожалуйста код, представленный в посте 92. Там что то в конце на мой взгляд не так, поскольку после генерации в Кубе строки 459-466 пропадают. Всё, что дописано перед этим нормально. Предполагаю по не опытности, что при удалении чего то ненужного зацепили строчку /* USER CODE END 4 */