Форум программистов, компьютерный форум, киберфорум
Микроконтроллеры ARM, Cortex, STM32
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.58/53: Рейтинг темы: голосов - 53, средняя оценка - 4.58
1 / 1 / 0
Регистрация: 14.12.2010
Сообщений: 119

STM32f100ret6b и матричная клава

01.09.2016, 23:14. Показов 12583. Ответов 49
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Народ помогите, зашёл в тупик. Написал код для матричной клавы.

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

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include "keyboard.h"
 
unsigned char decoded_key;
unsigned char find_key;
unsigned char key;
unsigned char old_key;
unsigned char valid_key;
uint8_t key_down;
uint8_t row;
const char keyss[4][4] =  {{'1','2','3','A'},
                           {'4','5','6','B'},
                           {'7','8','9','C'},
                           {'*','0','#','D'}};
                                                                                         
void TIM2_IRQHandler(void){
        if(((TIM2->SR & TIM_SR_UIF) != 0) & ((TIM2->DIER & TIM_DIER_UIE) != 0)){  
                TIM2->SR &= ~TIM_SR_UIF;
              KEYBOARD_SCAN();
        }
}                                            
                                                     
void EXTI15_10_IRQHandler(void){
        EXTI->PR |= EXTI_PR_PR11 | EXTI_PR_PR12 | EXTI_PR_PR13 | EXTI_PR_PR14; 
        NVIC_DisableIRQ(EXTI15_10_IRQn);
        GPIOB->BRR |= 0x1E0;
        TIM2->CR1 |= TIM_CR1_CEN;   
}
 
void KEYBOARD_INI(void){
        RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;             //CMD PORT B. cmd alt func
        GPIOB->CRH &= ~(GPIO_CRH_CNF11_0 | GPIO_CRH_CNF12_0 | GPIO_CRH_CNF13_0 | GPIO_CRH_CNF14_0);        //Scan Pins: PB11, PB12, PB13, PB14.
        GPIOB->CRH |= GPIO_CRH_CNF11_1 | GPIO_CRH_CNF12_1 | GPIO_CRH_CNF13_1 | GPIO_CRH_CNF14_1;         //Input Push-down.
      GPIOB->CRL &= ~(GPIO_CRL_CNF5_0 | GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0);       //Generate Pins: PB5, PB6, PB7, PB8.
        GPIOB->CRH &= ~GPIO_CRH_CNF8_0;
        GPIOB->CRL |= GPIO_CRL_MODE5_1 | GPIO_CRL_MODE6_1 | GPIO_CRL_MODE7_1;        //Output Push-pull.
    GPIOB->CRH |= GPIO_CRH_MODE8_1; 
        GPIOB->BSRR |= 0x1E0;
    
        //--------Setup TIMER 2---------------------------
        RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;                    //Enable TIMER 2
        TIM2->PSC = 0x5DBF;                     //Prescaler 24000 - 1/ Clock timer 1 kHz
        TIM2->CR1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD);                       //Count up, clock division, mode selection
        TIM2->ARR = 0x14;                      //Count up to 20/        
        NVIC_EnableIRQ(TIM2_IRQn);               //Enable interrupt for timer2
        TIM2->DIER |= TIM_DIER_UIE;                      //Interrupt update counter/ perepolnenie
        //TIM2->CR1 |= TIM_CR1_CEN;                        //Timer counter2 Enable!
        //------------------------------------------------
        
        //-------Setup Timer 3------------------------------
        //RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
        //TIM3->PSC = 0x5DBF;
        //TIM3->CR1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD);
        //TIM3->ARR = 0x28;
        //TIM3->CR1 |= TIM_CR1_CEN;
        //--------------------------------------------------
    
        //-------Setup EXTI-------------------------------
        AFIO->EXTICR[2] = AFIO_EXTICR3_EXTI11_PB;
        AFIO->EXTICR[3] = AFIO_EXTICR4_EXTI12_PB | AFIO_EXTICR4_EXTI13_PB | AFIO_EXTICR4_EXTI14_PB;
        EXTI->RTSR |= EXTI_RTSR_TR11 | EXTI_RTSR_TR12 | EXTI_RTSR_TR13 | EXTI_RTSR_TR14;
        EXTI->IMR |= EXTI_IMR_MR11 | EXTI_IMR_MR12 | EXTI_IMR_MR13 | EXTI_IMR_MR14;
        NVIC_EnableIRQ(EXTI15_10_IRQn);
        //------------------------------------------------
}
 
void KEYBOARD_SCAN(void){
        unsigned char i, j;
        if(key_down){
                if(!GPIOB->IDR & (1 << (row + 11))){
                        TIM2->CR1 &= ~TIM_CR1_CEN;
                        key_down = 0;   
                        NVIC_EnableIRQ(EXTI15_10_IRQn);
                            GPIOB->BSRR |= 0x1E0;
                        return;
                }
        }
                for(i = 0; i < 4; i++){
                        GPIOB->BRR |= 0x1E0;
                        GPIOB->BSRR |= (1 << (i + 5));
                        for(j = 0; j < 4; j++){
                                if(GPIOB->IDR & 1 << (j + 11)){
                                        key = keyss[j][i];
                                } 
                        }
                }
                                                if(old_key == 0){
                                        old_key = key;
                                } else {
                                        if(key == old_key){
                                                valid_key++;
                                        } else {
                                                valid_key = 0;
                                        }
                                        if(valid_key == 5){
                                                TIM2->CR1 &= ~TIM_CR1_CEN;  
                                                find_key = key;
                                                valid_key = 0;
                                                old_key = 0;
                                                key = 0;
                                                NVIC_EnableIRQ(EXTI15_10_IRQn);
                                                GPIOB->BSRR |= 0x1E0;
                                        }   
                                }
}


А тут вкратце я описал алгоритм

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

Алгоритм для матричной клавы

Настроить входы и выходы

Подать на все 4 пина входа клавы лог 1. Что бы при нажатии ЛЮБОЙ кнопки зафиксировать нажатие

Настроить таймер, и прерывание, что бы входил в прер каждые 20 милисек. При настройке не запускать таймер!

Настроить внешнее прерывание (что бы сработало при лог 1 на любом из сканирующих пинов клавы, 4 шт.)

!!! Прогон кнопки - это каждые 20 мс проверять нажата ли одна и таже кнопка. Если она нажата в течении опред кол-ва прогонов, то считаем, что нажатие кнопки состоялось!!!!!!

Ход событий:

Нажали кнопу, сработало внешнее прерывание, ввалились в вектор
сбрасываем флаги прерываний на всякий случай со всех 4 линий. Потому что нам важно что прер сработало при нажатии любой клавиши.
вырубаем внешнее прерывание, что бы повторно не сработало от дребезга, убираем все лог 1 с четырёх пинов клавы. Врубаем таймер, чтоб начал тикать. При прохождении 20 мс от начала прерывания (через 20 мс дребезг как раз закончится), таймера, - вваливаемся в вектор при переполнении.
Проверяем что прер прилетело именно от этого таймера, сбрасываем флаг прерывания, вызываем ф-ию скана клавы. Ф-ия выполнит скан и выход из прер таймера. Далее таймер опять тикает до 20 мс и всё повторяется. (Когда клавиша опознана таймер вырубается).

ФункциЯ скана:

два цикла. Один в другом. Внешний цикл скан по столбам, внутр цикл скан по строкам

Во внешнем цикле просто гоняем лог 1 по 4 портам (сдвигаем). Подали лог 1 на 1 пин, и внутр цикл сканит все 4 строки, далее внеш цикл перебрасывает лог 1 на след пин, и опять внутр цикл сканит все 4 строки. И так пока не пройдёт все 4 столба.

Итак. Во внеш цикле подаём(сдвигаем) лог 1 на 1 столб. Внутр цикл перебирает все 4 пина строк - проверяем есть ли на пинах лог 1. Если обнаруживем лог 1. записываем в переменную key символ. Прошли все циклы, в переменной key записана кнопка.
Далее проверяем. Если предыдушего нажатия кнопки не было, то есть это первый прогон кнопки (используем переменную old_key). То old_key = код нажатой кнопки (key)
Иначе(значит это 2 и более прогон кнопки)
Сравниваем код текущего прогона с кодом в прошлом прогоне. Если совпадает, то увелич счётчик прогонов (переменная valid_key).
иначе
обнуляем счётчик прогонов.
Далее после сравнивания прошлого прогона и текущего
проверяем Если кол-во прогонов == 5 (то есть мы дошли до 5 прогонов при этом все коды прогонов совпадали), то присваиваем переменной find_key кнопку. Эта переменная видна в файле main, где считывается на пустоту. Если в ней аоявляется значение отличное от 0, то там выводим на дисплей.
обнуляем valid_key, old_key, key, останавливаем таймер и врубаем снова разрешение на внешние прерывания.


Итог: клава отлично работает. НО меня не устраивает, что когда держишь кнопку, на эран подряд быстро сыпятся цифры. С другой стороны это может и хорошо. Но мне надо что бы было одно нажатие - один символ. То есть нажал кнопку и если ее держишь - на экране только 1 символ и второй не появится, пока не отпустишь кнопку и не нажмёшь снова.

Я пробовал сделать так:

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

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include "keyboard.h"
 
unsigned char decoded_key;
unsigned char find_key;
unsigned char key;
unsigned char old_key;
unsigned char valid_key;
uint8_t key_down;
uint8_t row;
const char keyss[4][4] =  {{'1','2','3','A'},
                           {'4','5','6','B'},
                           {'7','8','9','C'},
                           {'*','0','#','D'}};
                                                                                         
void TIM2_IRQHandler(void){
        if(((TIM2->SR & TIM_SR_UIF) != 0) & ((TIM2->DIER & TIM_DIER_UIE) != 0)){  
                TIM2->SR &= ~TIM_SR_UIF;
              KEYBOARD_SCAN();
        }
}                                            
                                                     
void EXTI15_10_IRQHandler(void){
        EXTI->PR |= EXTI_PR_PR11 | EXTI_PR_PR12 | EXTI_PR_PR13 | EXTI_PR_PR14; 
        NVIC_DisableIRQ(EXTI15_10_IRQn);
        GPIOB->BRR |= 0x1E0;
        TIM2->CR1 |= TIM_CR1_CEN;   
}
 
void KEYBOARD_INI(void){
        RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;             //CMD PORT B. cmd alt func
        GPIOB->CRH &= ~(GPIO_CRH_CNF11_0 | GPIO_CRH_CNF12_0 | GPIO_CRH_CNF13_0 | GPIO_CRH_CNF14_0);        //Scan Pins: PB11, PB12, PB13, PB14.
        GPIOB->CRH |= GPIO_CRH_CNF11_1 | GPIO_CRH_CNF12_1 | GPIO_CRH_CNF13_1 | GPIO_CRH_CNF14_1;         //Input Push-down.
      GPIOB->CRL &= ~(GPIO_CRL_CNF5_0 | GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0);       //Generate Pins: PB5, PB6, PB7, PB8.
        GPIOB->CRH &= ~GPIO_CRH_CNF8_0;
        GPIOB->CRL |= GPIO_CRL_MODE5_1 | GPIO_CRL_MODE6_1 | GPIO_CRL_MODE7_1;        //Output Push-pull.
    GPIOB->CRH |= GPIO_CRH_MODE8_1; 
        GPIOB->BSRR |= 0x1E0;
    
        //--------Setup TIMER 2---------------------------
        RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;                    //Enable TIMER 2
        TIM2->PSC = 0x5DBF;                     //Prescaler 24000 - 1/ Clock timer 1 kHz
        TIM2->CR1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD);                       //Count up, clock division, mode selection
        TIM2->ARR = 0x14;                      //Count up to 20/        
        NVIC_EnableIRQ(TIM2_IRQn);               //Enable interrupt for timer2
        TIM2->DIER |= TIM_DIER_UIE;                      //Interrupt update counter/ perepolnenie
        //TIM2->CR1 |= TIM_CR1_CEN;                        //Timer counter2 Enable!
        //------------------------------------------------
        
        //-------Setup Timer 3------------------------------
        //RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
        //TIM3->PSC = 0x5DBF;
        //TIM3->CR1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS | TIM_CR1_CKD);
        //TIM3->ARR = 0x28;
        //TIM3->CR1 |= TIM_CR1_CEN;
        //--------------------------------------------------
    
        //-------Setup EXTI-------------------------------
        AFIO->EXTICR[2] = AFIO_EXTICR3_EXTI11_PB;
        AFIO->EXTICR[3] = AFIO_EXTICR4_EXTI12_PB | AFIO_EXTICR4_EXTI13_PB | AFIO_EXTICR4_EXTI14_PB;
        EXTI->RTSR |= EXTI_RTSR_TR11 | EXTI_RTSR_TR12 | EXTI_RTSR_TR13 | EXTI_RTSR_TR14;
        EXTI->IMR |= EXTI_IMR_MR11 | EXTI_IMR_MR12 | EXTI_IMR_MR13 | EXTI_IMR_MR14;
        NVIC_EnableIRQ(EXTI15_10_IRQn);
        //------------------------------------------------
}
 
void KEYBOARD_SCAN(void){
        unsigned char i, j;
        if(key_down){
                if(!GPIOB->IDR & (1 << (row + 11))){
                        TIM2->CR1 &= ~TIM_CR1_CEN;
                        key_down = 0;   
                        NVIC_EnableIRQ(EXTI15_10_IRQn);
                      GPIOB->BSRR |= 0x1E0;
                        return;
                }
        }
                for(i = 0; i < 4; i++){
                        GPIOB->BRR |= 0x1E0;
                        GPIOB->BSRR |= (1 << (i + 5));
                        for(j = 0; j < 4; j++){
                                if(GPIOB->IDR & 1 << (j + 11)){
                                        key = keyss[j][i];
                                        if(!row){row = j;}
                                } 
                        }
                }
                        if(old_key == 0){
                                        old_key = key;
                                } else {
                                        if(key == old_key && !key_down){
                                                valid_key++;
                                        } else {
                                                valid_key = 0;
                                        }
                                        if(valid_key == 5 && !key_down){
                                                //TIM2->CR1 &= ~TIM_CR1_CEN;    
                                                find_key = key;
                                                valid_key = 0;
                                                old_key = 0;
                                                key = 0;
                                                key_down = 1;
                                                //NVIC_EnableIRQ(EXTI15_10_IRQn);
                                                //GPIOB->BSRR |= 0x1E0;
                                        }   
                                }
}


Ввёл переменную key_down. Когда ыиксирую факт нажатия присваиваю ей 1. И не останавливаю таймер. При следующих проходах сканится тот вход на котором была 1. И если она всё ещё там - return. А если там появился 0, то обнуляю нужные переменные, останавливаю таймер и вкрубаю внешнее прерывание.
Но у меня ничего не получается. Печатается 1 символ и всё. Даже нарисовал схему



Я не знаю что делать в промежутке от фикса кнопки до момента, когда её отпускают. Как отловить что кнопка ещё удерживается?.
Я знаю что может мой код не совсем корректен, так как обычно делают постоянный скан в циклах без перерыва. И по портам гоняют 0 а не 1. Но мне надо вот именно так, помогите с решением!!! плз
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
01.09.2016, 23:14
Ответы с готовыми решениями:

STM32 + матричная клава
Здравствуйте! Представьте, что вам дали задание написать опрос матричной клавы для коммерческого проекта. Естественно я пишу просто для...

Работа с выводом данных на LCD1602 с мк STM32F100RET6B
Вопрос наверное уже избитый, но надеюсь, что тут мне подскажут, помогут. ). Программлю в кеиле 5 Это мои функции #include...

Матричная арифметика
Надо перевести на С с паскаля uses crt; const nmax=10; var n:byte; type matr=array of integer; procedure Vvod(var mt:matr); ...

49
Эксперт .NET
 Аватар для Rius
13837 / 7879 / 1706
Регистрация: 25.05.2015
Сообщений: 23,922
Записей в блоге: 14
03.09.2016, 15:09
Студворк — интернет-сервис помощи студентам
Потому что при опросе вы видите только последствия и вынуждены судить о произошедшем по ним. А с прерываниями находитесь в гуще событий.

Добавлено через 1 минуту
Клавиатура, концевики, uart таймауты... Всё одно...
1
Модератор
Эксперт по электронике
8982 / 6749 / 921
Регистрация: 14.02.2011
Сообщений: 23,876
03.09.2016, 15:16
Цитата Сообщение от Rius Посмотреть сообщение
А с прерываниями находитесь в гуще событий.
а всегда ли это нужно?
допустим у меня идет обработка критических данных, то же Фурье, и тут такая клава, обработай меня
0
Эксперт .NET
 Аватар для Rius
13837 / 7879 / 1706
Регистрация: 25.05.2015
Сообщений: 23,922
Записей в блоге: 14
03.09.2016, 15:22
ValeryS, не всегда. Но никогда не говори "никогда".
Цитата Сообщение от Витальич Посмотреть сообщение
НИКОГДА НЕ САЖАЙТЕ КНОПКИ НА ВНЕШНИЕ ПРЕРЫВАНИЯ!!
1
 Аватар для Voland_
1983 / 1276 / 131
Регистрация: 04.01.2010
Сообщений: 4,607
03.09.2016, 17:14
Цитата Сообщение от Rius Посмотреть сообщение
Клавиатура, концевики, uart таймауты... Всё одно...
ну, я надеюсь, вы понимаете разницу между "концевиком" и матричной клавиатурой . Иначе все вышесказанное попросту бессмысленно. Я вам пытался объяснить, почему в случае с "матрицей" бессмысленно и даже вредно использовать прерывания по спадам и фронтам, но, похоже, вам все равно .

ЗЫ: к слову, системы на прерываниях именно в таких случаях могут дико сбоить, потому что при плохом контакте вы получаете ОЧЕНЬ МНОГО прерываний. В итоге система будет просто висеть в состоянии, например, отжатой кнопки. Или дико тормозить. В случае же поллинга этой ситуации никогда не случится... Так что тут кому что. Я с вами соглашусь по поводу "никогда не говори никогда", но не более того.
0
1 / 1 / 0
Регистрация: 14.12.2010
Сообщений: 119
03.09.2016, 17:29  [ТС]
ValeryS,

C
1
GPIOC->BRR |= 0x100;
и
C
1
 GPIOC->BSRR |= 0x100;
На этом пине сидит светодиод. Я его включаю когда прерывание по нараст. фронту и выключаю, когда прерывание по спаду. Просто сделал для проверки. А чем вас смутило, что BRR только для записи. Я и записываю туда конкретно в 8 бит 1, и светодиод загорается всё работает.

Вижу тенденцию отказаться от прерываний, неужели с ними так запарно? Я то как раз хотел чделать так, что бы когда клаву не используешь - таймер не тикал. Или я ошибаюсь, что постоянное тиканье таймера будет отбирать много ресурсов?
0
Эксперт .NET
 Аватар для Rius
13837 / 7879 / 1706
Регистрация: 25.05.2015
Сообщений: 23,922
Записей в блоге: 14
03.09.2016, 17:57
Voland_
В конечном итоге концевик это всё равно кнопка.
В случае с матрицей, прерывания на время сканирования можно отключить. Это я вам уже вторую страницу пытаюсь объяснить.

ЗЫ. Очень много прерываний тоже можно подавить. Бывает, что у прерываний есть отдельно флаги наличия/регистрации события, и отдельно флаги разрешения запуска обработчика по этому событию.
  • Имеем включенные прерывания по входу;
  • Нажимается ржавая кнопка, любая - в матрице или одиночная;
  • По первому прерыванию запускается таймер и отключается обработка прерывания по входу;
  • По прерыванию таймера проверяем наличие флага прерывания по входу(само прерывание запрещено);
    • Если флаг установлен, запускаем повторно таймер;
    • Если флаг не установлен, то сканируем матрицу и сохраняем результат;
  • Сбрасываем флаги прошлых прерываний;
  • Разрешаем обработку прерываний по входу.
Результата ждать чуть дольше. Но периодический опрос всё равно не потребуется.
1
Модератор
Эксперт по электронике
8982 / 6749 / 921
Регистрация: 14.02.2011
Сообщений: 23,876
03.09.2016, 18:03
Цитата Сообщение от supercelt Посмотреть сообщение
C
1
GPIOC->BRR |= 0x100;
и
C
1
 GPIOC->BSRR |= 0x100;
На этом пине сидит светодиод.
на каком пине?
ты знаешь для чего регистры BRR и BSRR ??
Цитата Сообщение от supercelt Посмотреть сообщение
Я и записываю туда конкретно в 8 бит 1,
нет ты не записываешь а делаешь чтение-модификация-запись
C
1
 GPIOC->BRR |= 0x100;
превратится в
C
1
2
3
tmp=GPIOC->BRR;
tmp=tmp|0x100;
GPIOC->BRR=tmp;
а поскольку регистр только для записи, то считается неизвестно что
и в результате можно бросить не только 8 бит, а еще несколько
в регистр BRR просто записывается число, и если бит числа установлен в 1 то соответствующий бит порта сбрасывается
вот так
C
1
 GPIOC->BRR = 0x100;
BSRR регистр для установки и сброса бит
младшие 16 бит для установки, старшие для сброса
одним движением руки можно установить и сбросить соответствующие биты
вот например установка 0 бита и сброс 1
C
1
 GPIOC->BSRR = 0x00020001;
0
1 / 1 / 0
Регистрация: 14.12.2010
Сообщений: 119
03.09.2016, 19:10  [ТС]
ValeryS, BRR и BSRR знаю для чего, вот про маску... просто много где в примерах видел, как в BRR делают с маской. А число 0x00020001; обязательно записывать впереди с тремя нулями. Я проверял, если нули впереди не ставишь, ничего не меняется.

Rius Вот очень похоже не мой алгоритм, только он не работает. В 4 пункте вы написали про проверку флага прерывания. А это не этот EXTI->PR? Его же сбросить сразу надо после того как зашли в обработчик. И последний пункт... Допустим во время того, как разрешаем прерывание, отжали кнопку, а там дребезг, только уже не на нажатие, а на отжатие. И будет каша
0
Эксперт .NET
 Аватар для Rius
13837 / 7879 / 1706
Регистрация: 25.05.2015
Сообщений: 23,922
Записей в блоге: 14
03.09.2016, 19:32
supercelt, возможно и PR.
Флаги обычно сбрасывают при входе в обработчике прерывания, чтобы обработчик не вызвался повторно. И в самом начале обработчика, чтобы дать возможность установиться новому флагу, если событие произойдёт за время выполнения обработчика.

Добавлено через 4 минуты
Код в МК выполняется очень быстро по сравнению с нажатием кнопок человеком.
Прерывания я предлагаю использовать лишь для определения момента времени, когда состояние клавиатуры будет стабильным.
Если при разрешении обработчика, на последнем шаге, возникнет дребезг, будет всё так же. Узнаете, что состояние изменилось. Можете определить, какая кнопка отжата. Если это не нужно, можете проигнорировать.
0
1 / 1 / 0
Регистрация: 14.12.2010
Сообщений: 119
03.09.2016, 19:37  [ТС]
Rius Если это PR, то в прерывании таймера при проверке он будет всегда сброшен, так как он сбрасывается в обработчике прерывания, ДО того как затикает таймер
0
Модератор
Эксперт по электронике
8982 / 6749 / 921
Регистрация: 14.02.2011
Сообщений: 23,876
03.09.2016, 19:38
Цитата Сообщение от supercelt Посмотреть сообщение
просто много где в примерах видел, как в BRR делают с маской.
с какой маской? это та так операцию ИЛИ (|) называешь?
Цитата Сообщение от supercelt Посмотреть сообщение
А число 0x00020001; обязательно записывать впереди с тремя нулями. Я проверял, если нули впереди не ставишь, ничего не меняется.
разумеется не меняется, это одно и тоже число
нули в начале ставят чтобы показать размерность
0x00 байт
0x0000 слово 16 бит
0x00000000 двойное слово 32бит
и все это 0
нужно это человеку, который сопровождает код, машине по барабану
0
Эксперт .NET
 Аватар для Rius
13837 / 7879 / 1706
Регистрация: 25.05.2015
Сообщений: 23,922
Записей в блоге: 14
03.09.2016, 19:39
supercelt, если это про вариант из 26 поста, то сброса в обработчике по EXTI не будет, так как сам обработчик не будет разрешён в NVIC. И к срабатыванию таймера флаг сохранится.
0
Модератор
Эксперт по электронике
8982 / 6749 / 921
Регистрация: 14.02.2011
Сообщений: 23,876
03.09.2016, 19:41
неправильно это
Цитата Сообщение от ValeryS Посмотреть сообщение
C
1
GPIOC->BSRR |= 0x00020001;
болезнь копипаста
вот так надо
C
1
GPIOC->BSRR = 0x00020001;
прошу пардону
0
1 / 1 / 0
Регистрация: 14.12.2010
Сообщений: 119
03.09.2016, 20:11  [ТС]
не ИЛИ, я говорю, что видел, как маской называют вот это |=

Rius А как без сброса в обработчике? Если его не сбросить, то можно сколько угодно влетать в прерывание по фронтам. У нас же как я понимаю внешнее прерывание что бы отследить нажатие. Вот как я мыслю. Нажали кнопку, прыгнули в обработчик прерывания, флаг не сбрасываем. А в это время идёт дребезг милисекунд так на 20. И пока этот дребезг будет идти мы всё время повторно будем ширкаться в прерывании.
0
Эксперт .NET
 Аватар для Rius
13837 / 7879 / 1706
Регистрация: 25.05.2015
Сообщений: 23,922
Записей в блоге: 14
03.09.2016, 20:14
supercelt, вы мыслите не по 26 посту, а по 19-му. Это разные варианты.
0
Модератор
Эксперт по электронике
8982 / 6749 / 921
Регистрация: 14.02.2011
Сообщений: 23,876
03.09.2016, 20:30
Цитата Сообщение от supercelt Посмотреть сообщение
не ИЛИ
а что это тогда?
Цитата Сообщение от supercelt Посмотреть сообщение
|=
???
0
1 / 1 / 0
Регистрация: 14.12.2010
Сообщений: 119
03.09.2016, 22:04  [ТС]
|= это всё равно что x = x | y

Добавлено через 11 минут
Ruis. Сдаётся мне схема из 26 поста будет долбить символы при нажатой кнопке
0
Модератор
Эксперт по электронике
8982 / 6749 / 921
Регистрация: 14.02.2011
Сообщений: 23,876
03.09.2016, 22:13
Цитата Сообщение от supercelt Посмотреть сообщение
что x = x | y
Ну и что же это такое???
Все, Понтий Пилат умывает руки.
0
 Аватар для Voland_
1983 / 1276 / 131
Регистрация: 04.01.2010
Сообщений: 4,607
03.09.2016, 22:47
Цитата Сообщение от Rius Посмотреть сообщение
Результата ждать чуть дольше. Но периодический опрос всё равно не потребуется.
изящная в общем-то идея . Пока что подводных камней не видно - в принципе ваш алгоритм работоспособный ).

ЗЫ: на поллинге клавиатур и вообще, кнопках я делал полноценные клавиатуры, с повторами, отличием событий нажатия и отжатия, фильтрацией дребезга. Были проекты с отслеживанием длинных и коротких нажатий. И как раз ваше решение не очень-то адаптировано под отслеживание длительности нажатия на кнопку и реакции на него. Поллингом это было сделать проще, чем на таймерах.
0
Эксперт .NET
 Аватар для Rius
13837 / 7879 / 1706
Регистрация: 25.05.2015
Сообщений: 23,922
Записей в блоге: 14
04.09.2016, 10:18
supercelt, Voland_,
Оба моих варианта призваны только определить стабильное состояние клавиатуры и передать его куда-то дальше.
А там уже можете и долбить им, и однократно обрабатывать, и время измерять.
Для определения длительности нажатия, например, надо:
  • узнать время процессора в момент сканирования матрицы (есть там какой-то регистр наверняка);
  • сохранить состояние матрицы и время куда-то для обработки;
  • там запомнить состояния и метки времени (словарь вида <клавиша, направление, время>);
  • по приходу отпускания клавиши найти время её нажатия;
  • найти разницу между метками времени.

Добавлено через 3 минуты
Последние три пункта лучше вынести из прерываний в главный цикл программы, либо в задачу RTOS.

Добавлено через 1 час 35 минут
Допилил демонстрационный код под пост 26.
Красный светодиод, переключение - запуск/перезапуск таймера.
Зелёный светодиод, состояние - зафиксированное состояние кнопки.
Интервал у таймера большой для наглядности. Дребезг эмулируется частыми щелчками кнопки.

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


Плата STM32F429I-DISCO.

Добавлено через 9 часов 26 минут
Основная часть кода, связанная с примером:
Кликните здесь для просмотра всего текста
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
 * keyboard.cpp
 *
 *  Created on: 3 сент. 2016 г.
 *      Author: Vladimir
 */
 
#include <Application/Drivers/keyboard.h>
#include <Application/application.h>
#include "stm32f4xx_hal.h"
 
extern TIM_HandleTypeDef htim6;
 
namespace Drivers
{
 
Keyboard::Keyboard()
{
}
 
Keyboard::~Keyboard()
{
}
 
void Keyboard::init()
{
  GPIO_InitTypeDef GPIO_InitStruct;
 
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
 
void Keyboard::handleEXTI(uint16_t pin)
{
  HAL_NVIC_DisableIRQ(EXTI0_IRQn);
 
  this->restartTimer(&htim6);
}
 
void Keyboard::handleTimer(TIM_HandleTypeDef *htim)
{
  if (__HAL_GPIO_EXTI_GET_FLAG(B1_Pin) == B1_Pin) {
    __HAL_GPIO_EXTI_CLEAR_FLAG(B1_Pin);
    this->restartTimer(htim);
  } else {
    HAL_TIM_Base_Stop_IT(&htim6);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
 
    // Здесь надо считать состояние клавиатуры и отправить сообщение на обработку вне прерывания.
    bool state = HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == GPIO_PIN_SET;
    app.leds()->setState(Drivers::Leds::Outputs::Green, state);
  }
}
 
void Keyboard::restartTimer(TIM_HandleTypeDef *htim)
{
  HAL_TIM_Base_Stop_IT(htim);
 
  app.leds()->setState(Drivers::Leds::Outputs::Red, !app.leds()->state(Drivers::Leds::Outputs::Red));
 
  //htim6.Instance = TIM6;
  htim->Init.Prescaler = 10000;
  htim->Init.CounterMode = TIM_COUNTERMODE_DOWN;
  htim->Init.Period = 10000;
  htim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  HAL_TIM_Base_Init(htim);
 
  __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
  HAL_TIM_Base_Start_IT(htim);
}
 
} /* namespace Drivers */


Стоит отметить, что у STM есть несколько линий EXTI, объединённых в одно прерывание, 9-5 и 15-10. Поэтому строки матрицы стоит завести именно на эти пины.

Также, флаги PR, выступающие флагами наличия дребезга, можно использовать и в периодическом методе опроса клавиатуры. Для определения установившегося состояния будет достаточно удостовериться, что с прошлого опроса ни один флаг не установился.
2
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
04.09.2016, 10:18

Матричная система
Определитель равен 0. Корни квадратного уравнения не извлекаются. Помогите с решением, голова вообще не работает уже. 10 штук решил таких...

матричная прогонка
нужно написать консольную программу на visual c ++ 6.00 ну или на более ранних заранее благодарю

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

Матричная игра
Возник вопрос, такой, я написал программу &quot;матричная игра&quot;. Для примера входные данные: 3 3 4 -1 -3 -2 1 3 0 2 -3 Выходные...

Матричная арифметика
Пусть дано двумерный массив А размером n * n. Вычислить матрицу C согласно варианту (№8. Обчислити C = (B A – 5A B)2), использовав функции...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru