STM32 CubeIDE Course for beginners with Examples
Table of Contents
STM32 CubeIDE Course:
STM32 CubeIDE Course for beginners with Examples- Welcome to my STM32CubeIDE Course specifically designed for Beginners. In this course, we will take a step-by-step approach to ensure that you gain a solid understanding of how to use CudeIDE for programming the STM32 Microcontroller.
Throughout this course, I will be using the most popular STM32F103C8T6 microcontroller board which is also known as Blue Pill, and for uploading the programming I will use the ST-Link V2.
Since, this course is for the absolute beginners, so, I will try my level best to explain each and every detail including,
- STM32 Blue Pill Board Technical specifications and Pinout.
- What is CubeIDE? and
- How to Download and Install STM32CudeIDE.
After covering the most basic things then I will practically show you, how to use the most commonly used electronics components with the STM32. Since this course is for the beginners, so first I will start with the easiest example which is controlling the STM32 onboard LED.
Then in the 2nd example, I will show you how to connect and control external LEDs. These LED blinking examples will help you in understanding; how to turn ON and turn OFF any GPIO pins on the STM32.
In 3rd example, I will show you, how to read a digital input on any GPIO pin of the STM32; for this, I will use a Pushbutton. We will be reading and controlling both at the same time. The STM32 board will sense the button click and then accordingly will turn ON or turn OFF the LED.
In 4th example, I will show you how to interface an I2C supported 16×2 LCD. This is one of the most commonly used LCDs; We will simply print the Hello World text message on the LCD module.
In the 5th example, I will show you how to use an analog sensor with the STM32 and display its value on the I2C supported 16×2 LCD. For demonstration purposes, I will be using a Potentiometer as the analog sensor. After understanding this example then you would be able to use all types of Analog sensors, like for example, LDRs, Flex Sensors, Force Sensors, etc. There is a long list of the Analog Sensors.
In the 6th example, I will show you how to connect an I2C Supported Oled display module with the STM32. I will write a very basic program to print some text on the Oled display module. This is really an important example because in most of the projects you will need an Oled display module to print text messages and sensor values.
In 7th example, I will explain how to make a simple distance measurement system using the most popular HC-SR04 Ultrasonic Sensor. We will simply measure the distance and then print it on the i2c supported SSD1306 Oled display module.
These examples will help you in getting started with the STM32 and CubeIDE. So, without any further delay, let’s get started!!!
Amazon Links:
Other Tools and Components:
Super Starter kit for Beginners
PCB small portable drill machines
*Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!
STM32F103C8T6:
Throughout this Course, I will use the STM32 “Blue Pill” board which is based on the STM32F103C8T6 microcontroller unit from STMicroelectronics STM32F1 series. The STM32 Blue Pill controller board is based on the ARM Cortex-m3 and is commonly known as the “Blue Pill” development board.
Let me share with you some of its key information.
Microcontroller Core: It is based on the ARM Cortex-M3 32-bit RISC processor, which offers a good balance between performance and power efficiency.
Clock Speed: The STM32F103C8T6 MCU typically operates at a maximum clock speed of 72 MHz.
Flash Memory: It comes with different variants offering various amounts of Flash memory. The most common one is 64 KB of Flash memory, which is used to store the program code.
RAM: It has 20 KB of SRAM (Static Random Access Memory) that is used for data storage during program execution.
Peripherals: The MCU includes a wide range of peripherals, including GPIO (General Purpose Input/Output) ports, timers, USART, SPI, I2C, ADC (Analog-to-Digital Converter), and more.
Operating Voltage: The operating voltage typically ranges from 2.0V to 3.6V, making it suitable for low-power applications.
Development Environment: To program and develop applications for the STM32F103C8T6 MCU, you can use the STM32CubeIDE, which is an integrated development environment provided by STMicroelectronics. Additionally, you can use various other development tools and programming languages like C/C++ and Assembly.
The STM32F1 series has been widely used in various applications, including industrial automation, consumer electronics, IoT devices, robotics, and more. Its versatility, performance, and affordability make it a popular choice among developers and hobbyists for various embedded projects.
STM32 Blue Pill Board Pinout:
It has a total of 40 pins, 20 on each side.
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1); HAL_Delay(100); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0); HAL_Delay(100); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); /*Configure GPIO pin : PC13 */ GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1); HAL_Delay(500); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 0); HAL_Delay(500); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET); /*Configure GPIO pins : PB6 PB7 PB8 PB9 */ GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ |
Multiple External Led STM32CubeIDE Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1); HAL_Delay(500); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 0); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 0); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 0); HAL_Delay(500); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1); HAL_Delay(500); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 0); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 0); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1); HAL_Delay(500); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 1); HAL_Delay(500); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, 0); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, 0); 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ // Variable to store the previous button state GPIO_PinState prevButtonState = GPIO_PIN_SET; while (1) { // Read the current button state GPIO_PinState currentButtonState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); // Check if the button state has changed if (currentButtonState != prevButtonState) { // Debounce the button HAL_Delay(50); // Read the button state again currentButtonState = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); // Update the previous button state prevButtonState = currentButtonState; // Check if the button is pressed (active LOW) if (currentButtonState == GPIO_PIN_RESET) { // Toggle the LED state HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1); } } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); /*Configure GPIO pin : PA1 */ GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /*Configure GPIO pin : PB1 */ GPIO_InitStruct.Pin = GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
/* create liquidcrystal_i2c.h file and paste in Core/Inc Folder */ #ifndef LIQUIDCRYSTAL_I2C_H_ #define LIQUIDCRYSTAL_I2C_H_ #include "stm32f1xx_hal.h" /* Command */ #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 /* Entry Mode */ #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 /* Display On/Off */ #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 /* Cursor Shift */ #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 /* Function Set */ #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 /* Backlight */ #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 /* Enable Bit */ #define ENABLE 0x04 /* Read Write Bit */ #define RW 0x0 /* Register Select Bit */ #define RS 0x01 /* Device I2C Address */ #define DEVICE_ADDR (0x27 << 1) void HD44780_Init(uint8_t rows); void HD44780_Clear(); void HD44780_Home(); void HD44780_NoDisplay(); void HD44780_Display(); void HD44780_NoBlink(); void HD44780_Blink(); void HD44780_NoCursor(); void HD44780_Cursor(); void HD44780_ScrollDisplayLeft(); void HD44780_ScrollDisplayRight(); void HD44780_PrintLeft(); void HD44780_PrintRight(); void HD44780_LeftToRight(); void HD44780_RightToLeft(); void HD44780_ShiftIncrement(); void HD44780_ShiftDecrement(); void HD44780_NoBacklight(); void HD44780_Backlight(); void HD44780_AutoScroll(); void HD44780_NoAutoScroll(); void HD44780_CreateSpecialChar(uint8_t, uint8_t[]); void HD44780_PrintSpecialChar(uint8_t); void HD44780_SetCursor(uint8_t, uint8_t); void HD44780_SetBacklight(uint8_t new_val); void HD44780_LoadCustomCharacter(uint8_t char_num, uint8_t *rows); void HD44780_PrintStr(const char[]); #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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
/* create liquidcrystal_i2c.c file and paste in Core/Src Folder */ #include "liquidcrystal_i2c.h" extern I2C_HandleTypeDef hi2c2; uint8_t dpFunction; uint8_t dpControl; uint8_t dpMode; uint8_t dpRows; uint8_t dpBacklight; static void SendCommand(uint8_t); static void SendChar(uint8_t); static void Send(uint8_t, uint8_t); static void Write4Bits(uint8_t); static void ExpanderWrite(uint8_t); static void PulseEnable(uint8_t); static void DelayInit(void); static void DelayUS(uint32_t); uint8_t special1[8] = { 0b00000, 0b11001, 0b11011, 0b00110, 0b01100, 0b11011, 0b10011, 0b00000 }; uint8_t special2[8] = { 0b11000, 0b11000, 0b00110, 0b01001, 0b01000, 0b01001, 0b00110, 0b00000 }; void HD44780_Init(uint8_t rows) { dpRows = rows; dpBacklight = LCD_BACKLIGHT; dpFunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; if (dpRows > 1) { dpFunction |= LCD_2LINE; } else { dpFunction |= LCD_5x10DOTS; } /* Wait for initialization */ DelayInit(); HAL_Delay(50); ExpanderWrite(dpBacklight); HAL_Delay(1000); /* 4bit Mode */ Write4Bits(0x03 << 4); DelayUS(4500); Write4Bits(0x03 << 4); DelayUS(4500); Write4Bits(0x03 << 4); DelayUS(4500); Write4Bits(0x02 << 4); DelayUS(100); /* Display Control */ SendCommand(LCD_FUNCTIONSET | dpFunction); dpControl = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; HD44780_Display(); HD44780_Clear(); /* Display Mode */ dpMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; SendCommand(LCD_ENTRYMODESET | dpMode); DelayUS(4500); HD44780_CreateSpecialChar(0, special1); HD44780_CreateSpecialChar(1, special2); HD44780_Home(); } void HD44780_Clear() { SendCommand(LCD_CLEARDISPLAY); DelayUS(2000); } void HD44780_Home() { SendCommand(LCD_RETURNHOME); DelayUS(2000); } void HD44780_SetCursor(uint8_t col, uint8_t row) { int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; if (row >= dpRows) { row = dpRows-1; } SendCommand(LCD_SETDDRAMADDR | (col + row_offsets[row])); } void HD44780_NoDisplay() { dpControl &= ~LCD_DISPLAYON; SendCommand(LCD_DISPLAYCONTROL | dpControl); } void HD44780_Display() { dpControl |= LCD_DISPLAYON; SendCommand(LCD_DISPLAYCONTROL | dpControl); } void HD44780_NoCursor() { dpControl &= ~LCD_CURSORON; SendCommand(LCD_DISPLAYCONTROL | dpControl); } void HD44780_Cursor() { dpControl |= LCD_CURSORON; SendCommand(LCD_DISPLAYCONTROL | dpControl); } void HD44780_NoBlink() { dpControl &= ~LCD_BLINKON; SendCommand(LCD_DISPLAYCONTROL | dpControl); } void HD44780_Blink() { dpControl |= LCD_BLINKON; SendCommand(LCD_DISPLAYCONTROL | dpControl); } void HD44780_ScrollDisplayLeft(void) { SendCommand(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); } void HD44780_ScrollDisplayRight(void) { SendCommand(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); } void HD44780_LeftToRight(void) { dpMode |= LCD_ENTRYLEFT; SendCommand(LCD_ENTRYMODESET | dpMode); } void HD44780_RightToLeft(void) { dpMode &= ~LCD_ENTRYLEFT; SendCommand(LCD_ENTRYMODESET | dpMode); } void HD44780_AutoScroll(void) { dpMode |= LCD_ENTRYSHIFTINCREMENT; SendCommand(LCD_ENTRYMODESET | dpMode); } void HD44780_NoAutoScroll(void) { dpMode &= ~LCD_ENTRYSHIFTINCREMENT; SendCommand(LCD_ENTRYMODESET | dpMode); } void HD44780_CreateSpecialChar(uint8_t location, uint8_t charmap[]) { location &= 0x7; SendCommand(LCD_SETCGRAMADDR | (location << 3)); for (int i=0; i<8; i++) { SendChar(charmap[i]); } } void HD44780_PrintSpecialChar(uint8_t index) { SendChar(index); } void HD44780_LoadCustomCharacter(uint8_t char_num, uint8_t *rows) { HD44780_CreateSpecialChar(char_num, rows); } void HD44780_PrintStr(const char c[]) { while(*c) SendChar(*c++); } void HD44780_SetBacklight(uint8_t new_val) { if(new_val) HD44780_Backlight(); else HD44780_NoBacklight(); } void HD44780_NoBacklight(void) { dpBacklight=LCD_NOBACKLIGHT; ExpanderWrite(0); } void HD44780_Backlight(void) { dpBacklight=LCD_BACKLIGHT; ExpanderWrite(0); } static void SendCommand(uint8_t cmd) { Send(cmd, 0); } static void SendChar(uint8_t ch) { Send(ch, RS); } static void Send(uint8_t value, uint8_t mode) { uint8_t highnib = value & 0xF0; uint8_t lownib = (value<<4) & 0xF0; Write4Bits((highnib)|mode); Write4Bits((lownib)|mode); } static void Write4Bits(uint8_t value) { ExpanderWrite(value); PulseEnable(value); } static void ExpanderWrite(uint8_t _data) { uint8_t data = _data | dpBacklight; HAL_I2C_Master_Transmit(&hi2c2, DEVICE_ADDR, (uint8_t*)&data, 1, 10); } static void PulseEnable(uint8_t _data) { ExpanderWrite(_data | ENABLE); DelayUS(20); ExpanderWrite(_data & ~ENABLE); DelayUS(20); } static void DelayInit(void) { CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; //~0x00000001; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; //0x00000001; DWT->CYCCNT = 0; /* 3 NO OPERATION instructions */ __ASM volatile ("NOP"); __ASM volatile ("NOP"); __ASM volatile ("NOP"); } static void DelayUS(uint32_t us) { uint32_t cycles = (SystemCoreClock/1000000L)*us; uint32_t start = DWT->CYCCNT; volatile uint32_t cnt; do { cnt = DWT->CYCCNT - start; } while(cnt < cycles); } |
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "liquidcrystal_i2c.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c2; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C2_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C2_Init(); /* USER CODE BEGIN 2 */ HD44780_Init(2); HD44780_Clear(); HD44780_SetCursor(0,0); HD44780_PrintStr("HELLO"); HD44780_SetCursor(6,0); HD44780_PrintStr("WORLD"); HAL_Delay(2000); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief I2C2 Initialization Function * @param None * @retval None */ static void MX_I2C2_Init(void) { /* USER CODE BEGIN I2C2_Init 0 */ /* USER CODE END I2C2_Init 0 */ /* USER CODE BEGIN I2C2_Init 1 */ /* USER CODE END I2C2_Init 1 */ hi2c2.Instance = I2C2; hi2c2.Init.ClockSpeed = 100000; hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c2.Init.OwnAddress1 = 0; hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c2.Init.OwnAddress2 = 0; hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c2) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C2_Init 2 */ /* USER CODE END I2C2_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "liquidcrystal_i2c.h" #include "stdio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ ADC_HandleTypeDef hadc1; I2C_HandleTypeDef hi2c2; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C2_Init(void); static void MX_ADC1_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ uint16_t adcValue; char adcValueStr[6]; /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C2_Init(); MX_ADC1_Init(); /* USER CODE BEGIN 2 */ HD44780_Init(2); HD44780_Clear(); HD44780_SetCursor(0,0); HD44780_PrintStr("Potentiometer:"); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1,500); adcValue = HAL_ADC_GetValue(&hadc1); sprintf(adcValueStr, "%5d", adcValue); HD44780_SetCursor(0, 1); HD44780_PrintStr(adcValueStr); HAL_Delay(500); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /** * @brief ADC1 Initialization Function * @param None * @retval None */ static void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Common config */ hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode = ENABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_9; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ } /** * @brief I2C2 Initialization Function * @param None * @retval None */ static void MX_I2C2_Init(void) { /* USER CODE BEGIN I2C2_Init 0 */ /* USER CODE END I2C2_Init 0 */ /* USER CODE BEGIN I2C2_Init 1 */ /* USER CODE END I2C2_Init 1 */ hi2c2.Instance = I2C2; hi2c2.Init.ClockSpeed = 100000; hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c2.Init.OwnAddress1 = 0; hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c2.Init.OwnAddress2 = 0; hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c2) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C2_Init 2 */ /* USER CODE END I2C2_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "fonts.h" #include "ssd1306.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c2; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C2_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C2_Init(); /* USER CODE BEGIN 2 */ SSD1306_Init(); char string[5]; SSD1306_GotoXY (0,0); SSD1306_Puts ("Hello", &Font_11x18, 1); SSD1306_GotoXY (0, 30); SSD1306_Puts ("World", &Font_11x18, 1); SSD1306_UpdateScreen(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief I2C2 Initialization Function * @param None * @retval None */ static void MX_I2C2_Init(void) { /* USER CODE BEGIN I2C2_Init 0 */ /* USER CODE END I2C2_Init 0 */ /* USER CODE BEGIN I2C2_Init 1 */ /* USER CODE END I2C2_Init 1 */ hi2c2.Instance = I2C2; hi2c2.Init.ClockSpeed = 400000; hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c2.Init.OwnAddress1 = 0; hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c2.Init.OwnAddress2 = 0; hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c2) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C2_Init 2 */ /* USER CODE END I2C2_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "ssd1306.h" #include "fonts.h" #include "stdio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c2; TIM_HandleTypeDef htim1; /* USER CODE BEGIN PV */ #define TRIG_PIN GPIO_PIN_9 #define TRIG_PORT GPIOB #define ECHO_PIN GPIO_PIN_8 #define ECHO_PORT GPIOB uint32_t pMillis; uint32_t Value1 = 0; uint32_t Value2 = 0; uint16_t Distance = 0; // cm char strCopy[15]; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C2_Init(void); static void MX_TIM1_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C2_Init(); MX_TIM1_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_Base_Start(&htim1); HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the TRIG pin low SSD1306_Init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET); // pull the TRIG pin HIGH __HAL_TIM_SET_COUNTER(&htim1, 0); while (__HAL_TIM_GET_COUNTER (&htim1) < 10); // wait for 10 us HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET); // pull the TRIG pin low pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout) // wait for the echo pin to go high while (!(HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 10 > HAL_GetTick()); Value1 = __HAL_TIM_GET_COUNTER (&htim1); pMillis = HAL_GetTick(); // used this to avoid infinite while loop (for timeout) // wait for the echo pin to go low while ((HAL_GPIO_ReadPin (ECHO_PORT, ECHO_PIN)) && pMillis + 50 > HAL_GetTick()); Value2 = __HAL_TIM_GET_COUNTER (&htim1); Distance = (Value2-Value1)* 0.034/2; SSD1306_GotoXY (0, 0); SSD1306_Puts ("Distance:", &Font_11x18, 1); sprintf(strCopy,"%d ", Distance); SSD1306_GotoXY (0, 30); SSD1306_Puts (strCopy, &Font_16x26, 1); SSD1306_UpdateScreen(); HAL_Delay(50); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /** * @brief I2C2 Initialization Function * @param None * @retval None */ static void MX_I2C2_Init(void) { /* USER CODE BEGIN I2C2_Init 0 */ /* USER CODE END I2C2_Init 0 */ /* USER CODE BEGIN I2C2_Init 1 */ /* USER CODE END I2C2_Init 1 */ hi2c2.Instance = I2C2; hi2c2.Init.ClockSpeed = 400000; hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c2.Init.OwnAddress1 = 0; hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c2.Init.OwnAddress2 = 0; hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c2) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C2_Init 2 */ /* USER CODE END I2C2_Init 2 */ } /** * @brief TIM1 Initialization Function * @param None * @retval None */ static void MX_TIM1_Init(void) { /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_IC_InitTypeDef sConfigIC = {0}; /* USER CODE BEGIN TIM1_Init 1 */ /* USER CODE END TIM1_Init 1 */ htim1.Instance = TIM1; htim1.Init.Prescaler = 71; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 65535; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } if (HAL_TIM_IC_Init(&htim1) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; sConfigIC.ICFilter = 0; if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM1_Init 2 */ /* USER CODE END TIM1_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* USER CODE BEGIN MX_GPIO_Init_1 */ /* USER CODE END MX_GPIO_Init_1 */ /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); /*Configure GPIO pin : PB8 */ GPIO_InitStruct.Pin = GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /*Configure GPIO pin : PB9 */ GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* USER CODE BEGIN MX_GPIO_Init_2 */ /* USER CODE END MX_GPIO_Init_2 */ } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, 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.