Урок 61
Модуль LCD 16×2. Работаем с кнопками
Часть 3
В прошлой части занятия мы написали практически все служебные функции для отслеживания изменения состояний кнопок.
Теперь будем писать код для того, чтобы данными функциями воспользоваться.
Но прежде нам нужно закончить ещё одну функцию.
Вернёмся в button.c и напишем тело нашего обработчика
void TIM_Callback(void)
{
if(tim_stat==0) return;
HAL_ADC_Stop_IT(&hadc1);
Read_Button_State(Button_Right);
Read_Button_State(Button_Up);
Read_Button_State(Button_Down);
Read_Button_State(Button_Left);
Read_Button_State(Button_Select);
HAL_ADC_Start_IT(&hadc1);
}
АЦП на время работы функции лучше вообще останавливать, во-первых потому что он все равно нам не нужен, у нас уже всё измерено, а во-вторых, если его не останавливать, то у меня функция могла обработать только вызовы двух кнопок, если добавлял третий, всё повисало.
Напишем две функции для сброса и установки статуса кнопки
//———————————————————
void ResetButtonState(uint8_t b, uint8_t st)
{
button_state[b] &= ~st;
}
//———————————————————
void SetButtonState(uint8_t b, uint8_t st)
{
button_state[b] |= st;
}
//———————————————————
void Buttons_Ini(void)
Добавим прототипы данных функций
void Buttons_Ini(void);
void ResetButtonState(uint8_t b, uint8_t st);
void SetButtonState(uint8_t b, uint8_t st);
Подключим в файле main.c глобальную переменную
/* Private variables ———————————————————*/
extern uint8_t button_state[5];
uint16_t ADC_Data=0;
Теперь в бесконечном цикле удалим часть старого кода и попытаемся определить статус одной кнопки и среагируем на него, также не забываем про флаги. Можно убавить задержку
while (1)
{
adc_value=ADC_Data;//занесём результат преобразований в переменную
LCD_SetPos(0,0);//спозиционируем курсор
sprintf(str1,»%d «,adc_value);//преобразуем результат в строку
LCD_String(str1);
LCD_SetPos(0, 1);
if (button_state[Button_Right]&ST_UNPRESSURE)
{
//предотвратим обращение к кнопке из таймера во время выполнения обработчика
SetButtonState(Button_Right,ST_LOCKED);
LCD_String(«RIGHT KEY «);
ResetButtonState(Button_Right,ST_UNPRESSURE);
SetButtonState(Button_Right,ST_PRESSURE);
ResetButtonState(Button_Right,ST_LOCKED);
}
HAL_Delay(50);//задержка перед следующим циклом
/* USER CODE END WHILE */
Проверим код, собрав его и прошив контроллер. Если всё работает и на отжатие правой кнопки мы получаем сообщение на дисплее, то пишем дальше.
Определим статус ещё одной кнопки
ResetButtonState(Button_Right,ST_LOCKED);
}
if (button_state[Button_Up]&ST_UNPRESSURE)
{
//предотвратим обращение к кнопке из таймера во время выполнения обработчика
SetButtonState(Button_Up,ST_LOCKED);
LCD_String(«UP KEY «);
ResetButtonState(Button_Up,ST_UNPRESSURE);
SetButtonState(Button_Up,ST_PRESSURE);
ResetButtonState(Button_Up,ST_LOCKED);
}
Опять соберём код, прошьём контроллер и проверим реакцию уже на две кнопки.
Если всё нормально, то можно. в принципе дописать код для обработки отжатий остальных трёх кнопок
ResetButtonState(Button_Up,ST_LOCKED);
}
if (button_state[Button_Down]&ST_UNPRESSURE)
{
//предотвратим обращение к кнопке из таймера во время выполнения обработчика
SetButtonState(Button_Down,ST_LOCKED);
LCD_String(«DOWN KEY «);
ResetButtonState(Button_Down,ST_UNPRESSURE);
SetButtonState(Button_Down,ST_PRESSURE);
ResetButtonState(Button_Down,ST_LOCKED);
}
if (button_state[Button_Left]&ST_UNPRESSURE)
{
//предотвратим обращение к кнопке из таймера во время выполнения обработчика
SetButtonState(Button_Left,ST_LOCKED);
LCD_String(«LEFT KEY «);
ResetButtonState(Button_Left,ST_UNPRESSURE);
SetButtonState(Button_Left,ST_PRESSURE);
ResetButtonState(Button_Left,ST_LOCKED);
}
if (button_state[Button_Select]&ST_UNPRESSURE)
{
//предотвратим обращение к кнопке из таймера во время выполнения обработчика
SetButtonState(Button_Select,ST_LOCKED);
LCD_String(«SELECT KEY»);
ResetButtonState(Button_Select,ST_UNPRESSURE);
SetButtonState(Button_Select,ST_PRESSURE);
ResetButtonState(Button_Select,ST_LOCKED);
}
Соберём код, прошьём контроллер и проверим реакцию на все кнопки. Реакция должна быть именно на отжатие
Чтобы код в бесконечном цикле не выглядел так громоздко, организуем его несколько по-другому.
Создадим счётчик и массив строк в main();
uint16_t adc_value=0;
uint8_t i=0;
char str2[5][11] =
{
«RIGHT KEY «,
«UP KEY «,
«DOWN KEY «,
«LEFT KEY «,
«SELECT KEY»
};
/* USER CODE END 1 */
Исправим код в бесконечном цикле
while (1)
{
adc_value=ADC_Data;//занесём результат преобразований в переменную
LCD_SetPos(0,0);//спозиционируем курсор
sprintf(str1,»%d «,adc_value);//преобразуем результат в строку
LCD_String(str1);
LCD_SetPos(0, 1);
for (i=0;i<5;i++)
{
if (button_state[i]&ST_UNPRESSURE)
{
//предотвратим обращение к кнопке из таймера во время выполнения обработчика
SetButtonState(i,ST_LOCKED);
LCD_String(str2[i]);
ResetButtonState(i,ST_UNPRESSURE);
SetButtonState(i,ST_PRESSURE);
ResetButtonState(i,ST_LOCKED);
}
}
HAL_Delay(50);//задержка перед следующим циклом
/* USER CODE END WHILE */
Ещё раз соберём код, прошьём контроллер и проверим работу.
Если всё нормально, то считаю цель занятия достигнутой.
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату можно приобрести здесь STM32F4-DISCOVERY
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Обучаюсь у вас программированию STМ с использованием Cube. Все достаточно толково (по крайней мере для тех кто в курсе). Но по данному уроку есть нюансы…
1. Интересный подход для работы с клавиатурой, НО
— избыточное количество флагов. Можно было состояние нажато и не нажато отслеживать одним флагом и т.д.
— такой подход не дает вешать на одну кнопку несколько ф-ций (например по длительному нажатию с наблюдением за состоянием…).
2. В объяснении часто присутствуют ошибки (во многих примерах), которые получается вылавливать только при рассмотрении исходного кода из примера (если это, конечно не является элементом обучения).
А так большое спасибо… И удачи вам…
Спасибо за комментарий.
Ошибки, конечно, бывают. Иначе уроки выходили бы раз в месяц. Тут уж ничего не поделаешь.