Определить разрыв в цепи

Немного сложнее использовать прерывания PCINT0, PCINT1, PCINT2. Сложность в том, что если фронт сигнала для прерываний INT0 и INT1 микроконтроллер может отслеживать сам, то для прерываний PCINT потребуется написать небольшой алгоритм, который будет считывать текущее состояние входа PINB, PINC или PIND (для прерываний PCINT0, PCINT1 и PCINT2 соответственно) и сравнивать его с предыдущим состоянием, сохранённым в переменной.

Так уж случилось, что по поводу прерываний PCINT мне задал вопрос один из читателей этой статьи, и разбираясь с этим вопросом, я обнаружил недоработки в коде библиотеки VE_AVR (VEduino). Так что для данного примера нужно обновить библиотеку VEduino до версии 0.17.

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

Чтобы решить этот вопрос, я собрал схему на двух биполярных транзисторах. Первый транзистор включает и выключает светодиод, а на базе второго происходит повышение напряжения с 2.3 Вольт до 3.5 Вольт, если цепь, питающую светодиод разорвать, отключив светодиод от цепи (равно как и включив в цепь нерабочий светодиод). При этом, если светодиод горит как положено, то на коллекторе транзистора Т2 (то есть на входе INPUT) будет напряжение 1.75 В, что соответствует логическому 0. При разрыве цепи напряжение на коллекторе повышается до 2.82 В, что соответствует логической единице.

Схема питания светодиода с определением наличия разрыва в цепи

Здесь надо заметить, что возможен так же ещё один случай - если цепь замкнута на землю. Тогда напряжение на базе транзистора упадёт до 1.23 Вольт. Напряжение на коллекторе (на входе INPUT) при этом падает до 0.65 В. То есть добавив в схему ещё один транзистор и задействовав ещё один вход микроконтроллера, можно также определять и наличие светодиода в цепи.

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

Схема питания светодиода с определением наличия разрыва в цепи или короткого замыкания

С помощью кнопки S1 мы включаем и выключаем светодиод. Если в цепи есть разрыв, то включается светодиод ALARMLED, если же цепь замкнута на землю, то при попытке включения светодиода ALARMLED начинает мигать.

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

Скетч доступен для скачивания по ссылке.

Функционально обработка прерываний PCINT1 по событиям на входах PCINT8/9/10 (входы Analog0/1/2) реализована в массивах флагов нажатия (buttonPressed) и отпускания (buttonReleased) кнопок с индексами 0, 1 и 2 соответственно.

#define BTN_COUNT 3 // Число кнопок


volatile uint8_t prevPINC; // Предыдущее состояние регистра PINC

volatile bool buttonPressed[BTN_COUNT] = { false, false, false };

volatile bool buttonReleased[BTN_COUNT] = { false, false, false };

В обработчике прерывания PCINT1_vect программа читает текущее состояние порта C (регистр PINC), и побитово сравнивает его с предыдущим запомненным состоянием, меняя значения флагов состояния кнопок. Например, если к Arduino подключены несколько кнопок, и мы нажмём несколько из них одновременно, то изменятся и сразу несколько флагов состояния.

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

ISR(PCINT1_vect)

{

uint8_t pinc = PINC; // Прочитать регистр PINC (битовое состояние выводов порта С)

uint8_t p = pinc ^ prevPINC; // Сравнить с помощью XOR с предыдущим состоянием

uint8_t mask = 1;

for(uint8_t n = 0; n < BTN_COUNT; ++n) {

if (p & 1 == 1) { // Если состояние вывода изменилось

buttonPressed[n] = ((pinc & mask) == 0);

buttonReleased[n] = (pinc & mask);

}

p = p >> 1;

mask = mask << 1;

}

prevPINC = pinc; // Запомнить текущее состояние PINC

}

В функции setup() настраиваются выводы Arduino как входы и выходы, а также производится настройка входов PCINT8/9/10 в качестве источников прерывания PCINT1, каковое также переводится в режим разрешения прерываний.

pinMode(BTNPIN, INPUT); // Вывод кнопки в режим ввода

pinMode(INPUTPIN, INPUT); // Вывод INPUT в режим ввода

pinMode(INPUT2PIN, INPUT); // Вывод INPUT2 в режим ввода

enablePinChangeInt8(); // Разрешить прерывание при изменении уровня на выводе A0

enablePinChangeInt9(); // Разрешить прерывание при изменении уровня на выводе A1

enablePinChangeInt10(); // Разрешить прерывание при изменении уровня на выводе A2

DEV_PCICTRL.enableInt1(); // Разрешить прерывание по вектору PCINT1_vect

prevPINC = PINC;

interrupts(); // Разрешить прерывания глобально

И наконец в функции loop() производится просмотр состояния флагов с последующей обработкой событий нажатия и отпускания кнопок. Если событий не произошло, микроконтроллер переводится в режим Power Down, из которого будет выведен при следующем прерывании.

bool noEvents = true;

for (uint8_t n = 0; n < BTN_COUNT; ++n) {

if (buttonPressed[n]) { // Нажата кнопка с номером n (например, A1 при n == 1)

buttonPressed[n] = false;

Serial.print("Pressed ");

Serial.println(n);

noEvents = false;

}

else if (buttonReleased[n]) { // Отпущена кнопка с номером n

buttonReleased[n] = false;

Serial.print("Released ");

Serial.println(n);

noEvents = false;

}

}

// Переход в режим сна, если нет событий

if (noEvents) {

DEV_SLEEP.setMode(SleepControl::PWR_DOWN); // Выбор режима сна

DEV_SLEEP.enableSleep(); // Разрешить переход в спящий режим

sleep(); // Переход в режим сна

}

Полностью скетч доступен для скачивания по ссылке. Для его работы требуется библиотека VEduino версии 0.17.