Аналоговые измерения с Arduino. Аналого-цифровые преобразования — АЦП

Описание портов, работающих как аналоговые входы, платформы Arduino (Atmega8, Atmega168, Atmega328, или Atmega1280)

Аналого-цифровой преобразователь

Микроконтроллеры Atmega, используемые в Arduino, содержат шестиканальный аналого-цифровой преобразователь (АЦП). Разрешение преобразователя составляет 10 бит, что позволяет на выходе получать значения от 0 до 1023. Основным применением аналоговых входов большинства платформ Arduino является чтение аналоговых датчиком, но в тоже время они имеют функциональность вводов/выводов широкого применения (GPIO) (то же, что и цифровые порты ввода/вывода 0 - 13).

Таким образом, при необходимости применения дополнительных портов ввода/вывода имеется возможность сконфигурировать неиспользуемые аналоговые входы.

Цоколевка

Выводы Arduino, соответствующие аналоговым входам, имеют номера от 14 до 19. Это относится только к выводам Arduino, а не к физическим номерам выводов микроконтроллера Atmega. Аналоговые входы могут использоваться как цифровые выводы портов ввода/вывода. Например, код программы для установки вывода 0 аналогового входа на порт вывода со значением HIGH:

pinMode(14, OUTPUT);
digitalWrite(14, HIGH);

Подтягивающие резисторы

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

digitalWrite(14, HIGH); // включить резистор на выводе аналогового входа 0

пока вывод работает как порт ввода.

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

Подробности и предостережения

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

Руководство на микроконтроллер Atmega не рекомендует производить быстрое переключение между аналоговыми входами для их чтения. Это может вызвать наложение сигналов и внести искажения в аналоговую систему. Однако после работы аналогового входа в цифровом режиме может потребоваться настроить паузу между чтением функцией analogRead() других входов.

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

В Arduino, сердцем которой является микроконтроллер Atmega, имеется один 10-битный АЦП. Это означает, что считанное значение напряжения может находиться в диапазоне от 0 — 1023. В зависимости от опорного напряжения 1024 значений будут распределены на соответствующий диапазон. В результате мы можем получить различную точность и различный диапазон напряжений, считываемых аналого-цифровым преобразователем.

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

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

Примечание: Arduino имеет несколько (в зависимости от версии) аналоговых входов, однако АЦП в ней только один. Это означает, что одновременно может быть считано значение только с одного из датчиков, подключенных к аналоговым входам A0… A5 (A0… A15 для Arduino MEGA).

Функция analogReference()

Для правильной работы АЦП требуется опорное напряжение (эталон). Для Arduino опорное напряжение может быть в диапазоне 0…5В (или 0… 3,3В для Arduino с напряжением питания 3,3В). В зависимости от типа используемого микроконтроллера у нас могут быть разные виды опорного напряжения.

Мы можем использовать внутренний или внешний источник опорного напряжения. Функция AnalogReference() предназначена для того, чтобы установить соответствующий источник опорного напряжения. Доступны следующие параметры этой функции:

  • DEFAULT: опорное напряжение составляет 5В или 3,3В (в зависимости от питания) — то есть, оно равно напряжению питания микроконтроллера;
  • INTERNAL: опорное напряжения составляет 1,1В для ATmega168, ATmega328 и 2,56В для ATmega8;
  • INTERNAL1V1: опорное напряжение составляет 1,1В — только для Arduino MEGA;
  • INTERNAL2V56: опорное напряжение составляет 2,56В — только для Arduino MEGA;
  • EXTERNAL: внешнее опорное напряжение, приложенное к выводу AREF — от 0 до 5В.

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

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

Наиболее точным вариантом является использование внешнего источника опорного напряжения. Существуют специальные источники опорного напряжения (ИОН). Плюсом является возможность получения необходимого точного опорного напряжения, например, 1,024В или 2,048В, что облегчает интерпретацию данных, считываемых АЦП. К недостаткам применения внешнего источника опорного напряжения можно отнести возможное увеличение стоимости проекта.

Синтаксис функции analogReference() показан в следующем примере:

AnalogReference(DEFAULT); //опорное напряжение = напряжение питания analogReference(INTERNAL); //опорное напряжение = 1,1В или 2,56В analogReference(EXTERNAL); //опорное напряжение = напряжение на AREF выводе

Функция analogRead()

Функция analogRead() обеспечивает считывание значения с одного из аналоговых входов. Считанное значение находится в диапазоне 0 — 1023 (10-битный АЦП). Необходимо указать номер аналогового входа, с которого будет происходить чтение данных.

Следующий пример иллюстрирует использование аналоговых входов:

#define analogPin 0 // потенциометр подключен к A0 int val = 0; // val — переменная, хранящая считанное значение void setup() { Serial.begin(9600); // инициализация последовательного порта } void loop() { val = analogRead(analogPin); // чтение значения напряжения с порта A0 Serial.println(val); // отправка измеренной величины на терминал }

Как видно, на приведенном выше примере, считанное значение напряжения передается через последовательный порт на компьютер.

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

Функция analogWrite()

Функция analogWrite() позволяет управлять выходом с помощью сигнала ШИМ. ШИМ часто используется в качестве замены обычного аналогового сигнала. Количество доступных выводов ШИМ зависит от типа используемого микроконтроллера в Arduino.

Так у Arduino на микроконтроллере:

  • Atmega8 — выводы 9, 10, 11;
  • Atmega128, Atmega168 и Atmega328 — выводы 3, 5, 6, 9, 10, 11;
  • Atmega1280 — выводы 2…13 и 44…46.

Частота переключения ШИМ большинства контактов составляет 490 Гц. Степень заполнения формируется числом от 0 до 255 (0 — без заполнения, 255 – полное заполнение).

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

#define ledPin 11 // светодиод подключен к контакту 9 #define analogPin 0 // потенциометр на А0 int val = 0; // val — переменная, хранящая значение A0 void setup() { pinMode(ledPin, OUTPUT); // устанавливаем контакт 9 как выход } void loop() { val = analogRead(analogPin); // чтение с потенциометра analogWrite(ledPin, val / 4); // пишем в ШИМ }

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

Чтобы ШИМ работал пропорционально вращению потенциометра, значение, полученное с A0, следует разделить на четыре. Это связано с тем, что данные с потенциометра лежат в диапазоне от 0 до 1024, а ШИМ принимает диапазон данных от 0 до 255.

В этом примере используется простое деление. В Arduino IDE имеется специальная функция map(), которая предназначена для пропорционального преобразования данных в новый диапазон значений.

ADS1115 это 16-разрядный Аналого-Цифровой Преобразователь, который может прекрасно расширить разрешающие и измерительные возможности Вашей Arduino. Он имеет внутренний ИОН (Источник Опорного Напряжения), 4 аналоговых входа, которые могут быть настроены в единичный, дифференциальный и сравнительный режимы измерения.

Важно: АЦП выдает 16-разрядное знаковое значение напряжения на входе. Т. е. максимальная величина шкалы напряжения не 65535 а 32768. Соответственно если необходимо использовать шкалу на все 16-бит можно только при условии дифференциального измерения, где на один из входов будет подключен внешний источник опорного напряжения а второй будет являться измерительным.
ОЧЕНЬ ВАЖНО!!!: Этот модуль очень боится перенапряжения как по входам, так и по питанию. Превышение напряжения более чем на 5% от напряжения питания его моментально сожжет.

Система установки адреса I2C ADS1115

Данное устройство предполагает установку адреса для I2C шины при помощи замыкания вывода ADDR на цифровые сигнальные выводы или выводы шины питания. Установка различных адресов I2C предназначена для того, чтоб при подключении двух и более различных устройств не происходило конфликта между ними, т. к. в лучшем случае модули у которых адреса совпадут просто не будут работать, а в худшем - работа всех модулей на шине может стать непредсказуемой. Как выглядит выбор адреса показано на рисунке ниже.

Схема подключения АЦП ADS1115 к Arduino

Данная схема показывает каким образом можно измерить напряжение с внутреннего стабилизатора на 3.3В



Подключив ADS1115 I2C АЦП к ардуино по вышеприведенной схеме, Вы можете попробовать в действии его уже прямо сейчас!

Для этого необходимо всего лишь выбрать в выпадающем списке ниже, Вашу плату, указать порт и нажать Run on Arduino .

Самую свежую библиотеку с примерами кода можно скачать из репозитория GITHUB

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

Если Вы испытываете затруднения с определением адреса устройства или не можете определить причину неработоспособности Вашего модуля i2c, запустите следующий код на Вашей Arduino Uno/Mega. Данная программа является сканером I2C порта управляемым через отладочный порт в режиме диалога. Она отображает на каких адресах есть устройства, а на каких их нет, а также поддерживаемые скорости общения (если вешаете несколько устройств, нужно выбирать скорость порта по самому медленному устройству, иначе при общении с более быстрым медленное может воспринять команды на себя и работа станет непредвиденной). Если подключение по I2C шине верное - данная программа это покажет.

Предлагаю полный спектр услуг по разработке систем автоматики и автоматизации бытового и промышленного направления. Имею в наличии готовые модульные решения для системы «умный дом»: вентиляция, отопление, освещение, дистанционное управление. Поверьте, «Умный дом», сегодня, уже не роскошь, а вполне ДОСТУПНАЯ для каждого из нас РЕАЛЬНОСТЬ!)))

Введение в библиотеку SPI для Arduino с примером скетча для 12-битного аналого-цифрового преобразователя LTC1286 и 16-битного цифро-аналогового преобразователя DAC714.

Об SPI

Последовательный периферийный интерфейс, более известный как SPI, был создан компанией Motorola для передачи данных между микроконтроллерами и периферийными устройствами, используя меньшее количество выводов по сравнению с параллельной шиной. SPI может использоваться для сопряжения любых возможных периферийных устройств, таких как датчики, сенсорные экраны и IMU датчики. SPI может даже использоваться для связи одного микроконтроллера с другим или для связи с физическими интерфейсами Ethernet, USB, USART, CAN и WiFi модулей. SPI чаще всего реализуется, как четырехпроводная шина с линиями для тактового сигнала, входа данных, выхода данных и выбора периферийного устройства. Линии тактового сигнала и данных используются всеми периферийными (или ведомыми) устройствами на шине, а вывод выбора ведомого служит для идентификации каждого подключенного периферийного устройства.

Все шины SPI должны содержать одно ведущее устройство (master) и одно или несколько ведомых устройств (slave). Некоторые устройства, такие как DAC714, используют дополнительные линии управления. В случае с DAC714 эта линия используется для очистки двойного буфера, позволяя использовать до трех микросхем DAC714 на одной пятипроводной шине, устраняя необходимость в дополнительной линии управления выборов ведомого устройства. Основными ограничивающими характеристиками шины SPI являются полоса пропускания и количество доступных выводов выбора ведомого устройства. Для микроконтроллеров в больших корпусах с тактовой частотой 20 МГц и выше и с сотнями выводов GPIO это вряд ли является ограничением.

Также об основных особенностях SPI можно прочитать в статье «Назад к основам: SPI (последовательный периферийный интерфейс) ».

Реализация SPI

Существует два основных способа реализации связи через SPI на Arduino или любом микроконтроллере. Первый и более распространенный метод - это аппаратный контроллер SPI. Arduino поставляется с библиотекой SPI для взаимодействия с аппаратным контроллером SPI, поэтому мы будем использовать эту библиотеку в наших примерах. Другой метод - программный SPI или "побитовое управление". Побитовое управление включает в себя ручное определение всех аспектов связи через SPI программно и может быть реализовано на любом выводе, в то время как аппаратный SPI должен быть подключен к конкретным SPI выводам микроконтроллера. Программный SPI намного медленнее, чем аппаратный SPI, и может использовать на себя ценную память программ и ресурсы процессора. Однако, в некоторых случаях, когда требуется несколько шин SPI на одном микроконтроллере, или при отладке нового SPI интерфейса побитовое управление может быть очень полезным.

Когда у ведомого устройства вывод выбора ведомого устройства принимает состояние низкого логического уровня, значит, это ведомое устройство пытается отправить данные по шине SPI ведущему устройству или принять данные. Когда у ведомого устройства вывод выбора ведомого устройства находится в состоянии логической единицы, это ведомое устройство игнорирует ведущее устройство, что позволяет нескольким ведомым устройствам совместно использовать одни и те же линии данных и тактового сигнала. Для передачи данных от ведомого устройства к ведущему служит линия MISO (Master In Slave Out, вход ведущего - выход ведомого), иногда называемая SDI (Serial Data In, вход последовательных данных). Для передачи данных от ведущего устройства к периферии используется линия MOSI (Master Out Slave In, выход ведущего - вход ведомого), также известная как SDO (Serial Data Out, выход последовательных данных). И наконец, тактовые импульсы от ведущего устройства SPI идут по линии, обычно называемой SCK (Serial Clock, последовательный тактовый сигнал) или SDC (Serial Data Clock, тактовый сигнал последовательных данных). В документации на Arduino предпочтение отдается названиям MISO, MOSI и SCK, поэтому мы будем использовать их.

Начало работы

Прежде чем приступить к написанию нового кода для периферийных устройств SPI, крайне важно обратить внимание на пару элементов в техническом описании новых компонентов. Во-первых, мы должны учитывать полярность и фазу тактового сигнала по отношению к данным. Они различаются у разных устройств и разных производителей. Полярность тактового сигнала может быть высокой или низкой и, как правило, называется CPOL (C lock POL arity). Когда CPOL = 0, логическая единица указывает на тактовый цикл, и когда CPOL = 1, логический ноль указывает на тактовый цикл. Фаза тактового сигнала, обычно называемая CPHA, указывает, когда данные захватываются и продвигаются в сдвиговом регистре относительно тактового сигнала. Для CPHA = 0 данные захватываются при нарастающем фронте тактового импульса, а продвигаются при спадающем фронте; а для CPHA = 1 - наоборот. Комбинация полярности и фазы тактового сигнала дает четыре отдельных режима данных SPI. SPI режим 0: CPOL и CPHA равны 0. SPI режим 1: CPOL = 0, а CPHA = 1. SPI режим 2: CPOL = 1, а CPHA = 0. И наконец, SPI режим 3: я уверен, вы сможете догадаться, в каких состояниях будут CPOL и CPHA.


Некоторые технические описания не испльзуют названия CPOL и CPHA, разработанные Freescale. Чтобы помочь понять режимы SPI, LTC1286 использует SPI режим 2. Взгляд на временную диаграмму в техническом описании поможет вам ознакомиться с SPI режимами данных. Для справки, DAC714 использует SPI режим 0 (спецификации DAC714 и LTC1286 приведены ниже). Далее нам нужно определить, как периферийное устройство сдвигает биты. Существует два возможных варианта: MSB или LSB - идет первым старший или младший значащий бит, и установить порядок функцией setBitOrder() . Наконец, нам нужно определить, какую тактовую частоту наше устройство может принять, и на какой тактовой частоте работает аппаратный SPI на нашей плате Arduino. В случае с Arduino Mega и платами с тактовой частотой 16 МГц по умолчанию значение тактового сигнала шины SPI составляет 4 МГц. Библиотека SPI для Arduino позволяет разделить тактовую частоту на 2, 4, 8, 16, 32, 64 и 128.

Библиотека SPI для Arduino

Библиотека SPI для Arduino за раз передает и принимает один байт (8 бит). Как мы увидим в примерах два и три, для этого требуется выполнить некоторые манипуляции с переданными и полученными байтами. Выводы аппаратного SPI на платах Arduino используются для разъема ICSP, на всех платах Arduino MOSI находится на выводе 4 ICSP разъема, MISO - на выводе 1, а SCK - на выводе 3. Если Arduino является ведущим устройством на шине SPI, любой ее вывод может использоваться в качестве вывода выбора ведомого устройства. Если Arduino является ведомым устройством на шине SPI, то для выбора ведомого должен использоваться вывод 10 на платах Uno и Duemilanove и вывод 53 на платах Mega 1280 и 2560.

Мы сфокусируемся на следующих функциях библиотеки SPI для Arduino:

  • SPISettings()
  • begin()
  • end()
  • beginTransaction()
  • endTransaction()
  • setBitOrder()
  • setClockDivider()
  • setDataMode()
  • transfer()

Пример 1

В примере датчика давления BarometricPressureSensor SCP1000 требует записи конкретных значений в определенные регистры для настройки SCP1000 для работы с низким уровнем шума. Этот скетч также содержит определенную команду для чтения и записи в SCP1000. Это самый важный шак во взаимодействии с периферийными SPI устройствами и требует внимательно изучения технического описания и временной диаграммы.

Const byte READ = 0b11111100; // команда чтения SCP1000 const byte WRITE = 0b00000010; // команда записи SCP1000 // Настройка SCP1000 для работы с низким уровнем шума: writeRegister(0x02, 0x2D); writeRegister(0x01, 0x03); writeRegister(0x03, 0x02); // дать датчику время для настройки: delay(100);

Пример 2

В примере 2 демонстрируется прием данных от 12-разрядного АЦП с помощью библиотеки SPI для Arduino. В качестве подопытного АЦП используется LTC1286. 1286 - это хорошо известный АЦП, который существует на рынке очень долгое время и имеет несколько аналогов. 1286 - это дифференциальный АЦП последовательного приближения, доступный в 8-выводном DIP корпусе, что делает его удобным для макетирования и прототипирования. Способ, которым мы получаем данные от LTC1286, также приведет к редкому сценарию, в котором побитовое управление менее сложно, чем использование библиотеки SPI для Arduino. Прикрепленное описание LTC1286 содержит временную диаграмму передачи данных, которая очень полезна для понимания кода. 1286 не требует настройки, а только передает данные. Это делает реализацию связи с 1286 на Arduino очень простой.

Однако, сложная часть заключается в том, как библиотека SPI будет интерпретировать то, что получила. Вызов SPI.transfer() обычно передает команду по каналу SPI и прослушивает его на предмет получения данных. В этом случае мы ничего не передаем: SPI.transfer(0) . Функция transfer принимает первый байт данных и присвает его byte_0 . Первый байт данных включает все принятые данные в то время, когда на CS (выбор ведомого) был низкий логический уровень. Он включает в себя два бита данных HI-Z , когда АЦП производит выборку аналогового напряжения для преобразования, и нулевой бит, указывающий начало пакета. Это означает, что наш первый байт будет содержать только пять полезных битов. Сразу после нашего первого вызова SPI.transfer(0) , мы вызываем эту функцию снова и на этот раз присваиваем ее результат переменной byte_1 . byte_1 будет содержать 8 бит данных, но нам интересны только семь из них. седьмой бид будет обычно совпадать с шестому, и его можно не учитывать, так как эффективное количество бит составляет только одиннадцать из двенадцати. По этой причине справедливо рассматривать LTC1286 как 11-разрядный АЦП. После отброса ненужных битов восстанавливается аналоговое значение.


Временная диаграмма получения данных от АЦП LTC1286 через шину SPI
// SPI выводы // SS вывод 48 // MISO вывод 50 // SCK вывод 52 #include const int spi_ss = 48; // вывод выбора ведомого SPI uint8_t byte_0, byte_1; // Первый и второй байты для чтения uint16_t spi_bytes; // Окончательное 12-разрядное сдвинутое значение float v_out; // Напряжение с десятичной запятой float vref = 5.0; // Опорное напряжение на выводе Vref void setup() { Serial.begin(9600); // Инициализировать последовательный порт и установить скорость pinMode(spi_ss, OUTPUT); // Установить SPI вывод выбора ведомого на выход digitalWrite(spi_ss, HIGH); // Убедиться, что на spi_ss установлена логическая единица SPI.begin(); // begin SPI } void loop() { // установить скорость, формат и полярность тактового сигнала/данных во время инициации SPI SPI.beginTransaction(SPISettings(1000, MSBFIRST, SPI_MODE2)); // установить CS вывод LTC в низкий уровень для инициации выборки АЦП и передачи данных digitalWrite(spi_ss, LOW); byte_0 = SPI.transfer(0); // read firt 8 bits byte_1 = SPI.transfer(0); // read second 8 bits // установить CS вывод LTC в высокий уровень, чтобы остановить LTC от передачи нулей digitalWrite(spi_ss, HIGH); // закрыть SPI соединение SPI.endTransaction(); // & B000 сбрасывает первые 3 бита (два HI-Z бита и один нулевой бит) и сдвинуть в spi_bytes // затем мы добавляем оставшийся байт, сдвинутый вправо для удаления бита 12 spi_bytes = (((byte_0 & B00011111) <<7) + (byte_1 >>1)); // и наконец, мы преобразуем значение в вольты. 1LSB = vref/2048 v_out = vref * (float(spi_bytes) / 2048.0); Serial.println(v_out, 3); delay(250); }

Пример 3

Мы увидели, как получать данные по SPI, теперь пришло время рассмотреть, как отправлять данные. В примере 3 рассматривается, как общаться с микросхемой с наследием, аналогичным LTC1286, но с совершенно противоположной функциональностью. DAC714 - это 16-разрядный цифро-аналоговый преобразователь. У DAC714 имеется дополнительный вывод связи, который включает дополнительную защелку данных. Это позволяет включать DAC714 последовательно с другими DAC714 (до двух штук) без дополнительной линии выбора ведомого. Двойной буфер DAC714 позволяет загружать два значения в DAC714 за каждый цикл. Временная диаграмма DAC714 приведена в техническом описании.


// Выводы SPI // SS вывод 48 // MOSI вывод 51 // MISO вывод 50 // SCK вывод 52 // latch вывод 46 #include const int spi_ss = 48; // регистр сдвига A0 DAC714P const int dac_lch = 46; // защелка ЦАП A1 DAC714 uint16_t input_0, input_1; // входные 16-битные значения uint8_t byte_0, byte_1, byte_2, byte_3; // байты для передачи SPI void setup() { Serial.begin(9600); pinMode(spi_ss, OUTPUT); pinMode(dac_lch, OUTPUT); digitalWrite(spi_ss, HIGH); digitalWrite(dac_lch, HIGH); SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV16); SPI.begin(); } void loop() { static uint16_t count = 0; input_0 = count; input_1 = -count; count += 1; Serial.println(input_0); Serial.println(input_1); // digitalWrite(spi_ss, LOW); // A0 byte_0 = (input_1 >> 8); byte_1 = (input_1 & 0xFF); byte_2 = (input_0 >> 8); byte_3 = (input_0 & 0xFF); SPI.transfer(byte_0); SPI.transfer(byte_1); SPI.transfer(byte_2); SPI.transfer(byte_3); digitalWrite(spi_ss, HIGH); digitalWrite(dac_lch, LOW); digitalWrite(dac_lch, HIGH); delay(3); }

Мы указали параметры SPI с помщью setDataMode() , setBitOrder() и setClockDivider() в void setup() , вместо использования SPI.beginTransaction() , просто, чтобы продемонстрировать еще один способо настройки. Снова испльзуется функция SPI.transfer() , но на этот раз нам неинтересен прием данных. Два 16-разрядных целых числа преобразуются в четыре байта для передачи через функцию SPI.transfer() . Сначала мы закружаем второе входное целое число, input_1 , первым потому, что оно будет зафиксировано и загружено после преобразования input_0 . Также обратите внимание, что делитель дает тактовую частоту, вероятно, намного медленную, чем максимальный тактовый сигнал, который DAC714 может принять.

Вот и всё! Надеюсь, статья оказалась полезной. Оставляйте комментарии!

Очень полезный модуль в составе микроконтроллера — аналого-цифровой преобразователь. Он позволяет микроконтроллеру измерять произвольное напряжение.
В мы описывали, как можно считать логическое состояние входа, то есть "0" или "1". Аналого-цифровой преобразователь считывает величину напряжения на выводах A0-A5. Это дает возможность считать данные с датчика освещенности, измерить напряжение питания и т.д.

Подготовка к работе

На нашем для освоения работы с АЦП есть три переменных резистора. Для их подключения к выводам A0-A2 установите перемычке так, как показано на рисунке:

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

Первый пример

Для начала попробуем просто считать напряжение на выводе микроконтроллера A0 и отправить его в COM-порт.
Делается это при помощи функции analogRead() . Этой функции нужно передать номер вывода, напряжение на котором должно быть измерено и она вернет текущее значение.
Загрузите на плату следующий пример:

int val; void setup() { Serial. begin(9600 ) ; } void loop() { val = analogRead(A0) ; Serial. println(val) ; delay(1000 ) ; }

В микроконтроллере Atmega8A, который используется на нашей плате , есть модуль АЦП с разрешением 10 бит и возможностью мультиплексирования шести входов. Эти входы пронумерованы A0-A6 (или 14-19).
Измерение производится относительно напряжения питания. Ни в коем случае нельзя подавать на вход отрицательное напряжение или напряжение, превышающее питание! Мы подключили ко входу переменный резистор и наше входное напряжение точно не выйдет за рамки питания.
Теперь разберемся с тем, что нам будет присылать плата. Раз разрешение 10 бит — в десятичном виде значение будет меняться от 0 до 1023. Измерение производится относительно 5-ти вольт, поэтому изменение показаний на 1 соответствует фактическому напряжению 5/1023=4.9мВ. То есть средствами встроенного АЦП микроконтроллера можно измерить напряжение с точностью до 4.9мВ.
Вернемся к скетчу. В результате выполнения строчки …

Val = analogRead(A0) ;

… в переменную val будет записано оцифрованное напряжение, считанное на выводе A0. Откройте монитор порта (Ctrl+Shift+M) и посмотрите, как меняются показания АЦП при вращении вала переменного резистора. Обратите внимание, что нет нужды настраивать вывод при этом на вход.
Теперь пора немного улучшить работу с модулем аналого-цифрового преобразователя. На практике младшие разряды АЦП могут сильно флуктуировать из-за шумов и их обычно отбрасывают, причем сразу два разряда. При этом остается 8ми-битное число с которым гораздо удобнее работать. Точность при этом получается 5/255=19.6мВ, чего вполне достаточно для большинства ситуаций.
Измените код так, чтобы он присылал 8ми-битное значение. Замените строку с чтением состояния АЦП на это:

Val = analogRead(A0) > > 2 ;

Теперь переменной val мы присваиваем значение считанное из АЦП сдвинутое на два бита вправо. Остальные биты просто отбрасываются.

Второй пример

Теперь мы можем плавно изменять яркость светодиода при помощи ШИМ-модуляции, задавая ее переменным резистором. Установите перемычку "color" так, как описано в . Этим самым вы подключены к 9му, 10му и 11му выводу сегменты трехцветного светодиода.
Для начала попробуем изменять яркость только одного светодиода:

# define BLUE 9 int val; void setup() { pinMode(BLUE, OUTPUT) ; } void loop() { analogWrite(BLUE, (analogRead(A0) > > 2 ) ) ; }

Довольно простой код, если вы помните, как работает функция . При вращении вала переменного резистора светодиод будет менять свою яркость от минимума до максимума.
Кстати, если вы уберете сдвиг на два бита, при выполнении функции analogWrite() будет наступать переполнение, так как она может принимать только значения от 0 до 255. Попробуйте убрать этот сдвиг и посмотрите, что получится.
И в заключении добавим управление всеми тремя светодиодами. Остается только найти отвертку, чтобы покрутить двумя остальными подстроечными резисторами.

# define BLUE 9 # define ORANGE 10 # define GREEN 11 int val; void setup() { pinMode(BLUE, OUTPUT) ; pinMode(ORANGE, OUTPUT) ; pinMode(GREEN, OUTPUT) ; } void loop() { analogWrite(BLUE, (analogRead(A0) > > 2 ) ) ; analogWrite(ORANGE, (analogRead(A1) > > 2 ) ) ; analogWrite(GREEN, (analogRead(A2) > > 2 ) ) ; }

Индивидуальные задания

  1. Оставьте на шилде только перемычку от резистора на выводе A0 и подключите пьезоизлучатель также, как