IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基于I2C/SPI总线的温湿度采集与OLED显示 -> 正文阅读

[嵌入式]基于I2C/SPI总线的温湿度采集与OLED显示

一、AHT20实现温湿度的数据采集

1、I2C协议层简述
(1)I2C协议的通讯过程
在这里插图片描述
这些图表示的是主机和从机通讯时,SDA 线的数据包序列。
其中 S 表示由主机的 I2C 接口产生的传输起始信号(S),这时连接到 I2C 总线上的所有
从机都会接收到这个信号。
起始信号产生后,所有从机就开始等待主机紧接下来 广播 的从机地址信号
(SLAVE_ADDRESS)。在 I2C 总线上,每个设备的地址都是唯一的,当主机广播的地址与
某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。
根据 I2C 协议,这个从机地址可以是 7 位或 10 位。
在地址位之后,是传输方向的选择位,该位为 0 时,表示后面的数据传输方向是由主
机传输至从机,即主机向从机写数据。该位为 1 时,则相反,即主机由从机读数据。
从机接收到匹配的地址后,主机或从机会返回一个应答(ACK)或非应答(NACK)信号,
只有接收到应答信号后,主机才能继续发送或接收数据。
(2)通讯的起始和停止信号
起始(S)和停止§信号是两种特殊的状态,见图 24-5。当 SCL 线是高电
平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。当 SCL 是高电平时 SDA
线由低电平向高电平切换,表示通讯的停止。起始和停止信号一般由主机产生。
在这里插入1.2图片描述
(3)数据有效性
I2C 使用 SDA 信号线来传输数据,使用 SCL信号线进行数据同步。见图 24-6。SDA 数
据线在 SCL的每个时钟周期传输一位数据。传输时,SCL为高电平的时候 SDA表示的数据
有效,即此时的 SDA为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL为低
电平时,SDA的数据无效,一般在这个时候 SDA进行电平切换,为下一次表示数据做好准
备。
在这里插入图片描述
每次数据传输都以字节为单位,每次传输的字节数不受限制。
(4)地址及数据方向
I2C 总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA 信号线发送设备地址(SLAVE_ADDRESS)来查找从机。I2C 协议规定设备地址可以是 7 位或 10 位,实际中 7 位的地址应用比较广泛。紧跟设备地址的一个数据位用来表示数据传输方向,它是数据方向位(R/W——),第 8 位或第 11 位。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
在这里插入图片描述
读数据方向时,主机会释放对 SDA 信号线的控制,由从机控制 SDA 信号线,主机接
收信号,写数据方向时,SDA 由主机控制,从机接收信号。
(5)响应
2C 的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种
信号。作为数据接收端时,当设备(无论主从机)接收到 I2C 传输的一个字节数据或地址后,
若希望对方继续发送数据,则需要向对方发送“应答(ACK)”信号,发送方会继续发送下
一个数据;若接收端希望结束数据传输,则向对方发送“非应答(NACK)”信号,发送方接
收到该信号后会产生一个停止信号,结束信号传输。
在这里插入图片描述
传输时主机产生时钟,在第 9 个时钟时,数据发送端会释放 SDA 的控制权,由数据接
收端控制 SDA,若 SDA 为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。

2、什么是软件I2C和硬件I2C
如果我们直接控制 STM32的两个 GPIO 引脚,分别用作 SCL及 SDA,按照上述信号的
时序要求,直接像控制 LED 灯那样控制引脚的输出(若是接收数据时则读取 SDA 电平),就
可以实现 I2C 通讯。同样,假如我们按照 USART 的要求去控制引脚,也能实现 USART 通
讯。所以只要遵守协议,就是标准的通讯,不管您如何实现它,不管是 ST 生产的控制器还
是 ATMEL 生产的存储器, 都能按通讯标准交互。

  • 软件I2C
    直接控制 GPIO 引脚电平产生通讯时序时,需要由 CPU 控制每个时刻的引脚状态的“软件模拟协议”方式

  • 硬件I2C
    STM32 的 I2C 片上外设专门负责实现 I2C 通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。
    3、AHT20实现温湿度数据的采集
    (1)关于代码
    编程代码的下载链接:完整代码
    具体代码添加步骤参考博客https://blog.csdn.net/hhhhhh277523/article/details/111397514
    (2)主要代码

  • 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");
}

(3)编译生成hex文件,烧录
在这里插入图片描述

(4)打开串口调试助手,查看温湿度数据采集结果

  • 打开串口调试助手时的温湿度
    在这里插入图片描述

  • 对芯片哈一口气后显示的温湿度
    在这里插入图片描述
    可以对比看到温度和湿度均升高

二、 基于SPI总线的OLED显示

1、SPI协议简介
与 I2C 的类似,SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。这里简单介绍它的通讯时序以及模式。
(1)SPI通讯时序
在这里插入图片描述这是一个主机的通讯时序。NSS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信
号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 NSS 为低
电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据
(2)CPOL/CPHA及通讯模式
上面讲述的图 25-2 中的时序只是 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 的“偶数边沿”采样
(3)SPI四种模式
由 CPOL 及 CPHA 的不同状态,SPI 分成了四种模式(见下表),主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式 0”与“模式 3”。
SPI的四种模式
(4)SYN32的SPI架构
STM32 的 SPI 外设可用作通讯的主机及从机,支持最高的 SCK 时钟频率为 fpclk/2 (STM32F103 型号的芯片默认 fpclk1为 72MHz,fpclk2为 36MHz),完全支持 SPI 协议的 4 种模式,数据帧长度可设置为 8 位或 16 位,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工(前面小节说明的都是这种模式)、双线单向以及单线模式。其中双线单向模式可以同时使用 MOSI 及 MISO 数据线向一个方向传输数据,可以加快一倍的传输速度。而单线模式则可以减少硬件接线,当然这样速率会受到影响。我们只讲解双线全双工模式。
STM32的SPI架构剖析
在这里插入图片描述
SPI通讯引脚
在这里插入图片描述
2、OLED屏显
(1)简介
LED 点阵彩色显示器的单个像素点内
包含红绿蓝三色 LED灯,显示原理类似我们实验板上的 LED彩灯,通过控制红绿蓝颜色的强度进行混色,实现全彩颜色输出,多个像素点构成一个屏幕。OLED 显示器与 LED 点阵彩色显示器的原理类似,但由于它采用的像素单元是“有机发光二极管”(Organic Light Emitting Diode),所以像素密度比普通 LED 点阵显示器高得多。
OLED的显存情况我们可以理解为:水平方向分布了128个像素点,垂直方向分布了64个像素点(如图一所示)。而驱动芯片在点亮像素点的时候,是以8个像素点为单位的。官方的例程推荐的是垂直扫描的方式,也就是先画垂直方向的8个像素点(如下图二所示),所以我们在画点的时候Y的取值为0-7,X的取值为0-127.

//存放格式如下.
//[0]0 1 2 3 ... 127    
//[1]0 1 2 3 ... 127    
//[2]0 1 2 3 ... 127    
//[3]0 1 2 3 ... 127    
//[4]0 1 2 3 ... 127    
//[5]0 1 2 3 ... 127    
//[6]0 1 2 3 ... 127    
//[7]0 1 2 3 ... 127 

图一

图二
(2)OLED汉字取模显示
生成汉字字模后进行画点,以列行式字模为例,先画Y方向的八个点,再画X方向的128个点。

/***************************************************************
X:X坐标 Y:Y方向的页数  no:取模的汉字的序号
你(0) 好(1)
{0x00,0x80,0x60,0xF8,0x07,0x40,0x20,0x18,0x0F,0x08,0xC8,0x08,0x08,0x28,0x18,0x00},
{0x01,0x00,0x00,0xFF,0x00,0x10,0x0C,0x03,0x40,0x80,0x7F,0x00,0x01,0x06,0x18,0x00},/*"你",0*/
 
{0x10,0x10,0xF0,0x1F,0x10,0xF0,0x00,0x80,0x82,0x82,0xE2,0x92,0x8A,0x86,0x80,0x00},
{0x40,0x22,0x15,0x08,0x16,0x61,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00},/*"好",1*/
 
****************************************************************/
void tt_Oled_ShowChinese(unsigned char x,unsigned char y,unsigned char no)
{      			    
    UINT8 t;
 
    tt_Oled_Set_Pos(x,y);	
    for(t=0;t<16;t++){
        tt_Oled_WR_Byte(dis_word[2*no][t],OLED_DATA);
    }	
    tt_Oled_Set_Pos(x,y+1);
    for(t=0;t<16;t++){	
        tt_Oled_WR_Byte(dis_word[2*no+1][t],OLED_DATA);
    }					
}

汉字大小是1616的,所以会占两页,按照上面的分析,先画的是第一页(168个点),对应tt_Oled_WR_Byte(dis_word[2no][t],OLED_DATA);,接着画第二页(168个点))tt_Oled_WR_Byte(dis_word[2*no][t],OLED_DATA);

3、STM32的SPI或IIC接口显示AHT20的温度和湿度
(1)下载显示工程代码
OLED显示代码链接
在这里插入图片描述

  • 温湿度显示代码
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);
}

  • 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
    在这里插入图片描述

  • AHT20
    在这里插入图片描述

  • USB串口
    在这里插入图片描述
    (3)打开前面的显示代码,编译生成hex文件,烧录
    在这里插入图片描述
    (4)结果显示
    在这里插入图片描述

4、STM32的SPI或IIC接口显示学号和姓名
(1)打开汉字取模软件生成字模
网盘下载链接(提取码jdzg)

  • 相关设置
    在这里插入图片描述
  • 输入文字生成字模
    在这里插入图片描述

(2)将字模编码复制添加到代码oledfont.h中的16*16部分
在这里插入图片描述

  • 显示代码
void TEST_Chinese(void)
{	
	GUI_ShowString(16,0,"631907030706",8,1);//学号
	GUI_ShowCHinese(16,20,16,"卡布布卡",1);//姓名
	delay_ms(1000);
	OLED_Clear(0);	
}
  • 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_Chinese();
		OLED_Clear(0); 
		delay_ms(1500);
  }
}

(3)编译生成hex文件,烧录并观察结果

卡布布卡

5、上下或左右的滑动显示长字符
(1)打开字模软件,生成字模
在这里插入图片描述

(2)添加字模编码到代码
在这里插入图片描述

  • 显示代码
void TEST_MainPage(void)
{	
	GUI_ShowCHinese(10,20,16,"卡卡欢迎您!",1);
	delay_ms(1500);		
	delay_ms(1500);
}
  • 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)
	{
 		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);//开滚动
		TEST_MainPage();//显示的内容
		OLED_Clear(0); 
		delay_ms(1500);
  }
}

(3)编译烧录,观察结果

卡卡欢迎您

三、总结

万事开头难,一开始还不能理解如何让字符显示在OLED显示屏上,后面参考了几篇学长学姐和其他博主的博客明白了一系列步骤的过程,在探究过程中分析代码的实现性,理解中文点阵字库的编码取模原理。在完成OLED显示的步骤中,逐步解决字符显现不完全、数字部分不显示的问题。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:30:12  更:2021-11-22 12:30:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/8 4:07:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码