AVR. Учебный курс. Делаем АЦП из Аналогового компаратора

Так сложилось, что основной МК с которым я работаю постоянно и на котором делаю подавляющее большинство задач это ATTiny2313 — он популярен, а, главное, это самый дешевый контроллер из всей линейки AVR с числом ног более 8. Я их брал числом около трех сотен за 18, чтоль, рублей штучка. Но вот западло — у него нет АЦП. Совсем нет. А тут он понадобился — нужно замерить сигнал с датчика. Засада. Не переходить же из-за такой фигни на более фаршированную ATTiny26 — она и стоит дороже и фиг где купишь у нас, да и что тогда делать с той прорвой ATTiny2313 что уже закуплена? Пораскинул мозгами…

А почему бы не сварганить АЦП последовательного сравнения? Конечно, быстродействие и точность будет не фонтан, зато, не меняя тип МК и всего с двумя копеечными деталями дополнительного обвеса, я получу полноценный, хоть и тормозной, 8ми разрядный АЦП, вполне удовлетворяющий моим скромным запросам!

Как работает АЦП последовательного сравнения.
Что у нас есть в ATTiny2313 аналогового? Правильно — аналоговый компаратор. Теперь достаточно подать на его вход замеряемый сигнал и методично сравнивать с опорным напряжением, линейно изменяя величину опорного напряжения. На каком из опорных напряжений произойдет сработка компаратора, тому и примерно равен измеряемый сигнал +/_ шаг изменения опорного.

Осталось получить переменное опорное напряжение, а чем, из сугубо цифрового выхода контроллера, можно вытянуть аналоговый сигнал? ШИМом! Предварительно его проинтегрировав. Для интеграции используем простейший RC фильтр. Конденсатор у нас будет интегрировать заряд, а резистор не даст сдохнуть порту при зарядке кондера. Результатом прогона ШИМ’а через подобный фильтр станет достаточно стабильное постоянное напряжение.

Осталось только прикинуть номиналы фильтра. Частота среза — частота, начиная с которой, фильтр начинает глушить переменную составляющую, у Г образного RC фильтра равна обратной величине из его постоянной времени w=1/RC. Я воткнул кондер на 0.33Е-6 Ф и резистор на 470 Ом, получилось что w=6447 рад/c. Поскольку угловая частота нам никуда не уперлась, то делим ее на 2pi = 6.28 получили около килогерца, 1026.6 Гц, если быть точным. Раз частота ШИМа у нас запросто может быть порядка десятков килогерц, то на выходе будет гладенькая такая постоянка, с незначительными пульсациями.

Теперь заворачиваем эту ботву на вход компаратора, на второй пускаем наш измеряемый сигнал и начинаем развлекаться с кодом. Получилась вот такая схема, собранная на той же макетке, что и прошлый раз. Тут, правда, не ATTiny2313, а Mega8 у которой АЦП есть, но мы пока забудем о его существовании. Красными линиями нарисован наш фильтр.

А это фотография платы. Резистор напаян снизу, на дорожки, а вот конденсаторы видно. Их два, так как я тут и на второй канал ШИМ повесил фильтр, правда так и не задействовал. Также видно, что белый провод от переменного резистора теперь идет на инверсный компаратора, а оранжевый идет с ноги конденсатора на прямой вход.

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

Прокомментирую лишь главную функцию Calc.
При вызове процедуры Calc у нас первым делом:

  1. Конфигурируется аналоговый компаратор и, главное, активизируются его прерывания. Описание и настройка компаратора уже были описаны ранее
  2. Затем в сканирующий регистр (R21) закидывается начальное значение сканирования 255.
  3. После чего это значение забрасывается в регистр сравнения ШИМ OCR1AL. ШИМ был заранее, в разделе init.asm сконфигурирован и запущен, так что сразу же на его выходе появляется сигнал скважностью (скважность это отношение длительности сигнала к периоду этого сигнала) 1 т.е., фактически, пока это просто единица.
  4. Выжидаем в функции Delay некоторое время, чтобы закончился переходный процесс (конденсатор не может мгновенно изменить свое напряжение)
  5. Уменьшем значение сканирующего регистра (что при загрузке в OCR1AL уменьшит скважность на 1/255), проверяем не стало ли оно нулю. Если нет, переходим на пункт 3.
Итогом станет последовательное уменьшение скважности сигнала с 1 до 0, с шагом в 1/255, что будет преобразовано после фильтра в уменьшающееся напряжение. А, так как в главной процедуре у меня Calc вызывается циклически, то на входе компаратора будет пила.

А остальное сделает компаратор. Как только он сработает, а он сработает в любом случае — ведь какое нибудь да напряжение обязательно есть на входе. То его прерывание возьмет текущее значение регистра сравнения OCR1AL, ведь именно на нем у нас совпало напряжение, и выдаст нам в качестве результата замера.

По окончании скан цикла, выданное значение отправляется в регистр UDR и улетает в комп, рисуя в окне Terminal’a график вращения ручки переменного резистора.

Как видно, вверху есть некоторый срез. Это связано с тем, что максимальное напряжение, которое может выдать нога МК, с учетом падений на всех резисторах, порядка 4.7 вольта, а с задающего потенциоматера я могу и все 5 выкрутить. Ну еще и верхушки заваливаются чуток. Если понизить частоту, то диапазон несколько расширится.

Вот так, применив немного смекалки, а также две дополнительные детали общей суммой в один рубль и десяток строк кода, я сэкономил кучу бабла =)

58 thoughts on “AVR. Учебный курс. Делаем АЦП из Аналогового компаратора”

  1. Вызывает жуткие сомнения линейность формируемой ШИМом пилы. Поскольку заряд емкости через резистор идет по экспоненте, (с ростом напряжения на емкости ток через резистор уменьшается), более-менее линейным будет только начальный участок пилы, когда напряжение на емкости много меньше полного.
    Для получения линейной пилы надо заряжать емкость стабильным током в течении заданного времени или до достижения заданного значения. Когда-то с простым стабилизатором тока на одном транзисторе с резистором в эмиттере и стабилитроном в базе мне удавалось получать нелинейность менее 1% (наилучшие результаты получались с ГТ109, хотя пробовал и КТ342). По совпадению считать значение счетчика.

    1. Так у меня частота ШИМ 33кГц, а частота пилы и 60Гц не будет. При том, что частота среза фильтра много выше. Т.е. между ступеньками все переходные процессы уже давным давно завершаются. Тут хочешь не хочешь линейность будет. Правда в переделах ступеньки компаратор может плавать как говно в проруби, но и это можно компенсировать, включая прерывание только после завершения всех переходных процессов. Я просто поленился оптимизировать.

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

  2. Все-таки, если не лень, померяй хотя бы точек десять в пределах шкалы. Интересно прикинуть погрешность. На активной — то нагрузке ШИМ линеен, а вот на RС…

      1. Довольно ровно, могло быть и хуже. До полшкалы так вообще хорошо, а последнюю треть можно не использовать или вносить поправку. Конечно, для более высокой точности (да и скорости) лучше использовать АЦП, но тема была для дешевых решений, а от них нет смысла требовать точность лучше 2-5%. Кстати, у нас ATTiny26L -8PI дешевле, чем ATTiny2313 — 20PU, и постоянно есть в продаже, а также ATTiny15L, ATTiny12-8SI. (я запас с год назад всех штук по 20).

  3. Доброго времени суток…
    респект твоей идее.
    честно это напомнило мне дельта-сигма модулятор.
    принцип тот же, но в данном случае, можно с поразительной легкостью
    пользоваться результатом преобразования (кодом), как я понял без доп расчетов
    Нелинейность ГОНА я думаю будет иметь место в любом случае из за интегратора.

    1. Можно же сделать 10битный ШИМ и скомпенсировать нелинейность за счет нелинейного изменения порога сравнения.

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

    1. Простота реализации и нетребовательность к ресурсам подкупают, да? Всего кондер и резистор, да три ноги МК, а каковы возможности :)

  4. Не, смотри, что выходит:
    про погрешность не думаем, все идеальное…
    Также Можно Провести ОБРАТНОЕ ПРЕОБРАЗОВАНИЕ!
    + доп память и получаем
    применение в дилеях, реверах, игрушках, диктофонах(чем черт не шутит)
    не говоря уже о самописцах и линиях задержки

    1. Частоты не хватит. Я выжал из него максимум 60Гц сканирования. После чего, пила начала безбожно заваливаться. А на экране пошли дикие шумы. Можно, конечно, с фильтром еще поиграться, уменьшить емкость, но не думаю, что это сильно спасет.

  5. минус как и у сигмы результат преобразования отличается на 1/255(для данного примера) от исх сигнала.
    А по дельта-сигме:
    1) посмотри, например, http://book.itep.ru/2/24/delt_241.htm
    (сейчас нагуглил)
    2) какие в ревербераторе ЛЕЛЬ мат обработки

    можно, конечно ЦАП, поставить вместо ШИМ, но от этого страдает идея :-)

    1. Вкрутим ЦАП получим либо интегральный АЦП, либо ту же дельтасигму — вопрос лишь в кодовом геморрое.

  6. Есть старый как все контроллеры без АЦП трюк — берем резистор на плюс , конденсатор — на землю, среднюю точку — на вход компаратора (одновременно и выход контроллера). Вторую ножку компаратора — на сигнал. Потом:
    — замыкаем выход контроллера/вход компаратора на землю (выдаем ноль), т.е. разряжаем конденсатор
    — Переключаем эту ножку на вход (конденсатор начинает заряжаться через резистор) и считаем время переключения выхода компаратора
    — По времени заряда можно однозначно определить напряжение на входе компаратора
    Если нужно линейно — вместо резистора можно поставить генератор тока.
    Такой способ например описан на microchip.com. Я думаю результат будет не хуже чем здесь, даже при бОльшей частоте опроса аналогового входа (т.к. измерение фактически происходит за один период сигнала).

  7. про то как сделать ацп на компараторе без ШИМа подробно описано в двух аппнотах
    как минимум
    а по поводу ШИМа думаю частоту выборок можно значительно увеличить если использовать метод половинного деления.
    тоесть выставляем 50%, считываем компаратор. если 0 то делим отрезок пополам
    50…100 и выставляем это значение: 50+(100-50)/2=75%. опять читаем компаратор. если 1 то делим отрезок 50…75 пополам 50+(75-50)/2=63% итд пока величина отрезка не станет равна 1%
    измеренным значением будет верхняя или нижняя граница отрезка
    в зависимости от соcтояния компаратора.
    разумеется проценты только для примера, делить надо динамический диапазон шима.
    если начинать поиск точки основываясь на результатах предыдущего измерения то еще быстрее.

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

      1. По сути, уважаемый Мах предложил то, что называется АЦП поразрядного уравновешивания. Только уж очень на пальцах объяснил :) И я совсем не понимаю, почему ты отбросил идею. На самом деле, из всех предложенных тут изгаляний (там и нелинейная пила, и генераторы тока, и прочая лобуда) поразрядное уравновешивание представляется самым адекватным поставленной задаче: минимум аппаратуры и простой код.
        Я AVR-ассемблера не знаю, раньше на 8080 да на 1816 работал, а дальше уж пришел Си и все победил. Но кажется мне (не на спор, конечно), что прога для такого АЦП будет практически такая же по размеру, как твой генератор ступенчатой пилы. Это раз.
        А два, это то, что мы тут всем миром ищем пояснения нелинейности, а ведь принцип-то простой: у нас вовсе не генератор пилы, коллеги! У нас генератор ступенчатого напряжения и на каждой ступеньке мы должны получить нечто установившееся, сравнить с измеряемым напряжением, а после либо зафиксировать переход, либо тыкать следующую ступеньку.
        И как же мы дожидаемся установления напряжения ступеньки? А выжидаем менее 65 мкс (если с частотой 60 Гц мы накручиваем 256 ступенек, да еще потом и разряжаем пилу). И подаем ступеньки на фильтр с ТАУ=160 мкс. Замечательно! Ступенька за 0,42 ТАУ пройдет около трети (!) своего пути к установившемуся значению. А мы ее в шею! Давай следующую! Время не ждет.
        А чего ему ждать? Его же, времечко наше, загнали в попу, т.к. и скорострельность хочицца, и задачу автор поставил недетскую — 256 ступенек прогенерить :)

        А теперь прикинь, как работает АЦП поразрядного уравновешивания. Мы делаем всего 8 (или 10) шагов. С твоим RC мы можем подождать на каждом шаге 400 мкс, это даст недоход при выставлении старшего разряда менее 1/521. Кстати, чуть изгалившись в коде, можем уменьшать время на каждом шаге. Но даже тупо 8 шагов по 400 мкс — вот тебе частота выборок не 60 Гц, а 1/(8*400E-6) — больше 300 Гц. И что-то мне подсказывает, что точность уже упрется в качество ШИМ-сигнала. Ведь здесь он берется не от источника опорного напряжения, а просто с ноги.

        Кстати, тот генератор пилы, о котором пишет Alb 17.12.08, это совсем не интегрирующий АЦП. Просто уравновешивающее напряжение растет, как сигнал интегратора — потому по времени можно найти до какого уровня дошло. Для него следует обеспечивать очень линейную пилу, быстро компарировать и хорошо измерять интервал времени. Никакие потуги сделать это так минималистски, как уважаемый автор со своим резистором и кондером — не пройдут. А если позволять расти по экспоненте, то и вовсе код возрастет, да еще и параметры R и C будут прямо влиять на точность.
        Ну, и про дельта-сигма… Лучше здесь не говорить. Это другая опера.

        Не хотел, естественно, никого задеть. Если что, то извините :)

        1. Ну кода там в самом деле много будет. У меня пустил шимом пилу и забыл. Весь код пилы около 6 команд (одна на запуск шима и еще 5 на цикл инкремента :)) А тут придется сверять, сравнивать, вычислять оставшуюся долю…

          1. Не рискну спорить. Было бы время, сварганил эти два варианта на своей (пока единственной из пощупаных) Атмеге48. Но, согласись, увеличение кода от 6 команд (на самом деле больше, ведь ты отлавливаешь сравнение и формируешь результат) до, скажем, 60 тех же команд… Фигня делов :)
            Ну, а АЦП какой выйдет… Трудно сказать. Я, чем больше думаю, тем больше подозреваю те самые ШИМ-выходы в ограниченном качестве. В смысле, что идет в нагрузку при 0 и при 1 — далеко не образцовые уровни. Поэтому подозреваю, что главное преимущество АЦП с поразрядным уравновешиванием — скорость. Если действительно оставлять незыблемым этот великолепный RC-усреднитель :)

  8. Маленькая поправочка — насколько мне помнится скважность это отношение периода колебания к длительности импульса. Она должна получаться всегда больше 1. А то что указано — это вроде называют «заполнением»

  9. Взял этот проект, чтобы попробывать сделать подобный АЦП, но с заведением на неинвертирующий вход линейно нарастающего напряжения от специального источника. Так этот проект был пот Atmega8, а мне нужно было для Atiny2313, как и советовалось в файле vectors.asm «…тупо скопировал из файла m8def.inc и заменил все .equ на .org, а потом дописал везде команду RETI.» Я также взял из tn2313def.inc, заменил все .equ на .org, удалил все знаки «=», кот. были в tn2313def.inc. Ну и соответственно сделал некоторые изменения в других файлах. Попробывал скомпилировать, ведь казалоь, что формально уже достаточно есть всего, чтоб компилировалось всё без ошибок. Но ошибки получились такие:
    vectors.asm(35): error: Use of undefined or forward referenced symbol ‘PCINTaddr’ in .org указывает на строчку .org PCINTaddr ;Pin Change Interrupt
    Следующая vectors.asm(36): error: Overlap in .cseg: addr=0x0 conflicts with 0x0:0x1
    Указывает на RETI после .org PCINTaddr ;Pin Change Interrupt
    Далее похожая vectors.asm(53): error: Overlap in .cseg: addr=0x7 conflicts with 0x7:0x8
    также указывает на RETI после .org URXCaddr
    И ещё несколько таких же. В чём здесь проблема, ничего не пойму?
    Вот здесь http://slil.ru/28269142 есть мой проект в Студии

    1. Во первых прибей линки которые для совместимости там даны. Это три в конце. ОРГ не позволяет две разные метки на один адрес.

      А вот чего оно не жрет PCINTaddr я так и не понял. Чем отличается от того же INT0???? глюк компилятора?
      Замени словесную метку прямым адресом, взятым из equ.

      Ну и там тебе еще SPH заремарить придется, т.к. у тиньки указатель стека однобайтный.

  10. Спасибо автору, буквально вчера выковырял динозавра AT90S1200 из мертвой материнки, и уже было посетовал над отсутствием АЦП на борту — а зря. Будем пробовать:)

  11. Извините. Не по теме спрашиваю. Посоветуйте пожалуйста интернет магазин проверенный вами где можно приобрести радиодетали

  12. Здравствуй DIHALT!
    Хочу собрать устройство контроля температуры двигателя мотоцикла на МК Atmega8.
    Задача такова: Есть два светодиода на панели, один желтый, второй красный.
    Нужно чтобы в диапазоне 90 градусов начинал моргать желтый светодиод, а когда температура превысила 100 градусов, загорался постоянным свечением красный светодиод.
    Вроде задача кажется легкой, но я не особо силен в контроллерах(Си немного понимаю)
    Подскажи пожалуйста, как можно организовать сие устройство? Можно обойтись компаратором?
    Или нужно подключать температурный датчик?

    1. Лучше применить датчик, можно аналоговый, с ним будет проще и много дешевле. Оцифровать на АЦП, откалибровать и все.

  13. Доброго времени суток, DI HALT.
    Подскажите где можно подробнее почитать о расчетах. Мне совершенно не понятно как Вы получили 1026,6Гц из 6,28. «0.33Е-6» это 0.33*E в -6 степени?

      1. Теперь все стало на много понятнее. Спасибо. Но все же хотелось бы подробнее почитать о расчете номиналов RC фильтра для разных нужд.
        Но есть еще один вопрос. Так как МК у меня AT90USB162 регистр UCSR1B(и другие примененные в этой статье) находятся в memory mapped. Записать в них число, представленным в проекте макросом, я не могу.
        Правильно ли я изменил макрос для Memory mapped регистров?(проверить трассировкой не могу так как для AT90USB162 нет симулятора)
        .MACRO outi
        .if @0 < 0x40
        LDI R16,@1
        OUT @0,R16
        .else
        LDI R16,@1
        STS @0,R16
        .endif
        .ENDMACRO
        И последний вопрос. (1<<RXEN1) — это значит что мы записываем "1" в бит RXEN1?

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

          Да, это и значит. Это операция сдвига. Т.е. мы 1 сдвигаем на позицию RXEN1

          1. Все заработало. Но вот проблема с компаратором. Я сделал что бы по прерыванию, от компаратора, у меня загорался светодиод. Опорное напряжение к примеру 2.5в так вот прерывание происходит(загорается светодиод) при значениях контролируемого напряжения в пределах от ~1.5 до ~3.5, что совершенно не правильно, как я понимаю. Код программы http://easyelectronics.ru/repository.php?act=view&id=119

            1. Тут надо осциллографом смотреть на то, что происходит с напряжениями. Вряд ли дело в коде, скорей всего компаратор прав. Тут же любой иголки хватит, чтобы схавать напряжение. Попробуй вместо пилы подать ровное напряжение с переменного резистора. Причем важно его не крутить, т.к. выбросы при вращении могут быть пойманы компаратором.

              1. Действительно. при подаче напряжения извне погрешность 0.05в. Тогда вопрос в другом. Допустим на термопаре при 41 градусе напряжение будет равно 2,5в. Данный в этой теме АЦП сработает и выдаст в регистр сравнения состояние ШИМ на момент срабатывания компаратора. Верно? Тогда получается что значение в регистре состояния каждый раз может быть разным(при 2.5в) если брать во внимание как вел себя компаратор. И если я не прав. То как я могу узнать или вычислить значение шим на момент срабатывания(к сожаления компьютера с Com портом нет, а юсб интерфейс для меня пока еще слишком сложен). И огромное Вам спасибо за столь оперативные ответы.

                1. Состояние ШИМ вы и так знаете, вы же его сами меняете. А текущее напряжение пилы можно вычислить умножая коэффициент заполнения на напряжение питания, которое даст максимум возможного для пилы.

                  1. Я задавал значение ШИМ как раз для того что бы увидеть при каком значение будет напряжение 2.5в, но ужаснулся от погрешности срабатывания компаратора. В последствии же как раз интересно задавать пилу ШИМом. А при определенных значениях термопары производить какие либо действия. Ну хотя я уже понял что если при OCR1AL,150 будет 2.5в то это и есть нужное значение для сравнения. но срабатывает оно все равно в диапазоне +/-1в от необходимого значения напряжения.

                    1. Тут только смотреть на сигнал с осциллографа, что там у вас за пила. Может на ней какие иголки есть или иные искажения формы сигнала. Тут от многого зависит. От разводки питания, например. ШИМ то он просто гонит временной сигнал, а реальная форма его зависит от того как энергия подводится к выводам. Компаратор то сравнивает по мгновенному значению и очень быстро. Будет там иголка — будет хватать когда попало.

  14. Привет ребят, как думаете — можно ли таким методом сделать логер напряжения на авто-аккумуляторе (напряжение в режиме простоя, в режиме работы генератора)?
    Насколько я понимаю, проблем особо нет, кроме стабилизированного питания самого контроллера и проводки, может еще стабилитрон на перед входом в компаратор воткнуть?

Добавить комментарий

Ваш e-mail не будет опубликован.

Перед отправкой формы:
Human test by Not Captcha