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 выкрутить. Ну еще и верхушки заваливаются чуток. Если понизить частоту, то диапазон несколько расширится.

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

Запись опубликована в рубрике AVR. Учебный курс с метками , , , , . Добавьте в закладки постоянную ссылку.

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

  1. Dlinyj говорит:

    Золотая идея. Дёшево и сердито, но смысл верный!

  2. _riko_ говорит:

    Голь на выдумки хитра!! )))))

  3. SWG говорит:

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

    • DI HALT говорит:

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

      • SWG говорит:

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

  4. SWG говорит:

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

    • DI HALT говорит:

      Во гляди. Ну, как ты и говорил, начиная с середины завал линейности.

      • SWG говорит:

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

  5. Elementus говорит:

    Человек знает свою профессию, молодец !!!!

  6. arkamax говорит:

    Пять баллов, и респект!

  7. adben говорит:

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

    • DI HALT говорит:

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

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

  8. adben говорит:

    Не, чем больше глобалю над темой, тем больше она нравится… :-)

  9. adben говорит:

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

    • DI HALT говорит:

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

  10. adben говорит:

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

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

  11. Alb говорит:

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

  12. Max говорит:

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

    • DI HALT говорит:

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

      • drvlas говорит:

        По сути, уважаемый Мах предложил то, что называется АЦП поразрядного уравновешивания. Только уж очень на пальцах объяснил :) И я совсем не понимаю, почему ты отбросил идею. На самом деле, из всех предложенных тут изгаляний (там и нелинейная пила, и генераторы тока, и прочая лобуда) поразрядное уравновешивание представляется самым адекватным поставленной задаче: минимум аппаратуры и простой код.
        Я 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 будут прямо влиять на точность.
        Ну, и про дельта-сигма… Лучше здесь не говорить. Это другая опера.

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

        • DI HALT говорит:

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

          • drvlas говорит:

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

  13. Flint говорит:

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

  14. > Я их брал числом около трех сотен за 18, чтоль, рублей штучка

    А это где так??? И вообще, какие можешь порекомендовать хорошие инет-магазины?

  15. Flint говорит:

    Взял этот проект, чтобы попробывать сделать подобный АЦП, но с заведением на неинвертирующий вход линейно нарастающего напряжения от специального источника. Так этот проект был пот 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=0×0 conflicts with 0×0:0×1
    Указывает на RETI после .org PCINTaddr ;Pin Change Interrupt
    Далее похожая vectors.asm(53): error: Overlap in .cseg: addr=0×7 conflicts with 0×7:0×8
    также указывает на RETI после .org URXCaddr
    И ещё несколько таких же. В чём здесь проблема, ничего не пойму?
    Вот здесь http://slil.ru/28269142 есть мой проект в Студии

    • DI HALT говорит:

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

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

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

  16. Flint говорит:

    Да, действительно, тепрь заработало. Спасибо

  17. rfgfq говорит:

    А как с одного вывода выходит пилообразный сигнал?

  18. angrykid говорит:

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

  19. Gordienko Victor говорит:

    как вы в Terminal’е рисуете график, там что то нужно настраивать ?

  20. Arthur говорит:

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

  21. Skywalker говорит:

    нечто подобное было у Мортона в его книге «Контроллеры AVR. Вводный курс». Схема — вот http://s017.radikal.ru/i440/1111/49/ca318bdc1917.jpg
    Думаю вам, уважаемый DI HALT, пояснений к ней не нужно)

  22. svetl говорит:

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

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