0% found this document useful (0 votes)
126 views

Microcontroller 1

This document discusses microcontroller projects including LED animations, timer interrupts, buttons and switches, digital clocks, schedulers, and communication. It provides example code and exercises for working with these microcontroller concepts and debugging circuits in Proteus simulation software.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
126 views

Microcontroller 1

This document discusses microcontroller projects including LED animations, timer interrupts, buttons and switches, digital clocks, schedulers, and communication. It provides example code and exercises for working with these microcontroller concepts and debugging circuits in Proteus simulation software.
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 100

Dr.

Le Trong Nhan
Mục lục

Chapter 1. LED Animations 9


1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2 First project on STM32Cube . . . . . . . . . . . . . . . . . . . . . . . . . 11
3 Simulation on Proteus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4 Exercise and Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.1 Exercise 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.2 Exercise 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.3 Exercise 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.4 Exercise 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.5 Exercise 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.6 Exercise 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.7 Exercise 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.8 Exercise 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.9 Exercise 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.10 Exercise 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

Chapter 2. Timer Interrupt and LED Scanning 27


1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2 Timer Interrupt Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3 Exercise and Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.1 Exercise 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.2 Exercise 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.3 Exercise 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.4 Exercise 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.5 Exercise 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.6 Exercise 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.7 Exercise 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

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

Page 6 HCMUT - Computer Engineering


5.9 Exercise 8: Adding code for increasing time duration value for
the amber LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.10 Exercise 9: Adding code for increasing time duration value for
the green LEDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.11 Exercise 10: To finish the project . . . . . . . . . . . . . . . . . . 60

Chapter 4. Digital Clock Project 61

Chapter 5. A cooperative scheduler 63


1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1.1 Super Loop Architecture . . . . . . . . . . . . . . . . . . . . . . . 64
1.2 Timer-based interrupts and interrupt service routines . . . . 65
2 What is a scheduler? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
2.1 The co-operative scheduler . . . . . . . . . . . . . . . . . . . . . 66
2.2 Function pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
2.3 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
2.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
2.3.2 The scheduler data structure and task array . . . . . 70
2.3.3 The initialization function . . . . . . . . . . . . . . . . 71
2.3.4 The ‘Update’ function . . . . . . . . . . . . . . . . . . . 71
2.3.5 The ‘Add Task’ function . . . . . . . . . . . . . . . . . . 72
2.3.6 The ‘Dispatcher’ . . . . . . . . . . . . . . . . . . . . . . 73
2.3.7 The ‘Delete Task’ function . . . . . . . . . . . . . . . . 75
2.3.8 Reducing power consumption . . . . . . . . . . . . . 75
2.3.9 Reporting errors . . . . . . . . . . . . . . . . . . . . . . 76
2.3.10 Adding a watchdog . . . . . . . . . . . . . . . . . . . . 77
2.3.11 Reliability and safety implications . . . . . . . . . . . 78
2.3.12 Portability . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4 Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5 Demonstration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6 Submission . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
7 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

Chapter 6. Flow and Error Control in Communication 81


1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
2 Proteus simulation platform . . . . . . . . . . . . . . . . . . . . . . . . . 83

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

Chapter 7. MIDTERM 2022 89


1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
2 Implement and Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
2.1 Proteus schematic - 1 point . . . . . . . . . . . . . . . . . . . . . 91
2.2 State machine Step 1 - 2 points . . . . . . . . . . . . . . . . . . . 91
2.3 State machine Step 2 - 2 points . . . . . . . . . . . . . . . . . . . 92
2.4 State machine Step 3 - 2 points . . . . . . . . . . . . . . . . . . . 92
2.5 Led Blinky for Debugging - 1 point . . . . . . . . . . . . . . . . . 93
2.6 Github and Demo . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3 Extra exercise - Engineer mindset -1 point . . . . . . . . . . . . . . . . 93

Chapter 8. GIỮA KÌ 2022 95


1 Giới thiệu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
2 Hiện thực và Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
2.1 Sơ đồ nguyên lý trên Proteus - 1 điểm . . . . . . . . . . . . . . . 97
2.2 State machine Step 1 - 2 điểm . . . . . . . . . . . . . . . . . . . . 97
2.3 State machine Step 2 - 2 điểm . . . . . . . . . . . . . . . . . . . . 98
2.4 State machine Step 3 - 2 points . . . . . . . . . . . . . . . . . . . 98
2.5 Led Blinky for Debugging - 1 điểm . . . . . . . . . . . . . . . . . 99
2.6 Github và Demo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
3 Bài tập thêm - Engineer mindset - 1 điểm . . . . . . . . . . . . . . . . . 99

Page 8 HCMUT - Computer Engineering


CHƯƠNG 1

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.

Hình 1.1: STM32Cube IDE for STM32 Programming

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

The rest of this manual consists of:

• Create a project on STM32Cube IDE

• Create a project on Proteus

• Simulate the project on Proteus

Finally, students are supposed to finish 10 different projects.

Page 10 HCMUT - Computer Engineering


2 First project on STM32Cube

Step 1: Launch STM32CubeIDE, from the menu File, select New, then chose STM32
Project

Hình 1.2: Create a new project on STM32CubeIDE

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

Hình 1.3: Select the target device

Step 3: Provide the Name and the Location for the project.

Microcontroller Page 11
Hình 1.4: Select the target device

It is important to notice that the Targeted Project Type should be STM32Cube. In


the case this option is disable, step 1 must be repeated. The location path should
not contain special characters (e.g. the space). Finally, click on the Next button.

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

5 /* USER CODE END 1 */

Page 12 HCMUT - Computer Engineering


Hình 1.5: Keep default firmware version

Hình 1.6: Set PA5 to GPIO Output mode

Microcontroller Page 13
Hình 1.7 : Provide a name for PA5

7 /* MCU Configuration
--------------------------------------------------------
*/
8

9 /* Reset of all peripherals , Initializes the Flash


interface and the Systick . */
10 HAL_Init () ;
11

12 /* USER CODE BEGIN Init */


13

14 /* USER CODE END Init */


15

16 /* Configure the system clock */


17 SystemClock_Config () ;
18

19 /* USER CODE BEGIN SysInit */


20

21 /* USER CODE END SysInit */


22

23 /* Initialize all configured peripherals */


24 MX_GPIO_Init () ;
25 /* USER CODE BEGIN 2 */
26

27 /* USER CODE END 2 */


28

29 /* Infinite loop */
30 /* USER CODE BEGIN WHILE */
31

32 while (1)
33 {

Page 14 HCMUT - Computer Engineering


34 HAL_GPIO_TogglePin ( LED_RED_GPIO_Port , LED_RED_Pin ) ;
35 HAL_Delay (1000) ;
36 /* USER CODE END WHILE */
37

38 /* USER CODE BEGIN 3 */


39 }
40 /* USER CODE END 3 */
41 }
Program 1.1: First blinky LED project

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:

Hình 1.8: Config for hex file output

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:

Hình 1.9: Compile the project and generate Hex file

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.

Page 16 HCMUT - Computer Engineering


3 Simulation on Proteus

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.

Hình 1.10: Create a new project on Proteus

Step 2: Provide the name and the location of the project, then click on Next button.

Hình 1.11: Provide project name and location

Step 3: For following dialog, just click on Next button as just a schematic is re-
quired for the lab.

Hình 1.12: Keep the default options by clicking on Next

Microcontroller Page 17
Step 4: Finally, click on Finish button to close the project wizard.

Hình 1.13: Finish the project wizard

Step 5: On the main page of the project, right click to select Place, Components,
From Libraries, as follows:

Hình 1.14: Select a component from the library

If there is an error with no library found, please restart the Proteus software with
Run as administrator option.

Page 18 HCMUT - Computer Engineering


Step 6: From the list of components in the library, select STM32F103C6, as follows:

Hình 1.15: Select STM32F103C6

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:

Hình 1.16: STM32 and an LED in the project

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.

Hình 1.18: Connect components and set the power to 3.3V

In this step, also double click on the power supply in order to provide the String
property to +3.3V.

Page 20 HCMUT - Computer Engineering


Step 8: Right click on the wire of the power supply and the ground, and select
Place wire Label

Hình 1.19: Place label for Power and Ground

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:

Hình 1.20: Finalize the schematic

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

Page 22 HCMUT - Computer Engineering


4 Exercise and Report

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.

Hình 1.23: State transitions for 2 LEDs

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.

Hình 1.24: Reference design for a 4 way traffic light

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:

Page 24 HCMUT - Computer Engineering


Hình 1.25: Display a number on 7 segment LED

This function is invoked in the while loop for testing as following:


1 int counter = 0;
2 while (1) {
3 if ( counter >= 10) counter = 0;
4 display7SEG ( counter ++) ;
5 HAL_Delay (1000) ;
6

7 }
Program 1.4: An example for your source code

Report 1: Present the schematic.

Report 2: Present the source code for display7SEG function.

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

In this exercise, a new Proteus schematic is designed to simulate an analog clock,


with 12 different number. The connections for 12 LEDs are supposed from PA4 to
PA15 of the STM32. The arrangement of 12 LEDs is depicted as follows.
Report 1: Present the schematic. Report 2: Implement a simple program to test the
connection of every single LED. This testing program should turn every LED in a
sequence.

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

Implement a function named setNumberOnClock(int num). The input for this


function is from 0 to 11 and an appropriate LED is turn on. Present the source
code of this function.

4.9 Exercise 9

Implement a function named clearNumberOnClock(int num). The input for this


function is from 0 to 11 and an appropriate LED is turn off.

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.

Page 26 HCMUT - Computer Engineering


CHƯƠNG 2

Timer Interrupt and LED Scanning


1 Introduction

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.

Hình 2.1: Four seven segment LED interface for a micro-controller

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-

Page 28 HCMUT - Computer Engineering


quency should be 50Hz.

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.

Hình 2.2: Default clock source for the system

Step 3: Configure the timer on the Parameter Settings, as follows:

Hình 2.3: Configure for Timer 2

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:

Page 30 HCMUT - Computer Engineering


• The target is to set an interrupt timer to 10ms

• 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:

Hình 2.4: Enable timer interrupt

Finally, save the configuration file to generate the source code.

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

9 /* USER CODE BEGIN 2 */


10 HAL_TIM_Base_Start_IT (& htim2 ) ;
11 /* USER CODE END 2 */ g3
12

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

The HAL_TIM_PeriodElapsedCallback function is an infinite loop, which is in-


voked every cycle of the timer 2, in this case, is 10ms.

Page 32 HCMUT - Computer Engineering


3 Exercise and Report

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:

Hình 2.5: Simulation schematic in Proteus

Components used in the schematic are listed bellow:

• 7SEG-COM-ANODE (connected from PB0 to PB6)

• 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.

Report 2: Present your source code in the HAL_TIM_PeriodElapsedCallback func-


tion.

Short question: What is the frequency of the scanning process?

3.2 Exercise 2

Extend to 4 seven segment LEDs and two LEDs (connected to PA4, labeled as DOT)
in the middle as following:

Hình 2.6: Simulation schematic in Proteus

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.

Report 2: Present your source code in the HAL_TIM_PeriodElapsedCallback func-


tion.

Short question: What is the frequency of the scanning process?

Page 34 HCMUT - Computer Engineering


3.3 Exercise 3

Implement a function named update7SEG(int index). An array of 4 integer num-


bers are declared in this case. The code skeleton in this exercise is presented as
following:

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

This function should be invoked in the timer interrupt, e.g update7SEG(index_led++).


The variable index_led is updated to stay in a valid range, which is from 0 to 3.

Report 1: Present the source code of the update7SEG function.

Report 2: Present the source code in the HAL_TIM_PeriodElapsedCallback.

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.

Report 1: Present the source code in the HAL_TIM_PeriodElapsedCallback.

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:

Page 36 HCMUT - Computer Engineering


1 int hour = 15 , minute = 8 , second = 50;
2

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.

Report 1: Present the source code in the updateClockBuffer function.

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.

Step 1: Declare variables and functions for a software timer, as following:


1 /* USER CODE BEGIN 0 */
2 int timer0_counter = 0;
3 int timer0_flag = 0;
4 int TIMER_CYCLE = 10;
5 void setTimer0 ( int duration ) {

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.

Step 2: The timer_run() is invoked in the timer interrupt as following:


1 void HA L_ TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim )
{
2

3 timer_run () ;
4

5 // YOUR OTHER CODE


6 }
Program 2.7: Software timer based timer interrupt

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?

Report 2: if in line 1 of the code above is changed to setTimer0(1), what happens


after that and why?

Report 3: if in line 1 of the code above is changed to setTimer0(10), what is changed


compared to 2 first questions and why?

Page 38 HCMUT - Computer Engineering


3.7 Exercise 7

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:

Hình 2.7 : LED matrix is added to the simulation

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.

Report 2: Implement the function, updateLEDMatrix(int index), which is similarly


to 4 seven led segments.
1 const int MAX_LED_MATRIX = 8;
2 int index_led_matrix = 0;
3 uint8_t matrix_buffer [8] = {0 x01 , 0 x02 , 0 x03 , 0 x04 , 0 x05 , 0
x06 , 0 x07 , 0 x08 };
4 void updateLEDMatrix ( int index ) {
5 switch ( index ) {
6 case 0:
7 break ;
8 case 1:
9 break ;
10 case 2:
11 break ;
12 case 3:
13 break ;
14 case 4:
15 break ;
16 case 5:
17 break ;
18 case 6:
19 break ;
20 case 7:
21 break ;
22 default :
23 break ;
24 }
25 }
Program 2.9: Function to display data on LED Matrix

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.

Page 40 HCMUT - Computer Engineering


CHƯƠNG 3

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 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:

• 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 com-
plex 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.

Page 42 HCMUT - Computer Engineering


In this lab, we consider how you read inputs from mechanical buttons in your em-
bedded application using an MCU.

Microcontroller Page 43
3 Basic techniques for reading from port pins

3.1 The need for pull-up resistors

Hình 3.1: Connecting a button to an MCU

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.

3.2 Dealing with switch bounces

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:

Page 44 HCMUT - Computer Engineering


Hình 3.2: The need of pull up resistors

Hình 3.3: A reliable way to connect a button to an MCU

Hình 3.4: Switch bounces

Microcontroller Page 45
• Rather than reading ‘A’ from a keypad, we may read ‘AAAAA’

• Counting the number of times that a switch is pressed becomes extremely


difficult

• 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 out-
comes 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

Page 46 HCMUT - Computer Engineering


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 en-
ergy devices make electrical noise, sometimes great amounts of electrical noise.
This noise 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
hardware 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 single 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), inac-
tive (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 3.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
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).

Page 48 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. 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.

Hình 3.5: Input Output Processing Patterns

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

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 3.6.

Hình 3.6: File Organization

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.

Page 50 HCMUT - Computer Engineering


Hình 3.7 : Step 1: Create a C source file for input reading

Hình 3.8: Step 2: Create a C header file for input processing

Microcontroller Page 51
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 3.2: Define constants buffers and button_reading function

Page 52 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 3.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 3.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 3.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 3.6: Timer interrupt callback function

Microcontroller Page 53
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.

Hình 3.9: An FSM for processing a button

Page 54 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 su-
per 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 3.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 3.8: Code in the input_processing.h file

Microcontroller Page 55
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 3.9: The code in the main.c file

Page 56 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 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 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 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.

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 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.

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 58 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 59
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 60 HCMUT - Computer Engineering


CHƯƠNG 4

Digital Clock Project


Page 62 HCMUT - Computer Engineering
CHƯƠNG 5

A cooperative scheduler
1 Introduction

1.1 Super Loop Architecture

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:

• (1) that it is simple, and therefore easy to understand, and

• (2) that it consumes virtually no system memory or CPU resources.

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 display must be refreshed 40 times every second.

• The calculated new throttle setting must be applied every 0.5 seconds.

• A time-frequency transform must be performed 20 times every second.

• 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 frequency-domain data must be classified 20 times every second.

• The keypad must be scanned every 200 ms.

• The master (control) node must communicate with all other nodes (sensor
nodes and sounder nodes) once per second.

• The new throttle setting must be calculated every 0.5 seconds.

Page 64 HCMUT - Computer Engineering


• The sensors must be sampled 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:

• Periodic tasks, to be performed (say) once every 100 ms

• One-shot tasks, to be performed once after a delay of (say) 50 ms

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:

• We know the precise duration of Task X

• This duration never varies

In practical applications, determining the precise task duration is rarely straight-


forward. Suppose we have a very simple task that does not interact with the out-
side world but, instead, performs some internal calculations. Even under these
rather restricted circumstances, changes to compiler optimization settings – even
changes to an apparently unrelated part of the program – can alter the speed at
which the task executes. This can make fine-tuning the timing very tedious and
error prone.
The second condition is even more problematic. Often in an embedded system
the task will be required to interact with the outside world in a complex way. In
these circumstances the task duration will vary according to outside activities in a
manner over which the programmer has very little control.

1.2 Timer-based interrupts and interrupt service routines

A better solution to the problems outlined is to use timer-based interrupts as a


means of invoking functions at particular times.

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?

There are two ways of viewing a scheduler:

• At one level, a scheduler can be viewed as a simple operating system that


allows tasks to be called periodically or (less commonly) on a one-shot basis.

• At a lower level, a scheduler can be viewed as a single timer interrupt ser-


vice routine that is shared between many different tasks. As a result, only
one timer needs to be initialized, and any changes to the timing generally re-
quires only one function to be altered. Furthermore, we can generally use the
same scheduler whether we need to execute one, ten or 100 different tasks.

1 void main ( void ) {


2 // Set up the scheduler
3 SCH_Init () ;
4 // Add the tasks (1 ms tick interval )
5 // Function_A will run every 2 ms
6 SCH_Add_Task ( Function_A , 0 , 2) ;
7 // Function_B will run every 10 ms
8 SCH_Add_Task ( Function_B , 1 , 10) ;
9 // Function_C will run every 15 ms
10 SCH_Add_Task ( Function_C , 3 , 15) ;
11 while (1) {
12 SCH_Dispatch_Tasks () ;
13 }
14 }
Program 5.3: Example of how a scheduler uses

2.1 The co-operative scheduler

A co-operative scheduler provides a single-tasking system architecture


Operation:

• Tasks are scheduled to run at specific times (either on a periodic or one-shot


basis)

Page 66 HCMUT - Computer Engineering


• When a task is scheduled to run it is added to the waiting list

• When the CPU is free, the next waiting task (if any) is executed

• The task runs to completion, then returns control to the scheduler

Implementation:

• The scheduler is simple and can be implemented in a small amount of code

• The scheduler must allocate memory for only a single task at a time

• The scheduler will generally be written entirely in a high-level language (such


as ‘C’)
• The scheduler is not a separate application; it becomes part of the developer’s
code

Performance:

• Obtaining rapid responses to external events requires care at the design stage
Reliability and safety:

Co-operate scheduling is simple, predictable, reliable and safe


A co-operative scheduler provides a simple, highly predictable environment. The
scheduler is written entirely in ‘C’ and becomes part of the application: this tends
to make the operation of the whole system more transparent and eases develop-
ment, maintenance and porting to different environments. Memory overheads are
17 bytes per task and CPU requirements (which vary with tick interval) are low.

2.2 Function pointers

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

A scheduler has the following key components:

• The scheduler data structure.

• An initialization function.

• A single interrupt service routine (ISR), used to update the scheduler at reg-
ular time intervals.

• A function for adding tasks to the scheduler.

• 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).

We consider each of the required components in this section

Page 68 HCMUT - Computer Engineering


2.3.1 Overview

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.

• We prepare the scheduler using the function SCH_Init().

• 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 timing of the Led_Display() function will be controlled by the function


SCH_Update(), an interrupt service routine triggered by the overflow of Timer
2:
1 void HAL_TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim ) {
2 SCH\ _Update ( ) ;
3 }
Program 5.6: Example of how to call SCH_Update function

• 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 }

Before considering these components in detail, we should acknowledge that this


is, undoubtedly, a complicated way of flashing an LED: if our intention were to
develop an LED flasher application that requires minimal memory and minimal
code size, this would not be a good solution. However, the key point is that we will
be able to use the same scheduler architecture in all our subsequent examples, in-
cluding a number of substantial and complex applications and the effort required
to understand the operation of this environment will be rapidly repaid.
It should also be emphasized that the scheduler is a ‘low-cost’ option: it consumes
a small percentage of the CPU resources (we will consider precise percentages
shortly). In addition, the scheduler itself requires no more than 17 bytes of mem-
ory for each task. Since a typical application will require no more than four to six
tasks, the task – memory budget (around 60 bytes) is not excessive, even on an
8-bit microcontroller.

2.3.2 The scheduler data structure and task array

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

15 // MUST BE ADJUSTED FOR EACH NEW PROJECT


16 # d e f i n e SCH_MAX_TASKS 40
17 # d e f i n e NO_TASK_ID 0
18 sTask SCH_tasks_G [ SCH_MAX_TASKS ] ;
Program 5.7: A struct of a task

The size of the task array


You must ensure that the task array is sufficiently large to store the tasks required
in your application, by adjusting the value of SCH_MAX_TASKS. For example, if
you schedule three tasks as follows:

• SCH_Add_Task(Function_A, 0, 2);

Page 70 HCMUT - Computer Engineering


• SCH_Add_Task(Function_B, 1, 10);

• 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.

2.3.3 The initialization function

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

2.3.4 The ‘Update’ function

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

23 void HAL_TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim ) {


24 SCH_Update ( ) ;
25 }
Program 5.9: Example of how to write an SCH_Update function

2.3.5 The ‘Add Task’ function

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

Here are some examples.


This set of parameters causes the function Do_X() to be executed once after 1,000
scheduler ticks:
SCH_Add_Task(Do_X,1000,0);
This does the same, but saves the task ID (the position in the task array) so that the
task may be subsequently deleted, if necessary (see SCH_Delete_Task() for further
information about the removal of tasks from the task array):
Task_ID = SCH_Add_Task(Do_X,1000,0);
This causes the function Do_X() to be executed regularly every 1,000 scheduler
ticks; the task will first be executed as soon as the scheduling is started:
SCH_Add_Task(Do_X,0,1000);
This causes the function Do_X() to be executed regularly every 1,000 scheduler
ticks; task will be first executed at T = 300 ticks, then 1,300, 2,300 etc:

Page 72 HCMUT - Computer Engineering


SCH_Add_Task(Do_X,300,1000);
1 /*
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− * −

2 SCH_Add_Task ( ) Causes a t a s k ( f u n c t i o n ) to be executed a t r e g u l a r


intervals
3 or a f t e r a user − defined delay
4 − * −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
*/
5 unsigned char SCH_Add_Task ( void ( * pFunction ) ( ) , unsigned i n t DELAY
, unsigned i n t PERIOD )
6 {
7 unsigned char Index = 0 ;
8 // F i r s t f i n d a gap in the a r r a y ( i f t h e r e i s one )
9 while ( ( SCH_tasks_G [ Index ] . pTask ! = 0 ) && ( Index <
SCH_MAX_TASKS) )
10 {
11 Index ++;
12 }
13 // Have we reached the end o f the l i s t ?
14 i f ( Index == SCH_MAX_TASKS)
15 {
16 // Task l i s t i s f u l l
17 // S e t the g l o b a l e r r o r v a r i a b l e
18 Error_code_G = ERROR_SCH_TOO_MANY_TASKS ;
19 // Also r e t u r n an e r r o r code
20 r e t u r n SCH_MAX_TASKS ;
21 }
22 // I f we ’ re here , t h e r e i s a space in the t a s k a r r a y
23 SCH_tasks_G [ Index ] . pTask = pFunction ;
24 SCH_tasks_G [ Index ] . Delay = DELAY ;
25 SCH_tasks_G [ Index ] . Period = PERIOD ;
26 SCH_tasks_G [ Index ] . RunMe = 0 ;
27 // r e t u r n p o s i t i o n o f t a s k ( to allow l a t e r d e l e t i o n )
28 r e t u r n Index ;
29 }
Program 5.10: An implementation of the scheduler ‘add task’ function

2.3.6 The ‘Dispatcher’

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 }

Program 5.11: An implementation of the scheduler ‘dispatch task’ function

The dispatcher is the only component in the Super Loop:

1 void main ( void )


2 {
3 ...
4 while ( 1 )
5 {
6 SCH_Dispatch_Tasks ( ) ;
7 }

Program 5.12: The dispatcher in the super loop

Do we need a Dispatch function?


At first inspection, the use of both the ‘Update’ and ‘Dispatch’ functions may seem
a rather complicated way of running the tasks. Specifically, it may appear that the
Dispatch function in unnecessary and that the Update function could invoke the
tasks directly. However, the split between the Update and Dispatch operations is
necessary, to maximize the reliability of the scheduler in the presence of long tasks.
Suppose we have a scheduler with a tick interval of 1 ms and, for whatever reason,
a scheduled task sometimes has a duration of 3 ms.
If the Update function runs the functions directly then – all the time the long task is
being executed – the tick interrupts are effectively disabled. Specifically, two ‘ticks’
will be missed. This will mean that all system timing is seriously affected and may
mean that two (or more) tasks are not scheduled to execute at all.
If the Update and Dispatch function are separated, system ticks can still be pro-
cessed while the long task is executing. This means that we will suffer task ‘jitter’
(the ‘missing’ tasks will not be run at the correct time), but these tasks will, even-
tually, run.

Page 74 HCMUT - Computer Engineering


2.3.7 The ‘Delete Task’ function

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

2.3.8 Reducing power consumption

An important feature of scheduled applications is that they can lend themselves to


low-power operation. This is possible because all modern MCU provide an ‘idle’
mode, where the CPU activity is halted, but the state of the processor is main-
tained. In this mode, the power required to run the processor is typically reduced
by around 50
This idle mode is particularly effective in scheduled applications because it may
be entered under software control, and the MCU returns to the normal operat-
ing mode when any interrupt is received. Because the scheduler generates regular
timer interrupts as a matter of course, we can put the system ‘ to sleep’ at the end
of every dispatcher call: it will then wake up when the next timer tick occurs.
This is an optional feature. Students can do by yourself by looking at the reference
manual of the MCU that is used.
1 void SCH_Go_To_Sleep ( ) {
2 // todo : Optional

Microcontroller Page 75
3 }
Program 5.14: An implementation of the scheduler ‘go to sleep’ function

2.3.9 Reporting errors

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;

To report these error codes, the scheduler has a function SCH_Report_Status(),


which is called from the Update function.
1 void SCH_Report_Status ( void ) {
2 # i f d e f SCH_REPORT_ERRORS
3 // ONLY APPLIES IF WE ARE REPORTING ERRORS
4 // Check f o r a new e r r o r code
5 i f ( Error_code_G ! = Last_error_code_G ) {
6 // Negative l o g i c on LEDs assumed
7 E r r o r _ p o r t = 255 − Error_code_G ;
8 Last_error_code_G = Error_code_G ;
9 i f ( Error_code_G ! = 0 ) {
10 E r r o r _ t i c k _ c o u n t _ G = 60000;
11 } else {
12 Error_tick_count_G = 0 ;
13 }
14 } else {
15 i f ( Error_tick_count_G != 0) {
16 i f ( − − E r r o r _ t i c k _ c o u n t _ G == 0 ) {
17 Error_code_G = 0 ; // Reset e r r o r code
18 }
19 }
20 }
21 # endif
22 }
Program 5.15: An implementation of the ‘report status’ function

Page 76 HCMUT - Computer Engineering


Note that error reporting may be disabled via the main.h header file:
1 // Comment t h i s l i n e out i f e r r o r r e p o r t i n g i s NOT r e q u i r e d
2 // # d e f i n e SCH_REPORT_ERRORS
3 //Where e r r o r r e p o r t i n g i s required , the port on which e r r o r codes
w i l l be d i s p l a y e d
4 // i s a l s o determined v i a main . h :
5 # i f d e f SCH_REPORT_ERRORS
6 // The port on which e r r o r codes w i l l be d i s p l a y e d
7 // ONLY USED IF ERRORS ARE REPORTED
8 # d e f i n e E r r o r _ p o r t PORTA
9 # endif
Program 5.16: Define a constant to allow errors are reported

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.

2.3.10 Adding a watchdog

The basic scheduler presented here does not provide support for a watchdog timer.
Such support can be useful and is easily added, as follows:

• Start the watchdog in the scheduler Start function.

• Refresh the watchdog in the scheduler Update function.

1 IWDG_HandleTypeDef hiwdg ;
2 s t a t i c u i n t 3 2 _ t counter_for_watchdog = 0 ;
3

4 void MX_IWDG_Init ( void ) {


5 hiwdg . I n s t a n c e = IWDG;
6 hiwdg . I n i t . P r e s c a l e r = IWDG_PRESCALER_32 ;
7 hiwdg . I n i t . Reload = 4 0 9 5 ;
8 i f ( HAL_IWDG_Init(&hiwdg ) ! = HAL_OK) {
9 Error_Handler ( ) ;
10 }
11 }
12 void Watchdog_Refresh ( void ) {
13 HAL_IWDG_Refresh(&hiwdg ) ;
14 }
15 unsigned char Is_Watchdog_Reset ( void ) {
16 i f ( counter_for_watchdog > 3 ) {
17 return 1;
18 }

Microcontroller Page 77
19 return 0;
20 }
21 void Watchdog_Counting ( void ) {
22 counter_for_watchdog ++;
23 }
24

25 void Reset_Watchdog_Counting ( void ) {


26 counter_for_watchdog = 0 ;
27 }
Program 5.17: An implementation of the ‘watchdog’ functions

2.3.11 Reliability and safety implications

• Make sure the task array is large enough

• Take care with function pointers

• Dealing with task overlap


Suppose we have two tasks in our application (Task A, Task B). We further
assume that Task A is to run every second and Task B every three seconds. We
assume also that each task has a duration of around 0.5 ms.
Suppose we schedule the tasks as follows (assuming a 1ms tick interval):
SCH_Add_Task(TaskA, 0, 1000);
SCH_Add_Task(TaskB, 0, 3000);
In this case, the two tasks will sometimes be due to execute at the same
time. On these occasions, both tasks will run, but Task B will always execute
after Task A. This will mean that if Task A varies in duration, then Task B will
suffer from ‘jitter’: it will not be called at the correct time when the tasks over-
lap.
Alternatively, suppose we schedule the tasks as follows:
SCH_Add_Task(TaskA, 0, 1000);
SCH_Add_Task(TaskB, 5, 3000);
Now, both tasks still run every 1,000 ms and 3,000 ms (respectively), as
required. However, Task A is explicitly scheduled always to run 5 ms before
Task B. As a result,Task B will always run on time.
In many cases, we can avoid all (or most) task overlaps simply by the ju-
dicious use of the initial task delays.

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.

Page 78 HCMUT - Computer Engineering


4 Problem

- Your system should have at least four functions:

• void SCH_Update(void):This function will be updated the remaining time of


each tasks that are added to a queue. It will be called in the interrupt timer,
for example 10 ms.

• void SCH_Dispatch_Tasks(void): This function will get the task in the queue
to run.

• uint32_t SCH_Add_Task(void (* pFunction)(), uint32_t DELAY, uint32_t PE-


RIOD): This function is used to add a task to the queue. It should return an
ID that is corresponding with the added task.

• uint8_t SCH_Delete_Task(uint32_t taskID): This function is used to delete


the task based on its ID.

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:

• A regular 10ms timer tick.

• Register a timeout to fire a callback every 10ms.

• Then, print the value returned by get_time every time this callback is re-
ceived.

• Note: Your timestamps must be at least accurate to the nearest 10ms.

• Register another timeout at a different interval in addition to the 500ms run-


ning concurrently (i.e. demo more than one timeout registered at a time).

• 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:

• Only supporting a single timeout registered at a time.

Microcontroller Page 79
• Delivering callbacks in the wrong order

• O(n) searches in the SCH_Update function.

• Interrupt frequencies greater than 10Hz, if your timer ticks regularly.

6 Submission

You need to

• Demonstrate your work in the lab class and then

• Submit your source code to the BKeL.

7 References

Page 80 HCMUT - Computer Engineering


CHƯƠNG 6

Flow and Error Control in


Communication
1 Introduction

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.

Page 82 HCMUT - Computer Engineering


2 Proteus simulation platform

Hình 6.1: Simulation circuit on Proteus


Some new components are listed bellow:

• 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.

3.1 UART Configuration

From the ioc file, select Connectivity, and then select the USART2. The parameter
settings for UART channel 2 (USART2) module are depicted as follows:

Hình 6.3: UART configuration in STMCube

Page 84 HCMUT - Computer Engineering


The UART channel in this lab is the Asynchronous mode, 9600 bits/s with no Par-
ity and 1 stop bit. After the uart is configured, the pins PA2 (Tx) and PA3(Rx) are
enabled.

Finally, the NVIC settings are checked to enable the UART interrupt, as follows:

Hình 6.4: Enable UART interrupt

3.2 ADC Input

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:

Hình 6.5: Enable UART interrupt

The ADC pin is configured to PA0 of the STM32, which is shown in the pinout view
dialog.

Finally, the PA5 is configured as a GPIO output, connected to a blinky LED.

4 UART loop-back communication

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

4 void HAL_UART_RxCpltCallback ( UART_HandleTypeDef * huart ) {


5 if ( huart - > Instance == USART2 ) {
6 HAL_UART_Transmit (& huart2 , & temp , 1 , 50) ;
7 HAL_UART_Receive_IT (& huart2 , & temp , 1) ;
8 }
9 }
10 /* USER CODE END 0 */
Program 6.1: Implement the UART interrupt service routine

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

10 HAL_UART_Receive_IT (& huart2 , & temp , 1) ;


11

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 ) ;

Page 86 HCMUT - Computer Engineering


6 HAL_UART_Transmit (& huart2 , ( void *) str , sprintf ( str , " % d \ n "
, ADC_value ) , 1000) ;
7 HAL_Delay (500) ;
8 }
Program 6.3: ADC reading from AN0

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

In this lab, a simple communication protocol is implemented as follows:

• From the console, user types !RST# to ask for a sensory data.

• The STM32 response the ADC_value, following a format !ADC=1234#, where


1234 presents for the value of ADC_value variable.

• The user ends the communication by sending !OK#

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.

6.1 Command parser

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

9 // HAL_UART_Transmit (& huart2 , & temp , 1 , 50) ;


10 buffer [ index_buffer ++] = temp ;
11 if ( index_buffer == 30) index_buffer = 0;

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

A state machine to extract a command is implemented in the while(1) of the main


function, as follows:
1 while (1) {
2 if ( buffer_flag == 1) {
3 command_parser_fsm () ;
4 buffer_flag = 0;
5 }
6 }
Program 6.5: State machine to extract the command

The output of the command parser is to set command_flag and command_data.


In this project, there are two commands, RTS and OK. The program skeleton is
proposed as follows:
1 while (1) {
2 if ( buffer_flag == 1) {
3 command_parser_fsm () ;
4 buffer_flag = 0;
5 }
6 uart_communiation_fsm () ;
7 }
Program 6.6: Program structure

6.2 Project implementation

Students are proposed to implement 2 FSM in seperated modules. Students are


asked to design the FSM before their implementations in STM32Cube.

Page 88 HCMUT - Computer Engineering


CHƯƠNG 7

MIDTERM 2022
1 Introduction

In this midterm project, a count-down system is designed and implemented in


Proteus simulation. As it can be seen from Fig. 8.1, main components used in this
project are the STM32F103C6, one LED, one LED7 segment and 3 different but-
tons.

Hình 7.1: Proteus schematic for count-down system

The main functions of the system are listed bellow:

• LED7 segment is used to display a counter ranging from 0 to 9.

• 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.

Page 90 HCMUT - Computer Engineering


• All the buttons must be DEBOUNCING by using a timer interrupt service
routing. A timeout for long press event is 3 seconds.

• There is no HAL_Delay() function in your source code. All the delay behavior
must be based on a software timer.

• This report must be submitted with your answer.

• GitHub link for the source code and demo video link must be public access.

2 Implement and Report

2.1 Proteus schematic - 1 point

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.

2.2 State machine Step 1 - 2 points

A state machine is required in this step to perform just only the normal-press (or a
button push) behavior of three buttons:

• Whenever the RESET is pressed, the counter value is 0.

• When INC is pressed, the counter is increased by 1. When counter is 9, it


comes back to 0.

• When DEC is pressed, the counter is decreased by 1. When counter is 0, it


rolls back to 9.

The value of the counter is displayed on the LED7 Segment.

Your report: Present your state machine in this part.

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:

Hình 7.2: Long press behavior for INC button

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.

2.4 State machine Step 3 - 2 points

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.

Page 92 HCMUT - Computer Engineering


2.5 Led Blinky for Debugging - 1 point

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.

2.6 Github and Demo

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)

Your github link

And a link for just one demo video is also needed to present here.

Your video link

3 Extra exercise - Engineer mindset -1 point

In this course, we encourage you to obtain an innovative mindset to solve daily


problem. In this question, we would expect you to write a C program to solve the
following problem.

Suffix with Unit

EXample:

1 suffixWithUnit(123) => 123

2 suffixWithUnit(1234) => 1.234 Kilo

3 suffixWithUnit(12345) => 12.345 Kilo

4 suffixWithUnit(1234567) => 1.234567 Mega

5 suffixWithUnit(12345678) => 12.345678 Mega

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.

Page 94 HCMUT - Computer Engineering


CHƯƠNG 8

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.

Hình 8.1: Proteus schematic for count-down system

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:

Page 96 HCMUT - Computer Engineering


• Chu kì của ngắt timer là 10ms. Giá trị của counter khi cài đặt timer có thể là
9 hoặc 10 đều được chấp nhận (trong trường hợp này, prescaller là 7999).

• 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.

2 Hiện thực và Report

2.1 Sơ đồ nguyên lý trên Proteus - 1 điểm

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ý.

2.2 State machine Step 1 - 2 điểm

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 nút RESET is được nhấn, giá trị counter sẽ là 0.

• 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.

Report: Trình bày máy trạng thái của hệ thống.

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

2.3 State machine Step 2 - 2 điểm

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 8.2: Long press behavior for INC button

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.

2.4 State machine Step 3 - 2 points

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.

Page 98 HCMUT - Computer Engineering


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.

2.5 Led Blinky for Debugging - 1 điểm

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.

2.6 Github và Demo

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.

Your github link

Link cho 1 video demo cũng sẽ được trình bày ở phần này.

Your video link

3 Bài tập thêm - Engineer mindset - 1 điểm

In this course, we encourage you to obtain an innovative mindset to solve daily


problem. In this question, we would expect you to write a C program to solve the
following problem.

Suffix with Unit

EXample:

1 suffixWithUnit(123) => 123

Microcontroller Page 99
2 suffixWithUnit(1234) => 1.234 Kilo

3 suffixWithUnit(12345) => 12.345 Kilo

4 suffixWithUnit(1234567) => 1.234567 Mega

5 suffixWithUnit(12345678) => 12.345678 Mega

Prototype
1 string suffixWithUnit ( double number ) {
2 }

How would you solve them? Please share your thinking to solve this problem and
provide your answer.

Page 100 HCMUT - Computer Engineering

You might also like