STM32 uses hardware IIC to read SHTC3 temperature and humidity sensor and display it on OLED screen

STM32 uses hardware I2C to read the data of SHTC3 temperature and humidity sensor and display it on the 0.96-inch OLED screen.

I use STM32F103C8T6, and the program is written in ST standard library.

Realization rendering


Introduction to I2C protocol

The I2C communication protocol (Inter-Integrated Circuit) was developed by Philps. Because it has few pins, simple hardware implementation, and strong scalability, it does not require external transceiver devices (those level conversion chips) for communication protocols such as USART and CAN. , is now widely used in communication between multiple integrated circuits (ICs) within a system.

I2C has only one data bus SDA (Serial Data Line), the serial data bus can only send data bit by bit, which belongs to serial communication and adopts half-duplex communication

Half-duplex communication: Two-way communication can be realized, but it cannot be carried out in two directions at the same time. It must be carried out alternately. In fact, it can also be understood as a simplex communication that can switch directions. A data line is required.
For the I2C communication protocol, it is divided into the physical layer and the protocol layer. The physical layer specifies the characteristics of the mechanical and electronic functional parts (hardware part) in the communication system to ensure the transmission of original data in physical media. The protocol layer mainly stipulates the communication logic, and unifies the data packaging and unpacking standards of the sending and receiving parties (software level).

I2C physical layer

Common connection methods between I2C communication devices

(1) It is a bus that supports multiple devices. A "bus" refers to a signal line shared by multiple devices. In one I2C communication bus, multiple I2C communication devices can be connected to support multiple communication masters and multiple communication slaves.

(2) An I2C bus uses only two bus lines, a bidirectional serial data line SDA (Serial Data Line), and a serial clock line SCL (Serial Data Line). The data line is used to represent data, and the clock line is used for data transmission and reception synchronization

(3) The bus is connected to the power supply through a pull-up resistor. When the I2C device is idle, it will output a high-impedance state , and when all devices are idle and output a high-impedance state, the pull-up resistor will pull the bus to a high level .

During I2C communication, the GPIO port of the microcontroller must be set to open-drain output, otherwise it may cause a short circuit.

For more information about STM32's I2C and how to use it, you can read this article: https://url.zeruns.tech/JC0Ah

I will not explain in detail here.

SHTC3 temperature and humidity sensor

SHTC3 data sheet download address: https://url.zeruns.tech/WpLDy

You can get a general information by browsing the data sheet. SHTC3 is a sensor that can detect temperature and humidity. Temperature
range: -40°C~125°C
Humidity range: 0%~100%
Working voltage: 1.6v~3.6v
Communication method: i2c
clock Frequency: three modes are 0 ~ 100kHz 0 ~ 400kHz 0 ~ 1000kHz

Find the following key information

Temperature and humidity device address and read and write commands

In the actual use process, the device address of SHTC3 needs to be sent together with the read and write data/command direction bits to form a byte. The lowest bit of the byte is the read and write data/command direction bits, and the upper 7 bits are the device address of SHTC3.

If you want to write data or commands to SHTC3 through I2C, after the I2C start signal, you need to send "1110 0000", that is, 0xE0 to SHTC3. "Notify SHTC3 that the next step is to write data or command operation.

If you want to read the data in SHTC3 through I2C, after the I2C start signal, you need to send "1110 0001", that is, 0xE1 to SHTC3. In addition to addressing the device address through the upper 7 bits "1110 000", you also need to pass the lowest bit "1" "Notify SHTC3 that the next step is to read data.
Simply put, 0xE0 means write data, and 0xE1 means read data. However, when using STM32 hardware I2C, you only need to enter 0xE0, and the lowest standard library will handle it.

Read temperature and humidity data

=

It can be seen that, in addition to the different order of data obtained, different commands also have a difference between Clock Stretching Enable and Disable.

Clock Stretching is the meaning of clock stretching. If the Clock Stretching Enable command is used, then after sending the measurement command, SHTC3 will pull down the I2C clock line SCL during the measurement of temperature and humidity data by SHTC3, so as to prohibit the host from sending commands to SHTC3. When the humidity data is measured, SHTC3 will release the clock line SCL.

If the Clock Stretching Disable command is used, SHTC3 will not pull down the I2C clock line SCL during the process of SHTC3 measuring data, but if the host sends commands or data during the process of SHTC3 measuring data, SHTC3 will not respond For the host, the host can judge whether SHTC3 has completed the data measurement by whether there is a response signal from SHTC3.

From the data sheet, a measurement cycle package summarizes four steps:

  1. Send a wakeup command.
  2. Send measurement command
  3. Read the data after the measurement is completed.
  4. Send a sleep command.

The above wake-up command and sleep command can be queried in the data sheet.

Summarized as follows:

  1. Wake up SHTC3: first send the write command (0xE0), then send the high bit of the wake up command (0x35), and then send the low bit of the wake up command (0x17).
  2. Waiting for wakeup: The maximum wakeup time written in the data sheet is 240us, and the waiting time is longer than this.
  3. Send collection command: first send the write command (0xE0), and then send the high and low bits of the collection command. There are multiple collection instructions, and you can choose according to your needs.
  4. Receive data: send a read command (0xE1), and receive 6 bytes of data continuously. If the collected command is to store the temperature first, then the 1st-2nd byte of these 6 bytes is the temperature value, and the 3rd byte is the temperature check. The 4th-5th byte is the humidity value, and the 6th byte is the humidity check. If the collected instruction is to store the humidity first, the first 3 bytes and the last 3 bytes are opposite.
  5. Go to sleep: Send a write command, and then send a sleep command to enter sleep.

Calculation of data

It can be seen from the shtc3 data sheet

For example: the collected humidity value is 0x6501, converted to decimal is 25857.
Then: Humidity = 100 * 25857 / 65536 = 39.45 (unit: %) The
collected temperature value is 0x6600, converted to decimal is 26112.
Then: temperature = -45 + 175 * 26112 / 65536 = 24.72 (unit: ℃)

components needed

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
DuPont line: https://s.click.taobao.com/xAkAJRu
Breadboard: https://s.click.taobao.com/ShJAJRu
ST-LINK V2: https://s.click.taobao.com/C8ftZRu

program

Here are three main codes, main.c, shtc3.c and oled.c, please download the compressed package linked below.

Complete project file: https://url.zeruns.tech/EXCvo

The SCL of the SHTC3 and OLED module is connected to PB6, and the SDA is connected to PB7.

Use VSCode instead of Keil to realize the development of STM32 and 51 MCU: https://blog.zeruns.tech/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();	//初始化看门狗
	OLED_Init();			//初始化OLED屏
	SHTC3_I2C_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;			//声明变量存放温湿度数据

		if(ReadSHTC3(&Hum,&Temp))	//读取温湿度数据
		{
    
    
			if(Temp>=0)
			{
    
    
				char String[10];
				sprintf(String, "%.2fC", Temp);	//格式化字符串输出到字符串变量
				OLED_ShowString(1, 3, String);	//显示温度
				/*
				OLED_ShowNum(1,3, (uint8_t)Temp, numlen((uint8_t)Temp));//显示温度整数部分
				OLED_ShowChar(1, 3+numlen((uint8_t)Temp), '.');			//显示小数点
				OLED_ShowNum(1,3+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2);	//显示温度小数部分
				OLED_ShowChar(1, 3+numlen((uint8_t)Temp)+1+2, 'C');		//显示符号
				*/

				sprintf(String, "%.2f%%", Hum);	//格式化字符串输出到字符串变量
				OLED_ShowString(2, 3, String);	//显示湿度
				/*
				OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum));	//显示湿度整数部分
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.');			//显示小数点
				OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2);	//显示湿度小数部分
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%');			//显示符号
				*/
			}else
			{
    
    
				char String[10];
				sprintf(String, "-%.2fC", Temp);//格式化字符串输出到字符串变量
				OLED_ShowString(1, 3, String);	//显示温度
				/*
				OLED_ShowChar(1, 3, '-');			//显示负号
				OLED_ShowNum(1,3+1, (uint8_t)Temp, numlen((uint8_t)Temp));	//显示温度整数部分
				OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp), '.');			//显示小数点
				OLED_ShowNum(1,3+1+numlen((uint8_t)Temp)+1, (uint8_t)(Temp*100)%100, 2);	//显示温度小数部分
				OLED_ShowChar(1, 3+1+numlen((uint8_t)Temp)+1+2, 'C');		//显示符号
				*/

				sprintf(String, "%.2f%%", Hum);	//格式化字符串输出到字符串变量
				OLED_ShowString(2, 3, String);	//显示湿度
				/*
				OLED_ShowNum(2,3, (uint8_t)Hum, numlen((uint8_t)Hum));	//显示湿度整数部分
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum), '.');			//显示小数点
				OLED_ShowNum(2,3+numlen((uint8_t)Hum)+1, (uint8_t)(Hum*100)%100, 2);	//显示湿度小数部分
				OLED_ShowChar(2, 3+numlen((uint8_t)Hum)+1+2, '%');			//显示符号
				*/
			}
		}
		else
		{
    
    
			err_count++;
			OLED_ShowNum(4,11, err_count, numlen(err_count));	//显示错误次数计数
		}
	/*
	https://blog.zeruns.tech
	*/

		Delay_ms(100);	//延时100毫秒

		IWDG_FeedDog();	//喂狗(看门狗,超过1秒没有执行喂狗则自动复位)
	}
}

/**
  * @brief  计算整数长度
  * @param  num 要计算长度的整数
  * @retval 长度值
  */
uint16_t numlen(uint16_t num)
{
    
    
    uint16_t len = 0;        // 初始长度为0
    for(; num > 0; ++len)    // 判断num是否大于0,否则长度+1
        num /= 10;	         // 使用除法进行运算,直到num小于1
    return len;              // 返回长度的值
}

SHTC3.c

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

/*SHTC3地址*/
#define SHTC3_ADDRESS 0xE0

/*设置使用哪一个I2C*/
#define I2Cx I2C1

/*
https://blog.zeruns.tech
*/

/**
  * @brief  CRC校验,CRC多项式为:x^8+x^5+x^4+1,即0x31
  * @param  DAT 要校验的数据
  * @retval 校验码
  */
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;
}

/*发送起始信号*/
void SHTC3_I2C_START(){
    
    
    while( I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));//等待总线空闲
	I2C_GenerateSTART(I2Cx, ENABLE);//发送起始信号
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);//检测EV5事件
}

/*发送停止信号*/
void SHTC3_I2C_STOP(){
    
    
    I2C_GenerateSTOP(I2Cx, ENABLE);//发送停止信号
}

/**
  * @brief  发送两个字节数据
  * @param  MSB 高8位
  * @param  LSB 低8位
  * @retval 无
  */
void SHTC3_WriteByte(uint8_t MSB,uint8_t LSB)
{
    
    
	SHTC3_I2C_START();  //发送起始信号
	
	I2C_Send7bitAddress(I2Cx, SHTC3_ADDRESS, I2C_Direction_Transmitter);    //发送设备写地址
	while(I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);  //检测EV6事件

    I2C_SendData(I2Cx, MSB);//发送高8位数据
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检测EV8事件
   
	I2C_SendData(I2Cx, LSB);//发送低8位数据
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检测EV8事件
	
	I2C_GenerateSTOP(I2Cx, ENABLE);//发送停止信号
	
}

/**
  * @brief  读取数据
  * @retval 读取到的字节数据
  */
uint8_t SHTC3_ReadData()
{
    
    
    while (!I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED));//检测EV7事件
	return I2C_ReceiveData(I2Cx);//读取数据并返回
}

/*软件复位SHTC3*/
void SHTC3_SoftReset(void)                    
{
    
    
    SHTC3_WriteByte(0x80,0x5D);    //重置SHTC3
}

/*引脚初始化*/
void SHTC3_I2C_Init(void)
{
    
    
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//使能I2C1时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟
 
	/*STM32F103芯片的硬件I2C1: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;               //定义结构体配置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;		//设置输出模式为开漏输出,需接上拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);              //初始化GPIO
	
	I2C_DeInit(I2Cx);	//将外设I2C寄存器重设为缺省值
	I2C_InitTypeDef  I2C_InitStructure;                 //定义结构体配置I2C
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//工作模式
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//时钟占空比,Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x30;	//主机的I2C地址,用不到则随便写,无影响
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//使能应答位
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置地址长度7位
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//I2C传输速度,400K,根据自己所用芯片手册查看支持的速度。	
	I2C_Init(I2Cx, &I2C_InitStructure);         //初始化I2C

	I2C_Cmd(I2Cx, ENABLE);  //启用I2C

	SHTC3_WriteByte(0X35,0X17);//唤醒SHTC3
	
	Delay_us(200);
}

/**
  * @brief  读取SHTC3数据
  * @param  *Hum 湿度
  * @param  *Temp 温度
  * @retval 1 - 读取成功;0 - 读取失败
  */
uint8_t ReadSHTC3(float *Hum,float *Temp)
{
    
    
    uint16_t HumData,TempData,HumCRC,TempCRC;//声明变量存放读取的数据
    
    /*SHTC3_WriteByte(0X35,0X17);//唤醒SHTC3
	
	Delay_us(300);*/
    
    SHTC3_WriteByte(0X5C,0X24);//发送指令,先读取湿度数据,时钟拉伸(测量数据期间拉低SCL时钟线,占用总线)
	
    SHTC3_I2C_START();//发送起始信号
    
	I2C_Send7bitAddress(I2Cx,SHTC3_ADDRESS,I2C_Direction_Receiver);//发送设备读地址
	
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR);//检测EV6事件
	
    HumData = SHTC3_ReadData(); //读取湿度高8位数据
    HumData=HumData<<8;         //左移8位
	HumData |= SHTC3_ReadData();//读取湿度低8位数据
    HumCRC = SHTC3_ReadData();  //读取湿度CRC校验数据

    TempData = SHTC3_ReadData();//读取温度高8位数据
    TempData=TempData<<8;       //左移8位
	TempData |= SHTC3_ReadData();//读取温度低8位数据
    TempCRC = SHTC3_ReadData(); //读取温度CRC校验数据

    SHTC3_I2C_STOP();   //发送停止信号
		
	//SHTC3_WriteByte(0XB0,0X98);//发送休眠指令

    if( SHTC3_CRC_CAL(HumData)==HumCRC && SHTC3_CRC_CAL(TempData)==TempCRC ){
    
       //对接收到数据进行CRC校验
       *Hum = (float)HumData*100/65536;        //将接收的16位二进制数据转换为10进制湿度数据
       *Temp = (float)TempData*175/65536-45;   //将接收的16位二进制数据转换为10进制温度数据
       return 1;
    }
    else{
    
    
        return 0;
    }
}

OLED.c

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

/*OLED屏地址*/
#define OLED_ADDRESS 0x78

/*设置哪一个使用I2C*/
#define I2Cx I2C1

/*
https://blog.zeruns.tech
*/

/*引脚初始化*/
void OLED_I2C_Init(void)
{
    
    
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//使能I2C1时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟
 
	/*STM32F103芯片的硬件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;		//设置输出模式为开漏输出,需接上拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_DeInit(I2Cx);	//将外设I2C寄存器重设为缺省值
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//工作模式
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//时钟占空比,Tlow/Thigh = 2
	I2C_InitStructure.I2C_OwnAddress1 = 0x30;	//主机的I2C地址,用不到则随便写,无影响
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//使能应答位
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置地址长度7位
	I2C_InitStructure.I2C_ClockSpeed = 400000;	//I2C传输速度,400K,根据自己所用芯片手册查看支持的速度。	
	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));
	
	//发送起始信号
	I2C_GenerateSTART(I2Cx, ENABLE);
	//检测EV5事件
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
	//发送设备写地址
	I2C_Send7bitAddress(I2Cx, OLED_ADDRESS, I2C_Direction_Transmitter);
	//检测EV6事件
	while( I2C_CheckEvent(I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
	
	//发送要操作设备内部的地址
	I2C_SendData(I2Cx, addr);
	//检测EV8_2事件
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	
	I2C_SendData(I2Cx, data);//发送数据
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

	//发送停止信号
	I2C_GenerateSTOP(I2Cx, ENABLE);
}
 
/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(unsigned char Command)//写命令
{
    
    
	I2C_WriteByte(0x00, Command);
}
 
/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
*/
void OLED_WriteData(unsigned char Data)//写数据
{
    
    
	I2C_WriteByte(0x40, Data);
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点,向下方向的坐标,范围:0~7
  * @param  X 以左上角为原点,向右方向的坐标,范围:0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
    
    
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置低4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置高4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
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部分清屏
  * @param  Line 行位置,范围:1~4
  * @param  start 列开始位置,范围:1~16
  * @param  end 列开始位置,范围:1~16
  * @retval 无
  */
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);		//设置光标位置在上半部分
		for (i = 0; i < 8; i++)
		{
    
    
			OLED_WriteData(0x00);			//显示上半部分内容
		}
		OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
		for (i = 0; i < 8; i++)
		{
    
    
			OLED_WriteData(0x00);		//显示下半部分内容
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置,范围:1~4
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的一个字符,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
    
          	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
    
    
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
    
    
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
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次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
    
    
	uint32_t Result = 1;
	while (Y--)
	{
    
    
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
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显示数字(十进制,带符号数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-2147483648~2147483647
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
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显示数字(十六进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFFFFFF
  * @param  Length 要显示数字的长度,范围:1~8
  * @retval 无
  */
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显示数字(二进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
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初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
    
    
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
    
    
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}

Part of the content refers to the following two articles:

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

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

recommended reading

Guess you like

Origin blog.csdn.net/u012513463/article/details/127688867