Кнопочный ввод с помощью ацп

AVR. Учебный курс. Использование АЦП

Многие AVR имеют на борту АЦП последовательного приближения.

АЦП это десятиразрядное, но при точности +/- 2 минимально значащих разрядов его можно смело считать восьмиразрядным 🙂 Так как в младших двух разрядах всегда мусор какой то, не похожий на полезный сигнал.

Тем не менее это неплохой инструмент для контроля напряжения, в восьмиразрядном режиме имеющий 256 отсчетов и выдающее частоту дискретизации до 15кГц (15 тысяч выборок в секунду).
 

Конфигурация источника
Сигнал в АЦП подается через мультиплексор, с одного из восьми (в лучшем случае, часто бывает меньше) входов.

Выбор входа осуществляется регистром ADMUX, а точнее его битами MUX3…MUX0. Записанное туда число определяет выбраный вход. Например, если MUX3..

0 = 0100, то подключен вывод ADC4.
 

Кроме того, существует несколько служебных комбинаций битов MUX, использующихся для калибровки.

Например, 1110 подключает к АЦП внутренний источник опорного напряжения на 1.22 вольта. А если записать в MUX3..

0 все единицы, то АЦП будет изнутри посажено на землю. Это полезно для выявления разных шумов и помех.  

У старших AVR семейства Mega (8535, 16, 32, 128) есть возможность включить АЦП в режиме дифференциального входа. Это когда на два входа приходят разные напряжения. Одно вычитается из другого, да еще может умножаться на коэффициент усиления. Зачем это нужно? А, например, когда надо замерить перекос напряжения измерительного моста. У какого-нибудь тензомоста при входном напряжении в пять вольт выходные сигналы будут различаться между собой всего лишь 30мВ, вот и поймай его. А так подал на диф вход, подогнал нужный коэффициент усиления и красота!
 

Таблицу значений MUX3..0 для диф включения я не буду тут приводить, она находится легко в даташите, зовется она «Input Channel and Gain Selections«. Я поясню лишь один тонкий момент.

В режиме выбора диф входа встречаются такие комбинации как: первый вход ADC0 и второй вход тоже ADC0 ну и коэффициент усиления еще. Как так? Ведь для диф входа нужно два разных входа! Вначале подумал опечатка, поднял даташит на другую АВРку — та же ботва.

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

Делаешь первое преобразование на такой фигне, получаешь смещение нуля. А потом вычитаешь его из всех полученных значений, что резко повышает точность.
 

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

Выбор опорного сигнала
Это максимальное напряжение, которое будет взято за максимум при измерениях. Опорное напряжение должно быть как можно стабильней, без помех и колебаний — от этого кардинальным образом зависит точность работы АЦП. Задается он в битах REFS1..0 регистра ADMUX.

  • По дефолту там стоит REFS1..0 = 00 — внешний ИОН, подключенный к входу AREF. Это может быть напряжение со специальной микросхемы опорного напряжения, или же со стабилитрона какого, если нужно замерять небольшое напряжение, заметно меньшее чем напряжение питания, скажем от 0 до 1 вольт, то чтобы было точнее, и чтобы оно не затерялось на фоне пятивольтового питания, то на AREF мы заводим опорное напряжение в 1 вольт.
  • REFS1..0 = 01 — тут просто берется напряжение питания. У всех почти Мег с АЦП есть вход AVCC — вот это напряжение питания для AЦП и порта на который это АЦП повешено. Подавать туда плюс питания желательно через LC фильтр, чтобы не было искажений.
  • REFS1..0 = 11 — внутренний источник опорного напряжения на 2.56 вольт. Честно говоря, качество этого источника мне сильно не понравилось. С ним показания АЦП плавают как говно в проруби. Но если невозможно обеспечить гладкую и стабильную подачу напряжения на AREF или AVCC то прокатит. Кстати, внутренний ИОН подключен к выводу AREF так что можно повесить туда кондер и попробовать его чуть чуть сгладить. Немного, но помогает.
  •  

Выбор режима запуска преобразования
В регистре SFIOR под АЦП отведено аж три бита. ADTS2..0 которые управляют режимами запуска АЦП.
 

  • По дефолту ADTS2..0 = 000 и это значит, что преобразование идет в непрерывном режиме. Ну или по ручному запуску.
  • ADTS2..0 = 001 — запуск АЦП от аналогового компаратора. Удобно блин. Например, чтобы не замерять постоянно входную величину, а запрограммировать компаратор на то, что как только у него вылезет что-либо выше порога, так тут же захватывать это дело на АЦП.
  • ADTS2..0 = 010 — запуск от внешнего прерывания INT0
  • ADTS2..0 = 011 — по совпадению таймера T0
  • ADTS2..0 = 100 — по переполнению таймера Т0
  • ADTS2..0 = 101 — по совпадению с таймера Т1
  • ADTS2..0 = 110 — По переполнению таймера Т1
  • ADTS2..0 = 111 — По событию «захват» таймера Т1

Скорость работы АЦП
Частота выборки АЦП задается в битах предделителя ADPS2…0 регистра ADCSR.

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

Прерывания.
Естественно у АЦП есть прерывания. В данном случае это прерывание по окончанию преобразования.

Его можно разрешить битом ADIE, а внаглую вручную палится оно по флагу ADIF (регистр ADCSRA).

Флаг ADIF автоматом снимается при уходе на вектор прерывания по АЦП.
 

Данные с АЦП сваливаются в регистровую пару ADCH:ADCL откуда их можно забрать. Причем тут есть один прикольный момент. Регистровая пара то у нас ведь 16ти разрядная, а АЦП имеет разрядность 10бит.

В итоге, лишь один регистр занят полностью, а второй занимает лишь оставшиеся два бита.

Так вот, выравнивание может быть как по правому краю — старшие два бита в ADCH, а младшие в ADCL, либо по левому — старшие биты в ADCH, а два младших бита в ADCL.
 

[x][x][x][x][x][x][9][8]:[7][6][5][4][3][2][1][0] или [9][8][7][6][5][4][3][2]:[1][0][x][x][x][x][x][x]
Зачем это сделано? А это выборка разрядности так оригинально организована. Как я уже говорил, в младших разрядах все равно мусор и шумы (по крайней мере я от них так и не смог избавиться, как ни старался) . Так вот. Делаем выравнивание по левому краю. И загребаем старшие разряды только из регистра ADCH, а на младший забиваем. Итого, у нас число отсчетов становится 256. За выравнивание отвечает бит ADLAR в регистре ADMUX 0 — выравнивание по правой границе, 1 — по левой.
 

Запуск преобразования в ручном или непрерывном режиме.


Для запуска преобразования нужно вначале разрешить работу ADC, установкой бита ADEN в регистре ADCSR и в том же регистре ткнуть в бит ADSC.

Для запуска непрерывного преобразование (одно за другим) нужно также выставить бит ADFR (ADATE в некоторых AVR).
 

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

проц останавливается, все замирает. Работает только WatchDog и блок АЦП.

Как только данные сосчитаются, генерируется прерывание которое будит процессор, он уходит на обработчик прерывания от АЦП и дальше все своим чередом.
 

А теперь приведу парочку примеров простой инициализации и работы с АЦП. Микроконтроллер ATMega16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
; Мой любимый макрос для записи в порты :)))) .MACRO outi LDI R16,@1 OUT @0,R16 .ENDM  
; ADC Init – Инициализурем АЦП. Это можно сунуть куда – нибудь в начало кода   OUTI ADCSRA,(1

Источник: http://easyelectronics.ru/avr-uchebnyj-kurs-ispolzovanie-acp.html

Использование АЦП микроконтроллера в качестве интерфейса клавиатуры

Журнал РАДИОЛОЦМАН, июль 2015

Les Hughson

EDN

ATMega168 – это прекрасный универсальный 8-битный AVR микроконтроллер фирмы Atmel. Он имеет 23 вывода общего назначения (GPIO), но иногда, по мере роста вашего проекта, контактов ввода/вывода начинает не хватать.

Такое недавно случилось и со мной, когда из 23 доступных GPIO два я занял внешним керамическим резонатором, один вывод под линию сброса, 3 для последовательного порта, 14 для ЖК индикатора и 3 для управления RGB светодиодом.

Таким образом, все 23 вывода были использованы, и для управления четырьмя кнопками ничего не оставалось. Что же делать? Решение предлагается в этой статье.

Внимательно посмотрев в техническую документацию на ATMega168, можно заметить, что количество доступных выводов для вариантов в 28-выводном корпусе DIP и 32-выводном корпусе TQFP не одно и то же.

Версия в корпусе TQFP, помимо рекламируемых 23 GPIO, имеет пару дополнительных выводов VCC и GND, а также два входа АЦП.

Таким образом, если я смогу с помощью двух дополнительных входов АЦП считывать данные четырех кнопок, все будет OK, и моя конструкция будет спасена.

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

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

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

Ну что же, сначала мне показалось, что это будет несложно. Я просто должен был включить цепочку резисторов между моими четырьмя кнопками и одним из входов АЦП, чтобы каждая кнопка подтягивалась к земле различным количеством резисторов, равномерно распределенных между VCC и GND (Рисунок 1). Однако, когда я попытался это сделать, оказалось, что все не так просто, как представлялось вначале.

Рисунок 1. Резистивная цепь для четырех кнопок.

Я рассматривал вариант использования лестничной цепочки резисторов R-2R, но для этого потребовались бы переключатели на два направления, чтобы выходы соединялись с VCC или GND и не оставались висящими в воздухе.

Поразмышляв об этом некоторое время, я понял, что пошел по слишком трудному пути, и решение должно быть более простым. Есть два доступных входа АЦП, поэтому, если к каждому из них я подключу только по две кнопки, мне потребуется декодировать лишь 22, или 4 возможных состояния кнопок вместо 16 (Рисунок 2).

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

Рисунок 2. Более простая схема с двумя кнопками.

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

Рисунок 3. Часть схемы с двумя парами кнопок.

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

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

В колонках Vout и Counts представлены входное напряжение АЦП и его выходной код. Колонка Count Mid Points содержит коды АЦП, средние относительно ожидаемых значений; именно они используются для дифференциации различных входов и декодирования нажатых кнопок.

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

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

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

Рисунок 4. Зависимость входного напряжения АЦП от номера комбинациинажатых кнопок для схемы на Рисунке 3.

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

Кажется логичным увеличить количество кнопок с двух до четырех, продлив прогрессию сопротивлений резисторов от 10K, 20K до 10K, 20K, 40K, 80K.

Однако это не решает проблем нелинейности или потери части рабочего диапазона АЦП.

Более хорошим решением была бы замена подтягивающего резистора (R1 на Рисунке 3) источником постоянного тока. Цепочка подключенных к кнопкам резисторов преобразовывала бы постоянный ток в хорошие, равномерно распределенные значения выходных напряжений.

Вероятно, подобное можно сделать с помощью источника опорного напряжения (например, TL431) и транзистора.

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

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

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

После ряда экспериментов получилась схема, изображенная на Рисунке 5.

Рисунок 5. Схема с четырьмя кнопками.

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

Втрое плечо используется в качестве источника опорного напряжения АЦП, равного максимальному входному напряжению. Сопротивления резисторов выбраны так, чтобы R10 = R15, а R9 равнялось эквивалентному сопротивлению включенных параллельно резисторов R11 … R14, что обеспечивает полное использование входного диапазона АЦП.

Небольшие величины сопротивлений R15 и R10 улучшают линейность за счет снижения фактического размаха напряжения.

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

Нужные сопротивления R13 и R14 можно набрать путем последовательного соединения резисторов 20 кОм, а R9 может состоять из пары параллельно соединенных резисторов 10 кОм, к которым последовательно подключены три параллельных резистора 1 кОм.

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

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

Продолжая геометрическую прогрессию резисторов, эту схему можно расширить до пяти и, возможно, до шести кнопок.

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

Рисунок 6. Зависимость входного напряжения АЦП от номера комбинациинажатых кнопок для схемы на Рисунке 5.

Опыт, приобретенный в процессе создания и исследования схемы, выявил незначительную проблему, связанную с выводом AREF микроконтроллера, потребляющим небольшой ток и вносящим ошибку в опорное напряжение. Ток вывода AREF приводится в справочных данных на микроконтроллер, и может быть учтен.

Для компенсации вносимой этим выводом ошибки последовательно с R10 необходимо включить резистор 33 Ом. После такой коррекции экспериментальные результаты, полученные при использовании 8-битного АЦП, отличались от расчетных значений из электронной таблицы не более чем на один отсчет.

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

Рисунок 7. Макет схемы с четырьмя кнопками.

Есть возможность обойтись вообще без R9 и R10, воспользовавшись напряжением 1.1 В внутреннего источника опорного микроконтроллера. В этом случае сопротивление R15 потребуется изменить с 1 кОм на 1.5 кОм. Однако тогда на результаты измерений будет влиять точность напряжения VCC.

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

Загрузки

Проектные файлы в формате OpenOffice

Источник: https://www.rlocman.ru/shem/schematics.html?di=163566

Учебный курс. АЦП микроконтроллера AVR или как подключить 4 кнопки к одному выводу. Часть 2

  //основной цикл программы – опрос кнопочного буфера
  while(1)  {    tmp = KeyBuf;

    if (tmp)

    {      tmp–;      PORTC = ~(1 180)

    KeyBuf = KEY_S3;

  else if (AdcBuf > 120)

    KeyBuf = KEY_S2;

  else if (AdcBuf > 35)

    KeyBuf = KEY_S1;

  else

    KeyBuf = KEY_NULL; 

  //запускаем преобразование и выходим

  StartConvAdc();

}

Пояснения к коду

Номера кнопок

  В программе мы используем переменную, в которой хранится номер нажатой кнопки. Задаем с помощью директивы #define символические имена этим номерам.

#define KEY_NULL  0
#define KEY_S1    1
#define KEY_S2    2
#define KEY_S3    3
#define KEY_S4    4

Переменная – кнопочный буфер

   Переменная, в которой хранится номер нажатой кнопки, используется и в прерывании и в основном коде программы. В прерывании в нее производится запись, а в основном коде она читается. Перед такими переменными нужно использовать квалификатор volatile. Он сообщает компилятору, что переменная может менять свое значение “непредсказуемым образом” и что не стоит ее оптимизировать.

//кнопочный буфер
volatile unsigned char KeyBuf = 0;

  Кстати, эта переменная глобальная. Мы объявили ее вне функций. Поэтому она будет доступна в любой точке кода (ниже своего объявления).

Опрос буфера в основном коде программы

   В функции main микроконтроллер инициализирует периферию и попадает в бесконечный цикл while. В цикле он без конца считывает кнопочный буфер, проверяет его на нулевое значение (if(tmp != 0) …и if(tmp) … – эквивалентные записи) и если буфер не пуст, зажигает соответствующий светодиод. Если буфер пуст – все светодиоды гасятся.

while(1)  {    tmp = KeyBuf;

    if (tmp)

    {      tmp–;      PORTC = ~(1 180)            KeyBuf = KEY_S3;

      else

           if (AdcBuf > 120)                  KeyBuf = KEY_S2;

          else

                if (AdcBuf > 50)                      KeyBuf = KEY_S1;

                else

                     KeyBuf = KEY_NULL;   но согласитесь, что такая запись менее наглядна.

Устраняем ложные срабатывания

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

АЦП за это время успевает сделать несколько преобразований, а ничего не подозревающий микроконтроллер “подумать”, что были быстро нажаты несколько кнопок. Давайте устраним этот момент. Основной код (функцию main) трогать не будем, допишем только обработчик прерывания.

unsigned char comp = 0;
#pragma vector=ADC_vect
__interrupt void adc_my(void) {

  unsigned char AdcBuf;   

  unsigned char Key;
  static unsigned char LastState; 

  //считываем старший регистр АЦП

  AdcBuf = ADCH; 

  //проверяем в какой диапазон попадает его значение

  if (AdcBuf > 240)     Key = KEY_S4;

  else if (AdcBuf > 180)

    Key = KEY_S3;

  else if (AdcBuf > 120)

    Key = KEY_S2;

  else if (AdcBuf > 50)

    Key = KEY_S1;

  else

    Key = KEY_NULL; //если какая-нибудь из кнопка нажата//сравниваем предыдущее и текущее состояние//если совпадают – проверяем счетчик comp, //если нет – обнуляем его//кнопка считается нажатой, если она удерживается в течении 100

//преобразований АЦП

  if (Key)  {

    if (Key == LastState)

    {

      if (comp > 100)

        KeyBuf = Key;      

      else

        comp++;    }

    else

    {      LastState = Key;      comp = 0;    }  }

  else

  {    comp = 0;    KeyBuf = KEY_NULL;    LastState = KEY_NULL;  }

  //запускаем преобразование и выходим

  StartConvAdc();}   Мы ввели три дополнительные переменные: comp, LastState и Key.   Переменная Key временно хранит номер нажатой кнопки. Эта локальная переменная, ее значение  размещается в регистре общего назначения (РОН) и оно доступно только на время выполнения функции.  Переменная LastState хранит номер предыдущей нажатой кнопки. Она объявлена как статическая (с ключевым словом static). Физически она располагается в ОЗУ и поэтому ее значение сохраняется между вызовами функции!! Заметьте, что и Key и LastState не были инициализированы при объявлении. В них может находиться любое число, а вовсе не ноль, как иногда думают.     После того как микроконтроллер соотнесет результат преобразования АЦП с одним из диапазонов и запишет номер нажатой кнопки, он сравнит переменные Key и LastState друг с другом. Если они окажутся равны, переменная comp будет увеличена на единицу, если нет – обнулена. Как только значение comp превысит установленный порог, в нашем примере – 100, микроконтроллер перепишет значение переменной Key в кнопочный буфер KeyBuf.   Подобный алгоритм часто используется для борьбы с дребезгом кнопок.

Итак, из этих двух статей вы узнали

–  Основные характеристики АЦП-  Как инициализировать и запустить АЦП-  Как перевести значение напряжения на входе АЦП в цифровой код-  Конструкция if (…) else if(…)-  Зачем нужен квалификатор volatile-  Как объявить статическую переменную и в чем ее особенность-  Алгоритм для борьбы с дребезгом кнопок

Файлы проекта – АЦП микроконтроллера AVR

Схема для нашего примера

У вас недостаточно прав для комментирования.

Источник: http://chipenable.ru/index.php/item/20

Урок 10. АЦП в AVR микроконтроллерах. Простой вольтметр на AVR

АЦП — Аналого-цифровой преобразователь. Из названия можно догадаться, что на вход подается аналоговый сигнал, который преобразуется в число.

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

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

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

Одной из важнейших характеристик является разрешающая способность, которая влияет на точность измерения. Весь диапазон измерения разбивается на части. Минимум ноль, максимум напряжение ИОН. Для 8 битного АЦП это 2^8=256 значений, для 10 битного 2^10=1024 значения. Таким образом, чем выше разрядность тем точнее можно измерять сигнал.

Допустим вы измеряете сигнал от 0 до 10В. Микроконтроллер используем Atmega8, с 10 битным АЦП. Это значит что диапазон 10В будет разделен на 1024 значений.  10В/1024=0,0097В — с таким шагом мы сможем измерять напряжение. Но учтите, что микроконтроллер будет считать, величину 0.0097, 0.0098, 0.0099… одинаковыми.

Тем не менее шаг в 0,01 это достаточно неплохо. Однако, есть несколько рекомендаций, без которых эта точность не будет соблюдена, например для измерения с точностью 10бит, частота на которой работает АЦП должна быть 50-200 кГц. Первое преобразование занимает 25 циклов и 13 циклов далее. Таким образом, при частоте 200кГц мы сможем максимум выжать
200 000/13 = 15 384 измерений.

В качестве источника опорного напряжения можно использовать внутренний источник и внешний. Напряжение внутреннего источника (2,3-2,7В) не рекомендуется использовать, по причине низкой стабильности. Внешний источник подключается к ножке AVCC или Aref, в зависимости от настроек программы.

При использовании АЦП ножка AVCC должна быть подключена. Напряжение AVCC не должно отличаться от напряжения питания микроконтроллера более чем на 0,3В. Как было сказано, максимальное измеряемое напряжение равно опорному напряжению(Vref), находится оно в диапазоне 2В-AVCC. Таким образом, микроконтроллер не может измерить более 5В.

Чтобы расширить диапазон измерения, нужно измерять сигнал через делитель напряжения. Например, максимальное измеряемое напряжение 10В, опорное напряжение 5В. Чтобы расширить диапазон измерения, нужно уменьшить измеряемый сигнал в 2 раза.

Формула для расчета делителя выглядит так:

Uвых =  UвхR2/(R1 + R2)

Подставим наши значения в формулу:

5 = 10*R2/(R1+R2)

(R1+R2)=2*R2

R1=R2

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

Следовательно, когда мы измеряем напряжение через делитель, нужно полученное значение АЦП умножить на коэффициент=Uвых/Uвх.

Полная формула вычисления измеряемого напряжения будет выглядеть так:
U=(опорное напряжение*значение АЦП*коэффициент делителя)/число разрядов АЦП

Пример: опорное 5В, измеренное значение АЦП = 512, коэффициент делителя =2, АЦП 10разрядный.

(5*512*2)/1024=5В — реальное измеренное значение напряжения.

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

Перейдем к программной реализации. Создаем проект с указанными параметрами. Также подключим дисплей на порт D для отображения информации.

Измерение будет производиться в автоматическом режиме, обработка кода в прерывании, опорное напряжение подключаем к ножке AVCC. По сути нам нужно только обрабатывать получаемые данные. Измеренные данные хранятся в переменной adc_data[0]. Если нужно опрашивать несколько каналов, то выбираем какие каналы сканировать, а данные будут для ножки 0 в adc_data[0], для ножки 1 в adc_data[1] и т.д.

В основном цикле добавим строки:

result=((5.00*adc_data[0])/1024.00); //пересчитываем значение АЦП в вольты sprintf(lcd_buffer,»U=%.2fV»,result);   //помещаем во временную переменную результат

lcd_puts(lcd_buffer);                          //выводим на экран

Источник: http://avr-start.ru/?p=590

Делаем модульный многоканальный АЦП

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

В таком случае либо берётся готовое промышленное решение (которое, разумеется, стоит дорого, но часто является избыточным), либо делается что-то самодельное. В самом банальном случае это может быть плата Arduino с бесконечным циклом из analogRead и serial.write.

Если входных данных много (больше, чем аналоговых входов), то потребуется несколько плат, придумывать как их правильно опрашивать с компьютера и т. д.

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

Чтобы понять, подойдёт ли это решение вам, предлагаю ознакомится с его характеристиками:

Максимальное число каналов: 44;

Частота дискретизации: 1000 Герц;
Разрешение: 8 бит. Характеристики достаточно посредственные, однако для многих задач могут подойти. Это ведь не осциллограф, а система опроса датчиков. К тому же на её примере можно познакомится с использованием USART не совсем по назначению. Система состоит из отдельных модулей АЦП на базе микроконтроллера ATMEGA8 (можно применить другой МК семейства AVR с АЦП и аппаратным модулем USART, если немного изменить прошивку). Модулей может быть один или несколько, каждый предоставляет 6 или 8 АЦП в зависимости от корпуса микроконтроллера (выводная версия имеет 6 АЦП, а для поверхностного монтажа 8), только суммарное количество каналов не должно превышать 44. Главная особенность в том, что вне зависимости от количества модулей требуется лишь один USART со стороны компьютера (это может быть USB-переходник или аппаратный COM-порт). Это достигается засчёт того, что USART'ы всех микроконтроллеров соединяются последовательно (RX одного к TX другого), а RX и TX пины крайних в цепочке уже подсоединяются к компьютеру. Тут надо заметить то, что разрядность моего АЦП не совсем 8 бит — возможно лишь 255 градаций вместо 256. Значение 0xFF зарезервировано для особой цели. Если микроконтроллер получает его, то начинает выдавать каждый раз значение с очередного канала своего АЦП, а когда они кончаются ретранслирует 0xFF дальше по цепочке. Если же на вход USART приходит значение отличное от 0xFF, то микросхема просто пересылает байт далее. Таким образом передав одно произвольное значение и 44 0xFF можно получить значения со всех каналов всех АЦП (если АЦП меньше, то лишние каналы будут равны 0xFF). Произвольное значение нужно для того, чтобы все модули сбросили указатель на текущий канал АЦП, который надо передавать при получении 0xFF. В реальности удобнее передавать 45 0xFF, чтобы надёжно определять окончание приёма (если получили 0xFF, значит каналы закончились). Принципиальная схема содержит не очень много деталей:Программа для AVR выглядит предельно просто и занимает чуть меньше 300 байт памяти:#include
#include
#include
#include
// Firmware options
#define USART_BAUDRATE 460800
#define LED_PIN 1
#define ADC_COUNT 6
#define STARTUP_DELAY 1000
// Calculated UBRR value
#define UBRR (F_CPU / (16 * (uint32_t)USART_BAUDRATE) – 1)
// Global variables
uint8_t adc[ADC_COUNT]; // Buffer
uint8_t cur_in_adc; // Input byte index
uint8_t cur_out_adc; // Output byte index
// USART interrupt handler
ISR(USART_RXC_vect) { // Read data from USART uint8_t buffer = UDR; if (buffer == 0xFF) { if (cur_out_adc > 8; UCSRA = 0; UCSRB = _BV(RXCIE) | _BV(RXEN) | _BV(TXEN); UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // Enable interrupts sei(); // Main loop while (1) { // Reset watchdog timer wdt_reset(); // Select ADC channel ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(ADLAR) | cur_in_adc; // Start conversion and wait until it performed ADCSRA |= _BV(ADIF) | _BV(ADSC); while (ADCSRA & _BV(ADSC)); // Put value from ADC to buffer uint8_t value = ADCH; adc[cur_in_adc] = (value != 0xFF) ? value : 0xFE; // Switch to next channel cur_in_adc++; if (cur_in_adc >= ADC_COUNT) { cur_in_adc = 0; } }
}

В репозитории на GitHub вы можете найти файлы схемы и печатной платы KiCad, а также пример программы для компьютера, которая читает данные с этой системы и выдаёт их в формате CSV.

Удобно, что последовательный порт используется практически на пределе своих возможностей, поэтому не требуется заботится о синхронизации данных (я просто отправляю каждую секунду сразу 1000 команд на чтение АЦП) — если мы передаём 46 килобайт данных каждую секунду со скоростью 460800 бит в секунду, то можно быть полностью уверенным, что блоки из 46 байт данных (один замер) будут приходить каждую миллисекунду (хотя буферизация ядром ОС и USB-переходником, конечно, внесёт задержку, но замеры всегда будут производится с нужной частотой). Печатная плата была спроектирована в KiCad:Все платы соединяются в цепочку, у последней платы RX и TX соединяются джампером. Качество работы АЦП можно оценить по этому изображению пилы на 10 Гц:Для сравнения изображение с осциллографа DS203 (он же и выступает генератором):К сожалению, у меня нет более качественного источника сигнала, но для низкочастотных сигналов моя система должна подойти. Надо отметить, что не каждый преобразователь USART-USB обеспечивает скорость 460800 бит/сек при полной загрузке канала. Преобразователь на базе CP2102 заставил меня долго искать ошибку в собственном коде, пока я не попробовал FT232. Также наблюдается потеря порядка 0.17% данных (в программе для компьютера приняты меры, чтобы не терялась синхронизация данных). Скорее всего это вызвано плохой линией USART, либо недоработкой в программе. В общем, для 90% применений не должно быть критично, но ставить на АЭС скорее всего не стоит.

Ну и напоследок скажу, что себестоимость одного модуля получается около 50-60 рублей, если заказывать все детали из Китая, так что решение должно быть достаточно привлекательным.

Источник: https://habr.com/post/239447/

Ацп микроконтроллера atmega8, цифровой вольтметр

АЦП – аналогово-цифровой преобразователь (ADC- Analog-to-Digital Converter). Преобразует некий аналоговый сигнал в цифровой. Битность АЦП определяет точность преобразования сигнала. Время преобразования – соответственно скорость работы АЦП.

АЦП встроен во многих микроконтроллерах семейства AVR и упрощает использование микроконтроллера во всяких схемах регулирования, где требуется оцифровывать некий аналоговый сигнал.
Рассмотрим принцип работы АЦП.

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

Входное напряжение = Опорное напряжение*Z/2^N, где N – битность АЦП. Условимся, что этот регистр, как у ATmega8, 10-ти битный. Преобразование в нашем случае проходит в 10 стадий. Старший бит Z9 выставляется в единицу.

Далее генерируется напряжение (Опорное напряжение*Z/1024), это напряжение, с помощью аналогового компаратора сравнивается с входным, если оно больше входного, бит Z9 становиться равным нулю, а если меньше – остается единицей.

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

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

С наводками и помехами следует бороться с помощью индуктивности и емкости, как советует производитель в даташите:

В микроконтроллерах AVR как источник опорного напряжения может использоваться вывод AREF, или внутренние источники 2,56В или 1,23В.

Также источником опорного напряжения может быть напряжение питания. В некоторых корпусах и моделях микроконтроллеров есть отдельные выводы для питания АЦП: AVCC и AGND.

Выводы ADCn – каналы АЦП.

С какого канала будет оцифровываться сигнал можно выбрать с помощью мультиплексора.
Теперь продемонстрируем примером сказанное выше. Соорудим макет, который будет работать как вольтметр с цифровой шкалой. Условимся, что максимальное измеряемое напряжение будет 10В. Также пусть наш макет выводит на ЖКИ содержимое регистра ADC.

Схема подключения:

Обвязка микроконтроллера и ЖКИ WH1602A стандартна. X1 – кварцевый резонатор на 4 Мгц, конденсаторы С1,С2 – 18-20 пФ. R1-C7 цепочка на выводе reset по 10 кОм и 0,1 мкФ соответственно.

Сигнальный светодиод D1 и ограничивающий резистор R2 200 Ом и R3 – 20 Ом. Регулировка контраста ЖКИ – VR1 на 10 кОм. Источник опорного напряжения мы будем использовать встроенный на 2,56В.

С помощью делителя R4-R5 мы добьемся максимального напряжения 2,5В на входе PC0, при напряжении на щупе 10В.

R4 – 3 кОм, R5 – 1 кОм, в их номиналу нужно отнестись тщательно, но если не возможности подобрать точно такие, можно сделать любой резистивный делитель 1:4 и программно подкорректировать показания, если это потребуется. Дроссель на 10мкГн и конденсатор на 0,1 мкФ для устранения шумов и наводок на АЦП на схеме не показан. Их наличие подразумевается само собой, если используется АЦП. Теперь дело за программой:

Программа на языке Си:

  1. #include

  2. #define RS 2 //RS=PD2

  3. #define E 3 //E=PD3

  4. #define TIME 10 //Константа временной задержки для ЖКИ

  5. //Частота тактирование МК – 4Мгц

  6. #define R_division 3.837524 //=R4/R5 константа

  7. unsigned int u=0; //Глобальная переменная с содержимым преобразования

  8. void pause (unsigned int a)

  9. {

  10. unsigned int i;

  11. for (i=a;i>0;i–);

  12. }

  13. void lcd_com (unsigned char lcd) //Передача команды ЖКИ

  14. {

  15. unsigned char temp;

  16. temp=(lcd&~(1

Источник: https://avrlab.com/node/94

ATmega8: использование АЦП на примере датчика освещенности собраного на фоторезисторе

разделы: AVR , дата: 24 октября 2015г

схема подключения

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

Фоторезистор будет подключаться по схеме делителя напряжения, где в верхнее плечо будет установлен постоянный резистор 10K, а в нижнее сам фоторезистор. Нулевая точка будет поключена к пину ADC0 микроконтроллера.

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

Но вернемся к фоторезистору. Модель котрая мне попалась под руку – VT93N2. Даташит на него представлен ниже:

Чтобы иметь предстваление как это все должно работать, я сначала проверил это на Arduino с помощью такого скетча:

Результат работы был таким:

Здесь низкие значения соответствуют слабой освещености, а высокие – относительно яркой.

    Теперь, некоторые моменты реализации АПЦ в AVR микроконтроллерах:
  1. АЦП там один. НО можно задавать несколько пинов, с которых АЦП будет последовательно снимать показания в порядке очереди. Это называется каналами.
  2. АЦП там 10-битный, но младшие два бита относятся к зоне погрешности, чтобы снимать с них достоверную информацию, нужно чтобы частота оцифровки не превышала определенного значения, и нужно использовать специальный спящий режим ADC Noise Reduction, когда программа и перефирия отключаются ради бесшумной работы АЦП. Другими словами, приходится использовать 8-битный АЦП или проще использовать внешний АЦП.
  3. Значения АЦП выводятся в долях между землей и опорным напряжением. И то и то должно подаваться от эталонных источников, иначе качество выходной “цифры” будет неважнецкое.
  4. Приминительно к ATmega8, там существует два управляющих регистра АЦП: ADCSRA и ADMUX.
  5. Т.к. АЦП формально 10-битный, есть два регистра приемника: ADCH и ADCL. Существует специальный порядок чтения этих регистров, чтобы пока питается один, другой не менял своего значения при очередном преобразовании.
  6. Установкой бита ADLAR в регистре ADMUX, возможен 8-режим, т.е. когда старшие 8 битов пишутся в ADCH, а значением ADCL, как правило, пренебрегают.
  7. Имеется два режима работы АЦП: разовое и беспрерывная работа с определенной частотой. Частота дискретизации(оцифовки) задается тремя младшими битами ADPS2:ADPS0 в регистре ADCSRA.
  8. Имется прерывание по вектору ADC, кторое вызывается при окончании дискретизации. Т.е. это момент, когда можно забрать свежие данные. Вместо прерывания можно пользоваться ADIF флагом в регистре ADCSRA.

Пора снова вернуться к фоторезистору. Код который у меня получился в итоге:

#include
#include
#include
#include static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); volatile uint8_t dataADC;
ISR(ADC_vect)
{ dataADC=ADCH;
}; int main(void)
{ init_uart(); DDRB |= (1

Источник: http://www.count-zero.ru/2015/adc/

Использование модулей АЦП и ШИМ в микроконтроллерах PIC16

Микроконтроллеры PIC16 имеют на борту 10-ти разрядный модуль аналого-цифрового преобразователя (АЦП) последовательного приближения. Метод последовательного приближения предполагает получение результата за несколько измерений (сравнений), с постепенным увеличением точности в каждом последующем сравнении.

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

На рисунке ниже представлена структурная схема аналогового входа АЦП:

Здесь Rs – это внутреннее сопротивление источника напряжения, ANx – линия порта микроконтроллера, обладающая емкостью Cpin и током утечки Iu. Внутренние соединения микроконтроллера имеют сопротивление Ric.

Переключатель SS имеющий сопротивление Rss, подключает линию порта ANx к конденсатору Chold модуля АЦП. Коммутация переключателя SS производится при выборе аналогового канала, каждому каналу соответствует свой переключатель.

Сопротивление переключателя защелки зависит от напряжения питания, график зависимости показан на рисунке справа.

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

После выбора аналогового канала битами CHS(2:0) регистра ADCON0, необходимо организовать определенную паузу (Tacq) перед началом преобразования, для того чтобы конденсатор Chold успел зарядиться.

В технической документации приводится расчет этого времени, который представлен ниже:

В основном на время заряда влияет внутреннее сопротивление источника напряжения Rs, которое не должно превышать 10 кОм, для компенсации внутреннего тока утечки Iu, кроме этого влияние оказывает сопротивление переключателя защелки Rss и емкость самого конденсатора Chold, которая может различаться у различных моделей микроконтроллеров.

Время преобразования составляет 12Tad, где Tad это время получения одного бита, для корректного результата Tad не должно быть меньше 1,6 мкс. Время Tad в зависимости от частоты тактового генератора подбирается настройкой битов ADCS(2:0), в даташите на микроконтроллер для этого приводится таблица, где можно подобрать правильное значение.

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

В принципе, если аналоговый канал не меняется и частота преобразований небольшая (время между преобразованиями больше чем Tacq+2Tad), можно и не рассчитывать временные задержки Tacq, 2Tad, и забыть о них.

Чтобы использовать линии порта микроконтроллера для АЦП, необходимо их настроить как аналоговые входы битами ANS(7:0) регистра ANSEL, при этом линия порта должна быть настроена на вход битами регистра TRIS. После этого выбирается требуемый аналоговый канал.

Результат преобразования (10 бит) сохраняется в регистрах ADRESH и ADRESL. Эти регистры представляют собой спаренный 16-ти разрядный регистр, запись результата может выполняться с правым или левым выравниваем, как показано на картинке ниже.

Настройка выравнивания осуществляется с помощью бита ADFM регистра ADCON0.

В качестве опорного напряжения может использоваться внешнее напряжение с вывода Vref, или внутреннее Vdd от источника питания, настройка осуществляется битом VCFG.

Для увеличения точности результата опорное напряжение должно быть стабилизированным с минимальным уровнем пульсаций. При опорном напряжении Vref =5 В, получим дискретность 5В/1024=0,0049 В=4,9 мВ для 10-ти битного результата.

Для получения 8-ми битного результата необходимо применить левое выравнивание и считывать только регистр ADRESH, в этом случае для Vref =5 В дискретность составит 5В/256=0,0195 В=9,5 мВ.

Включение модуля АЦП производится битом ADON регистра ADCON0, запуск преобразования осуществляется установкой бита GO/-DONE регистра ADCON0, который аппаратно сбрасывается после окончания преобразования, то есть проверкой этого бита можно определить конец преобразования.

Перейдем к рассмотрению 10-ти разрядного ШИМ (широтно-импульсная модуляция) в микроконтроллерах PIC16. ШИМ осуществляется посредством модуля CCP, который настраивается в регистре CCP1CON, и содержит 16-ти разрядный регистр CCPR1, состоящий из двух регистров CCPR1H и CCPR1L.

Сигнал от модуля в режиме ШИМ передается на вывод CCP1 микроконтроллера, который должен быть настроен на выход. Для реализации ШИМ используется таймер TMR2, период ШИМ задается в регистре PR2, старшие 8 бит длительности импульса задаются в регистре CCPR1L , младшие 2 бита в регистре CCP1CON(5:4).

Ниже представлена структурная схема модуля ШИМ:

Когда значение таймера TMR2 в процессе инкремента сравнивается с числом в регистре PR2, происходит обнуление TMR2, одновременно с этим устанавливается высокий логический уровень на выводе CCP1 (если длительность импульса в регистрах CCPR1L и CCP1CON равна нулю, высокий логический уровень не устанавливается).

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

Буферизация необходима для возможности записи нового значения длительности импульса в регистры CCPR1L, CCP1CON, без искажения предыдущего значения. Биты в регистре CCPR1L и CCP1CON(5:4) могут быть изменены в любое время, но значение в регистре CCPR1H не изменяется, пока не произойдет совпадение значений TMR2 и PR2. В ШИМ режиме регистр CCPR1H доступен только для чтения.

Таймер TMR2 и внутренний двухразрядный счетчик образуют условный 10-ти разрядный счетчик, при этом если TMR2 инкрементируется в каждом машинном цикле с частотой Fosc/4 (при коэффициенте предделителя 1:1), то внутренний двухразрядный счетчик тактируется за каждый период тактового генератора с частотой Fosc, тем самым получается условный 10-ти разрядный счетчик. Когда значение CCPR1H и внутренней двухразрядной защелки сравнивается со значением TMR2 и внутреннего двухразрядного счетчика, на выводе CCP1 устанавливается низкий логический уровень. Ниже можно увидеть временную диаграмму одного периода ШИМ:

Период ШИМ можно рассчитать по следующей формуле из даташита:

Tшим=(PR2+1) ×4×Tosc×(коэффициент предделителя TMR2)

По мне лучше переписать данное уравнение в более удобную форму:

Fшим=Fosc/(4×(PR2+1)×(коэффициент предделителя TMR2))

Подставляя частоту тактового генератора, например, в килогерцах, получим результирующую частоту ШИМ в тех же единицах, так как остальные параметры безразмерные.

В даташите также приведена таблица с рассчитанными значениями частоты и разрешения ШИМ для частоты тактового генератора в 20 МГц:

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

Выполним несколько расчетов для частоты тактового генератора в 4 МГц. Значение разрешения ШИМ примем равным 8 бит, для получения наибольших частот ШИМ для заданной частоты тактового генератора, значение длительности импульса необходимо загружать в регистры CCPR1L и CCP1CON с “правым выравниванием”.

То есть старшие 6 бит длительности импульса загружаем в биты (5:0) регистра CCPR1L (в 6-й и 7-й бит CCPR1L записываем нули), а младшие 2 бита длительности импульса в регистр CCP1CON(5:4) как показано на рисунке ниже:

При этом числовое значение для регистра PR2, определяющее период ШИМ, составит 0x3F=63.

Посчитаем частоту ШИМ при коэффициенте предделителя TMR2 равного (1:1):

Fшим=4000 кГц/(4×(63+1)×1)=15,625 кГц

Для коэффициента предделителя TMR2 (1:4):

Fшим=4000 кГц/(4×(63+1)×4)=3,9 кГц

При коэффициенте (1:16) получим: Fшим=976 Гц.

Используя “левое выравнивание” можно получить наименьшие значения частот ШИМ для заданной частоты тактового генератора, при этом значение длительности импульса загружается только в регистр CCPR1L (в 5-й и 4-й бит CCP1CON записываем нули), как показано на рисунке ниже:

Числовое значение для регистра PR2 составит 0xFF=255, для коэффициентов предделителя TMR2 (1:1; 1:4; 1:16) получим частоты ШИМ 3,9 кГц; 976 Гц; 244 Гц. Подбор частоты путем различного “выравнивания” возможен, если только разрешение ШИМ меньше 10 бит. Таким образом, выбрав требуемое разрешение и варьируя частотой тактового генератора, коэффициентом предделителя TMR2, применяя различное “выравнивание”, можно подбирать различные частоты для ШИМ.

Теперь рассмотрим практическое применение модулей АЦП и ШИМ на основе 8-ми выводного микроконтроллера PIC12F683.

Будем регулировать яркость светодиода мощностью в один ватт при помощи переменного резистора, схема представлена ниже:

Как видно из схемы, измерение напряжения производится на среднем выводе переменного резистора, линия микроконтроллера GP0/AN0 используется в качестве аналогового входа модуля АЦП. Напряжение на среднем выводе переменного резистора варьируется от 0 до 5В, для АЦП используется внутреннее опорное напряжение от источника питания Vdd, то есть 5В. Разрешение АЦП и ШИМ я настроил на 8 бит, это значение очень часто применяется в конструкциях. Полученный после преобразования байт передается в модуль ШИМ, сигнал от которого с вывода GP2/CCP1 передается на затвор полевого транзистора, который коммутирует светодиод.

Код программы представлен ниже, в принципе необходимо только настроить АЦП и ШИМ, а дальше все просто:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;              movlw       b'01100001'     ;установка тактовой частоты микроконтроллера              movwf       OSCCON          ;в 4 МГц, внутренний тактовый генератор              movlw       b'00000000'     ;запись нулей в выходные защелки              movlw       0x07            ;выключение компараторов              movlw       b'11111011'     ;настройка линии GP2 на выход для модуля ШИМ,                movwf       TRISIO          ;остальные линии на вход;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;              bsf         STATUS,RP0      ;установка времени преобразования для АЦП              movlw       b'01010001'     ;Tad = 4мкс (Fosc/16), настройка линии              movwf       ANSEL           ;GP0(AN0) как аналоговый вход для АЦП, остальные              bcf         STATUS,RP0      ;линии цифровые входы              movlw       b'00000000'     ;Установка левого выравнивания результата АЦП,                movwf       ADCON0          ;внутренний источник опорного напряжения от Vdd,                                          ;выбор аналогового канала AN0, преобразование                                          ;не запущено, модуль АЦП выключен;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;              movlw       .255            ;запись числа 255 в регистр периода ШИМ              movlw       .0              ;запись нулей в регистры длительности импульса              movwf       CCPR1L          ;ШИМ, CCPR1L и CCP1CON(5:4)              movlw       b'00000110'     ;установка предделителя TMR2 (1:16)              movwf       T2CON           ;включение таймера TMR2              movlw       b'00001100'     ;включение CCP1 модуля в режим ШИМ                movwf       CCP1CON         ;при этом ранее сброшенные младшие биты (5:4)                                          ;длительности импульса ШИМ не меняем;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;              bsf         ADCON0,ADON     ;включение модуля АЦПbegin         bsf         ADCON0,GO_DONE  ;установка бита GO_DONE регистра ADCON0 для                                          ;запуска преобразования АЦПprov          btfsc       ADCON0,GO_DONE  ;опрос бита GO_DONE              goto        prov            ;бит GO_DONE не равен 0, преобразование не                                          ;закончено, переход на метку prov              movf        ADRESH,W        ;бит GO_DONE равен 0, преобразование закончено,              movwf       CCPR1L          ;копирование значения регистра ADRESH в регистр                                          ;CCPR1L, то есть результат преобразования                                          ;загружаем в регистр длительности импульса ШИМ              call        pause           ;вызов подпрограммы паузы              goto        begin           ;переход на метку begin, для повторного;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;pause         movlw       .130            ;подпрограмма паузы 100 мс              return                      ;выход из подпрограммы

Вначале идет настройка внутреннего тактового генератора микроконтроллера на 4 МГц, запись нулей в выходные защелки, отключение компараторов. Далее настраиваем линию GP2/CCP1 на выход, чтобы использовать ее для ШИМ, остальные линии на вход, в том числе и GP0/AN0 для АЦП, эти настройки производятся в регистре TRISIO, который расположен в 1-ом банке.

Следующим шагом будет настройка модуля АЦП.

В регистре ANSEL (находится в 1-ом банке) устанавливаем время преобразования одного бита Tad равное 4 мкс (Fosc/16), для частоты тактового генератора в 4 МГц из таблицы в даташите микроконтроллера доступно два значения: 2 мкс (Fosc/8) и 4 мкс (Fosc/16).

В этом же регистре настраиваем линию GP0/AN0 как аналоговый вход для правильного функционирования АЦП, остальные линии как цифровые входы. Затем следует настройка регистра ADCON0, где устанавливаем левое выравнивание результата преобразования для регистров ADRESH, ADRESL.

Для 8-ми битного результата считывать будем только регистр ADRESH. Здесь же выбираем внутренний источник опорного напряжения Vdd, при этом модуль АЦП не включаем, и преобразование не запускаем. На этом настройку модуля можно считать завершенной.

Далее настраиваем ШИМ, первым делом устанавливаем период в регистре PR2 (находится в 1-ом банке). Будем использовать 8-ми битный ШИМ с загрузкой длительности импульса с “левым выравниванием”, то есть только в регистр CCPR1L, соответственно в PR2 записываем 0xFF=255.

После этого устанавливаем длительность импульса равной нулю, чтобы после запуска ШИМ вывод GP2/CCP1 оставался в низком логическом уровне, в принципе этого можно и не делать.

Неиспользуемые в данном случае младшие биты (5:4) длительности импульса в регистре CCP1CON должны быть сброшены, иначе получим некорректный результат. В регистре T2CON устанавливаем коэффициент предделителя TMR2 равным (1:16), и запускаем таймер.

Частота ШИМ составит 244 Гц, чего вполне достаточно, для исключения мерцания светодиода. В регистре CCP1CON включаем режим ШИМ, при этом следим чтобы биты (5:4) были сброшены, как было сказано выше. Вот и все, ШИМ настроен и запущен.

Далее идет рабочая программа: включаем модуль АЦП, затем запускаем преобразование установкой бита GO/-DONE, кстати, включение модуля и запуск преобразования должны выполняться разными командами, о чем говориться в даташите.

Путем цикличной проверки бита GO/-DONE, определяем конец преобразования. Копируем результат преобразования из регистра ADRESH в регистр длительности импульса CCPR1L.

Уходим на паузу в 100 мс, после чего переходим на метку begin, для выполнения нового цикла, в принципе все просто.

Ниже представлен видеоролик демонстрирующий работу модулей АЦП и ШИМ. Здесь в вышеприведенную схему я дополнительно добавил цифровое табло на драйвере MC14489AP, для отображения результата измерения модуля АЦП.

Прошивка МК и исходник+модель Proteus 7.7

Последние записи:

Источник: http://radiolaba.ru/microcotrollers/ispolzovanie-moduley-atsp-i-shim-v-mikrokontrollerah-pic16.html

Ссылка на основную публикацию
Adblock
detector
",css:{backgroundColor:"#000",opacity:.6}},container:{block:void 0,tpl:"
"},wrap:void 0,body:void 0,errors:{tpl:"
",autoclose_delay:2e3,ajax_unsuccessful_load:"Error"},openEffect:{type:"fade",speed:400},closeEffect:{type:"fade",speed:400},beforeOpen:n.noop,afterOpen:n.noop,beforeClose:n.noop,afterClose:n.noop,afterLoading:n.noop,afterLoadingOnShow:n.noop,errorLoading:n.noop},o=0,p=n([]),h={isEventOut:function(a,b){var c=!0;return n(a).each(function(){n(b.target).get(0)==n(this).get(0)&&(c=!1),0==n(b.target).closest("HTML",n(this).get(0)).length&&(c=!1)}),c}},q={getParentEl:function(a){var b=n(a);return b.data("arcticmodal")?b:(b=n(a).closest(".arcticmodal-container").data("arcticmodalParentEl"),!!b&&b)},transition:function(a,b,c,d){switch(d=null==d?n.noop:d,c.type){case"fade":"show"==b?a.fadeIn(c.speed,d):a.fadeOut(c.speed,d);break;case"none":"show"==b?a.show():a.hide(),d();}},prepare_body:function(a,b){n(".arcticmodal-close",a.body).unbind("click.arcticmodal").bind("click.arcticmodal",function(){return b.arcticmodal("close"),!1})},init_el:function(d,a){var b=d.data("arcticmodal");if(!b){if(b=a,o++,b.modalID=o,b.overlay.block=n(b.overlay.tpl),b.overlay.block.css(b.overlay.css),b.container.block=n(b.container.tpl),b.body=n(".arcticmodal-container_i2",b.container.block),a.clone?b.body.html(d.clone(!0)):(d.before("
"),b.body.html(d)),q.prepare_body(b,d),b.closeOnOverlayClick&&b.overlay.block.add(b.container.block).click(function(a){h.isEventOut(n(">*",b.body),a)&&d.arcticmodal("close")}),b.container.block.data("arcticmodalParentEl",d),d.data("arcticmodal",b),p=n.merge(p,d),n.proxy(e.show,d)(),"html"==b.type)return d;if(null!=b.ajax.beforeSend){var c=b.ajax.beforeSend;delete b.ajax.beforeSend}if(null!=b.ajax.success){var f=b.ajax.success;delete b.ajax.success}if(null!=b.ajax.error){var g=b.ajax.error;delete b.ajax.error}var j=n.extend(!0,{url:b.url,beforeSend:function(){null==c?b.body.html("
"):c(b,d)},success:function(c){d.trigger("afterLoading"),b.afterLoading(b,d,c),null==f?b.body.html(c):f(b,d,c),q.prepare_body(b,d),d.trigger("afterLoadingOnShow"),b.afterLoadingOnShow(b,d,c)},error:function(){d.trigger("errorLoading"),b.errorLoading(b,d),null==g?(b.body.html(b.errors.tpl),n(".arcticmodal-error",b.body).html(b.errors.ajax_unsuccessful_load),n(".arcticmodal-close",b.body).click(function(){return d.arcticmodal("close"),!1}),b.errors.autoclose_delay&&setTimeout(function(){d.arcticmodal("close")},b.errors.autoclose_delay)):g(b,d)}},b.ajax);b.ajax_request=n.ajax(j),d.data("arcticmodal",b)}},init:function(b){if(b=n.extend(!0,{},a,b),!n.isFunction(this))return this.each(function(){q.init_el(n(this),n.extend(!0,{},b))});if(null==b)return void n.error("jquery.arcticmodal: Uncorrect parameters");if(""==b.type)return void n.error("jquery.arcticmodal: Don't set parameter \"type\"");switch(b.type){case"html":if(""==b.content)return void n.error("jquery.arcticmodal: Don't set parameter \"content\"");var e=b.content;return b.content="",q.init_el(n(e),b);case"ajax":return""==b.url?void n.error("jquery.arcticmodal: Don't set parameter \"url\""):q.init_el(n("
"),b);}}},e={show:function(){var a=q.getParentEl(this);if(!1===a)return void n.error("jquery.arcticmodal: Uncorrect call");var b=a.data("arcticmodal");if(b.overlay.block.hide(),b.container.block.hide(),n("BODY").append(b.overlay.block),n("BODY").append(b.container.block),b.beforeOpen(b,a),a.trigger("beforeOpen"),"hidden"!=b.wrap.css("overflow")){b.wrap.data("arcticmodalOverflow",b.wrap.css("overflow"));var c=b.wrap.outerWidth(!0);b.wrap.css("overflow","hidden");var d=b.wrap.outerWidth(!0);d!=c&&b.wrap.css("marginRight",d-c+"px")}return p.not(a).each(function(){var a=n(this).data("arcticmodal");a.overlay.block.hide()}),q.transition(b.overlay.block,"show",1*")),b.overlay.block.remove(),b.container.block.remove(),a.data("arcticmodal",null),n(".arcticmodal-container").length||(b.wrap.data("arcticmodalOverflow")&&b.wrap.css("overflow",b.wrap.data("arcticmodalOverflow")),b.wrap.css("marginRight",0))}),"ajax"==b.type&&b.ajax_request.abort(),p=p.not(a))})},setDefault:function(b){n.extend(!0,a,b)}};n(function(){a.wrap=n(document.all&&!document.querySelector?"html":"body")}),n(document).bind("keyup.arcticmodal",function(d){var a=p.last();if(a.length){var b=a.data("arcticmodal");b.closeOnEsc&&27===d.keyCode&&a.arcticmodal("close")}}),n.arcticmodal=n.fn.arcticmodal=function(a){return e[a]?e[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?void n.error("jquery.arcticmodal: Method "+a+" does not exist"):q.init.apply(this,arguments)}}(jQuery)}var debugMode="undefined"!=typeof debugFlatPM&&debugFlatPM,duplicateMode="undefined"!=typeof duplicateFlatPM&&duplicateFlatPM,countMode="undefined"!=typeof countFlatPM&&countFlatPM;document["wri"+"te"]=function(a){let b=document.createElement("div");jQuery(document.currentScript).after(b),flatPM_setHTML(b,a),jQuery(b).contents().unwrap()};function flatPM_sticky(c,d,e=0){function f(){if(null==a){let b=getComputedStyle(g,""),c="";for(let a=0;a=b.top-h?b.top-h{const d=c.split("=");return d[0]===a?decodeURIComponent(d[1]):b},""),c=""==b?void 0:b;return c}function flatPM_testCookie(){let a="test_56445";try{return localStorage.setItem(a,a),localStorage.removeItem(a),!0}catch(a){return!1}}function flatPM_grep(a,b,c){return jQuery.grep(a,(a,d)=>c?d==b:0==(d+1)%b)}function flatPM_random(a,b){return Math.floor(Math.random()*(b-a+1))+a}