Microcontroller 1
Microcontroller 1
Le Trong Nhan
Mục lục
Microcontroller Page 5
3.8 Exercise 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.9 Exercise 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.10 Exercise 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Chapter 3. Buttons/Switches 41
1 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3 Basic techniques for reading from port pins . . . . . . . . . . . . . . . 44
3.1 The need for pull-up resistors . . . . . . . . . . . . . . . . . . . . 44
3.2 Dealing with switch bounces . . . . . . . . . . . . . . . . . . . . 44
4 Reading switch input (basic code) using STM32 . . . . . . . . . . . . . 49
4.1 Input Output Processing Patterns . . . . . . . . . . . . . . . . . 49
4.2 Setting up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.2.1 Create a project . . . . . . . . . . . . . . . . . . . . . . . 50
4.2.2 Create a file C source file and header file for input
reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.3 Code For Read Port Pin and Debouncing . . . . . . . . . . . . . 52
4.3.1 The code in the input_reading.c file . . . . . . . . . . 52
4.3.2 The code in the input_reading.h file . . . . . . . . . . 53
4.3.3 The code in the timer.c file . . . . . . . . . . . . . . . . 53
4.4 Button State Processing . . . . . . . . . . . . . . . . . . . . . . . 54
4.4.1 Finite State Machine . . . . . . . . . . . . . . . . . . . . 54
4.4.2 The code for the FSM in the input_processing.c file 55
4.4.3 The code in the input_processing.h . . . . . . . . . . 55
4.4.4 The code in the main.c file . . . . . . . . . . . . . . . . 56
5 Exercises and Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.1 Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.2 Exercise 1: Sketch an FSM . . . . . . . . . . . . . . . . . . . . . . 58
5.3 Exercise 2: Proteus Schematic . . . . . . . . . . . . . . . . . . . . 58
5.4 Exercise 3: Create STM32 Project . . . . . . . . . . . . . . . . . . 58
5.5 Exercise 4: Modify Timer Parameters . . . . . . . . . . . . . . . 58
5.6 Exercise 5: Adding code for button debouncing . . . . . . . . . 58
5.7 Exercise 6: Adding code for displaying modes . . . . . . . . . . 59
5.8 Exercise 7: Adding code for increasing time duration value for
the red LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
Microcontroller Page 7
3 Project configurations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.1 UART Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.2 ADC Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
4 UART loop-back communication . . . . . . . . . . . . . . . . . . . . . . 85
5 Sensor reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6 Project description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.1 Command parser . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.2 Project implementation . . . . . . . . . . . . . . . . . . . . . . . 88
LED Animations
1 Introduction
In this manual, the STM32CubeIDE is used as an editor to program the ARM micro-
controller. STM32CubeIDE is an advanced C/C++ development platform with pe-
ripheral configuration, code generation, code compilation, and debug features for
STM32 microcontrollers and microprocessors.
The most interest of STM32CubeIDE is that after the selection of an empty STM32
MCU or MPU, or preconfigured microcontroller or microprocessor from the selec-
tion of a board, the initialization code generated automatically. At any time during
the development, the user can return to the initialization and configuration of the
peripherals or middleware and regenerate the initialization code with no impact
on the user code. This feature can simplify the initialization process and speedup
the development application running on STM32 micro-controller. The software
can be downloaded from the link bellow:
https://ubc.sgp1.digitaloceanspaces.com/BKU_Softwares/STM32/stm32cubeide_1.7.0.zip
Moreover, for a hangout class, the program is firstly simulated on Proteus. Students
are also supposed to download and install this software as well:
https://ubc.sgp1.digitaloceanspaces.com/BKU_Softwares/STM32/Proteus_8.10_SP0_Pro.exe
Step 1: Launch STM32CubeIDE, from the menu File, select New, then chose STM32
Project
The IDE needs to download some packages, which normally takes time in this first
time a project is created.
Step 2: Select the STM32F103C6 in the following dialog, then click on Next
Step 3: Provide the Name and the Location for the project.
Microcontroller Page 11
Hình 1.4: Select the target device
Step 4: On the last dialog, just keep the default firmware version and click on Fin-
ish button.
Step 5: The project is created and the wizard for configuration is display. This
utility from CubeIDE can simplify the configuration process for an ARM micro-
controller like the STM32.
From the configuration windows, select Pin configuration, select the pin PA5 and
set to GPIO Output mode, since this pin is connected to an LED in the STM32 de-
velopment kit.
Step 6: Right click on PA5 and select Enter user lable, and provide the name for
this pin (e.g. LED_RED). This step helps programming afterward more memo-
rable.
Finally, save the configuration process by pressing Ctrl + S and confirm this step
by clicking on OK button. The code generation is started.
Step 7: Implement the first blinky project in the main function as follow:
1 int main ( void )
2 {
3 /* USER CODE BEGIN 1 */
4
Microcontroller Page 13
Hình 1.7 : Provide a name for PA5
7 /* MCU Configuration
--------------------------------------------------------
*/
8
29 /* Infinite loop */
30 /* USER CODE BEGIN WHILE */
31
32 while (1)
33 {
Actually, what is added to the main function is line number 34 and 35. Please put
your code in a right place, otherwise it can be deleted when the code is gener-
ated (e.g. change the configuration of the project). When coding, frequently use
the suggestions by pressing Ctrl+Space.
Step 7: Due to the simulation on Proteus, the hex file should be generated from
STM32Cube IDE. From menu Project, select Properties to open the dialog bellow:
Navigate to C/C++ Build, select Settings, MCU Post build outputs, and check to
the Intel Hex file.
Step 8: Build the project by clicking on menu Project and select Build Project.
Please check on the output console of the IDE to be sure that the hex file is gener-
ated, as follow:
Microcontroller Page 15
The hex file is located under the Debug folder of your project, which is used for
the simulation in Proteus afterward. In the case a development kit is connected to
your PC, from menu Run, select Run to download the program to the hardware
platform.
In the case there are multiple project in a work-space, double click on the project
name to activate this project. Whenever a project is built, check the output files to
make sure that you are working in a right project.
For an online training, a simulation on Proteus can be used. The details to create
an STM32 project on Proteus are described bellow.
Step 1: Launch Proteus (with administration access) and from menu File, select
New Project.
Step 2: Provide the name and the location of the project, then click on Next button.
Step 3: For following dialog, just click on Next button as just a schematic is re-
quired for the lab.
Microcontroller Page 17
Step 4: Finally, click on Finish button to close the project wizard.
Step 5: On the main page of the project, right click to select Place, Components,
From Libraries, as follows:
If there is an error with no library found, please restart the Proteus software with
Run as administrator option.
Repeat step 5 and 6 to select an LED, named LED-RED in Proteus. Finally, these
components are appeared on the DEVICES windows, which is on left hand side as
follows:
Step 7: Place the components to the project: right click on the main page, select
on Place, Component, and select device added in Step 6. To add the Power and the
Ground, right click on the main page, select on Place, Terminal. The result in this
step is expected as follows:
Microcontroller Page 19
Hình 1.17 : Place components to the project
Step 8: Start wiring the circuit. The negative pin of the LED is connected to PA5
while its positive pin is connected to the power supply. For the power and the
ground on the right, just make a short wire, which will labeled in the next step.
In this step, also double click on the power supply in order to provide the String
property to +3.3V.
This step is required as VDDA and VSSA of the STM32 must be connected to pro-
vide the reference voltage. Therefore, VDDA is connected to 3.3V, while the VSSA is
connected to the Ground. Finally, the image of our schematic is shown bellow:
Microcontroller Page 21
Step 9: Double click on the STM32, and set the Program File to the Hex file, which
is generated from Cube IDE, as following:
Hình 1.21: Set the program of the STM32 to the hex file from Cube IDE
From now, the simulation is ready to start by clicking on the menu Debug, and
select on Run simulation. To stop the simulation, click on Debug and select Stop
VMS Debugging. Moreover, there are some quick access bottom on the left corner
of the Proteus to start or stop the simulation, as shown following:
Hình 1.22: Quick access buttons to start and stop the simulation
If everything is success, students can see the LED is blinking every second. Please
stop the simulation before updating the project, either in Proteus or STM32Cube
IDE. However, the step 9 (set the program file for STM32 in Proteus) is required to
do once. Beside the toggle instruction, student can set or reset a pin as following:
1 while (1) {
2 HAL_GPIO_WritePin ( LED_RED_GPIO_Port , LED_RED_Pin ,
GPIO_PIN_SET ) ;
3 HAL_Delay (1000) ;
4 HAL_GPIO_WritePin ( LED_RED_GPIO_Port , LED_RED_Pin ,
GPIO_PIN_RESET ) ;
5 HAL_Delay (1000) ;
6 }
Program 1.2: An example for LED blinky
4.1 Exercise 1
From the simulation on Proteus, one more LED is connected to pin PA6 of the
STM32 (negative pin of the LED is connected to PA6). The component suggested
in this exercise is LED-YELLOW, which can be found from the device list.
In this exercise, the status of two LEDs are switched every 2 seconds, as demon-
strated in the figure bellow.
Report 1: Depict the schematic from Proteus simulation in this report. The caption
of the figure is a downloadable link to the Proteus project file (e.g. a github link).
Report 2: Present the source code in the infinite loop while of your project. If a
user-defined functions is used, it is required to present in this part. A brief descrip-
tion can be added for this function (e.g. using comments). A template to present
your source code is presented bellow.
1 while (1) {
2 HAL_GPIO_TogglePin ( GPIOA , GPIO_PIN_5 ) ;
3 HAL_Delay (1000) ;
4 }
Program 1.3: An example for your source code
4.2 Exercise 2
Extend the first exercise to simulate the behavior of a traffic light. A third LED,
named LED-GREEN is added to the system, which is connected to PA7. A cycle in
this traffic light is 5 seconds for the RED, 2 seconds for the YELLOW and 3 seconds
for the GREEN. The LED-GREEN is also controlled by its negative pin.
Similarly, the report in this exercise includes the schematic of your circuit and a
your source code in the while loop.
Report 1: Present the schematic.
Microcontroller Page 23
Report 2: Present the source code in while.
4.3 Exercise 3
Extend to the 4-way traffic light. Arrange 12 LEDs in a nice shape to simulate the
behaviors of a traffic light. A reference design can be found in the figure bellow.
4.4 Exercise 4
Add only one 7 led segment to the schematic in Exercise 3. This component can
be found in Proteus by the keyword 7SEG-COM-ANODE. For this device, the com-
mon pin should be connected to the power supply and other pins are supposed to
connected to PB0 to PB6. Therefore, to turn-on a segment in this 7SEG, the STM32
pin should be in logic 0 (0V).
Implement a function named display7SEG(int num). The input for this function
is from 0 to 9 and the outputs are listed as following:
7 }
Program 1.4: An example for your source code
4.5 Exercise 5
Integrate the 7SEG-LED to the 4 way traffic light. In this case, the 7SEG-LED is used
to display countdown value.
In this exercise, only source code is required to present. The function display7SEG
in previous exercise can be re-used.
4.6 Exercise 6
Microcontroller Page 25
Hình 1.26: 12 LEDs for an analog clock
4.7 Exercise 7
Implement a function named clearAllClock() to turn off all 12 LEDs. Present the
source code of this function.
1 void clearAllClock () {
2 // TODO
3 }
Program 1.5: Function Implementation
4.8 Exercise 8
4.9 Exercise 9
4.10 Exercise 10
Integrate the whole system and use 12 LEDs to display a clock. At a given time,
there are only 3 LEDs are turn on for hour, minute and second information.
Timers are one of the most important features in modern micro-controllers. They
allow us to measure how long something takes to execute, create non-blocking
code, precisely control pin timing, and even run operating systems. In this manual,
how to configure a timer using STM32CubeIDE is presented how to use them to
flash an LED. Finally, students are proposed to finalize 10 exercises using timer
interrupt for applications based LED Scanning.
Design an interface for with multiple LED (seven segment or matrix) displays which
is to be controlled is depends on the number of input and output pins needed for
controlling all the LEDs in the given matrix display, the amount of current that
each pin can source and sink and the speed at which the micro-controller can
send out control signals. With all these specifications, interfacing can be done for
4 seven segment LEDs with a micro-controller is proposed in the figure above.
In the above diagram each seven segment display is having 8 internal LEDs, lead-
ing to the total number of LEDs is 32. However, not all the LEDs are required to
turn ON, but one of them is needed. Therefore, only 12 lines are needed to control
the whole 4 seven segment LEDs. By controlling with the micro-controller, we can
turn ON an LED during a same interval TS . Therfore, the period for controlling all
4 seven segment LEDs is 4TS . In other words, these LEDs are scanned at frequecy
f = 1/4TS . Finally, it is obviously that if the frequency is greater than 30Hz (e.g. f =
50Hz), it seems that all LEDs are turn ON at the same time.
In this manual, the timer interrupt is used to design the interval TS for LED scan-
ning. Unfortunately, the simulation on Proteus can not execute at high frequency,
the frequency f is set to a low value (e.g. 1Hz). In a real implementation, this fre-
Microcontroller Page 29
2 Timer Interrupt Setup
Step 1: Create a simple project, which LED connected to PA5. The manual can be
found in the first lab.
Step 2: Check the clock source of the system on the tab Clock Configuration (from
*.ioc file). In the default configuration, the internal clock source is used with 8MHz,
as shown in the figure bellow.
Select the clock source for timer 2 to the Internal Clock. Finally, set the prescaller
and the counter to 7999 and 9, respectively. These values are explained as follows:
• The clock source is 8MHz, by setting the prescaller to 7999, the input clock
source to the timer is 8MHz/(7999+1) = 1000Hz.
• The interrupt is raised when the timer counter is counted from 0 to 9, mean-
ing that the frequency is divided by 10, which is 100Hz.
• The frequency of the timer interrupt is 100Hz, meaning that the period is
1/100Hz = 10ms.
Step 4: Enable the timer interrupt by switching to NIVC Settings tab, as follows:
Step 5: On the main() function, call the timer init function, as follows:
1 int main ( void )
2 {
3 HAL_Init () ;
4 SystemClock_Config () ;
5
6 MX_GPIO_Init () ;
7 MX_TIM2_Init () ;
8
13 while (1) {
14
15 }
16 }
Program 2.1: Init the timer interrupt in main
Please put the init function in a right place to avoid conflicts when code genera-
tion is executed (e.g. ioc file is updated).
Microcontroller Page 31
Step 6: Add the interrupt service routine function, this function is invoked every
10ms, as follows:
1 /* USER CODE BEGIN 4 */
2 void HA L_ TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim )
{
3
4 }
5 /* USER CODE END 4 */
Program 2.2: Add an interrupt service routine
Step 7: To run a LED Blinky demo using interrupt, a short manual is presented as
follows:
1 /* USER CODE BEGIN 4 */
2 int counter = 100;
3 void HA L_ TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim )
{
4 counter - -;
5 if ( counter <= 0) {
6 counter = 100;
7 HAL_GPIO_TogglePin ( LED_RED_GPIO_Port , LED_RED_Pin ) ;
8 }
9 }
10 /* USER CODE END 4 */
Program 2.3: LED Blinky using timer interrupt
3.1 Exercise 1
The first exercise show how to interface for multiple seven segment LEDs to STM32F103C6
micro-controller (MCU). Seven segment displays are common anode type, mean-
ing that the anode of all LEDs are tied together as a single terminal and cathodes
are left alone as individual pins.
In order to save the resource of the MCU, individual cathode pins from all the
seven segment LEDs are connected together, and connect to 7 pins of the MCU.
These pins are popular known as the signal pins. Meanwhile, the anode pin of
each seven segment LEDs are controlled under a power enabling circuit, for in-
stance, an PNP transistor. At a given time, only one seven segment LED is turned
on. However, if the delay is small enough, it seems that all LEDs are enabling.
Implement the circuit simulation in Proteus with two 7-SEGMENT LEDs as follow-
ing:
• LED-RED
• PNP
• RES
• STM32F103C6
Microcontroller Page 33
Students are proposed to use the function display7SEG(int num) in the Lab 1 in
this exercise. Implement the source code in the interrupt callback function to dis-
play number "1" on the first seven segment and number "2" for second one. The
switching time between 2 LEDs is half of second.
Report 1: Capture your schematic from Proteus and show in the report.
3.2 Exercise 2
Extend to 4 seven segment LEDs and two LEDs (connected to PA4, labeled as DOT)
in the middle as following:
Blink the two LEDs every second. Meanwhile, number 3 is displayed on the third
seven segment and number 0 is displayed on the last one (to present 12 hour and
a half ). The switching time for each seven segment LED is also a half of second
(500ms). Implement your code in the timer interrupt function.
Report 1: Capture your schematic from Proteus and show in the report.
Microcontroller Page 35
1 const int MAX_LED = 4;
2 int index_led = 0;
3 int led_buffer [4] = {1 , 2 , 3 , 4};
4 void update7SEG ( int index ) {
5 switch ( index ) {
6 case 0:
7 // Display the first 7 SEG with led_buffer [0]
8 break ;
9 case 1:
10 // Display the second 7 SEG with led_buffer [1]
11 break ;
12 case 2:
13 // Display the third 7 SEG with led_buffer [2]
14 break ;
15 case 3:
16 // Display the forth 7 SEG with led_buffer [3]
17 break ;
18 default :
19 break ;
20 }
21 }
Program 2.4: An example for your source code
Students are proposed to change the values in the led_buffer array for unit test
this function, which is used afterward.
3.4 Exercise 4
Change the period of invoking update7SEG function in order to set the frequency
of 4 seven segment LEDs to 1Hz. The DOT is still blinking every second.
3.5 Exercise 5
Implement a digital clock with hour and minute information displayed by 2 seven
segment LEDs. The code skeleton in the main function is presented as follows:
3 while (1) {
4 second ++;
5 if ( second >= 60) {
6 second = 0;
7 minute ++;
8 }
9 if ( minute >= 60) {
10 minute = 0;
11 hour ++;
12 }
13 if ( hour >=24) {
14 hour = 0;
15 }
16 updateClockBuffer () ;
17 HAL_Delay (1000) ;
18 }
Program 2.5: An example for your source code
The function updateClockBuffer will generate values for the array led_buffer ac-
cording to the values of hour and minute. In the case these values are 1 digit num-
ber, digit 0 is added.
3.6 Exercise 6
The main target from this exercise to reduce the complexity (or reduce code pro-
cessing) in the timer interrupt. The time consumed in the interrupt can lead to the
nested interrupt issue, which can crash the whole system. A simple solution can
disable the timer whenever the interrupt occurs, the enable it again. However, the
real-time processing is not guaranteed anymore.
In this exercise, a software timer is created and its counter is count down every
timer interrupt is raised (every 10ms). By using this timer, the Hal_Delay(1000)
in the main function is removed. In a MCU system, non-blocking delay is better
than blocking delay. The details to create a software timer are presented bellow.
The source code is added to your current program, do not delete the source code
you have on Exercise 5.
Microcontroller Page 37
6 timer0_counter = duration / TIMER_CYCLE ;
7 timer0_flag = 0;
8 }
9 void timer_run () {
10 if ( timer0_counter > 0) {
11 timer0_counter - -;
12 if ( timer0_counter == 0) timer0_flag = 1;
13 }
14 }
15 /* USER CODE END 0 */
Program 2.6: Software timer based timer interrupt
Please change the TIMER_CYCLE to your timer interrupt period. In the manual
code above, it is 10ms.
3 timer_run () ;
4
Step 3: Use the timer in the main function by invoked setTimer0 function, then
check for its flag (timer0_flag). An example to blink an LED connected to PA5 using
software timer is shown as follows:
1 setTimer0 (1000) ;
2 while (1) {
3 if ( timer0_flag == 1) {
4 HAL_GPIO_TogglePin ( LED_RED_GPIO_Port , LED_RED_Pin ) ;
5 setTimer0 (2000) ;
6 }
7 }
Program 2.8: Software timer is used in main fuction to blink the LED
Report 1: if in line 1 of the code above is miss, what happens after that and why?
Upgrade the source code in Exercise 5 (update values for hour, minute and sec-
ond) by using the software timer and remove the HAL_Delay function at the end.
Moreover, the DOT (connected to PA4) of the digital clock is also moved to main
function.
Report 1: Present your source code in the while loop on main function.
3.8 Exercise 8
Move also the update7SEG() function from the interrupt timer to the main. Finally,
the timer interrupt only used to handle software timers. All processing (or complex
computations) is move to an infinite loop on the main function, optimizing the
complexity of the interrupt handler function.
Report 1: Present your source code in the the main function. In the case more extra
functions are used (e.g. the second software timer), present them in the report as
well.
3.9 Exercise 9
This is an extra works for this lab. A LED Matrix is added to the system. A reference
design is shown in figure bellow:
In this schematic, two new components are added, including the MATRIX-8X8-
RED and ULN2803, which is an NPN transistor array to enable the power supply
for a column of the LED matrix. Students can change the enable signal (from ENM0
to ENM7) if needed. Finally, the data signal (from ROW0 to ROW7) is connected to
PB8 to PB15.
Microcontroller Page 39
Report 1: Present the schematic of your system by capturing the screen in Proteus.
Student are free to choose the invoking frequency of this function. However, this
function is supposed to invoked in main function. Finally, please update the ma-
trix_buffer to display character "A".
3.10 Exercise 10
Create an animation on LED matrix, for example, the character is shifted to the
left.
Report 1: Briefly describe your solution and present your source code in the re-
port.
Buttons/Switches
1 Objectives
• 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.
2 Introduction
Embedded systems usually use buttons (or keys, or switches, or any form of me-
chanical 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 aircraft 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 “inactive." 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:
Microcontroller Page 43
3 Basic techniques for reading from port pins
Figure 6.2 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 3.2.
So a reliable way to connect a button/switch to an MCU is that we explicitly use an
external pull-up resistor as shown in Figure 3.3.
In practice, all mechanical switch contacts bounce (that is, turn on and off, re-
peatedly, for short period of time) after the switch is closed or opened as shown in
Figure 3.4.
Every system that uses any kind of mechanical switch must deal with the issue of
debouncing. 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:
Microcontroller Page 45
• Rather than reading ‘A’ from a keypad, we may read ‘AAAAA’
• 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:
• 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
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 out-
comes every time we read a button:
• 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
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
essentially 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 com-
pare 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
Microcontroller Page 47
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).
• 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.
For both input and output processing, we have a similar pattern to work with. Nor-
mally, 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 process-
ing. In the case of output processing, the driver uses the buffer data to output to
the hardware.
Figure 3.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_processin
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 49
4.2 Setting up
Please follow the instruction in Labs 1 and 2 to create a project that includes:
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 3.6.
Steps 1 (Figure 3.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 3.8): Do the same for the C header file in the folder Inc.
Microcontroller Page 51
4.3 Code For Read Port Pin and Debouncing
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 3.5: Prototype in input_reading.h file
Microcontroller Page 53
4.4 Button State Processing
• 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.
Please note that fsm_for_input_processing function should be called inside the su-
per loop of the main functin.
1 # include " main . h "
2 # include " input_reading . h "
3
1 # ifndef INC_INPUT_PROCESSING_H_
2 # define INC_INPUT_PROCESSING_H_
3
6 # endif /* INC_INPUT_PROCESSING_H_ */
Program 3.8: Code in the input_processing.h file
Microcontroller Page 55
4.4.4 The code in the main.c file
5.1 Specifications
You are required to build an application of a traffic light in a cross road which in-
cludes 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 corre-
sponding to each road.
• 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 be-
haviours 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
described above with the amber LEDs.
Mode 4 - Modify time duration for the green LEDs: Similar for the red LEDs
described above with the green LEDs.
Microcontroller Page 57
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.
Your task in this exercise is to draw a Proteus schematic for the problem above.
Please add your report here.
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.
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 imple-
mented 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.
Following the example of button reading and debouncing in the previous section,
your tasks in this exercise are:
• To add code for increasing mode when the first button is pressed.
• To add code for blinking LEDs depending on the mode that is selected.
5.8 Exercise 7: Adding code for increasing time duration value for
the red LEDs
• 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.
5.9 Exercise 8: Adding code for increasing time duration value for
the amber LEDs
• 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.
• 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.
Microcontroller Page 59
5.11 Exercise 10: To finish the project
A cooperative scheduler
1 Introduction
1 }
2 void main ( void ) {
3 // Prepare f o r Task X
4 X_Init ( ) ;
5 while ( 1 ) { // ’ f o r ever ’ ( Super Loop )
6 X ( ) ; // Perform the t a s k
7 }
8 }
Program 5.1: Super loop program
The main advantages of the Super Loop architecture illustrated above are:
However, we get ‘nothing for nothing’: Super Loops consume little memory or pro-
cessor resources because they provide few facilities to the developer. A particular
limitation with this architecture is that it is very difficult to execute Task X at pre-
cise intervals of time: as we will see, this is a very significant drawback.
For example, consider a collection of requirements assembled from a range of dif-
ferent embedded projects (in no particular order):
• The current speed of the vehicle must be measured at 0.5 second intervals.
• The calculated new throttle setting must be applied every 0.5 seconds.
• If the alarm sounds, it must be switched off (for legal reasons) after 20 min-
utes.
• If the front door is opened, the alarm must sound in 30 seconds if the correct
password is not entered in this time.
• The engine vibration data must be sampled 1,000 times per second.
• The master (control) node must communicate with all other nodes (sensor
nodes and sounder nodes) once per second.
We can summarize this list by saying that many embedded systems must carry out
tasks at particular instants of time. More specifically, we have two kinds of activity
to perform:
This is very difficult to achieve with the primitive architecture shown in Program
above. Suppose, for example, that we need to start Task X every 200 ms, and that
the task takes 10 ms to complete. Program below illustrates one way in which we
might adapt the code in order to try to achieve this.
1 }
2 void main ( void ) {
3 // Prepare f o r Task X
4 X_Init ( ) ;
5 while ( 1 ) { // ’ f o r ever ’ ( Super Loop )
6 X() ; // Perform the t a s k (10 ms duration )
7 Delay_190ms ( ) ; // Delay f o r 190 ms
8 }
9 }
Program 5.2: Trying to use the Super Loop architecture to execute tasks at regular
intervals
The approach is not generally adequate, because it will only work if the following
conditions are satisfied:
Microcontroller Page 65
An interrupt is a hardware mechanism used to notify a processor that an ‘event’
has taken place: such events may be internal events or external events.
When an interrupt is generated, the processor ‘jumps’ to an address at the bottom
of the CODE memory area. These locations must contain suitable code with which
the microcontroller can respond to the interrupt or, more commonly, the locations
will include another ‘jump’ instruction, giving the address of suitable ‘interrupt
service routine’ located elsewhere in (CODE) memory.
Please see lab 3 for the more information of this approach.
2 What is a scheduler?
• When the CPU is free, the next waiting task (if any) is executed
Implementation:
• The scheduler must allocate memory for only a single task at a time
Performance:
• Obtaining rapid responses to external events requires care at the design stage
Reliability and safety:
One area of the language with which many ‘C’ programmers are unfamiliar is the
function pointer. While comparatively rarely used in desktop programs, this lan-
guage feature is crucial in the creation of schedulers: we therefore provide a brief
introductory example here.
The key point to note is that – just as we can, for example, determine the starting
address of an array of data in memory – we can also find the address in memory
at which the executable code for a particular function begins. This address can be
used as a ‘pointer’ to the function; most importantly, it can be used to call the func-
tion. Used with care, function pointers can make it easier to design and implement
complex programs. For example, suppose we are developing a large, safety-critical,
application, controlling an industrial plant. If we detect a critical situation, we may
wish to shut down the system as rapidly as possible. However, the appropriate way
to shut down the system will vary, depending on the system state. What we can
do is create a number of different recovery functions and a function pointer. Every
time the system state changes, we can alter the function pointer so that it is always
pointing to the most appropriate recovery function. In this way, we know that –
if there is ever an emergency situation – we can rapidly call the most appropriate
function, by means of the function pointer.
Microcontroller Page 67
1 // −−−−−− P r i v a t e f u n c t i o n p r o to t y pe s −−−−−−−−−−−−−−−−−−−−−−−−−−−−−
2 void Square_Number ( i n t , i n t * ) ;
3
4 i n t main ( void )
5 {
6 int a = 2 , b = 3;
7 / * Declares pFn to be a p o i n t e r to fn with
8 i n t and i n t p o i n t e r parameters ( r e t u r n i n g void ) * /
9 void ( * pFn ) ( i n t , i n t * ) ;
10
11 i n t Result_a , Result_b ;
12 pFn = Square_Number ; // pFn holds address o f Square_Number
13 p r i n t f ( " Function code s t a r t s a t address : %u\n" , ( tWord ) pFn ) ;
14 p r i n t f ( " Data item a s t a r t s a t address : %u\n\n" , ( tWord ) &a ) ;
15 // C a l l ‘ Square_Number ’ in the conventional way
16 Square_Number ( a , &R e s u l t _ a ) ;
17 // C a l l ‘ Square_Number ’ using f u n c t i o n p o i n t e r
18 ( * pFn ) ( b,& Result_b ) ;
19 p r i n t f ( "%d squared i s %d ( using normal fn c a l l ) \n" , a , R e s u l t _ a
);
20 p r i n t f ( "%d squared i s %d ( using fn p o i n t e r ) \n" , b , Result_b ) ;
21 while ( 1 ) ;
22 return 0;
23 }
24
25 void Square_Number ( i n t a , i n t * b )
26 { // Demo − c a l c u l a t e square o f a
27 *b = a * a ;
28 }
Program 5.4: Example of how to use function pointers
2.3 Solution
• An initialization function.
• A single interrupt service routine (ISR), used to update the scheduler at reg-
ular time intervals.
• A dispatcher function that causes tasks to be executed when they are due to
run.
• A function for removing tasks from the scheduler (not required in all appli-
cations).
Before discussing the scheduler components, we consider how the scheduler will
typically appear to the user. To do this we will use a simple example: a scheduler
used to flash a single LED on and off repeatedly: on for one second off for one
second etc.
1 i n t main ( void ) {
2 // I n i t a l l the requirments f o r the system to run
3 System_Initialization () ;
4 // I n i t a schedule
5 SCH_Init ( ) ;
6 //Add a t a s k to r e p e a t l y c a l l in every 1 second .
7 SCH_Add_Task ( Led_Display , 0 , 1000) ;
8 while ( 1 ) {
9 SCH_Dispatch_Tasks ( ) ;
10 }
11 return 0;
12 }
Program 5.5: Example of how to use a scheduler
• We assume that the LED will be switched on and off by means of a ‘task’
Led_Display(). Thus, if the LED is initially off and we call Led_Display() twice,
we assume that the LED will be switched on and then switched off again.
To obtain the required flash rate, we therefore require that the scheduler calls
Led_Display() every second ad infinitum.
• After preparing the scheduler, we add the function Led_Display() to the sched-
uler task list using the SCH_Add_Task() function. At the same time we specify
that the LED will be turned on and off at the required rate as follows:
SCH_Add_Task(Led_Display, 0, 1000);
We will shortly consider all the parameters of SCH_Add_Task(), and examine
its internal structure.
• The ‘Update’ function does not execute the task: it calculates when a task is
due to run and sets a flag. The job of executing LED_Display() falls to the dis-
patcher function (SCH_Dispatch_Tasks()), which runs in the main (‘super’)
loop:
Microcontroller Page 69
1 while (1) {
2 SCH_Dispatch_Tasks () ;
3 }
At the heart of the scheduler is the scheduler data structure: this is a user-defined
data type which collects together the information required about each task.
1
2 t ypedef s t r u c t {
3 // P o i n t e r to the t a s k ( must be a ’ void ( void ) ’ f u n c t i o n )
4 void ( * pTask ) ( void ) ;
5 // Delay ( t i c k s ) u n t i l the f u n c t i o n w i l l ( next ) be run
6 u i n t 3 2 _ t Delay ;
7 // I n t e r v a l ( t i c k s ) between subsequent runs .
8 u i n t 3 2 _ t Period ;
9 // Incremented ( by scheduler ) when t a s k i s due to execute
10 u i n t 8 _ t RunMe ;
11 // This i s a h i n t to s o l v e the question below .
12 u i n t 3 2 _ t TaskID ;
13 } sTask ;
14
• SCH_Add_Task(Function_A, 0, 2);
• SCH_Add_Task(Function_C, 3, 15);
then SCH_MAX_TASKS must have a value of three (or more) for correct operation
of the scheduler.
Note also that, if this condition is not satisfied, the scheduler should generate an
error code.
Like most of the tasks we wish to schedule, the scheduler itself requires an ini-
tialization function. While this performs various important operations – such as
preparing the scheduler array (discussed earlier) and the error code variable (dis-
cussed later) – the main purpose of this function is to set up a timer that will be
used to generate the regular ‘ticks’ that will drive the scheduler.
1 void SCH_Init ( void ) {
2 unsigned char i ;
3 f o r ( i = 0 ; i < SCH_MAX_TASKS ; i ++) {
4 SCH_Delete_Task ( i ) ;
5 }
6 // Reset the g l o b a l e r r o r v a r i a b l e
7 // − SCH_Delete_Task ( ) w i l l generate an e r r o r code ,
8 // ( because the t a s k a r r a y i s empty )
9 Error_code_G = 0 ;
10 Timer_init ( ) ;
11 Watchdog_init ( ) ;
12 }
Program 5.8: Example of how
The ‘Update’ function is involved in the ISR. It is invoked when the timer is over-
flow.
When it determines that a task is due to run, the update function increments the
RunMe field for this task: the task will then be executed by the dispatcher, as we
discuss later.
1 void SCH_Update ( void ) {
2 unsigned char Index ;
3 // NOTE: c a l c u l a t i o n s are in * TICKS * ( not m i l l i s e c o n d s )
4 f o r ( Index = 0 ; Index < SCH_MAX_TASKS ; Index ++) {
5 // Check i f t h e r e i s a t a s k a t t h i s l o c a t i o n
6 i f ( SCH_tasks_G [ Index ] . pTask ) {
7 i f ( SCH_tasks_G [ Index ] . Delay == 0 ) {
8 // The t a s k i s due to run
9 // Inc . the ’RunMe ’ f l a g
10 SCH_tasks_G [ Index ] . RunMe += 1 ;
Microcontroller Page 71
11 i f ( SCH_tasks_G [ Index ] . Period ) {
12 // Schedule p e r i o d i c t a s k s to run again
13 SCH_tasks_G [ Index ] . Delay = SCH_tasks_G [ Index ] .
Period ;
14 }
15 } else {
16 // Not y e t ready to run : j u s t decrement the delay
17 SCH_tasks_G [ Index ] . Delay −= 1 ;
18 }
19 }
20 }
21 }
22
As the name suggests, the ‘Add Task’ function is used to add tasks to the task array,
to ensure that they are called at the required time(s). Here is the example of add
task function: unsigned char SCH_Add_Task ( Task_Name , Initial_Delay, Period
)
The parameters for the ‘Add Task’ function are described as follows:
• Task_Name: the name of the function (task) that you wish to schedule
• Initial_Delay: the delay (in ticks) before task is first executed. If set to 0, the
task is executed immediately.
• Period: the interval (in ticks) between repeated executions of the task. If set
to 0, the task is executed only once
As we have seen, the ‘Update’ function does not execute any tasks: the tasks that
are due to run are invoked through the ‘Dispatcher’ function.
1 void SCH_Dispatch_Tasks ( void )
2 {
3 unsigned char Index ;
4 // Dispatches ( runs ) the next t a s k ( i f one i s ready )
5 f o r ( Index = 0 ; Index < SCH_MAX_TASKS ; Index ++) {
6 i f ( SCH_tasks_G [ Index ] . RunMe > 0 ) {
7 ( * SCH_tasks_G [ Index ] . pTask ) ( ) ; // Run the t a s k
Microcontroller Page 73
8 SCH_tasks_G [ Index ] . RunMe −= 1 ; // Reset / reduce RunMe
flag
9 // P e r i o d i c t a s k s w i l l a u t o m a t i c a l l y run again
10 // − i f t h i s i s a ’ one shot ’ task , remove i t from the
array
11 i f ( SCH_tasks_G [ Index ] . Period == 0 )
12 {
13 SCH_Delete_Task ( Index ) ;
14 }
15 }
16 }
17 // Report system s t a t u s
18 SCH_Report_Status ( ) ;
19 // The scheduler e n t e r s i d l e mode a t t h i s point
20 SCH_Go_To_Sleep ( ) ;
21 }
When tasks are added to the task array, SCH_Add_Task() returns the position in the
task array at which the task has been added: Task_ID = SCH_Add_Task(Do_X,1000,0);
Sometimes it can be necessary to delete tasks from the array. To do so, SCH_Delete_Task()
can be used as follows: SCH_Delete_Task(Task_ID)
1 /*
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
*/
2 unsigned char SCH_Delete_Task ( const t B y t e TASK_INDEX ) {
3 unsigned char Return_code ;
4 i f ( SCH_tasks_G [ TASK_INDEX ] . pTask == 0 ) {
5 // No t a s k a t t h i s l o c a t i o n . . .
6 //
7 // S e t the g l o b a l e r r o r v a r i a b l e
8 Error_code_G = ERROR_SCH_CANNOT_DELETE_TASK
9
10 // . . . a l s o r e t u r n an e r r o r code
11 Return_code = RETURN_ERROR ;
12 } else {
13 Return_code = RETURN_NORMAL;
14 }
15 SCH_tasks_G [ TASK_INDEX ] . pTask = 0 x0000 ;
16 SCH_tasks_G [ TASK_INDEX ] . Delay = 0 ;
17 SCH_tasks_G [ TASK_INDEX ] . Period = 0 ;
18 SCH_tasks_G [ TASK_INDEX ] . RunMe = 0 ;
19 r e t u r n Return_code ; // r e t u r n s t a t u s
20 }
Program 5.13: An implementation of the scheduler ‘delete task’ function
Microcontroller Page 75
3 }
Program 5.14: An implementation of the scheduler ‘go to sleep’ function
Hardware fails; software is never perfect; errors are a fact of life. To report errors
at any part of the scheduled application, we can use an (8-bit) error code variable
Error_code_G
unsigned char Error_code_G = 0;
To record an error we include lines such as:
• Error_code_G = ERROR_SCH_TOO_MANY_TASKS;
• Error_code_G = ERROR_SCH_WAITING_FOR_SLAVE_TO_ACK;
• Error_code_G = ERROR_SCH_WAITING_FOR_START_COMMAND_FROM_MASTER;
• Error_code_G = ERROR_SCH_ONE_OR_MORE_SLAVES_DID_NOT_START;
• Error_code_G = ERROR_SCH_LOST_SLAVE;
• Error_code_G = ERROR_SCH_CAN_BUS_ERROR;
• Error_code_G = ERROR_I2C_WRITE_BYTE_AT24C64;
Note that, in this implementation, error codes are reported for 60,000 ticks (1 minute
at a 1 ms tick rate). The simplest way of displaying these codes is to attach eight
LEDs (with suitable buffers) to the error port, as discussed in IC DRIVER [page
134]: Figure 14.3 illustrates one possible approach.
What does that error code mean? The forms of error reporting discussed here are
low-level in nature and are primarily intended to assist the developer of the appli-
cation or a qualified service engineer performing system maintenance. An addi-
tional user interface may also be required in your application to notify the user of
errors, in a more user-friendly manner.
The basic scheduler presented here does not provide support for a watchdog timer.
Such support can be useful and is easily added, as follows:
1 IWDG_HandleTypeDef hiwdg ;
2 s t a t i c u i n t 3 2 _ t counter_for_watchdog = 0 ;
3
Microcontroller Page 77
19 return 0;
20 }
21 void Watchdog_Counting ( void ) {
22 counter_for_watchdog ++;
23 }
24
2.3.12 Portability
3 Objectives
The aim of this lab is to design and implement a cooperate scheduler to accurately
provide timeouts and trigger activities. You should add a file for the scheduler im-
plementation and modify the main system call loop to handle timer interrupts.
• void SCH_Dispatch_Tasks(void): This function will get the task in the queue
to run.
You should add more functions if you think it will help you to solve this problem.
Your main program must have 5 tasks running periodically in 0.5 second, 1 second,
1.5 seconds, 2 seconds, 2.5 seconds.
5 Demonstration
You should be able to show some test code that uses all the functions specified in
the driver interface.
Specifically set up and demonstrate:
• Then, print the value returned by get_time every time this callback is re-
ceived.
• Before entering the main loop, set up a few calls to SCH_Add_Task. Make sure
the delay used is long enough such that the loop is entered before these wake
up. These callbacks should just print out the current timestamp as each delay
expires.
Note this is not a complete list. The following designs are considered unsatisfac-
tory:
Microcontroller Page 79
• Delivering callbacks in the wrong order
6 Submission
You need to
7 References
Flow control and Error control are the two main responsibilities of the data link
layer, which is a communication channel for node-to-node delivery of the data.
The functions of the flow and error control are explained as follows.
Flow control mainly coordinates with the amount of data that can be sent before
receiving an acknowledgment from the receiver and it is one of the major duties
of the data link layer. For most of the communications, flow control is a set of pro-
cedures that mainly tells the sender how much data the sender can send before it
must wait for an acknowledgment from the receiver.
A critical issue, but not really frequently occurred, in the flow control is that the
processing rate is slower than the transmission rate. Due to this reason each re-
ceiving device has a block of memory that is commonly known as buffer, that is
used to store the incoming data until this data will be processed. In case the buffer
begins to fill-up then the receiver must be able to tell the sender to halt the trans-
mission until once again the receiver become able to receive.
Meanwhile, error control contains both error detection and error correction. It
mainly allows the receiver to inform the sender about any damaged or lost frames
during the transmission and then it coordinates with the re-transmission of those
frames by the sender.
The term Error control in the communications mainly refers to the methods of er-
ror detection and re-transmission. Error control is mainly implemented in a sim-
ple way and that is whenever there is an error detected during the exchange, then
specified frames are re-transmitted and this process is also referred to as Auto-
matic Repeat request(ARQ).
The target in this lab is to implement a UART communication between the STM32
and a simulated terminal. A data request is sent from the terminal to the STM32.
Afterward, computations are performed at the STM32 before a data packet is sent
to the terminal. The terminal is supposed to reply an ACK to confirm the commu-
nication successfully or not.
• Terminal: Right click, choose Place, Virtual Instrument, then select VIRTUAL
TERMINAL.
• Variable resistor (RV2): Right click, choose Place, From Library, and search for
the POT-HG device. The value of this device is set to the default 1k.
• Volt meter (for debug): Right click, choose Place, Virtual Instrument, the se-
lect DC VOLTMETER.
• OPAMP (U3): Right click, choose Place, From Library, and search for the OPAMP
device.
The opamp is used to design a voltage follower circuit, which is one of the most
popular applications for opamp. In this case, it is used to design an adc input sig-
nal, which is connected to pin PA0 of the MCU.
Double click on the virtual terminal and set its baudrate to 9600, 8 data bits, no
parity and 1 stop bit, as follows:
Microcontroller Page 83
Hình 6.2: Terminal configuration
3 Project configurations
A new project is created with following configurations, concerning the UART for
communications and ADC input for sensor reading. The pin PA5 should be an
GPIO output, for LED blinky.
From the ioc file, select Connectivity, and then select the USART2. The parameter
settings for UART channel 2 (USART2) module are depicted as follows:
Finally, the NVIC settings are checked to enable the UART interrupt, as follows:
In order to read a voltage signal from a simulated sensor, this module is required.
By selecting on Analog, then ADC1, following configurations are required:
The ADC pin is configured to PA0 of the STM32, which is shown in the pinout view
dialog.
This source is required to add in the main.c file, to verify the UART communication
channel: sending back any character received from the terminal, which is well-
known as the loop-back communication.
Microcontroller Page 85
1 /* USER CODE BEGIN 0 */
2 uint8_t temp = 0;
3
When a character (or a byte) is received, this interrupt service routine is invoked.
After the character is sent to the terminal, the interrupt is activated again. This
source code should be placed in a user-defined section.
Finally, in the main function, the proposed source code is presented as follows:
1 int main ( void )
2 {
3 HAL_Init () ;
4 SystemClock_Config () ;
5
6 MX_GPIO_Init () ;
7 MX_USART2_UART_Init () ;
8 MX_ADC1_Init () ;
9
12 while (1)
13 {
14 HAL_GPIO_TogglePin ( LED_RED_GPIO_Port , LED_RED_Pin ) ;
15 HAL_Delay (500) ;
16 }
17
18 }
Program 6.2: Implement the main function
5 Sensor reading
A simple source code to read adc value from PA0 is presented as follows:
1 uint32_t ADC_value = 0;
2 while (1)
3 {
4 HAL_GPIO_TogglePin ( LED_RED_GPIO_Port , LED_RED_Pin ) ;
5 ADC_value = HAL_ADC_GetValue (& hadc1 ) ;
Every half of second, the ADC value is read and its value is sent to the console. It is
worth noticing that the number ADC_value is convert to ascii character by using
the sprintf function.
The default ADC in STM32 is 13 bits, meaning that 5V is converted to 4096 decimal
value. If the input is 2.5V, ADC_value is 2048.
6 Project description
• From the console, user types !RST# to ask for a sensory data.
The timeout for waiting the !OK# at STM32 is 3 seconds. After this period, its packet
is sent again. The value is kept as the previous packet.
This module is used to received a command from the console. As the reception
process is implement by an interrupt, the complexity is considered seriously. The
proposed implementation is given as follows.
Firstly, the received character is added into a buffer, and a flag is set to indicate that
there is a new data.
1 # define MAX_BUFFER_SIZE 30
2 uint8_t temp = 0;
3 uint8_t buffer [ MAX_BUFFER_SIZE ];
4 uint8_t index_buffer = 0;
5 uint8_t buffer_flag = 0;
6 void HAL_UART_RxCpltCallback ( UART_HandleTypeDef * huart ) {
7 if ( huart - > Instance == USART2 ) {
8
Microcontroller Page 87
12
13 buffer_flag = 1;
14 HAL_UART_Receive_IT (& huart2 , & temp , 1) ;
15 }
16 }
Program 6.4: Add the received character into a buffer
MIDTERM 2022
1 Introduction
• The RESET button is used to reset the counter value to 0. Meanwhile, the
INC and DEC buttons are used to increase and decrease the counter value,
respectively. There are two events need to handle for these buttons, including
the normal-press and long-press.
• The D1 LED is blinking every second, which is normally used to monitor the
execution of the system.
Students are supposed to following the section bellow, to finalize the project and
fill in reports for their implementations. Some important notes for your midterm
are listed bellow:
• The timer interrupt is 10ms. The value for counter is 9 (10 is also acceptable)
when the pre-scaller is 7999.
• There is no HAL_Delay() function in your source code. All the delay behavior
must be based on a software timer.
• GitHub link for the source code and demo video link must be public access.
In this part, students propose the connection of the LED7 segment and 3 buttons
to the STM32F103C6.
Your report: The schematic of your system is presented here. The screen can be
captured and present in this part.
A state machine is required in this step to perform just only the normal-press (or a
button push) behavior of three buttons:
Your report: Present a main function, which is used to implement the state ma-
chine. This function should be invoked in main().
1 void fsm_simple_buttons_run () {
2 // TODO
3 }
Program 7.1: Implementation of the state machine
Microcontroller Page 91
2.3 State machine Step 2 - 2 points
In this part, long-press events for INC and DEC buttons are added to the project.
For a button, this event is raised after 3 seconds keep pressing the button.
When a long-press event is detected, the value of counter keeps changing every 1
second until the button is released. For example, the current value of counter is
2 and the INC button is pressed. The value of counter immediately increased by
1, or counter = 3. The INC button keeps pressing for 3 seconds, then the value of
counter is 4. As long as the INC button is pressed, the value continues increasing
every 1 second. This behavior is illustrated in the Figure bellow:
The behaviors of the DEC button are reversed to the INC button. The value of
counter is also roll back if it reaches 0 or 9.
Your report: Present your whole state machine when the long press events are
added.
Your report: Present a main function, which is used to implement additional states.
Minor changes in the previous source code are note required to present here.
Finally, where there is no button event after 10 seconds, the value of of counter is
counted down and stopped at 0. If the INC or DEC are pressed again, the status of
the system comes back to previous state, which is designed in Subsection 2 or 3.
Your report: Present your whole state machine for the 10s time-out event.
Your report: Present a main function, which is used to implement additional states.
Minor changes in the previous source code are note required to present here.
Finally, for many projects based on microcontroller, there is an LED keeps blink-
ing every second. In this project, the LED connected to PA5 is used to perform this
feature.
Your report: Present your solution and the source code for this feature. It can be
very simple source code or a new state machine for this LED. If a state machine is
used, please present it in the report.
A link to your github presented the last commit of your project is provided in
this section. This link contains all files in your STMCube project (configurations,
header and source files)
And a link for just one demo video is also needed to present here.
EXample:
Microcontroller Page 93
Prototype
1 string suffixWithUnit ( double number ) {
2 }
How would you solve them? Please share your thinking to solve this problem and
provide your answer.
GIỮA KÌ 2022
1 Giới thiệu
Trong project giữa kì này, một hệ thống đếm lùi sẽ được hiện thực trên phần mềm
mô phỏng Proteus. Như được trình bày ở Hình 8.1, các thành phần chính của hệ
thống bao gồm vi điều khiển STM32F103C6, một đèn LED, một LED 7 đoạn và 3
nút nhấn đơn.
Một số tính năng chính của hệ thống được trình bày như sau:
• LED 7 đoạn dùng để hiển thị giá trị của counter, có giá trị từ 0 đến 9.
• Nút RESET được dùng để reset giá trị của counter về 0. Trong khi đó, nút
nhấn INC và DEC được dùng để tăng hoặc giảm giá trị của counter. Có 2 sự
kiện cần phải xử lý cho các nút nhấn, là nhấn thường và nhấn giữa. Trong dự
án này, một nút nhấn được coi là nhấn giữ, nếu nó giữ nguyên trạng thái đó
trong 3 giây liên tiếp.
• Đèn LED D1 được dùng để theo dõi hoạt động của hệ thống, nó sẽ luân phiên
chớp tắt mỗi giây.
Sinh viên sẽ hiện thực dự án của mình theo từng bước yêu cầu ở phần bên dưới.
Trong mỗi phần, sinh viên cần trình bày các yêu cầu về report. Một số lưu ý quan
trọng cho phần hiện thực như sau:
• Tất cả các nút nhấn đều được xử lý chống rung bằng ngắt timer 10ms. Một
nút nhấn sẽ được xem là nhấn đè nếu nó được giữ liên tục trong 3 giây.
• Không sử dụng HAL_Delay() trong việc hiện thực. Tất cả các hiệu ứng thời
gian đều phải được hiện thực dựa trên software timer.
• Report này cần được nộp lại kèm theo các câu trả lời của sinh viên.
• Link github và video demo trong report này được chỉnh quyền truy cập pub-
lic.
Trong phần này, sinh viên đề xuất các kết nối của LED 7 đoạn và 3 nút nhấn với
STM32F103C6.
Report: Trình bày sơ đồ nguyên lý tại đây. Sinh viên có thể chụp màn hình Proteus
cho phần sơ đồ nguyên lý.
Một máy trạng thái sẽ được thiết kế để thực hiện các chức năng với trạng thái nhấn
bình thường (normal-press) cho 3 nút nhấn, như sau:
• Khi INC được nhấn, giá trị của counter tăng 1 đơn vị. Khi nó đến 9, giá trị tiếp
theo là 0.
• Khi DC được nhấn, giá trị của counter giảm 1 đơn vị. Khi giá trị của nó là 0,
giá trị tiếp theo là 9.
Giá trị của biến counter được hiển thị lên LED 7 đoạn.
Your report: Trình bày hiện thực của hàm dùng để hiển thực máy trạng thái ở trên.
Thông thường, hàm này sẽ được gọi trong main().
Microcontroller Page 97
1 void fsm_simple_buttons_run () {
2 // TODO
3 }
Program 8.1: Hiện thực máy trạng thái
Trong phần này, việc nhấn giữ (long-press) cho nút INC và DEC được thêm vào
trong dự án. Đối với nút nhấn, sự kiện long-press xảy ra khi nó được nhấn giữ liên
tục sau 3 giây.
Khi một sự kiện nhấn giữ được phát hiện, giá trị của counter liên tục thay đổi mỗi
1 giây. Ví dụ, giá trị hiện tại của counter là 2. Khi nút INC được nhấn, ngay lập tức
giá trị của nó sẽ tăng lên 3. Tuy nhiên, khi INC tiếp tục được giữ, 3 giây sau, sự kiện
long-press xảy ra, giá trị của counter tăng lên 4. Từ lúc này, giá trị của counter sẽ
tăng lên 1 mỗi giây, cho đến khi nút INC được thả ra. Giá trị của biến counter này
được minh họa như hình bên dưới.
Hành vi của nút DEC sẽ ngược lại với nút INC. Giá trị của biến counter sẽ xoay
vòng khi nó đến 0 hoặc 9.
Report: Trình bày toàn bộ máy trạng thái cho đến bước này.
Report: Sinh viên trình bày một hàm chính dùng để hiện thực các trạng thái mới.
Các thay đổi trên máy trạng thái cũ không cần phải trình bày ở đây.
Cuối cùng, khi không có nút nào được nhấn, hệ thống tự động đếm lùi biến counter
và dừng lại khi nó bằng 0. Sau đó, nếu nút INC hoặc DEC được nhấn, trạng thái của
hệ thống lại quay về một trong các trạng thái đã thiết kế trước đó.
Your report: Trình bày toàn bộ máy trạng thái, khi sự kiện time-out 10s được thêm
vào.
Cuối cùng, trong nhiều dự án dựa trên vi điều khiển, có 1 LED luôn luôn chớp tắt
mỗi giây, để giám sát hoạt động của hệ thống. Trong project này, LED nối với chân
PA5 sẽ được dùng để hiện thực tính năng này.
Report: Sinh viên trình bày giải pháp của mình cho tính năng này. Giải pháp có
thể rất đơn giản với vài dòng code hoặc thiết kế máy trạng thái và lập trình cho
nó. Trong trường hợp thứ 2, sinh viên trình bày máy trạng thái trước khi trình bày
phần hiện thực mã nguồn.
Link github cho dự án của sinh viên được trình bày ở đây, bao gồm tất cả các files
trong dự án (configurations, header and source files). Link này là lần commit sau
cùng trong project giữa kì của sinh viên.
Link cho 1 video demo cũng sẽ được trình bày ở phần này.
EXample:
Microcontroller Page 99
2 suffixWithUnit(1234) => 1.234 Kilo
Prototype
1 string suffixWithUnit ( double number ) {
2 }
How would you solve them? Please share your thinking to solve this problem and
provide your answer.