Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Вопрос по АЦП
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
SasaVitebsk
При работе с АЦП делаю заранее заданное демпфирование по каналам. То есть порядок выборки каналов у меня постоянно скачет. Это поменять нельзя. АЦП запущено в "Free running mode" и проверяется по таймеру. Время с момента переключения канала и до выборки значения АЦП вдвое превышает означенные 25 тактов (пробовал и увеличивать).

По каким-то, для меня непонятным причинам, иногда (достаточно редко) с АЦП в память попадает не текущее значение АЦП (точнее не то, что должно быть), а с предыдущего канала. Причём если я в данной точке останавливаюсь по JTAG, то в АЦП микросхемы вижу правильное значение. Например:
Код
    127             default:
    128               x0=Adc[TekChan].X1=ADCH;                            // Прочитать значение АЦП
   \                     ??pvPWWLvl1_7:
   \   000000B8   91300079           LDS     R19, 121
   \                     ??pvPWWLvl1_9:
   \   000000BC   8334               STD     Z+4, R19
   \   000000BE   2E23               MOV     R2, R19

То есть по databreakpoint останавливаюсь в последней строчке и вижу в АЦП значение FF к примеру, а в ячейку уже занесено 83. Каналы и всё прочее выставляется верно.

Создаётся впечатление, что АЦП не успевает завершить операцию. Но, как я уже писал, при увеличении времени в разы сама ошибка остаётся.

Может я чего не знаю. Может необходимо как то обновить значение. Типа прочитать два раза или что-то ещё.

Я в непонятках.
Baser
Может быть там присутствуют наложения циклов АЦП и таймера из-за несинхронности процесса. Момент переключения каналов иногда попадает на такт АЦП, когда каналы переключать нельзя.
Но я "Free running mode" никогда не применял, поэтому это только догадки. В datasheet этот момент описан как-то обтекаемо и я до конца его смысл не понимаю sad.gif Там в явном виде не написано, что допускается переключать канал в любое время.

А почему вы применили "Free running mode"? Я всегда применяю одиночные преобразования по таймеру и никогда таких проблем не имел. Для гарантии окончания переходных процессов после переключения каналов можно просто проводить АЦП через раз:
перекл.канал / вкл.АЦП / прочитали результат, перекл.канал / вкл.АЦП / прочитали результат, перекл.канал / и т.д.
GDI
При переключении мультиплексора, вроде, рекомендуют делать холостое измерение, это даже гдето в даташитах написано...
Сергей Борщ
Я тоже склоняюсь к мысли, что это биения от несовпадения частоты преобразования и частоты чтения/смены канала.
Цитата(SasaVitebsk @ Jan 15 2008, 00:53) *
АЦП запущено в "Free running mode" и проверяется по таймеру.
Т.е. вы его читаете в прерывании таймера и иногда меняете канал? В описании сказано примерно так, что если преобразование уже началось, то смена канала подействует только при следующем преобразовании. Это не ваш случай?
Если я правильно понял раздел ADC Input Channels, то в вашем случае сделал бы так: сбросил ADIF, вычитал ADCH, проверил бы ADIF, если он снова стоит - повторил бы сброс и чтение, записал в ADMUX новый канал, дождался ADIF, отбросил бы этот результат (он скорее всего от предыдущего канала), ждал следующего ADIF.

Но я тоже не использовал его в режиме FreeRunning.

Цитата(GDI @ Jan 15 2008, 10:52) *
это даже гдето в даташитах написано...
Где конкретно? Покажите мне это место в даташите.
GDI
Ну может не в даташитах smile.gif но на форуме эта тема поднималась и не раз... и каждый раз все говорили - "ну его ф топку этот фри ран..." smile.gif
Сергей Борщ
Цитата(GDI @ Jan 15 2008, 12:04) *
"ну его ф топку этот фри ран..." smile.gif
Вот и я при чтении даташита пришел к тому же выводу - в режиме FreeRunning, если канал переключается не в прерывании АЦП первое измерение после переключения канала может относится как к старому, так и к новому каналу, поэтому его нужно отбросить. Это логически вытекает из описания и понятно. Но именно в режиме FreeRunning, если канал переключается не в прерывании АЦП, а не просто "При переключении мультиплексора". Поэтому я и уточнил - может есть еще какие-то тонкости.
xemul
Цитата(Сергей Борщ @ Jan 15 2008, 11:58) *
Где конкретно? Покажите мне это место в даташите.

Рекомендация о пропуске первого результата при смене канала во Free Running Mode действительно где-то была (то ли в даташитах, то ли в аппнотах).
Цитата
Вот и я при чтении даташита пришел к тому же выводу - в режиме FreeRunning, если канал переключается не в прерывании АЦП первое измерение после переключения канала может относится как к старому, так и к новому каналу, поэтому его нужно отбросить.

Не совсем так. С точки зрения АЦП измерение по-любому будет относиться к старому каналу, а переключение на новый произойдет только после окончания преобразования по старому каналу. Более того, старый результат будет читаться вплоть до окончания первого преобразования по новому каналу.
Ну а с точки зрения программы действительно непредсказуемо.

Если чтение выполняется по какому-нибудь тику с периодом, заведомо большим времени преобразования АЦП, то проще всего переключение и чтение синхронизировать софтовым флагом, взводимым при переключении канала и сбрасываемым при чтении результата АЦП.
Код
// on ADC channel change
   SkipADCResult = 1;
...
// on ADC read
   if(SkipADCResult) SkipADCResult = 0;
   else x0=Adc[TekChan].X1=ADCH;
pokos
Насколько я понимаю процесс жизнедеятельности этого АЦП, мультиплексор каналов никак не синхрится от самого процесса преобразования.
Поэтому вполне очевидно, что при переключении мультиплексора в произвольные моменты времени на выходе АЦП могут быть любые результаты, в том числе, и вообще не относящиеся к какому-либо каналу. Ведь выборка УВХ длится 1,5 такта.
xemul
Цитата(pokos @ Jan 15 2008, 17:01) *
Насколько я понимаю процесс жизнедеятельности этого АЦП, мультиплексор каналов никак не синхрится от самого процесса преобразования.
Поэтому вполне очевидно, что при переключении мультиплексора в произвольные моменты времени на выходе АЦП могут быть любые результаты, в том числе, и вообще не относящиеся к какому-либо каналу. Ведь выборка УВХ длится 1,5 такта.

Неправильно понимаете.
Физическое переключение каналов выполняется только по окончании текущего преобразования.
Цитата
The MUXn and REFS1:0 bits in the ADMUX Register are single buffered through a temporary
register to which the CPU has random access. This ensures that the channels and reference
selection only takes place at a safe point during the conversion. The channel and reference
selection is continuously updated until a conversion is started. Once the conversion starts, the
channel and reference selection is locked to ensure a sufficient sampling time for the ADC.

Соответственно выборка производится с совершенно конкретного канала.
Сергей Борщ
Цитата(pokos @ Jan 15 2008, 16:01) *
Насколько я понимаю процесс жизнедеятельности этого АЦП, мультиплексор каналов никак не синхрится от самого процесса преобразования.
Напрасно вы так понимаете. В описании написано другое - запись из регистра в мультиплексор блокируется в начале преобразования. Т.е. что было в регистре на момент начала преобьразования - то и окажется в мультиплексоре.


Цитата(xemul @ Jan 15 2008, 14:04) *
Не совсем так. С точки зрения АЦП измерение по-любому будет относиться к старому каналу, а переключение на новый произойдет только после окончания преобразования по старому каналу.
Ну да. А поскольку оно FreeRunning, то предугадать, когда этот момент наступит относительно нашей записи в регистр мультиплексора невозможно.
Anjey_N
Хочу тоже задать вопрос!
МК ATmega8, задействован только один канал.
При непрерывном преобразовании нужно ли разрешать прерывания АЦП?

И ещё такой вопрос. Можно ли в режиме одиночного преобразования запускать АЦП (ADSC=1) один раз в 1 мсек, например в прерываниях таймера ТС0. Сделать 16 измерений, все 16 результатов просуммировать и сумму сдвинуть вправо на 4 разряда. Такими действиями можно убрать колебания 2 мл разрядов АЦП? И ещё: нужно ли потом результат обрабатывать по алгоритму скользящего среднего?
Может туманно написал, так извините! help.gif
Baser
Цитата(Anjey_N @ Jan 15 2008, 19:03) *
При непрерывном преобразовании нужно ли разрешать прерывания АЦП?

Можно не разрешать

Цитата
Можно ли в режиме одиночного преобразования запускать АЦП (ADSC=1) один раз в 1 мсек, например в прерываниях таймера ТС0. Сделать 16 измерений, все 16 результатов просуммировать и сумму сдвинуть вправо на 4 разряда. Такими действиями можно убрать колебания 2 мл разрядов АЦП?

Можно. Это и есть оверсамплинг.

Цитата
И ещё: нужно ли потом результат обрабатывать по алгоритму скользящего среднего?

А это как вы хотите. Если больше колебаний не наблюдается, то зачем это делать?
Anjey_N
Цитата(Baser @ Jan 15 2008, 21:24) *
Можно не разрешать
Можно. Это и есть оверсамплинг.
А это как вы хотите. Если больше колебаний не наблюдается, то зачем это делать?


спасибо, это то что я хотел услышать!
SasaVitebsk
Не буду утверждать, так как для каких либо утверждений требуется произвести соответствующие исследования. Но на мой взгляд, я делаю всё верно. Тем не менее хомуты есть. Особенно настораживает то что происходят они не регулярно, а крайне редко. Что наводит на размышления о каких то хомутах отнюдь не в проге.

Спасибо всем - я всё устранил. Теперь всё работает.

Общие замечания такие. Согласно даташиту (в моём прочтении) в режиме FRM, не требуется сбрасывать флаг ADIF как и не требуется запускать преобразование. Это происходит автоматически. Ты можешь просто читать текущее значение. Я не нашёл время первого измерения относительно смены канала, но похоже оно должно быть 25 тактов АЦП. Все последующие по 13 тактов. Таким образом максимальное время выставления нового значения должно быть не больше 13*2+25=51 такт. Я проверял на 75. Это как-то не согласуется с даташитом. В то же время прямого хомута в проге нет, так как я сейчас просто перевёл прогу на одиночное преобразование (Изменил инициализацию и вставил запуск) и всё стало работать как часы.

Ранее я применял FRM правда на м8/м88 и время считывания показаний значительно превышало время преобразования. Всё работало. Остаётся только гадать сколько именно требуется АЦП для правильного отображения инфы.

2 Anjey_N

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

Алгоритм скользящего среднего обычно делают так. Хранят в кольцевом буфере 16 последних значений и хранят сумму этих значений (а не высчитывают её каждый раз) при получении нового значения прибавляют его к сумме, а из суммы вычитают затираемое. И затирают новым значением старое. Используют нужное колличество бит. Если буфер расчитан на 256 значений, то можно просто использовать старший байт суммы (при 8 битовом АЦП).

Колебания младшего бита АЦП убрать никаким образом нельзя так как это ваша точность. Таким способом вы просто сглаживаете кривую.
singlskv
Цитата(Сергей Борщ @ Jan 15 2008, 13:48) *
Вот и я при чтении даташита пришел к тому же выводу - в режиме FreeRunning, если канал переключается не в прерывании АЦП первое измерение после переключения канала может относится как к старому, так и к новому каналу, поэтому его нужно отбросить. Это логически вытекает из описания и понятно.
Ну и где Вы такое прочитали в даташите ?
Читаем внимательно:
In Free Running mode, always select the channel before starting the first conversion.
The channel selection may be changed one ADC clock cycle after writing one to ADSC.

Правда это относится только к старту первого преобразования...
Еще раз читаем внимательно, разговор о Free Running и выставление флага ADSC касается
только первого преобразования...
Для подтверждения этой мысли читаем даташит еще чуть подробнее:
Note that the conversion starts on the following rising ADC clock edge after ADSC is written.
А вот это нам указывает на то что преобразование будет запущенно не в момент когда мы скажем
ADSC, а когда наступит этот самый "rising ADC clock edge", тока не нужно расказывать что он придет
абсолютно в произвольный момент времени.... Он придет абсолютно синхронно через считаемое
количество тактов после ADEN и ADSC, тока нужно научится считать эти такты...
SasaVitebsk
Цитата(singlskv @ Jan 16 2008, 00:56) *
Он придет абсолютно синхронно через считаемое
количество тактов после ADEN и ADSC, тока нужно научится считать эти такты...


Ещё раз обращаю Ваше внимание на то, что ADSC, ADEN, ADIF я вообще не трогал. И согласно даташиту это не требуется (только ADSC и ADEN при инициализации АЦП). И так я работал без особых проблем в других проектах. Я предложил мой расчёт для получения результата. Тем не менее при съёме с периодом 75 тактов АЦП примерно 1 раз в 15 - 20 секунд и более (иногда до нескольких минут) АЦП запущенное на частоте 16М/128=125кГц каким то образом ловило предыдущий канал. Это я установил абсолютно точно. При одиночном запуске я сейчас считываю значение через 50 тактов. Всё работает отлично. Может быть при смене канала требуется дополнительное время? Это, в общем то логично, но я не нашёл такого упоминания в даташите. Может данный режим используется без смены канала?

ЗЫ: Кристалл at90can128. Там возможна такая фишка, как задание источника перезапуска АЦП в этом самом FRM. Этим источником может являться к примеру таймер.
pokos
Цитата(SasaVitebsk @ Jan 16 2008, 02:54) *
....Тем не менее при съёме с периодом 75 тактов АЦП примерно 1 раз в 15 - 20 секунд и более (иногда до нескольких минут) АЦП запущенное на частоте 16М/128=125кГц каким то образом ловило предыдущий канал.

Ну, вообще-то про предыдущий канал прямо написано:
"If both ADFR and ADEN is written to one, an interrupt event can occur at any time. If the
ADMUX Register is changed in this period, the user cannot tell if the next conversion is
based on the old or the new settings."

А про несинхронность обновления каналов, да, каюсь, был неправ. Нашёл таки, что оно действительно синхрится с преобразованием и в free-run режиме тоже.
SasaVitebsk
Цитата(pokos @ Jan 16 2008, 12:24) *
Ну, вообще-то про предыдущий канал прямо написано:


То есть иными словами, необходимо два раза считывать и брать второй результат. Я правильно понял? Либо наверное перестартовывать.

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

Вроде разобрались всем миром.
Всем спасибо за обсуждение.
pokos
"ADMUX can be safely updated in the following
ways:
1. When ADFR or ADEN is cleared.
2. During conversion, minimum one ADC clock cycle after the trigger event.
3. After a conversion, before the Interrupt Flag used as trigger source is cleared.
When updating ADMUX in one of these conditions, the new settings will affect the next
ADC conversion."
Сергей Борщ
Цитата(singlskv @ Jan 15 2008, 22:56) *
Ну и где Вы такое прочитали в даташите ?
.....
Он придет абсолютно синхронно через считаемое
количество тактов после ADEN и ADSC, тока нужно научится считать эти такты...
Вот любите вы поспорить. Если вы можете в программе с кучей прерываний в любой момент сказать, сколько тактов прошло с момента запуска АЦП (предварительно поделив на частоту тактирования АЦП, вычтя 25 и поделя на 13, учтя и время этих действий тоже) - то конечно, вы всегда знаете момент начала очередного преобразования. Но поскольку в реальной программе к моменту, когда вдруг потребовалось сменить канал прошло "хз сколько" тактов, то можно говорить, что момент смены неизвестен со всеми вытекающими.
oran-be
Я отжимался со свободным режимом - отлично работает. Я использовал прерывание. При входе в перывание сразу переключал мультиплексор на следующий канал. И никаких попаданий значений не туда, куда надо. Это похоже, у вас имеется некоторое, довольно большое значение выходного сопротивления источника сигнала. Если в этом случае вы переключите мультиплексор, подождете 25 тактов, и снимете данные, то получите некое значение, чем то похожее на предыдущий канал. но с поправкой на текущий. smile.gif В свободном режиме надо сначала выдернуть значение, после чего запустится новая конверсия, а потом сразу переключить мультиплексор на новый канал. Но значение будет получено с того канала, который был на момент выдергивания значения из АЦП!
singlskv
Цитата(Сергей Борщ @ Jan 16 2008, 18:19) *
Вот любите вы поспорить.
Ну иногда да, особенно когда начинают рассказывать про магические перетекания из
канала в канал, про необходимые пропуски преобразований, итд....
Сделай все по даташиту и проблем не будет.
Не, ну конечно Атмел сильно намудрил с описанием функциональности АЦП, но после
примерно 3-4 прочтения становится все более менее понятно...
Цитата
Если вы можете в программе с кучей прерываний в любой момент сказать, сколько тактов прошло с момента запуска АЦП (предварительно поделив на частоту тактирования АЦП, вычтя 25 и поделя на 13, учтя и время этих действий тоже) - то конечно, вы всегда знаете момент начала очередного преобразования. Но поскольку в реальной программе к моменту, когда вдруг потребовалось сменить канал прошло "хз сколько" тактов, то можно говорить, что момент смены неизвестен со всеми вытекающими.

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

P.S. Кстати, а что это за магическое число 25 про которое все здесь говорят ?
В даташите 25 это только первое преобразование после ADEN или преобразование после смены
Reference, во всех остальных случаях 25 неактуально.
Кстати, во всех своих прогах, при инициализации АЦП делаю это холостое "длинное" преобразование
на 25 тактов, ну и дальше 25 уже нигде не встречается...

P.P.S. Даташит раздел АЦП : "However, the simplest method is to..."
Если мы обсуждаем наипростейшие варианты работы с АЦП и никуда не торопимся, тогда конечно
проще подождать пару преобразований до получения правильного результата... smile.gif
_Pasha
В самом первом посте увидел инструкцию STD. У меня были глюки, когда было обращение STD /LDD к данным на границе 256-байтовой страницы. А где у Вас данные из АЦП?
Для меня это настолько геморройная тема была...
Представьте себе человека, находящегося на вершине безумия, который перенес блок данных в более безопасное место. Попустило. С тех пор, у кого ни спрашиваю, никто не знает такого глюка MCU core.
Причем, проявляется он как раз на частоте 16 Мгц. НАПРЯГИТЕ ПАМЯТЬ!!! Неужели ни у кого такого не было ???
_Diman_
Разьясните пожалуйста пару моментов.
13 - 260 μs Conversion Time
значит максимальная частота при 10бит ~77kHz?

By default, the successive approximation circuitry requires an input clock frequency
between 50 kHz and 200 kHz to get maximum resolution.
Как перевел диапазон 50-200к даёт максимальное разрешение.
Вообщем интересует, при каком значении частоты младший бит будет отличатся стабильностью? По эксперементам у меня при 125kHz (относительно 62,5 усред. на 8) начинает прыгать.
_Pasha
Цитата(_Diman_ @ Jan 17 2008, 01:42) *
По эксперементам у меня при 125kHz (относительно 62,5 усред. на 8) начинает прыгать.

Выходное сопротивление источника сказывается. Смысл: при работе sample&hold.
Какое у Вас вых. сопр.?
P.S. Проверить предположение можно, подключив кондюк ~470 пФ на вход АЦП.
_Diman_
Небольшоё, аккумулятор-> 510 ом и 0,2uF на землю. Я "относительно" написал потому что у меня два канала шим стабилизации тока, когда канал выкл, вызывается ф. ожидания стабилизации напряжения, затем его измерения, в это время второй канал наводит помехи. Вот их влияние намного видней на 125kHz, если б не чего не фонило, то считывалось бы одинаково. Просто не понял в даташите 13 - 260 μs Conversion Time а у меня 125к ->8uSek и еще
The ADC module contains a prescaler, which generates an acceptable ADC clock frequency
from any CPU frequency above 100 kHz.
И это 50-200к
Трудности с переводом (учил немецкий), ощущения что вылез за разрешение 10 бит, растолкуйте пожалуйста.
SasaVitebsk
Ещё раз поясняю младший бит не может "отличаться устойчивостью". Это ваша точность. Чтобы вам понятнее было представьте что у вас измеряемое значение 212.5 (в единицах АЦП). Соответственно АЦП будет показывать то 212, то 213. Несмотря на устойчивость. Поэтому сколько бы разрядов АЦП не имело всегда точность не менее +/- единица младшего разряда. По поводу погрешностей и прочего - почитайте даташит - там достаточно детально всё расписано и сведено в графики. От себя замечу, что точность АЦП AVR мне не понравилась. Правда я и не пытался добиваться высокоточных результатов. Применение внешнего АЦП как правило даёт более хорошие результаты по стабильности и точности.

По поводу каких-то ошибок в озу - нигде и никогда не слышал. То что вы видите в распечатке - это кусок листинга который формирует Си компилятор. Мне и в голову не приходило самому распределять там память. Как линкер её распределил, так я и работал. Из того что я уже приводил - понятно что ошибка возникает именно при чтении из АЦП. Вроде в этой ветке разобрались с её природой. То есть виноват я сам, так как нечего притягивать за уши те способы работы, которые для этого не предназначены. FRM предназначен для слежения за сигналом из одного канала, на сколько я понял. При применении перебора каналов необходимо использовать другие режимы работы АЦП, так как FRM не имеет в этом случае абсолютно никакого выигрыша, а доставляет головную боль.

2 _Diman_. По поводу задаваемого вами вопроса - поищите по форуму. Месяца два-три назад мощная тема была где спецы аналоговые это обсасывают. Так как есть понятие "максимальная частота преобразований" для АЦП, а есть понятие "полоса пропускания".
IGK
Цитата(SasaVitebsk @ Jan 17 2008, 01:33) *
... FRM предназначен для слежения за сигналом из одного канала, на сколько я понял. При применении перебора каналов необходимо использовать другие режимы работы АЦП, так как FRM не имеет в этом случае абсолютно никакого выигрыша, а доставляет головную боль.


Не выдержал... Саша, Вы же здесь один из гуру, не делайте таких скоропалительных выводов.
Нормально этот режим работает, если точно привязываться к записи мультиплексора. А это можно правильно сделать, только используя прерывание самого АЦП...
У меня в серийном изделии нормально оцифровываются все 8 каналов. Прерываний всего 3. Прерывание ADC используется для диспетчера задач и переключения каналов.
Но есть одно но :-), может, Вы именно на него наткнулись. Переключать каналы надо осторожно, у меня были глюки с данными, когда я слишком рано записывал новый адрес канала в ADMUX, до завершения новой выборки. Вот тогда и было некое дрожание и привирание в результатах. Тогда я увеличил выдержку до задания нового канала - вставил фоновую прогу формирования вспомогательных ШИМов,- и все зааработало.
Еще одно неудобство для меня заключалось в том, что я коммутирую входные сигналы на АЦП, и было довольно трудно рассчитать правильное время коммутации - нужно учитывать время успокоения усилителя и прочее.
Сейчас все работает правильно.
SasaVitebsk
Сейчас изделие отправили на натурные испытания на МТЗ. Это закрывается только первый этап из трёх данной темы. Потом начнётся устранение хомутов мелких и изделие ко мне ещё вернётся. Хорошо - уговорили. Я детально исследую данный вопрос, чтобы не быть голословным, и подробно доложу. smile.gif

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

Причина такой фразы проста. При FRM я использовал текст запуска преобразования:
Код
     if(TekChan==(MAXADC-1)) ADMUX=KADMUX | VIRTCHAN;    // Выбрать виртуальный канал
     else ADMUX=KADMUX | TekChan;                        // Выбрать канал


При одиночном преобразовании я делаю следующее
Код
     if(TekChan==(MAXADC-1)) ADMUX=KADMUX | VIRTCHAN;    // Выбрать виртуальный канал
     else ADMUX=KADMUX | TekChan;                        // Выбрать канал
     ADCSRA =KADCSRA;                                    // Начать отсчёт заново


Если я в автоматическом преобразовании сделаю перестарт, то будет то же самое что и при одиночном измерении. Если же я буду делать два чтения, то текст разрастётся ещё больше. Таким образом выигрыша по коду не получается. Выигрыша по точности - тоже. Так зачем же тогда?

Расчёт тактов у меня осуществляется очень точно. Правда не по прерыванию АЦП, а по таймеру. Давайте посчитаем вместе с вами. У меня прерывания идут с частотой 5кгц. То есть 200мкс. АЦП настроено на работу с частотой 16M/128 = 125kHz. Или 8мкс, то есть 25 тактов на прерывание. Естественно может быть +/-. Я пробовал от запуска до измерения 3 прерывания. Это 75 +/-. Согласно рассчётам я могу попасть на середину предыдущего преобразования (оно будет запорчено), и потом должно пройти 2 полных 25+13. То есть по максимуму 25+13+13 = 51 такт. Но у меня больше!!! Как минимум на одно! И, главное хомут возникает нерегулярно, а достаточно редко! Представляете что иногда проходит несколько минут!!!

Видимо при работе по прерыванию от АЦП гарантировано сбрасывается значение. А у меня работа несколько другая. У меня 3 канала АЦП + один частотный + 2 CAN. Я выставил канал - прочитал а потом к примеру перешёл на CAN, а АЦП - молотит. Через некоторое время, я, нечитая, перехожу на новый канал. Я не усматриваю здесь проблемы, но отрицать её - значит грешить против фактов!

Короче, давайте на время прервём пока беспредметный спор. Я точно и подробно диагностирую, так как у меня всё настроено, и приведу результаты в данной ветке. Думаю в среду где-то. Когда прибор у меня на столе лежать будет.
_Pasha
Цитата(IGK @ Jan 17 2008, 19:38) *
Но есть одно но :-), может, Вы именно на него наткнулись. Переключать каналы надо осторожно, у меня были глюки с данными, когда я слишком рано записывал новый адрес канала в ADMUX, до завершения новой выборки.


Чушь! Вы чего-то не договариваете. Потому что ADMUX буферизован, и "слишком рано" записать нельзя. "Слишком поздно" - это можно, но тогда на следующем прерывании будет принят предыдущий канал.
IGK
Цитата(_Pasha @ Jan 17 2008, 22:14) *
Чушь! Вы чего-то не договариваете. Потому что ADMUX буферизован, и "слишком рано" записать нельзя. "Слишком поздно" - это можно, но тогда на следующем прерывании будет принят предыдущий канал.


Да, каюсь, не договорил. У меня ADMUX+2 канала, именно чтобы не принимать предыдущий канал, и только тогда работает нормально...
Так, как в даташите, +1, как раз дает примерно то же, что описал SasaVitebsk. А насчет буферизации - я же писал, что не стал проводить всесторонних исследований, нормально заработало после сдвига.
Просто это первое, что я проверил. Дело в том, что у меня есть второе, совсем уж непредсказуемое по времени появления, прерывание. И первое, что бросилось мне в глаза, это периодическая и довольно редкая запись в мультиплексор после окончания нового цикла выборки. Я сформировал стробы на программный осциллограф и увидел довольно точную привязку этих событий к "дрожанию" данных. Ну и сделал сдвиг.

Pasha - я, например, точно не знаю, как там буфериризация сделана. Может, криво. Может, я сам код криво написал - хотя там ровно два байта того кода...

SasaVitebsk, буду ждать результатов.
singlskv
Цитата(SasaVitebsk @ Jan 16 2008, 02:54) *
Тем не менее при съёме с периодом 75 тактов АЦП примерно 1 раз в 15 - 20 секунд и более (иногда до нескольких минут) АЦП запущенное на частоте 16М/128=125кГц каким то образом ловило предыдущий канал. Это я установил абсолютно точно.

Кстати, тут еще вспомнил один нюансик в работе АЦП.
Судя по частоте появления ошибки, возможно Вы натыкаетесь на то, что преобразование
заканчивается сразу же после считывания ADCL и до считывания ADCH.
В этой ситуации запись со стороны ADC к ADCL и ADCH заблокированна и очередное
преобразование просто теряется.
_Pasha
Цитата(singlskv @ Jan 18 2008, 13:36) *
...сразу же после считывания ADCL и до считывания ADCH.
В этой ситуации запись со стороны ADC к ADCL и ADCH заблокированна и очередное
преобразование просто теряется.

Вообще-то в момент чтения ADCL содержимое ADCH записывается во временный регистр, извините за банальность smile.gif , и таким образом за раз читаются все 16(10) бит.
Было бы нелогично запрещать операции записи результата АЦП до завершения чтения временного регистра.
Вы ничего не путаете?
singlskv
Цитата(_Pasha @ Jan 18 2008, 17:26) *
Вообще-то в момент чтения ADCL содержимое ADCH записывается во временный регистр, извините за банальность smile.gif , и таким образом за раз читаются все 16(10) бит.
Было бы нелогично запрещать операции записи результата АЦП до завершения чтения временного регистра.
Вы ничего не путаете?
А где это вы нашли временный регистр для хранения ADCH ?
Вы случайно с таймерными 16бит регистрами не путаете ? smile.gif

Читаем внимательно даташит:
Otherwise, ADCL must be read first, then ADCH, to ensure that the content of the data
registers belongs to the same conversion. Once ADCL is read, ADC access to data registers is
blocked. This means that if ADCL has been read, and a conversion completes before ADCH is
read, neither register is updated and the result from the conversion is lost. When ADCH is read,
ADC access to the ADCH and ADCL Registers is re-enabled.
_Pasha
Цитата(singlskv @ Jan 18 2008, 18:35) *
А где это вы нашли временный регистр для хранения ADCH ?
Вы случайно с таймерными 16бит регистрами не путаете ? smile.gif
Читаем внимательно даташит:

Мда-с...
Проблема восприятия большого количества инфы...
Спасибо, вразумили. beer.gif
Тогда это действительно многое объясняет. Получается, что в авто-триггерных режимах даже из-за приоритета прерывания можно на граблях кататься.
SasaVitebsk
Ну это не мой вариант. Я работаю с 8-ми битным режимом и читаю только ADCH.
singlskv
Цитата(SasaVitebsk @ Jan 19 2008, 17:18) *
Ну это не мой вариант. Я работаю с 8-ми битным режимом и читаю только ADCH.
Ну тогда покажите код, телепатить не всегда получается. laughing.gif
_Pasha
Цитата(singlskv @ Jan 19 2008, 19:55) *
Ну тогда покажите код, телепатить не всегда получается. laughing.gif

Вы, сишники, народ абстрактный... biggrin.gif вот SasaVitebsk нам сишный код и покажет.
SasaVitebsk, листинг последний горбатый более-менее детально выложите.
singlskv
Цитата(_Pasha @ Jan 19 2008, 22:43) *
Вы, сишники, народ абстрактный... biggrin.gif
Если это ко мне, то я не синшник, у меня чуть другой ник.... sad.gif
Похоже с первого раза вразумить не удалось.... smile.gif
Вы видимо крутой асемблерщик ? хотите посоревноваться в написании кода на асм ?
тогда welcome, тока другую тему откройте...
Цитата
вот SasaVitebsk нам сишный код и покажет.
Ну и ? У него(сишного кода), есть очень четкий эквивалент на асм в виде
листинга компилятора...
_Pasha
Цитата(singlskv @ Jan 20 2008, 00:04) *
Вы видимо крутой асемблерщик ? хотите посоревноваться в написании кода на асм ?
тогда welcome, тока другую тему откройте...

Ну и ? У него(сишного кода), есть очень четкий эквивалент на асм в виде
листинга компилятора...


1. Открою, но чуть позже, извините.
2. У сишного кода нет четкого эквивалента. Оный был бы у Паскаля, но, к сожалению (моему), данная тема ниже плинтуса. Все. Пока флейма не будет.
defunct
2 SashaVitebsk.

IMHO считывать АЦП по таймеру немного неестветвенно (не природно) выглядит.

Я пользуюсь таким подходом - периодически запускаю АЦП в Single Conv режиме, с флажком "доверия" == "0", т.е. результат первого преобразования я всегда считаю неправильным. По прерыванию от АЦП вычитываю и выбрасываю результат, перезапускаю АЦП для того же самого канала и устанавливаю флажек "доверия" в "TRUE", что означает, что следующему результату однозначно можно доверять. На следующем прерывании от АЦП вычитываю и сохраняю результат текущего канала как (Vintegral = Vcurrent - Vaverage) и меняю канал, флажек "доверия" сбрасываю в "0" (т.е. первый рез-тат после смены канала будет выброшен), и так до тех пор пока не получу результат последнего, интересующего меня, канала.

Т.о. единожды запустив опрос АЦП - у меня получаются данные по всем каналам и опрос сам собой прекращается. Эта схема всегда работает - как часы.

Никогда не заморачивался с подсчетом тактов и ассемблером в этом вопросе.
alux
Имею проблему с выводом по UART результата измерения АЦП (Mega48, 20Мгц), работающего в free running mode. Настроен только на измерение с одного канала. Синхронизация передачи значения АЦП в BCD формате по UART осуществляется в главном цикле по флагу DISPLAY. UART работает с использованием прерывания (avr306 - interrupt controlled UART). В программе еще используется прерывание от таймера1 (вызывается через 10 мсек), в котором осуществляется динамическая индикация и обработка кнопок.
Код
void Initialise(void)
{
.................
  //ADC initialisation:
  ADMUX=(1<<REFS1)|(1<<REFS0);//Установка Photo_Channel (ADC0),  
                     //Internal 1.1V Voltage Reference with external capacitor at AREF pin
  //Старт непрерывного преобразования с частотой 156,25 кГц, Предделитель = 128,
  //Разрешить прерывание АЦП
  ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

////////////////////////////////////////////////
void main(void)
{
  Initialise();          
  __enable_interrupt();
  for(;;)
  {
    if(Flags&DISPLAY)
    {
      itoa(tmp, buf2);
      TransmitByte(*(buf2+0)+'0');
      TransmitByte(*(buf2+1)+'0');
      TransmitByte(*(buf2+2)+'0');
      TransmitByte(*(buf2+3)+'0');
      TransmitByte(13);   //CR
      TransmitByte(10);   //LF
//     delay_ms(250);
      Flags &= ~DISPLAY;
    }
   __sleep();   //Idle mode
  }
}

//////////////////////////////
#pragma vector=ADC_vect
__interrupt void ADC_ISR(void)
{
  acum += ADC;

  if(++SampleCounter == max)
  {
    value = acum/max;
    SampleCounter=0;
    acum=0;
    
      if(!(Flags&DISPLAY))
      {
        tmp = value;
        Flags |= DISPLAY;
      }
  }
....
}

Собственно, я подошел к сути проблемы.
На терминале получаю значения АЦП, чередующиеся с 0, типа так:

0031
0031
0030
0000
0000
0000
0031
0031
0030
0000
0000
0000

Если остановить таймер1, то выводит то, что положено - без нулей. Если запустить таймер1 и после очередной отправки результата по UART вставить задержку 250ms (в приведенном коде закомментирована), то выводит также без нулей. При уменьшении задержки до 100ms начинают вылазить нули.
Почему так происходит?

PS. Выводить результат на UART нужно максимально быстро. Single Mode не предлагать.
SasaVitebsk
А Flags у вас объявлен как volatile?
Просто похоже данная проблема к АЦП не имеет отношения. Обычный хомут какой-то. Попробуйте по шагам пройти, либо asm просмотрите.
alux
Цитата(SasaVitebsk @ Apr 15 2008, 12:19) *
А Flags у вас объявлен как volatile?

Flags объявлен глобально, без volatile. volatile в данном случае здесь ни при чем. Но на всякий случай проверил: с volatile проблема осталась. На мой взгляд проблема как-то связана с прерываниями. Если за время обработки прерывания от таймера1 произошло несколько прерываний от АЦП (которые имеют низкий приоритет), то после окончания обработки прерывания от таймера1 попадаем в обработчик прерывания от АЦП. В момент между считываниями ADCL и ADCH возможна ситуация, когда происходит следующее преобразование АЦП, и предыдущий результат теряется. Но не обнуляется ведь!
Это только моя версия происходящего. Какие будут еще ваши предположения?

PS. Чем объяснить такую строгую периодичность: после трех правильных данных идут три нулевых?

PS2. Версию, что таймер своими шумами влияет на работу АЦП, я тоже отбрасываю.
Может быть нехватка стека? Но я не использую ресурсоемких функций. И пробовал увеличить CSTACK(0x100), RSTACK(0x64). Не помогло.
Сергей Борщ
Цитата(alux @ Apr 15 2008, 15:38) *
volatile в данном случае здесь ни при чем.
Но Flags должен быть volatile, также как и tmp. Лучше сразу писать правильно, чем ждать, когда начнется "при чем". Есть еще одно потенциальное место для бага: Flags &= ~DISPLAY; Оно компилируется примерно в такое:
Код
register Tmp = Flags; (1)
Tmp = Tmp & DISPLAY; (2)
Flags = Tmp; (3)
Что произойдет, если между командами 1 и 3 произойдет прерывание, изменяющее Flags? Обращения к подобным переменным надо делать атомарными, т.е. с запретом прерываний.
Цитата(alux @ Apr 15 2008, 15:38) *
Но не обнуляется ведь!
Попробуйте присваивать tmp в прерывании константу, т.е. исключите АЦП из получения результата.
alux
Цитата(Сергей Борщ @ Apr 15 2008, 17:07) *
Попробуйте присваивать tmp в прерывании константу, т.е. исключите АЦП из получения результата.

Присвоил переменным Flags, value, tmp квалификатор volatile. В прерывании АЦП делаю tmp=123;. В итоге на терминал выводится последовательность 123 без промежуточных нулей.
Цитата(Сергей Борщ @ Apr 15 2008, 17:07) *
Обращения к подобным переменным надо делать атомарными, т.е. с запретом прерываний.

В главном цикле при обнулении флага сделал так:
Код
oldState = __save_interrupt();
__disable_interrupt();
      Flags &= ~DISPLAY;  // Critical section goes here
__restore_interrupt(oldState);
..................

Еще вычитал в документации, что для неразрывного кода используется ключевое слово __monitor. Но оно, насколько я понял, относится к функциям.

PS. В общем, обнуление флага сделал в критической секции. Проблема осталась. Мне кажется проблема кроется в чем-то другом. Мои идеи иссякли. help.gif

PS2. Эксперимент с присвоением константы 123 переменной tmp показывает, что проблема кроется в АЦП? Т.е. так, как я описывал выше?


PS3. Прошу прощения за свою невнимательность... Проблемы, как таковой и не было!!!
Если мерять реальное напряжение (у меня меряется напряжение на фотодатчике), то все нормально - никаких нулей нет! При отсутствии отраженного сигнала (напряжение минимально), получаю промежуточные нули. Видимо, это действительно связано с шумами таймера, которое отражается в виде меандра амплитудой около 30 (в кодах АЦП).

Спасибо, СергейБорщ, SasaVitebsk, что указали на потенциальный источник ошибок.
SasaVitebsk
Предложу вам другой вариант работы с флагами. Компилится красивее.

Код
struct
{
uint8_t             bHartTx    :     1,                        // Признак работы модема в режиме передачи (1) или приёма (0)
                    bBit    :    1;                        // Значение текущего принимаемого бита
} Flag;

.....

Flag.bBit = 0;

....

if(Flag.bBit)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.