Программируем микроконтроллер PIC в качестве ведомого устройства I2C для пользовательского датчика и интерфейса ввода/вывода
У датчика нет интерфейса I2C? Нет проблем. Запрограммируйте микроконтроллер PIC в качестве ведомого устройства I2C для пользовательского датчика и интерфейса ввода/вывода. Для примера мы используем три датчика DHT22 на одном интерфейсе I2C.
Шина I2C (Inter-Integrated Circuit) – это распространенная и удобная технология для подключения устройств к встраиваемым контроллерам. Этот популярный протокол последовательного интерфейса позволяет микроконтроллерной плате, такой как Arduino UNO, взаимодействовать с периферийным устройством, например, с датчиком, используя два провода связи, тактовый и данные (SCL и SDA), и два провода питания (Vcc и GND). Каждое периферийное устройство I2C имеет адрес, что позволяет подключать несколько устройств к тем же соединениям I2C, и что является основной причиной популярности данной шины.
Но что вы будете делать, если необходимое вам периферийное устройство не использует протокол связи I2C? Одним из решений является разворачивание вами собственного интерфейса I2C. Данный проект представляет собой относительно простой и недорогой способ сделать это.
В частности, мы будем использовать экономичную микросхему PIC12F1840 в качестве ведомого устройства I2C, которое будет взаимодействовать с Arduino UNO, а также с большинством любых других микроконтроллерных плат, использующих I2C схему «один ведущий / несколько ведомых».
Чтобы проиллюстрировать данную методику так, чтобы была хоть какая-то польза, микроконтроллер PIC будет взаимодействовать с тремя датчиками температуры и влажности DHT22 (он же AM2302). Конечным результатом является простой способ развертывания всех трех датчиков с использованием одной шины I2C. Эту методику можно расширить, включив сюда другие пользовательские датчики, а также пользовательские расширения ввода/вывода и их комбинации.
Аппаратное обеспечение
Схема состоит из интерфейса I2C на PIC и трех датчиков DHT22. PIC интерфейс I2C содержит микросхему 12F1840, несколько вспомогательных компонентов, разъем/перемычку (JP1) для подключения платы к ведущему устройству и три дополнительных разъема/перемычки для подключения трех плат датчиков. Четыре контакта на J1 для подключения к Arduino UNO – это +5V, GND, SDA и SCL, линии которых доступны на разъемах платы Arduino. 12F1840 имеет 6 выводов ввода/вывода. Два из этих выводов, RA1 и RA2, образуют линии SDA и SCL для связи по I2C. К этим линиям я подключил подтягивающие резисторы по 10 кОм (R1 и R2), и всё работает хорошо; для своего приложения вы можете подобрать номиналы этих резисторов. Кроме того, может потребоваться полностью убрать эти резисторы, если у вас в вашей системе уже есть подтягивающие резисторы на линиях I2C.
Три вывода ввода/вывода, RA0, RA4 и RA5, подключаются к линиям данных датчиков DHT22 #1, #2 и #3 соответственно. Оставшийся вывод (RA3) не используется и остается неподключенным. При использовании дополнительных схем расширения этот вывод может быть задействован как вход общего назначения или для включения аппаратного сброса (MCLR).
Компонент | Описание |
---|---|
U1 | PIC 12F1840 |
C1 | Конденсатор 0,1 мкФ |
R1, R2 | Резистор 10 кОм |
J1, DHT1-3 | 4-пиновый разъем/перемычка |
Кабели | 3 x 4-пиновый |
Каждый из датчиков монтируется на отдельной плате с намерением разместить их на некотором расстоянии друг от друга и подключить к плате I2C с помощью 4-контактных кабелей. Поскольку на DHT22 можно использовать только 3 из 4 контактов, вы можете использовать 3-контактные кабели и разъемы.
Платы датчиков также относительно просты, содержат только датчик DHT22, подтягивающий резистор на линии данных и конденсатор развязки на линиях питания.
Компонент | Описание |
---|---|
DHT22 | Датчик DHT22 |
C1 | Конденсатор 0,1 мкФ |
R1 | Резистор 4,7 кОм |
J1 | 4-пиновый разъем/перемычка |
Данный проект разрабатывался для работы с напряжением 5 вольт, но я также протестировал его при питании от 3,3 вольт, и всё отлично работало, поскольку это напряжение находится в рабочем диапазоне как микроконтроллера PIC, так и DHT22.
Реализация пользовательского датчика I2C
PIC12F1840 является примером усовершенствованной 8-разрядной архитектуры среднего класса от Microchip. Это 8-выводный микроконтроллер, который включает в себя большое число функций периферийного устройства. Главной из этих функций периферийного устройства, для данного проекта, является модуль синхронного последовательного интерфейса MSSP (Master Synchronous Serial Port). Эта функция позволяет нам реализовать интерфейс I2C таким образом, что чип будет работать как ведомое устройство I2C. Возможно, самое главное, что программное обеспечение уже сделано для нас в примечании к применению (AN734C). Код из этого примечания к применению составляет основу нашего средства I2C. Он управляется прерываниями и позволит чипу, по сути, работать в качестве 32-байтового массива памяти, позволяющего совершать между ведущим и ведомым устройствами двунаправленные чтение и запись. Прилагаемая программа включает в себя код из примечания к применению, немного измененный для использования на 12F1840.
Ведущее устройство I2C (в этом примере Arduino UNO) будет выдавать команды ведомому устройству I2C путем записи в выбранное место в массиве памяти. PIC, как ведомое устройство I2C, будет выполнять команды (т.е. считывать показания датчиков DHT22) и отправлять назад информацию о состоянии и информацию с датчиков через массив памяти. Затем устройство-мастер считывает массив памяти для извлечения показаний датчиков.
Карта памяти
Мы будем говорить о 32-байтном массиве памяти как о значениях от Array[0]
до Array[31]
. Array[0]
служит местом, куда мастер (Arduino) выдавать команду, и где ведомый (PIC) будет сообщать о состоянии, которое будет считываться мастером. В этом отношении PIC вернет одно из трех значений, описанных в таблице ниже. Эти значению указывают включенное состояние, успешность выполнения допустимой команды и получение недопустимой или неизвестной команды.
Статус Array[0] – значение устанавливается микроконтроллером PIC для сообщения о состоянии | |
---|---|
0x00 | Устройство включено. Кроме того, данное значение устанавливается при программном сбросе микроконтроллера. |
0x01 | Последняя команда была выполнена успешно. |
0xF1 | Была запрошена недопустимая (неизвестная) команда. |
Arduino будет записывать в Array[0]
значение одной из четырех возможных команд, описанных ниже. Они указывают PIC микроконтроллеру о необходимости либо прочитать показания одного из датчиков DHT, либо перезагрузиться.
Команда Array[0] – значение устанавливается платой Arduino для выдачи команды | |
---|---|
0x02 | Прочитать показания датчика DHT #1 |
0x03 | Прочитать показания датчика DHT #2 |
0x04 | Прочитать показания датчика DHT #3 |
0x10 | Выполнить программную перезагрузку PIC микроконтроллера |
Когда команда о считывании показаний датчика была получена, путем записи в Array[0]
значения 0x02
, 0x03
или 0x04
, и PIC микроконтроллер вернул статус об успешном выполнении, путем записи в Array[0]
значения 0x01
, данные будут доступны для Arduino путем считывания массива памяти.
DHT22 возвращает 5 байтов, чтобы показать влажность (относительную влажность воздуха или RH) и температуру (в градусах Цельсия) следующим образом: RH целая часть, RH часть после десятичной запятой, температура целая часть, температура часть после десятичной запятой и контрольная сумма, которая представляет собой сумму всех четырех байтов AND 255. Эти 5 байтов будут записаны в разные ячейки в памяти PIC микроконтроллера после команд чтения датчиков. Ячейки Array[16]
–Array[31]
не используются, и свободная память в них доступна для пользовательского использования.
Распределение места в Array – значения устанавливаются микроконтроллером PIC для чтения платой Arduino | |
---|---|
Array[1] –Array[5] | Датчик DHT #1: RH целая часть, RH часть после десятичной запятой, температура целая часть, температура часть после десятичной запятой и контрольная сумма |
Array[6] –Array[10] | Датчик DHT #2: RH целая часть, RH часть после десятичной запятой, температура целая часть, температура часть после десятичной запятой и контрольная сумма |
Array[11] –Array[15] | Датчик DHT #3: RH целая часть, RH часть после десятичной запятой, температура целая часть, температура часть после десятичной запятой и контрольная сумма |
Array[16] –Array[31] | Доступно для использования |
И последняя команда, 0x10
, вызывает программную перезагрузку микроконтроллера PIC12F1840. Обычно в использовании данной команды нет необходимости, но она может быть полезна в случае неожиданных ситуаций.
Чтение показаний датчика влажности и температуры DHT22
Работа по считыванию показаний датчиков DHT22 и хранение их в массиве памяти также ложится на микроконтроллер PIC. Низкая цена, доступность и относительная надежность DHT22 сделали его использование повсеместным. Однако последовательный протокол, который используется этим чипом, требует очень точной временно́й работы. Эти требования к синхронизации могут стать проблемой для систем, использующих относительно медленный ввод/вывод, и для систем (например, микросхем систем на кристалле, SoC), чьи многочисленные высокоприоритетные функции могут сделать требуемые выделения времени неудобными. В этом отношении имеет смысл «разгрузить» работу на отдельную микросхему.
Связь с датчиком осуществляется через специальный «однопроводный» последовательный протокол, который не следует путать с однопроводным протоколом от Dallas/Maxim semiconductor – они совершенно разные. В данной связи задействованы три соединения (Vcc, GND и данные). Вывод данных – это место, где происходят все манипуляции, и тщательное прочтение технического описания даст более подробную информацию.
Вкратце, следующие шаги описывают взаимодействие со стороны PIC микроконтроллера, необходимое для считывания датчика, а прилагаемая программа MPASM была подробно прокомментирована для облегчения прослеживания действий программы:
- Установить линию данных в режим выхода, установить на ней низкий уровень и ждать 18 микросекунд.
- Установить на линии данных высокий уровень на 30 микросекунд.
- Установить линию данных в режим входа и мониторить её состояние. DHT22 должен установить на ней низкий уровень на 80 микросекунд, а затем высокий уровень на 80 микросекунд.
- Шаги 1-3 являются преамбулой к потоку данных. Затем DHT22 установит на линии данных низкий уровень на 50 микросекунд, что сигнализирует о старте бита данных.
- Теперь всё становится немного сложнее. После времени стартового бита DHT22 установит на линии данных высокий уровень либо на ~27 микросекунд, либо на 70 микросекунд. Первый сигнал соответствует биту данных '0', а второй сигнал – биту данных '1'.
- После передачи бита данных (шаг 5 выше) DHT22 перейдет к следующему 50-микросекундному стартовому биту, за которым следует следующий бит данных (либо '0', либо '1', как описано в шаге 5), и эта последовательность повторяется для 40 битов, составляющих 5 байтов данных датчика (влажность целая часть, влажность часть после десятичной запятой, температура целая часть, температура часть после запятой и контрольная сумма).
Вы можете видеть, что протокол требует точной синхронизации, и что время считывания датчика является переменным значением в том смысле, что оно зависит от количества битов '0' и '1' в чтении. Одна из стратегий состоит в том, чтобы измерить каждый из интервалов и затем решить, соответствует ли бит данных интервалу '0' или '1'. Я выбрал другую стратегию, описанную ниже, которая менее требовательна и очень надежна.
После преамбулы мы ждем окончания времени стартового бита, затем ждем около 43 микросекунд, после чего считываем состояние линии данных. Если на линии данных уровень низкий, то бит был равен '0', и мы находимся во времени передачи следующего стартового бита. В этом случае мы записывает бит как '0' и ждем окончания времени стартового бита перед чтением следующего бита данных. Если на линии данных уровень высокий, то бит был равен '1', и мы всё еще находимся во времени передачи бита данных. В этом случае, мы записываем бит как '1' и ждем начала времени следующего стартового бита, затем ждем окончания времени стартового бита перед чтением следующего бита данных.
Когда PIC микроконтроллер закончит считывание запрошенного датчика DHT22, данные будут доступны для чтения через интерфейс I2C. В этом случае Array[0]
будет считан как 0x01
, что указывает на успешное завершение последней команды.
Получение данных датчика на Arduino UNO
Доступ к ячейкам памяти для запроса и чтения данных датчика через интерфейс I2C осуществляется обычным способом. Читатели, которые программировали Arduino для работы с I2C, узнают этапы,а добавленный скетч будет хорошо прокомментирован для облегчения отслеживания работы программы.
Во-первых, для использования библиотеки I2C вам необходим оператор:
#include <Wire.h>
Затем вам необходим I2C адрес PIC микроконтроллера. Он программно установлен в коде MPASM для PIC микроконтроллера в строке:
#define I2C_ADDRESS 0x32 ; Адрес ведомого
Этот адрес должен быть сдвинут вправо на один бит, чтобы сформировать адрес, который будет использоваться Arduino. Те, кто знаком с 7-разрядной адресацией I2C, узнают этот часто неудобный шаг. Смещение 0x32 вправо даст нам 0x19, и это будет адрес I2C, который используется Arduinio. Конечно, вы можете установить этот адрес в коде MPASM в любое доступное значение для адреса I2C, которое у вас есть.
Чтобы начать обмен, используйте:
Wire.begin(); // подключается к шине в качестве мастера, обычно выполняется в setup()
Ниже в качестве примера приведены шаги для выдачи команды считывания датчика #2 (значение переменной address
было установлено в 0x19
):
Wire.beginTransmission(address);
Wire.write(0); // индекс в буфере
Wire.write(3); // байт команды (3 = прочитать DHT22 #2)
Wire.endTransmission();
delay(50); // ждем выполнения команды - для начала 50 миллисекунд
Задержка в последней строке ждет, пока завершится чтение датчика, и, возможно, она длиннее, чем необходимо, но лучше быть осторожным. Вы можете попробовать уменьшить задержку, если это необходимо для вашего приложения.
На этом этапе данные от датчика DHT22 #2 должны быть в Array[6]
-Array[11]
. Чтобы запросить данные, используем следующее:
Сначала прочитаем байт статуса в Array[0]
.
Wire.beginTransmission(address);
Wire.write(0); // индекс в буфере
Wire.endTransmission();
Wire.requestFrom(address, 1);
// обновить байт статуса
DHT[0]=Wire.read();
Проверим, равен ли байт статуса (теперь в DHT[0]
) 0x01
, что указывает на успешность выполнения команды. Затем получим 5 байтов данных DHT (из Array[6]
-Array[11]
) с помощью следующего кода:
Wire.beginTransmission(address);
Wire.write(6); // индекс в буфере (указывает на данные для DHT #2)
Wire.endTransmission();
Wire.requestFrom(address, 5);
После того, как мы поместили эти данные в программный массив DHT[6]
-DHT[10]
, мы преобразуем данные во влажность (RH) и температуру и, наконец, проверим совпадает ли контрольная сумма:
// Влажность
RHz2 = DHT[6];
RHz2 *= 256;
RHz2 += DHT[7];
RHz2 /= 10;
// Температура
TempCz2 = DHT[8] & 0x7f;
TempCz2 *= 256;
TempCz2 += DHT[9];
TempCz2 /= 10;
if(DHT[8]&0x80)
{
TempCz2*=-1;
}
TempFz2=TempCz2 * 9 / 5 +32;
// Контрольная сумма
if ((DHT[6]+DHT[7]+DHT[8]+DHT[9] & 255) != DHT[10])
{
Serial.print(" *** BAD CRC! *** ");
}
else
{
Serial.print(" CRC is good ");
}
Два других датчика DHT22 считываются таким же образом, используя соответствующие коды команд.
Приведенный ниже код скетча примера для Arduino просто считывает все три датчика каждые 5 секунд и отображает данные в мониторе последовательного порта.
Запуск
Программное обеспечение, необходимое для работы схем, приведено ниже и состоит из исходного кода MPASM для 12F1840, HEX файла для 12F1840 и тестового скетча для Arduino. Вам нужно будет запрограммировать микроконтроллер PIC12F1840. Если вы раньше использовали PIC-контроллеры, это не должно быть сложным. Если вы не знакомы с PIC-контроллерами, то это может показаться сложной задачей. Однако есть много недорогих программаторов, в том числе несколько самодельных разработок, которые получили большую поддержку. Ассемблер MPASM доступен бесплатно от Microchip.
Кроме того, начиная с мелкого PIC-контроллера, такого как 12F1840, задача становится менее сложной, чем может показаться. Вы можете собрать приведенный исходный код MPASM для программирования, как есть, или внести изменения, такие как адрес I2C. В качестве альтернативы, большинство программаторов позволит вам напрямую использовать HEX файл для программирования микроконтроллера.
Заключение
Данный проект призван обеспечить полезное дополнение к более крупному проекту на встраиваемом контроллере. В частности, он хорошо подходит для тех проектов, где вам необходимо контролировать влажность и температуру в нескольких зонах. Он также является отправной точкой для использования шины I2C в пользовательских приложениях. Хотя 12F1840 дешев и подходит для этого проекта, расширения могут быть реализованы на более крупных PIC-микроконтроллерах, которые содержат требуемый функционал MSSP и обеспечивают большее количество выводов ввода/вывода общего назначения. Таким образом, вполне возможно реализовать недорогие комбинации интерфейсов датчиков и расширения ввода/вывода на базе I2C.