In this tutorial, we will learn to interface SSD1306 OLED with STM32 Blue Pill and program it using STM32CubeIDE and HAL libraries. An OLED (organic light-emitting diode) is used frequently in displaying texts, bitmap images, shapes, and different types of clocks. They offer good view angles and pixel density in a cost-effective manner. At first, we will take a look at the 0.96-inch OLED display, its pinout, connections with the STM32 board, and then use STMCube IDE to program our module to display different texts on the OLED display.
SSD1306 0.96 inch OLED Display
OLED stands for organic light-emitting diode. Its name shows that it is a flat light emitting technology that is developed when two organic thin films are connected in series between two electric conductors. When an electric current is supplied to these conductors then the organic compound is made which emits the bright light. Typically, one conductor is the transparent conductor between these two conductors therefore there is no need for any backlight to emits the light. Therefore, this OLED display has improved image quality, full viewing angle, high brightness, better contrast, wide color range, low power consumption, more efficient and reliable as compared to a simple LCD display. It is mainly used in digital display devices such as computer monitors, mobile phones, handheld games, and televisions screens, etc.
Although there are several types of OLED displays available in the market the one which we will be using is the SSD1306 0.96-inch OLED display. The main component of all different types of OLED displays is an SSD1306 controller which uses I2C or SPI protocol to communicate with the microcontrollers. The OLED performs faster in SPI communication but it is popular with I2C communication. The reason for the popularity is the lower number of pins. The OLED displays can vary in size, color, and shape but are primarily programmed in a similar way.
Let us take a look at the OLED display which we will be using in this article. It is called SSD1306 0.96-inch OLED display which has 128×64 pixels and communicates only via I2C protocol with the development boards. It is cheap and readily available in the market.
Below you can see the pinout of this OLED Display.
Recommended Reading: Monochrome 0.96” OLED Display
SSD1306 OLED Pinout
There are four pins in this display. Imprinted as VCC, GND, SCL, and SDA respectively. The VCC and GND pins will power the OLED display and will be connected with the STM32 power supply pins as they require a driving voltage of 3.3-5V. The SCL and SDA pins are necessary for generating the clock signal and in the transmission of data respectively. Both of these pins will be connected with the I2C pins of the STM32 board.
Now let us have a look at the specifications for this model:
Size | 0.96 inch |
Terminals | 4 |
Pixels | 128×64 |
Communication | I2C only |
VCC | 3.3V-5V |
Operating Temperature | -40℃ to +80℃ |
Interfacing SSD1306 OLED Display with STM32 Blue Pill
For this project we will require the following components:
- STM32 Blue Pill
- SSD1306 OLED
- Connecting Wires
- Breadboard
As we have seen above, the OLED display has 4 terminals which we will connect with the STM32 Blue Pill board. As the OLED display requires an operating voltage in the range of 3.3-5V, we will connect the VCC terminal with 3.3V which will be in common with the Blue Pill board. The SCL and SDA pins of the OLED will be connected with the I2C1 SDA and SCL pins of the STM32 board respectively. PB7 is I2C1_SDA pin and PB6 is I2C1_SCL pin. Moreover, the grounds will be in common.
The connections between the two devices can be seen below.
STM32 Blue Pill | SSD1306 OLED Display |
---|---|
3.3V | VCC |
PB7 | SDA |
PB6 | SCL |
GND | GND |
OLED with STM32 Blue Pill Code
We will use STM32Cube IDE to program our STM32 board. Open the IDE and head over to a new project.
Then for the target selection, specify the STM32 Blue Pill board number. After that click on any column as shown in the picture below. Then click the ‘Next’ button.
Specify the name of your project then click ‘Finish’ to complete the setup of your project.
Now head over to Connectivity > I2C1. Select the I2C mode as ‘I2C.’ Then go to ‘Parameter Settings’ and set the I2C speed mode as ‘Fast Mode.’ This is necessary for the SSD1306 OLED.
Now go to System Core > RCC then select ‘Crystal/Ceramic Resonator’ in from the High Speed Clock feature.
Now we have enabled the RCC external clock source.
Clock Configuration
Next, go to the Clock Configuration found at the top. This will open the following window. Here we will select the clock frequency.
You can specify your system clock. We will set it as 72 MHz. These are the configurations we have set:
Now we will save our file. Press Ctrl + S. The following window will appear. Click ‘Yes.’ This will generate a template code for you.
Another window will appear that will ask if you want to open the perspective. Click ‘Yes.’
STM32 OLED Library
As we are working with an SSD1306 OLED with our STM32 Nucleo, we will require the ssd1306.h and fonts.h libraries. These libraries will be used to access different functions that will enable us to display texts and numbers on the OLED in various ways.
ssd1306.h
Go to Core > Inc and create a new file called ‘ssd1306.h‘ Copy the following code from this link and save it to this file.
In ssd1306.h file, remove #include “i2c.h” (line 49) and add extern I2C_HandleTypeDef hi2c1;
fonts.h
Go to Core > Inc and create a new file called ‘fonts.h‘ Copy the following code from this link and save it to this file.
ssd1306.c
Similarly, head over to Core > Src and create a new file called ‘ssd1306.c‘ Copy the following code from this link and save it to this file.
fonts.c
Head over to Core > Src and create a new file called ‘fonts.c‘ Copy the following code from this link and save it to this file.
STM32 Blue Pill OLED Code
We will use the ssd1306 library functions to display text and numbers on the OLED in different fonts.
Now let us look at our main.c file that was generated. Inside the main.c file, make sure the following code is part of your script by including the lines of code given below.
#include "main.h"
#include "fonts.h"
#include "ssd1306.h"
I2C_HandleTypeDef hi2c1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
SSD1306_Init();
char string[5];
SSD1306_GotoXY (0,0);
SSD1306_Puts ("SSD1306", &Font_11x18, 1);
SSD1306_GotoXY (0, 30);
SSD1306_Puts ("OLED DEMO", &Font_11x18, 1);
SSD1306_UpdateScreen();
HAL_Delay (1000);
SSD1306_ScrollRight(0,5);
HAL_Delay(3000);
SSD1306_ScrollLeft(0,5);
HAL_Delay(3000);
SSD1306_Stopscroll();
SSD1306_Clear();
SSD1306_GotoXY (30,0);
SSD1306_Puts ("COUNTER", &Font_11x18, 1);
while (1)
{
for (int num=1;num<=1000;num++)
{
itoa(num,string, 10);
SSD1306_GotoXY (0, 30);
SSD1306_Puts (" ", &Font_16x26, 1);
SSD1306_UpdateScreen();
if(num<10) {
SSD1306_GotoXY (53, 30); // 1 DIGIT
}
else if (num<100) {
SSD1306_GotoXY (45, 30); // 2 DIGITS
}
else {
SSD1306_GotoXY (37, 30); // 3 DIGITS
}
SSD1306_Puts (string, &Font_16x26, 1);
SSD1306_UpdateScreen();
HAL_Delay (500);
}
}
}
Working of the Code
We start off by including fonts.h and ssd1306.h libraries.
#include "fonts.h"
#include "ssd1306.h"
Inside the main function, first all the peripherals are initialized, system clock is configured and all the configured peripherals are initialized.
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
The first step is to initialize the OLED. We use the SSD1306_Init() function for initialization.
SSD1306_Init();
Next we will set the x and the y axis position from where the text should start. SSD1306_gotoXY() function is used to set the write pointers. We have passed (0,0) as the parameter hence the text starts from the upper left corner. The first parameter is the x-axis position that (+x) increases towards the right. The second parameter is the y-axis position that (+y) increases downwards.
SSD1306_GotoXY (0,0);
After setting the starting x-axis and y-axis positions, we will write the text on the OLED using the function SSD1306_Puts(). This function takes in three parameters which is the string to be displayed, the font name and the color of the text. We want to display the text ‘SSD1306’ in Font_11x18 and in white color.
SSD1306_Puts ("SSD1306", &Font_11x18, 1);
Similarly, we will set the x and y axis position at (0,30) and print the text ‘OLED DEMO’ in the same font and color. Call SSD1306_UpdateScreen() to display the texts.
SSD1306_GotoXY (0, 30);
SSD1306_Puts ("OLED DEMO", &Font_11x18, 1);
SSD1306_UpdateScreen();
HAL_Delay (1000);
After displaying the text for a second, we will start scrolling it in the right and left direction. To scroll the text in the right direction, we will call SSD1306_ScrollRight(). This function takes in two parameters. The first parameter is the start row and the second parameter is the end row. We will scroll the text in the right direction at start row 0 and end row 5 for 3 seconds.
SSD1306_ScrollRight(0,5);
HAL_Delay(3000);
To scroll the text in the left direction, we will call SSD1306_ScrollLeft(). This function takes in two parameters. The first parameter is the start row and the second parameter is the end row. We will scroll the text in the left direction at start row 0 and end row 5 for 3 seconds.
SSD1306_ScrollLeft(0,5);
HAL_Delay(3000);
After that we will stop the scrolling and clear the screen.
SSD1306_Stopscroll();
SSD1306_Clear();
Next, we will set the x and y axis position at (30, 0) and print the text ‘COUNTER’ in Font_11x18 and white color. We aim to display numbers from 0-1000 under the ‘COUNTER’ text. These numbers will be center aligned and will update to a new number after every second. This will be accomplished by using a for loop that runs for 1000 iterations within the infinite while loop. The starting x and y-axis positions will be changed accordingly so the numbers are displayed in the center of the screen.
SSD1306_GotoXY (30,0);
SSD1306_Puts ("COUNTER", &Font_11x18, 1);
while (1)
{
for (int num=1;num<=1000;num++)
{
itoa(num,string, 10);
SSD1306_GotoXY (0, 30);
SSD1306_Puts (" ", &Font_16x26, 1);
SSD1306_UpdateScreen();
if(num<10) {
SSD1306_GotoXY (53, 30); // 1 DIGIT
}
else if (num<100) {
SSD1306_GotoXY (45, 30); // 2 DIGITS
}
else {
SSD1306_GotoXY (37, 30); // 3 DIGITS
}
SSD1306_Puts (string, &Font_16x26, 1);
SSD1306_UpdateScreen();
HAL_Delay (500);
}
}
main.c file
This is how a complete main.c file will be after modification.
#include "main.h"
#include "fonts.h"
#include "ssd1306.h"
I2C_HandleTypeDef hi2c1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
SSD1306_Init();
char string[5];
SSD1306_GotoXY (0,0);
SSD1306_Puts ("SSD1306", &Font_11x18, 1);
SSD1306_GotoXY (0, 30);
SSD1306_Puts ("OLED DEMO", &Font_11x18, 1);
SSD1306_UpdateScreen();
HAL_Delay (1000);
SSD1306_ScrollRight(0,5);
HAL_Delay(3000);
SSD1306_ScrollLeft(0,5);
HAL_Delay(3000);
SSD1306_Stopscroll();
SSD1306_Clear();
SSD1306_GotoXY (30,0);
SSD1306_Puts ("COUNTER", &Font_11x18, 1);
while (1)
{
for (int num=1;num<=1000;num++)
{
itoa(num,string, 10);
SSD1306_GotoXY (0, 30);
SSD1306_Puts (" ", &Font_16x26, 1);
SSD1306_UpdateScreen();
if(num<10) {
SSD1306_GotoXY (53, 30); // 1 DIGIT
}
else if (num<100) {
SSD1306_GotoXY (45, 30); // 2 DIGITS
}
else {
SSD1306_GotoXY (37, 30); // 3 DIGITS
}
SSD1306_Puts (string, &Font_16x26, 1);
SSD1306_UpdateScreen();
HAL_Delay (500);
}
}
}
/**
* @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 I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
}
/* 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 main.c file after modifying it. Now we are ready to build our project.
Building the Project
To build our project press Ctrl + B or go to Project > Build All.
Your project will start building. After a few moments, your project will be successfully built if there are no errors.
Connecting ST-Link Programmer with STM32
Now as we have successfully built our project let us move ahead and upload the code to our STM32 board. To do that, first, we will have to connect our Blue Pill STM32 with an ST-Link programmer. We will be using ST-Link V2.
This will provide an interface between our computer and our STM32 board. It consists of 10 pins. We will be using pin2 SWDIO, pin6 SWCLK, pin4 GND, and pin8 3.3V to connect with our STM32 board. The SWDIO is the data input/output pin and the SWCLK is the clock pin. Follow the pin configuration given on the ST-LINK V2 to identify each pin.
Follow the table below to connect both devices correctly.
STM32 | ST-LINK V2 |
VCC 3.3V pin | pin8 3.3V |
SWDIO pin | pin2 SWDIO |
SWCLK pin | pin6 SWCLK |
GND pin | pin4 GND |
Additionally, move the BOOT jumper to the right to enable the microcontroller to go into programming mode.
- Now connect your ST-LINK V2 with your computer via the USB port. Both devices will power ON.
- Next press the RUN button in the IDE. The ‘Edit configuration’ window will open up. Click ‘OK’.
- After a few moments, the code will be successfully sent to the STM32 board. Otherwise, press the RESET button on your STM32 board.
- Now to bring the Blue pill back to normal mode make sure you bring the BOOT jumper back at its place.
Once the code is uploaded to the board, the OLED starts displaying the different messages. Watch the video below to have a better insight.
You may also like to read:
Where is the implementation of functions ssd1306_I2C_Write, ssd1306_I2C_WriteMulti, ssd1306_I2C_IsDeviceConnected, etc?
Seems your tutorial is missing a critical source code part with platform-dependent implementation of these functions.
Links to OLED header files are provided in the tutorial, you need to read the article first.
I still couldn’t find the ssd1306_i2c.h file, where is it?
https://github.com/SL-RU/stm32libs/tree/master/stm32f10x/ssd1306
uint8_t slaveAddress = 0x3C;
HAL_StatusTypeDef status = HAL_I2C_IsDeviceReady(&hi2c1, slaveAddress, 3, 100000);
I added this code as I am not getting any result and the status is HAL_BUSY and never comes out of it any thoughts?
Can’t create fonts.c file.
File name is discouraged. File extension does not correspond to known source file types.
Сreation of element failed.
Reason:
‘Can’t create fonts.c file.’ ia an ivalid name on this platform.