STM32单片机软件模拟I2C读取AM2320温湿度传感器数据

news2024/10/7 8:20:30

STM32单片机使用软件模拟IIC读取AM2320温湿度传感器的数据并显示在0.96寸OLED屏上。

我用的单片机是STM32F103C8T6,程序用的是ST标准库写的。

STM32使用硬件I2C读取SHTC3温湿度传感器:https://blog.zeruns.tech/archives/692.html
STM32单片机读取AHT10温湿度传感器数据:https://blog.zeruns.tech/archives/693.html

实现效果图

I2C协议简介

I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备(那些电平转化芯片),现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

I2C只有一跟数据总线 SDA(Serial Data Line),串行数据总线,只能一位一位的发送数据,属于串行通信,采用半双工通信

半双工通信:可以实现双向的通信,但不能在两个方向上同时进行,必须轮流交替进行,其实也可以理解成一种可以切换方向的单工通信,同一时刻必须只能一个方向传输,只需一根数据线.
对于I2C通讯协议把它分为物理层和协议层物理层规定通讯系统中具有机械、电子功能部分的特性(硬件部分),确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准(软件层面)。

I2C物理层

I2C 通讯设备之间的常用连接方式

(1) 它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。

(2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线SDA(Serial Data Line ),一条串行时钟线SCL(Serial Data Line )。数据线即用来表示数据,时钟线用于数据收发同步

(3) 总线通过上拉电阻接到电源。当 I2C 设备空闲时会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平

I2C通信时单片机GPIO口必须设置为开漏输出,否则可能会造成短路。

关于更多STM32的I2C相关信息和使用方法可以看这篇文章:https://url.zeruns.tech/JC0Ah

我这里就不详细讲解了。

AM2320温湿度传感器

介绍

AM2320 数字温湿度传感器是一款含有己校准数字信号输出的温湿度复合型传感器。采用专用的温湿度采集技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电容式感湿元件和一个高精度集成测温元件,并与一个高性能微处理器相连接。该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。AM2320 通信方式采用单总线、标准 I2C 两种通信方式。标准单总线接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达 20 米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选择。I2C 通信方式采用标准的通信时序,用户可直接挂在 I2C通信总线上,无需额外布线,使用简单。两种通信方式都采用直接输出经温度补偿后的湿度、温度及校验 CRC 等数字信息,用户无需对数字输出进行二次计算,也无需要对湿度进行温度补偿,便可得到准确的温湿度信息。两种通信方式可自由切换,用户可自由选择,使用方便,应该领域广泛。产品为 4 引线,连接方便,特殊封装形式可根据用户需求而提供。

AM2320数据手册下载地址:https://url.zeruns.tech/74o6F

浏览数据手册可以得到一个大概信息:

  • 温度范围:-40℃~80℃
  • 温度误差:±0.5℃
  • 湿度范围:0%~99.9%
  • 湿度误差:±3%
  • 工作电压:3.1v~5.5v
  • 通讯方式:I2C或单总线
  • 时钟频率:100kHz以内

找到如下几个关键信息

设备地址和读写命令

在实际的使用过程中,AM2320的设备地址需要与读写数据/命令方向位组成一个字节同时发送,字节的最低位为读写数据/命令方向位,高7位是AM2320的设备地址。

如果要通过I2C写数据或命令给AM2320,在I2C起始信号之后,需要发送“1011 1000”,即0xB8给AM2320,除了通过高7位“1011 100”的设备地址寻址还通过最低位“0”通知AM2320接下来是写数据或命令操作。

如果要通过I2C读取AM2320中的数据,在I2C起始信号之后,需要发送“1011 1001”,即0xB9给AM2320,除了通过高7位“1011 100”的设备地址寻址还通过最低位“1”通知AM2320接下来是读取数据的操作。
简单来说就是,0xB8表示写数据,0xB9表示读数据。

读取温湿度数据




从数据手册可知,一个读取周期包概括三个步骤:

  1. 唤醒传感器
  2. 发送读指令
  3. 读返回数据

总结如下:

  1. 唤醒传感器:起始信号+发送0xB8+等待(>800us)+停止信号
  2. 发送读指令:START+发送0xB8(SLA)+0x03(功能码)+0x00(起始地址)+0x04(寄存器长度)+STOP
  3. 接收数据:发送读取指令(0xB9),连续接收8个字节数据。接收到的数据分别为 数据长度+湿度高位+湿度低位+温度高位+温度低位+CRC校验码低字节+CRC校验码高字节
  4. 对接收到的数据进行转换处理。

数据的计算

由AM2320数据手册可知


例如:采集到的湿度数值是0x01F4,换算成十进制是500。
则:湿度 = 500 / 10 = 50.0 (单位:%)
采集到的温度数值是0x00FA,换算成十进制是250。
则:温度 = 250 / 10 = 25.0 (单位:℃)

需要用的元件

  • STM32最小系统板:https://s.click.taobao.com/M2LgRPu
  • AM2320:https://s.click.taobao.com/t7jgRPu
  • OLED模块:https://s.click.taobao.com/w67u9Pu
  • 杜邦线:https://s.click.taobao.com/anyfRPu
  • 面包板:https://s.click.taobao.com/7Vlu9Pu
  • ST-LINK V2:https://s.click.taobao.com/dtbt9Pu
  • 电阻:https://s.click.taobao.com/vbQu9Pu
  • 逻辑分析仪:https://s.click.taobao.com/y2br9Pu

程序

这里就放出main.c、AM2320.c和OLED.c这三个主要的代码,其他的请下载下面链接的压缩包。

完整工程文件:https://url.zeruns.tech/AM2320

AM2320和OLED模块的 SCL接PB12,SDA接PB13。 如果AM2320单独用别的IO口那要记得接上拉电阻,5KΩ左右就行。

使用VSCode代替Keil实现STM32和51单片机的开发: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清屏
}

时序图

实际运行的AM2320发送指令和读取数据的时序图。

推荐阅读

  • 高性价比和便宜的VPS/云服务器推荐: https://www.awsl9527.cn/archives/41.html
  • 使用NPS搭建内网穿透服务器,带Web面板:https://www.awsl9527.cn/archives/748.html
  • Linux搭建网站教程,建站教程:https://www.awsl9527.cn/archives/1094.html
  • 我的世界服务器搭建教程:https://blog.zeruns.tech/tag/mc/
  • 基于STM32和HC-SR04模块实现超声波测距功能:https://blog.zeruns.tech/archives/680.html
  • ESP8266开发环境搭建及项目演示:https://blog.zeruns.tech/archives/526.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/73494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣242.有效的字母异位词(Java语言,排序法、散列表法)

题目描述&#xff1a; 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 解题思路&#xff1a; 思路1&#xff1a;排序法 根据题目意思…

[附源码]Python计算机毕业设计SSM基于微信的基层党建信息系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

容量测试解决了什么问题?

前面几篇性能测试知识科普系列的文章&#xff0c;介绍了性能测试中的核心术语和指标、常用测试策略、压测工具选型、性能需求分析、测试能力分层、新手学习路径以及监控分析工具相关的内容&#xff0c;这些知识可以说是性能测试最基本的能力&#xff0c;也是日常工作中需要经常…

三大宇宙速度

文章目录第一宇宙速度&#xff1a;7.9km/s第二宇宙速度&#xff1a;11.2km/s第三宇宙速度&#xff1a;16.7km/s光年太阳系第一宇宙速度&#xff1a;7.9km/s 1.第一宇宙速度(环地飞行速度)&#xff1a;航空器绕地球飞行&#xff0c;成为地球卫星的最低速度。 2.提出者牛顿。基于…

springcloud-05分布式事务

第五章 分布式事务Seata 在微服务系统中&#xff0c;分布式事务是我们必须要面临和解决的问题&#xff01;&#xff01;&#xff01; 1. 分布式事务问题的产生 下图中的例子可以很好的解释分布式事务问题出现的场景&#xff1a; 图中问题的产生就在于更新库存数量是1个单独…

泛型Java

泛型 泛型&#xff1a;标签 集合接口和集合类在jdk5.0之后都修改为带泛型的结构在实例化集合类时&#xff0c;可以指明具体的泛型类型指明完以后&#xff0c;在集合类或者集合接口中凡是定义类或接口时&#xff0c;内部结构使用到泛型的位置&#xff0c;都指定为实例化时的泛…

Java姓名与手机号中间用*号代替 字符串*号替换

/*** 姓名与手机号中间用*号代替*/public static String caseStarSymbol(String nameStr){ // String nameStr "程序源码论坛"; // String mobile "15888888888";String nameStr1 null;char[] r nameStr.toCharArray(); // char[] m mobi…

服务器硬件规格常用查看命令——磁盘相关命令

smartctl smartctl是一个能够控制和监控磁盘的SMART&#xff08;Self-Monitoring&#xff0c;Analysis and Reporting Technology&#xff0c;自我监测、分析和报告技术&#xff09;命令。 使用方法&#xff1a; 提示&#xff1a;使用该命令只能查看到裸盘设备的型号信息&…

ATtiny13与Proteus仿真-开发与Proteus仿真环境搭建

ATtiny13开发与Proteus仿真环境搭建 本文将详细介绍如何搭建ATtiny13开发环境与Proteus仿真环境。 1、ATtiny13开发环境搭建 本系列文章将使用Microchip Studio作为ATtiny13的集成开发环境(IDE)。 Microchip Studio 是一个集成开发环境 (IDE),用于开发和调试 AVR 和 SAM 微…

Microsoft .NET Desktop Runtime (Framework)

Microsoft .NET Desktop Runtime (Framework) 微软NET Framework或数据库网络是一种软件技术&#xff0c;包括Microsoft的几种编程语言。安装每个窗口后所需的工具之一是NETFramework技术。会的。基于这种技术编写的许多软件工具&#xff0c;它们将得到支持。 -是的。 微软得分…

什么是运放的输入失调电压

大家可以看到这个电路&#xff0c;运放的同相端和反相端都接在0V&#xff0c;大家觉得运放的输出电压是多少。 很多同学觉得&#xff0c;在开环条件下根据运放的传输曲线&#xff0c;当同相端电压等于反相端电压时&#xff0c;运放的输出电压等于0&#xff0c; 可是实际测量运放…

语音识别之Kaldi:神经网络实战

ASR神经网络实战 kaldi语音识别理论与实践课程学习。 之前学习了基于GMM-HMM的传统语音识别&#xff1a;GMM-HMM 其中也包含Kaldi架构的简介&#xff0c;语音数据的预处理&#xff0c;特征提取等过程。 今天学习基于神经网络的语音识别。 神经网络训练脚本 以TDNN为例。 K…

Python IDE之 pycharm的十大奇技淫巧

说到Python的IDE&#xff0c;可能大家都会想到一个非常有名的工具&#xff0c;就是pycharm&#xff0c;他是最受欢迎的一个Python开发工具&#xff0c;其原因就是因为功能强大&#xff0c;适合构建大型项目&#xff0c;当然啦&#xff0c;功能强大同时也就意味着操作复杂&#…

语音学发音语音学笔记

词汇表 articulators 咬合架发音器 cochlea 耳蜗 consonants 元音 dialect 方言 eardrum 鼓膜 endolymph 内淋巴 Epiglottis 喉头盖 formants 共振峰 fricative 摩擦音 Larynx 喉 meatus 耳道 monosyllabic 单音节 pinna 耳廊 pitch 音调 pitch harmonics 音高泛音 phonation …

【云原生 | Kubernetes 实战】09、K8s 控制器 Replicaset 入门到企业实战应用

目录 K8s 控制器 Replicaset 一、Replicaset 控制器&#xff1a;概念、原理解读 1.1 Replicaset 概述 1.2 Replicaset 工作原理&#xff1a;如何管理 Pod &#xff1f; 二、Replicaset 资源清单文件编写技巧 三、Replicaset 使用案例&#xff1a;部署 Guestbook 留言板 …

第16章 母函数

第16章 母函数 母函数是离散数学领域最意外、最有用的发明之一。粗略来讲&#xff0c;母函数将序列问题转化为代数问题。 组合数学中常常出现普通型母函数、指数型母函数、狄利克雷型母函数 16.1 无穷级数 通俗地说,母函数F(x)就是无穷级数 符号[xnx^nxn]F(x)表示母函数F(x…

策略模式学习

0.引言 最近想整理一下代码。我的想法是使用继承的方案&#xff0c;使用多态写一个interface&#xff0c;然后不同的方法来继承它。最近ChatGPT比较火&#xff0c;顺便问了一下它&#xff1a; 在C代码设计中&#xff0c;我对同一算法设计了不同的实现&#xff0c;例如计算平均…

使用 Web 应用程序示例在 Java 中进行安全编码

使用 Web 应用程序示例在 Java 中进行安全编码 使用 Java 中的 Online Shop Web 应用程序示例了解最常见的漏洞以及如何避免它们 课程英文名&#xff1a;Secure coding Dive into Injections with Java & Spring boot 此视频教程共36.0小时&#xff0c;中英双语字幕&…

1x9 Dual SC Optical Transceivers

1、Pin Assignment & Description TD, TD-: DC coupled LVPECL inputs for the transmitter. 50Ω differential lines. RD, RD-: Open-emitter out circuits. DC coupled LVPECL outputs for the receiver. 50Ω differential lines. SD: Signal Detect. Normal opti…

【工业控制】多变量动态矩阵预测控制(DMC)【含Matlab源码 1499期】

⛄一、简介&#xff08;附课程报告&#xff09; 1引言 众所周知&#xff0c;上世纪 60 年代初形成的现代控制理论在航空、航天等领域取得了辉煌的成果。 然而人们不久就发现在完美的理论与控制之间还存在着巨大的鸿沟。主要表现在以下几个方面: 1.现代控制理论的基点是对象精确…