cancel
Showing results for 
Search instead for 
Did you mean: 

Keypad input using interrupt (Rising Callback) with OLED

blade_runner_2004
Associate

Good day
I am using a NUCLEO-STMG0B1RE board and i am trying to handle a 4x3 matrix keypad inputs using interrupts to switch menus on an OLED screen. The keypad inputs worked when I used continuous polling for keypad inputs. The OLED needs to continuously update (at least once per second) with data from ADC. This also worked when I used continuous polling, but the keypad inputs was a bit sluggish and I am scared it will slow my processor down for future use.

With the current code I am trying to do the following:

  • when key pressed, corresponding column goes high
  • interrupt triggered
  • Rising callback sets KeypadtriggerDetected = 1 and stores the column
  • KeypadtriggerDetected activates if statement in while(1) which pulls all the rows low, then sets them high one at a time.
  • When the key pressed is identified from keymap, that key gets processed with updates the state the OLED menu should be in
  • that state then gets passed to displayMenuOLED to display the corresponding menu


With the current code I have to spam keys to get a input.

I will add code for relevant sections as well as my main.c file


Private variables:

/* USER CODE BEGIN PV */
const char keymap[4][3] = { { '1', '2', '3' }, { '4', '5', '6' }, { '7', '8',
		'9' }, { '*', '0', '#' } };

GPIO_TypeDef *row_ports[4] = { GPIOC, GPIOB, GPIOB, GPIOB };
uint16_t row_pins[4] = { GPIO_PIN_0, GPIO_PIN_2, GPIO_PIN_6, GPIO_PIN_15 };

GPIO_TypeDef *col_ports[3] = { GPIOA, GPIOA, GPIOC };
uint16_t col_pins[3] = { GPIO_PIN_12, GPIO_PIN_11, GPIO_PIN_1 };

volatile char last_key = '\0';
volatile int col = -1;


typedef enum {
	PAGE_DEFAULT,
	PAGE_1,
	PAGE_2,
	PAGE_3,
	MENU_LV1,
	MENU_LV1_LOAD,
	MENU_LV1_UNITS,
	MENU_LV2_COUNT,
	MENU_LV2_ADD,
	MENU_ADD_UNITS_INPUT
} AppState;

volatile uint32_t KeypadtriggerDetected = 0;
volatile uint32_t KeypadtriggerTick = 0;
static AppState currentState = PAGE_DEFAULT;
int desired_load_state = -1;  // -1 = none, 0 = OFF, 1 = ON
static uint32_t last_adc_update = 0;
uint8_t force_oled_refresh = 0;


in main:

ssd1306_Init();  // Initialize the OLED display
displayMenuOLED(PAGE_DEFAULT); // default page on startup

// set row pins high (output)
for (int i = 0; i < 4; i++)
    HAL_GPIO_WritePin(row_ports[i], row_pins[i], GPIO_PIN_SET);

int start = HAL_GetTick();
uint32_t debounceTime = 80;

 

in while(1):

// Handle keypad press (triggered by column interrupt)
if (KeypadtriggerDetected == 1) {

    // Scan each row to identify which key is pressed
    for (int row = 0; row < 4; row++) {

        // Set all rows LOW before driving one HIGH
        for (int i = 0; i < 4; i++) {
            HAL_GPIO_WritePin(row_ports[i], row_pins[i], GPIO_PIN_RESET);
        }

        // Set the current row HIGH
        HAL_GPIO_WritePin(row_ports[row], row_pins[row], GPIO_PIN_SET);

        // Check if the column pin that triggered interrupt is still HIGH
        if (col >= 0 && col < 3 && HAL_GPIO_ReadPin(col_ports[col], col_pins[col]) == GPIO_PIN_SET) {
            // Key at (row, col) is being pressed
            char key = keymap[row][col];
            last_key = key;  // Store the key for processing
            break;           // Exit once key is found
        }
    }

    // Restore all rows to HIGH after scanning
    for (int i = 0; i < 4; i++) {
        HAL_GPIO_WritePin(row_ports[i], row_pins[i], GPIO_PIN_SET);
    }

    // Debounce and finalize the key detection
    if (last_key != '\0' && (HAL_GetTick() - KeypadtriggerTick > debounceTime)) {
        process_key_press(last_key);
        force_oled_refresh = 1;
        last_key = '\0';
        KeypadtriggerDetected = 0;
        col = -1;  // Clear column index for next interrupt
    }
// Check if ADC data is ready and if enough time (500 ms) has passed since last processing
if (adc_ready && HAL_GetTick() - last_adc_update >= 500) {

    process_adc_buffer();              // Process ADC data: calculate voltage, current, power, energy, etc.
    update_LED_D3_UnitsStatus();       // Update LED D3 based on remaining energy units
    update_LED_D5_PowerAlarm();        // Update LED D5 based on power threshold (e.g. > 3800W)

    // Only update the OLED display if user is on one of the real-time data screens
    if (currentState == PAGE_DEFAULT || currentState == PAGE_1 ||
        currentState == PAGE_2 || currentState == PAGE_3) {
        displayMenuOLED(currentState); // Refresh OLED with updated values
    }

    last_adc_update = HAL_GetTick();  // Update timestamp for next sampling window
}

}


in EXTI_Rising_Callback:

void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin) {

	if (GPIO_Pin == GPIO_PIN_4 || GPIO_Pin == B1_Pin) {
		ButtontriggerDetected = 1;
		ButtontriggerTick = HAL_GetTick();
		return;
	}

	if (GPIO_Pin == GPIO_PIN_1 || GPIO_Pin == GPIO_PIN_11 || GPIO_Pin == GPIO_PIN_12) {
			KeypadtriggerDetected = 1;
			KeypadtriggerTick = HAL_GetTick();

			// Set global col index (NOT a local variable)
			if (GPIO_Pin == GPIO_PIN_12)
				col = 0;
			else if (GPIO_Pin == GPIO_PIN_11)
				col = 1;
			else if (GPIO_Pin == GPIO_PIN_1)
				col = 2;
		}
}

 

Thank you in advace.

0 REPLIES 0