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

STM32 CubeIDE Course

Uploaded by

Muhammad Rifai
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
246 views

STM32 CubeIDE Course

Uploaded by

Muhammad Rifai
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 78

MODUL DASAR STM STM32F103C8T6

DENGAN CUBEIDE DAN ST-Link V2

Pembelajaran STM32 pada modul ini meliputi :


1. Pemahaman Pin STM32 Blue Pill.
2. Bagaimana menggunakan Software CubeIDE.
3. Bagaimana mengupload STM32CudeIDE menggunakan ST-Link V2.

PIN STM32F103C8T6:

Power Pins:
It has two 3.3V pins connected to the onboard 3.3V regulated power supply which is
used to power up the onboard components including the microcontroller and other
external components like sensors and breakout boards. So, if you need 3.3 volts to
power up your sensor; you can simply connect 3.3V from any of these two pins.
Ground Pins:
It has 3 ground pins, one on this side, and the other two on this side. All these ground
pins are interconnected so you can take a ground connection from any of these 3 pins.
5V Pin:
The 5V pin is the Input Power pin means you can connect an external regulated 5V
power supply to this pin to power up the STM32 board. You don’t need to worry about
the microcontroller, the onboard 3.3V regulator will convert the 5 volts into 3.3 volts.
While performing your initial experiments; you don’t need to connect an external 5V
regulated power supply. You can just use your laptop as the power source. But sooner
or later, you will need an external power supply, because its impractical to use your
laptop or PC as the power source. So, I recommend you should build yourself a 5V and
3A power supply. In some examples, I have used my designed 5V and 3A power supply
for powering up the STM32 Microcontroller board.

STM32F103C8T6 GPIOs:
It has a total of 32 GPIO pins numbered as B0 to B15, C13 to C15, and A0 to A15.
While in the Pinout diagram these are numbered as PB, PC, and PA.
STM32F103C8T6 Analog Pins:
It has a total of 10 analog pins starting from A0 to B1. These pins are connected to the
built-in analog-to-digital converter (ADC) of the microcontroller and can be used to
measure analog signals.
I2C pins:
It has two i2c supported ports. The I2C1 is available on pins B6 and B7. B6 is the SCL
and B7 is the SDA. The I2C 2nd port is available on pins B10 and B11. B10 is the SCL and
B11 is the SDA.
Serial communication Pins:
It has 3 Serial ports. The USART1 is available on pins A9 and A10 (A9 is the Tx and A10
is the Rx). USART2 is available on pins A2 and A3(A2 is the Tx and A3 is the Rx).
USART3 is available on pins B10 and B11(B10 is the Tx and B11 is the Rx).
And it also has pins for the SPI, PWM, and CAN. All the pins are clearly labeled.
Let me also tell you. When you hold the STM32 controller board like this, All the pins on
the right side from B9 to B12 are 5V tolerant except the B5 pin. PB_5 or B5 is 3.3V
standard Pin.
All the pins on the left side from Vbat “VB” to the Reset pin are 3.3V Standard pins
except the B10 and B11 which are 5V tolerant Pins.
JTAG/SWD Pins:
These 4 pins on the STM32 Board are the SWD (Serial Wire Debug) pins that allow you
to connect a debugger or a programmer like the ST-Link V2.

ST-Link V2:

The ST-link V2 allows you to program the firmware onto STM32 microcontrollers and
also provides debugging capabilities. It connects to the STM32 Microcontroller’s SWD
Pins for programming and debugging operations. It is designed to work seamlessly with
the STM32CubeIDE which is an integrated development environment; provided by
STMicroelectronics. I think, I have shared enough valuable information with you guys
and now its time to download and install the STM32CubeIDE.
STM32CubeIDE Download & Installation:
While you are on the stm32cubeide official page go to the SMT32 Developer Zone and
from the drop down list select STM32CubeIDE.

Click on the Download STM32CubeIDE button.

Download the one as per your needs. In my case I am going to continue with Windows
Latest Version.
If you are already a registered use then the STM32CubeIDE software download with
start right away. But if you are not a registered user then first you will have to create an
account. Just go through the following steps. If in case, you face any difficulty then you
can watch my Video Tutorial given at the end of this Article.
 Accept the License Agreement…
 Click on the Register button…
 Click on the Create Account…
 Enter the required details…
 Finally, check the box and click on the Register button…
 Next, open your email account and click on the Validate Now…
 Complete your registration by entering your password…
 Click the submit button and you will be notified “The registration has been
completed. Please login here…
 Enter your registered email id and password and click on the Login button…
 Now, you can click on the Download STM32CubeIDE and start downloading the
STM32CubeIDE…
 Extract the downloaded Zip Folder…
 Open the folder and run the .exe file to start the installation process…
There is nothing complicated, just follow the instructions, and if any message pops up
click on the accept button “It might be a driver”, anyway you will know.
The STM32CubeIDE has been installed.

Example #1: STM32F103C8T6 Onboard Led Blinking

In this first example, we are going to control the STM32 onboard LED which is
connected to PC13 Pin. Before, we start the programming, first we will need to connect
the STM32 board to the laptop or PC. For this, we are going to use the ST-LINK V2.
Simply connect the ST-LINK V2 3.3V and GND pins to the 3.3V and GND pins on the
STM32 board. Connect the SWCLK and SWDIO pins of the ST-Link to the STM32 SWCLK
and SWDIO Pins.

Finally, connect the ST-Link V2 to your Laptop/PC. Throughout these examples, the ST-
Link V2 will remain connected to the STM32 board.

Each time, you upload program to the STM32 board, you will have to connect the ST-
Link V2 to the Laptop/PC.
Open the STM32CubeIDE Software. If you are running this for the first time you might
get a notification or message “Select a directory as workspace” where you want to
store all your projects or you can go with the default address.

Finally, click on the Launch Button. STM32CubeIDE will display the welcome screen,
indicating that the IDE has been successfully launched.

To start a new project, go to the File Menu, then to New, and click on the STM32 Project.
STM32CubeIDE may take a moment to load and download the necessary libraries.
Please be patient during this process as it may take some time depending on the speed
of your internet connection.
On the Target Selection Window and while the MCU Selector Tab is active; select the
Commercial Part Number that is STM32F103C8T6, Select it from the MCUs list and
then click on the Next Button.

Write the project name, select the Targeted Language, target Binary Type, and Targeted
Project Type. Finally, click on the Finish Button.
On the Pinout & Configuration Tab click on the System Core and select SYS and click on
the Debug and select Serial Wire.

Click on PC13 and select GPIO_Output as the onboard LED is connected to this pin.
Finally, click on the save button if it asks you Do you want to generate Code? click yes,
again press the yes button.
STM32CubeIDE will generate the necessary code based on your pin configuration and
open the associated perspective. This perspective provides you with the appropriate
tools and views for further development and customization of your project. While you
are on the main.c file, scroll down. Now, in the while(1) function we will write code for
blinking the LED.
STM32CubeIDE Led Blinking Code:
1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21
22 /* Private includes ----------------------------------------------------------*/
23 /* USER CODE BEGIN Includes */
24
25 /* USER CODE END Includes */
26
27 /* Private typedef -----------------------------------------------------------*/
28 /* USER CODE BEGIN PTD */
29
30 /* USER CODE END PTD */
31
32 /* Private define ------------------------------------------------------------*/
33 /* USER CODE BEGIN PD */
34
35 /* USER CODE END PD */
36
37 /* Private macro -------------------------------------------------------------*/
38 /* USER CODE BEGIN PM */
39
40 /* USER CODE END PM */
41
42 /* Private variables ---------------------------------------------------------*/
43
44 /* USER CODE BEGIN PV */
45
46 /* USER CODE END PV */
47
48 /* Private function prototypes -----------------------------------------------*/
49 void SystemClock_Config(void);
50 static void MX_GPIO_Init(void);
51 /* USER CODE BEGIN PFP */
52
53 /* USER CODE END PFP */
54
55 /* Private user code ---------------------------------------------------------*/
56 /* USER CODE BEGIN 0 */
57
58 /* USER CODE END 0 */
59
60 /**
61 * @brief The application entry point.
62 * @retval int
63 */
64 int main(void)
65 {
66 /* USER CODE BEGIN 1 */
67
68 /* USER CODE END 1 */
69
70 /* MCU Configuration--------------------------------------------------------*/
71
72 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
73 HAL_Init();
74
75 /* USER CODE BEGIN Init */
76
77 /* USER CODE END Init */
78
79 /* Configure the system clock */
80 SystemClock_Config();
81
82 /* USER CODE BEGIN SysInit */
83
84 /* USER CODE END SysInit */
85
86 /* Initialize all configured peripherals */
87 MX_GPIO_Init();
88 /* USER CODE BEGIN 2 */
89
90 /* USER CODE END 2 */
91
92 /* Infinite loop */
93 /* USER CODE BEGIN WHILE */
94 while (1)
95 {
96
97 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);
98 HAL_Delay(100);
99 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
100 HAL_Delay(100);
101
102 /* USER CODE END WHILE */
103
104 /* USER CODE BEGIN 3 */
105 }
106 /* USER CODE END 3 */
107 }
108
109 /**
110 * @brief System Clock Configuration
111 * @retval None
112 */
113 void SystemClock_Config(void)
114 {
115 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
116 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
117
118 /** Initializes the RCC Oscillators according to the specified parameters
119 * in the RCC_OscInitTypeDef structure.
120 */
121 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
122 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
123 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
124 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
125 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
126 {
127 Error_Handler();
128 }
129
130 /** Initializes the CPU, AHB and APB buses clocks
131 */
132 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
133 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
134 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
135 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
136 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
137 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
138
139 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
140 {
141 Error_Handler();
142 }
143 }
144
145 /**
146 * @brief GPIO Initialization Function
147 * @param None
148 * @retval None
149 */
150 static void MX_GPIO_Init(void)
151 {
152 GPIO_InitTypeDef GPIO_InitStruct = {0};
153 /* USER CODE BEGIN MX_GPIO_Init_1 */
154 /* USER CODE END MX_GPIO_Init_1 */
155
156 /* GPIO Ports Clock Enable */
157 __HAL_RCC_GPIOC_CLK_ENABLE();
158 __HAL_RCC_GPIOA_CLK_ENABLE();
159
160 /*Configure GPIO pin Output Level */
161 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
162
163 /*Configure GPIO pin : PC13 */
164 GPIO_InitStruct.Pin = GPIO_PIN_13;
165 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
166 GPIO_InitStruct.Pull = GPIO_NOPULL;
167 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
168 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
169
170 /* USER CODE BEGIN MX_GPIO_Init_2 */
171 /* USER CODE END MX_GPIO_Init_2 */
172 }
173
174 /* USER CODE BEGIN 4 */
175
176 /* USER CODE END 4 */
177
178 /**
179 * @brief This function is executed in case of error occurrence.
180 * @retval None
181 */
182 void Error_Handler(void)
183 {
184 /* USER CODE BEGIN Error_Handler_Debug */
185 /* User can add his own implementation to report the HAL error return state */
186 __disable_irq();
187 while (1)
188 {
189 }
190 /* USER CODE END Error_Handler_Debug */
191 }
192
193 #ifdef USE_FULL_ASSERT
194 /**
195 * @brief Reports the name of the source file and the source line number
196 * where the assert_param error has occurred.
197 * @param file: pointer to the source file name
198 * @param line: assert_param error line source number
199 * @retval None
200 */
201 void assert_failed(uint8_t *file, uint32_t line)
202 {
203 /* USER CODE BEGIN 6 */
204 /* User can add his own implementation to report the file name and line number,
205 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
206 /* USER CODE END 6 */
207 }
208 #endif /* USE_FULL_ASSERT */
Once the code is completed click on the Hammer Icon or Build, There are no errors and
warnings now we can click on the Play button

Click on the Debugger Tab, Select ST-LINK, and Click on Show Generator options.
Set the Reset Mode to Software system reset, click the Apply button, and then click the
OK button.

After pressing the OK button, if you see the shutdown command invoked.
Then you can remove the ST-Link V2, and Plug it again to restart the STM32 Controller
board.

You can see the Onboard LED is blinking. We just built our first Led blinking project.
In next example, I am going to explain how to control external LEDs.

Example #2: STM32F103C8T6 External Leds


In this 2nd example, we are going to control external LEDs. But to keep things simple
and easy to follow, first let’s start with a single LED and afterwards we will control
multiple LEDs. Anyway, let me explain the connections.
The Cathode legs of all the Leds are connected together and then connected to the GND
pin on the STM32 board. The Anode Legs of all the LEDs are connected to the GPIO pins
PB6, PB7, PB8, and PB9 through these current limiting resistors (330 ohms).
Connect the ST-Link V2 to your Laptop/PC.
The following steps are exactly the same as explained in the first example.
While the STM32CubeIDE is open, go to the File, then to New, and click on the STM32
Project. STM32CubeIDE may take a moment to load and download the necessary
libraries. Please be patient during this process as it may take some time depending on
the speed of your internet connection.
On the Target Selection Window and while the MCU Selector Tab is active; select
the Commercial Part Number that is STM32F103C8T6. Select it from the MCUs
list and then click on the Next Button. Write the project name, Select the Targeted
Language,Target Binary Type, and Targeted Project Type.
On the Pinout & Configuration Tab click on the System Core and select SYS and Click on
the Debug and select Serial Wire.
Click on PB9 and select GPIO_Output as the external LED is connected to this pin.

Repeat the same steps for the other three GPIO pins to which the external LEDs are
connected. Finally, click on the save button if it asks you Do you want to generate Code?
click yes, again press the yes button.
STM32CubeIDE will generate the necessary code based on your pin configuration and
open the associated perspective. This perspective provides you with the appropriate
tools and views for further development and customization of your project. When the
main.c file is opened, Scroll down, and in the while(1) function we will write code for
blinking one LED that is connected to B9 or PB9.
Single External Led STM32CubeIDE Code:
1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21
22 /* Private includes ----------------------------------------------------------*/
23 /* USER CODE BEGIN Includes */
24
25 /* USER CODE END Includes */
26
27 /* Private typedef -----------------------------------------------------------*/
28 /* USER CODE BEGIN PTD */
29
30 /* USER CODE END PTD */
31
32 /* Private define ------------------------------------------------------------*/
33 /* USER CODE BEGIN PD */
34
35 /* USER CODE END PD */
36
37 /* Private macro -------------------------------------------------------------*/
38 /* USER CODE BEGIN PM */
39
40 /* USER CODE END PM */
41
42 /* Private variables ---------------------------------------------------------*/
43
44 /* USER CODE BEGIN PV */
45
46 /* USER CODE END PV */
47
48 /* Private function prototypes -----------------------------------------------*/
49 void SystemClock_Config(void);
50 static void MX_GPIO_Init(void);
51 /* USER CODE BEGIN PFP */
52
53 /* USER CODE END PFP */
54
55 /* Private user code ---------------------------------------------------------*/
56 /* USER CODE BEGIN 0 */
57
58 /* USER CODE END 0 */
59
60 /**
61 * @brief The application entry point.
62 * @retval int
63 */
64 int main(void)
65 {
66 /* USER CODE BEGIN 1 */
67
68 /* USER CODE END 1 */
69
70 /* MCU Configuration--------------------------------------------------------*/
71
72 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
73 HAL_Init();
74
75 /* USER CODE BEGIN Init */
76
77 /* USER CODE END Init */
78
79 /* Configure the system clock */
80 SystemClock_Config();
81
82 /* USER CODE BEGIN SysInit */
83
84 /* USER CODE END SysInit */
85
86 /* Initialize all configured peripherals */
87 MX_GPIO_Init();
88 /* USER CODE BEGIN 2 */
89
90 /* USER CODE END 2 */
91
92 /* Infinite loop */
93 /* USER CODE BEGIN WHILE */
94 while (1)
95 {
96
97 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1);
98 HAL_Delay(500);
99 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 0);
100 HAL_Delay(500);
101
102 /* USER CODE END WHILE */
103
104 /* USER CODE BEGIN 3 */
105 }
106 /* USER CODE END 3 */
107 }
108
109 /**
110 * @brief System Clock Configuration
111 * @retval None
112 */
113 void SystemClock_Config(void)
114 {
115 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
116 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
117
118 /** Initializes the RCC Oscillators according to the specified parameters
119 * in the RCC_OscInitTypeDef structure.
120 */
121 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
122 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
123 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
124 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
125 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
126 {
127 Error_Handler();
128 }
129
130 /** Initializes the CPU, AHB and APB buses clocks
131 */
132 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
133 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
134 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
135 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
136 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
137 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
138
139 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
140 {
141 Error_Handler();
142 }
143 }
144
145 /**
146 * @brief GPIO Initialization Function
147 * @param None
148 * @retval None
149 */
150 static void MX_GPIO_Init(void)
151 {
152 GPIO_InitTypeDef GPIO_InitStruct = {0};
153 /* USER CODE BEGIN MX_GPIO_Init_1 */
154 /* USER CODE END MX_GPIO_Init_1 */
155
156 /* GPIO Ports Clock Enable */
157 __HAL_RCC_GPIOA_CLK_ENABLE();
158 __HAL_RCC_GPIOB_CLK_ENABLE();
159
160 /*Configure GPIO pin Output Level */
161 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9,
162 GPIO_PIN_RESET);
163
164 /*Configure GPIO pins : PB6 PB7 PB8 PB9 */
165 GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
166 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
167 GPIO_InitStruct.Pull = GPIO_NOPULL;
168 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
169 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
170
171 /* USER CODE BEGIN MX_GPIO_Init_2 */
172 /* USER CODE END MX_GPIO_Init_2 */
173 }
174
175 /* USER CODE BEGIN 4 */
176
177 /* USER CODE END 4 */
178
179 /**
180 * @brief This function is executed in case of error occurrence.
181 * @retval None
182 */
183 void Error_Handler(void)
184 {
185 /* USER CODE BEGIN Error_Handler_Debug */
186 /* User can add his own implementation to report the HAL error return state */
187 __disable_irq();
188 while (1)
189 {
190 }
191 /* USER CODE END Error_Handler_Debug */
192 }
193
194 #ifdef USE_FULL_ASSERT
195 /**
196 * @brief Reports the name of the source file and the source line number
197 * where the assert_param error has occurred.
198 * @param file: pointer to the source file name
* @param line: assert_param error line source number
199
* @retval None
200
*/
201
void assert_failed(uint8_t *file, uint32_t line)
202
{
203
/* USER CODE BEGIN 6 */
204
/* User can add his own implementation to report the file name and line number,
205
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
206
/* USER CODE END 6 */
207
}
208
#endif /* USE_FULL_ASSERT */

Multiple External Led STM32CubeIDE Code:


1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1);
2 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1);
3 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
4 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1);
5
6 HAL_Delay(500);
7
8 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 0);
9 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 0);
10 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0);
11 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 0);
12
13 HAL_Delay(500);
14
15 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1);
16 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1);
17 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
18 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1);
19
20 HAL_Delay(500);
21
22 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1);
23 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 0);
24 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0);
25 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1);
26
27 HAL_Delay(500);
28
29 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1);
30 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1);
31 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
32 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1);
33
34 HAL_Delay(500);
35 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 0);
36 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1);
37 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
38 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 0);
39 HAL_Delay(500);
Copy the above code, and paste it in your main.c file. I am sure now you know how to
save the file, and how to run the project, I already explained this in the Example #1.
Anyway, Let’s click on the Play button.

You can see this time I am able to control multiple LEDs. Now, you can modify this
program to control less or more LEDs. In next example, I am going to explain how to
read digital input.
Example #3: STM32F103C8T6 Digital Input
In this 3rd example, you are going to learn how to read a digital input on any GPIO pin of
the STM32 microcontroller board. We will be monitoring and controlling both at the
same time. Each time the button is pressed the Led is going to change its state. Anyway,
Let’s take a look at the wiring.

The Anode Leg of the Led is connected to the STM32 GPIO pin PA1 through a 330 ohm
current limiting resistor and the Cathode Leg of the Led is connected to the GND.
One side of the Push Button is connected to the GND and the other side of the Push
Button is connected to 3.3V through a 10k ohm Pull-Up resistor. A wire from the middle
is connected to the STM32 GPIO Pin PB1. When the button is open the PB1 pin reads
3.3V and when the button is pressed it reads 0V.
Just like example number1 and example number2 connect the STM32 board to the
Laptop/PC via the ST-Link V2.
The following steps are exactly the same as explained in the first example. Incase if there is
any confusion; watch the video tutorial given at the end of this article.
While the STM32CubeIDE is open, go to the File, then to New, and click on the STM32
Project. STM32CubeIDE may take a moment to load and download the necessary libraries.
Please be patient during this process as it may take some time depending on the speed of
your internet connection.
On the Target Selection Window and while the MCU Selector Tab is active; select
the Commercial Part Number that is STM32F103C8T6. Select it from the MCUs
list and then click on the Next Button. Write the project name, Select the Targeted
Language,Target Binary Type, and Targeted Project Type.
On the Pinout & Configuration Tab click on the System Core and select SYS and click on
the Debug and select Serial Wire.

On the Pinout View,


Click on PA1 and select GPIO_Output.
Click on PB1 and select GPIO_Input.
Finally, click on the save button if it asks you Do you want to generate Code? click yes;
and again press the yes button.
STM32CubeIDE will generate the necessary code based on your pin configuration and
open the associated perspective. This perspective provides you with the appropriate
tools and views for further development and customization of your project.
I have already written some code, so I am going to copy and paste it in the existing code;
in the main.c file.

Push Button STM32CubeIDE Code:


1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21
22 /* Private includes ----------------------------------------------------------*/
23 /* USER CODE BEGIN Includes */
24
25 /* USER CODE END Includes */
26
27 /* Private typedef -----------------------------------------------------------*/
28 /* USER CODE BEGIN PTD */
29
30 /* USER CODE END PTD */
31
32 /* Private define ------------------------------------------------------------*/
33 /* USER CODE BEGIN PD */
34
35 /* USER CODE END PD */
36
37 /* Private macro -------------------------------------------------------------*/
38 /* USER CODE BEGIN PM */
39
40 /* USER CODE END PM */
41
42 /* Private variables ---------------------------------------------------------*/
43
44 /* USER CODE BEGIN PV */
45
46 /* USER CODE END PV */
47
48 /* Private function prototypes -----------------------------------------------*/
49 void SystemClock_Config(void);
50 static void MX_GPIO_Init(void);
51 /* USER CODE BEGIN PFP */
52
53 /* USER CODE END PFP */
54
55 /* Private user code ---------------------------------------------------------*/
56 /* USER CODE BEGIN 0 */
57
58 /* USER CODE END 0 */
59
60 /**
61 * @brief The application entry point.
62 * @retval int
63 */
64 int main(void)
65 {
66 /* USER CODE BEGIN 1 */
67
68 /* USER CODE END 1 */
69
70 /* MCU Configuration--------------------------------------------------------*/
71
72 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
73 HAL_Init();
74
75 /* USER CODE BEGIN Init */
76
77 /* USER CODE END Init */
78
79 /* Configure the system clock */
80 SystemClock_Config();
81
82 /* USER CODE BEGIN SysInit */
83
84 /* USER CODE END SysInit */
85
86 /* Initialize all configured peripherals */
87 MX_GPIO_Init();
88 /* USER CODE BEGIN 2 */
89
90 /* USER CODE END 2 */
91
92 /* Infinite loop */
93 /* USER CODE BEGIN WHILE */
94 // Variable to store the previous button state
95 GPIO_PinState prevButtonState = GPIO_PIN_SET;
96 while (1)
97 {
98
99 // Read the current button state
100 GPIO_PinState currentButtonState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
101
102 // Check if the button state has changed
103 if (currentButtonState != prevButtonState)
104 {
105 // Debounce the button
106 HAL_Delay(50);
107
108 // Read the button state again
109 currentButtonState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
110
111 // Update the previous button state
112 prevButtonState = currentButtonState;
113
114 // Check if the button is pressed (active LOW)
115 if (currentButtonState == GPIO_PIN_RESET)
116 {
117 // Toggle the LED state
118 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
119 }
120 }
121 /* USER CODE END WHILE */
122
123 /* USER CODE BEGIN 3 */
124 }
125 /* USER CODE END 3 */
126 }
127
128 /**
129 * @brief System Clock Configuration
130 * @retval None
131 */
132 void SystemClock_Config(void)
133 {
134 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
135 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
136
137 /** Initializes the RCC Oscillators according to the specified parameters
138 * in the RCC_OscInitTypeDef structure.
139 */
140 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
141 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
142 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
143 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
144 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
145 {
146 Error_Handler();
147 }
148
149 /** Initializes the CPU, AHB and APB buses clocks
150 */
151 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
152 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
153 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
154 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
155 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
156 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
157
158 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
159 {
160 Error_Handler();
161 }
162 }
163
164 /**
165 * @brief GPIO Initialization Function
166 * @param None
167 * @retval None
168 */
169 static void MX_GPIO_Init(void)
170 {
171 GPIO_InitTypeDef GPIO_InitStruct = {0};
172 /* USER CODE BEGIN MX_GPIO_Init_1 */
173 /* USER CODE END MX_GPIO_Init_1 */
174
175 /* GPIO Ports Clock Enable */
176 __HAL_RCC_GPIOA_CLK_ENABLE();
177 __HAL_RCC_GPIOB_CLK_ENABLE();
178
179 /*Configure GPIO pin Output Level */
180 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
181
182 /*Configure GPIO pin : PA1 */
183 GPIO_InitStruct.Pin = GPIO_PIN_1;
184 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
185 GPIO_InitStruct.Pull = GPIO_NOPULL;
186 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
187 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
188
189 /*Configure GPIO pin : PB1 */
190 GPIO_InitStruct.Pin = GPIO_PIN_1;
191 GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
192 GPIO_InitStruct.Pull = GPIO_NOPULL;
193 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
194
195 /* USER CODE BEGIN MX_GPIO_Init_2 */
196 /* USER CODE END MX_GPIO_Init_2 */
197 }
198
199 /* USER CODE BEGIN 4 */
200
201 /* USER CODE END 4 */
202
203 /**
204 * @brief This function is executed in case of error occurrence.
205 * @retval None
206 */
207 void Error_Handler(void)
208 {
209 /* USER CODE BEGIN Error_Handler_Debug */
210 /* User can add his own implementation to report the HAL error return state */
211 __disable_irq();
212 while (1)
213 {
214 }
215 /* USER CODE END Error_Handler_Debug */
216 }
217
218 #ifdef USE_FULL_ASSERT
219 /**
220 * @brief Reports the name of the source file and the source line number
221 * where the assert_param error has occurred.
222 * @param file: pointer to the source file name
223 * @param line: assert_param error line source number
224 * @retval None
225 */
226 void assert_failed(uint8_t *file, uint32_t line)
227 {
228 /* USER CODE BEGIN 6 */
229 /* User can add his own implementation to report the file name and line number,
230 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
231 /* USER CODE END 6 */
232 }
233 #endif /* USE_FULL_ASSERT */
Copy the above program and paste it into your main.c file. Save the file, and click on the
hammer icon, and play button as we did in the example number1, just repeat the same
steps.
You can see, each time I press the button, the Led changes its state. You can replace the
push button with a digital IR Sensor and the LED with a Relay to make a contactless
AC/DC load control system. You can use any type of Digital Sensor. Even you can
convert this simple project into a security system using a Digital PIR Sensor. Anyway, in
next example we are going to use I2C supported 16×2 LCD with the STM32.

Example #4: STM32F103C8T6 16×2 LCD


In this 4th example, I am going to show you, how to print text on this I2C supported 16×2
LCD. This is one of the most commonly used LCD used for displaying text messages and
sensor values. Let me explain the connections.

Connect the VCC and GND pins of the I2C supported 16×2 LCD to the STM32 board 5V
and GND pins. Connect the SCL and SDA pins of the 16×2 LCD to the STM32 GPIO Pins
PB10 and PB11. You can follow this circuit diagram.
Connect your STM32 board to the Laptop/PC via ST-link as explained in the previous
examples.
The following steps are exactly the same as explained in the first example. Incase if there is
any confusion; watch the video tutorial given at the end of this article.
While the STM32CubeIDE is open, go to the File, then to New, and click on the STM32
Project. STM32CubeIDE may take a moment to load and download the necessary libraries.
Please be patient during this process as it may take some time depending on the speed of
your internet connection.
On the Target Selection Window and while the MCU Selector Tab is active; select
the Commercial Part Number that is STM32F103C8T6. Select it from the MCUs
list and then click on the Next Button. Write the project name, Select the Targeted
Language,Target Binary Type, and Targeted Project Type.
As usual, on the Pinout & Configuration Tab click on the System Core and select SYS and
click on the Debug and select Serial Wire.
Click on the connectivity and select I2C2. You can see the PB_10 and PB_11 pins are
selected.

Finally, click on the save button if it asks you Do you want to generate Code? click yes…
again press the yes button.
STM32CubeIDE will generate the necessary code based on your pin configuration and
open the associated perspective. This perspective provides you with the appropriate
tools and views for further development and customization of your project.
On left side, click on Core then Inc, right click and select new File.
Write the file name liquidcrystal.h.

You can see the liquidcrystal.h header file has been added.
Now copy and paste the following code.

Liquidcystal_i2c.h STM32CubeIDE Header File Code:


1 /* create liquidcrystal_i2c.h file and paste in Core/Inc Folder */
2
3 #ifndef LIQUIDCRYSTAL_I2C_H_
4 #define LIQUIDCRYSTAL_I2C_H_
5
6 #include "stm32f1xx_hal.h"
7
8 /* Command */
9 #define LCD_CLEARDISPLAY 0x01
10 #define LCD_RETURNHOME 0x02
11 #define LCD_ENTRYMODESET 0x04
12 #define LCD_DISPLAYCONTROL 0x08
13 #define LCD_CURSORSHIFT 0x10
14 #define LCD_FUNCTIONSET 0x20
15 #define LCD_SETCGRAMADDR 0x40
16 #define LCD_SETDDRAMADDR 0x80
17
18 /* Entry Mode */
19 #define LCD_ENTRYRIGHT 0x00
20 #define LCD_ENTRYLEFT 0x02
21 #define LCD_ENTRYSHIFTINCREMENT 0x01
22 #define LCD_ENTRYSHIFTDECREMENT 0x00
23
24 /* Display On/Off */
25 #define LCD_DISPLAYON 0x04
26 #define LCD_DISPLAYOFF 0x00
27 #define LCD_CURSORON 0x02
28 #define LCD_CURSOROFF 0x00
29 #define LCD_BLINKON 0x01
30 #define LCD_BLINKOFF 0x00
31
32 /* Cursor Shift */
33 #define LCD_DISPLAYMOVE 0x08
34 #define LCD_CURSORMOVE 0x00
35 #define LCD_MOVERIGHT 0x04
36 #define LCD_MOVELEFT 0x00
37
38 /* Function Set */
39 #define LCD_8BITMODE 0x10
40 #define LCD_4BITMODE 0x00
41 #define LCD_2LINE 0x08
42 #define LCD_1LINE 0x00
43 #define LCD_5x10DOTS 0x04
44 #define LCD_5x8DOTS 0x00
45
46 /* Backlight */
47 #define LCD_BACKLIGHT 0x08
48 #define LCD_NOBACKLIGHT 0x00
49
50 /* Enable Bit */
51 #define ENABLE 0x04
52
53 /* Read Write Bit */
54 #define RW 0x0
55
56 /* Register Select Bit */
57 #define RS 0x01
58
59 /* Device I2C Address */
60 #define DEVICE_ADDR (0x27 << 1)
61
62 void HD44780_Init(uint8_t rows);
63 void HD44780_Clear();
64 void HD44780_Home();
65 void HD44780_NoDisplay();
66 void HD44780_Display();
67 void HD44780_NoBlink();
68 void HD44780_Blink();
69 void HD44780_NoCursor();
70 void HD44780_Cursor();
71 void HD44780_ScrollDisplayLeft();
72 void HD44780_ScrollDisplayRight();
73 void HD44780_PrintLeft();
74 void HD44780_PrintRight();
75 void HD44780_LeftToRight();
76 void HD44780_RightToLeft();
77 void HD44780_ShiftIncrement();
78 void HD44780_ShiftDecrement();
79 void HD44780_NoBacklight();
80 void HD44780_Backlight();
81 void HD44780_AutoScroll();
82 void HD44780_NoAutoScroll();
83 void HD44780_CreateSpecialChar(uint8_t, uint8_t[]);
84 void HD44780_PrintSpecialChar(uint8_t);
85 void HD44780_SetCursor(uint8_t, uint8_t);
86 void HD44780_SetBacklight(uint8_t new_val);
87 void HD44780_LoadCustomCharacter(uint8_t char_num, uint8_t *rows);
88 void HD44780_PrintStr(const char[]);
89
90 #endif /* LIQUIDCRYSTAL_I2C_H_ */
Save this file.
Then go to the Src and create a new File. Write the File name liquidcrystal_i2c.c.

Copy the following code and paste it in the liquidcrystal_i2c.c file.


liquidcrystal_i2c.c code:
1 /* create liquidcrystal_i2c.c file and paste in Core/Src Folder */
2
3 #include "liquidcrystal_i2c.h"
4
5 extern I2C_HandleTypeDef hi2c2;
6
7 uint8_t dpFunction;
8 uint8_t dpControl;
9 uint8_t dpMode;
10 uint8_t dpRows;
11 uint8_t dpBacklight;
12
13 static void SendCommand(uint8_t);
14 static void SendChar(uint8_t);
15 static void Send(uint8_t, uint8_t);
16 static void Write4Bits(uint8_t);
17 static void ExpanderWrite(uint8_t);
18 static void PulseEnable(uint8_t);
19 static void DelayInit(void);
20 static void DelayUS(uint32_t);
21
22 uint8_t special1[8] = {
23 0b00000,
24 0b11001,
25 0b11011,
26 0b00110,
27 0b01100,
28 0b11011,
29 0b10011,
30 0b00000
31 };
32
33 uint8_t special2[8] = {
34 0b11000,
35 0b11000,
36 0b00110,
37 0b01001,
38 0b01000,
39 0b01001,
40 0b00110,
41 0b00000
42 };
43
44 void HD44780_Init(uint8_t rows)
45 {
46 dpRows = rows;
47
48 dpBacklight = LCD_BACKLIGHT;
49
50 dpFunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
51
52 if (dpRows > 1)
53 {
54 dpFunction |= LCD_2LINE;
55 }
56 else
57 {
58 dpFunction |= LCD_5x10DOTS;
59 }
60
61 /* Wait for initialization */
62 DelayInit();
63 HAL_Delay(50);
64
65 ExpanderWrite(dpBacklight);
66 HAL_Delay(1000);
67
68 /* 4bit Mode */
69 Write4Bits(0x03 << 4);
70 DelayUS(4500);
71
72 Write4Bits(0x03 << 4);
73 DelayUS(4500);
74
75 Write4Bits(0x03 << 4);
76 DelayUS(4500);
77
78 Write4Bits(0x02 << 4);
79 DelayUS(100);
80
81 /* Display Control */
82 SendCommand(LCD_FUNCTIONSET | dpFunction);
83
84 dpControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
85 HD44780_Display();
86 HD44780_Clear();
87
88 /* Display Mode */
89 dpMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
90 SendCommand(LCD_ENTRYMODESET | dpMode);
91 DelayUS(4500);
92
93 HD44780_CreateSpecialChar(0, special1);
94 HD44780_CreateSpecialChar(1, special2);
95
96 HD44780_Home();
97 }
98
99 void HD44780_Clear()
100 {
101 SendCommand(LCD_CLEARDISPLAY);
102 DelayUS(2000);
103 }
104
105 void HD44780_Home()
106 {
107 SendCommand(LCD_RETURNHOME);
108 DelayUS(2000);
109 }
110
111 void HD44780_SetCursor(uint8_t col, uint8_t row)
112 {
113 int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
114 if (row >= dpRows)
115 {
116 row = dpRows-1;
117 }
118 SendCommand(LCD_SETDDRAMADDR | (col + row_offsets[row]));
119 }
120
121 void HD44780_NoDisplay()
122 {
123 dpControl &= ~LCD_DISPLAYON;
124 SendCommand(LCD_DISPLAYCONTROL | dpControl);
125 }
126
127 void HD44780_Display()
128 {
129 dpControl |= LCD_DISPLAYON;
130 SendCommand(LCD_DISPLAYCONTROL | dpControl);
131 }
132
133 void HD44780_NoCursor()
134 {
135 dpControl &= ~LCD_CURSORON;
136 SendCommand(LCD_DISPLAYCONTROL | dpControl);
137 }
138
139 void HD44780_Cursor()
140 {
141 dpControl |= LCD_CURSORON;
142 SendCommand(LCD_DISPLAYCONTROL | dpControl);
143 }
144
145 void HD44780_NoBlink()
146 {
147 dpControl &= ~LCD_BLINKON;
148 SendCommand(LCD_DISPLAYCONTROL | dpControl);
149 }
150
151 void HD44780_Blink()
152 {
153 dpControl |= LCD_BLINKON;
154 SendCommand(LCD_DISPLAYCONTROL | dpControl);
155 }
156
157 void HD44780_ScrollDisplayLeft(void)
158 {
159 SendCommand(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
160 }
161
162 void HD44780_ScrollDisplayRight(void)
163 {
164 SendCommand(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
165 }
166
167 void HD44780_LeftToRight(void)
168 {
169 dpMode |= LCD_ENTRYLEFT;
170 SendCommand(LCD_ENTRYMODESET | dpMode);
171 }
172
173 void HD44780_RightToLeft(void)
174 {
175 dpMode &= ~LCD_ENTRYLEFT;
176 SendCommand(LCD_ENTRYMODESET | dpMode);
177 }
178
179 void HD44780_AutoScroll(void)
180 {
181 dpMode |= LCD_ENTRYSHIFTINCREMENT;
182 SendCommand(LCD_ENTRYMODESET | dpMode);
183 }
184
185 void HD44780_NoAutoScroll(void)
186 {
187 dpMode &= ~LCD_ENTRYSHIFTINCREMENT;
188 SendCommand(LCD_ENTRYMODESET | dpMode);
189 }
190
191 void HD44780_CreateSpecialChar(uint8_t location, uint8_t charmap[])
192 {
193 location &= 0x7;
194 SendCommand(LCD_SETCGRAMADDR | (location << 3));
195 for (int i=0; i<8; i++)
196 {
197 SendChar(charmap[i]);
198 }
199 }
200
201 void HD44780_PrintSpecialChar(uint8_t index)
202 {
203 SendChar(index);
204 }
205
206 void HD44780_LoadCustomCharacter(uint8_t char_num, uint8_t *rows)
207 {
208 HD44780_CreateSpecialChar(char_num, rows);
209 }
210
211 void HD44780_PrintStr(const char c[])
212 {
213 while(*c) SendChar(*c++);
214 }
215
216 void HD44780_SetBacklight(uint8_t new_val)
217 {
218 if(new_val) HD44780_Backlight();
219 else HD44780_NoBacklight();
220 }
221
222 void HD44780_NoBacklight(void)
223 {
224 dpBacklight=LCD_NOBACKLIGHT;
225 ExpanderWrite(0);
226 }
227
228 void HD44780_Backlight(void)
229 {
230 dpBacklight=LCD_BACKLIGHT;
231 ExpanderWrite(0);
232 }
233
234 static void SendCommand(uint8_t cmd)
235 {
236 Send(cmd, 0);
237 }
238
239 static void SendChar(uint8_t ch)
240 {
241 Send(ch, RS);
242 }
243
244 static void Send(uint8_t value, uint8_t mode)
245 {
246 uint8_t highnib = value & 0xF0;
247 uint8_t lownib = (value<<4) & 0xF0;
248 Write4Bits((highnib)|mode);
249 Write4Bits((lownib)|mode);
250 }
251
252 static void Write4Bits(uint8_t value)
253 {
254 ExpanderWrite(value);
255 PulseEnable(value);
256 }
257
258 static void ExpanderWrite(uint8_t _data)
259 {
260 uint8_t data = _data | dpBacklight;
261 HAL_I2C_Master_Transmit(&hi2c2, DEVICE_ADDR, (uint8_t*)&data, 1, 10);
262 }
263
264 static void PulseEnable(uint8_t _data)
265 {
266 ExpanderWrite(_data | ENABLE);
267 DelayUS(20);
268
269 ExpanderWrite(_data & ~ENABLE);
270 DelayUS(20);
271 }
272
273 static void DelayInit(void)
274 {
275 CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
276 CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
277
278 DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; //~0x00000001;
279 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; //0x00000001;
280
281 DWT->CYCCNT = 0;
282
283 /* 3 NO OPERATION instructions */
284 __ASM volatile ("NOP");
285 __ASM volatile ("NOP");
286 __ASM volatile ("NOP");
287 }
288
289 static void DelayUS(uint32_t us) {
290 uint32_t cycles = (SystemCoreClock/1000000L)*us;
291 uint32_t start = DWT->CYCCNT;
292 volatile uint32_t cnt;
293
294 do
295 {
296 cnt = DWT->CYCCNT - start;
297 } while(cnt < cycles);
298 }
Save this file.
Now, go to the main.c file and copy and paste the following code.
I2C 16×2 LCD Hello World Programming:
1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21 #include "liquidcrystal_i2c.h"
22
23 /* Private includes ----------------------------------------------------------*/
24 /* USER CODE BEGIN Includes */
25
26 /* USER CODE END Includes */
27
28 /* Private typedef -----------------------------------------------------------*/
29 /* USER CODE BEGIN PTD */
30
31 /* USER CODE END PTD */
32
33 /* Private define ------------------------------------------------------------*/
34 /* USER CODE BEGIN PD */
35
36 /* USER CODE END PD */
37
38 /* Private macro -------------------------------------------------------------*/
39 /* USER CODE BEGIN PM */
40
41 /* USER CODE END PM */
42
43 /* Private variables ---------------------------------------------------------*/
44 I2C_HandleTypeDef hi2c2;
45
46 /* USER CODE BEGIN PV */
47
48 /* USER CODE END PV */
49
50 /* Private function prototypes -----------------------------------------------*/
51 void SystemClock_Config(void);
52 static void MX_GPIO_Init(void);
53 static void MX_I2C2_Init(void);
54 /* USER CODE BEGIN PFP */
55
56 /* USER CODE END PFP */
57
58 /* Private user code ---------------------------------------------------------*/
59 /* USER CODE BEGIN 0 */
60
61 /* USER CODE END 0 */
62
63 /**
64 * @brief The application entry point.
65 * @retval int
66 */
67 int main(void)
68 {
69 /* USER CODE BEGIN 1 */
70
71 /* USER CODE END 1 */
72
73 /* MCU Configuration--------------------------------------------------------*/
74
75 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
76 HAL_Init();
77
78 /* USER CODE BEGIN Init */
79
80 /* USER CODE END Init */
81
82 /* Configure the system clock */
83 SystemClock_Config();
84
85 /* USER CODE BEGIN SysInit */
86
87 /* USER CODE END SysInit */
88
89 /* Initialize all configured peripherals */
90 MX_GPIO_Init();
91 MX_I2C2_Init();
92 /* USER CODE BEGIN 2 */
93
94 HD44780_Init(2);
95 HD44780_Clear();
96 HD44780_SetCursor(0,0);
97 HD44780_PrintStr("HELLO");
98 HD44780_SetCursor(6,0);
99 HD44780_PrintStr("WORLD");
100 HAL_Delay(2000);
101
102
103 /* USER CODE END 2 */
104
105 /* Infinite loop */
106 /* USER CODE BEGIN WHILE */
107 while (1)
108 {
109 /* USER CODE END WHILE */
110
111 /* USER CODE BEGIN 3 */
112 }
113 /* USER CODE END 3 */
114 }
115
116 /**
117 * @brief System Clock Configuration
118 * @retval None
119 */
120 void SystemClock_Config(void)
121 {
122 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
123 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
124
125 /** Initializes the RCC Oscillators according to the specified parameters
126 * in the RCC_OscInitTypeDef structure.
127 */
128 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
129 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
130 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
131 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
132 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
133 {
134 Error_Handler();
135 }
136
137 /** Initializes the CPU, AHB and APB buses clocks
138 */
139 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
140 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
141 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
142 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
143 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
144 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
145
146 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
147 {
148 Error_Handler();
149 }
150 }
151
152 /**
153 * @brief I2C2 Initialization Function
154 * @param None
155 * @retval None
156 */
157 static void MX_I2C2_Init(void)
158 {
159
160 /* USER CODE BEGIN I2C2_Init 0 */
161
162 /* USER CODE END I2C2_Init 0 */
163
164 /* USER CODE BEGIN I2C2_Init 1 */
165
166 /* USER CODE END I2C2_Init 1 */
167 hi2c2.Instance = I2C2;
168 hi2c2.Init.ClockSpeed = 100000;
169 hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
170 hi2c2.Init.OwnAddress1 = 0;
171 hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
172 hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
173 hi2c2.Init.OwnAddress2 = 0;
174 hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
175 hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
176 if (HAL_I2C_Init(&hi2c2) != HAL_OK)
177 {
178 Error_Handler();
179 }
180 /* USER CODE BEGIN I2C2_Init 2 */
181
182 /* USER CODE END I2C2_Init 2 */
183
184 }
185
186 /**
187 * @brief GPIO Initialization Function
188 * @param None
189 * @retval None
190 */
191 static void MX_GPIO_Init(void)
192 {
193 /* USER CODE BEGIN MX_GPIO_Init_1 */
194 /* USER CODE END MX_GPIO_Init_1 */
195
196 /* GPIO Ports Clock Enable */
197 __HAL_RCC_GPIOB_CLK_ENABLE();
198 __HAL_RCC_GPIOA_CLK_ENABLE();
199
200 /* USER CODE BEGIN MX_GPIO_Init_2 */
201 /* USER CODE END MX_GPIO_Init_2 */
202 }
203
204 /* USER CODE BEGIN 4 */
205
206 /* USER CODE END 4 */
207
208 /**
209 * @brief This function is executed in case of error occurrence.
210 * @retval None
211 */
212 void Error_Handler(void)
213 {
214 /* USER CODE BEGIN Error_Handler_Debug */
215 /* User can add his own implementation to report the HAL error return state */
216 __disable_irq();
217 while (1)
218 {
219 }
220 /* USER CODE END Error_Handler_Debug */
221 }
222
223 #ifdef USE_FULL_ASSERT
224 /**
225 * @brief Reports the name of the source file and the source line number
226 * where the assert_param error has occurred.
227 * @param file: pointer to the source file name
228 * @param line: assert_param error line source number
229 * @retval None
230 */
231 void assert_failed(uint8_t *file, uint32_t line)
232 {
233 /* USER CODE BEGIN 6 */
234 /* User can add his own implementation to report the file name and line number,
235 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
236 /* USER CODE END 6 */
237 }
238 #endif /* USE_FULL_ASSERT */
Save and Run the code as explained in example #1.
This LCD doesn’t work with 3.3 volts supplied via ST-link V2. So, we have to options, we
can directly connect the STM32 to the Laptop/PC to run this project. Or we can use an
external regulated 5V power supply. In my can case, I used the external 5V and 3A
power supply. I have a detailed article on this. Simply search the 5V and 3A power
supply.
You can see the Message HELLO WORLD printed on the I2C supported 16×2 LCD. In
next example, we are going to interface an analog sensor with the STM32 board and we
will print its value on the 16×2 LCD.

Example #5: STM32F103C8T6 Analog Sensor

In this 5th example, I am going to use an analog sensor with the STM32 board. I will read
its value and then print its value on the I2C supported 16×2 LCD. You can see, I am using
a potentiometer as the analog sensor. For this experiment you can use any analog
sensor like for example an LDR, etc. Anyway, I am going to continue with this
Potentiometer, let me explain the interfacing.

The 16×2 LCD wiring remains exactly the same as explained in the previous example.
The Potentiometer leftmost and rightmost legs are connected to the STM32 3.3V and
GND pins. And the middle leg of the Potentiometer is connected to the PB1. All the
Analog pins are 3.3V standard pins so make sure the voltage on the analog pins doesn’t
exceed 3.3 volts.
Connect the STM32 microcontroller board via ST-Link V2 to your Laptop/PC.
I am going to modify the previous project. Go to the I2CLcd.ioc. Click on the Analog and
select ADC1… and under the ADC1 Mode and configuration select IN9, which is the GPIO
PB1 pin on the STM32. Click on the Continuous Conversion and Enable it.
Finally, click on the save button if it asks you Do you want to generate Code? click yes…
again press the yes button… STM32CubeIDE will generate the necessary code based on
your pin configuration and open the associated perspective. This perspective provides
you with the appropriate tools and views for further development and customization of
your project.
Copy the following code and paste it in your main.c file.

Analog Sensor STM32CubeIDE Code:


1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21 #include "liquidcrystal_i2c.h"
22 #include "stdio.h"
23
24 /* Private includes ----------------------------------------------------------*/
25 /* USER CODE BEGIN Includes */
26
27 /* USER CODE END Includes */
28
29 /* Private typedef -----------------------------------------------------------*/
30 /* USER CODE BEGIN PTD */
31
32 /* USER CODE END PTD */
33
34 /* Private define ------------------------------------------------------------*/
35 /* USER CODE BEGIN PD */
36
37 /* USER CODE END PD */
38
39 /* Private macro -------------------------------------------------------------*/
40 /* USER CODE BEGIN PM */
41
42 /* USER CODE END PM */
43
44 /* Private variables ---------------------------------------------------------*/
45 ADC_HandleTypeDef hadc1;
46
47 I2C_HandleTypeDef hi2c2;
48
49 /* USER CODE BEGIN PV */
50
51 /* USER CODE END PV */
52
53 /* Private function prototypes -----------------------------------------------*/
54 void SystemClock_Config(void);
55 static void MX_GPIO_Init(void);
56 static void MX_I2C2_Init(void);
57 static void MX_ADC1_Init(void);
58 /* USER CODE BEGIN PFP */
59
60 /* USER CODE END PFP */
61
62 /* Private user code ---------------------------------------------------------*/
63 /* USER CODE BEGIN 0 */
64
65 uint16_t adcValue;
66 char adcValueStr[6];
67
68 /* USER CODE END 0 */
69
70 /**
71 * @brief The application entry point.
72 * @retval int
73 */
74 int main(void)
75 {
76 /* USER CODE BEGIN 1 */
77
78 /* USER CODE END 1 */
79
80 /* MCU Configuration--------------------------------------------------------*/
81
82 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
83 HAL_Init();
84
85 /* USER CODE BEGIN Init */
86
87 /* USER CODE END Init */
88
89 /* Configure the system clock */
90 SystemClock_Config();
91
92 /* USER CODE BEGIN SysInit */
93
94 /* USER CODE END SysInit */
95
96 /* Initialize all configured peripherals */
97 MX_GPIO_Init();
98 MX_I2C2_Init();
99 MX_ADC1_Init();
100 /* USER CODE BEGIN 2 */
101
102 HD44780_Init(2);
103 HD44780_Clear();
104 HD44780_SetCursor(0,0);
105 HD44780_PrintStr("Potentiometer:");
106
107 /* USER CODE END 2 */
108
109 /* Infinite loop */
110 /* USER CODE BEGIN WHILE */
111 while (1)
112 {
113
114 HAL_ADC_Start(&hadc1);
115
116
117 HAL_ADC_PollForConversion(&hadc1,500);
118 adcValue = HAL_ADC_GetValue(&hadc1);
119 sprintf(adcValueStr, "%5d", adcValue);
120 HD44780_SetCursor(0, 1);
121 HD44780_PrintStr(adcValueStr);
122 HAL_Delay(500);
123
124
125
126 /* USER CODE END WHILE */
127
128 /* USER CODE BEGIN 3 */
129 }
130 /* USER CODE END 3 */
131 }
132
133 /**
134 * @brief System Clock Configuration
135 * @retval None
136 */
137 void SystemClock_Config(void)
138 {
139 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
140 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
141 RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
142
143 /** Initializes the RCC Oscillators according to the specified parameters
144 * in the RCC_OscInitTypeDef structure.
145 */
146 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
147 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
148 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
149 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
150 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
151 {
152 Error_Handler();
153 }
154
155 /** Initializes the CPU, AHB and APB buses clocks
156 */
157 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
158 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
159 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
160 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
161 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
162 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
163
164 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
165 {
166 Error_Handler();
167 }
168 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
169 PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
170 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
171 {
172 Error_Handler();
173 }
174 }
175
176 /**
177 * @brief ADC1 Initialization Function
178 * @param None
179 * @retval None
180 */
181 static void MX_ADC1_Init(void)
182 {
183
184 /* USER CODE BEGIN ADC1_Init 0 */
185
186 /* USER CODE END ADC1_Init 0 */
187
188 ADC_ChannelConfTypeDef sConfig = {0};
189
190 /* USER CODE BEGIN ADC1_Init 1 */
191
192 /* USER CODE END ADC1_Init 1 */
193
194 /** Common config
195 */
196 hadc1.Instance = ADC1;
197 hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
198 hadc1.Init.ContinuousConvMode = ENABLE;
199 hadc1.Init.DiscontinuousConvMode = DISABLE;
200 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
201 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
202 hadc1.Init.NbrOfConversion = 1;
203 if (HAL_ADC_Init(&hadc1) != HAL_OK)
204 {
205 Error_Handler();
206 }
207
208 /** Configure Regular Channel
209 */
210 sConfig.Channel = ADC_CHANNEL_9;
211 sConfig.Rank = ADC_REGULAR_RANK_1;
212 sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
213 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
214 {
215 Error_Handler();
216 }
217 /* USER CODE BEGIN ADC1_Init 2 */
218
219 /* USER CODE END ADC1_Init 2 */
220
221 }
222
223 /**
224 * @brief I2C2 Initialization Function
225 * @param None
226 * @retval None
227 */
228 static void MX_I2C2_Init(void)
229 {
230
231 /* USER CODE BEGIN I2C2_Init 0 */
232
233 /* USER CODE END I2C2_Init 0 */
234
235 /* USER CODE BEGIN I2C2_Init 1 */
236
237 /* USER CODE END I2C2_Init 1 */
238 hi2c2.Instance = I2C2;
239 hi2c2.Init.ClockSpeed = 100000;
240 hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
241 hi2c2.Init.OwnAddress1 = 0;
242 hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
243 hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
244 hi2c2.Init.OwnAddress2 = 0;
245 hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
246 hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
247 if (HAL_I2C_Init(&hi2c2) != HAL_OK)
248 {
249 Error_Handler();
250 }
251 /* USER CODE BEGIN I2C2_Init 2 */
252
253 /* USER CODE END I2C2_Init 2 */
254
255 }
256
257 /**
258 * @brief GPIO Initialization Function
259 * @param None
260 * @retval None
261 */
262 static void MX_GPIO_Init(void)
263 {
264 /* USER CODE BEGIN MX_GPIO_Init_1 */
265 /* USER CODE END MX_GPIO_Init_1 */
266
267 /* GPIO Ports Clock Enable */
268 __HAL_RCC_GPIOB_CLK_ENABLE();
269 __HAL_RCC_GPIOA_CLK_ENABLE();
270
271 /* USER CODE BEGIN MX_GPIO_Init_2 */
272 /* USER CODE END MX_GPIO_Init_2 */
273 }
274
275 /* USER CODE BEGIN 4 */
276
277 /* USER CODE END 4 */
278
279 /**
280 * @brief This function is executed in case of error occurrence.
281 * @retval None
282 */
283 void Error_Handler(void)
284 {
285 /* USER CODE BEGIN Error_Handler_Debug */
286 /* User can add his own implementation to report the HAL error return state */
287 __disable_irq();
288 while (1)
289 {
290 }
291 /* USER CODE END Error_Handler_Debug */
292 }
293
294 #ifdef USE_FULL_ASSERT
295 /**
296 * @brief Reports the name of the source file and the source line number
297 * where the assert_param error has occurred.
298 * @param file: pointer to the source file name
299 * @param line: assert_param error line source number
300 * @retval None
301 */
302 void assert_failed(uint8_t *file, uint32_t line)
303 {
304 /* USER CODE BEGIN 6 */
305 /* User can add his own implementation to report the file name and line number,
306 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
307 /* USER CODE END 6 */
308 }
309 #endif /* USE_FULL_ASSERT */
Save the code and run this project as explained in the example #1.

You can see, the value changes as I rotate the knob of the Potentiometer. You can use
this same program with all types of analog sensors for reading and displaying the raw
data. Next, I am going to explain how to use an I2C supported Oled Display Module with
the STM32 controller board.

Example #6: STM32F103C8T6 Oled Display

In this 6th example, I am going to show you, how to print text on this I2C supported
SSD1306 Oled display Module. In most of your upcoming projects, you will need this
Oled display module for printing text messages and Sensors values. In this example, I
will print the HELLO WORLD message on this Oled display module. Anyway, let’s take a
look at the connections.
The SSD1306 Oled display module VCC and GND pins are connected to the STM32 3.3V
and GND pins. The SCL and SDA pins are connected to the STM32 GPIO pins PB10 and
PB11. PB10 is the SCL and PB11 is the SDA.
Connect your STM32 via ST-Link V2 to your Laptop/PC.
Click on the I2C Speed Mode and select Fast Mode.

Finally, click on the save button if it asks you Do you want to generate Code? click yes…
again press the yes button… STM32CubeIDE will generate the necessary code based on
your pin configuration and open the associated perspective. This perspective provides
you with the appropriate tools and views for further development and customization of
your project.
Next, download the below SSD1306 oled display module libraries and add .c and .h files
exactly the same way as we did in the example #4. We will add .c files in the Src and .h
files in the Inc.
Download SSD1306 Oled display Libraries:
You can see all the required .h and .c files have been added. Now, we can close all these
files because these are already added in the Src and Inc folders. Click on the main.c file
and copy the following code and paste it in your main.c file.

SSD1306 Oled Display STM32CubeIDE Code:


1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21 #include "fonts.h"
22 #include "ssd1306.h"
23
24 /* Private includes ----------------------------------------------------------*/
25 /* USER CODE BEGIN Includes */
26
27 /* USER CODE END Includes */
28
29 /* Private typedef -----------------------------------------------------------*/
30 /* USER CODE BEGIN PTD */
31
32 /* USER CODE END PTD */
33
34 /* Private define ------------------------------------------------------------*/
35 /* USER CODE BEGIN PD */
36
37 /* USER CODE END PD */
38
39 /* Private macro -------------------------------------------------------------*/
40 /* USER CODE BEGIN PM */
41
42 /* USER CODE END PM */
43
44 /* Private variables ---------------------------------------------------------*/
45 I2C_HandleTypeDef hi2c2;
46
47 /* USER CODE BEGIN PV */
48
49 /* USER CODE END PV */
50
51 /* Private function prototypes -----------------------------------------------*/
52 void SystemClock_Config(void);
53 static void MX_GPIO_Init(void);
54 static void MX_I2C2_Init(void);
55 /* USER CODE BEGIN PFP */
56
57 /* USER CODE END PFP */
58
59 /* Private user code ---------------------------------------------------------*/
60 /* USER CODE BEGIN 0 */
61
62 /* USER CODE END 0 */
63
64 /**
65 * @brief The application entry point.
66 * @retval int
67 */
68 int main(void)
69 {
70 /* USER CODE BEGIN 1 */
71
72 /* USER CODE END 1 */
73
74 /* MCU Configuration--------------------------------------------------------*/
75
76 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
77 HAL_Init();
78
79 /* USER CODE BEGIN Init */
80
81 /* USER CODE END Init */
82
83 /* Configure the system clock */
84 SystemClock_Config();
85
86 /* USER CODE BEGIN SysInit */
87
88 /* USER CODE END SysInit */
89
90 /* Initialize all configured peripherals */
91 MX_GPIO_Init();
92 MX_I2C2_Init();
93 /* USER CODE BEGIN 2 */
94 SSD1306_Init();
95 char string[5];
96
97 SSD1306_GotoXY (0,0);
98 SSD1306_Puts ("Hello", &Font_11x18, 1);
99 SSD1306_GotoXY (0, 30);
100 SSD1306_Puts ("World", &Font_11x18, 1);
101 SSD1306_UpdateScreen();
102
103 /* USER CODE END 2 */
104
105 /* Infinite loop */
106 /* USER CODE BEGIN WHILE */
107 while (1)
108 {
109 /* USER CODE END WHILE */
110
111 /* USER CODE BEGIN 3 */
112 }
113 /* USER CODE END 3 */
114 }
115
116 /**
117 * @brief System Clock Configuration
118 * @retval None
119 */
120 void SystemClock_Config(void)
121 {
122 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
123 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
124
125 /** Initializes the RCC Oscillators according to the specified parameters
126 * in the RCC_OscInitTypeDef structure.
127 */
128 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
129 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
130 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
131 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
132 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
133 {
134 Error_Handler();
135 }
136
137 /** Initializes the CPU, AHB and APB buses clocks
138 */
139 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
140 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
141 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
142 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
143 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
144 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
145
146 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
147 {
148 Error_Handler();
149 }
150 }
151
152 /**
153 * @brief I2C2 Initialization Function
154 * @param None
155 * @retval None
156 */
157 static void MX_I2C2_Init(void)
158 {
159
160 /* USER CODE BEGIN I2C2_Init 0 */
161
162 /* USER CODE END I2C2_Init 0 */
163
164 /* USER CODE BEGIN I2C2_Init 1 */
165
166 /* USER CODE END I2C2_Init 1 */
167 hi2c2.Instance = I2C2;
168 hi2c2.Init.ClockSpeed = 400000;
169 hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
170 hi2c2.Init.OwnAddress1 = 0;
171 hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
172 hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
173 hi2c2.Init.OwnAddress2 = 0;
174 hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
175 hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
176 if (HAL_I2C_Init(&hi2c2) != HAL_OK)
177 {
178 Error_Handler();
179 }
180 /* USER CODE BEGIN I2C2_Init 2 */
181
182 /* USER CODE END I2C2_Init 2 */
183
184 }
185
186 /**
187 * @brief GPIO Initialization Function
188 * @param None
189 * @retval None
190 */
191 static void MX_GPIO_Init(void)
192 {
193 /* USER CODE BEGIN MX_GPIO_Init_1 */
194 /* USER CODE END MX_GPIO_Init_1 */
195
196 /* GPIO Ports Clock Enable */
197 __HAL_RCC_GPIOB_CLK_ENABLE();
198 __HAL_RCC_GPIOA_CLK_ENABLE();
199
200 /* USER CODE BEGIN MX_GPIO_Init_2 */
201 /* USER CODE END MX_GPIO_Init_2 */
202 }
203
204 /* USER CODE BEGIN 4 */
205
206 /* USER CODE END 4 */
207
208 /**
209 * @brief This function is executed in case of error occurrence.
210 * @retval None
211 */
212 void Error_Handler(void)
213 {
214 /* USER CODE BEGIN Error_Handler_Debug */
215 /* User can add his own implementation to report the HAL error return state */
216 __disable_irq();
217 while (1)
218 {
219 }
220 /* USER CODE END Error_Handler_Debug */
221 }
222
223 #ifdef USE_FULL_ASSERT
224 /**
225 * @brief Reports the name of the source file and the source line number
226 * where the assert_param error has occurred.
227 * @param file: pointer to the source file name
228 * @param line: assert_param error line source number
229 * @retval None
230 */
231 void assert_failed(uint8_t *file, uint32_t line)
232 {
233 /* USER CODE BEGIN 6 */
234 /* User can add his own implementation to report the file name and line number,
235 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
236 /* USER CODE END 6 */
237 }
238 #endif /* USE_FULL_ASSERT */
You can save this code and run this program as explained in example #1.

You can see the Hello World message on the SSD1306 Oled display Module. Now, you
can add this Oled display module in all those projects where you need to print text or
sensor values. In the next example, we are going to measure the distance using the
Ultrasonic sensor and then we will print the measured distance on this Oled display
Module.
Example #7: STM32F103C8T6 Ultrasonic Sensor

In this 7th example, we are going to make a distance measurement system using the HC-
SR04 Ultrasonic sensor with the STM32 microcontroller board. We will measure the
distance in Centimeters and then we will print it on the SSD1306 Oled display Module.
Anyway, let’s go ahead and take a look at the connections.

The Oled display module wiring with the STM32 remains exactly the same. The
Ultrasonic Sensor VCC and GND pins are connected to the STM32 board 5V and GND
pins. The Echo and Trigger pins are connected to the STM32 GPIO Pins PB8 and PB9.
PB8 and PB9 are 5V tolerant pins. So, it doesn’t matter if I use 5V to power up the
Ultrasonic Sensor.
Connect your STM32 via the ST-Link V2 to your Laptop/PC as explained in example #1.
I am going to continue with the same Oled display module programming. Go to the
Oled.ioc, I am going to use the same SCL and SDA pins for the Oled display Module. Go to
the System Core and click on the RCC and Select Crystal/Ceramic Resonator.

Next, go to Timers and select TIM1. Select the Clock Source as Internal Clock. Next, click
on the PA8 pin and select TIM1_CH1.

Next, click on the PB9 and set it as OUTPUT. Then click on the PB8 and set it as INPUT.
Then go to Clock Configuration and change the HCLK to 72Mhz.

Finally, click on the save button if it asks you Do you want to generate Code? click yes…
again press the yes button… STM32CubeIDE will generate the necessary code based on
your pin configuration and open the associated perspective. This perspective provides
you with the appropriate tools and views for further development and customization of
your project.
I have already written this code for you, so simply copy and paste it in the main.c file.

Ultrasonic Sensor STM32CubeIDE Code:


1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21 #include "ssd1306.h"
22 #include "fonts.h"
23 #include "stdio.h"
24
25 /* Private includes ----------------------------------------------------------*/
26 /* USER CODE BEGIN Includes */
27
28 /* USER CODE END Includes */
29
30 /* Private typedef -----------------------------------------------------------*/
31 /* USER CODE BEGIN PTD */
32
33 /* USER CODE END PTD */
34
35 /* Private define ------------------------------------------------------------*/
36 /* USER CODE BEGIN PD */
37
38 /* USER CODE END PD */
39
40 /* Private macro -------------------------------------------------------------*/
41 /* USER CODE BEGIN PM */
42
43 /* USER CODE END PM */
44
45 /* Private variables ---------------------------------------------------------*/
46 I2C_HandleTypeDef hi2c2;
47
48 TIM_HandleTypeDef htim1;
49
50 /* USER CODE BEGIN PV */
51 #define TRIG_PIN GPIO_PIN_9
52 #define TRIG_PORT GPIOB
53 #define ECHO_PIN GPIO_PIN_8
54 #define ECHO_PORT GPIOB
55 uint32_t pMillis;
56 uint32_t Value1 = 0;
57 uint32_t Value2 = 0;
58 uint16_t Distance = 0; // cm
59 char strCopy[15];
60
61
62
63
64 /* USER CODE END PV */
65
66 /* Private function prototypes -----------------------------------------------*/
67 void SystemClock_Config(void);
68 static void MX_GPIO_Init(void);
69 static void MX_I2C2_Init(void);
70 static void MX_TIM1_Init(void);
71 /* USER CODE BEGIN PFP */
72
73 /* USER CODE END PFP */
74
75 /* Private user code ---------------------------------------------------------*/
76 /* USER CODE BEGIN 0 */
77
78 /* USER CODE END 0 */
79
80 /**
81 * @brief The application entry point.
82 * @retval int
83 */
84 int main(void)
85 {
86 /* USER CODE BEGIN 1 */
87
88 /* USER CODE END 1 */
89
90 /* MCU Configuration--------------------------------------------------------*/
91
92 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
93 HAL_Init();
94
95 /* USER CODE BEGIN Init */
96
97 /* USER CODE END Init */
98
99 /* Configure the system clock */
100 SystemClock_Config();
101
102 /* USER CODE BEGIN SysInit */
103
104 /* USER CODE END SysInit */
105
106 /* Initialize all configured peripherals */
107 MX_GPIO_Init();
108 MX_I2C2_Init();
109 MX_TIM1_Init();
110 /* USER CODE BEGIN 2 */
111 HAL_TIM_Base_Start(&htim1);
112 HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the TRIG
113 pin low
114 SSD1306_Init();
115
116 /* USER CODE END 2 */
117
118 /* Infinite loop */
119 /* USER CODE BEGIN WHILE */
120 while (1)
121 {
122 HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET); // pull the TRIG pin
123 HIGH
124 __HAL_TIM_SET_COUNTER(&htim1, 0);
125 while (__HAL_TIM_GET_COUNTER (&htim1) < 10); // wait for 10 us
126 HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the
127 TRIG pin low
128
129 pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout)
130 // wait for the echo pin to go high
131 while (!(HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 10
132 > HAL_GetTick());
133 Value1 = __HAL_TIM_GET_COUNTER (&htim1);
134
135 pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout)
136 // wait for the echo pin to go low
137 while ((HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 50 >
138 HAL_GetTick());
139 Value2 = __HAL_TIM_GET_COUNTER (&htim1);
140
141 Distance = (Value2-Value1)* 0.034/2;
142
143 SSD1306_GotoXY (0, 0);
144 SSD1306_Puts ("Distance:", &Font_11x18, 1);
145 sprintf(strCopy,"%d ", Distance);
146 SSD1306_GotoXY (0, 30);
147 SSD1306_Puts (strCopy, &Font_16x26, 1);
148 SSD1306_UpdateScreen();
149 HAL_Delay(50);
150 /* USER CODE END WHILE */
151
152 /* USER CODE BEGIN 3 */
153 }
154 /* USER CODE END 3 */
155 }
156
157 /**
158 * @brief System Clock Configuration
159 * @retval None
160 */
161 void SystemClock_Config(void)
162 {
163 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
164 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
165
166 /** Initializes the RCC Oscillators according to the specified parameters
167 * in the RCC_OscInitTypeDef structure.
168 */
169 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
170 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
171 RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
172 RCC_OscInitStruct.HSIState = RCC_HSI_ON;
173 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
174 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
175 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
176 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
177 {
178 Error_Handler();
179 }
180
181 /** Initializes the CPU, AHB and APB buses clocks
182 */
183 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
184 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
185 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
186 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
187 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
188 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
189
190 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
191 {
192 Error_Handler();
193 }
194 }
195
196 /**
197 * @brief I2C2 Initialization Function
198 * @param None
199 * @retval None
200 */
201 static void MX_I2C2_Init(void)
202 {
203
204 /* USER CODE BEGIN I2C2_Init 0 */
205
206 /* USER CODE END I2C2_Init 0 */
207
208 /* USER CODE BEGIN I2C2_Init 1 */
209
210 /* USER CODE END I2C2_Init 1 */
211 hi2c2.Instance = I2C2;
212 hi2c2.Init.ClockSpeed = 400000;
213 hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
214 hi2c2.Init.OwnAddress1 = 0;
215 hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
216 hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
217 hi2c2.Init.OwnAddress2 = 0;
218 hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
219 hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
220 if (HAL_I2C_Init(&hi2c2) != HAL_OK)
221 {
222 Error_Handler();
223 }
224 /* USER CODE BEGIN I2C2_Init 2 */
225
226 /* USER CODE END I2C2_Init 2 */
227
228 }
229
230 /**
231 * @brief TIM1 Initialization Function
232 * @param None
233 * @retval None
234 */
235 static void MX_TIM1_Init(void)
236 {
237
238 /* USER CODE BEGIN TIM1_Init 0 */
239
240 /* USER CODE END TIM1_Init 0 */
241
242 TIM_ClockConfigTypeDef sClockSourceConfig = {0};
243 TIM_MasterConfigTypeDef sMasterConfig = {0};
244 TIM_IC_InitTypeDef sConfigIC = {0};
245
246 /* USER CODE BEGIN TIM1_Init 1 */
247
248 /* USER CODE END TIM1_Init 1 */
249 htim1.Instance = TIM1;
250 htim1.Init.Prescaler = 71;
251 htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
252 htim1.Init.Period = 65535;
253 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
254 htim1.Init.RepetitionCounter = 0;
255 htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
256 if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
257 {
258 Error_Handler();
259 }
260 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
261 if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
262 {
263 Error_Handler();
264 }
265 if (HAL_TIM_IC_Init(&htim1) != HAL_OK)
266 {
267 Error_Handler();
268 }
269 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
270 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
271 if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) !=
272 HAL_OK)
273 {
274 Error_Handler();
275 }
276 sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
277 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
278 sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
279 sConfigIC.ICFilter = 0;
280 if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1) !=
281 HAL_OK)
282 {
283 Error_Handler();
284 }
285 /* USER CODE BEGIN TIM1_Init 2 */
286
287 /* USER CODE END TIM1_Init 2 */
288
289 }
290
291 /**
292 * @brief GPIO Initialization Function
293 * @param None
294 * @retval None
295 */
296 static void MX_GPIO_Init(void)
297 {
298 GPIO_InitTypeDef GPIO_InitStruct = {0};
299 /* USER CODE BEGIN MX_GPIO_Init_1 */
300 /* USER CODE END MX_GPIO_Init_1 */
301
302 /* GPIO Ports Clock Enable */
303 __HAL_RCC_GPIOD_CLK_ENABLE();
304 __HAL_RCC_GPIOB_CLK_ENABLE();
305 __HAL_RCC_GPIOA_CLK_ENABLE();
306
307 /*Configure GPIO pin Output Level */
308 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
309
310 /*Configure GPIO pin : PB8 */
311 GPIO_InitStruct.Pin = GPIO_PIN_8;
312 GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
313 GPIO_InitStruct.Pull = GPIO_NOPULL;
314 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
315
316 /*Configure GPIO pin : PB9 */
317 GPIO_InitStruct.Pin = GPIO_PIN_9;
318 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
319 GPIO_InitStruct.Pull = GPIO_NOPULL;
320 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
321 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
322
323 /* USER CODE BEGIN MX_GPIO_Init_2 */
324 /* USER CODE END MX_GPIO_Init_2 */
325 }
326
327 /* USER CODE BEGIN 4 */
328
329 /* USER CODE END 4 */
330
331 /**
332 * @brief This function is executed in case of error occurrence.
333 * @retval None
334 */
335 void Error_Handler(void)
336 {
337 /* USER CODE BEGIN Error_Handler_Debug */
338 /* User can add his own implementation to report the HAL error return state */
339 __disable_irq();
340 while (1)
341 {
342 }
343 /* USER CODE END Error_Handler_Debug */
344 }
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
345
* where the assert_param error has occurred.
346
* @param file: pointer to the source file name
347
* @param line: assert_param error line source number
348
* @retval None
349
*/
350
void assert_failed(uint8_t *file, uint32_t line)
351
{
352
/* USER CODE BEGIN 6 */
353
/* User can add his own implementation to report the file name and line number,
354
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
Now, save the code, and click on the play button as explained in example #1.

We just built ourselves a distance measurement system using the HC-SR04 Ultrasonic
Sensor, an Oled display module, and the STM32 microcontroller board. So, that’s all for
now.

You might also like