Форум программистов, компьютерный форум, киберфорум
Наши страницы
Микроконтроллеры ARM, Cortex, STM32
Войти
Регистрация
Восстановить пароль
 
supercelt
0 / 0 / 0
Регистрация: 14.12.2010
Сообщений: 35
#1

Сканирование матричной клавиатуры - ARM, Cortex, STM32 микроконтроллер

25.08.2017, 17:08. Просмотров 332. Ответов 11
Метки нет (Все метки)

Здравствуйте! Помогите пожалуйста разобраться. Я пишу опросник матричной клавы 3х4 на СИ в Keil под мк stm32f4.
Сразу скажу вот что, код ниже отлично работал у меня на stm32f100.
файл С
Кликните здесь для просмотра всего текста

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void KEYBOARD_SCAN(void){ //Функция для сканирования клавы
    int i;
    start_scan = 0; //Сбрасываем флаг, после вызова этой функции
    for(counter_row_scan = 0; counter_row_scan < ROWS; counter_row_scan++){ //Сканирование рядов
        row_port[counter_row_scan]->BRR = (1 << row_pin[counter_row_scan]); //Выставляем 0 на текущем пине рядов
            for(counter_col_scan = 0; counter_col_scan < COLS; counter_col_scan++){ //Сканирование колонок
                if(!(col_port[counter_col_scan]->IDR & (1 << col_pin[counter_col_scan]))){ //Если на текущем пине колонок обнаружился 0
                    counter_release = 0; //Обнулить счётчик отпускания кнопки
                    key = keys[counter_row_scan][counter_col_scan]; //Записать текущеесостояние кнопки из массива
                    if(key == old_key){ //Если текущая кнопка равна кнопке с прошлого сканирования
                        if(KEYB_COUNT_SCAN == counter_pressed++){ //Если кнопку нажимают в течении количества сканирований, указанных в параметрах, то засчитываем нажатие кнопки
                            find_key = key; //Кнопка найдена
                        }
                        if(KEYB_HOLD_COUNTER_PERIOD == counter_pressed){ //Срабатывание от длительного нажатия кнопки //Если кнопка нажата в течении указанного в параметрах количества секунд
                            find_hold_key = key; //Символ пустышка, для опознования, что сработало долгое нажатие определённой кнопки.
                        }
                    }else{
                        old_key = key; //В следующем сканировании, old key это будет значение кнопки просканенное в прошлый раз
                    }
                }else{
                    if(counter_pressed > KEYB_COUNT_SCAN){ //Если количество сканирований нажатой кнопки больше указанных в параметрах
                    if((COLS * KEYB_COUNT_SCAN) == counter_release++){ //счетчик что кнопку отпустили
                        counter_pressed = 0; //Сброс счётчика нажатой кнопки
                        counter_release = 0; //Сброс счётчика отпущенной кнопки
                        TIM2->CR1 &= ~TIM_CR1_CEN; //Выключение таймера2
                        TIM2->CNT = 0; //Обнуление счётчика таймера2
                        old_key = 0;
                        key = 0;
                        for(i = 0; i < ROWS; i++){
                            row_port[i]->BRR = (1 << row_pin[i]); //Все пины рядов обратно в 0
                            EXTI->FTSR |= (1 << col_pin[i]); //Включаем прерывания путём вкл условия срабатывания прерывания (по спаду фронта)
                        }
                        return; //Кнопка отжата, всё выключили, больше в этой функции нечего делать, поэтому принудительно выходим
                    }
                }
            }
        }
        row_port[counter_row_scan]->BSRR = (1 << row_pin[counter_row_scan]); //После скана всех колонок поднимаем лог уровень на текущем ряде в 1
    }
}


Файл h
Кликните здесь для просмотра всего текста

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#ifndef KEYBOARD_H
#define KEYBOARD_H
 
#include "stm32f10x.h"
#include "lcd.h"
//НАСТРОЙКИ=====================================================================
#define ROWS 4 //Объявляем, что на клаве 4 ряда кнопок
#define COLS 3 //Объявляем, что на клаве 3 колонки кнопок
#define KEYB_COUNT_SCAN 5 //Количество полных сканирований клавы, после которых принимается решение, что нажатие и отпускание кнопки засчитано.
#define KEYB_SCAN_PERIOD 10 //Keyboard period scan, ms
#define KEYB_HOLD_PERIOD 1000 //Period hold button, ms Продолжительность времени, за которое засчитываем удержание кнопки. Т.е. Длительное нажатие. 
#define KEYB_LED 0 //Если объявлен этот дефайн, то используется светодоид подтверждения нажатия клавиш. Если не используется, закомментировать строку. 0 - если светодиод включается подачей нуля на пин, 1 - если подачей единицы на пин
#define KEYB_BUZZER 1 //Если объявлен этот дефайн, то используется буззер подтверждения нажатия клавиш. Если не используется, закомментировать строку. 0 - если буззер включается подачей нуля на пин, 1 - если подачей единицы на пин
#define KEYB_ROW1_PORT GPIOB //Порт 1 ряда
#define KEYB_ROW2_PORT GPIOB //Порт 2 ряда
#define KEYB_ROW3_PORT GPIOB //Порт 3 ряда
#define KEYB_ROW4_PORT GPIOB //Порт 4 ряда
#define KEYB_ROW_PIN1 5 //Пин 1 ряда
#define KEYB_ROW_PIN2 6 //Пин 2 ряда 
#define KEYB_ROW_PIN3 7 //Пин 3 ряда
#define KEYB_ROW_PIN4 8 //Пин 4 ряда
#define KEYB_COL1_PORT GPIOB //Порт 1 колонки
#define KEYB_COL2_PORT GPIOB //Порт 2 колонки
#define KEYB_COL3_PORT GPIOB //Порт 3 колонки
//#define KEYB_COL4_PORT GPIOB //Порт 4 колонки
#define KEYB_COL_PIN1 11 //Пин 1 колонки 
#define KEYB_COL_PIN2 12 //Пин 2 колонки 
#define KEYB_COL_PIN3 13 //Пин 3 колонки 
//#define KEYB_COL_PIN4 14 //Пин 4 колонки 
//Конец НАСТРОЕК================================================================================================
#define KEYB_HOLD_COUNTER_PERIOD (KEYB_HOLD_PERIOD / KEYB_SCAN_PERIOD)
#define GPIO_MODE_ROWS 0x5 //Режим пина для рядов GP Out, 10MHz, Open Drain
#define GPIO_MODE_COLS 0x8 //Режим пина для колонок In, Push Pull
 
 
void KEYBOARD_INI(void);
void KEYBOARD_SCAN(void);
extern uint16_t start_scan;
extern char find_key, find_hold_key;
#endif


Принцип работы у меня такой:
строки делались на выход открытый коллектор с подтяжкой к 0. Колонки - вход с подтяжкой к 1. Так же на колонки вешаю внешнее прерывание по спаду.
Как только нажали любую кнопку, сработало прерывание, после этого все строки поднимаем в 1. Далее запускаем таймер, который раз в 10 милисекунд вызывает ф-ию
сканирования клавы. Даллее там идут 2 цикла один в другом. Внешний - это проход по строкам, внутренний - проход по колонкам. Итак, первая итерация внешнего цикла.
Установили 0 на 1й строке, далее в цикле сканятся колонки, ищется ноль. Если нашли, то записываем кнопку. Потом поднимаем это строку снова в 1. Далее 2 итерация, 2 строку в 0, сканим
колонки....и так далее. прогнали всю клаву по строкам и колонкам один раз, пауза 10 мс, снова прогнали. И так 5 раз. Если в течении всех 5 раз обнаружено нажатие одной и той же кнопки,
то нажатие считаем засчитанным. В коде есть ещё часть, которая засчитывает удержание кнопки и ее отпускание, но это уже не так важно здесь. Как я уже говорил на f100 это работало.
Но я перешёл на f4 и немного подправив код... обнаружил что он не работает. Че я только не делал....
Вобщем в итоге что бы было понятней. Я сделал одинаковые условия. То есть код уменьшен до самой сути, частота камня выставлена такая же как на f100 то биш 24 мгц. Ну и все
остальные параметры такие же.
Теперь вот код
Кликните здесь для просмотра всего текста

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void keyboard_scan(void){
    int i;
    start_scan = 0; //Сбрасываем флаг, после вызова этой функции
    for(counter_row_scan = 0; counter_row_scan < ROWS; counter_row_scan++){ //Сканирование рядов
        row_port[counter_row_scan]->BSRRH = (1 << row_pin[counter_row_scan]); //Выставляем 0 на текущем пине рядов
        for(counter_col_scan = 0; counter_col_scan < COLS; counter_col_scan++){ //Сканирование колонок
            tmp = col_port[counter_col_scan]->IDR;
            if(!(tmp & (1 << col_pin[counter_col_scan]))){ //Если на текущем пине колонок обнаружился 0
                ke[i++] = keys[counter_row_scan][counter_col_scan];
            }
        }
        row_port[counter_row_scan]->BSRRL = (1 << row_pin[counter_row_scan]); //После скана всех колонок поднимаем лог уровень на текущем ряде в 1
    }
}


Немного пояснения: эта функция теперь не вызывается по таймеру, она постоянно крутится в майне. Это сделано для проверки. Потому что нерабочий момент кроется именно
в сканах в циклах. При нажатии допустим кнопки 1. В отладчике я должен получить вот такой массив ke = {1,1,1,1}. Но вместо этого я получаю ke = {1,4,5,*}. то есть все что в первой
колонке. В оригинальном коде, где переменная key все время перезаписывается, там соответственно получаем - *. В итоге: если я нажимаю на кнопки последнего ряда: *,0,# то все ок.
Но если я нажму 2. то получу 0. Если 3, то #. то есть последнюю кнопку из колонки. Поэтому я и уменьшил код, что бы посмотреть что набивается при каждой итерации.
Картина гласит, что почему-то когда я гоняю 0 по рядам, на столбе всегда 0. Если подумать, что порт не успевает устанавливаться, то наверное это не так. Ведь на сотке эти циклы работали, то есть они прыгали на 24 мгц. И на f4 тоже 24.
И конфигурацию OSPEEDR я пробовал менять на выходах.... вот такая у меня проблема(
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
25.08.2017, 17:08
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Сканирование матричной клавиатуры (ARM, Cortex, STM32 микроконтроллер):

Программа для матричной клавиатуры стенда SDK-1.1
Очень нужен текст программы, работающей с матричной клавиатурой стенда....

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

msp430g2353 сканирование и отправка irda кода
Здравствуйте! Подскажите, пожалуйста, реально ли сканировать коды пульта...

Опрос матричной клавиатуры
Здравствуйте. Подключил к Pinboard 1.1 (atmega16) матричную мембранную...

Метод опроса матричной клавиатуры.
Добрый день. Возник один вопрос. В моем устройстве используется матричная...

Контроллер матричной клавиатуры с выходом на i2c
Нужно подключить матричную клавиатуру (4x4) к raspberry. Использовать...

11
_SayHello
521 / 284 / 94
Регистрация: 30.07.2015
Сообщений: 1,026
25.08.2017, 21:13 #2
supercelt, покажи инициализацию портов
0
supercelt
0 / 0 / 0
Регистрация: 14.12.2010
Сообщений: 35
25.08.2017, 22:34  [ТС] #3
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define KEY_ROW1_PORT GPIOB //Порт 1 ряда
#define KEY_ROW2_PORT GPIOB //Порт 2 ряда
#define KEY_ROW3_PORT GPIOB //Порт 3 ряда
#define KEY_ROW4_PORT GPIOB //Порт 4 ряда
#define KEY_ROW_PIN1 5 //Пин 1 ряда
#define KEY_ROW_PIN2 6 //Пин 2 ряда 
#define KEY_ROW_PIN3 7 //Пин 3 ряда
#define KEY_ROW_PIN4 8 //Пин 4 ряда
#define KEY_COL1_PORT GPIOC //Порт 1 колонки
#define KEY_COL2_PORT GPIOC //Порт 2 колонки
#define KEY_COL3_PORT GPIOC //Порт 3 колонки
//#define KEY_COL4_PORT GPIOx //Порт 4 колонки
#define KEY_COL_PIN1 10 //Пин 1 колонки 
#define KEY_COL_PIN2 11 //Пин 2 колонки 
#define KEY_COL_PIN3 12 //Пин 3 колонки 
//#define KEY_COL_PIN4 X //Пин 4 колонки
C
1
2
3
4
GPIO_TypeDef * row_port[ROWS] = {KEY_ROW1_PORT, KEY_ROW2_PORT, KEY_ROW3_PORT, KEY_ROW4_PORT}; //Запихиваем названия портов рядов в массив
uint8_t row_pin[ROWS] = {KEY_ROW_PIN1, KEY_ROW_PIN2, KEY_ROW_PIN3, KEY_ROW_PIN4}; //Запихиваем номера пинов рядов в массив
GPIO_TypeDef * col_port[COLS] = {KEY_COL1_PORT, KEY_COL2_PORT, KEY_COL3_PORT}; //Запихиваем названия портов колонок в массив
uint8_t col_pin[COLS] = {KEY_COL_PIN1, KEY_COL_PIN2, KEY_COL_PIN3}; //Запихиваем номера пинов колонок в массив
C
1
2
3
4
5
6
7
8
9
    //GPIO
    for(i = 0; i < ROWS; i++){
        port_cmd(row_port[i]); //Включаем тактирование порта ряда
        gpio_setup(row_port[i], row_pin[i], 0x01, 1, 0x01, 0x01); //настройка рядов выходов. Порт, пин, MODER, OTYPER, OSPEEDR, PUPDR
    }
    for(i = 0; i < COLS; i++){
        port_cmd(col_port[i]); //Включаем тактирование порта столбца
        gpio_setup(col_port[i], col_pin[i], 0x00, 0, 0x01, 0x01); //настройка рядов выходов. Порт, пин, MODER, OTYPER, OSPEEDR, PUPDR
    }
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void port_cmd(GPIO_TypeDef * port){ //функция для включения тактирования портов клавиатуры
    if(port == GPIOA){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    }
    if(port == GPIOB){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
    }
    if(port == GPIOC){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
    }
    if(port == GPIOD){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
    }
    if(port == GPIOE){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN;
    }
    if(port == GPIOF){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN;
    }
    if(port == GPIOG){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;
    }
    if(port == GPIOH){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN;
    }
    if(port == GPIOI){
        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOIEN;
    }
}
 
void gpio_setup(GPIO_TypeDef * port, uint8_t pin, uint8_t moder, uint8_t otyper, uint8_t ospeedr, uint8_t pupdr){
    port->MODER &= ~(GPIO_MODER_MODER0 << (pin*2));
    port->MODER |= (moder << (pin*2));
    if(moder & 0x03){ //Если настроено как выход
        port->OTYPER &= ~((GPIO_OTYPER_OT_0) << pin);
        port->OTYPER |= (otyper << pin);
        port->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pin*2));
        port->OSPEEDR |= (ospeedr << (pin*2));
    }
    port->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (pin*2));
    port->PUPDR |= (pupdr << (pin*2));
}
0
_SayHello
521 / 284 / 94
Регистрация: 30.07.2015
Сообщений: 1,026
25.08.2017, 23:05 #4
Цитата Сообщение от supercelt Посмотреть сообщение
port->OTYPER |= (otyper << pin);
случаем не в push-pull надо сделать выходы?
ведь вот тут
C++
1
2
row_port[counter_row_scan]->BSRRH = (1 << row_pin[counter_row_scan]);
row_port[counter_row_scan]->BSRRL = (1 << row_pin[counter_row_scan]);
вы дергаете вверх вниз , а у вас открытый коллектор. в любом случае схемотехника мне не известна вашей схемы
0
supercelt
0 / 0 / 0
Регистрация: 14.12.2010
Сообщений: 35
25.08.2017, 23:45  [ТС] #5
Смотрите, вот именно что строки надо не в пуш пулл, а в открытый коллектор, иначе бы пришлось ставить в цепь строк диоды, дабы не спалить мк при одновременном нажатии 2 кнопок. Схемотехника проще некуда. Клава просто напрямую подключена на порты. Допустим нажали кнопку во 2 строке и 2 колонке. Так вот когда на 2 ряд мы подадим 0, то есть установим низкоомное сопротивление на этом пине, то подтянутый к питанию вход сольётся в этот открытый коллектор. И вот тогда-то на входе упадет потенциал и мы его пофиксим как 0.
Есть ещё один ньюанс! В коде, который в первом посте, если после строки, опускания ряда в 0
C
1
 row_port[counter_row_scan]->BSRRH = (1 << row_pin[counter_row_scan]); //Выставляем 0 на текущем пине рядов
добавить
C
1
delay_us(300);
и соответственно после такой же строки где возвращаем единицу, то всё начинает работать. Но задержки это не то что некомильфо, а ахтунг. О чем может говорить, если ставлю задержку и все начинает работать?
0
_SayHello
521 / 284 / 94
Регистрация: 30.07.2015
Сообщений: 1,026
25.08.2017, 23:51 #6
supercelt, все, на счет схемы в голове вообразил. Картинки больше люблю) в голову лезет только дребезг, хотя какой тут дребезг..
0
locm
2126 / 854 / 112
Регистрация: 28.10.2011
Сообщений: 2,670
Записей в блоге: 6
25.08.2017, 23:58 #7
Надеюсь подтягивающие резисторы есть в схеме?
0
supercelt
0 / 0 / 0
Регистрация: 14.12.2010
Сообщений: 35
26.08.2017, 00:03  [ТС] #8
_SayHello, не дребезг это уже при обработке. Здесь такое ощущение что проблема в инерции)

Добавлено через 1 минуту
locm, на f100 их не было. Они и не нужны, подтяжка была внутренними резисторами. И все работало раньше.
0
_SayHello
521 / 284 / 94
Регистрация: 30.07.2015
Сообщений: 1,026
26.08.2017, 00:38 #9
supercelt, попробуйте убрать подтяжку по выходу вообще, насколько я помню в stm32f100 в режиме открытого коллектора подтяжку поставить было нельзя. Возможно из за этого у вас емкость входная не успевает поспевать
0
supercelt
0 / 0 / 0
Регистрация: 14.12.2010
Сообщений: 35
26.08.2017, 00:51  [ТС] #10
_SayHello, пробовал и с подтяжкой к 1 и к 0 и без вообще.
0
Rius
Эксперт .NET
4559 / 2873 / 703
Регистрация: 25.05.2015
Сообщений: 8,859
Записей в блоге: 10
Завершенные тесты: 4
26.08.2017, 08:17 #11
supercelt, можно попроще сделать, раз таймер всё равно задействован. http://www.cyberforum.ru/arm/thread1801707-page2.html#post9510268
0
supercelt
0 / 0 / 0
Регистрация: 14.12.2010
Сообщений: 35
26.08.2017, 22:21  [ТС] #12
Rius, так это же моя тема) Я тогда пробовал так, ничего не получилось(
0
26.08.2017, 22:21
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
26.08.2017, 22:21
Привет! Вот еще темы с решениями:

Опрос матричной клавиатуры 5*3 с помощью прерывания
Здравствуйте! Начал изучение atmega с матричной клавиатуры и возник затык. На...

ATtiny2313. ASM. Динамический опрос матричной клавиатуры
Помогите написать программу на асм для микроконтроллера аттини 2313 или атмега8...

Как организовать опрос матричной клавиатуры + DS18B20?
Господа, подскажите, пожалуйста, каким образом организовать опрос матричной...

(AVR GCC) Подключение матричной клавиатуры на аналоговый вход
Подскажите как можено подключить матричную клаву на аналоговый вход таким...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru