PCINTx обработчик прерываний

pavlikspb
Offline
Зарегистрирован: 19.07.2012

Добрый вечер!

Подскажите пожалуйста - как выделить pin, который сработал по прерыванию PCINTx.

Evgen
Evgen аватар
Offline
Зарегистрирован: 10.06.2011

Его нельзя выделить, но его можно назначить регистром PCMSKx.

Вот тут обсуждалось: arduino.ru/forum/programmirovanie/dopolnitelnye-vneshnie-preryvaniya

leshak
Offline
Зарегистрирован: 29.09.2011

pavlikspb пишет:

Добрый вечер!

Подскажите пожалуйста - как выделить pin, который сработал по прерыванию PCINTx.

Запоминать состояния пинов при каждой сработке. При каждой сработке сравнивать "с предыдущем состоянием". Кто "не совпадает", тот и "сработал."

pavlikspb
Offline
Зарегистрирован: 19.07.2012

leshak пишет:

Запоминать состояния пинов при каждой сработке. При каждой сработке сравнивать "с предыдущем состоянием". Кто "не совпадает", тот и "сработал."

Да, но при таком способе регистрация происходит как на передний фронт, так и на задний....

на данный момент я реализовал так:

ISR(PCINT0_vect ) 
{
  // первая пара
  if((PINB & 0x03) == 1){
    // Ваш код
  }
  else if((PINB & 0x03) == 2){
    // Ваш код
  }
  // вторая пара
  else if((PINB & 0x0C) == 4){
    // Ваш код
  }
  else if((PINB  & 0x0C) == 8){
    // Ваш код
  }

Импульс имеет отрицательную форму. Может это можно упростить или улучшить?

 

leshak
Offline
Зарегистрирован: 29.09.2011

pavlikspb пишет:

leshak пишет:

Запоминать состояния пинов при каждой сработке. При каждой сработке сравнивать "с предыдущем состоянием". Кто "не совпадает", тот и "сработал."

Да, но при таком способе регистрация происходит как на передний фронт, так и на задний....

Ну? И что мы никак не сможем отличить фронт от бека?

Вот смотрите. Я вам говорю "кто-то только что включил или выключил свет в комнате". Но я не знаю он включил или выключил. Глядя на лампочку (или положение выключателя) вы можете ответить что именно он сделал? Включил или выключил?

Вот так же и с пином. Вы знаете что "пин поменял значение", можете узнать его текущие состояние, в чем проблема понять по какому фронту он изменился?

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

  • 0 - 0 - пин не менялся
  • 0 - 1 - пин поймал фронт
  • 1 - 1 - пин не менялся
  • 0 - 1 - пин поймал бек

 

pavlikspb пишет:

на данный момент я реализовал так:

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

pavlikspb
Offline
Зарегистрирован: 19.07.2012

Вот весь код

volatile long sensor_1 = 0;
volatile int sensor_1Count = 0;
volatile long sensor_2 = 0;
volatile int sensor_2Count = 0;

ISR(PCINT0_vect ) 
{
  // первый датчик
  if((PINB & 0x03) == 1){
    sensor_1Count++;
    sensor_1 = sensor_1 << 1;
    sensor_1 |= 1;
  }
  else if((PINB & 0x03) == 2){
    sensor_1Count++;
    sensor_1 = sensor_1 << 1; 
  }
  // второй датчик
  else if((PINB & 0x0C) == 4){
    sensor_2Count++;
    sensor_2 = sensor_2 << 1;
    sensor_2 |= 1;
  }
  else if((PINB  & 0x0C) == 8){
    sensor_2Count++;
    sensor_2 = sensor_2 << 1; 
  }
}

void setup() {
  DDRB = 0x30;
  PORTB = 0x0F;
  PCICR = 0x01; 
  PCMSK0 = 0x0F;
  delay(10);
  Serial.begin(9600);
}

void loop() {
  if(sensor_1Count >= 8){
    Serial.println(sensor_1);
    sensor_1 = 0;
    sensor_1Count = 0;
  }
  if(sensor_2Count >= 8){
    Serial.println(sensor_2);
    sensor_2 = 0;
    sensor_2Count = 0;
  }
}


 

leshak
Offline
Зарегистрирован: 29.09.2011

 Вы бы как-то прокомментировали свой "вот весь код". Что это? "Вот зацените работающий код" или "вот попробовал так, но не работает" (тогда в чем проявляется)?

На первый взгляд код "странен". Но может быть и работающий (смотря что вы хотели от него добится).

По крайней мере с изначальной задачей "узнать какой пин сработал" - IMHO коррелирует слабо. Сравнений с "прошлым состоянием" так нигде и не наблюдается (хотя вроде была попытка запоминать его, изощренная ;) ). Обрабатываются какие-то случаи когда сразу пара пинов установленна в HIGH (строки 14 и 24) - чего в изначальной задаче не упоминалась. Да и вкомментариях упоминается только два сенсора, а не четыре. Хотя подтяжку, опять-таки, включаете для 4-рех.

Зачем-то, в строке 31, включили два пина на выход. 

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

 

pavlikspb
Offline
Зарегистрирован: 19.07.2012

Прошу прощения, что не мог сразу указать конечную цель, так как ее даже не представлял (была только поставлена задача), поэтому задавал вопросы и решал их по мере поступления. Сообственно суть в том, что есть две цифровые клавиатуры передающие код в формате wiegand8 (т.е. на одну используется три провода D0, D1 и земля). В состоянии покоя на D0 и D1 присутствует логическая единица, как только на клавиатуре нажата цифра передается пакет из 8 бит, где "0" передаются по D0, а "1" по D1. Задача была выводить эти цифры на LCD(вывод пока не реализован), а проблема в том, что документации на них нет - соответственно формат и последовательность тоже была неизвестна.

Два пина на выход, это светодиод и реле, но в коде я пока до них еще не дошел...

Если запоминать состояние порта целиком, то потом придется все равно делить на два дисплея каждую клавиатуру...

Еще раз прошу прощения....

Код работает - большое спасибо!!!

leshak
Offline
Зарегистрирован: 29.09.2011

pavlikspb пишет:

wiegand8 (т.е. на одну используется три провода D0, D1 и земля). В состоянии покоя на D0 и D1 присутствует логическая единица, как только на клавиатуре нажата цифра передается пакет из 8 бит, где "0" передаются по D0, а "1" по D1. 

Честно говоря не слышал про такую шину. Да и утвреждение "0 передается по D0" , а "1 по D1" вызвает какое-то непонятный зуд можжечка. Никогда не слышал про передечу 0 и 1 по разным проводам. Да и не понимаю зачем такое могло понадобится.

А вот если предположить что одна из них - линия данных (и нули и единицы по ней гонятся), а падение второй в ноль означает "читай новый бит с линии данных" (то есть - тактируюуща), то шина сразу становится подозрительно похожа на SPI (или другой какого родственика). И тогда можно не мудрится с прерываниями, а попробовать воспользоваться аппаратным SPI, и весь байт принимать одним простым read()

 

pavlikspb
Offline
Зарегистрирован: 19.07.2012

 Подробнее по формату здесь ru.wikipedia.org/wiki/Виганд_(интерфейс)

Проблема в том. что каждый производитель для определения номера может использовать разную последовательность бит, например цифра один может быть у одного производителя 00000010 или 01000000 у другого. И еще обычно один бит из 8 используется для проверки четности/нечетности.

Кстати я вместо клавиатуры подключил считыватель с форматом Wiegand26 в условии заменил 8 на 26 и карточка доступа прочиталась нормально, отсюда возник следующий вопрос - как унифицировать код, чтобы можно было читать и wiegand8 и wiegand26 и wiegand32 и т.д.? Предполагаю замерять время между посылкой первого и второго бита в посылке, а потом по этому интервалу складывать биты до тех пор пока есть изменения в этом интервале.

leshak
Offline
Зарегистрирован: 29.09.2011

pavlikspb пишет:

Подробнее по формату здесь ru.wikipedia.org/wiki/Виганд_(интерфейс)

Век учись - дураком помрешь :)  У меня, почему-то не вышло сходу нагуглить. Ценная, лично для меня, ветка получилась. Уже второй раз "что-то новое узнал" :)

Но, исходя из этого. Ваш код, скорее всего, все равно имеет "скрытую лажу".  Все-таки запоминать прошлое состояние порта и выяснить "какой же именно пин" поменялся - все равно нужно.

Представте что у вас будет происходить если оба сенсора начнут передавать данные одновременно?

Придположим data1 первого упал в ноль. Вы, в первый сенсор записали 1. Но... если до того, как data1 вернется в логическую 1 у вас произойдет импульс на втором сенсоре, то есть обработчик вызовется еще раз - у вас опять произойдет чтение DATA1, и в первый сенсор попадет лишняя 1. А потом, кого отпустится DATA1 - произойдет "мусорное чтение" данных второго сенсора.

pavlikspb пишет:

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

Хе-хе. Это вы еще с инфакрасными пультами не корячились. Ну, то есть, большинство-то, все-таки, попадают под тот или иной известный проктокол, но... не все. Причем там даже само кодирование может отличатся. У одних "смысл" несет длинна импульсов, у других - паузы между ними и т.п. Ну а содержимое кода - вообще полная анархия :)

pavlikspb пишет:

wiegand8 и wiegand26 и wiegand32 и т.д.? Предполагаю замерять время между посылкой первого и второго бита в посылке, а потом по этому интервалу складывать биты до тех пор пока есть изменения в этом интервале.

Ну "направления правильное". Хотя ваша ссылка на вики, наталкивает на другой, хотя и родственный подход

wiki пишет:

Разделение кадров осуществляется по таймауту. Реально минимальное время между кадрами 0,5 сек., рекомендуемый таймаут для контроллера СКУД — 50…250мс.

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

 

 

pavlikspb
Offline
Зарегистрирован: 19.07.2012

leshak пишет:

Но, исходя из этого. Ваш код, скорее всего, все равно имеет "скрытую лажу".  Все-таки запоминать прошлое состояние порта и выяснить "какой же именно пин" поменялся - все равно нужно.

Представте что у вас будет происходить если оба сенсора начнут передавать данные одновременно?

Придположим data1 первого упал в ноль. Вы, в первый сенсор записали 1. Но... если до того, как data1 вернется в логическую 1 у вас произойдет импульс на втором сенсоре, то есть обработчик вызовется еще раз - у вас опять произойдет чтение DATA1, и в первый сенсор попадет лишняя 1. А потом, кого отпустится DATA1 - произойдет "мусорное чтение" данных второго сенсора.
 

Да, такой эффект есть -  особенно при одинаковых комбинациях кода...надо подумать, как это более оптимально решить, ведь если я правильно понимаю процессор при работе на частоте 18МГц обрабатывает 18 миллионов команд, тогда за 40 микросекунд(длина импульса передачи бита) он обработает 720 простых команд, а если сложных, то можно и не уложиться?

leshak
Offline
Зарегистрирован: 29.09.2011

 Я не понимаю во что вы собрались "укладываться". Честно говоря - лень хмурить мозг. Тем более что "это тут ни причем". Проблема-то в "одновременности". А подход "будем верить что одновременно импульсы никогда не не совпадут" - изначально не правильный.

Я уж не знаю, в какой раз говорю: вам нужно запоминать состояние порта. Что сложно в том что-бы сделать prevPINB=PINB в конце обработчика? И, потом, сравнением "текущего" с "предыдущим" выяснять какой пин сработал.  Не хотите сами это делать - в начале ветки вам давали ссылку где это уже обсуждалось. Там, вроде, есть и ссылочки на готовые библы.

leshak
Offline
Зарегистрирован: 29.09.2011

 > И, потом, сравнением "текущего" с "предыдущим"

Даже сравнивать, вообщем-то не нужно. Нужно провеить что "в старом пин был 1" (prevPINB), а в новом он "0" (PINB). В сообщении #4 я уже перебирал вам все комбинации "старого-нового" сотояния.

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

leshak
Offline
Зарегистрирован: 29.09.2011

 Вообщем примерно так (не запускал, но идея думаю понятна):

#define DATA_PORT PINB // сделаем дефайном, что-бы потом можно было, если нужно, переехать на другой порт
#define SENSOR_1_MASK (1<<4) // предположим на 4-том бите у нас датчик. Делаем маску типа B00010000, то есть вход PB5, он же digital pin 13

volatile byte PrevDataPort;

ISR(PCINT0_vect )
{
  if( (PrevDataPort & SENSOR_1_MASK) && !(DATA_PORT & SENSOR_1_MASK)){ 
    // сработал наш пин на ниспадающем фроне
  }
  PrevDataPort=DATA_PORT;// запомнили состояния порта
}

 

pavlikspb
Offline
Зарегистрирован: 19.07.2012

leshak пишет:

 Вообщем примерно так (не запускал, но идея думаю понятна):

#define DATA_PORT PINB // сделаем дефайном, что-бы потом можно было, если нужно, переехать на другой порт
#define SENSOR_1_MASK (1<<4) // предположим на 4-том бите у нас датчик. Делаем маску типа B00010000, то есть вход PB5, он же digital pin 13

volatile byte PrevDataPort;

ISR(PCINT0_vect )
{
  if( (PrevDataPort & SENSOR_1_MASK) && !(DATA_PORT & SENSOR_1_MASK)){ 
    // сработал наш пин на ниспадающем фроне
  }
  PrevDataPort=DATA_PORT;// запомнили состояния порта
}

 

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

Нормальный код клавиатуры:01011101

Ненормальный:010111010

Интересно откуда он берется?

 

pavlikspb
Offline
Зарегистрирован: 19.07.2012

 А еще раз через десять и вторая клавиатура стала добавлять ноль

pavlikspb
Offline
Зарегистрирован: 19.07.2012
 

Сделал вот так:

ISR(PCINT0_vect )
{ 
int temp_status = DATA_PORT; 
//данные с порта читаем один раз и запоминаем на все время обработки прерывания
  if( (PrevDataPort & SENSOR_1_MASK) && !(temp_status & SENSOR_1_MASK)){ 
    // сработал наш пин на ниспадающем фроне
  }
	  PrevDataPort=temp_status;// запомнили состояния порта
}




 пока работает....

pavlikspb
Offline
Зарегистрирован: 19.07.2012

pavlikspb пишет:

 

Сделал вот так:

ISR(PCINT0_vect )
{ 
int temp_status = DATA_PORT; 
//данные с порта читаем один раз и запоминаем на все время обработки прерывания
  if( (PrevDataPort & SENSOR_1_MASK) && !(temp_status & SENSOR_1_MASK)){ 
    // сработал наш пин на ниспадающем фроне
  }
	  PrevDataPort=temp_status;// запомнили состояния порта
}




 пока работает....

 

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