Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Похожие статьи:


Реклама ⓘ

Библиотека матричной клавиатуры для STM32F4 Discovery

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

Начнем с подключения всех необходимых библиотек, объявления переменных, а так же присвоения структурам GPIO_InitTypeDef и TIM_TimeBaseInitTypeDef более простых имен GPIO и TIM соответственно. 

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_tim.h"
#include "Keyboard.h"

//******************************Объявляем переменные******************************************

uint16_t Sdvig = 0, Vkl = 0, Otkl = 0, y = 0, Temp = 0; Temp2 = 0, stolb = 0, Symbol = 0;
GPIO_InitTypeDef GPIO;											//Структура, содержащая настройки порта
TIM_TimeBaseInitTypeDef TIM;									//Структура, содержащая настройки таймеров

//**********************************************************************************************

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

//******************************Создаем используемые функции************************************

void init_keyboard_port(void)									//Конфигурация порта, к которому подключена клавиатура
	{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);		//Включаем тактирование порта А

	GPIO.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;//Выбираем выводы, которые будут читать состояние клавиатуры
	GPIO.GPIO_Mode = GPIO_Mode_IN;								//Выводы работают на вход
	GPIO.GPIO_Speed = GPIO_Speed_2MHz;							//Скорость работы выводов
	GPIO.GPIO_OType = GPIO_OType_PP;							//Тип значений порта - двухуровневый
	GPIO.GPIO_PuPd = GPIO_PuPd_DOWN;							//Начальное значение выводов - 0
	GPIO_Init(GPIOA, &GPIO);									//Инициализация настроек

	GPIO.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;			//Выбираем выводы, которые будут задавать высокий потенциал
	GPIO.GPIO_Mode = GPIO_Mode_OUT;								//Выводы работают на выход
	GPIO.GPIO_Speed = GPIO_Speed_2MHz;							//Скорость работы выводов
	GPIO.GPIO_OType = GPIO_OType_PP;							//Тип значений порта - двухуровневый
	GPIO.GPIO_PuPd = GPIO_PuPd_DOWN;							//Начальное значение выводов - 0
	GPIO_Init(GPIOA, &GPIO);									//Инициализация настроек
	}

void init_TIMs(void)											//Конфигурация таймера клавиатуры (TIM2)
	{

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);		//Включаем тактирование таймера

	TIM.TIM_Period = 1000;										//Период отсчета таймера
	TIM.TIM_Prescaler = 16;										//Предделитель частоты таймера
	TIM.TIM_CounterMode = TIM_CounterMode_Up;					//Счет таймера "вверх"
	TIM_TimeBaseInit(TIM2, &TIM);								//Применение прописанных настроек к таймеру 2
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//Разрешение прерываний по таймеру 2
	TIM_TimeBaseStructInit(&TIM);								//Заполняем структуру TIM_TimeBaseStructInit введенными выше параметрами

	TIM_Cmd(TIM2, ENABLE);										//Включение таймера 2
   	NVIC_EnableIRQ(TIM2_IRQn);									//Включение прерываний по таймеру 2
	}


void TIM2_IRQHandler(void)										//Обработчик, запускаемый при оставновке тамера №2
	{
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);					//Очистка состояния таймера
	Keyboard();													//Обращаемся к функции Keyboard
	}

В этой части программы мы заполняем стандартные структуры GPIO_InitTypeDef и TIM_TimeBaseInitTypeDef необходимыми параметрами, чтобы задать для таймера и порта ввода вывода требуемый режим работы. В обработчике запроса на прерывание от второго таймера (функция TIM2_IRQHandler) мы обращаемся к функции Keyboard(), листинг которой представлен ниже:

void Keyboard (void)											//Функция для работы с клавиатурой
	{
	for(Sdvig = 16; Sdvig <= 64; Sdvig = Sdvig*2)				//Цикл, позволяющий получить информацию о нажатой кнопке
	{
	GPIO_Write(GPIOA, Sdvig); 									//Выставление логической 1 на выходах столбов
	Temp = 0b1111 & GPIO_ReadInputData(GPIOA);					//Применение маски, для чтения только младшей тетрады порта А
		if (Temp > 0)											//Если значение младшей тетрады порта А отличается от 0
		{	if ((y == 0))										//Если "флаг" сигнализирует о том, что ни одна из кнопок не нажата
			{	for (Temp; Temp == Temp2; Vkl++)				//Ждем в течении 10ти итераций окончание дребезга контактов
				{	if (Vkl > 10)
					{Key_Number();								//Обращаемся к функции Key_Number
					return;										//Выходим из цикла
					}
				}
			}
																//После нажатия новой кнопки присваиваем переменной Temp2 значение
																//переменнойTemp,чтобы можно было войти
				Temp2 = Temp;									//в цикл for (Temp; Temp == Temp2; Vkl++)
				Vkl = 0;										//Сбрасываем счетчик подверждения нажатия
				Otkl = 0;										//Сбрасываем счетчик подтверждения отпускания
		}
	if ((y == 1) &&	(Temp == 0))								//Если мы вывели значение нажатой кнопки на экран и кнопку отпустили
		{	if (Otkl == 10)										//Ждем 10 итераций
			{
				y = 0;											//Сбрасываем флаг записи, разрешая вывести на экран следующую цифру
			}
			Otkl ++;
		}
	}
	}

Алгоритм, который реализуется данной частью кода в общем виде описан в предыдущей статье с той лишь разницей, что теперь выводы, относящиеся к столбам клавиатуры являются питающими (PA4 - PA6), а выводы строк сканируются на наличие высокого потенциала (PA0 - PA3). Цикл for(Sdvig = 16; Sdvig <= 64; Sdvig = Sdvig*2) присваивает переменной Sdvig значения 0b10000 (16), затем 0b100000 (32), затем 0b1000000 (64), а команда GPIO_Write(GPIOA, Sdvig) выставляет на порту, к которому подключена клавиатура значение этой переменной, то есть "сдвигает" напряжение логической единицы от пина PA4 до PA6 и нам остается только определить, на каком из первых четырех контактов находится высокий потенциал при нажатии кнопки.

Так как нам необходимо зафиксировать изменение только на младших выводах, то уместно применить операцию побитного логического умножения (так называемую "маску"), чтобы не фиксировать изменения на выводах 4, 5, 6. Это позволит упростить следующие логические выражения, и выполнять последующий код только после того, как будет нажата какая - либо клавиша.

Если на одном из первых четырех выводов зафиксировано напряжение логической единицы (значение переменной Temp будет отличаться от 0), мы проверяем значение переменной y (флаг, разрешающий обрабатывать нажатие следующей кнопки), в случае, если оно равно 0, переходим к циклу  for (Temp; Temp == Temp2; Vkl++), который в течении 10ти итераций проверяет, остается ли нажатой кнопка, и затем переходит к выполнению функции Key_Number(). 10 итераций необходимы для прекращения дребезга контактов, а так же предохраняет от ложной регистрации нажатий. Переменная у, является флагом, единичное значение которого запрещает регистрировать любые другие нажатия клавиш и сигнализирует о том, что нажатая до этого кнопка еще не отпущена. Программа будет "ждать" до тех пор, пока мы не отпустим уже нажатую кнопку и только тогда значение переменной у станет равным 0.

void Key_Number()													//Функция для получения значения нажатой клавиши из значения порта А
{
	switch (Temp | Sdvig)											//Операция логического сложения первых двух тетрад порта А
	{
		case 17: Symbol = 1; y = 1; break;
		case 18: Symbol = 4; y = 1; break;
		case 20: Symbol = 7; y = 1; break;
		case 24: Symbol = 10; y = 1; break;			//Символ *

		case 33: Symbol = 2; y = 1; break;
		case 34: Symbol = 5; y = 1; break;
		case 36: Symbol = 8; y = 1; break;
		case 40: Symbol = 0; y = 1; break;

		case 65: Symbol = 3; y = 1; break;
		case 66: Symbol = 6; y = 1; break;
		case 68: Symbol = 9; y = 1; break;
		case 72: Symbol = 11; y = 1; break;		//Символ #

		default: y = 1; break;				//Если нажата недопустимая комбинация, сбрасываем значение порта
	}
}

В функции Key_Number() мы выполняем операцию поразрядного логического сложения для переменных Temp и Sdvig, что позволяет получить значение, выставленное в порт, и однозначно определить, какая клавиша нажата в данный момент. В данном случае нет возможности использовать команду типа GPIO_Read*******, так как первые четыре вывода работают на вход, а остальные на выход и в итоге, при попытке чтения, мы получаем ложные данные. Если нажаты одновременно несколько клавиш, то программа будет ждать момента, когда они будут отпущены.

Основным недостатком данной библиотеки является сложность настройки "под себя". Клавиатура всегда должна быть подключена только к одному порту и пины должны идти подряд, но можно очень легко изменить порт, к которому подключается клавиатура на любой другой. При большом желании можно "поднять" пины, то есть изменить PA0 - PA6 на, например, PA7 - PA13, но при этом необходимо будет изменить условие для цикла for(Sdvig = 16; Sdvig <= 64; Sdvig = Sdvig*2), а так же исправить  в функции Key_Number() результаты case`ов.

Для демонстрации работы библиотеки был использован дисплей WH1602d, а так же,найденная на просторах интернета, библиотека для его подключения (пины для подключения дисплея перечислены в самом начале прикрепленного ниже проекта). Также в проекте была немного дополнена функция Key_Number(), функцией вывода данных на дисплей. К статье прикреплены файлы самой библиотеки, а так же файлы демонстрационного проекта. При разработке использовалась среда CooCox CoIDE v 1.7.6.

Прикрепленные файлы:

Теги:

Опубликована: 0 0
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (0) | Я собрал (0) | Подписаться

Статью еще никто не комментировал. Вы можете стать первым.
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическая мощность?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

AVR-программатор USB ASP
AVR-программатор USB ASP
Автомобильный GPS-трекер с GSM/GPRS и дистанционным управлением МиниПК MK809V - 4 ядра, Android 4.4.2
вверх