STM32 microcontroller software simulates I2C to read AM2320 temperature and humidity sensor data

STM32 microcontroller uses software to simulate IIC to read the data of AM2320 temperature and humidity sensor and display it on the 0.96-inch OLED screen.

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

STM32 uses hardware I2C to read SHTC3 temperature and humidity sensor: https://blog.zeruns.tech/archives/692.html
STM32 MCU reads AHT10 temperature and humidity sensor data: https://blog.zeruns.tech/archives/693. html

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.

AM2320 temperature and humidity sensor

introduce

AM2320 digital temperature and humidity sensor is a temperature and humidity composite sensor with calibrated digital signal output. The use of dedicated temperature and humidity acquisition technology ensures that the product has extremely high reliability and excellent long-term stability. The sensor includes a capacitive humidity sensing element and a high-precision integrated temperature measuring element, and is connected with a high-performance microprocessor. The product has the advantages of excellent quality, ultra-fast response, strong anti-interference ability, and high cost performance. The communication mode of AM2320 adopts two communication modes of single bus and standard I2C. Standard single-bus interface makes system integration easy and fast. The ultra-small size, extremely low power consumption, and the signal transmission distance can reach more than 20 meters, making it the best choice for various applications and even the most demanding applications. The I2C communication method adopts standard communication timing, and the user can directly hang on the I2C communication bus without additional wiring and is easy to use. Both communication methods use the direct output of temperature-compensated humidity, temperature, and digital information such as CRC checks. The user does not need to perform secondary calculations on the digital output, and does not need to perform temperature compensation on the humidity to obtain accurate temperature and humidity. information. The two communication methods can be switched freely, the user can choose freely, it is easy to use, and should have a wide range of fields. The product is 4-lead, easy to connect, and special packaging can be provided according to user needs.

AM2320 data sheet download address: https://url.zeruns.tech/74o6F

You can get a rough idea by browsing the datasheet:

  • Temperature range: -40℃~80℃
  • Temperature error: ±0.5℃
  • Humidity range: 0%~99.9%
  • Humidity error: ±3%
  • Working voltage: 3.1v~5.5v
  • Communication method: I2C or single bus
  • Clock frequency: within 100kHz

Find the following key information

Device address and read and write commands

In the actual use process, the device address of AM2320 needs to be sent together with the read and write data/command direction bits to form a byte at the same time. 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 AM2320.

If you want to write data or commands to AM2320 through I2C, after the I2C start signal, you need to send "1011 1000", that is, 0xB8 to AM2320. "Notify AM2320 that the next step is to write data or command operation.

If you want to read the data in AM2320 through I2C, after the I2C start signal, you need to send "1011 1001", that is, 0xB9 to AM2320. "Notify AM2320 that the next step is to read data.
Simply put, 0xB8 means write data, and 0xB9 means read data.

Read temperature and humidity data




From the data sheet, a read cycle package summarizes three steps:

  1. wake up sensor
  2. send read command
  3. read return data

Summarized as follows:

  1. Wake up the sensor: start signal + send 0xB8 + wait (>800us) + stop signal
  2. Send read command: START+send 0xB8(SLA)+0x03(function code)+0x00(start address)+0x04(register length)+STOP
  3. Receive data: Send a read command (0xB9), and receive 8 bytes of data continuously. The received data are data length + high humidity + low humidity + high temperature + low temperature + CRC check code low byte + CRC check code high byte
  4. Convert the received data.

Calculation of data

It can be known from the AM2320 data sheet


For example: the collected humidity value is 0x01F4, converted to decimal is 500.
Then: Humidity = 500 / 10 = 50.0 (unit: %) The
collected temperature value is 0x00FA, converted to decimal is 250.
Then: temperature = 250 / 10 = 25.0 (unit: ℃)

components needed

program

Here are the three main codes, main.c, AM2320.c and OLED.c, please download the compressed package linked below for others.

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

SCL of AM2320 and OLED module is connected to PB12, SDA is connected to PB13. If AM2320 uses other IO ports alone, remember to connect a pull-up resistor, about 5KΩ will do.

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 "AM2320.h"
#include "IWDG.h"

int main(void)
{
    
    
	IWDG_Configuration(); //初始化看门狗
	AM2320_I2C_Init();
	OLED_Init();

	OLED_ShowString(1, 1, "T:");
	OLED_ShowString(2, 1, "H:");

	uint16_t i = 0;
	uint16_t err_count = 0;

	while (1)
	{
    
    
		OLED_ShowNum(4, 1, i, 5);
		float Temp, Hum; //声明变量存放温湿度数据

		if (ReadAM2320(&Hum, &Temp)) //读取温湿度数据
		{
    
    
			if (Temp >= 0)
			{
    
    
				char String[10];
				sprintf(String, "+%.2fC", Temp); //格式化字符串输出到字符串变量
				OLED_ShowString(1, 3, String);	 //显示温度

				sprintf(String, " %.2f%%", Hum); //格式化字符串输出到字符串变量
				OLED_ShowString(2, 3, String);	 //显示湿度
			}
			else
			{
    
    
				char String[10];
				sprintf(String, "-%.2fC", Temp); //格式化字符串输出到字符串变量
				OLED_ShowString(1, 3, String);	 //显示温度

				sprintf(String, " %.2f%%", Hum); //格式化字符串输出到字符串变量
				OLED_ShowString(2, 3, String);	 //显示湿度
			}
		}
		else
		{
    
    
			err_count++;
			OLED_ShowNum(3, 1, err_count, 5); //显示错误次数计数
		}
		Delay_ms(100);
		i++;
		if (i >= 99999)
			i = 0;
		if (err_count >= 99999)
			err_count = 0;
		IWDG_FeedDog(); //喂狗(看门狗,超过1秒没有执行喂狗则自动复位)
	}
	// blog.zeruns.tech
}

AM2320.c

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

/*
作者博客:https://blog.zeruns.tech
微信公众号:zeruns-gzh
B站主页:https://space.bilibili.com/8320520
*/

/*AM2320地址*/
#define AM2320_ADDRESS 0xB8

/*引脚配置*/
#define AM2320_SCL GPIO_Pin_12
#define AM2320_SDA GPIO_Pin_13
#define AM2320_W_SCL(x) GPIO_WriteBit(GPIOB, AM2320_SCL, (BitAction)(x))
#define AM2320_W_SDA(x) GPIO_WriteBit(GPIOB, AM2320_SDA, (BitAction)(x))
#define AM2320_R_SDA() GPIO_ReadInputDataBit(GPIOB, AM2320_SDA)
#define AM2320_R_SCL() GPIO_ReadInputDataBit(GPIOB, AM2320_SCL)
/*当 STM32 的 GPIO 配置成开漏输出模式时,它仍然可以通过读取
GPIO 的输入数据寄存器获取外部对引脚的输入电平,也就是说它同时具有浮空输入模式的功能*/

/**
 * @brief  CRC校验计算
 * @param  *ptr 要计算的字节数据(以数组变量形式存储)
 * @param  len  要计算的字节个数(数组长度)
 * @retval CRC校验码
 */
unsigned short CRC16(unsigned char *ptr, unsigned char len)
{
    
    
	unsigned short crc = 0xFFFF;
	unsigned char i;
	while (len--)
	{
    
    
		crc ^= *ptr++;
		for (i = 0; i < 8; i++)
		{
    
    
			if (crc & 0x01)
			{
    
    
				crc >>= 1;
				crc ^= 0xA001;
			}
			else
			{
    
    
				crc >>= 1;
			}
		}
	}
	return crc;
}

/**
 * @brief  I2C开始
 * @param  无
 * @retval 无
 */
void AM2320_I2C_Start(void)
{
    
    
	AM2320_W_SDA(1);
	Delay_us(2); //延时2微秒
	AM2320_W_SCL(1);
	Delay_us(4);
	AM2320_W_SDA(0);
	Delay_us(3);
	AM2320_W_SCL(0);
	Delay_us(5);
}

/**
 * @brief  I2C停止
 * @param  无
 * @retval 无
 */
void AM2320_I2C_Stop(void)
{
    
    
	AM2320_W_SDA(0);
	Delay_us(3);
	AM2320_W_SCL(1);
	Delay_us(4);
	AM2320_W_SDA(1);
	Delay_us(4);
}

/**
 * @brief  I2C发送一个字节
 * @param  Byte 要发送的一个字节
 * @retval 无
 */
void AM2320_I2C_SendByte(uint8_t Byte)
{
    
    
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
    
    
		AM2320_W_SDA((Byte << i) & 0x80);
		AM2320_W_SCL(1);
		Delay_us(4);
		AM2320_W_SCL(0);
		Delay_us(5);
	}
	AM2320_W_SDA(1); //释放SDA总线
}

/**
 * @brief  等待应答信号
 * @param  无
 * @retval 1-非应答信号,0-应答信号
 */
uint8_t WaitAck(void)
{
    
    
	uint8_t ret;

	AM2320_W_SCL(1);
	Delay_us(4);
	if (AM2320_R_SDA())
	{
    
    
		ret = 1;
	}
	else
	{
    
    
		ret = 0;
	}
	AM2320_W_SCL(0);
	Delay_us(5);
	return ret;
}

/**
 * @brief  I2C读取一个字节
 * @param  NACK 1-非应答信号,0-应答信号
 * @retval 读取到的字节数据
 */
uint8_t AM2320_I2C_ReadByte(uint8_t NACK)
{
    
    
	uint8_t i, Byte = 0;
	AM2320_W_SDA(1); //释放SDA总线
	for (i = 0; i < 8; i++)
	{
    
    
		AM2320_W_SCL(1);
		Delay_us(4);
		Byte = Byte | (AM2320_R_SDA() << (7 - i));
		AM2320_W_SCL(0);
		Delay_us(5);
	}
	AM2320_W_SDA(NACK); //发送应答/非应答信号
	AM2320_W_SCL(1);
	Delay_us(4);
	AM2320_W_SCL(0);
	Delay_us(5);
	AM2320_W_SDA(1); //释放SDA总线
	return Byte;
}

/*唤醒传感器*/
void AM2320_Wake(void)
{
    
    
	AM2320_I2C_Start();
	AM2320_I2C_SendByte(AM2320_ADDRESS);
	WaitAck();
	Delay_us(1000); //延时1000微秒
	AM2320_I2C_Stop();
}

/*引脚初始化*/
void AM2320_I2C_Init(void)
{
    
    
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB时钟

	GPIO_InitTypeDef GPIO_InitStructure;			 //定义结构体配置GPIO
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = AM2320_SCL;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = AM2320_SDA;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	AM2320_W_SCL(1);
	AM2320_W_SDA(1);
	AM2320_Wake(); //唤醒传感器
}

/**
 * @brief  读取AM2320数据
 * @param  *Hum 湿度
 * @param  *Temp 温度
 * @retval 1 - 读取成功;0 - 读取失败
 */
uint8_t ReadAM2320(float *Hum, float *Temp)
{
    
    
	uint8_t Data[8];

	AM2320_I2C_Start(); //发送起始信号
	AM2320_I2C_SendByte(AM2320_ADDRESS);
	if (WaitAck()) //判断应答信号
	{
    
    
		AM2320_I2C_Stop(); //发送停止信号
		Delay_us(50);
		//再尝试读取一次
		AM2320_I2C_Start(); //发送起始信号
		AM2320_I2C_SendByte(AM2320_ADDRESS);
		if (WaitAck()) //判断应答信号
		{
    
    
			Delay_us(20);
			AM2320_I2C_Stop(); //发送停止信号
			return 0;
		}
		else
		{
    
    
			Delay_us(20);//这里AM2320会莫名其妙地拉低SCL一段时间导致发送的数据出错,所以要延时20微秒等这段时间过去了AM2320释放SCL再继续
			AM2320_I2C_SendByte(0x03); //发送功能码
			WaitAck();				   //等待应答信号
			AM2320_I2C_SendByte(0x00); //发送要读取的寄存器起始地址
			WaitAck();				   //等待应答信号
			AM2320_I2C_SendByte(0x04); //发送要读取的寄存器长度
			WaitAck();				   //等待应答信号
			Delay_us(20);//这里AM2320会莫名其妙地拉低SCL一段时间导致停止信号发送失败,所以延时20微秒等这段时间过去了AM2320释放SCL再继续
			AM2320_I2C_Stop();		   //发送停止信号
		}
	}
	else
	{
    
    
		Delay_us(20);//这里AM2320会莫名其妙地拉低SCL一段时间导致发送的数据出错,所以要延时20微秒等这段时间过去了AM2320释放SCL再继续
		AM2320_I2C_SendByte(0x03); //发送功能码
		WaitAck();				   //等待应答信号
		AM2320_I2C_SendByte(0x00); //发送要读取的寄存器起始地址
		WaitAck();				   //等待应答信号
		AM2320_I2C_SendByte(0x04); //发送要读取的寄存器长度
		WaitAck();				   //等待应答信号
		Delay_us(20);//这里AM2320会莫名其妙地拉低SCL一段时间导致停止信号发送失败,所以延时20微秒等这段时间过去了AM2320释放SCL再继续
		AM2320_I2C_Stop();		   //发送停止信号
	}

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

	AM2320_I2C_Start();
	AM2320_I2C_SendByte(AM2320_ADDRESS | 0x01); //发送读取指令
	WaitAck();
	Delay_us(35);
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
    
    
		if (i != 7)
		{
    
    
			Data[i] = AM2320_I2C_ReadByte(0);
		}
		else
		{
    
    
			Data[i] = AM2320_I2C_ReadByte(1); //读取最后一个字节时发送非应答信号
		}
	}
	AM2320_I2C_Stop();

	if (CRC16(Data, 6) == (Data[6] | (Data[7] << 8))) //校验数据
	{
    
    
		*Hum = ((((uint16_t)Data[2]) << 8) | Data[3]) / 10.0; //计算湿度数据
		if (Data[4] >> 7)									  //判断温度数值是否为负
		{
    
    
			*Temp = ((((uint16_t)(Data[4] && 0x7F) << 8)) | Data[5]) / -10.0; //计算负温度
		}
		else
		{
    
    
			*Temp = ((((uint16_t)Data[4]) << 8) | Data[5]) / 10.0; //计算正温度
		}
		return 1;
	}
	return 0;
}

OLED.c

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

/*引脚配置*/
#define OLED_SCL GPIO_Pin_12
#define OLED_SDA GPIO_Pin_13
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, OLED_SCL, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, OLED_SDA, (BitAction)(x))

/*引脚初始化*/
void OLED_I2C_Init(void)
{
    
    
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = OLED_SCL;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = OLED_SDA;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
 * @brief  I2C开始
 * @param  无
 * @retval 无
 */
void OLED_I2C_Start(void)
{
    
    
	OLED_W_SDA(1);
	OLED_W_SCL(1);
	OLED_W_SDA(0);
	OLED_W_SCL(0);
}

/**
 * @brief  I2C停止
 * @param  无
 * @retval 无
 */
void OLED_I2C_Stop(void)
{
    
    
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
 * @brief  I2C发送一个字节
 * @param  Byte 要发送的一个字节
 * @retval 无
 */
void OLED_I2C_SendByte(uint8_t Byte)
{
    
    
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
    
    
		OLED_W_SDA(Byte & (0x80 >> i));
		OLED_W_SCL(1);
		OLED_W_SCL(0);
	}
	OLED_W_SDA(1); //释放SDA总线
	OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
	OLED_W_SCL(0);
}

/**
 * @brief  OLED写命令
 * @param  Command 要写入的命令
 * @retval 无
 */
void OLED_WriteCommand(uint8_t Command)
{
    
    
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78); //从机地址
	OLED_I2C_SendByte(0x00); //写命令
	OLED_I2C_SendByte(Command);
	OLED_I2C_Stop();
}

/**
 * @brief  OLED写数据
 * @param  Data 要写入的数据
 * @retval 无
 */
void OLED_WriteData(uint8_t Data)
{
    
    
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78); //从机地址
	OLED_I2C_SendByte(0x40); //写数据
	OLED_I2C_SendByte(Data);
	OLED_I2C_Stop();
}

/**
 * @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清屏
}

Timing diagram

The timing diagram of the actual running AM2320 sending instructions and reading data.

recommended reading

Guess you like

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