Dr.
Le Trong Nhan
Contents
Chapter 1. Buttons/Switches 7
1 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3 Basic techniques for reading from port pins . . . . . . . . . . . . . . . . . . . 9
3.1 The need for pull-up resistors . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 Dealing with switch bounces . . . . . . . . . . . . . . . . . . . . . . . . 10
4 Reading switch input (basic code) using STM32 . . . . . . . . . . . . . . . . . 13
4.1 Input Output Processing Patterns . . . . . . . . . . . . . . . . . . . . . 13
4.2 Setting up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2.1 Create a project . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2.2 Create a file C source file and header file for input reading . 14
4.3 Code For Read Port Pin and Debouncing . . . . . . . . . . . . . . . . . 16
4.3.1 The code in the input_reading.c file . . . . . . . . . . . . . . . 16
4.3.2 The code in the input_reading.h file . . . . . . . . . . . . . . . 17
4.3.3 The code in the timer.c file . . . . . . . . . . . . . . . . . . . . 17
4.4 Button State Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.4.1 Finite State Machine . . . . . . . . . . . . . . . . . . . . . . . . 18
4.4.2 The code for the FSM in the input_processing.c file . . . . . 19
4.4.3 The code in the input_processing.h . . . . . . . . . . . . . . . 19
4.4.4 The code in the main.c file . . . . . . . . . . . . . . . . . . . . 20
5 Exercises and Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.1 Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.2 Exercise 1: Sketch an FSM . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.3 Exercise 2: Proteus Schematic . . . . . . . . . . . . . . . . . . . . . . . 22
5.4 Exercise 3: Create STM32 Project . . . . . . . . . . . . . . . . . . . . . . 22
5.5 Exercise 4: Modify Timer Parameters . . . . . . . . . . . . . . . . . . . 22
5.6 Exercise 5: Adding code for button debouncing . . . . . . . . . . . . . 22
Microcontroller Page 5
5.7 Exercise 6: Adding code for displaying modes . . . . . . . . . . . . . . 23
5.8 Exercise 7: Adding code for increasing time duration value for the red
LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.9 Exercise 8: Adding code for increasing time duration value for the
amber LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.10 Exercise 9: Adding code for increasing time duration value for the
green LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.11 Exercise 10: To finish the project . . . . . . . . . . . . . . . . . . . . . . 24
Page 6 HCMUT - Computer Engineering
CHAPTER 1
Buttons/Switches
1 Objectives
In this lab, you will
• Learn how to add new C source files and C header files in an STM32 project,
• Learn how to read digital inputs and display values to LEDs using a timer interrupt
of a microcontroller (MCU).
• Learn how to debounce when reading a button.
• Learn how to create an FSM and implement an FSM in an MCU.
2 Introduction
Embedded systems usually use buttons (or keys, or switches, or any form of mechanical
contacts) as part of their user interface. This general rule applies from the most basic
remote-control system for opening a garage door, right up to the most sophisticated air-
craft autopilot system. Whatever the system you create, you need to be able to create a
reliable button interface.
A button is generally hooked up to an MCU so as to generate a certain logic level when
pushed or closed or “active" and the opposite logic level when unpushed or open or “in-
active." The active logic level can be either ‘0’ or ‘1’, but for reasons both historical and
electrical, an active level of ’0’ is more common.
We can use a button if we want to perform operations such as:
• Drive a motor while a switch is pressed.
• Switch on a light while a switch is pressed.
• Activate a pump while a switch is pressed.
These operations could be implemented using an electrical button without using an MCU;
however, use of an MCU may well be appropriate if we require more complex behaviours.
For example:
• Drive a motor while a switch is pressed.
Condition: If the safety guard is not in place, don’t turn the motor. Instead sound a
buzzer for 2 seconds.
• Switch on a light while a switch is pressed.
Condition: To save power, ignore requests to turn on the light during daylight hours.
• Activate a pump while a switch is pressed
Condition: If the main water reservoir is below 300 litres, do not start the main
pump: instead, start the reserve pump and draw the water from the emergency
tank.
In this lab, we consider how you read inputs from mechanical buttons in your embedded
application using an MCU.
Page 8 HCMUT - Computer Engineering
3 Basic techniques for reading from port pins
3.1 The need for pull-up resistors
Figure 1.1: Connecting a button to an MCU
Figure 1.1 shows a way to connect a button to an MCU. This hardware operates as follows:
• When the switch is open, it has no impact on the port pin. An internal resistor
on the port “pulls up" the pin to the supply voltage of the MCU (typically 3.3V for
STM32F103). If we read the pin, we will see the value ‘1’.
• When the switch is closed (pressed), the pin voltage will be 0V. If we read the pin, we
will see the value ‘0’.
However, if the MCU does not have a pull-up resistor inside, when the button is pressed,
the read value will be ‘0’, but even we release the button, the read value is still ‘0’ as shown
in Figure 1.2.
Figure 1.2: The need of pull up resistors
Microcontroller Page 9
So a reliable way to connect a button/switch to an MCU is that we explicitly use an exter-
nal pull-up resistor as shown in Figure 1.3.
Figure 1.3: A reliable way to connect a button to an MCU
3.2 Dealing with switch bounces
In practice, all mechanical switch contacts bounce (that is, turn on and off, repeatedly, for
short period of time) after the switch is closed or opened as shown in Figure 1.4.
Figure 1.4: Switch bounces
Every system that uses any kind of mechanical switch must deal with the issue of de-
bouncing. The key task is to make sure that one mechanical switch or button action is
only read as one action by the MCU, even though the MCU will typically be fast enough
to detect the unwanted switch bounces and treat them as separate events. Bouncing can
be eliminated by special ICs or by RC circuitry, but in most cases debouncing is done in
software because software is “free".
As far as the MCU concerns, each “bounce" is equivalent to one press and release of an
“ideal” switch. Without appropriate software design, this can give several problems:
• Rather than reading ‘A’ from a keypad, we may read ‘AAAAA’
• Counting the number of times that a switch is pressed becomes extremely difficult
Page 10 HCMUT - Computer Engineering
• If a switch is depressed once, and then released some time later, the ‘bounce’ may
make it appear as if the switch has been pressed again (at the time of release).
The key to debouncing is to establish a minimum criterion for a valid button push, one
that can be implemented in software. This criterion must involve differences in time - two
button presses in 20ms must be treated as one button event, while two button presses in
2 seconds must be treated as two button events. So what are the relevant times we need
to consider? They are these:
• Bounce time: most buttons seem to stop bouncing within 10ms
• Button press time: the shortest time a user can press and release a button seems to
be between 50 and 100ms
• Response time: a user notices if the system response is 100ms after the button press,
but not if it is 50ms after
Combining all of these times, we can set a few goals
• Ignore all bouncing within 10ms
• Provide a response within 50ms of detecting a button push (or release)
• Be able to detect a 50ms push and a 50ms release
The simplest debouncing method is to examine the keys (or buttons or switches) every N
milliseconds, where N > 10ms (our specified button bounce upper limit) and N <= 50ms
(our specified response time). We then have three possible outcomes every time we read
a button:
• We read the button in the solid ‘0’ state
• We read the button in the solid ‘1’ state
• We read the button while it is bouncing (so we will get either a ‘0’ or a ‘1’)
Outcomes 1 and 2 pose no problems, as they are what we would always like to happen.
Outcome 3 also poses no problem because during a bounce either state is acceptable. If
we have just pressed an active-low button and we read a ’1’ as it bounces, the next time
through we are guaranteed to read a ’0’ (remember, the next time through all bouncing
will have ceased), so we will just detect the button push a bit later. Otherwise, if we read a
’0’ as the button bounces, it will still be ’0’ the next time after all bouncing has stopped, so
we are just detecting the button push a bit earlier. The same applies to releasing a button.
Reading a single bounce (with all bouncing over by the time of the next read) will never
give us an invalid button state. It’s only reading multiple bounces (multiple reads while
bouncing is occurring) that can give invalid button states such as repeated push signals
from one physical push.
So if we guarantee that all bouncing is done by the time we next read the button, we’re
good. Well, almost good, if we’re lucky...
MCUs often live among high-energy beasts, and often control the beasts. High energy
devices make electrical noise, sometimes great amounts of electrical noise. This noise
Microcontroller Page 11
can, at the worst possible moment, get into your delicate button-and-high-value-pullup
circuit and act like a real button push. Oops, missile launched, sorry!
If the noise is too intense we cannot filter it out using only software, but will need hard-
ware of some sort (or even a redesign). But if the noise is only occasional, we can filter
it out in software without too much bother. The trick is that instead of regarding a sin-
gle button ‘make’ or ‘break’ as valid, we insist on N contiguous makes or breaks to mark
a valid button event. N will be a factor of your button scanning rate and the amount of
filtering you want to add. Bigger N gives more filtering. The simplest filter (but still a big
improvement over no filtering) is just an N of 2, which means compare the current button
state with the last button state, and only if both are the same is the output valid.
Note that now we have not two but three button states: active (or pressed), inactive (or
released), and indeterminate or invalid (in the middle of filtering, not yet filtered). In
most cases we can treat the invalid state the same as the inactive state, since we care in
most cases only about when we go active (from whatever state) and when we cease being
active (to inactive or invalid). With that simplification we can look at simple N = 2 filtering
reading a button wired to STM32 MCU:
1 void button_reading ( void ) {
2 static unsigned char last_button ;
3 unsigned char raw_button ;
4 unsigned char filtered_button ;
5 last_button = raw_button ;
6 raw_button = HAL_GPIO_ReadPin ( BUTTON_1_GPIO_Port ,
BUTTON_1_Pin ) ;
7 if ( last_button == raw_button ) {
8 filtered_button = raw_button ;
9 }
10 }
Program 1.1: Read port pin and deboucing
The function button_reading() must be called no more often than our debounce time
(10ms).
To expand to greater filtering (larger N), keep in mind that the filtering technique essen-
tially involves reading the current button state and then either counting or reseting the
counter. We count if the current button state is the same as the last button state, and if
our count reaches N we then report a valid new button state. We reset the counter if the
current button state is different than the last button state, and we then save the current
button state as the new button state to compare against the next time. Also note that the
larger our value of N the more often our filtering routine must be called, so that we get a
filtered response within our specified 50ms deadline. So for example with an N of 8 we
should be calling our filtering routine every 2 - 5ms, giving a response time of 16 - 40ms
(>10ms and <50ms).
Page 12 HCMUT - Computer Engineering
4 Reading switch input (basic code) using STM32
To demonstrate the use of buttons/switches in STM32, we use an example which requires
to write a program that
• Has a timer which has an interrupt in every 10 milliseconds.
• Reads values of button PB0 every 10 milliseconds.
• Increases the value of LEDs connected to PORTA by one unit when the button PB0
is pressed.
• Increases the value of PORTA automatically in every 0.5 second, if the button PB0 is
pressed in more than 1 second.
4.1 Input Output Processing Patterns
For both input and output processing, we have a similar pattern to work with. Normally,
we have a module named driver which works directly to the hardware. We also have a
buffer to store temporarily values. In the case of input processing, the driver will store
the value of the hardware status to the buffer for further processing. In the case of output
processing, the driver uses the buffer data to output to the hardware.
Figure 1.5: Input Output Processing Patterns
Figure 1.5 shows that we should have an input_reading module to processing the buttons,
then store the processed data to the buffer. Then a module of input_output_processing_fsm
will process the input data, and update the output buffer. The output driver gets the value
from the output buffer to transfer to the hardware.
Microcontroller Page 13
4.2 Setting up
4.2.1 Create a project
Please follow the instruction in Labs 1 and 2 to create a project that includes:
• PB0 as an input port pin,
• PA0-PA7 as output port pins, and
• Timer 2 10ms interrupt
4.2.2 Create a file C source file and header file for input reading
We are expected to have files for button processing and led display as shown in Figure 1.6.
Figure 1.6: File Organization
Steps 1 (Figure 1.7): Right click to the folder Src, select New, then select Source File. There
will be a pop-up. Please type the file name, then click Finish.
Step 2 (Figure 1.8): Do the same for the C header file in the folder Inc.
Page 14 HCMUT - Computer Engineering
Figure 1.7: Step 1: Create a C source file for input reading
Figure 1.8: Step 2: Create a C header file for input processing
Microcontroller Page 15
4.3 Code For Read Port Pin and Debouncing
4.3.1 The code in the input_reading.c file
1 # include " main . h "
2 // we aim to work with more than one buttons
3 # define N0_OF_BUTTONS 1
4 // timer interrupt duration is 10 ms , so to pass 1 second ,
5 // we need to jump to the interrupt service routine 100 time
6 # define D URATION_FOR_AUTO_INCREASING 100
7 # define BUTTON_IS_PRESSED GPIO_PIN_RESET
8 # define BUTTON_IS_RELEASED GPIO_PIN_SET
9 // the buffer that the final result is stored after
10 // debouncing
11 static GPIO_PinState buttonBuffer [ N0_OF_BUTTONS ];
12 // we define two buffers for debouncing
13 static GPIO_PinState debounceButtonBuffer1 [ N0_OF_BUTTONS ];
14 static GPIO_PinState debounceButtonBuffer2 [ N0_OF_BUTTONS ];
15 // we define a flag for a button pressed more than 1 second .
16 static uint8_t flagForButtonPress1s [ N0_OF_BUTTONS ];
17 // we define counter for automatically increasing the value
18 // after the button is pressed more than 1 second .
19 static uint16_t counterForButtonPress1s [ N0_OF_BUTTONS ];
20 void button_reading ( void ) {
21 for ( char i = 0; i < N0_OF_BUTTONS ; i ++) {
22 debounceButtonBuffer2 [ i ] = debounceButtonBuffer1 [ i ];
23 debounceButtonBuffer1 [ i ] = HAL_GPIO_ReadPin (
BUTTON_1_GPIO_Port , BUTTON_1_Pin ) ;
24 if ( debounceButtonBuffer1 [ i ] == debounceButtonBuffer2 [ i
])
25 buttonBuffer [ i ] = debounceButtonBuffer1 [ i ];
26 if ( buttonBuffer [ i ] == BUTTON_IS_PRESSED ) {
27 // if a button is pressed , we start counting
28 if ( counterForButtonPress1s [ i ] <
DU RAT ION_FOR_AUTO_INCREASING ) {
29 counterForButtonPress1s [ i ]++;
30 } else {
31 // the flag is turned on when 1 second has passed
32 // since the button is pressed .
33 flagForButtonPress1s [ i ] = 1;
34 // todo
35 }
36 } else {
37 counterForButtonPress1s [ i ] = 0;
38 flagForButtonPress1s [ i ] = 0;
39 }
40 }
41 }
Program 1.2: Define constants buffers and button_reading function
Page 16 HCMUT - Computer Engineering
1 unsigned char is_button_pressed ( uint8_t index ) {
2 if ( index >= N0_OF_BUTTONS ) return 0;
3 return ( buttonBuffer [ index ] == BUTTON_IS_PRESSED ) ;
4 }
Program 1.3: Checking a button is pressed or not
1 unsigned char is_button_pressed_1s ( unsigned char index ) {
2 if ( index >= N0_OF_BUTTONS ) return 0 xff ;
3 return ( flagForButtonPress1s [ index ] == 1) ;
4 }
Program 1.4: Checking a button is pressed more than a second or not
4.3.2 The code in the input_reading.h file
1 # ifndef INC_INPUT_READING_H_
2 # define INC_INPUT_READING_H_
3 void button_reading ( void ) ;
4 unsigned char is_button_pressed ( unsigned char index ) ;
5 unsigned char is_button_pressed_1s ( unsigned char index ) ;
6 # endif /* INC_INPUT_READING_H_ */
Program 1.5: Prototype in input_reading.h file
4.3.3 The code in the timer.c file
1 # include " main . h "
2 # include " input_reading . h "
3
4 void HAL_TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim )
5 {
6 if ( htim - > Instance == TIM2 ) {
7 button_reading () ;
8 }
9 }
Program 1.6: Timer interrupt callback function
Microcontroller Page 17
4.4 Button State Processing
4.4.1 Finite State Machine
To solve the example problem, we define 3 states as follows:
• State 0: The button is released or the button is in the initial state.
• State 1: When the button is pressed, the FSM will change to State 1 that is increasing
the values of PORTA by one value. If the button is released, the FSM goes back to
State 0.
• State 2: while the FSM is in State 1, the button is kept pressing more than 1 second,
the state of FSM will change from 1 to 2. In this state, if the button is kept pressing,
the value of PORTA will be increased automatically in every 500ms. If the button is
released, the FSM goes back to State 0.
Figure 1.9: An FSM for processing a button
Page 18 HCMUT - Computer Engineering
4.4.2 The code for the FSM in the input_processing.c file
Please note that fsm_for_input_processing function should be called inside the super loop
of the main functin.
1 # include " main . h "
2 # include " input_reading . h "
3
4 enum ButtonState { BUTTON_RELEASED , BUTTON_PRESSED ,
B UTTON_PRESSED_MORE_THAN_1_SECOND } ;
5 enum ButtonState buttonState = BUTTON_RELEASED ;
6 void fsm_for_input_processing ( void ) {
7 switch ( buttonState ) {
8 case BUTTON_RELEASED :
9 if ( is_button_pressed (0) ) {
10 buttonState = BUTTON_PRESSED ;
11 // INCREASE VALUE OF PORT A BY ONE UNIT
12 }
13 break ;
14 case BUTTON_PRESSED :
15 if (! is_button_pressed (0) ) {
16 buttonState = BUTTON_RELEASED ;
17 } else {
18 if ( is_button_pressed_1s (0) ) {
19 buttonState = BUTTON_PRESSED_MORE_THAN_1_SECOND ;
20 }
21 }
22 break ;
23 case BUTTON_PRESSED_MORE_THAN_1_SECOND :
24 if (! is_button_pressed (0) ) {
25 buttonState = BUTTON_RELEASED ;
26 }
27 // todo
28 break ;
29 }
30 }
Program 1.7: The code in the input_processing.c file
4.4.3 The code in the input_processing.h
1 # ifndef INC_INPUT_PROCESSING_H_
2 # define INC_INPUT_PROCESSING_H_
3
4 void fsm_for_input_processing ( void ) ;
5
6 # endif /* INC_INPUT_PROCESSING_H_ */
Program 1.8: Code in the input_processing.h file
Microcontroller Page 19
4.4.4 The code in the main.c file
1 # include " main . h "
2 # include " input_processing . h "
3 // don ’t modify this part
4 int main ( void ) {
5 HAL_Init () ;
6 /* Configure the system clock */
7 SystemClock_Config () ;
8 /* Initialize all configured peripherals */
9 MX_GPIO_Init () ;
10 MX_TIM2_Init () ;
11 while (1)
12 {
13 // you only need to add the fsm function here
14 fsm_for_input_processing () ;
15 }
16 }
Program 1.9: The code in the main.c file
Page 20 HCMUT - Computer Engineering
5 Exercises and Report
5.1 Specifications
You are required to build an application of a traffic light in a cross road which includes
some features as described below:
• The application has 12 LEDs including 4 red LEDs, 4 amber LEDs, 4 green LEDs.
• The application has 4 seven segment LEDs to display time with 2 for each road. The
2 seven segment LEDs will show time for each color LED corresponding to each
road.
• The application has three buttons which are used
- to select modes,
- to modify the time for each color led on the fly, and
- to set the chosen value.
• The application has at least 4 modes which is controlled by the first button. Mode
1 is a normal mode, while modes 2 3 4 are modification modes. You can press the
first button to change the mode. Modes will change from 1 to 4 and back to 1 again.
Mode 1 - Normal mode:
- The traffic light application is running normally.
Mode 2 - Modify time duration for the red LEDs: This mode allows you to change
the time duration of the red LED in the main road. The expected behaviours of this
mode include:
- All single red LEDs are blinking in 2 Hz.
- Use two seven-segment LEDs to display the value.
- Use the other two seven-segment LEDs to display the mode.
- The second button is used to increase the time duration value for the red LEDs.
- The value of time duration is in a range of 1 - 99.
- The third button is used to set the value.
Mode 3 - Modify time duration for the amber LEDs: Similar for the red LEDs de-
scribed above with the amber LEDs.
Mode 4 - Modify time duration for the green LEDs: Similar for the red LEDs de-
scribed above with the green LEDs.
Microcontroller Page 21
5.2 Exercise 1: Sketch an FSM
Your task in this exercise is to sketch an FSM that describes your idea of how to solve the
problem.
Please add your report here.
5.3 Exercise 2: Proteus Schematic
Your task in this exercise is to draw a Proteus schematic for the problem above.
Please add your report here.
5.4 Exercise 3: Create STM32 Project
Your task in this exercise is to create a project that has pin corresponding to the Proteus
schematic that you draw in previous section. You need to set up your timer interrupt is
about 10ms.
Please add your report here.
5.5 Exercise 4: Modify Timer Parameters
Your task in this exercise is to modify the timer settings so that when we want to change
the time duration of the timer interrupt, we change it the least and it will not affect the
overall system. For example, the current system we have implemented is that it can blink
an LED in 2 Hz, with the timer interrupt duration is 10ms. However, when we want to
change the timer interrupt duration to 1ms or 100ms, it will not affect the 2Hz blinking
LED.
Please add your report here.
5.6 Exercise 5: Adding code for button debouncing
Following the example of button reading and debouncing in the previous section, your
tasks in this exercise are:
• To add new files for input reading and output display,
• To add code for button debouncing,
• To add code for increasing mode when the first button is pressed.
Please add your report here.
Page 22 HCMUT - Computer Engineering
5.7 Exercise 6: Adding code for displaying modes
Your tasks in this exercise are:
• To add code for display mode on seven-segment LEDs, and
• To add code for blinking LEDs depending on the mode that is selected.
Please add your report here.
5.8 Exercise 7: Adding code for increasing time duration value for the
red LEDs
Your tasks in this exercise are:
• to use the second button to increase the time duration value of the red LEDs
• to use the third button to set the value for the red LEDs.
Please add your report here.
5.9 Exercise 8: Adding code for increasing time duration value for the
amber LEDs
Your tasks in this exercise are:
• to use the second button to increase the time duration value of the amber LEDs
• to use the third button to set the value for the amber LEDs.
Please add your report here.
5.10 Exercise 9: Adding code for increasing time duration value for the
green LEDs
Your tasks in this exercise are:
• to use the second button to increase the time duration value of the green LEDs
• to use the third button to set the value for the green LEDs.
Please add your report here.
Microcontroller Page 23
5.11 Exercise 10: To finish the project
Your tasks in this exercise are:
• To integrate all the previous tasks to one final project
• To create a video to show all features in the specification
• To add a report to describe your solution for each exercise.
• To submit your report and code on the BKeL
Page 24 HCMUT - Computer Engineering