基于I2C/SPI总线的温湿度采集与OLED显示

一、I2C总线通信协议

(一)I2C协议

1、什么是I2C协议
I2C 通讯协议(Inter-Integrated Circuit)是由 Phiilps 公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要 USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
2、I2C 协议的物理层和协议层
物理层
I2C是一个支持设备的总线。可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。对于I2C 总线,只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。
I2C 通讯设备常用连接方式(引用野火资料中的图)
在这里插入图片描述
协议层
主要是定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等。
通讯的起始和停止信号
在这里插入图片描述
数据有效性
在这里插入图片描述
从图中可以看出I2C在通讯的时候,只有在SCL处于高电平时,SDA的数据传输才是有效的。SDA 信号线是用于传输数据,SCL 信号线是保证数据同步。
响应
在这里插入图片描述
当SDA传输数据后,接收方对接受到的数据进行一个应答。如果希望继续进行传输数据,则回应应答信号(低电平),否则回应非应答信号(高电平)。

(二)硬件I2C

直接利用 STM32 芯片中的硬件 I2C 外设。
硬件I2C的使用
只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置 1,此时外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。

(三)软件I2C

直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。
软件I2C的使用
需要在控制产生 I2C 的起始信号时,控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。

(四)两者的差别

硬件 I2C 直接使用外设来控制引脚,可以减轻 CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为 SCL 和 SDA,软件模拟 I2C 则可以使用任意 GPIO 引脚,相对比较灵活。对于硬件I2C用法比较复杂,软件I2C的流程更清楚一些。如果要详细了解I2C的协议,使用软件I2C可能更好的理解这个过程。在使用I2C过程,硬件I2C可能通信更加快,更加稳定。

二、stm32通过I2C接口实现温湿度(AHT20)的采集

(一)实验要求

每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)

(二)硬件连接

AHT20的SCL——STM32F103的PB6SDA——PB7GND——GNDVCC——5V
在这里插入图片描述

(三)创建工程

在这里插入图片描述

(四)代码编写

完整工程下载https://github.com/Sunlight-Dazzling/stm32-AHT20/tree/master

(五)运行结果

在KEIL中生成.hex文件后,烧录程序
在这里插入图片描述
利用串口助手观察结果
在这里插入图片描述
对着传感器呼一口气或用手触摸一下后,发现温度升高,实验成功!
在这里插入图片描述

三、了解SPI协议

(一)SPI的定义

SPI(Serial Peripheral Interface)就是串行外围设备接口。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚。SPI 是一个环形总线结构,由 ss(cs)、sck、sdi、sdo 构成,时序主要是在 sck 的控制下,两个双向移位寄存器进行数据交换。
上升沿发送、下降沿接收、高位先发送。
上升沿到来的时候,sdo 上的电平将被发送到从设备的寄存器中。
下降沿到来的时候,sdi 上的电平将被接收到主设备的寄存器中。

(二)SPI的连接方式

在这里插入图片描述
SS( Slave Select):从设备选择信号线,常称为片选信号线。
SCK (Serial Clock):时钟信号线,用于通讯数据同步。
MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。
MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。

(三)SPI的通讯过程

在这里插入图片描述
MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

四、OLED显示屏显示自己的学号和姓名

完整工程下载:百度网盘分享链接 提取码:bphx

1、打开工程,找到test.c文件
修改成想要显示的名字和学号,汉字需要进行取模
在这里插入图片描述
2、汉字取模

软件下载:C语言字模文件的使用,PCtoLCD2002(字模软件) 最新版 附使用指南

打开软件,点击选项进行以下设置
在这里插入图片描述
输入想要取模的汉字,点击生成字模
在这里插入图片描述
可以选择保存字模,更加方便,保存为一个txt文件
在这里插入图片描述
3、修改程序
在工程中找到gui.c文件中的oledfont.h文件,把刚刚获取的16×16的字模复制进去
在这里插入图片描述
4、编译运行,生成.hex文件,烧录程序
在这里插入图片描述
5、硬件连接
在这里插入图片描述
在这里插入图片描述
6、结果显示
在这里插入图片描述

五、OLED显示屏显示AHT20的温度和湿度

完整工程下载: https://github.com/Sunlight-Dazzling/stm32_AHT20_OLED

打开工程文件后编译运行,生成.hex文件,烧录之后即可看到运行结果
硬件需要同时接入OLED显示屏和传感器
在这里插入图片描述

六、OLED显示屏滑动显示长字符

完整工程下载: https://github.com/Sunlight-Dazzling/LEDORoll

同姓名的显示一样,需要进行汉字的取模,过程同上不再赘述
在这里插入图片描述
在这里插入图片描述
同样找到gui.c文件中的oledfont.h文件,把刚刚获取的16×16的字模复制进去
在这里插入图片描述
也要记得修改test.c文件
在这里插入图片描述
结果显示
在这里插入图片描述
注:闪屏现象是由于手机摄像头的拍摄,人眼观看时是没有问题的

七、参考🔗

1、零死角玩转STM32—F103指南者
2、stm32通过I2C接口实现温湿度(AHT20)的采集
3、基于STM32的温湿度采集——OLED显示
4、stm32通过I2C接口实现温湿度(AHT20)的采集
5、0.96寸OLED在STM32f103上实现滚动显示长字符
6、C语言字模文件的使用,PCtoLCD2002(字模软件) 最新版 附使用指南

版权声明:本文为CSDN博主「-安纳西-」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yiwanyinhe/article/details/121397080

目录

一、I2C总线协议了解

 1、I2C协议含义:

2、I2C 协议的物理层和协议层

①物理层

 ②协议层

 3、12C的两种方式——硬件I2C和软件I2C

①硬件I2C

②软件I2C

③两者的差别

二、温度采集至上机位

1、主要代码:

2、编译并烧录

三、基于SPI的OLED显示 

1、SPI协议简介:

2、OLED引脚接法

3、修改代码 

4、效果呈现

1、编译烧录进板

2、结果呈现 

四、 显示温度和湿度

1、主要代码

 2、编译烧录进板效果呈现

五、滑动显示长字符

1、代码

2、编译烧录进板

3、结果呈现 

 六、参考文献


一、I2C总线协议了解

 1、I2C协议含义:

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

2、I2C 协议的物理层和协议层

①物理层

I2C是一个支持设备的总线。可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。对于I2C 总线,只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。
I2C 通讯设备常用连接方式(引用野火资料中的图)

 ②协议层

主要是定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等。

通讯的起始和停止信号

当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。

当 SCL 是高电平时 SDA线由低电平向高电平切换,表示通讯的停止。

 可以看出I2C在通讯的时候,只有在SCL处于高电平时,SDA的数据传输才是有效的。SDA 信号线是用于传输数据,SCL 信号线是保证数据同步。

 当SDA传输数据后,接收方对接受到的数据进行一个应答。如果希望继续进行传输数据,则回应应答信号(低电平),否则回应非应答信号(高电平)。

 3、12C的两种方式——硬件I2C和软件I2C

①硬件I2C

直接利用 STM32 芯片中的硬件 I2C 外设。

硬件I2C的使用
只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置 1,此时外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。

②软件I2C

直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。

软件I2C的使用
需要在控制产生 I2C 的起始信号时,控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。

③两者的差别

硬件 I2C 直接使用外设来控制引脚,可以减轻 CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为 SCL 和 SDA,软件模拟 I2C 则可以使用任意 GPIO 引脚,相对比较灵活。对于硬件I2C用法比较复杂,软件I2C的流程更清楚一些。如果要详细了解I2C的协议,使用软件I2C可能更好的理解这个过程。在使用I2C过程,硬件I2C可能通信更加快,更加稳定。

二、温度采集至上机位

1、主要代码:

mian.c:

#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"


int main(void)
{	
	delay_init();     //延时初始化  
	uart_init(115200);	 //串口初始化
	IIC_Init();
		while(1)
	{
		printf("温度湿度显示");
		read_AHT20_once();
		delay_ms(1500);
  }
}

bsp_i2c.c:

#include "bsp_i2c.h"
#include "delay.h"

uint8_t   ack_status=0;
uint8_t   readByte[6];
uint8_t   AHT20_status=0;

uint32_t  H1=0;  //Humility
uint32_t  T1=0;  //Temperature

uint8_t  AHT20_OutData[4];
uint8_t  AHT20sendOutData[10] = {0xFA, 0x06, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF};

void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //í?íìê?3?
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
 
	IIC_SCL=1;
	IIC_SDA=1;
 
}
//2úéúIIC?eê?D?o?
void IIC_Start(void)
{
	SDA_OUT();     //sda??ê?3?
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//?ˉ×?I2C×ü??£?×?±?·¢?í?ò?óê?êy?Y 
}	  
//2úéúIICí£?1D?o?
void IIC_Stop(void)
{
	SDA_OUT();//sda??ê?3?
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//·¢?íI2C×ü???áê?D?o?
	delay_us(4);							   	
}
//μè′yó|′eD?o?μ?à′
//·μ???μ£o1£??óê?ó|′e꧰ü
//        0£??óê?ó|′e3é1|
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDAéè???aê?è?  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//ê±?óê?3?0 	   
	return 0;  
} 
//2úéúACKó|′e
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//2?2úéúACKó|′e		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC·¢?íò???×??ú
//·μ??′ó?úóD?Tó|′e
//1£?óDó|′e
//0£??Tó|′e			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
		SDA_OUT(); 	    
    IIC_SCL=0;//à-μíê±?ó?aê?êy?Y′?ê?
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //??TEA5767?aèy???óê±??ê?±?D?μ?
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//?á1??×??ú£?ack=1ê±£?·¢?íACK£?ack=0£?·¢?ínACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDAéè???aê?è?
  for(i=0;i<8;i++ )
	{
    IIC_SCL=0; 
    delay_us(2);
		IIC_SCL=1;
    receive<<=1;
    if(READ_SDA)receive++;   
		delay_us(1); 
  }					 
	if (!ack)
			IIC_NAck();//·¢?ínACK
	else
			IIC_Ack(); //·¢?íACK   
	return receive;
}
 
void IIC_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr)
{
	IIC_Start();  
	
	if(device_addr==0xA0) //eepromμ??·′óóú1×??ú
		IIC_Send_Byte(0xA0 + ((addr/256)<<1));//·¢?í??μ??·
	else
		IIC_Send_Byte(device_addr);	    //·¢?÷?tμ??·
	IIC_Wait_Ack(); 
	IIC_Send_Byte(addr&0xFF);   //·¢?íμíμ??·
	IIC_Wait_Ack(); 
	IIC_Send_Byte(data);     //·¢?í×??ú							   
	IIC_Wait_Ack();  		    	   
  IIC_Stop();//2úéúò???í£?1ì??t 
	if(device_addr==0xA0) //
		delay_ms(10);
	else
		delay_us(2);
}
 
uint16_t IIC_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead)  //?á??′??÷?ò?áêy?Y
{	
		uint16_t data;
		IIC_Start();  
		if(device_addr==0xA0)
			IIC_Send_Byte(0xA0 + ((addr/256)<<1));
		else
			IIC_Send_Byte(device_addr);	
		IIC_Wait_Ack();
		IIC_Send_Byte(addr&0xFF);   //·¢?íμíμ??·
		IIC_Wait_Ack(); 
 
		IIC_Start();  	
		IIC_Send_Byte(device_addr+1);	    //·¢?÷?tμ??·
		IIC_Wait_Ack();
		if(ByteNumToRead == 1)//LM75???èêy?Y?a11bit
		{
			data=IIC_Read_Byte(0);
		}
		else
			{
				data=IIC_Read_Byte(1);
				data=(data<<8)+IIC_Read_Byte(0);
			}
		IIC_Stop();//2úéúò???í£?1ì??t	    
		return data;
}


/**********
*é???2?·??aIO?ú?£?éI2C????
*
*′ó?aò????aê??aAHT20μ?????I2C
*oˉêy??óDIICoíI2Cμ???±e£???×¢òa£?£?£?£?£?
*
*2020/2/23×?oóDT??è??ú
*
***********/
void  read_AHT20_once(void)
{
	delay_ms(10);

	reset_AHT20();
	delay_ms(10);

	init_AHT20();
	delay_ms(10);

	startMeasure_AHT20();
	delay_ms(80);

	read_AHT20();
	delay_ms(5);
}


void  reset_AHT20(void)
{

	I2C_Start();

	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();
	if(ack_status) printf("1");
	else printf("1-n-");
	I2C_WriteByte(0xBA);
	ack_status = Receive_ACK();
		if(ack_status) printf("2");
	else printf("2-n-");
	I2C_Stop();

	/*
	AHT20_OutData[0] = 0;
	AHT20_OutData[1] = 0;
	AHT20_OutData[2] = 0;
	AHT20_OutData[3] = 0;
	*/
}



void  init_AHT20(void)
{
	I2C_Start();

	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();
	if(ack_status) printf("3");
	else printf("3-n-");	
	I2C_WriteByte(0xE1);
	ack_status = Receive_ACK();
	if(ack_status) printf("4");
	else printf("4-n-");
	I2C_WriteByte(0x08);
	ack_status = Receive_ACK();
	if(ack_status) printf("5");
	else printf("5-n-");
	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();
	if(ack_status) printf("6");
	else printf("6-n-");
	I2C_Stop();
}



void  startMeasure_AHT20(void)
{
	//------------
	I2C_Start();

	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();
	if(ack_status) printf("7");
	else printf("7-n-");
	I2C_WriteByte(0xAC);
	ack_status = Receive_ACK();
	if(ack_status) printf("8");
	else printf("8-n-");
	I2C_WriteByte(0x33);
	ack_status = Receive_ACK();
	if(ack_status) printf("9");
	else printf("9-n-");
	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();
	if(ack_status) printf("10");
	else printf("10-n-");
	I2C_Stop();
}



void read_AHT20(void)
{
	uint8_t   i;

	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}

	//-------------
	I2C_Start();

	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	readByte[0]= I2C_ReadByte();
	Send_ACK();

	readByte[1]= I2C_ReadByte();
	Send_ACK();

	readByte[2]= I2C_ReadByte();
	Send_ACK();

	readByte[3]= I2C_ReadByte();
	Send_ACK();

	readByte[4]= I2C_ReadByte();
	Send_ACK();

	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();

	I2C_Stop();

	//--------------
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;

		H1 = (H1*1000)/1024/1024;

		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];

		T1 = (T1*2000)/1024/1024 - 500;

		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;

		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;

		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("lyy");

	}
	printf("\r\n");
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");
}




uint8_t  Receive_ACK(void)
{
	uint8_t result=0;
	uint8_t cnt=0;

	IIC_SCL = 0;
	SDA_IN(); 
	delay_us(4);

	IIC_SCL = 1;
	delay_us(4);

	while(READ_SDA && (cnt<100))
	{
		cnt++;
	}

	IIC_SCL = 0;
	delay_us(4);

	if(cnt<100)
	{
		result=1;
	}
	return result;
}



void  Send_ACK(void)
{
	SDA_OUT();
	IIC_SCL = 0;
	delay_us(4);

	IIC_SDA = 0;
	delay_us(4);

	IIC_SCL = 1;
	delay_us(4);
	IIC_SCL = 0;
	delay_us(4);

	SDA_IN();
}



void  SendNot_Ack(void)
{
	SDA_OUT();
	IIC_SCL = 0;
	delay_us(4);

	IIC_SDA = 1;
	delay_us(4);

	IIC_SCL = 1;
	delay_us(4);

	IIC_SCL = 0;
	delay_us(4);

	IIC_SDA = 0;
	delay_us(4);
}


void I2C_WriteByte(uint8_t  input)
{
	uint8_t  i;
	SDA_OUT();
	for(i=0; i<8; i++)
	{
		IIC_SCL = 0;
		delay_ms(5);

		if(input & 0x80)
		{
			IIC_SDA = 1;
			//delaymm(10);
		}
		else
		{
			IIC_SDA = 0;
			//delaymm(10);
		}

		IIC_SCL = 1;
		delay_ms(5);

		input = (input<<1);
	}

	IIC_SCL = 0;
	delay_us(4);

	SDA_IN();
	delay_us(4);
}	


uint8_t I2C_ReadByte(void)
{
	uint8_t  resultByte=0;
	uint8_t  i=0, a=0;

	IIC_SCL = 0;
	SDA_IN();
	delay_ms(4);

	for(i=0; i<8; i++)
	{
		IIC_SCL = 1;
		delay_ms(3);

		a=0;
		if(READ_SDA)
		{
			a=1;
		}
		else
		{
			a=0;
		}

		//resultByte = resultByte | a;
		resultByte = (resultByte << 1) | a;

		IIC_SCL = 0;
		delay_ms(3);
	}

	SDA_IN();
	delay_ms(10);

	return   resultByte;
}


void  set_AHT20sendOutData(void)
{
	/* --------------------------
	 * 0xFA 0x06 0x0A temperature(2 Bytes) humility(2Bytes) short Address(2 Bytes)
	 * And Check (1 byte)
	 * -------------------------*/
	AHT20sendOutData[3] = AHT20_OutData[0];
	AHT20sendOutData[4] = AHT20_OutData[1];
	AHT20sendOutData[5] = AHT20_OutData[2];
	AHT20sendOutData[6] = AHT20_OutData[3];

//	AHT20sendOutData[7] = (drf1609.shortAddress >> 8) & 0x00FF;
//	AHT20sendOutData[8] = drf1609.shortAddress  & 0x00FF;

//	AHT20sendOutData[9] = getXY(AHT20sendOutData,10);
}


void  I2C_Start(void)
{
	SDA_OUT();
	IIC_SCL = 1;
	delay_ms(4);

	IIC_SDA = 1;
	delay_ms(4);
	IIC_SDA = 0;
	delay_ms(4);

	IIC_SCL = 0;
	delay_ms(4);
}



void  I2C_Stop(void)
{
	SDA_OUT();
	IIC_SDA = 0;
	delay_ms(4);

	IIC_SCL = 1;
	delay_ms(4);

	IIC_SDA = 1;
	delay_ms(4);
}

2、编译并烧录

通过编译获得hex文件:

 烧录进板

 连接串口结果展示:

三、基于SPI的OLED显示 

1、SPI协议简介:

SPI定义:

SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface) ,即串行外围设
备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC 、 LCD 等设备与 MCU 间,
要求通讯速率较高的场合。

SPI物理层:

SS( Slave Select):从设备选择信号线,常称为片选信号线。
SCK (Serial Clock):时钟信号线,用于通讯数据同步。
MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。
MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。

SPI协议层

SPI 基本通讯过程

MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。 

通讯的起始和停止信号
在图 25-2 中的标号① 处, NSS 信号线由高变低,是 SPI 通讯的起始信号。 NSS 是每个
从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选
中了,开始准备与主机通讯。在图中的标号⑥ 处, NSS 信号由低变高,是 SPI 通讯的停止
信号,表示本次通讯结束,从机的选中状态被取消。
数据有效性

SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。 MOSI 及
MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据
传输时, MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同
样的协定,一般都会采用图 25-2 中的 MSB 先行模式。
CPOL/CPHA 及通讯模式

2、OLED引脚接法

 0.96寸OLED显示屏相关介绍
参考下面链接:

 0.96inch SPI OLED Module - LCD wiki

3、修改代码 

修改test.c中的TEST_MainPage函数中GUI_ShowString,GUI_ShowCHinese的参数 

 获得名字的存储代码,设置字模输出选项

 输入名字:

 再将对应的字模点阵加入到oledfont.h里

主程序:

mian.c

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)
	while(1) 
	{	
		TEST_MainPage();         //主界面显示测试
		
	}
}

4、效果呈现

1、编译烧录进板

编译:

 烧录:

2、结果呈现 

四、 显示温度和湿度

1、主要代码

main.c

#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"
#include "sys.h"
 
#include "oled.h"
#include "gui.h"
#include "test.h"
 
int main(void)
{	
	delay_init();	    	       //延时函数初始化    	  
	uart_init(115200);	 
	IIC_Init();
		  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0); 
	while(1)
	{
		//printf("温度湿度显示");
		read_AHT20_once();
		OLED_Clear(0); 
		delay_ms(1500);
  }
}

 2、编译烧录进板效果呈现

五、滑动显示长字符

1、代码

main.c

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)
	OLED_WR_Byte(0x2E,OLED_CMD);        //关闭滚动
  OLED_WR_Byte(0x27,OLED_CMD);        //水平向左或者右滚动 26/27
  OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
	OLED_WR_Byte(0x00,OLED_CMD);        //起始页 0
	OLED_WR_Byte(0x07,OLED_CMD);        //滚动时间间隔
	OLED_WR_Byte(0x07,OLED_CMD);        //终止页 7
	OLED_WR_Byte(0x00,OLED_CMD);        //虚拟字节
	OLED_WR_Byte(0xFF,OLED_CMD);        //虚拟字节
	TEST_MainPage();
	OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动
	while(1) 
	{	
		
		
	}
}

按照上面第三的步骤修改代码:

 

2、编译烧录进板

 

3、结果呈现 

 六、参考文献

 https://blog.csdn.net/qq_43279579/article/details/111597278 

基于SPI通信方式的OLED显示_不#曾&轻听的博客-CSDN博客

基于STM32的0.96寸OLED显示屏显示数据_Harriet的博客-CSDN博客_基于stm32的oled显示时间

 

版权声明:本文为CSDN博主「cat king_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_48176053/article/details/121474115

目录

一、 学习I2C总线通信协议

二、理解OLED屏显和汉字点阵编码原理

三、总结


一、 学习I2C总线通信协议

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

 协议层:

I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。

1.软件I2C
为了使工程更加有条理,我们把读写EEPROM相关的代码独立分开存储,方便以后移植。在“工程模板”之上新建“bsp_i2c_ee.c”及“bsp_i2c_ee.h”文件,这些文件也可根据您的喜好命名,它们不属于STM32 HAL库的内容,是由我们自己根据应用需要编写的。

编程要点

(1) 配置通讯使用的目标引脚为开漏模式;

(2) 使能I2C外设的时钟;

(3) 配置I2C外设的模式、地址、速率等参数并使能I2C外设;

(4) 编写基本I2C按字节收发的函数;

(5) 编写读写EEPROM存储内容的函数;

(6) 编写测试程序,对读写数据进行校验。

2.硬件I2C

 3.阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)
代码下载看文献第二个博客

AHT20芯片的使用过程,下载好后参考位置

二、理解OLED屏显和汉字点阵编码原理

 
1.显示自己的学号和姓名;
程序请看文献第三个博客

三个主要函数,可以修改输出想要的、

 

看结果

 2.显示AHT20的温度和湿度;
选择上面的两个程序,将第二个工程以下文件放在第一个工程这个文件下,除了sys.c,sys.h,usart.c,usart.h

 加入到工程里面

修改

 

 上面程序代码

void read_AHT20(void)
{
    uint8_t   i;
    for(i=0; i<6; i++)
    {
        readByte[i]=0;
    }
 
    //-------------
    I2C_Start();
 
    I2C_WriteByte(0x71);
    ack_status = Receive_ACK();
    readByte[0]= I2C_ReadByte();
    Send_ACK();
 
    readByte[1]= I2C_ReadByte();
    Send_ACK();
 
    readByte[2]= I2C_ReadByte();
    Send_ACK();
 
    readByte[3]= I2C_ReadByte();
    Send_ACK();
 
    readByte[4]= I2C_ReadByte();
    Send_ACK();
 
    readByte[5]= I2C_ReadByte();
    SendNot_Ack();
    //Send_ACK();
 
    I2C_Stop();
 
    //--------------
    if( (readByte[0] & 0x68) == 0x08 )
    {
        H1 = readByte[1];
        H1 = (H1<<8) | readByte[2];
        H1 = (H1<<8) | readByte[3];
        H1 = H1>>4;
 
        H1 = (H1*1000)/1024/1024;
 
        T1 = readByte[3];
        T1 = T1 & 0x0000000F;
        T1 = (T1<<8) | readByte[4];
        T1 = (T1<<8) | readByte[5];
 
        T1 = (T1*2000)/1024/1024 - 500;
 
        AHT20_OutData[0] = (H1>>8) & 0x000000FF;
        AHT20_OutData[1] = H1 & 0x000000FF;
 
        AHT20_OutData[2] = (T1>>8) & 0x000000FF;
        AHT20_OutData[3] = T1 & 0x000000FF;
    }
    else
    {
        AHT20_OutData[0] = 0xFF;
        AHT20_OutData[1] = 0xFF;
 
        AHT20_OutData[2] = 0xFF;
        AHT20_OutData[3] = 0xFF;
        printf("lyy");
 
    }
    printf("\r\n");
    
    printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
    printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
    printf("\r\n");
    t=T1/10;
    t1=T1%10;
    a=(float)(t+t1*0.1);
    h=H1/10;
    h1=H1%10;
    b=(float)(h+h1*0.1);
    sprintf(strTemp,"%.1f",a);   //调用Sprintf函数把DHT11的温度数据格式化到字符串数组变量strTemp中  
  sprintf(strHumi,"%.1f",b);    //调用Sprintf函数把DHT11的湿度数据格式化到字符串数组变量strHumi中  
    //printf(strTemp);
    //printf("/r/n");
    GUI_ShowCHinese(16,00,16,"温湿度显示",1);
    GUI_ShowCHinese(16,20,16,"温度",1);
    GUI_ShowString(53,20,strTemp,16,1);
    GUI_ShowCHinese(16,38,16,"湿度",1);
    GUI_ShowString(53,38,strHumi,16,1);
    delay_ms(1500);        
    delay_ms(1500);
    delay_ms(1500);
    delay_ms(1500);
    
}
 

 结果

3.上下或左右的滑动显示长字符,比如“Hello,欢迎来到重庆交通大学物联网205实训室!”或者一段歌词或诗词(最好使用硬件刷屏模式)。

在第二个工程的基础上修改

 

 结果

 所有代码,找不到可以参考文献博客

链接: https://pan.baidu.com/s/12T1pvoM6YUXnz_oInsYc9g 提取码: 4597

三、总结

这次试验收获很大,学习了很多新的知识,融会贯通了旧知识,加强了动手能力综合素质。
 

版权声明:本文为CSDN博主「HILYX」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/HILYX/article/details/122378826

一、STM32之基于I2C的温湿度采集(AHT20)

1.I2C协议

详情参考:I2C协议

2.AHT20温湿度采集

程序

主函数:

#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"


int main(void)
{	
	delay_init();     
	uart_init(115200);	//串口通信波特率115200
	IIC_Init();
		while(1)
	{
		printf("温度湿度显示");
		read_AHT20_once();
		delay_ms(1500);
  }
}

模块读取函数:

void  read_AHT20_once(void)
{
	delay_ms(10);

	reset_AHT20();//重置AHT20芯片
	delay_ms(10);

	init_AHT20();//初始化AHT20芯片
	delay_ms(10);

	startMeasure_AHT20();//开始测试AHT20芯片
	delay_ms(80);

	read_AHT20();//读取AHT20采集的到的数据
	delay_ms(5);
}

I2C读取及输出温湿度的函数:

void read_AHT20(void)
{
	uint8_t   i;

	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}
	I2C_Start();//I2C启动

	I2C_WriteByte(0x71);//I2C写数据
	ack_status = Receive_ACK();//收到的应答信息
	readByte[0]= I2C_ReadByte();//I2C读取数据
	Send_ACK();//发送应答信息

	readByte[1]= I2C_ReadByte();
	Send_ACK();

	readByte[2]= I2C_ReadByte();
	Send_ACK();

	readByte[3]= I2C_ReadByte();
	Send_ACK();

	readByte[4]= I2C_ReadByte();
	Send_ACK();

	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();

	I2C_Stop();//I2C停止函数
	//如果读取过程没有问题,就对读到的数据进行相应的处理
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;

		H1 = (H1*1000)/1024/1024;

		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];

		T1 = (T1*2000)/1024/1024 - 500;

		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;

		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;

		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("读取失败");

	}
	printf("\r\n");
	//根据温度和湿度的计算公式,计算后输出结果,通过串口发送到上位机显示
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");
}


接线烧录

(芯片在实验时未拍照)

效果视频

AH20

二、STM32在oled上显示文字滚动

1.了解SPI(串行外设接口)

参考:《【野火®】零死角玩转STM32—F103-MINI》

2.字模

在这里插入图片描述

3.代码

百度网盘,提取码bphx
修改部分代码:
在这里插入图片描述在这里插入图片描述

4.接线烧录

// OLED模块 STM32单片机
// VCC 接 DC 5V/3.3V //OLED屏电源正
// GND 接 GND //OLED屏电源地
//=液晶屏数据线接线====//
//本模块默认数据总线类型为4线制SPI
// OLED模块 STM32单片机
// D1 接 PB15 //OLED屏SPI写信号
//=液晶屏控制线接线====//
// OLED模块 STM32单片机
// CS 接 PB11 //OLED屏片选控制信号
// RES 接 PB12 //OLED屏复位控制信号
// DC 接 PB10 //OLED屏数据/命令选择控制信号
// D0 接 PB13 //OLED屏SPI时钟信号

5.演示

在这里插入图片描述

三、STM32之基于I2C的温湿度采集(AHT20)oled显示

1.代码

完整代码
温度显示代码:

void read_AHT20(void)
{
	uint8_t   i;
	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}

	//-------------
	I2C_Start();

	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	readByte[0]= I2C_ReadByte();
	Send_ACK();

	readByte[1]= I2C_ReadByte();
	Send_ACK();

	readByte[2]= I2C_ReadByte();
	Send_ACK();

	readByte[3]= I2C_ReadByte();
	Send_ACK();

	readByte[4]= I2C_ReadByte();
	Send_ACK();

	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();

	I2C_Stop();

	//--------------
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;

		H1 = (H1*1000)/1024/1024;

		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];

		T1 = (T1*2000)/1024/1024 - 500;

		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;

		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;

		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("lyy");

	}
	/*通过串口显示采集得到的温湿度
	printf("\r\n");
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");*/
	t=T1/10;
	t1=T1%10;
	a=(float)(t+t1*0.1);
	h=H1/10;
	h1=H1%10;
	b=(float)(h+h1*0.1);
	sprintf(strTemp,"%.1f",a);   //调用Sprintf函数把DHT11的温度数据格式化到字符串数组变量strTemp中  
    sprintf(strHumi,"%.1f",b);    //调用Sprintf函数把DHT11的湿度数据格式化到字符串数组变量strHumi中  
	GUI_ShowCHinese(16,00,16,"温湿度显示",1);
	GUI_ShowCHinese(16,20,16,"温度",1);
	GUI_ShowString(53,20,strTemp,16,1);
	GUI_ShowCHinese(16,38,16,"湿度",1);
	GUI_ShowString(53,38,strHumi,16,1);
	delay_ms(1500);		
	delay_ms(1500);
}


点阵汉字:
在这里插入图片描述`
0x00,0x00,0x23,0xF8,0x12,0x08,0x12,0x08,0x83,0xF8,0x42,0x08,0x42,0x08,0x13,0xF8,
0x10,0x00,0x27,0xFC,0xE4,0xA4,0x24,0xA4,0x24,0xA4,0x24,0xA4,0x2F,0xFE,0x00,0x00,/“温”,0/
0x00,0x00,0x27,0xF8,0x14,0x08,0x14,0x08,0x87,0xF8,0x44,0x08,0x44,0x08,0x17,0xF8,
0x11,0x20,0x21,0x20,0xE9,0x24,0x25,0x28,0x23,0x30,0x21,0x20,0x2F,0xFE,0x00,0x00,/“湿”,1/
0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20,
0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E,/“度”,2/
0x00,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,
0x04,0x40,0x44,0x44,0x24,0x44,0x14,0x48,0x14,0x50,0x04,0x40,0xFF,0xFE,0x00,0x00,/“显”,3/
0x00,0x00,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x01,0x00,
0x01,0x00,0x11,0x10,0x11,0x08,0x21,0x04,0x41,0x02,0x81,0x02,0x05,0x00,0x02,0x00,/“示”,4/

`
在这里插入图片描述main函数:

#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"
#include "sys.h"

#include "oled.h"
#include "gui.h"
#include "test.h"

int main(void)
{	
	delay_init();	    	       //延时函数初始化    	  
	uart_init(115200);	 
	IIC_Init();
		  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0); 
	while(1)
	{
		//printf("温度湿度显示");
		read_AHT20_once();
		OLED_Clear(0); 
		delay_ms(1500);
  }
}


2.接线烧录演示

温度显示oled

四、滚送演示

1.点阵字图

在这里插入图片描述`
0x00,0x80,0x00,0x80,0xFC,0x80,0x04,0xFC,0x05,0x04,0x49,0x08,0x2A,0x40,0x14,0x40,
0x10,0x40,0x28,0xA0,0x24,0xA0,0x45,0x10,0x81,0x10,0x02,0x08,0x04,0x04,0x08,0x02,/“欢”,0/
0x00,0x00,0x20,0x80,0x13,0x3C,0x12,0x24,0x02,0x24,0x02,0x24,0xF2,0x24,0x12,0x24,
0x12,0x24,0x12,0xB4,0x13,0x28,0x12,0x20,0x10,0x20,0x28,0x20,0x47,0xFE,0x00,0x00,/“迎”,1/
0x01,0x00,0x01,0x00,0x01,0x00,0x7F,0xFC,0x01,0x00,0x11,0x10,0x09,0x10,0x09,0x20,
0xFF,0xFE,0x03,0x80,0x05,0x40,0x09,0x20,0x31,0x18,0xC1,0x06,0x01,0x00,0x01,0x00,/“来”,2/
0x00,0x04,0xFF,0x84,0x08,0x04,0x10,0x24,0x22,0x24,0x41,0x24,0xFF,0xA4,0x08,0xA4,
0x08,0x24,0x08,0x24,0x7F,0x24,0x08,0x24,0x08,0x04,0x0F,0x84,0xF8,0x14,0x40,0x08,/“到”,3/
0x20,0x50,0x20,0x48,0x2F,0xFE,0x20,0x40,0x27,0xFC,0xFC,0x44,0x27,0xFC,0x24,0x44,
0x27,0xFC,0x24,0x44,0x20,0x08,0x2F,0xFE,0x22,0x08,0x21,0x08,0x21,0x28,0x20,0x10,/“博”,4/
0x02,0x00,0x01,0x00,0x7F,0xFE,0x40,0x02,0x88,0x04,0x0F,0xF0,0x10,0x20,0x2C,0x40,
0x03,0x80,0x1C,0x70,0xE0,0x0E,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,/“客”,5/

`
完整代码

2.接线烧录演示

在这里插入图片描述

五、总结

通过本次实验,增强了我的动手能力

六、参考文献

温湿度模块实验

版权声明:本文为CSDN博主「瑾年OvO」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_58247857/article/details/121567958

实验一

实验目的

学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。具体任务:

1)解释什么是“软件I2C”和“硬件I2C”? (阅读野火配套教材的第23章“I2C–读写EEPROM”原理章节)

2)阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)。

I2C简介

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

2.I2C 协议的物理层和协议层
①物理层
I2C是一个支持设备的总线。可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机。对于I2C 总线,只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。
I2C 通讯设备常用连接方式(引用野火资料中的图)在这里插入图片描述

②协议层

主要是定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等。

通讯的起始和停止信号
在这里插入图片描述

数据有效性
在这里插入图片描述

从图中可以看出I2C在通讯的时候,只有在SCL处于高电平时,SDA的数据传输才是有效的。SDA 信号线是用于传输数据,SCL 信号线是保证数据同步。
响应在这里插入图片描述

当SDA传输数据后,接收方对接受到的数据进行一个应答。如果希望继续进行传输数据,则回应应答信号(低电平),否则回应非应答信号(高电平)。

3.I2C的两种方式——硬件I2C和软件I2C
①硬件I2C
直接利用 STM32 芯片中的硬件 I2C 外设。

硬件I2C的使用 只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置
1,此时外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。

②软件I2C
直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。

软件I2C的使用 需要在控制产生 I2C 的起始信号时,控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的
GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。

③两者的差别
硬件 I2C 直接使用外设来控制引脚,可以减轻 CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为 SCL 和 SDA,软件模拟 I2C 则可以使用任意 GPIO 引脚,相对比较灵活。对于硬件I2C用法比较复杂,软件I2C的流程更清楚一些。如果要详细了解I2C的协议,使用软件I2C可能更好的理解这个过程。在使用I2C过程,硬件I2C可能通信更加快,更加稳定。

实验过程

main.c


int main(void)
{	
	delay_init();       
	uart_init(115200);	 
	IIC_Init();
		while(1)
	{
		printf("温度湿度显示");
		read_AHT20_once();
		delay_ms(1500);
  }
}

AHT20芯片的使用

void  read_AHT20_once(void)
{
	delay_ms(10);

	reset_AHT20();//重置AHT20芯片
	delay_ms(10);

	init_AHT20();//初始化AHT20芯片
	delay_ms(10);

	startMeasure_AHT20();//开始测试AHT20芯片
	delay_ms(80);

	read_AHT20();//读取AHT20采集的到的数据
	delay_ms(5);
}

AHT20芯片读取数据

void read_AHT20(void)
{
	uint8_t   i;

	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}
	I2C_Start();//I2C启动

	I2C_WriteByte(0x71);//I2C写数据
	ack_status = Receive_ACK();//收到的应答信息
	readByte[0]= I2C_ReadByte();//I2C读取数据
	Send_ACK();//发送应答信息

	readByte[1]= I2C_ReadByte();
	Send_ACK();

	readByte[2]= I2C_ReadByte();
	Send_ACK();

	readByte[3]= I2C_ReadByte();
	Send_ACK();

	readByte[4]= I2C_ReadByte();
	Send_ACK();

	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();

	I2C_Stop();//I2C停止函数
	//判断读取到的第一个字节是不是0x08,0x08是该芯片读取流程中规定的,如果读取过程没有问题,就对读到的数据进行相应的处理
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;

		H1 = (H1*1000)/1024/1024;

		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];

		T1 = (T1*2000)/1024/1024 - 500;

		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;

		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;

		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("读取失败!!!");

	}
	printf("\r\n");
	//根据AHT20芯片中,温度和湿度的计算公式,得到最终的结果,通过串口显示
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");
}

然后注意连接板子

SCL连接PB6,SDA连接PB7
vcc和gnd分别接电源和地
然后打开野火
得到如下结果
请添加图片描述
可以看到在发送温度湿度
完成

实验二

实验目的

理解OLED屏显和汉字点阵编码原理,使用STM32F103的SPI或IIC接口实现以下功能:

  1. 显示自己的学号和姓名;

  2. 显示AHT20的温度和湿度;

  3. 上下或左右的滑动显示长字符,比如“Hello,欢迎来到重庆交通大学物联网205实训室!”或者一段歌词或诗词(最好使用硬件刷屏模式)。

SPI简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是由 Motorola 公司提出的一种高速的,全双工,同步的通信总线,被广泛地使用在 ADC、LCD 等设备与 MCU 间要求通讯速率较高的场合。SPI总线系统可直接与各个厂家生产的多种标准外围器件连接,该接口一般使用4条线:串行时钟线(SCK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOST和低电平有效的从机选择线C/S(有的SPI接口芯片带有中断信号线INT或INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。
在这里插入图片描述

1.SPI时序

上图中的时序只是 SPI 其中一种通讯模式,SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性 CPOL”和“时钟相位 CPHA”的概念。
时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)。CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。
时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。

2.SPI工作模式
根据 CPOL 及 CPHA 的不同状态,SPI 分成了四种模式,见下图,主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式 0”与“模式 3”。

在这里插入图片描述

3.SPI 优缺点
SPI 优点

支持全双工通信 通信简单 数据传输速率块

SPI 缺点

没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据 可靠性上有一定的缺陷。

OLED简介

1.OLED原理
OLED(OrganicLight-Emitting Diode),又称为有机电激光显示、有机发光半导体(OrganicElectroluminesence Display,OLED)。OLED属于一种电流型的有机发光器件,是通过载流子的注入和复合而致发光的现象,发光强度与注入的电流成正比。OLED在电场的作用下,阳极产生的空穴和阴极产生的电子就会发生移动,分别向空穴传输层和电子传输层注入,迁移到发光层。当二者在发光层相遇时,产生能量激子,从而激发发光分子最终产生可见光。

下面是7针SPI通信OLED的图片:
在这里插入图片描述

2.点阵编码原理与显示
汉字点阵编码

在汉字的点阵字库中,每个字节的每个位都代表一个汉字的一个点,每个汉字都是由一个矩形的点阵组成,0 代表没有点,1 代表有点,将 0 和 1
分别用不同颜色画出,就形成了一个汉字,常用的点阵矩阵有 1212, 1414, 16*16 三 种字库。
字库根据字节所表示点的不同有分为横向矩阵和纵向矩阵,目前多数的字库都是横向矩阵的存储方式(用得最多的应该是早期 UCDOS
字库),纵向矩阵一 般是因为有某些液晶是采用纵向扫描显示法,为了提高显示速度,于是便把字库 矩阵做成纵向,省得在显示时还要做矩阵转换。

OLED点阵显示

点阵屏像素按128列X64行组织,每一行128个像素单元的阴极是连接在一起,作为公共极(COM),每一列64个像素单元的阳极也连接在一起,作为一段(SEG)。行列交叉点上的LED就是一个显示单元,即一个像素。要点亮一个像素,只要在该像素所在列电极上加上正电压、行电极接地。同样,要驱动一整行图像,就需要同时把128列信号加载到列电极上,把该行行电极接地。该行显示时,其他63行均不能显示,其行电极应为高电平或悬空。
可见,整屏的显示,只能分时扫描进行,一行一行的显示,每次显示一行。行驱依次产生低电平扫描各行,列驱动读取显示数据依次加载到列电极上。扫描一行的时间称为行周期,完成一次全屏扫描,就叫做一帧。一般帧频大于60,人眼观察不到逐行显示。每行扫描显示用时叫占空比,占空比小,为达到相同的显示亮度,驱动电流就大。SSD1306段驱动最大电流为100uA,当整行128个像素全部点亮时,行电极就要流过12.8mA的电流。

实验过程

显示自己的学号和姓名:
主要函数:
main.c

int main(void)
{	
	delay_init();	    	       //延时函数初始化	  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0);             //清屏(全黑)
	while(1) 
	{	
		TEST_MainPage();         //主界面显示测试
		OLED_Clear(0); 
		/*Test_Color();            //刷屏测试
		OLED_Clear(0); 
		Test_Rectangular();      //矩形绘制测试
		OLED_Clear(0); 
		Test_Circle();           //圆形绘制测试
		OLED_Clear(0); 
		Test_Triangle();         //三角形绘制测试
		OLED_Clear(0);  
		
		TEST_English();          //英文显示测试
		OLED_Clear(0);
		TEST_Number_Character(); //数字和符号显示测试
		OLED_Clear(0); 
		
		TEST_Chinese();          //中文显示测试
		OLED_Clear(0); 
		TEST_BMP();              //BMP单色图片显示测试
		OLED_Clear(0); 
		TEST_Menu1();            //菜单1显示测试
		OLED_Clear(0); 
		TEST_Menu2();            //菜单2显示测试
		OLED_Clear(0); */
	}
}

本次实验只需要调用mainpage函数

在test.c文件中修改函数
TEST_MainPage():

void TEST_MainPage(void)
{	
	GUI_ShowString(28,0,"CCChenn",16,1);
	GUI_ShowCHinese(28,20,16,"曹元辰",1);
	GUI_ShowString(4,48,"631907030701",16,1);
	
	delay_ms(1500);		
	delay_ms(1500);
}

在oledfond.,h文件中加入需要显示的16*16的字模

const typFNT_GB16 cfont16[] = 
{
"曹",0x04,0x40,0x04,0x40,0xFF,0xFE,0x04,0x40,0x3F,0xF8,0x24,0x48,0x3F,0xF8,0x24,0x48,
0x3F,0xF8,0x00,0x00,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x10,0x10,/*"曹",0*/
"元",0x00,0x00,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x04,0x40,0x04,0x40,
0x04,0x40,0x04,0x40,0x08,0x40,0x08,0x42,0x10,0x42,0x20,0x42,0x40,0x3E,0x80,0x00,/*"元",1*/
"辰",0x00,0x00,0x3F,0xFC,0x20,0x00,0x20,0x00,0x2F,0xF8,0x20,0x00,0x20,0x00,0x3F,0xFC,
0x24,0x80,0x24,0x88,0x24,0x50,0x24,0x20,0x44,0x10,0x45,0x08,0x86,0x06,0x04,0x00,/*"辰",2*/

};

其中字模需要通过字模提取软件提取
修改字模选项如下
请添加图片描述
然后将字模复制到刚刚说的文件中,就可以了
在这里插入图片描述
OLED 主要函数

//OLED控制用函数
void OLED_WR_Byte(unsigned dat,unsigned cmd);     							   		    
void OLED_Display_On(void);
void OLED_Display_Off(void);
void OLED_Set_Pos(unsigned char x, unsigned char y);
void OLED_Reset(void);
void OLED_Init_GPIO(void);	   							   		    
void OLED_Init(void);
void OLED_Set_Pixel(unsigned char x, unsigned char y,unsigned char color);
void OLED_Display(void);
void OLED_Clear(unsigned dat); 

//OLED滚动显示
void OLED_Display_scroll(void); 

连线如下
在这里插入图片描述
结果如下
请添加图片描述
(注意,我这里看到会有闪屏现象是因为手机录制的帧率和OLED 屏幕显示的帧率相同有一个时延。)

显示AHT20的温度和湿度:

主要函数
这里是显示温度湿度的函数

void read_AHT20(void)
{
	uint8_t   i;
	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}

	//-------------
	I2C_Start();

	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	readByte[0]= I2C_ReadByte();
	Send_ACK();

	readByte[1]= I2C_ReadByte();
	Send_ACK();

	readByte[2]= I2C_ReadByte();
	Send_ACK();

	readByte[3]= I2C_ReadByte();
	Send_ACK();

	readByte[4]= I2C_ReadByte();
	Send_ACK();

	readByte[5]= I2C_ReadByte();
	SendNot_Ack();
	//Send_ACK();

	I2C_Stop();

	//--------------
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		H1 = H1>>4;

		H1 = (H1*1000)/1024/1024;

		T1 = readByte[3];
		T1 = T1 & 0x0000000F;
		T1 = (T1<<8) | readByte[4];
		T1 = (T1<<8) | readByte[5];

		T1 = (T1*2000)/1024/1024 - 500;

		AHT20_OutData[0] = (H1>>8) & 0x000000FF;
		AHT20_OutData[1] = H1 & 0x000000FF;

		AHT20_OutData[2] = (T1>>8) & 0x000000FF;
		AHT20_OutData[3] = T1 & 0x000000FF;
	}
	else
	{
		AHT20_OutData[0] = 0xFF;
		AHT20_OutData[1] = 0xFF;

		AHT20_OutData[2] = 0xFF;
		AHT20_OutData[3] = 0xFF;
		printf("lyy");

	}
	printf("\r\n");
	
	printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
	printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
	printf("\r\n");
	t=T1/10;
	t1=T1%10;
	a=(float)(t+t1*0.1);
	h=H1/10;
	h1=H1%10;
	b=(float)(h+h1*0.1);
	sprintf(strTemp,"%.1f",a);   //调用Sprintf函数把DHT11的温度数据格式化到字符串数组变量strTemp中  
  sprintf(strHumi,"%.1f",b);    //调用Sprintf函数把DHT11的湿度数据格式化到字符串数组变量strHumi中  
	//printf(strTemp);
	//printf("/r/n");
	
	GUI_DrawLine(0, 10, WIDTH-1, 10,1);
	GUI_DrawLine(WIDTH/2-1,11,WIDTH/2-1,HEIGHT-1,1);
	GUI_DrawLine(WIDTH/2-1,10+(HEIGHT-10)/2-1,WIDTH-1,10+(HEIGHT-10)/2-1,1);

	GUI_ShowString(0,1,"2021-11-20",8,1);
	GUI_ShowString(74,1,"Saturday",8,1);
	GUI_ShowString(14,HEIGHT-1-10,"Cloudy",8,1);
	
	GUI_ShowString(WIDTH/2-1+2,13,"TEMP",8,1);
	GUI_DrawCircle(WIDTH-1-19, 25, 1,2);
	GUI_ShowString(WIDTH-1-14,20,"C",16,1);
	GUI_ShowString(WIDTH/2-1+9,20,strTemp,16,1);
	GUI_ShowString(WIDTH/2-1+2,39,"Humid",8,1);
	GUI_ShowString(WIDTH/2-1+5,46,strHumi,16,1);
	delay_ms(1000);		
	delay_ms(1000);	
}

main函数

int main(void)
{	
	delay_init();	    	       //延时函数初始化    	  
	uart_init(115200);	 
	IIC_Init();
		  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0); 
	while(1)
	{
		//printf("温度湿度显示");
		read_AHT20_once();
		OLED_Clear(0); 
		delay_ms(1);
  }
}

结果如下请添加图片描述

上下或左右的滑动显示长字符:
main函数

int main(void)
{	
	delay_init();	    	       //延时函数初始化    	  
	uart_init(115200);	 
	IIC_Init();
		  
	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
	OLED_Init();			         //初始化OLED  
	OLED_Clear(0); 
	while(1)
	{
		TEST_MainPage();
		OLED_Display_scroll();
  }
}

在oled.c文件中加入以下函数

void OLED_Display_scroll(void)
{
    OLED_WR_Byte(0x2e,OLED_CMD);//关滚动
    OLED_WR_Byte(0x2a,OLED_CMD);//29向右,2a向左(垂直水平滚动)
    OLED_WR_Byte(0x00,OLED_CMD);//A:空字节
    OLED_WR_Byte(0x00,OLED_CMD);//B:水平起始页
    OLED_WR_Byte(0x00,OLED_CMD);//C:水平滚动速度
    OLED_WR_Byte(0x07,OLED_CMD);//D:水平结束页
    OLED_WR_Byte(0x01,OLED_CMD);//E:每次垂直滚动位移
	OLED_WR_Byte(0x2f,OLED_CMD);//开滚动
}

在oled.h文件中加入该函数的声明
然后去oledfond.h文件中将需要用的字模放进去

运行结果
请添加图片描述

参考文献

https://blog.csdn.net/qq_43279579/article/details/111597278
https://blog.csdn.net/qq_45237293/article/details/111712565
https://blog.csdn.net/qq_43279579/article/details/111414037

版权声明:本文为CSDN博主「CCChenn mi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_47366551/article/details/121440846

目录

一、 学习I2C总线通信协议

二、理解OLED屏显和汉字点阵编码原理

三、总结


一、 学习I2C总线通信协议

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

 协议层:

I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。

1.软件I2C
为了使工程更加有条理,我们把读写EEPROM相关的代码独立分开存储,方便以后移植。在“工程模板”之上新建“bsp_i2c_ee.c”及“bsp_i2c_ee.h”文件,这些文件也可根据您的喜好命名,它们不属于STM32 HAL库的内容,是由我们自己根据应用需要编写的。

编程要点

(1) 配置通讯使用的目标引脚为开漏模式;

(2) 使能I2C外设的时钟;

(3) 配置I2C外设的模式、地址、速率等参数并使能I2C外设;

(4) 编写基本I2C按字节收发的函数;

(5) 编写读写EEPROM存储内容的函数;

(6) 编写测试程序,对读写数据进行校验。

2.硬件I2C

 3.阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)
代码下载看文献第二个博客

AHT20芯片的使用过程,下载好后参考位置

二、理解OLED屏显和汉字点阵编码原理

 
1.显示自己的学号和姓名;
程序请看文献第三个博客

三个主要函数,可以修改输出想要的、

 

看结果

 2.显示AHT20的温度和湿度;
选择上面的两个程序,将第二个工程以下文件放在第一个工程这个文件下,除了sys.c,sys.h,usart.c,usart.h

 加入到工程里面

修改

 

 上面程序代码

void read_AHT20(void)
{
    uint8_t   i;
    for(i=0; i<6; i++)
    {
        readByte[i]=0;
    }
 
    //-------------
    I2C_Start();
 
    I2C_WriteByte(0x71);
    ack_status = Receive_ACK();
    readByte[0]= I2C_ReadByte();
    Send_ACK();
 
    readByte[1]= I2C_ReadByte();
    Send_ACK();
 
    readByte[2]= I2C_ReadByte();
    Send_ACK();
 
    readByte[3]= I2C_ReadByte();
    Send_ACK();
 
    readByte[4]= I2C_ReadByte();
    Send_ACK();
 
    readByte[5]= I2C_ReadByte();
    SendNot_Ack();
    //Send_ACK();
 
    I2C_Stop();
 
    //--------------
    if( (readByte[0] & 0x68) == 0x08 )
    {
        H1 = readByte[1];
        H1 = (H1<<8) | readByte[2];
        H1 = (H1<<8) | readByte[3];
        H1 = H1>>4;
 
        H1 = (H1*1000)/1024/1024;
 
        T1 = readByte[3];
        T1 = T1 & 0x0000000F;
        T1 = (T1<<8) | readByte[4];
        T1 = (T1<<8) | readByte[5];
 
        T1 = (T1*2000)/1024/1024 - 500;
 
        AHT20_OutData[0] = (H1>>8) & 0x000000FF;
        AHT20_OutData[1] = H1 & 0x000000FF;
 
        AHT20_OutData[2] = (T1>>8) & 0x000000FF;
        AHT20_OutData[3] = T1 & 0x000000FF;
    }
    else
    {
        AHT20_OutData[0] = 0xFF;
        AHT20_OutData[1] = 0xFF;
 
        AHT20_OutData[2] = 0xFF;
        AHT20_OutData[3] = 0xFF;
        printf("lyy");
 
    }
    printf("\r\n");
    
    printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
    printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
    printf("\r\n");
    t=T1/10;
    t1=T1%10;
    a=(float)(t+t1*0.1);
    h=H1/10;
    h1=H1%10;
    b=(float)(h+h1*0.1);
    sprintf(strTemp,"%.1f",a);   //调用Sprintf函数把DHT11的温度数据格式化到字符串数组变量strTemp中  
  sprintf(strHumi,"%.1f",b);    //调用Sprintf函数把DHT11的湿度数据格式化到字符串数组变量strHumi中  
    //printf(strTemp);
    //printf("/r/n");
    GUI_ShowCHinese(16,00,16,"温湿度显示",1);
    GUI_ShowCHinese(16,20,16,"温度",1);
    GUI_ShowString(53,20,strTemp,16,1);
    GUI_ShowCHinese(16,38,16,"湿度",1);
    GUI_ShowString(53,38,strHumi,16,1);
    delay_ms(1500);        
    delay_ms(1500);
    delay_ms(1500);
    delay_ms(1500);
    
}
 

 结果

3.上下或左右的滑动显示长字符,比如“Hello,欢迎来到重庆交通大学物联网205实训室!”或者一段歌词或诗词(最好使用硬件刷屏模式)。

在第二个工程的基础上修改

 

 结果

 所有代码,找不到可以参考文献博客

链接: https://pan.baidu.com/s/12T1pvoM6YUXnz_oInsYc9g 提取码: 4597

三、总结

这次试验收获很大,学习了很多新的知识,融会贯通了旧知识,加强了动手能力综合素质。
 

版权声明:本文为CSDN博主「HILYX」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/HILYX/article/details/122378826

生成海报
点赞 0

HILYX

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

USB转TTL给ESP8266-12F芯片下载程序

物料准备: 最近在写ESP8266的程序,想着自己动手做一块电路板,于是学习了一下怎么给一块ESP8266-12F芯片烧录程序,作为新手小白,过程中遇到了一些问题&#xff0

基于STM32的智能数据采集系统

基于STM32的智能数据采集系统 介绍 由STM32C8T6作为主控芯片,控制温湿度传感器与光照强度传感器来检测温湿度值和光照强度值,取得的数据经过处理后可在0.96寸OLED显示屏上实时显示,还可