Ввод числа с матричной клавиатуры.

DualPrince
Offline
Зарегистрирован: 22.08.2014

Здравствуйте! Помогите, пожалуйста , с вводом чисел с матричной клавиатуры(http://dvrobot.ru/238/454.html).

Проблема такая: мне надо чтобы после нажатии клавиши A(11) я смог ввести нужное число (например 3560)

и чтобы оно созранилось в переменой(int) для последующей обработки( в моём случае мне надо ввести значение в милилитрах и когда вода проходила через датчик(http://dvrobot.ru/240/121.html) сначение уменьшалось.

Вот небольшой код который я нашёл и немного переделал. Всё сделал немножко убого.

int rows[]={9,8,7,6}; //указываем пины строк
int cols[]={5,4,3,2}; //и столбцов
//массив с соответствиями кодов нажатых кнопок
int simbols[][2]={
  {0,1}, //Клавиша 1
  {1,2}, //Клавиша 2
  {2,3}, //Клавиша 3
  {3,11}, //Клавиша A
  {4,4}, //Клавиша 4
  {5,5}, //Клавиша 5
  {6,6}, //Клавиша 6
  {7,12}, //Клавиша B
  {8,7}, //Клавиша 7
  {9,8}, //Клавиша 8
  {10,9}, //Клавиша 9
  {11,13}, //Клавиша C
  {12,16}, //Клавиша *
  {13,0}, //Клавиша 0
  {14,15}, //Клавиша #
  {15,14} //Клавиша D
};
int FindKey(unsigned int keyCode){
  for(int i=0;i<16;i++){
    if(simbols[i][0]==keyCode){
      return simbols[i][1];
    }
  }
  return '-'; //если код на найден возвращаем ‘-‘
}

void setup() {
  Serial.begin(9600);
  Serial.println("Starting...");
  //определяем пины для строк и столбцов
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
}

void loop() {
  for(int i=0;i<4;i++){ //цикл для перехода по всем строкам
    digitalWrite(rows[i], HIGH); //подаем на текущую строку высокий уровень
    for(int j=0;j<4;j++){ //цикл для переходов по всем столбцам
      if(digitalRead(cols[j])==HIGH){ //если уровень высокий, то кнопка нажата
        delay(250);
        //Serial.print(FindKey(4*i+j)); //выводим в терминал код нажатой кнопки
        int  key = FindKey(4*i+j);
        if (key) {
          Serial.println(key);
        }
      }
    }
    digitalWrite(rows[i], LOW);  //выключаем высокий уровень для пройденной строки
  }
}







 

nevkon
Offline
Зарегистрирован: 20.01.2015

Есть 2 варианта: делается поиск до первого обнаружения нажатия и обнаружение всех нажатий (а вдруг вы все 16 кнопок нажмете разом). Алгоритмы будут отличаться. У вас в коде очень сложно все сделано, к тому же с ошибками которые компилятор не видит.

DualPrince
Offline
Зарегистрирован: 22.08.2014

Как это можно сделать? Код то можно сказать не мой, а сам додуматься как это сделать не могу.

maksim
Offline
Зарегистрирован: 12.02.2012

Код индусский, да еще и не ваш... ну получите вы число, а что дальше?

//определяем пины для строк и столбцов
byte rows[] = {9,8,7,6}; //указываем пины строк
byte cols[] = {5,4,3,2}; //и столбцов
//массив с соответствиями кодов нажатых кнопок
char simbols[][4] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}  
};

int mililiters = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Starting...");
  for(byte i = 0; i < 4; i++) pinMode(cols[i], INPUT);
  for(byte i = 0; i < 4; i++) pinMode(rows[i], OUTPUT);
}

void loop() {
  char  key = 0;
  for(int i=0; i < 4; i++){ //цикл для перехода по всем строкам
    digitalWrite(rows[i], HIGH); //подаем на текущую строку высокий уровень
    for(int j=0; j < 4; j++){ //цикл для переходов по всем столбцам
      if(digitalRead(cols[j])==HIGH)
      {
        key = simbols[i][j]; //если уровень высокий, то кнопка нажата
        delay(100);
      }
    }
    digitalWrite(rows[i], LOW);  //выключаем высокий уровень для пройденной строки
  }

  if(key) {
    Serial.println(key);
    static int chislo = 0;
    static byte vvod = 0;
    if(key == 'A') 
    {
      chislo = 0;
      vvod = 1;
    }
    else if(vvod > 0 && key == '#')
    {
      mililiters = chislo/10;
      vvod = 0;
      Serial.println(mililiters);
    }
    else if(key >= '0' && key <= '9')
    {
      if(vvod > 0)
      {
        chislo =+ key;
        chislo *= 10;
      }
    }
  }
}

после окончания ввода числа необходимо нажать #.

DualPrince
Offline
Зарегистрирован: 22.08.2014

Моя идея такая: программма для разлива хотелось бы чтобы работала так чтобы при нажатии A вводился объём милилитрах , кнопкой B вводилось количество разливов , кнопкой C вводилось промижуток между разливом и на дисплее отображалось объём и количество разливов. Что-то мне подсказывает что это не так просто.

maksim
Offline
Зарегистрирован: 12.02.2012

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

DualPrince
Offline
Зарегистрирован: 22.08.2014

Спасибо! Буду разбираться.

maksim
Offline
Зарегистрирован: 12.02.2012
//определяем пины для строк и столбцов
byte rows[] = {9,8,7,6}; //указываем пины строк
byte cols[] = {5,4,3,2}; //и столбцов
//массив с соответствиями кодов нажатых кнопок
char simbols[][4] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}  
};

int mililiters = 0, kolichestvo = 0, promejutok = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Starting...");
  for(byte i = 0; i < 4; i++) pinMode(cols[i], INPUT);
  for(byte i = 0; i < 4; i++) pinMode(rows[i], OUTPUT);
}

void loop() {
  char  key = 0;
  for(int i=0; i < 4; i++){ //цикл для перехода по всем строкам
    digitalWrite(rows[i], HIGH); //подаем на текущую строку высокий уровень
    for(int j=0; j < 4; j++){ //цикл для переходов по всем столбцам
      if(digitalRead(cols[j])==HIGH)
      {
        key = simbols[i][j]; //если уровень высокий, то кнопка нажата
        delay(100);
      }
    }
    digitalWrite(rows[i], LOW);  //выключаем высокий уровень для пройденной строки
  }

  if(key) {
    Serial.println(key);
    static int chislo = 0;
    static byte vvod = 0;
    if(key >= 'A' && key <= 'D') 
    {
      chislo = 0;
      if(key == 'A') vvod = 1;
      if(key == 'B') vvod = 2;
      if(key == 'C') vvod = 3;
    }
    else if(key == '#' && vvod > 0)
    {
      if(vvod == 1) mililiters = chislo, Serial.print("mililiters = "), Serial.println(mililiters);
      if(vvod == 2) kolichestvo = chislo, Serial.print("kolichestvo = "), Serial.println(kolichestvo);
      if(vvod == 3) promejutok = chislo, Serial.print("promejutok ="), Serial.println(promejutok);
      chislo = 0;
      vvod = 0;
    }
    else if(key >= '0' && key <= '9')
    {
      if(vvod > 0)
      {
        chislo *= 10;
        chislo =+ key-48;
      }
    }
  }
}

 

DualPrince
Offline
Зарегистрирован: 22.08.2014

Спасибо вам большое за это!.  Всё понятно что написано,хоть и сам данный алгоритм долго бы делал. Вот только проблемма с вводом ЧИСЛА так и не исправилось, ЦИФРЫ вводятся от 0 до 9 а число(например 50) непойму как сделать. Если только как нибудь перевести цифры в стринговый тип для получения числа, а потом с помощью функции toInt() (http://www.arduino.cc/en/Reference/StringToInt) перевести в переменую. Возможно это бред какой-то.

maksim
Offline
Зарегистрирован: 12.02.2012

Читайте внимательно:

maksim пишет:

после окончания ввода числа необходимо нажать #.

DualPrince
Offline
Зарегистрирован: 22.08.2014

Я так и делаю. Нажимаю A ввожу 50 и милилитры у меня выстовляет 0 , ввожу 53  и милилитры показывает 3. Тоесть последнюю введёную цифру а не всё число.

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

Ошибка в 59 строке

maksim
Offline
Зарегистрирован: 12.02.2012

Точно опечатка =+ поменять на +=.

DualPrince
Offline
Зарегистрирован: 22.08.2014

Спасибо! Уже поменял + задержку побольше сделал до 200 мс чтоб-бы быстро не вводилось.

DualPrince
Offline
Зарегистрирован: 22.08.2014

Здравствуйте [maksim] , можно ли сделать так: есть переменая L (например перемен 1000) и вторая переменая K (например 3).

когда L равна 0,  K  уменьшается на 1(то есть остаётся 2) после этого L сново становится сново 1000 и продолжается это до того пока K не будит 0

maksim
Offline
Зарегистрирован: 12.02.2012

Конечно можно. Вот тут все есть для решения этой задачи. А начать можно с этого.

DualPrince
Offline
Зарегистрирован: 22.08.2014

Спасибо

DualPrince
Offline
Зарегистрирован: 22.08.2014

Сам разобрался как это сделать.

fil_and
Offline
Зарегистрирован: 12.05.2015

И ещё про конечные автоматы почитай и диаграмы состояний.

напоимер тут

http://is.ifmo.ru/download/switch.pdf

http://is.ifmo.ru/

b121708
Offline
Зарегистрирован: 18.01.2014

Здравствуйте.Подскажите,почему в протеусе не работает?

b121708
Offline
Зарегистрирован: 18.01.2014

извените-подтяжек на входах не поставил

Виктор Смирнов
Offline
Зарегистрирован: 17.03.2017

Максим, здравствуйте. Нужна помощь профессионального программиста. Если интересно позвоните +7-985-766-26-50 Виктор.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Добрый вечер, заказал для проекта клавиатуру 1 строка 6 столбцов. На прошлой неделе пришла, вна фото(в нее также встроен светодиод, но его не пользовал).

На просторах интернета нашел код без библиотек, для моей клавитуры как раз. Смысл такой: по нажатию клавиши в монитор порта выводится символ этой клавиши. Переписал через регистровое представление пинов ардуино ( у меня uno), лично мне так  читабельнее. Все работает, но мешает развитию delay. 

/*** Подключение матричной клавиатуры к микроконтроллеру AVR ***/
#include <avr/io.h> // библиотека в которой находятся определения констант, имен регистров avr

const char value[6] = {'1', '2', '3', '4', '5', '6'};

void setup() {
  /*pinMode (8, OUTPUT);
  pinMode (9, OUTPUT);
  pinMode (10, OUTPUT);
  pinMode (11, OUTPUT);
  pinMode (12, OUTPUT);
  pinMode (13, OUTPUT);
  pinMode (6, INPUT_PULLUP);  // порт на вход с подтяжкой (принимают нули на строках)*/
  DDRB |= (1 << PB5)|(1 << PB4)|(1 << PB3)|(1 << PB2)|(1 << PB1)|(1 << PB0); // инициализируем порты на выход(столбцы)
  DDRC &= ~(1 << PC2); // порт на вход(строка)
  PORTC |= 1 << PC2; // подключить внутренние подтягивающие pull-up резисторы
  
  Serial.begin(9600); // открываем Serial порт
}

void scan_key(){ // создаем функцию для чтения кнопок
  for (byte i = 0; i <= 5; i++) {  // цикл, передающий 0 по всем столбцам
    PORTB &= ~(1 << i);  //digitalWrite(PinOut[i - 1], LOW); // если i меньше 6 , то отправляем 0 на ножку
    if (!(PINC&0x04)) { //if (digitalRead(PinIn) == LOW) { // если порт входа равен 0, то..
        Serial.println(value[i]); 
        delay(175);
    }
        PORTB |= 1 << i;  //digitalWrite(PinOut[i - 1], HIGH); // подаём обратно высокий уровень
  }
}

void loop() {
  scan_key(); // используем функцию опроса матричной клавиатуры
}

"Можно кстати опрашивать клавиатуру через прерывание по таймеру" - Я так и решил действовать. На сайте http://www.instructables.com/id/Arduino-Timer-Interrupts/ вполне понятно объяснено о работе таймеров, конкетно о прерывание по таймеру. Вот код взятый оттуда blink на таймере 1 каждую секунду

//#define F_CPU 16000000UL
#include <avr/io.h> // библиотека в которой находятся определения констант, имен регистров avr
#include <avr/interrupt.h>  

boolean toggle1 =0;

void setup() {
  DDRB |= 1 << PB5;
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0; // начальное значение таймера
  OCR1A = 15624; // 16000000 / (1024 * 1) - 1 = 15624
  TCCR1B |= (1<<CS10)|(1<<CS12)|(1<<WGM12); // делитель f/1024, устанавливаем режим СТС (сброс по совпадению)
  TIMSK1 = (1 << OCIE1A); //устанавливаем бит разрешения прерывания по совпадению с OCR1A
  sei(); // Выставляем бит общего разрешения прерываний
}

ISR(TIMER1_COMPA_vect){ // timer 1
  if(toggle1){
    PORTB |= 1 << PB5;
    toggle1 = 0;
  }
  else {
    PORTB &= ~(1 << PB5);
    toggle1 = 1;
  }
}

void loop() {

}

Применительно к клаве от сериал соединения отказался сразу поскольку уже где то сталкивался, что в прерывании могут потеряться байты по UART. На основе предыдущего написал код, который задейтвует 5 клавиш клавиатуры, шестую заменил светодиод. Смысл в вызове функции обработчика прерываний каждые 50 мс для устранения дребезга, но код не фурычит. Алгоритм кода - по нажатию любой клавиши должен загореться диод. Подскажите в чем ошибся? 

//#define F_CPU 16000000UL
#include <avr/io.h> // библиотека в которой находятся определения констант, имен регистров avr
#include <avr/interrupt.h>  

volatile byte i;

void setup() {
  DDRB |= (1 << PB5)|(1 << PB4)|(1 << PB3)|(1 << PB2)|(1 << PB1)|(1 << PB0); // инициализируем порты на выход(столбцы)
  DDRC &= ~(1 << PC2); // порт на вход(строка)
  PORTC |= 1 << PC2; // подключить внутренние подтягивающие pull-up резисторы
  
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0; // начальное значение таймера
  OCR1A = 780; // 16000000 / (1024 * 20) - 1 = 780 -> частота опроса клавиатуры 50 мс
  TCCR1B |= (1<<CS10)|(1<<CS12)|(1<<WGM12); // делитель f/1024, устанавливаем режим СТС (сброс по совпадению)
  TIMSK1 = (1 << OCIE1A); //устанавливаем бит разрешения прерывания по совпадению с OCR1A
  sei(); // Выставляем бит общего разрешения прерываний
}

ISR(TIMER1_COMPA_vect){ // timer 1
  for (i = 0; i <= 4; i++) {  // цикл, передающий 0 по всем столбцам
    PORTB &= ~(1 << i);  // если i меньше 6 , то отправляем 0 на ножку
    if (!(PINC&0x04)) {  // если порт входа равен 0, то..
        PORTB |= 1 << PB5;
    }
        PORTB |= 1 << i;  // подаём обратно высокий уровень
  }
}

void loop() {

}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Конкретно светодиод не реагирует на нажатия, видимо не отрабатывается обрабтка клавиш. Буду очень благодарен если подскажите где копать

Иван_
Offline
Зарегистрирован: 23.02.2018

   Добрый день. 

   Как обычно просьба сильно не пинать начинающего.

    Стоит задача во время выполнения основного цикла работы программы в водить новые данные для работы программы с клавиатуры при этом отображая значения вводимых данных на LCD экране. Программа будет состоять из нескольких параллельных блоков которые будут исполнятся в зависимости от введенных данных. Планировал осуществлять ввод данных по функции внешнего прерывания attachInterrupt но не получается так как из нутри прерывания не получается вывести информацию на дисплей. В общем я думаю что задача достаточно стандартная. Если у кого есть мысли по этому поводу прошу поделится. Постоянно опрашивать клавиатуру в loop сильно не желательно так как изменения значений переменных будут происходить достаточно редко. буду признателен за реальные советы. 

 

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

Иван_ пишет:

   Добрый день. 

   Как обычно просьба сильно не пинать начинающего.

    Стоит задача во время выполнения основного цикла работы программы в водить новые данные для работы программы с клавиатуры при этом отображая значения вводимых данных на LCD экране. Программа будет состоять из нескольких параллельных блоков которые будут исполнятся в зависимости от введенных данных. Планировал осуществлять ввод данных по функции внешнего прерывания attachInterrupt но не получается так как из нутри прерывания не получается вывести информацию на дисплей. В общем я думаю что задача достаточно стандартная. Если у кого есть мысли по этому поводу прошу поделится. Постоянно опрашивать клавиатуру в loop сильно не желательно так как изменения значений переменных будут происходить достаточно редко. буду признателен за реальные советы. 

 

 в прерывании выставить флаг обработки клавиатуры, далее в лупе вызвать эту программу, в программе - если флаг выставлен - обрабатываем, что вам надо, если нет ничего не делаем, я бы делал так

Иван_
Offline
Зарегистрирован: 23.02.2018

Спасибо за ответ. Можно конечно попробывать и с флагом. Но есть один момент так как основное время программа будет крутится в функциях то прейдется с какойто переодичностью допустим 1 раз в 30 сек. опрашивать состояние флага а это не совсем хорошо. Если есть возможность опрашивать флаг только после прохождения прерывания то буду благодарен если подскажете как это сделать.

Иван_
Offline
Зарегистрирован: 23.02.2018

Хотя если в каждой функции опрашивать флаг то оно может и ничего получится.