#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
//Declaring functions (JI)
void setup_gpio(); //This function initializes outputs and inputs. (JI)
char scan_keypad(); //This function continuously scans the keypad and responds on user input. (JI)
char readKeyPadSingleScan(char _keys[4][4], uint8_t _scanColPins[4], uint8_t _rowPins[4]); //Non-blocking keypad function declaration. (JI)
char readKeyPadBlock(char _keys[4][4], int scanColPins[4], int _rowPins[4]); //Blocking keypad function declaration. (JI)
//Defining the matrix for the keypad (4x4). (JI)
#define ROWS 4
#define COLS 4
//Declaring arrays with GPIO pins for the rows and columns. (JI)
int scanRowPins[ROWS] = {38, 37, 36, 35};
int colPins[COLS] = {0, 45, 48, 47};
//Declaring a two-dimensional array using the rows and columns arrays created above. (JI)
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
void app_main() {
setup_gpio(); //Calling function to initialize inputs and outputs. (JI)
int counter = 0; //Counter to verify block polling is working. (JI)
while (1) {
printf("Before blocking, counter: %d\n", counter); //Printing message to verify blocking is working. (JI)
char key = readKeyPadBlock(keys, colPins, scanRowPins); //The key variable will be updated with a value from the readKeyPadBlock function. (JI)
counter++; //Counter only increments after a key press. (JI)
printf("After blocking, counter: %d\n", counter); //Print message after successful button press to verify blocking is working. (JI)
printf("You pressed: %c\n", key);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
//Function definition to initialize rows and columns as outputs and inputs. (JI)
void setup_gpio() {
for (int i = 0; i < ROWS; i++) {
gpio_set_direction(scanRowPins[i], GPIO_MODE_OUTPUT); //Initializing rows as outputs. (JI)
gpio_set_level(scanRowPins[i], 1);
}
for (int i = 0; i < COLS; i++) {
gpio_set_direction(colPins[i], GPIO_MODE_INPUT); //Initializing columns as inputs. (JI)
gpio_set_pull_mode(colPins[i], GPIO_PULLUP_ONLY);
}
}
//Function definition for the keypad scanning. (JI)
char scan_keypad() {
uint8_t scanVal; //Declaring an unsigned 8-bit variable. The value of the button pressed will be stored in this variable. (JI)
for (int i = 0; i < ROWS; i++) { //Scan through the each row, one at a time to get their values. (JI)
scanVal = ~(1 << i); //One's complement the value of each row. If a button is pressed, only one row will be different. One's complement is required because the circuit is in a pull-up resistor configuration. (JI)
//This portion takes the newly updated value of the button press from scanVal, and iterates through each bit to read its binary value.
for (int j = 0; j < ROWS; j++) {
gpio_set_level(scanRowPins[j], (scanVal >> j) & 1); //Here, the button pressed is identified by extracting the bits from each row. The row with a zero in one of the bits means that specific button was pressed in that row. (JI)
}
vTaskDelay(pdMS_TO_TICKS(5));
//Debounce operation to prevent accidental additional inputs or errors. (JI)
for (int k = 0; k < COLS; k++) { //Iterate through each column. (JI)
if (gpio_get_level(colPins[k]) == 0) { //If any of the columns detect a '0', it means a button is being pressed.
while (gpio_get_level(colPins[k]) == 0) { //While that button is being pressed, continuously repeat a 50ms delay until the button is depressed. (JI)
vTaskDelay(pdMS_TO_TICKS(50));
}
return keys[k][i]; //Return the values of the intersecting column and row. These values will be stored in the 'key' variable in app_main.
}
}
}
return '\0';
}
//Function definition for the non-blocking polling. (JI)
char readKeyPadSingleScan(char _keys[4][4], uint8_t _scanColPins[4], uint8_t _rowPins[4]) {
uint8_t scanVal;
char retVal = '\0'; // Setting default return value to null character when no key is pressed
// Debug print statement to show when scanning is happening
printf("Performing single non-blocking keypad scan...\n");
// Scanning each row just like in scan_keypad(), but using the local parameters
for (int i = 0; i < ROWS; i++) {
// Creating the binary pattern to set one row LOW at a time
scanVal = ~(1 << i);
// Setting the GPIO levels based on scanVal pattern
for (int j = 0; j < ROWS; j++) {
// Using _rowPins (parameter) instead of scanRowPins (global)
gpio_set_level(_rowPins[j], (scanVal >> j) & 1);
}
// Small delay to allow GPIO levels to stabilize
vTaskDelay(pdMS_TO_TICKS(5));
// Checking each column for a pressed button
for (int k = 0; k < COLS; k++) {
// Using _scanColPins (parameter) instead of colPins (global)
if (gpio_get_level(_scanColPins[k]) == 0) {
// Return immediately when a key is detected (JI)
retVal = _keys[k][i];
return retVal;
}
}
}
// If no key was pressed, return the null character
return retVal;
}
//Function definition for the blocking polling. (JI)
char readKeyPadBlock(char _keys[4][4], int _scanColPins[4], int _rowPins[4]) {
uint8_t scanVal; //This variable will hold the binary value of the button that is pressed. (JI)
while (1) {
printf("Scanning keypad (blocking)...\n"); //Print a message to show block polling is working. (JI)
for (int i = 0; i < ROWS; i++) {
scanVal = ~(1 << i); //Scanning through the row, one's complementing the results and storing them in scanVal. (JI)
for (int j = 0; j < ROWS; j++) {
gpio_set_level(_rowPins[j], (scanVal >> j) & 1); //Reading the result and sending an electrical path to the pins that have a "0" (JI).
}
vTaskDelay(pdMS_TO_TICKS(5));
for (int k = 0; k < COLS; k++) {
if (gpio_get_level(_scanColPins[k]) == 0) { //If a button is pressed, then proceed with the code below. (JI)
while (gpio_get_level(_scanColPins[k]) == 0) { //While the button is pressed or held, continuously delay by 50ms until the button is released. (JI)
vTaskDelay(pdMS_TO_TICKS(50));
}
return _keys[i][k]; //Return the values of the row and column that are electrically active. (JI)
}
}
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1