|
Simone: a game of visual memory and speed
|
Portable functions to interact with the keyboard FSM library. All portable functions must be implemented in this file. More...
#include <stdio.h>#include <stdbool.h>#include <stdint.h>#include <math.h>#include "port_keyboard.h"#include "port_system.h"#include "stm32f4_system.h"#include "stm32f4_keyboard.h"#include "keyboards.h"Functions | |
| stm32f4_keyboard_hw_t * | _stm32f4_keyboard_get (uint8_t keyboard_id) |
| Get the keyboard struct with the given ID. More... | |
| void | _timer_scan_column_config (void) |
| Configure the timer that controls the duration of the column scanning. More... | |
| void | port_keyboard_init (uint8_t keyboard_id) |
| Configure the HW specifications of a given keyboard. More... | |
| void | port_keyboard_excite_row (uint8_t keyboard_id, uint8_t row_idx) |
| Set the given row to high and lower the others. More... | |
| void | port_keyboard_start_scan (uint8_t keyboard_id) |
| Start the scanning of a keyboard. More... | |
| void | port_keyboard_stop_scan (uint8_t keyboard_id) |
| Stop the scanning of a keyboard. More... | |
| void | port_keyboard_excite_next_row (uint8_t keyboard_id) |
| Update the row to be excited. More... | |
| bool | port_keyboard_get_key_pressed_status (uint8_t keyboard_id) |
| Return the status of the keyboard (pressed or not). More... | |
| void | port_keyboard_set_key_pressed_status (uint8_t keyboard_id, bool status) |
| Set the status of the keyboard (pressed or not). More... | |
| bool | port_keyboard_get_row_timeout_status (uint8_t keyboard_id) |
| Return the status of the column timeout flag. More... | |
| void | port_keyboard_set_row_timeout_status (uint8_t keyboard_id, bool status) |
| Set the status of the row timeout flag. More... | |
| char | port_keyboard_get_key_value (uint8_t keyboard_id) |
| Return the char representing the key pressed of a given keyboard based on its row that is being excited. This assumes that the matrix of chars is flattened (i.e., it is not a 2D array, but all rows are in a single array), thus it is necessary to calculate only one index. More... | |
| char | port_keyboard_get_invalid_key_value (uint8_t keyboard_id) |
| Return the null key value of a given keyboard. More... | |
Variables | |
| static GPIO_TypeDef * | keyboard_main_row_ports [] |
| Array of GPIO ports for the rows of the main keyboard. More... | |
| static uint8_t | keyboard_main_row_pins [] |
| Array of GPIO pins for the rows of the main keyboard. More... | |
| static GPIO_TypeDef * | keyboard_main_col_ports [] |
| Array of GPIO ports for the columns of the main keyboard. More... | |
| static uint8_t | keyboard_main_col_pins [] |
| Array of GPIO pins for the columns of the main keyboard. More... | |
| stm32f4_keyboard_hw_t | keyboards_arr [] |
| Array of elements that represents the HW characteristics of the keyboards connected to the STM32F4 platform. More... | |
Portable functions to interact with the keyboard FSM library. All portable functions must be implemented in this file.
| stm32f4_keyboard_hw_t* _stm32f4_keyboard_get | ( | uint8_t | keyboard_id | ) |
Get the keyboard struct with the given ID.
| keyboard_id | Keyboard ID. |
| void _timer_scan_column_config | ( | void | ) |
Configure the timer that controls the duration of the column scanning.
This function configures the timer to generate an internal interrupt to control the duration of a column scanning. The duration of the column scanning is defined in the PORT_KEYBOARDS_TIMEOUT_MS macro. This function is called by the port_keyboard_init() public function to configure the timer that controls the column scanning.
Use the timer indicated in the main page.
There are 2 ways to calculate the ARR and PSC values. You can implement any of them to configure the timer duration according to the PORT_KEYBOARDS_TIMEOUT_MS macro. Do not set the values directly!
Option 1. Efficient algorithm.
This option is the most efficient way to calculate the ARR and PSC values. It is based on the fact that the ARR value is near or equal to its maximum value (65535.0). And only one update of the PSC is needed. This eliminates the need for a loop, which could be slow if ARR is much larger than 0xFFFF.
Option 2. Iterative algorithm.
This option, on the other hand, starts with PSC equals 0 and re-computes in an iterative loop the values of ARR and PSC. ⚠️ WARNING! This option is not recommended. You should consider that this option is much slower than the efficient algorithm and this option will make the test_port_keyboard take a long time to execute (several seconds up to few minutes).
ARR and PSC registers. The math.h library is included to use the round() function. ARR and PSC registers, cast the result of the round() function to uint32_t. TODO alumnos:
✅ 1. Enable the clock of the timer that controls the column scanning. Check the reference manual of the STM32F4 to know if the timer is connected to the APB1 or APB2 bus (i.e.
RCC->APB1ENRorRCC->APB2ENR).
✅ 2. Disable the counter of the timer (registerCR1of the timer) because we want to configure it.
✅ 3. Enable the autoreload preload (bitARPEof the registerCR1) to enable the autoreload register.
✅ 4. Set the counter of the timer to 0 (registerCNT) to start counting from 0.
✅ 5. Compute the prescaler and the auto-reload register to set the duration of the timer interrupt. The duration is defined in thePORT_KEYBOARDS_TIMEOUT_MSmacro. Use any of the two options to calculate theARRandPSCvalues:Option 1. Efficient algorithm.
➡️ 6a. ConvertSystemCoreClockto double and store them in local variables. This is to ensure that the subsequent calculations are performed in floating-point arithmetic.
➡️ 6b. Compute an initial value for thePSCregister considering the maximum value ofARR(65535.0). Store it in a local variable of typedouble.
💡 Use all variable and numbers asdouble(i.e., use 1.0 instead of 1).
💡 Use the functionround()from themath.hlibrary to round the value of thePSC. Use round instead of casting todouble, because casting trunks the decimal value.
➡️ 6c. Re-compute the value of theARRregister with the previously computedPSCvalue. Store it in a local variable of typedouble.
💡 This would makeARRless than or equal to 65535.0, and we will adjustARRandPSCif necessary. This eliminates the need for a loop, which could be slow ifARRis much larger than 0xFFFF.
💡 Use the functionround()to round the value of theARR.
➡️ 6d. Check if the new value ofARRis greater than 0xFFFF (or 65535.0). If it is, incrementPSCby 1.0 and recalculateARR. This is to ensure thatARRdoes not exceed 0xFFFF.
💡 Because the re-computed value ofARRwill be close to its maximum, adding 1.0 to thePSCis enough to ensure that theARRdoes not exceeds it maximum value and we do not need to iterate over it, making this algorithm faster than using awhile(){}loop.
➡️ 6e. Continue in ✅ 7.
Option 2. Iterative algorithm.
➡️ 6a. ConvertSystemCoreClockto double and store them in local variables. This is to ensure that the subsequent calculations are performed in floating-point arithmetic.
➡️ 6b. Compute an initial value for theARRregister considering a minimum value ofPSC(0.0). Store it in a local variable of typedouble.
💡 Use all variable and numbers asdouble(i.e., use 1.0 instead of 1).
💡 Use the functionround()from themath.hlibrary to round the value of theARR. Use round instead of casting todouble, because casting trunks the decimal value.
➡️ 6c. If the value ofARRis greater than 0xFFFF (or 65535.0), increment thePSCby 1.0 and recalculateARR. This is to ensure thatARRdoes not exceed 0xFFFF.
➡️ 6d. Do this in an iterativewhile(){}loop.
➡️ 6e. Continue in ✅ 7.
✅ 6. Load the values computed forARRandPSCinto the corresponding registers of the timer.
💡 Ensure that values are rounded before casting.
✅ 7. ThePSCandARRvalues are currently in the preload registers. To load them into the active registers we need an update event. We achieve this by setting theUGbit of theEGRregister. It is important to do this at the end of the configuration of the timer.
✅ 8. Clear the update interrupt flag (bitUIFof the registerSR) to avoid an unwanted interrupt.
✅ 9. Enable the interrupts of the timer by setting theUIEbit of theDIERregister.
✅ 10. Set the priority of the timer interrupt in the NVIC using theNVIC_SetPriority()function and theTIMx_IRQninterrupt with the level of priority and sub-priority shown in the main page.
| void port_keyboard_excite_next_row | ( | uint8_t | keyboard_id | ) |
Update the row to be excited.
✅ 1. Update the
current_excited_rowto move to the next row. If the index equals or greater than the number of rows indicated in the layout, move to the first row.
💡 You can use the%(modulo) operator to get the remainder of the division
✅ 2. Call the functionport_keyboard_excite_row()to excite the new row.
| keyboard_id | Keyboard ID. This index is used to get the correct keyboard status struct. |
| void port_keyboard_excite_row | ( | uint8_t | keyboard_id, |
| uint8_t | row_idx | ||
| ) |
Set the given row to high and lower the others.
TODO alumnos:
✅ 1. Retrieve the keyboard configuration struct calling
_stm32f4_keyboard_get()
✅ 2. Iterate through all the rows indexes (you can get the number of rows from the keyboard's layout) and set them to LOW to ensure that only one row is excited at the same time.
✅ 3. Set the given row to HIGH.
💡 You can use theBSRRor call thestm32f4_system_gpio_write()function.
| keyboard_id | ID of the keyboard to be scanned. |
| row_idx | Index of the row to be excited. |
| char port_keyboard_get_invalid_key_value | ( | uint8_t | keyboard_id | ) |
Return the null key value of a given keyboard.
This function retrieves the null key value defined in the keyboard layout.
TODO alumnos:
✅ 1. Retrieve and return the
null_keyfield from the keyboard layout. Call the function_stm32f4_keyboard_get()to get the keyboard struct.
| keyboard_id |
| bool port_keyboard_get_key_pressed_status | ( | uint8_t | keyboard_id | ) |
Return the status of the keyboard (pressed or not).
TODO alumnos:
✅ 1. Return the value of the field
flag_key_pressed. Call the function_stm32f4_keyboard_get()to get the keyboard struct.
| keyboard_id | Keyboard ID. This index is used to get the correct keyboard status struct. |
| char port_keyboard_get_key_value | ( | uint8_t | keyboard_id | ) |
Return the char representing the key pressed of a given keyboard based on its row that is being excited. This assumes that the matrix of chars is flattened (i.e., it is not a 2D array, but all rows are in a single array), thus it is necessary to calculate only one index.
✅ 1. Determine the value (char) of the key pressed based on the index of the current scanned row and the index of the column that provoked the interrupt, and return it.
💡 You can calculate the key index as:key index = (excited row * num columns) + column interrupting.
| keyboard_id | Keyboard ID. This index is used to get the correct keyboard status struct. |
| bool port_keyboard_get_row_timeout_status | ( | uint8_t | keyboard_id | ) |
Return the status of the column timeout flag.
TODO alumnos:
✅ 1. Return the value of the field
flag_row_timeout. Call the function_stm32f4_keyboard_get()to get the keyboard struct.
| keyboard_id | Keyboard ID. This index is used to get the correct keyboard status struct. |
| void port_keyboard_init | ( | uint8_t | keyboard_id | ) |
Configure the HW specifications of a given keyboard.
Assuming we are using an STM32F4-based platform, this function must call the following functions:
TODO alumnos:
✅ 1. Retrieve the keyboard configuration struct calling
_stm32f4_keyboard_get()
✅ 2. Call functionstm32f4_system_gpio_config()with the right arguments to configure the rows as outputs with no pull up neither pull down connections (you can get the number of rows from the keyboard's layout).
✅ 3. Call functionstm32f4_system_gpio_config()with the right arguments to configure the columns as inputs with pull down connections. Call also functionstm32f4_system_gpio_config_exti()with the right parameters to to configure interruption mode in rising and falling edges and enabling the interrupt line for each column. Finally, call functionstm32f4_system_gpio_exti_enable()with priority level to1and the subpriority level to1for all columns. All keyboards will have the same priority levels for their columns in the basic implementation. You can get the number of columns from the keyboard's layout.
✅ 4. Clean/set all the fields of the keyboard struct to their initial values. Set the value ofcurrent_excited_rowto-1to indicate that no row is being excited at the beginning.
✅ 5. Configure the timer that controls the duration of the column scanning by calling the function_timer_scan_column_config()
💡 If any of the IRQs associated with the EXTI lines of the columns coincides with other IRQs in the system, the priority levels of this IRQ will be equal to the priority levels of the other peripheral.
| keyboard_id | Keyboard ID. This index is used to select the element of the keyboards_arr[] array |
| void port_keyboard_set_key_pressed_status | ( | uint8_t | keyboard_id, |
| bool | status | ||
| ) |
Set the status of the keyboard (pressed or not).
This function is used to update the status of the keyboard when a key press is detected or cleared.
TODO alumnos:
✅ 1. Set the value of the field
flag_key_pressedto the given status. Call the function_stm32f4_keyboard_get()to get the keyboard struct.
| keyboard_id | |
| status |
| void port_keyboard_set_row_timeout_status | ( | uint8_t | keyboard_id, |
| bool | status | ||
| ) |
Set the status of the row timeout flag.
This function is used to update the status of the row timeout flag when a timeout occurs or is cleared.
TODO alumnos:
✅ 1. Set the value of the field
flag_row_timeoutto the given status. Call the function_stm32f4_keyboard_get()to get the keyboard struct.
| keyboard_id | Keyboard ID. This index is used to get the correct keyboard status struct. |
| status | New status of the row timeout flag. |
| void port_keyboard_start_scan | ( | uint8_t | keyboard_id | ) |
Start the scanning of a keyboard.
This function starts the scanning of a keyboard by enabling the timer that controls the duration of the column scanning and setting the first row to HIGH.
TODO alumnos:
✅ 1. Reset the
flag_row_timeoutto indicate that a new scan is starting.
✅ 2. Reset the counter (CNT) of the timer.
✅ 3. Set the first row to be excited to HIGH and the others to LOW. You must use the appropiate function to do it.
✅ 4. Enable the timer interrupt in the NVIC using theNVIC_EnableIRQ()function and theTIMx_IRQninterrupt. ✅ 5. Enable the counter of the timer (registerCR1of the timer) to start the column scanning.
| keyboard_id | Keyboard ID. This index is used to get the correct keyboard status struct. |
| void port_keyboard_stop_scan | ( | uint8_t | keyboard_id | ) |
Stop the scanning of a keyboard.
TODO alumnos:
✅ 1. Disable the counter of the timer (register
CR1of the timer) to stop the column scanning.
✅ 2. Disable the timer interrupt in the NVIC using theNVIC_DisableIRQ()function and theTIMx_IRQninterrupt.
✅ 3. Set all rows to LOW.
| keyboard_id | Keyboard ID. This index is used to get the correct keyboard status struct. |
|
static |
Array of GPIO pins for the columns of the main keyboard.
|
static |
Array of GPIO ports for the columns of the main keyboard.
|
static |
Array of GPIO pins for the rows of the main keyboard.
|
static |
Array of GPIO ports for the rows of the main keyboard.
| stm32f4_keyboard_hw_t keyboards_arr[] |
Array of elements that represents the HW characteristics of the keyboards connected to the STM32F4 platform.
Array of elements that represents the HW characteristics of the keyboards.
This is an extern variable that is declared in stm32f4_keyboard.h. It represents an array of hardware keyboards.