一、基于I2C接口的温湿度采集(AHT20)
1.I2C
(1)什么是I2C
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
- 主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。
名称 | 速度 |
---|
标准模式 | 100 Kbps | 快速模式 | 400 Kbps | 高速模式 | 3.4 Mbps | 超快速模式 | 5 Mbps |
(2)I2C协议
为了方便介绍,我们将I2C协议分为物理层和协议层,这里我们只介绍协议层的内容
- I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节
- I2C协议规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。起始和结束信号总是由主设备产生(意味着从设备不可以主动通信?所有的通信都是主设备发起的,主可以发出询问的command,然后等待从设备的通信)。
- 起始和结束信号产生条件:总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。
(3)工作原理
SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路为开漏输出。需通过上拉电阻接电源VCC。当总线空闲时,两根线都是高电平,连接总线的外同器件都是CMOS器件,输出级也是开漏电路。在总线上消耗的电流很小,因此,总线上扩展的器件数量主要由电容负载来决定,因为每个器件的总线接口都有一定的等效电容。而线路中电容会影响总线传输速度。当电容过大时,有可能造成传输错误。所以,其负载能力为400pF,因此可以估算出总线允许长度和所接器件数量。
名称 | 简介 |
---|
串行数据线(SDA) | 数据线,D代表Data也就是数据,就是用来传输数据的 | 串行时钟线(SCL) | 时钟线,C代表Clock也就是时钟,也就是控制数据发送的时序的 |
(4)硬件I2C和软件I2C
名称 | 简介 | 使用方法 |
---|
硬件I2C | 硬件I2C是直接调用内部寄存器进行配置,直接利用 STM32 芯片中的硬件 I2C 外设。 | 只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置 1,此时外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。 | 软件I2C | 直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。 | 需要在控制产生 I2C 的起始信号时,控制作为 SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL 线切换为低电平,这样就输出了一个标准的 I2C 起始信号。 |
- 两者的区别:硬件 I2C 直接使用外设来控制引脚,可以减轻 CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为 SCL 和 SDA,软件模拟 I2C 则可以使用任意 GPIO 引脚,相对比较灵活。对于硬件I2C用法比较复杂,软件I2C的流程更清楚一些。如果要详细了解I2C的协议,使用软件I2C可能更好的理解这个过程。在使用I2C过程,硬件I2C可能通信更加快,更加稳定。
- 当硬件I2C管脚不够用时,可以采用软件I2C
2.实现AHT20温湿度采集
AHT20的详细信息
(1)电路连接
本实验采取软件I2C实现,GIPO引脚选用的是PB6,PB7
将AHT20温湿度传感器的SCL连接STM32的PB6,SDA连接PB7,即SCL→PB6、SDA→PB7 TTL转USB模块与STM32的接线方式为:3.3V→3.3V、GND→GND、RX→TX、TX→RX 如图:
(2)代码部分
软件选用Keil5,烧录工具选用FLYMCU,串口调试助手选用sscom5.13.1
AHT20芯片使用部分代码:
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);
}
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();
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("读取失败!!!");
}
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");
}
将代码在keil5中运行后,即可得到hex文件
(3)烧录程序
- 打开软件FlyMcu,找到执行生成的hex文件,点击开始编程,等待片刻后,烧录完成
(4)读取温湿度信息
二、OLED显示温湿度信息
此实例接着上一个实例,将串口助手上的温湿度显示改用OLED屏显
1.OLED
(1)什么是OLED
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。
(2)OLED的优点
OLED显示技术具有自发光的特性
OLED采用非常薄的有机材料涂层和玻璃基板,当有电流通过时,这些有机材料就会发光,而且OLED显示屏幕可视角度大,并且能够节省电能。这样同样的显示,OLED效果要来得好一些。
(3)0.96寸OLED模块
- 模块有单色和双色两种可选,单色为纯蓝色,而双色则为黄蓝双色。单色模块每个像素点只有亮与不亮两种情况,没有颜色区分;
- 尺寸小,显示尺寸为0.96寸,而模块的尺寸仅为27mm*26mm大小;
- 高分辨率,该模块的分辨率为128*64;
- 多种接口方式,该模块提供了总共4种接口包括:6800、8080两种并行接口方式、 4线的穿行SPI接口方式、IIC接口方式(此实验采用IIC接口方式)
- 不需要高压,直接接3.3V就可以工作了。
(4)OLED模块工作模式选择
OLED模块有四种工作模式
4种模式通过模块的BS1/BS2设置(通过硬件来设置),BS1/BS2的设置与模块接口模式的关系如下表
接口方式 | BS1 | BS2 |
---|
4线SPI | 0 | 0 | IIC | 1 | 0 | 8位6800 | 0 | 1 | 8位8080 | 1 | 1 |
2.实现OLED显示温湿度信息
(1)电路连接
- STM32与TTL转USB模块的连接以及与温湿度传感器的连接与第一个实例中的连接方式相同
- OLED与STM32的连接为
VCC→3.3V、GND→GND、D1→PB15、C5→PB11、RE5→PB12、DC→PB10、D0→PB13
(2)代码部分
显示温湿度的代码:
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();
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");
}
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(strHumi,"%.1f",b);
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,
"度",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,
"湿",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,
"显",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,
"示",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,
主体函数的代码:
#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();
OLED_Init();
OLED_Clear(0);
while(1)
{
read_AHT20_once();
OLED_Clear(0);
delay_ms(1500);
}
}
将代码在keil5中运行后,即可得到hex文件
(3)烧录程序
- 打开软件FlyMcu,找到执行生成的hex文件,点击开始编程,等待片刻后,烧录完成
(4)显示温湿度信息
- OLED屏上成功显示温湿度,并且温湿度符合室内温湿度范围,实验成功!
三、OLED滚动显示中文信息
1.汉字点阵
(1)什么是汉字点阵
所谓一个字的点阵. 其实就是指这个汉字用多少个象素点来描述. 每个象素点显示为什么颜色, 通常情况下, HZK16采用的是16*16点阵, 即256个象素点描述一个汉字.
(2)点阵字库的存储
在汉字的点阵字库中,每个字节的每个位都代表一个汉字的一个点,每个汉字都是由一个矩形的点阵组成,0 代表没有,1 代表有点,将 0 和 1 分别用不同颜色画出,就形成了一个汉字,常用的点阵矩阵有 12 * 12,14 * 14, 16 * 16 三种字库。 字库根据字节所表示点的不同有分为横向矩阵和纵向矩阵,目前多数的字库都是横向矩阵的存储方式,我们接下去所描述的都是指横向矩阵字库。
(3)点阵字库的举例说明
现在以16×16点阵为例,说明其字库的储存
对于 16×16 的矩阵来说,它所需要的位数共是 16×16=256 个位,每个字节为 8 位,因此,每个汉字都需要用 256 / 8 = 32 个字节来表示。即每两个字节代表一行的 16 个点,共需要 16 行,显示汉字时,只需一次性读取 32 个字节,并将每两个字节为一行打印出来,即可形成一个汉字。 点阵结构如下图所示:
2.实现OLED显示姓名和学号
(1)电路连接
OLED和STM32的连线和实现OLED显示温湿度信息实例的连线方式相同
(2)字体取模
- 打开字体取模工具,点击选项
- 进入选项界面后,将部分参数修改至下图设置,点击确定
- 先将字体改为宋体,然后输入自己的名字,点击生成字模后,在下面空白处会出现字模的十六进制字模
- 双击打开oledfont,将刚才得到的字模复制到红框框选处,作为字模库的一部分,方便调用。
(3)代码部分
中文显示部分代码:
void GUI_ShowCHinese(u8 x,u8 y,u8 hsize,u8 *str,u8 mode)
{
while(*str!='\0')
{
if(hsize == 16)
{
GUI_ShowFont16(x,y,str,mode);
}
else if(hsize == 24)
{
GUI_ShowFont24(x,y,str,mode);
}
else if(hsize == 32)
{
GUI_ShowFont32(x,y,str,mode);
}
else
{
return;
}
x+=hsize;
if(x>WIDTH-hsize)
{
x=0;
y+=hsize;
}
str+=2;
}
}
主体函数部分代码:
int main(void)
{
delay_init();
OLED_Init();
OLED_Clear(0);
while(1)
{
TEST_MainPage();
}
}
将代码在keil5中运行后,即可得到hex文件
(4)烧录程序
- 打开软件FlyMcu,找到执行生成的hex文件,点击开始编程,等待片刻后,烧录完成
- 运行结果,OLED上成功显示学号和姓名
3.OLED滚动显示中文信息
由于电路连接和字体取模的步骤方法和上面实例一样,所以跳过,直接来到代码部分
(1)代码部分
主体函数部分代码:
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
int main(void)
{
delay_init();
NVIC_Configuration();
OLED_Init();
OLED_Clear(0);
OLED_WR_Byte(0x2E,OLED_CMD);
OLED_WR_Byte(0x27,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x07,OLED_CMD);
OLED_WR_Byte(0x07,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xFF,OLED_CMD);
TEST_MainPage();
OLED_WR_Byte(0x2F,OLED_CMD);
}
显示部分代码:
void TEST_MainPage(void)
{
GUI_ShowCHinese(10,20,16,"故将别语恼佳人,欲看梨花枝上雨",1);
delay_ms(1500);
delay_ms(1500);
}
水平左右滚动部分代码:
OLED_WR_Byte(0x2E,OLED_CMD);
OLED_WR_Byte(0x26,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x07,OLED_CMD);
OLED_WR_Byte(0x07,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xFF,OLED_CMD);
OLED_WR_Byte(0x2F,OLED_CMD);
(2)烧录程序
- 打开软件FlyMcu,找到执行生成的hex文件,点击开始编程,等待片刻后,烧录完成
(3)实物结果显示
- 如图,诗句在OLED屏上滚动显示
总结
本篇博客介绍了I2C总线通信协议的基本概念,I2C的工作原理以及硬件I2C和软件I2C的区别,特别强调了在当硬件I2C管脚不够用时,可以采用软件I2C的方法。并且通过实例向大家展示如何采用软件I2C结合AHT20温湿度传感器来采集温湿度,并且上传到PC端显示。介绍了OLED的基本概念、优点以及工作模式,并且通过实例向大家展示如何通过OLED显示温湿度传感器采集到的数据。介绍了汉字点阵的基本含义,向大家展示了如何进行汉字取模,并且如何将取模得到的汉字在OLED屏上显示,并且扩展到如何滚动显示。
参考文章
硬件I2C与模拟I2C I2C详解(一) I2C协议简介 stm32通过I2C接口实现温湿度(AHT20)的采集 【常用模块】OLED显示模块(原理讲解、STM32实例操作) 基于STM32的温湿度采集——OLED显示 0.96寸OLED在STM32f103上实现滚动显示长字符
|