STM32 reads SHTC3 temperature and humidity sensor using hardware I2C

STM32 uses hardware I²C to read SHTC3 temperature & humidity sensor data and display it on a 0.96-inch OLED screen.

I’m using an STM32F103C8T6 and the ST Standard Peripheral Library.

Electronics / MCU discussion group: 2169025065

Result Photos


I²C Protocol Brief

The I²C (Inter-Integrated Circuit) bus was developed by Philips. It needs few pins, is easy to implement in hardware, scales well, and does not require external transceivers like USART or CAN (level-shifters), so it is widely used for on-board communication between ICs.

I²C has only one data line—SDA (Serial Data Line). Data are sent bit by bit, so it is serial and half-duplex.

Half-duplex: bidirectional, but only one direction at a time; essentially a switchable simplex link. One data line is enough.

We divide I²C into the physical layer (hardware) and the protocol layer (software rules).

I²C Physical Layer

Typical I²C bus connection

  1. Multi-device bus: several devices share the same signal lines. One I²C bus can contain multiple masters and slaves.
  2. Only two wires: bidirectional SDA (Serial Data) and SCL (Serial Clock). SDA carries data; SCL synchronizes it.
  3. Pull-up resistors tie the bus to VCC. When idle, devices go high-Z; with all devices idle the resistors pull the lines high.

MCU GPIOs must be configured as open-drain; otherwise a short-circuit may occur.

For more STM32 I²C details see: https://url.zeruns.com/JC0Ah

SHTC3 Temperature & Humidity Sensor

SHTC3 datasheet: https://url.zeruns.com/WpLDy

From the datasheet:
Temperature range: –40 °C ~ 125 °C
Humidity range: 0 % ~ 100 %
Supply: 1.6 V ~ 3.6 V
Interface: I²C
Clock: 0–100 kHz, 0–400 kHz, 0–1 MHz

Device Address & R/W Command

The 7-bit slave address plus R/W bit form one byte.
LSb = 0 → write, LSb = 1 → read.

Write: 0xE0 (1110 0000)
Read: 0xE1 (1110 0001)

With STM32 hardware I²C you only pass 0xE0; the library handles the R/W bit.

Reading Temperature & Humidity

Commands differ in data order and Clock-Stretching Enable/Disable.

Clock stretching: after the measurement command SHTC3 may hold SCL low until conversion is done (Enable), or simply NACK any access while busy (Disable).

One measurement cycle:

  1. Send wake-up command.
  2. Send measurement command.
  3. Read 6 bytes of data.
  4. Send sleep command.

Summary:

  1. Wake: 0xE0, 0x35, 0x17.
  2. Wait ≥ 240 µs.
  3. Start measurement: 0xE0, CommandHigh, CommandLow (choose one).
  4. Read: 0xE1, receive 6 bytes.
    If temp first: bytes 1-2 = temp, 3 = temp-CRC, 4-5 = RH, 6 = RH-CRC.
    If RH first the order is reversed.
  5. Sleep: 0xE0, SleepCommand.

Data Conversion

Per datasheet:

Humidity = 100 × raw / 65536 [%]
Temperature = –45 + 175 × raw / 65536 [°C]

Example:
RH raw = 0x6501 = 25857 → 100 × 25857 / 65536 = 39.45 %
Temp raw = 0x6600 = 26112 → –45 + 175 × 26112 / 65536 = 24.72 °C

Required Parts

STM32 minimum system board: https://s.click.taobao.com/bqMwZRu
SHTC3 module: https://s.click.taobao.com/WxACJRu
OLED module: https://s.click.taobao.com/aNlvZRu
Jumper wires: https://s.click.taobao.com/xAkAJRu
Breadboard: https://s.click.taobao.com/ShJAJRu
ST-LINK V2: https://s.click.taobao.com/C8ftZRu

Code

Only main.c, shtc3.c and oled.c are shown below; download the full project from the link.

Complete project: https://url.zeruns.com/EXCvo

Wire SHTC3 and OLED SCL → PB6, SDA → PB7.

Using VS Code instead of Keil for STM32 & 51 development: https://blog.zeruns.com/archives/690.html

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "IWDG.h"
#include "SHTC3.h"

uint16_t numlen(uint16_t num);

int main(void)
{
	IWDG_Configuration();	// Init independent watchdog
	OLED_Init();			// Init OLED
	SHTC3_I2C_Init();		// Init SHTC3
	
	OLED_ShowString(1, 1, "T:");
	OLED_ShowString(2, 1, "H:");
	OLED_ShowString(4, 1, "err_count:");

	uint32_t a=0;
	uint16_t err_count=0;
	
	while (1)
	{
		a++;
		OLED_ShowNum(3, 1, a, 9);
		if(a==999999999)a=0;

		float Temp,Hum;			// Variables for temp & RH

		if(ReadSHTC3(&Hum,&Temp))	// Read sensor
		{
			if(Temp>=0)
			{
				char String[10];
				sprintf(String, "%.2fC", Temp);	// Format to string
				OLED_ShowString(1, 3, String);	// Display temperature
				/*
				OLED_ShowNum(1,3, (uint8_t)Temp, numlen((uint8_t)Temp));// Integer part
				OLED_ShowChar(1, 3+numlen((uint8_t)Temp), '.');			// Decimal point
				OLED_ShowNum(1,3+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2);	// Fraction
				OLED_ShowChar(1, 3+numlen((uint8_t)Temp)+1+2, 'C');		// Unit
				*/
```sprintf(String, "%.2f%%", Hum);	//Format string output to string variable
				OLED_ShowString(2, 3, String);	//Display humidity
				/*
				OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum));	//Display humidity integer part
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.');			//Display decimal point
				OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2);	//Display humidity decimal part
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%');			//Display symbol
				*/
			}else
			{
				char String[10];
				sprintf(String, "-%.2fC", Temp);//Format string output to string variable
				OLED_ShowString(1, 3, String);	//Display temperature
				/*
				OLED_ShowChar(1, 3, '-');			//Display negative sign
				OLED_ShowNum(1,3+1, (uint8_t)Temp, numlen((uint8_t)Temp));	//Display temperature integer part
				OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp), '.');			//Display decimal point
				OLED_ShowNum(1,3+1+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2);	//Display temperature decimal part
				OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp)+1+2, 'C');		//Display symbol
				*/

				sprintf(String, "%.2f%%", Hum);	//Format string output to string variable
				OLED_ShowString(2, 3, String);	//Display humidity
				/*
				OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum));	//Display humidity integer part
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.');			//Display decimal point
				OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2);	//Display humidity decimal part
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%');			//Display symbol
				*/
			}
		}
		else
		{
			err_count++;
			OLED_ShowNum(4,11, err_count, numlen(err_count));	//Display error count
		}
	/*
	https://blog.zeruns.com
	*/

		Delay_ms(100);	//Delay 100 milliseconds

		IWDG_FeedDog();	//Feed the dog (watchdog, auto-reset if not fed within 1 second)
	}
}

/**
  * @brief  Calculate integer length
  * @param  num Integer to calculate length
  * @retval Length value
  */
uint16_t numlen(uint16_t num)
{
    uint16_t len = 0;        // Initial length is 0
    for(; num > 0; ++len)    // Check if num is greater than 0, otherwise length +1
        num /= 10;	         // Use division until num is less than 1
    return len;              // Return length value
}

SHTC3.c

#include "stm32f10x.h"
#include "Delay.h"

/*SHTC3 address*/
#define SHTC3_ADDRESS 0xE0

/*Set which I2C to use*/
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/

/**
  * @brief  CRC check, CRC polynomial: x^8+x^5+x^4+1, i.e. 0x31
  * @param  DAT Data to be verified
  * @retval Check code
  */
uint8_t SHTC3_CRC_CAL(uint16_t DAT)
{
	uint8_t i,t,temp;
	uint8_t CRC_BYTE;

	CRC_BYTE = 0xFF;
	temp = (DAT>>8) & 0xFF;

	for(t = 0; t < 2; t++)
	{
		CRC_BYTE ^= temp;
		for(i = 0;i < 8;i ++)
		{
			if(CRC_BYTE & 0x80)
			{
				CRC_BYTE <<= 1;
				CRC_BYTE ^= 0x31;
			}
			else
			{
				CRC_BYTE <<= 1;
			}
		}

		if(t == 0)
		{
			temp = DAT & 0xFF;
		}
	}

	return CRC_BYTE;
}

/*Send start signal*/
void SHTC3_I2C_START(){
    while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));//Wait for bus idle
	I2C_GenerateSTART(I2Cx, ENABLE);//Send start signal
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//Check EV5 event
}

/*Send stop signal*/
void SHTC3_I2C_STOP(){
    I2C_GenerateSTOP(I2Cx, ENABLE);//Send stop signal
}

/**
  * @brief  Send two bytes of data
  * @param  MSB High 8 bits
  * @param  LSB Low 8 bits
  * @retval None
  */
void SHTC3_WriteByte(uint8_t MSB,uint8_t LSB)
{
	SHTC3_I2C_START();  //Send start signal
	
	I2C_Send7bitAddress(I2Cx, SHTC3_ADDRESS, I2C_Direction_Transmitter);    //Send device write address
	while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  //Check EV6 event

    I2C_SendData(I2Cx, MSB);//Send high 8 bits data
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Check EV8 event
   
	I2C_SendData(I2Cx, LSB);//Send low 8 bits data
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//Check EV8 event
	
	I2C_GenerateSTOP(I2Cx, ENABLE);//Send stop signal
	
}

/**
  * @brief  Read data
  * @retval Read byte data
  */
uint8_t SHTC3_ReadData()
{
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//Check EV7 event
	return I2C_ReceiveData(I2Cx);//Read data and return
}

/*Software reset SHTC3*/
void SHTC3_SoftReset(void)                    
{
    SHTC3_WriteByte(0x80,0x5D);    //Reset SHTC3
}

/*Pin initialization*/
void SHTC3_I2C_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//Enable I2C1 clock
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//Enable GPIOB clock
 
	/*STM32F103 chip hardware I2C1: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;               //Define structure to configure GPIO
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;		//Set output mode to open-drain, needs pull-up resistor
	GPIO_Init(GPIOB, &GPIO_InitStructure);              //Initialize GPIO
	
	I2C_DeInit(I2Cx);	//Reset I2C peripheral registers to default values
	I2C_InitTypeDef  I2C_InitStructure;                 //Define structure to configure I2C
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//Working mode
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//Clock duty cycle, Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x30;	//Host I2C address, write randomly if not used, no effect
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//Enable acknowledge bit
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Set address length 7 bits
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//I2C transfer speed, 400K, check supported speed in your chip manual.	
	I2C_Init(I2Cx, &I2C_InitStructure);         //Initialize I2C

	I2C_Cmd(I2Cx, ENABLE);  //Enable I2C

	SHTC3_WriteByte(0X35,0X17);//Wake up SHTC3
	
	Delay_us(200);
}

/**
  * @brief  Read SHTC3 data
  * @param  *Hum Humidity
  * @param  *Temp Temperature
  * @retval 1 - Read success; 0 - Read failure
  */
uint8_t ReadSHTC3(float *Hum,float *Temp)
{
    uint16_t HumData,TempData,HumCRC,TempCRC;//Declare variables to store read data
    
    /*SHTC3_WriteByte(0X35,0X17);//Wake up SHTC3
	
	Delay_us(300);*/
    
    SHTC3_WriteByte(0X5C,0X24);//Send command, read humidity data first, clock stretching (pull SCL clock line low during measurement, occupy bus)
	
    SHTC3_I2C_START();//Send start signal
    
	I2C_Send7bitAddress(I2Cx,SHTC3_ADDRESS,I2C_Direction_Receiver);//Send device read address
	
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//Check EV6 event
	
    HumData = SHTC3_ReadData(); //Read humidity high 8 bits data
    HumData=HumData<<8;         //Left shift 8 bits
	HumData |= SHTC3_ReadData();//Read humidity low 8 bits data
    HumCRC = SHTC3_ReadData();  //Read humidity CRC check data

    TempData = SHTC3_ReadData();//Read temperature high 8 bits data
    TempData=TempData<<8;       //Left shift 8 bits
	TempData |= SHTC3_ReadData();//Read temperature low 8 bits data
    TempCRC = SHTC3_ReadData(); //Read temperature CRC check data

    SHTC3_I2C_STOP();   //Send stop signal
		
	//SHTC3_WriteByte(0XB0,0X98);//Send sleep command

    if( SHTC3_CRC_CAL(HumData)==HumCRC && SHTC3_CRC_CAL(TempData)==TempCRC ){   //CRC check received data
       *Hum = (float)HumData*100/65536;        //Convert received 16-bit binary data to decimal humidity data
       *Temp = (float)TempData*175/65536-45;   //Convert received 16-bit binary data to decimal temperature data
       return 1;
    }
    else{
        return 0;
    }
}

OLED.c

#include "stm32f10x.h"
#include "OLED_Font.h"

/*OLED screen address*/
#define OLED_ADDRESS 0x78

/*Set which I2C to use*/
#define I2Cx I2C1

/*
https://blog.zeruns.com
*/

/*Pin initialization*/
void OLED_I2C_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//Enable I2C1 clock
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//Enable GPIOB clock
 
	/*STM32F103 chip hardware I2C: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;		//Set output mode to open-drain, needs pull-up resistor
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_DeInit(I2Cx);	//Reset I2C peripheral registers to default values
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//Working mode
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//Clock duty cycle, Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x30;	//Host I2C address, write randomly if not used, no effect
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//Enable acknowledge bit
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//Set address length 7 bits
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//I2C transfer speed, 400K, check supported speed in your chip manual.	
	I2C_Init(I2Cx, &I2C_InitStructure);

	I2C_Cmd(I2Cx, ENABLE);
}

void I2C_WriteByte(uint8_t addr,uint8_t data)
{
	
	while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
	
	//Send start signal
	I2C_GenerateSTART(I2Cx, ENABLE);
	//Check EV5 event
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
	//Send device write address
	I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);
	//Check EV6 event
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
	
	//Send address inside device to operate
	I2C_SendData(I2Cx, addr);
	//Check EV8_2 event
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(I2Cx, data);//Send data
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

	//Send stop signal
	I2C_GenerateSTOP(I2Cx, ENABLE);
}
 
/**
  * @brief  OLED write command
  * @param  Command Command to write
  * @retval None
  */
void OLED_WriteCommand(unsigned char Command)//Write command
{
	I2C_WriteByte(0x00, Command);
}
 
``````c
/**
  * @brief  OLED write data
  * @param  Data data to be written
  * @retval none
*/
void OLED_WriteData(unsigned char Data)          // write data
{
    I2C_WriteByte(0x40, Data);
}

/**
  * @brief  OLED set cursor position
  * @param  Y coordinate downward from top-left origin, range: 0~7
  * @param  X coordinate rightward from top-left origin, range: 0~127
  * @retval none
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
    OLED_WriteCommand(0xB0 | Y);                    // set Y position
    OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));    // set high 4 bits of X
    OLED_WriteCommand(0x00 | (X & 0x0F));           // set low 4 bits of X
}

/**
  * @brief  OLED clear screen
  * @param  none
  * @retval none
  */
void OLED_Clear(void)
{  
    uint8_t i, j;
    for (j = 0; j < 8; j++)
    {
        OLED_SetCursor(j, 0);
        for(i = 0; i < 128; i++)
        {
            OLED_WriteData(0x00);
        }
    }
}

/**
  * @brief  OLED partial clear
  * @param  Line row position, range: 1~4
  * @param  start column start position, range: 1~16
  * @param  end column end position, range: 1~16
  * @retval none
  */
void OLED_Clear_Part(uint8_t Line, uint8_t start, uint8_t end)
{  
    uint8_t i, Column;
    for(Column = start; Column <= end; Column++)
    {
        OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);      // set cursor to upper half
        for (i = 0; i < 8; i++)
        {
            OLED_WriteData(0x00);          // clear upper half
        }
        OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);  // set cursor to lower half
        for (i = 0; i < 8; i++)
        {
            OLED_WriteData(0x00);        // clear lower half
        }
    }
}

/**
  * @brief  OLED show a character
  * @param  Line row position, range: 1~4
  * @param  Column column position, range: 1~16
  * @param  Char character to display, range: ASCII visible characters
  * @retval none
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{       
    uint8_t i;
    OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);      // set cursor to upper half
    for (i = 0; i < 8; i++)
    {
        OLED_WriteData(OLED_F8x16[Char - ' '][i]);         // display upper half
    }
    OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);  // set cursor to lower half
    for (i = 0; i < 8; i++)
    {
        OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);     // display lower half
    }
}

/**
  * @brief  OLED show string
  * @param  Line starting row, range: 1~4
  * @param  Column starting column, range: 1~16
  * @param  String string to display, range: ASCII visible characters
  * @retval none
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
    uint8_t i;
    for (i = 0; String[i] != '\0'; i++)
    {
        OLED_ShowChar(Line, Column + i, String[i]);
    }
}

/**
  * @brief  OLED power function
  * @retval returns X raised to the power of Y
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
    uint32_t Result = 1;
    while (Y--)
    {
        Result *= X;
    }
    return Result;
}

/**
  * @brief  OLED display number (decimal, unsigned)
  * @param  Line starting row, range: 1~4
  * @param  Column starting column, range: 1~16
  * @param  Number number to display, range: 0~4294967295
  * @param  Length length of number to display, range: 1~10
  * @retval none
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
    uint8_t i;
    for (i = 0; i < Length; i++)                            
    {
        OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
    }
}

/**
  * @brief  OLED display number (decimal, signed)
  * @param  Line starting row, range: 1~4
  * @param  Column starting column, range: 1~16
  * @param  Number number to display, range: -2147483648~2147483647
  * @param  Length length of number to display, range: 1~10
  * @retval none
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
    uint8_t i;
    uint32_t Number1;
    if (Number >= 0)
    {
        OLED_ShowChar(Line, Column, '+');
        Number1 = Number;
    }
    else
    {
        OLED_ShowChar(Line, Column, '-');
        Number1 = -Number;
    }
    for (i = 0; i < Length; i++)                            
    {
        OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
    }
}

/**
  * @brief  OLED display number (hexadecimal, unsigned)
  * @param  Line starting row, range: 1~4
  * @param  Column starting column, range: 1~16
  * @param  Number number to display, range: 0~0xFFFFFFFF
  * @param  Length length of number to display, range: 1~8
  * @retval none
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
    uint8_t i, SingleNumber;
    for (i = 0; i < Length; i++)                            
    {
        SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
        if (SingleNumber < 10)
        {
            OLED_ShowChar(Line, Column + i, SingleNumber + '0');
        }
        else
        {
            OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
        }
    }
}

/**
  * @brief  OLED display number (binary, unsigned)
  * @param  Line starting row, range: 1~4
  * @param  Column starting column, range: 1~16
  * @param  Number number to display, range: 0~1111 1111 1111 1111
  * @param  Length length of number to display, range: 1~16
  * @retval none
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
    uint8_t i;
    for (i = 0; i < Length; i++)                            
    {
        OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
    }
}

/**
  * @brief  OLED initialization
  * @param  none
  * @retval none
  */
void OLED_Init(void)
{
    uint32_t i, j;
    
    for (i = 0; i < 1000; i++)          // power-on delay
    {
        for (j = 0; j < 1000; j++);
    }
    
    OLED_I2C_Init();            // port initialization
    
    OLED_WriteCommand(0xAE);    // display off
    
    OLED_WriteCommand(0xD5);    // set display clock divide ratio/oscillator frequency
    OLED_WriteCommand(0x80);
    
    OLED_WriteCommand(0xA8);    // set multiplex ratio
    OLED_WriteCommand(0x3F);
    
    OLED_WriteCommand(0xD3);    // set display offset
    OLED_WriteCommand(0x00);
    
    OLED_WriteCommand(0x40);    // set display start line
    
    OLED_WriteCommand(0xA1);    // set segment remap, 0xA1 normal 0xA0 reverse
    
    OLED_WriteCommand(0xC8);    // set COM output scan direction, 0xC8 normal 0xC0 reverse

    OLED_WriteCommand(0xDA);    // set COM pins hardware configuration
    OLED_WriteCommand(0x12);
    
    OLED_WriteCommand(0x81);    // set contrast control
    OLED_WriteCommand(0xCF);

    OLED_WriteCommand(0xD9);    // set pre-charge period
    OLED_WriteCommand(0xF1);

    OLED_WriteCommand(0xDB);    // set VCOMH deselect level
    OLED_WriteCommand(0x30);

    OLED_WriteCommand(0xA4);    // set entire display on/off

    OLED_WriteCommand(0xA6);    // set normal/inverse display

    OLED_WriteCommand(0x8D);    // set charge pump
    OLED_WriteCommand(0x14);

    OLED_WriteCommand(0xAF);    // display on
        
    OLED_Clear();               // OLED clear screen
}

Some content referenced from the following two articles:

https://blog.csdn.net/mj475002864/article/details/114027993

https://blog.csdn.net/k666499436/article/details/124686559

Recommended Reading

1 Like