Neste tutorial é apresentada a implementação de um programa para a leitura um teclado matricial de 4 linhas e 4 colunas. Primeiramente é implementado o código com o uso da técnica de varredura, após por interrupção.
O teclado utilizado neste tutorial é semelhante ao mostrado na fig. 1, encontrado em diversas aplicações comerciais e industriais, como caixas eletrônicos e IHM (Interfaces Homem Máquina). O uso deste tipo de teclado visa economizar a quantidade de pinos utilizados no MCU, onde normalmente seria usada uma entrada digital para conectar cada chave. Sendo assim, seriam necessárias 16 entradas para conectar as 16 teclas, o que é uma quantidade grande de pinos.
Figura 1. teclado matricial 4x4.
Fonte: Dx.com
O diagrama deste teclado, visto na Fig. 2, mostra que há uma matriz de contatos formada por 4 linhas e 4 colunas. Quando uma tecla é pressionada a conexão mecânica é fechada, conectando uma linha à uma coluna, como mostra a Fig. 3, quando a tecla n° 1 é pressionada.
Figura 2. Esquema de ligação das teclas.
Fonte: Elaborado pelo autor.
Figura 3. Tecla pressionada.
Fonte: Elaborado pelo autor.
Para que um circuito digital perceba o pressionamento de uma tecla, é necessário transmitir um nível lógico para uma entrada digital. Portanto, precisamos, por exemplo, transmitir um nível lógico "alto", do pino 1 para o pino 5. Para que as demais teclas sejam percebidas por um circuito, cada linha precisa ser energizada para que as colunas transmitam o nível "alto" para cada tecla pressionada.
Figura 4. Ligações do teclado 4x4.
Fonte: Elaborado pelo autor.
Tabela 1. Lista de conexões
LIN1 = PD0
LIN2 = PD1
LIN3 = PD2
LIN4 = PD3
COL1 = PD4
COL2 = PD5
COL3 = PD6
COL4 = PD7
Fonte: Elaborado pelo autor.
O circuito mostrado na Fig. 4, mostra o esquema de ligações usado para este exemplo, com algumas precauções adicionais. A Tabela 1, lista as conexões feitas entre a placa e o teclado usado.
Em cada linha temos um resistor no valor de 220 Ohms para evitar um curto-circuito caso as teclas 1 e 4 sejam pressionadas ao mesmo tempo. Os resistores de 10 KOhms garantem que os pinos "COL" sejam mantidos em um nível lógico estável, evitando que os mesmos fiquem flutuando.
A placa atual permite habilitar resistores de pulldown internos ao MCU, eliminando a necessidade de adicionar estes 4 resistores externamente.
Antes de conectar o circuito na placa é necessário configurar os pinos do MCU, usando a tela de configuração, como apresentado na Figura 5. Com o código gerado, é necessário adicionar o código para efetuar a varredura das teclas como no trecho abaixo.
while (1) {
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_0, 1);
i_tecla = 255;
i = 0;
do {
HAL_Delay(2);
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_4) == 1) { //chave pressionada?
i_tecla = 1; //pressionou a tecla '1'
}
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_5) == 1) { //chave pressionada?
i_tecla = 2; //pressionou a tecla '2'
}
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_6) == 1) { //chave pressionada?
i_tecla = 3; //pressionou a tecla '3'
}
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_7) == 1) { //chave pressionada?
i_tecla = 10; //pressionou a tecla 'A'
}
i++;
} while (i < 10 && i_tecla == 255);
if (i_tecla != 255)
i_tecla_ant = i_tecla;
i_tecla = 255;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_0, 0);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, 1);
i = 0;
do {
HAL_Delay(2);
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_4) == 1) { //chave pressionada?
i_tecla = 4; //pressionou a tecla '4'
}
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_5) == 1) { //chave pressionada?
i_tecla = 5; //pressionou a tecla '5'
}
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_6) == 1) { //chave pressionada?
i_tecla = 6; //pressionou a tecla '6'
}
if (HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_7) == 1) { //chave pressionada?
i_tecla = 11; //pressionou a tecla 'B'
}
i++;
} while (i < 10 && i_tecla == 255);
if (i_tecla != 255)
i_tecla_ant = i_tecla;
i_tecla = 255;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, 0);
}
O trecho do programa acima mantém cada linha do teclado ativa por um tempo de 20ms, permitindo a varredura das 4 teclas da linha ativa. Quando uma tecla é pressionada a variável i_tecla recebe o valor e após o laço este valor é armazenado em i_tecla_ant.
A utilização de interrupção torna a varredura das teclas mais ágil e libera o laço principal para implementação de outras tarefas. O método de configuração da interrupção é o mesmo descrito no tutorial Programa 1 - pisca LED e ler tecla . A diferença para o exemplo atual está na necessidade de configurar mais pinos como fonte de interrupção (COL1 a COL4). A Figura 5 mostra a configuração de cada pino que deve gerar interrupção.
Figura 5. Habilitação de interrupção para PD4.
Fonte: Elaborado pelo autor.
Cada pino precisa ser configurado para habilitar o resistor interno de pulldown e GPIO Mode para habilitar a interrupção na borda de subida do sinal no pino, como visto na Figura 6.
Figura 6. Configuração dos pinos de interrupção.
Fonte: Elaborado pelo autor.
O código do laço principal fica reduzido a poucas linhas que realizam o acionamento dos sinais de linha, como visto no código abaixo.
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
gi_linha=1;
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_0, 1);
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_0, 0);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, 1);
gi_linha=2;
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_1, 0);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 1);
gi_linha=3;
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, 0);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_3, 1);
gi_linha=4;
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_3, 0);
}
/* USER CODE END 3 */
}
O código da rotina de tratamento da interrupção fica como visto a seguir.
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
switch(GPIO_Pin){
case GPIO_PIN_4:
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
switch(gi_linha){
case 1:
gi_tecla=1;
break;
case 2:
gi_tecla=4;
break;
case 3:
gi_tecla=7;
break;
case 4:
gi_tecla=15;
break;
}
break;
case GPIO_PIN_5:
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
switch(gi_linha){
case 1:
gi_tecla=2;
break;
case 2:
gi_tecla=5;
break;
case 3:
gi_tecla=8;
break;
case 4:
gi_tecla=0;
break;
}
break;
case GPIO_PIN_6:
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
switch(gi_linha){
case 1:
gi_tecla=3;
break;
case 2:
gi_tecla=6;
break;
case 3:
gi_tecla=9;
break;
case 4:
gi_tecla=16;
break;
}
break;
case GPIO_PIN_7:
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
switch(gi_linha){
case 1:
gi_tecla=10;
break;
case 2:
gi_tecla=11;
break;
case 3:
gi_tecla=12;
break;
case 4:
gi_tecla=13;
break;
}
break;
}
}
/* USER CODE END 4 */