The fifth chapter is dedicated to explaining AMetal in depth. The content of this section includes 5.3 Keyboard Scanning Interface and 5.4 PWM Interface.
5.3 Keyboard Scanning Interface
>>> 5.3.1 Single Independent Button
1. Button Behavior
As shown in Figure 5.1, there are three ways to determine the operation behavior of the buttons: no button press, button press, and button release. The ret_flag flag is used to distinguish these three states, which are typically 0xFF (invalid), 0 (pressed), and 1 (released). It's also possible for an error trigger to occur, where ret_flag is set to 0xFF.
Figure 5.1 Independent Button Circuit Diagram
Since the time a button is pressed is usually hundreds of milliseconds, which is long compared to the MCU’s speed, it’s unnecessary to constantly detect the button. Instead, it's sufficient to check the GPIO level at regular intervals, such as every 10 ms. The detection method is as follows (1 represents high level, 0 represents low level):
(1) When no key is pressed, PIO0_1 is 1 because it has a weak pull-up resistor.
(2) When the KEY is pressed, PIO0_1 becomes 0. After a 10ms debounce delay, if PIO0_1 is 1, it indicates an error trigger; if it remains 0, it means the key is pressed, and ret_flag is set to 0 to perform the corresponding action.
(3) When the KEY is released, PIO0_1 returns to 1. After another 10ms debounce, if PIO0_1 is 0, it indicates an error trigger; if it remains 1, it means the button has been released, and ret_flag is set to 1 to perform the corresponding action.
It can be seen that the GPIO state should be scanned every 10ms, regardless of whether a key is pressed or not. Each scan result can be considered as the current key value (key_current_value). Due to button jitter, it's necessary to compare the current key value with the next scan’s value to eliminate false triggers. Since the next scan’s value is unknown, the previous scan’s value must be saved until the next scan is completed. At the end of each scan, key_current_value is stored into key_last_value. For the next scan, key_last_value always holds the last key value. If the new scan’s key_current_value matches key_last_value, it’s considered valid and stored into key_final_value; otherwise, it’s regarded as an error trigger.
2. GPIO Status
From the GPIO status, there are four possible states: 1/1, 1/0, 0/0, and 0/1. During initialization:
Key_last_value = 1, key_final_value = 1, ret_flag = 0xFF
Here, “1†and “0†in 1/0 represent the current key value before and after the scan, respectively, and ret_flag is initialized to 0xFF for each scan.
(1) 1/1: If the GPIO remains high, the state hasn’t changed (no key pressed).
Key_last_value = 1, key_current_value = 1, key_final_value = 1, ret_flag = 0xFF
(2) 1/0: If the GPIO transitions from 1 to 0 (key pressed), wait 10ms for debouncing. Key_last_value = 1, key_current_value = 0, key_final_value = 1, ret_flag = 0xFF. If the GPIO remains 0 (key still pressed), perform the corresponding action.
Key_last_value = 0, key_current_value = 0
Because both scans show 0, the key value is saved to key_final_value, and ret_flag is set to 0, indicating a key press. Otherwise, it’s considered an error trigger.
(3) 0/1: If the GPIO changes from 0 to 1 (button released), wait 10ms for debouncing.
Key_last_value = 0, key_current_value = 1, key_final_value = 0, ret_flag = 0xFF
If the GPIO remains 1 (button actually released), perform the corresponding action.
Key_last_value = 1, key_current_value = 1
Since both scans show 1, the key value is saved to key_final_value, and ret_flag is set to 1, indicating the button has been released. Otherwise, it’s considered an error trigger.
3. Related Functions and Examples
The board-level initialization and independent key scan functions are detailed in Listing 5.42.
Listing 5.42 Independent Key Scanner (key1.c)
The process of determining whether the current key value is equal across scans does not use a loop statement, avoiding the impact of the 10ms delay on the scanning program, thereby significantly improving the MCU’s performance. To make it easier to use, you can wrap key1.c as the key1.h interface, as shown in Listing 5.43.
Listing 5.43 key1.h File Contents
If the return value of key_scan() is 0, it means a key is pressed; if it’s 1, the key has been released; if it’s 0xFF, no key is pressed. The corresponding test procedure is detailed in Listing 5.44. If a key is pressed, the buzzer will beep once; when the button is released, LED0 will toggle.
Listing 5.44 Independent Key Sample Program
>>> 5.3.2 Multiple Independent Buttons
The principle of scanning multiple independent buttons is similar to that of a single button. In a single button, only one bit is used per variable. Since a bit’s value (0 or 1) can represent two states of a button, when multiple buttons are required, multiple bits can be utilized, one per button. At the same time, since the level corresponding to the pressed state of an independent button may not always be low, the level corresponding to the pin and the pressed button is saved in a structure array. For example:
The pins corresponding to the four buttons defined in the code segment above are PIO0_1, PIO0_2, PIO0_3, and PIO0_5, respectively, with their respective key levels assumed to be 1, 0, 1, and 0. There is only one independent button here, so the structure array contains only one button’s information:
Based on the idea of a single button, the program keyn.c that supports multiple independent buttons can be modified, as shown in Listing 5.45.
Listing 5.45 Scanner Supporting Multiple Independent Buttons (keyn.c)
At the same time, the function interface declaration and relevant type definitions are stored in keyn.h, as shown in Listing 5.46.
Listing 5.46 keyn.h File Contents
Obviously, the programming approach for multiple independent buttons is the same as for a single button, with most of the code remaining unchanged. The difference lies in using multiple bit operations for multiple buttons. When only one button is present, any change in the key value indicates a state change. However, for multiple buttons, detecting a key value change alone is not enough to identify which button has changed. A clever solution is to use a change variable to track bit changes. Since there are multiple keys, the scan function’s return value cannot simply indicate press or release with 0 or 1 but must also include information identifying which button. The return value is an 8-bit unsigned number, where the highest bit indicates press (0) or release (1), and the lower 7 bits identify the specific key (0 to N-1, where N is the number of keys). When the ith button is pressed, its return value is i; when it is released, the return value is (1 << 7) | i.
To facilitate subsequent use, the above program is added to the key.h interface without considering the software timer. The sample program using multiple buttons also activates the buzzer for a "beep" sound and toggles LED0 when the button is released, as shown in Listing 5.47.
Listing 5.47 Supports Multiple Independent Key Sample Programs
If a software timer is used, a software timer initialization function must also be added, such as:
Since a software timer is used, the keyn_scan() function is called automatically. When a button event is detected, the button handler is triggered. However, when the module is packaged, the role of the button handler is not known, so a callback mechanism is needed. This involves registering a function by the application, which is then automatically called when the event occurs, as shown below:
Then, the initialization function is redefined with the software timer, as shown in Listing 5.48:
Add the definition of the above function declaration and callback function to the keyn.h file (Listing 5.48) and add the implementation code to the keyn.c file (Listing 5.49).
Listing 5.48 keyn.h File Contents
Clearly, when a button event occurs, the registered callback function is automatically called during the initialization function.
Listing 5.49 Adds an Initialization Interface with a Software Timer
>>> 5.3.3 Matrix Keyboard
Although the matrix connection method improves I/O efficiency, the method of distinguishing and judging button actions is more complex, making it commonly used in computers. The following example uses the 2×2 matrix keyboard circuit shown in Figure 4.15 to introduce the programming method for progressive column-by-column keyboard scanning. The corresponding interface is detailed in matrixkey.h and the interface is shown in Listing 5.50. The corresponding implementation is detailed in matrixkey.c, as shown in Listing 5.51.
Listing 5.50 matrixkey.h File Contents
Listing 5.51 matrixkey.c File Contents
To allow other codes to reuse the programs from previous independent buttons as much as possible, the scanned button states correspond to the bits of the key values, with KEY0 corresponding to bit0, KEY1 to bit1, etc. When a key is pressed, its corresponding bit is 0; when released, it is 1. Additionally, to obtain the key value, the row line must be configured as an output in the initialization function, while the column line is configured as an input. It can be seen that the main difference between the matrix keyboard and the independent key is the way the key value is obtained. To maximize code reuse, the matrix keyboard keys are associated with the bits of the key value. That is, when a key is pressed, its corresponding bit is 0; when released, it is 1.
The two com terminals of the digital tube shown in Figure 5.2 are multiplexed with the columns of the matrix keyboard. PIO0_17 and PIO0_23 are both com0 and com1 of the digital tube, and the column lines KL0 and KL1 of the matrix keyboard are designed to share the same pins. As a keyboard scan, the column line needs to be configured as an input. When scanning as a digital tube, the com terminal needs to be set as an output. Obviously, when the matrix keyboard is used in conjunction with the digital tube, it needs to be packaged as an interface, adding the function matrixkey_scan_with_digtron() to the matrixkey.c shown in Listing 5.52, and the new interface is added to matrixkey.h, as shown in Listing 5.53.
Figure 5.2 LED Display Circuit Diagram
Listing 5.52 matrixkey_scan_with_digtron() Function Implementation
A new g_col_level[] array is added to save the keyboard state before starting the scan. After the matrix keyboard scan is complete, not only are the column pins restored to the output state, but the pin levels are also restored to their pre-scan state, ensuring that the entire keyboard scanning program does not affect the common pin shared with the digital tube.
Listing 5.53 matrixkey.h File Contents
5.4 PWM Interface
A current whose magnitude and direction change periodically with time is called AC. The most basic waveform in AC is a sine wave, and other waveforms are non-sinusoidal. Signals used in devices like computers, televisions, and radars are pulse waves, sawtooth waves, etc., and their voltage and current waveforms are all non-sinusoidal alternating currents.
PWM (Pulse Width Modulation) is a pulse coding technique that varies the pulse width according to the signal level. The period of the pulse width modulated wave is fixed, and the coded value is represented by the duty ratio (high level / period, the ratio of the active level in the entire signal period, ranging from 0 to 100%). PWM can be used to digitally encode analog signal levels or control motor speed or LED brightness by regulating the output energy over the entire period of the high (or low) level.
The PWM signal is generated by a counter and comparator. A threshold is set in the comparator, and the counter increments at a certain frequency. When the counter value is less than the threshold, a high level is output; when it exceeds the threshold, a low level is output. When the counter overflows and resets to 0, it returns to the initial level state, forming a PWM waveform, as shown in Figure 5.3.
Figure 5.3 PWM Waveform Diagram
When the counter value is less than the threshold, a high level is output; when it exceeds the threshold, a low level is output. The threshold is 45, and the counter has a maximum value of 100. The PWM waveform has three key points: the start point is 1, the counter value is 0; the counter reaches the threshold 2, the I/O state is inverted; the counter reaches the maximum value 3, the I/O state is inverted, and the counter value is reset to 0 to restart counting.
>>> 5.4.1 Initialization
Before using the PWM general-purpose interface, the PWM initialization must be completed to obtain the standard PWM instance handle. In the LPC82x, the SCT (State Configurable Timer) provides a PWM output function. It is essentially a state-programmable timer that can be used as a normal timer, input capture, PWM output, etc. It is very powerful. Here, it is used solely as a PWM function, and AMetal provides an example initialization function that uses the SCT as a PWM function. Its function prototype is:
The function returns the PWM instance handle of type am_pwm_handle_t, which will be used as the argument to the handle parameter in the PWM general interface. The type am_pwm_handle_t (am_pwm.h) is defined as follows:
Since the PWM instance handle returned by the function is only passed as a parameter to the PWM general interface, there is no need to do anything else with the handle, so there is absolutely no need to know anything about the type. It is important to note that if the value of the instance handle returned by the function is NULL, the initialization fails and the instance handle cannot be used.
Directly call the instance initialization function to complete the SCT initialization and get the corresponding instance handle:
When the SCT is used as the PWM function, it supports 6 channels, allowing 6 PWMs to be output simultaneously. The corresponding I/O ports for each channel are shown in Table 5.8.
Table 5.8 I/O Ports Corresponding to Each Channel
>>> 5.4.2 PWM Interface Function
AMetal provides three PWM standard output interface functions, as shown in Table 5.9.
Table 5.9 PWM Standard Interface Functions
1. Configuring the PWM Channel
Configure the cycle time and high time of a PWM channel. The function prototype is:
If AM_OK is returned, the configuration is successful; if -AM_EINVAL is returned, the configuration fails. The sample program is shown in Listing 5.54.
Listing 5.54 am_pwm_config() Sample Program
2. Enable Channel Output
Enable the channel output so that the corresponding channel starts to output the waveform. The function prototype is:
If AM_OK is returned, the enable is successful and the output waveform is started. If -AM_EINVAL is returned, the enable fails. For the sample program, see Listing 5.55.
Listing 5.55 am_pwm_enable() Sample Program
3. Disable Channel Output
Disable the channel output and turn off the waveform output of the corresponding channel. The function prototype is:
If AM_OK is returned, the disable is successful; if -AM_EINVAL is returned, the disable fails. For the sample program, see Listing 5.56.
Listing 5.56 am_pwm_disable() Sample Program
>>> 5.4.3 Buzzer Interface Function
In the buzzer sounding program, although the delay time is only 500us, it is very resource-intensive for the MCU, as it prevents the MCU from doing anything else during the delay. We can use the PWM output function of the MCU to directly output a pulse width modulation waveform through the PWM to drive the buzzer to sound. Assume the waveform period is 1ms, and the high and low levels occupy equal time, i.e., a 50% duty ratio. For the sample program, see Listing 5.57.
Listing 5.57 Buzzer Sounding Sample Program
To facilitate the control of the buzzer, the buzzer general interface is written based on the PWM interface function, and the declaration and implementation of the interface function are placed in the buzzer.h and buzzer.c files, respectively. When you need to use the buzzer, you can directly call the buzzer-related interface, as shown in Listing 5.58.
Listing 5.58 Buzzer Universal Interface
When the interface is defined, the implementation is all placed in the buzzer.c file, as shown in Listing 5.59.
Listing 5.59 Buzzer Universal Interface Implementation
This series features transparent LED screens, which are suitable for various indoor places and various stores, especially in storefronts with floor-to-ceiling glass windows. Installing the transparent screen against the glass wall has a very good effect. When the display is turned off, the audience can watch the indoor environment from the outdoors, and when the display is turned on, they can directly watch the content played on the display.It not only preserves the visibility in the store, but also can effectively display advertising videos to the other party, which is very practical.
Transparent Led Screen,Led Wall Display,Building Led Display Panel,Sport Led Screena
Guangzhou Cheng Wen Photoelectric Technology Co., Ltd. , https://www.cwdisplay.com