Подключение матричной клавиатуры по I2C

pencraft
Offline
Зарегистрирован: 17.01.2012

Написал небольшую инструкцию как подключить матричную клавиатуру к Arduino по I2C. Тема вроде бы простая, но не обошлось без "подводных камней". Может ктоу-нибудь поможет сэкономить время...

-------------------------------

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

Матричная клавиатура организуется следующим образом (см. рис. 1). Кнопки группируются в матрицу, состоящую из определенного количества строк и столбцов. Каждая кнопка, таким образом, имеет четко определенные координаты (номер строки и номер столбца).

Рис. 1. Схема матричной клавиатуры

Рис. 1. Схема матричной клавиатуры

Матричная клавиатура представляет собой довольно «экономичный» способ подключения большого числа кнопок к нашему устройству. Благодаря ее использованию удается значительно сократить количество портов, расходуемых на обслуживание кнопок. Так, например, для подключения 16 кнопок матричной клавиатуры требуется уже не 16 пинов, а всего 8 (4 столбца и 4 строки). Для опроса клавиатуры можно подавать, например, на выходы контроллера, подключенные к столбцам сканирующие импульсы, а на входах, подключенных к строкам определять, какая клавиша из соответствующего столбца нажата.
Более углубленно принцип работы матричной клавиатуры рассмотрен здесь:

http://easyelectronics.ru/matrichnaya-klaviatura.html

Матричную клавиатуру можно спаять самостоятельно из обычных кнопок, а можно купить уже готовую. В известных интернет-магазинах широко распространены пленочные клавиатуры на 12 или 16 клавиш.

Например вот такие:

http://www.ebay.com/itm/New-High-Quality-4-x-4-Matrix-Array-16-Key-Membrane-Switch-Keypad-Keyboard-/280766893945?pt=AU_B_I_Electrical_Test_Equipment&hash=item415f02cf79

или вот такие:

http://www.ebay.com/itm/New-4x3-12-Key-Matrix-Membrane-Super-Slim-Switch-Keypad-Keyboard-General-Use-/380586917005?pt=LH_DefaultDomain_0&hash=item589cbf7c8d

При стоимости в 45-50 рублей они выглядят довольно прилично и вполне удобны для пользования.
Примеры готовых матричных клавиатур показаны на рис. 2.

Рис. 2. Виды матричных клавиатур

Рис. 2. Матричные клавиатуры: а) кнопочная, б) пленочная на 12 кнопок, в) пленочная на 16 кнопок

Далее мы будем рассматривать пленочную клавиатуру, изображенную на рис. 2. в).
Конечно, 8 пинов это не 16 ;) но, учитывая, что в Arduino Uno пинов не так уж и много, возникает желание сократить и это количество до минимума. Поэтому мы подключим клавиатуру, используя всего 2 пина, т. е. шину I2C. Почитать про шину I2C и ее использование в Arduino можно, например, здесь:

http://robocraft.ru/blog/arduino/786.html

Для подключения нам понадобится микросхема PCF8574, даташит можно взять, например, здесь:

http://www.datasheet4u.net/datasheet/P/C/F/PCF8574_PhilipsSemiconductors.pdf.html

Это I2C расширитель ввода-вывода на 8 линий. Подробнее о его использовании совместно с Arduino можно прочитать здесь:

http://mk90.blogspot.ru/2010/05/arduino-3.html

Распиновка микросхемы приведена на рис. 3:

Рис. 3. Микросхема PCF8574P

Рис. 3. Микросхема PCF8574P. Расположение и назначение выводов

Выводы A0, A1 и A2 – задают адрес конкретной микросхемы на шине I2C. Адрес каждой микросхемы задается семибитным числом. Четыре старших бита зависят от варианта исполнения микросхемы (PCF8574P – 0100, PCF8574AP – 0111). Три младших бита задаются путем подтягивания выводов к земле или питанию (через резистор 10кОм). Например для задания адреса 0x20 следует использовать микросхему без буквы A и все три вывода A0, A1 и A2 притянуть к нулю. Выводы 14 и 15 подключаются, соответственно, к выводам A5 и A4 Arduino, на которых, собственно, и реализована шина I2C. Питание +5В подается на вывод 16, GND – на вывод 8.

Клавиатура, изображенная на рис. 2. в), снабжена обычным разъемом на 8 контактов с шагом 2,54 мм. Если смотреть на клавиатуру сверху – первый контакт находится справа, 8-й слева. Столбцы подключены к контактам 1-4 в обратной последовательности (1–й столбец к 4 контакту, 2-й – к 3-му и т. д.), строки – к 5-8 аналогично. Схема распиновки показана на рис. 4:

Рис. 4. Расположение выводов пленочной клавиатуры 4x4

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

Столбцы:
J1 конт. 1 (COL3) – PCF конт. 12 (P7)
J1 конт. 2 (COL2) – PCF конт. 11 (P6)
J1 конт. 3 (COL1) – PCF конт. 10 (P5)
J1 конт. 4 (COL0) – PCF конт. 9 (P4)

Строки:
J1 конт. 5 (ROW3) – PCF конт. 7 (P3)
J1 конт. 6 (ROW2) – PCF конт. 6 (P2)
J1 конт. 7 (ROW1) – PCF конт. 5 (P1)
J1 конт. 8 (ROW0) – PCF конт. 4 (P0)

Внешние подтягивающие резисторы при подключении клавиатуры не требуются.

На этом «аппаратную часть» можно считать завершенной. На очереди – программная. Для работы с клавиатурой мы используем библиотеку i2ckeypad, разработанную Angel Sancho и опубликованную здесь:

http://playground.arduino.cc/Main/I2CPortExpanderAndKeypads

Правда опубликованная библиотека до сих пор не адаптирована к Arduino IDE 1.0 и выше, кроме того автор использовал не самую удобную распиновку клавиатуры, поэтому текст библиотеки придется несколько доработать напильником ;)))

Итак, скачиваем библиотеку отсюда:

https://sites.google.com/site/angelitodeb/Home/i2ckeypad-20090224-3_v0.1.tar.gz?attredirects=0

Создаем в папке \libraries основного каталога Arduino IDE папку i2ckeypad и копируем в нее файлы i2ckeypad.h и i2ckeypad.cpp из скачанного архива. Также в папке i2ckeypad создаем папку example, а в ней – папку i2ckeypad и копируем туда пример i2ckeypad.pde из того же архива.
Для обеспечения работоспособности библиотеки в Arduino IDE 1.0 и выше необходимо сделать следующее:

1. В файле i2ckeypad.cpp находим и удаляем строчку #include "WConstants.h"
2. Там же находим команды Wire.send(data) и Wire.receive(data) и заменяем их, соответственно, на Wire.write(data) и Wire.read(data).

Поскольку мы используем клавиатуру 4x4 – находим в том же файле строчку int num_cols = 3; и меняем 3 на 4.

Далее мы должны внести в библиотеку информацию о распиновке нашей клавиатуры. Для этого находим группу строк вида #define COL0 и вписываем имена входов-выходов PCF8574, к которым мы подключали нашу клавиатуру. Обратите внимание, указывать надо не номера ножек микросхемы, а имена входов-выходов (начинающиеся с буквы P):

#define COL0 4
#define COL1 5
#define COL2 6
#define COL3 7
#define ROW0 0
#define ROW1 1
#define ROW2 2
#define ROW3 3

Все. Библиотека готова к использованию. Подключаем Arduino с клавиатурой к USB-порту, запускаем IDE и открываем пример из библиотеки i2ckeypad:

#include <Wire.h>
#include <i2ckeypad.h>

#define ROWS 4
#define COLS 4

// With A0, A1 and A2 of PCF8574 to ground I2C address is 0x20
#define PCF8574_ADDR 0x20

i2ckeypad kpd = i2ckeypad(PCF8574_ADDR, ROWS, COLS);

void setup()
{
  Serial.begin(9600);

  Wire.begin();

  kpd.init();

  Serial.print("Testing keypad/PCF8574 I2C port expander arduino lib\n\n");
}

void loop()
{
  char key = kpd.get_key();

  if(key != '\0') {
        Serial.print(key);
  }
}

Как видим, библиотека требует подключения Wire.h – библиотеки для работы с шиной I2C, задания количества строк и столбцов клавиатуры и адреса микросхемы PCF8574 на шине. В строке:

i2ckeypad kpd = i2ckeypad(PCF8574_ADDR, ROWS, COLS);

мы создаем экземпляр объекта i2ckeypad с именем kpd и адресом PCF8574_ADDR. Параметры ROWS и COLS можно опустить, тогда (учитывая сделанные нами правки в библиотеке) по умолчанию будет принято, что у нас клавиатура 4x4.
Единственная функция get_key(); возвращает символ, сопоставленный с нажатой и отпущенной клавишей. Набор символов можно изменить, он задан в файле i2ckeypad.cpp в виде массива:

const char keymap[4][5] =
{
  "123A",
  "456B",
  "789C",
  "*0#D"
};

Компилируем пример, загружаем в Arduino, включаем Serial монитор, нажимаем на кнопки и видим в окне соответствующие символы. Обратите внимание, клавиатура выдает очередной символ не при нажатии, а при отпускании клавиши.
Таким образом мы с вами при помощи всего одной микросхемы подключили 16 кнопок, использовав всего 2 пина контроллера. Красота! ;))

Еще раз основные ссылки:

1. DiHalt о работе матричной клавиатуры: http://easyelectronics.ru/matrichnaya-klaviatura.html

2. Datasheet PCF8574: http://www.datasheet4u.net/datasheet/P/C/F/PCF8574_PhilipsSemiconductors.pdf.html

3. Библиотека i2ckeypad (нуждается в доработке): https://sites.google.com/site/angelitodeb/Home/i2ckeypad-20090224-3_v0.1.tar.gz?attredirects=0

--------------------

Первоначально эта статья была опубликована мной на nnm.ru, но там док по Arduino похоже окончательно умер :(((

Удачи всем,
Вадим Елисеев aka Pencraft.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

А что будет если нажать кнопки 1, 2 и 4 на Рис.1 одновременно и начать опрос? Я про защитные диоды. Не увидел в статье ни слова. В даташите на PCF8574 я посмотрел, там обычные входы-выходы. Потому если два выхода, на одном из них 0, а на другом единица соединятся, будет плохо. Впрочем, могу ошибаться, развейте сомнения, пожалуйста.

 

pencraft
Offline
Зарегистрирован: 17.01.2012

Спасибо за дополнение, это я упустил. Конечно, в классической схеме должны быть защитные диоды и подтягивающие резисторы. То, что "и без них все работает" наверняка не может гарантировать устойчивость схемы в различных ситуациях. Вот здесь: http://gendocs.ru/v256/?cc=4   рекомендуют делать так:

Цитирую:

Рассмотрим принцип работы схемы. В исходном состоянии на выводы РВ.4...РВ.7 подается сигнал логической единицы. Выводы порта РВ.0...РВ.3 настроены на ввод. Контроллер периодически опрашивает состояние клавиш путем изменения сигналов на выходах РВ.4...РВ.7 и считывания сигнала из РВ.0...РВ.3. В случае обнаружения замыкания контактов одной из клавиш, программа выполняет закрепленные за этой клавишей действия. В случае если при опросе клавиатуры обнаружится несколько одновременно нажатых клавиш, это считается ошибкой и никаких действий не выполняется. Если ни одна клавиша не нажата, никаких действий также не выполняется. Такой алгоритм используется в большинстве современных клавиатур.

И еще:

Особо следует отметить необходимость токоограничительных резисторов в обеих схемах. При замыкании ключа (или нескольких ключей) в одном столбце, например в первом, ток будет проходить от источника через подтягивающие резисторы тех строк, в которых нажаты клавиши к PB4 или Q0. При нажатии всех клавиш столбца реализуется худший случай с максимальной токовой нагрузкой на порт микроконтроллера PB4-PB7 или Q0-Q3. Значение тока должно быть ограниченно предельно допустимым для данного МК или микросхемы дешифратора, что задает условие минимальной величины сопротивления токоограничительных резисторов – оно должно быть таким, чтобы ток через каждый резистор был как минимум в m раз меньшим, чем предельно допустимый. Где m – это число строк матричной клавиатуры.
Диоды VD1…VD4 служат для защиты от короткого замыкания при нажатии нескольких клавиш в разных столбцах. Допустим, сканирующий код выбирает первый столбец (код 00) и защитные диоды отсутствуют. При этом, если например одновременно нажать клавиши К1 и К7, то ток от источника через подтягивающий резистор R1 пойдет к PB4 или Q0. И линия первой строки будет заземлена. Тогда при замкнутом контакте К7 произойдет короткое замыкание между PB5 или Q1 и PB4 или Q0. Защитные диоды препятствуют протеканию тока от PB5 или Q1 к PB4 или Q0 тем самым вводя все столбцы, кроме опрашиваемого, в высокоимпедансное состояние. Диоды VD1…VD4 необходимо выбирать из условия минимального падения напряжения на переходе, т.к. при нажатии клавиш это напряжение будет на регистрирующих выводах МК.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Ок, а то кто нибудь, кто не знает, соберет и попортит выходы.

EuBeginer
Offline
Зарегистрирован: 16.11.2015

Penkraft привет и огромное спасибо за данную статью и тему.

У меня задача управления 19 сервами от тумблеров и еще 3 сервы по прерываниям, но там проблем особых нет. Остановился на архитекутре с шиной IIC. Там  16  канальные контроллеры есть для серв и вот сейчас хочу понять как мне тумблеры подключить. Именно тумблеры поскольку они дают еще и наглядность в каком состоянии сервы (грубо говоря открыта или закрыта). Прочитав Вашу статью задался вопросом каким образом подключить именно тумблеры, поскольку у меня будет постоянно ситуация когда несколько тумблеров включены (это как сразу несколько кнопок). Есть ли некий адресный расширитель для шины IIC???

Что посоветуете?

EuBeginer
Offline
Зарегистрирован: 16.11.2015

А вот вроде по соседству нашел. И с адресацией все ясно. Знай опрашивай и управляй...

http://arduino.ru/forum/obshchii/eshche-raz-ob-uvelicheni-kolichestva-po...

 

bankir_1986
Offline
Зарегистрирован: 23.03.2015

А если вместо тумблеров использовать такт. кнопку со светодиодом. Например такую http://ru.aliexpress.com/item/Freeshipping20pcs-Tactile-Push-Button-Switch-Momentary-Tact-With-LED-12X12X7-0-4-pin-DIP-Through-Hole/32323895935.html Будет видно положение сервы.

jetpi
Offline
Зарегистрирован: 10.02.2016

Большое спасибо за полезную информацию.

Посоветуйте пожалуйста номинал токоограничительных резисторов, и я правильно понял, что их можно поставить между пинами А4,А5 и 14,15 пинами микросхемы? (или обязательно между микросхемой и клавиатурой?)

Также подскажите пожалуйста какие ставить диоды? Их как я понял нужно между клавиатурой и микросхемой? Верно?

Заранее спасибо!

EuBeginer
Offline
Зарегистрирован: 16.11.2015

А кто знает где достать расширитель I2C именно для клавы 4х4. Гуглил гуглил, но пока ничего найти не могу.

svm
Offline
Зарегистрирован: 06.11.2016

Есть еще одна интересная микросхема TM1650 и ей подобные, они испольэуются в DVB-T ресиверах, которые начали массово выходить из строя и если есть знакомые телемастера, то достать их не проблема. Микросхема представляет из себя I2C драйвер 7 сегментного четырехразрядного индикатора и клавиатуры 4х7. При нажатии выдает уникальный скан-код клавиши, при отпускании- другой уникальный код отпускания, который сохраняется до следующего нажатия.

Самопальный даташит на русском https://yadi.sk/i/h-GKf9hMyABLL  пример http://arduino.ru/forum/programmirovanie/kontroller-led-i-klaviatury-fd650v-kak-im-upravlyat#comment-232263

Pyotr
Offline
Зарегистрирован: 12.03.2014

Матричную клаву подключаю как на схеме.

Транзисторы любые n-p-n c небольшим Кусиления (до 100), при бОльшем значении возможно потребуются резисторы с базы каждого транзистора на землю около 47 кОм.

Распиновка матрицы 4х4.

Так выглядит на макетке.

 

Pyotr
Offline
Зарегистрирован: 12.03.2014

Скетч для теста. Нажимаем кнопки и смотрим в монитор соответствующее значение этой кнопке.
Если не нажато, значение 255 или В11111111.
 

#include <Wire.h>
unsigned int carrMillis;
unsigned int prevMill;
unsigned int interval = 500;

void setup(){
  Wire.begin(); 
  Serial.begin (9600);
}
void loop(){
  byte data = 0; // переменная
  carrMillis = word(millis());
  if(carrMillis - prevMill >= interval){
    prevMill += interval;
    Wire.requestFrom(0x38, 1); //делаем запрос 1 байта из
    //микросхемы с адресом 0x38. //Адресные A0,A1,A2 на землю.
    data = Wire.read();
    Serial.println(data, BIN);
  }
}

 

uservasil
Offline
Зарегистрирован: 09.07.2015

Эх вот бы еще это работало с прерыванием, цены бы не было)

или я что то не так делаю?

#include <Wire.h>
#include <i2ckeypad.h>

#define ROWS 4
#define COLS 4


#define PCF8574_ADDR 0x38

i2ckeypad kpd = i2ckeypad(PCF8574_ADDR, ROWS, COLS);

char key=0;

void setup()
{
  Serial.begin(9600);

  Wire.begin();

  kpd.init();
  
  digitalWrite(2, HIGH);        // прерывание 
  attachInterrupt(0, RC, FALLING);  // прерывание по спаду

  Serial.println("Testing keypad/PCF8574 I2C port expander arduino lib\n\n");
}


void loop()
{
}


void RC()
{
  key = kpd.get_key();
  if(key != '\0') {
        Serial.println(key);
  }
}

 

EuBeginer
Offline
Зарегистрирован: 16.11.2015

Всем привет!

PENCRAFT за статью огромное спасибо,но кое что не работает.

недавно начал проект по управлению шаговым... Так вот схема указанная в коменте #2 для данной матричной клавиатры не работет! Такая схема будет достаточной если клаву подключать непосредственно к Ардуине.

А при использовании I2C указанной PCF8574, диодов Шотки (1N5819) либо 1N4148 на входах 9,10,11,12 висит +5В и никакие нажатия не работают. Следует использовать схему PYOTRa см. комент #10  либо подтягивать катоды Диодов через 15кОм к земле.  Спасибо тебе за комент.  Тогда живет.

Транзисторы BC548 (КТ315) пробовал???

USERVASIL! Твой скетч не пробовал, но он работает, ты просто на порту ничего не видишь - не успеваешь нажать. Ты чем вызываешь прерывание? Вручную?

Для тестирования попробуй включи

Serial.print("key="), Serial.println(key);

и ты увидишь каждый раз после прерывания втрочку типа key= и пустота

А продолжая разговор о выходе из цикла хочу сказать, что после всех модификаций (ушел от delay к millis) и все равно плохо выхожу из цикла по нажатию "любой клавиши" этой самой клавы включенной через I2C. Приходится долго держать либо прыгать на ней. Иногда попадает досточно быстро. Подскажите пожалуйста.

 while (key == 0)
   
   {SPD=0; 
    digitalWrite (13, LOW); //гасим 13й
    digitalWrite(9, LOW); //стоп мотор
       key = kpd.get_key();
  lcd.clear();
  prevMILLIS = millis();
  while (millis() - prevMILLIS < 2000)
  {lcd.setCursor(0,0);
  lcd.print (L"TECT ЗABEPШEH");
  lcd.setCursor(0,1);
  lcd.print (L"УCПEШHO");}

  lcd.clear();
  prevMILLIS = millis();
  while (millis() - prevMILLIS < 2000)
  {lcd.setCursor(0, 0);
  lcd.print (L"ДЛЯ BЫXOДA");
  lcd.setCursor(0, 1);
   lcd.print (L"HAЖMИTE С");}
  } 

 

uservasil
Offline
Зарегистрирован: 09.07.2015

EuBeginer пишет:

USERVASIL! Твой скетч не пробовал, но он работает, ты просто на порту ничего не видишь - не успеваешь нажать. Ты чем вызываешь прерывание? Вручную?

Для тестирования попробуй включи

Serial.print("key="), Serial.println(key);

и ты увидишь каждый раз после прерывания втрочку типа key= и пустота

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

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Как говорил Штирлиц, важно не только зайти в разговор, но и выйти...
Выход из прерывания как оформляется, не увидел я его в тексте?

Pyotr
Offline
Зарегистрирован: 12.03.2014

EuBeginer пишет:

....А при использовании I2C указанной PCF8574, диодов Шотки (1N5819) либо 1N4148 на входах 9,10,11,12 висит +5В и никакие нажатия не работают. Следует использовать схему PYOTRa см. комент #10  либо подтягивать катоды Диодов через 15кОм к земле.  Спасибо тебе за комент.  Тогда живет.

Транзисторы BC548 (КТ315) пробовал???...


Транзисторы любые маломощные пойдут npn с усилением от нескольких единиц. КТ315 по цоколевке мне не подошли. Подтяжка в PCF8574 выдает 0.15 мА.

Такую же схему можно и к порту МК применить с включенной подтяжкой. Если кто не догадался;).
Чтение ещё проще:

byte key = PIND;//клава подкл. D0-D7 на УНО и подобных

uservasil
Offline
Зарегистрирован: 09.07.2015

ua6em пишет:

Как говорил Штирлиц, важно не только зайти в разговор, но и выйти...
Выход из прерывания как оформляется, не увидел я его в тексте?

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

int pin = 13;
volatile int state = LOW;
 
void setup()
{
  pinMode(pin, OUTPUT);
  attachInterrupt(0, blink, CHANGE);
}
 
void loop()
{
  digitalWrite(pin, state);
}
 
void blink()
{
  state = !state;
}

может подскажите как будет правильнее, в данный момент в этом коде при срабатывании прерывания, в порт пишется "wo" т.е. часть  Serial.println("work"); и ардуинка виснет.

#include <Wire.h>
#include <i2ckeypad.h>

#define ROWS 4
#define COLS 4

#define PCF8574_ADDR 0x38

i2ckeypad kpd = i2ckeypad(PCF8574_ADDR, ROWS, COLS);

volatile char key=0;

void setup()
{
  Serial.begin(9600);

  Wire.begin();

  kpd.init();
  
  digitalWrite(2, HIGH);        // прерывание 
  attachInterrupt(0, RC, FALLING);  // прерывание по спаду


  Serial.println("Testing keypad/PCF8574 I2C port expander arduino lib\n\n");
}

void loop()
{

}





void RC()
{
  Serial.println("work");
  key = kpd.get_key();
  if(key != '\0') {
        Serial.println(key);
  }
  detachInterrupt(0);
}

 

EuBeginer
Offline
Зарегистрирован: 16.11.2015

Uservasil!  такая операция как опрос I2C клавиатуры

 Serial.println("work");
40   key = kpd.get_key();
41   if(key != '\0') {
42         Serial.println(key);

да вообще все I2C операции очень тяжелы для прерываний. Это не работает.

Поэтому вы и видите только WO... дальше Ардуина улетает в шину и привет....

А вот detach Вы просто вырубаетет прерывание.

Вы что собственно хотите от прерывания? Вывести информацию на экран или опросить клаву???

Может проще некую процедуру вызывать??? И вот там  все заработает. 

Типа void ABC() и делайте там что хотите . Хотите цикл по опросу клавы, хотите выводите на экран. То есть, как Вы уже убедились, Ваше прерывание выполняется, а вот для выхода никакого detach не нужно. Оно само вернется. Попробуте оставить только печать в порт.

uservasil
Offline
Зарегистрирован: 09.07.2015

EuBeginer пишет:

Вы что собственно хотите от прерывания? Вывести информацию на экран или опросить клаву???

Может проще некую процедуру вызывать??? И вот там  все заработает. 

Типа void ABC() и делайте там что хотите . Хотите цикл по опросу клавы, хотите выводите на экран. То есть, как Вы уже убедились, Ваше прерывание выполняется, а вот для выхода никакого detach не нужно. Оно само вернется. Попробуте оставить только печать в порт.

мне требуется только опросить клаву, по поводу "проще некую процедуру вызывать" можно пример?

я так понимаю через переменную в loop?

EuBeginer
Offline
Зарегистрирован: 16.11.2015

Вот такой живой рабочий пример.

И крутитесь в это процедуре сколько хотите. А поскольку у меня есть проверка граничных значений, то я и delay могу себе позволить использовать. На глобальный процесс это не влияет. А вот в loop  уменя в основном разные прверки условий и переходы на разные процедуры (всего 5). 

void VVOD_CHISLA(); //определение процедуры ввода численных значений
//_________________________________________
void VVOD_CHISLA ()
{
 // Serial.println("procedure VVOD_CHISLA");
      key = kpd.get_key();
  if (key >= '0' && key <= '9')
 {
        chislo *= 10;
        chislo += key-48; // конвертируем ascii смволы в числа десятичной системы от 0 до 9.
        lcd.setCursor (0,1);
        lcd.print (chislo);
 }
 if (key == '#')
    {
      if(vvod == 1) 
      { if (chislo > 65000)
      { lcd.clear();
        lcd.setCursor(0,1);
        lcd.print (L"ЗHAЧ. ПPEBЫШEHO");
        delay (2000);
        chislo = 0;
        CYC=0;
        SPD=0;
        loop();}
  else {CYC = chislo;
  init_CYC = CYC;}
      //Serial.print("CYC "), Serial.println(CYC); 
      }
            
      if(vvod == 2) 
      { if (chislo > 99)
      {chislo = 0;
      lcd.clear();
      lcd.setCursor(0,1);
      lcd.print (L"ЗHAЧ. ПPEBЫШEHO");
      delay (2000);
     SPD=0;
     CYC=0;
     loop();
     }
  else {SPD = chislo;}
      //Serial.print("SPD "), Serial.println(SPD);  
      }
    }
  }

При выпонении одной из процедур (вращение шагового) у меня проверяется время выполнения одного из действий механизма, который приводится в действие этим самым мотором, и после выполнения команд этих прерываний процесс снова возвращается в процедуру МОТОР.

Вот пример скетча и двух внешних прерываний



void PR_MOTOR;// описание процедуры МОТОР

void setup()
{
attachInterrupt(0, PERED, FALLING); //передник Кожуха- датчик Холла 1
attachInterrupt(1, ZADNIK, RISING); //задник  Кожуха- датчик Холла 2
.....

// тут идет код дальше
//_______Прерывание по началу возврата_______
void ZADNIK()
{ VZRT = millis();
Serial.print ("VZRT = "); Serial.println (VZRT);
}
//_______Прерывание по концу возврата  _______ 
void PERED()
{dVZRT = millis()- VZRT;
-- CYC;
Serial.print ("dVZRT = "); Serial.println (dVZRT);
}

Выполнение команд печати через порт Serial.print работают четко, а вот вывод на дисплей через I2C не идет. Слишком она медленная. Пробовал, процесс естественно замедлялся и в итоге, то самое время, что я должен измерять УВЕЛИЧИВАЛОСЬ. Ну и весь мой станок останавливался по этому самому превышению. Поэтому от вывода информации на LCD1602 через шину во время работы прерываний я вынужден был отказаться.

Ну, вот как то так, может быть чем и помог Вам.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

EuBeginer пишет:

Выполнение команд печати через порт Serial.print работают четко, а вот вывод на дисплей через I2C не идет. Слишком она медленная. Пробовал, процесс естественно замедлялся и в итоге, то самое время, что я должен измерять УВЕЛИЧИВАЛОСЬ. Ну и весь мой станок останавливался по этому самому превышению. Поэтому от вывода информации на LCD1602 через шину во время работы прерываний я вынужден был отказаться.

Скорость работы I2C LCD1602 недавно обсуждалась здесь: http://arduino.ru/forum/apparatnye-voprosy/medlennaya-rabota-liquidcryst...

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

EuBeginer
Offline
Зарегистрирован: 16.11.2015

Andriano  привет!  Спасибо загляну, покумекаю. Может и поедет.

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

Спасибо за ссылку посмотрю.

EuBeginer
Offline
Зарегистрирован: 16.11.2015

Pyotr привет!  Попробовал схему с ВС548. И опечалился. Происходит самопроизвольное "нажатие" Осциллограф показал "грязь" на фоне высокого уровня, дребезг, помеха... Может от того, что собрал на макетке.

А вот такая схема, как сподтяжкой так без нее работает крайне устойчиво. Толко пришлось развернуть диоды АНОДОМ к PCF8574!!! Пробовал с Шоткой 1N5819 и с 1N4148. Все работает отлично. Вот схемка. Думаю надо наверное и шилдик развести.

Pyotr
Offline
Зарегистрирован: 12.03.2014

EuBeginer пишет:

Pyotr привет!  Попробовал схему с ВС548. И опечалился. Происходит самопроизвольное "нажатие" Осциллограф показал "грязь" на фоне высокого уровня, дребезг, помеха... Может от того, что собрал на макетке.

У ВС548 Кусиления несколько сотен, поэтому наводки на "висячую" базу заметны. Нужно базы подтянуть к земле резистором 22-47 кОм. Выше я об этом предупреждал.

У меня работают С1815 , у них усиление около 70.
А вообще нужно в этой схеме ставить цифровые транзисторы, у них нижняя подтяжка базы встроена в корпус.

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

 

EuBeginer
Offline
Зарегистрирован: 16.11.2015

ну по даташиту у ВС548 150-200 значится. Но усек. А С1815 у меня тоже есть, правда не куча.

ser314044
Offline
Зарегистрирован: 04.02.2018

Ндавно подключал клавиатуру от ик пульта муз. центра. Долго искал как подключить, клавиатуру такого типа.

void setup() {
Serial.begin(9600);
}

void loop() {
int pin = 12; //количество пинов
byte arra[pin + 1 ]={5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 19}; //пины клавиатуры
byte x = 0;
byte y = 0;    //номер кнопки

for (int i=0; i <= pin; i++) {    //все пины как вход
    pinMode(arra[i], INPUT);
   digitalWrite(arra[i], HIGH);
}
  
for (int i= pin; i >= 0; i=i-1) {   //один пин как выход, на остальных проверяем сигнал
  // Serial.print(i);
    pinMode(arra[i], OUTPUT);
    digitalWrite(arra[i], LOW);
    delay(1);
    
         for (int j=0; j < i; j++) {
         //  Serial.print(j);
          Serial.print(digitalRead(arra[j]));   //есть ли нажетие на кнопку
         // if (!digitalRead(arra[j])) Serial.print(arra[i]*100+arra[j]); //между какими пинами кнопка
           x = x + 1 ;   //следующая кнопка
           if ((digitalRead(arra[j])) == LOW) {
               if (y > 0)       y = 200;  //нажато больше одной кнопки
                else     y = x;    //нажата первая кнопка
                   }
              }
              
    pinMode(arra[i], INPUT); 
    digitalWrite(arra[i], HIGH);  
    delay(1);     
      }
      
Serial.println();
Serial.print(y);  //номер кнопки
Serial.println();
delay(1);  
}

Уже месяц работает без сбоев. Каждый пин по очереди работает как вход и выход. На 12 пинов можно подключить 78 кнопок, если каждую кнопку подключать через диод то можно подключить в два раза больше.

Liberty_C
Offline
Зарегистрирован: 26.12.2018
на счет "pcf8574_write(pcf, val) != PCF8574_OK" не парьтесь, просто ставьте код посылки или приема по адресу в I2C. И да, поставил на первые 4 выхода, последовательно 4 резистора 500 ом (какие нашел)
#include "klava.h"
 
uint8_t Pushch (pcf8574* pcf){
 
    uint8_t  adres = pcf->address;
  pcf->address = 0x4e;
         uint8_t push [] = {'*','0','#','D','7','8','9','C','4','5','6','B','1','2','3','A'};
          uint8_t val = 8,val1 = 0 , iter = 16,i=0;
          uint8_t* psource_val1 = &val1; 
 
          while (val){
        if (pcf8574_write(pcf, val) != PCF8574_OK) error(); 
        if (pcf8574_read(pcf, psource_val1) != PCF8574_OK) error();
        if(val!=val1)
        { val = iter;
          while(iter <= 128){
                                if (pcf8574_write(pcf, val) != PCF8574_OK) error(); 
                    if (pcf8574_read(pcf, psource_val1) != PCF8574_OK) error();
                    if(val!=val1){                      
                                 // printf("%c \n",push[i]); // для отладки в окне терминала раскоментировать.
                                  while(val!=val1){
                    if (pcf8574_write(pcf, val) != PCF8574_OK) error(); 
                    if (pcf8574_read(pcf, psource_val1) != PCF8574_OK) error();
                                  }
                                  pcf->address = adres;
                      return push[i];                                                                  
                                  }else i++;
                    val *=2;
                           } 
       }else i+=4;
       if(val == 1 ) val=0;
         val /=2; 
          }        
          pcf->address = adres;
          return 0;
}
 
uint8_t Push (pcf8574* pcf){
  uint8_t  adres = pcf->address;
  pcf->address = 0x4e;
 
         uint8_t push [] = {'*','0','#','D','7','8','9','C','4','5','6','B','1','2','3','A'};
          uint8_t val = 8,val1 = 0 , iter = 16,i=0;
          uint8_t* psource_val1 = &val1; 
 
          while (val){
        if (pcf8574_write(pcf, val) != PCF8574_OK) error(); 
        if (pcf8574_read(pcf, psource_val1) != PCF8574_OK) error();
        if(val!=val1)
        { val = iter;
          while(iter <= 128){
                                if (pcf8574_write(pcf, val) != PCF8574_OK) error(); 
                    if (pcf8574_read(pcf, psource_val1) != PCF8574_OK) error();
                    if(val!=val1){                      
                                 pcf->address = adres;
                      return push[i];                                                                  
                                  }else i++;
                    val *=2;
                           } 
       }else i+=4;
       if(val == 1 ) val=0;
         val /=2; 
          }   
            
  pcf->address = adres;
          return 0;
}
Любителям STM32f103C8T6 .
подключил как есть без резисторов. в хал  зависает иногда но бросил доводить до ума. может код каму понадобится.
 
DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Liberty_C пишет:

может код каму понадобится.
 
Такой - нет.