cancel
Showing results for 
Search instead for 
Did you mean: 

4x4 keypad with NUCLEO F411RE

Roman_E
Associate III
/*
 * ledfunc.c
 *
 *  Created on: Jan 16, 2026
 *      Author: roman
 */
#include "main.h"
#include <string.h>
#include <stdio.h>
#include "ledfunc.h"

uint8_t flag = 0;
uint8_t blue_but_click_number = 0;
char pressedKey='\0';
char readChar='\0';
//UART_HandleTypeDef huart2;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	flag = !flag;
	char main_str[50] = "Button clicked: ";
	    char num_str[45]; // Buffer to hold the string representation of the integer
	    // Convert the integer to a string
	    snprintf(num_str, sizeof(num_str), "%d times\r\n", ++blue_but_click_number);
	    // Concatenate the new string to the main string
	    // You must ensure main_str has enough allocated space for both parts
	    strncat(main_str, num_str, sizeof(main_str) - strlen(main_str) - 1);
	HAL_UART_Transmit(&huart2, (uint8_t*)main_str, strlen(main_str), HAL_MAX_DELAY);

	char msg[20];
	sprintf(msg, "PRESSED: %c\r\n", pressedKey); // Formats string + char
	HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 100);

}
void ledblibk(int blinknum) {
	for (int i = 0; i < blinknum; i++) {
		HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
		leddelay(500);
		HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
		leddelay(500);
	}
}
void leddelay(int ms) {
	SysTick->LOAD = 16000 - 1;
	SysTick->VAL = 0;
	SysTick->CTRL = 0x5;
	for (int i = 0; i < ms; i++) {
		while (!(SysTick->CTRL & 0x10000)) {
		}
	}
	SysTick->CTRL = 0;
}
void ledblink() {
	if (1 == flag) {
		HAL_GPIO_TogglePin( LD2_GPIO_Port, LD2_Pin);
		HAL_Delay(50);
	} else {
		HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
	}
}
char Keypad_Read(void) {
	char keypad_map[4][4] = {
	    {'1','2','3','A'},
	    {'4','5','6','B'},
	    {'7','8','9','C'},
	    {'*','0','#','D'}
	};
    for (int i = 0; i < 4; i++) {
        // 1. Set current row to LOW (assuming Row pins are PC0-PC3)
        HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_0 << i), GPIO_PIN_RESET);

        // 2. Check each column (assuming Column pins are PC4-PC7)
        for (int j = 0; j < 4; j++) {
            if (HAL_GPIO_ReadPin(GPIOC, (GPIO_PIN_4 << j)) == GPIO_PIN_RESET) {
                while(HAL_GPIO_ReadPin(GPIOC, (GPIO_PIN_4 << j)) == GPIO_PIN_RESET); // Debounce/Wait for release
                HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_0 << i), GPIO_PIN_SET); // Reset row before returning
                return keypad_map[i][j];
            }
        }
        // 3. Set row back to HIGH
        HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_0 << i), GPIO_PIN_SET);
    }
    return 0; // No key pressed
}

Hello all 

I learn ( code below) !!!

Blue button is on EXTI - works.  When press LED2 blinks, press again - LED stops blink ( work as expected)

Press on Blue button also sends message ( debugging purposes ) to Serial comm 9600 ( works as expected ) 

Now I added Keypad_Read() function to read pressed key ( this doesn't work ) Please advice me on getting

keypad only

connected 4x4 keypad as below

 

Screenshot-2023-05-14-at-7.59.31-AM-2048x1495.png

My code below

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2026 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "ledfunc.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim3;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM3_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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();
  MX_USART2_UART_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1) {
	ledblink();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	}
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief TIM3 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 999;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 499;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */
	HAL_TIM_Base_Start_IT(&htim3);
  HAL_TIM_Base_Start(&htim3);
  /* USER CODE END TIM3_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 9600;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : PC0 PC1 PC2 PC3 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : PC4 PC5 PC6 PC7 */
  GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

	if (htim == &htim3) {
		//HAL_GPIO_TogglePin( LD2_GPIO_Port, LD2_Pin);
		pressedKey=Keypad_Read() ;
	}
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
	/* User can add his own implementation to report the HAL error return state */
	__disable_irq();
	while (1) {
	}
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @PAram  file: pointer to the source file name
  * @PAram  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

and 

 
/*
 * ledfunc.c
 *
 *  Created on: Jan 16, 2026
 *      Author: roman
 */
#include "main.h"
#include <string.h>
#include <stdio.h>
#include "ledfunc.h"

uint8_t flag = 0;
uint8_t blue_but_click_number = 0;
char pressedKey='\0';
char readChar='\0';
//UART_HandleTypeDef huart2;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	flag = !flag;
	char main_str[50] = "Button clicked: ";
	    char num_str[45]; // Buffer to hold the string representation of the integer
	    // Convert the integer to a string
	    snprintf(num_str, sizeof(num_str), "%d times\r\n", ++blue_but_click_number);
	    // Concatenate the new string to the main string
	    // You must ensure main_str has enough allocated space for both parts
	    strncat(main_str, num_str, sizeof(main_str) - strlen(main_str) - 1);
	HAL_UART_Transmit(&huart2, (uint8_t*)main_str, strlen(main_str), HAL_MAX_DELAY);

	char msg[20];
	sprintf(msg, "PRESSED: %c\r\n", pressedKey); // Formats string + char
	HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 100);

}
void ledblibk(int blinknum) {
	for (int i = 0; i < blinknum; i++) {
		HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
		leddelay(500);
		HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
		leddelay(500);
	}
}
void leddelay(int ms) {
	SysTick->LOAD = 16000 - 1;
	SysTick->VAL = 0;
	SysTick->CTRL = 0x5;
	for (int i = 0; i < ms; i++) {
		while (!(SysTick->CTRL & 0x10000)) {
		}
	}
	SysTick->CTRL = 0;
}
void ledblink() {
	if (1 == flag) {
		HAL_GPIO_TogglePin( LD2_GPIO_Port, LD2_Pin);
		HAL_Delay(50);
	} else {
		HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
	}
}
char Keypad_Read(void) {
	char keypad_map[4][4] = {
	    {'1','2','3','A'},
	    {'4','5','6','B'},
	    {'7','8','9','C'},
	    {'*','0','#','D'}
	};
    for (int i = 0; i < 4; i++) {
        // 1. Set current row to LOW (assuming Row pins are PC0-PC3)
        HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_0 << i), GPIO_PIN_RESET);

        // 2. Check each column (assuming Column pins are PC4-PC7)
        for (int j = 0; j < 4; j++) {
            if (HAL_GPIO_ReadPin(GPIOC, (GPIO_PIN_4 << j)) == GPIO_PIN_RESET) {
                while(HAL_GPIO_ReadPin(GPIOC, (GPIO_PIN_4 << j)) == GPIO_PIN_RESET); // Debounce/Wait for release
                HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_0 << i), GPIO_PIN_SET); // Reset row before returning
                return keypad_map[i][j];
            }
        }
        // 3. Set row back to HIGH
        HAL_GPIO_WritePin(GPIOC, (GPIO_PIN_0 << i), GPIO_PIN_SET);
    }
    return 0; // No key pressed
}

 

11 REPLIES 11
Andrew Neil
Super User

You already have a thread on this:4 x 4 keypad with Nucleo 411RE and HAL.

Did you ever get that working?

 

What debugging have you done?

Have you stepped through your code in the debugger to see what's happening?

 

Do you have an oscilloscope or logic analyser to check the scanning?

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

Yes , I tried to get working : step-by-step
1) LED  blinks : yes it works

2) added Blue button to EXTI : yes it works

3) added USART ( mainly for debug purpose , it sends msg when Blue button clicked and number of but clicks ) : yes it works

4) added 4x4 read keypad ( added to timer interruption , and to see pressed key wanted the same way send to USART )   but it doesn't work

#4 - is where I stuck .

 

what about your old thread? Please don't just abandon it!

 

I meant have you single-stepped the code of your keypad function in the debugger ?

 

Debugging is a key skill in any type of development work - you can't just give up and say "it didn't work".

How To Debug.

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

My apologies, but "reply"  interface kinda complicated ( regarding previous thread)

If I have question - do I need to reply to myself ?

And yes , I tried to debug "readKey" function. No luck.

My understanding is, that timer interrupt being called many times per sec ( many times "readKey" to

find if key was pressed. And that function call is not blocked, like another thread which filles keyPressed variable)

But I couldn't debug it ( I put breakpoints and tried to see values ,may be I miss something 


@Roman_E wrote:

My apologies, but "reply"  interface kinda complicated ( regarding previous thread)


Seriously?

There's a 'Reply' button on each post (the black one) - use that to make a reply specifically to one post;

There's also a box at the bottom of the page (with yellow button) - use that to make a general contribution to the topic:

 

Image1.png

But don't just abandon that thread.

People have made suggestions to you - so give them some feedback.

You can use @ to mention multiple people in one post.

For your keyboard scanning, forget about timers for now - just call it in a loop, and single-step in the debugger.

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.
Roman_E
Associate III

O will try (learn reply technique) Sorry 1> time

and may be u r right ( put keyRead func inside loop ) 

will try


@Roman_E wrote:

and may be u r right ( put keyRead func inside loop ) 


That's for the purpose of debugging.

Once it's working, then you probably will want it timer-driven.

 

Another key lesson in development is to start simple; do one simple thing at a time; get it thoroughly tested and working before moving on and adding complications.

A complex system that works is invariably found to have evolved from a simple system that worked.
A complex system designed from scratch never works and cannot be patched up to make it work.

Hi @Andrew Neil 
I just tries as you suggested ( put keyRead() func in main loop)

Yes it works !  I can see pressed keys ( as a debug output I use USART ) 
In non-embedded I would use printf or (System.ut.println Java) 
Thanks !
But I still think that I need to use "separate thread"  ( like timer interruption)?

my readKey() func doesn't handle some "specifics" bouncing/debouncing" etc
Am I right ?

 

@Roman_E 

I have one comment. Please avoid calling printf in an interrupt context: 

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	flag = !flag;
	char main_str[50] = "Button clicked: ";
	    char num_str[45]; // Buffer to hold the string representation of the integer
	    // Convert the integer to a string
	    snprintf(num_str, sizeof(num_str), "%d times\r\n", ++blue_but_click_number);
	    // Concatenate the new string to the main string
	    // You must ensure main_str has enough allocated space for both parts
	    strncat(main_str, num_str, sizeof(main_str) - strlen(main_str) - 1);
	HAL_UART_Transmit(&huart2, (uint8_t*)main_str, strlen(main_str), HAL_MAX_DELAY);

	char msg[20];
	sprintf(msg, "PRESSED: %c\r\n", pressedKey); // Formats string + char
	HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), 100);

}

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.