基于I2C/SPI总线的温湿度采集与OLED显示
1 I2C总线通信协议
I2C 总线在物理连接上非常简单,分别由 SDA(串行数据线) 和 SCL(串行时钟线) 及 上拉电阻 组成。I2C的通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。 I2C通信方式为 半双工 ,只有一根SDA线,同一时间只可以单向通信,485也为半双工,SPI和uart为双工。
1.1 软件I2C
(1)软件I2C一般是用GPIO管脚,用软件控制管脚状态以模拟I2C通信波形。 (2)软件I2C由于不受管脚限制,接口比较灵活。 (3)模拟I2C 是通过GPIO,软件模拟寄存器的工作方式。 (4)软件I2C是程序员使用程序控制SCL,SDA线输出高低电平,模拟I2C协议的时序。一般较硬件I2C稳定,但是程序较为繁琐,不过不难。
1.2 硬件I2C
(1)所谓硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的。 (2)硬件I2C的效率要远高于软件的。 (3)硬件(固件)I2C是直接调用内部寄存器进行配置。 (4)硬件I2C程序员只要调用I2C的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出。但是有些单片机的硬件I2C不太稳定,调试问题较多。
1.3 区别软件I2C和硬件I2C
2 AHT20温湿度采集并通过串口发送
题目: 使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)。
2.1 电路连接
采用软件I2C实现,电路连线中,使用AHT20芯片、USB转RS232模块 ,采用的GPIO口是PB6 和PB7 ,因此SCL连接PB6,SDA连接PB7。
2.2 代码分析
完整代码 ①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");
}
2.3 实验结果
烧录 串口发送并显示:可以看到开始时是显示的室内的温湿度,用手将AHT20芯片捂住使之温度升高,显示结果也跟着升高。【温度从21升高到26,湿度从57升高到99.9】
3 OLED屏显和汉字点阵编码原理
-
SPI 是串行外设接口(Serial Peripheral Interface)的缩写。是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线。 SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。
(1)SDO/MOSI – 主设备数据输出,从设备数据输入; (2)SDI/MISO – 主设备数据输入,从设备数据输出; (3)SCLK– 时钟信号,由主设备产生; (4)CS/SS –从设备使能信号,由主设备控制。当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低或者是拉高。
SPI通信过程 MOSI 与 MISO 的信号只在 NSS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。 -
OLED也被称之为第三代显示技术,其显示原理与LCD有着本质上的区别,主要是通过电场驱动,有机半导体材料和发光材料通过过载流子注入和复合后实现发光。 电路连接:OLED屏、STM32F103开发板。
4 OLED屏显示实例
4.1 文字显示
4.1.1 提取字模
首先利用取点阵字模的软件将要显示的汉字的16进制数据提取出来【数字和英文呢不需要提取】,这里采用的软件是字模提取V2.1 CopyLeft By Horse2000 。 打开工程文件,在右侧列表里找到oledfont.h 文件 将刚才复制的十六进制码按照如下格式粘贴进去 然后在test.c 文件中,对TEST_MainPage 函数做相应的修改
4.1.2 代码分析
完整代码:提取码:bphx 代码需要修改的部分在上述文字提取中已说明。详细修改如下: ①修改字模数据
"张",0x01,0x00,0xF9,0x08,0x09,0x08,0x09,0x10,0x09,0x20,0x79,0x40,0x41,0x00,0x47,0xFE,
0x41,0x40,0x79,0x20,0x09,0x20,0x09,0x10,0x09,0x08,0x09,0x44,0x51,0x82,0x21,0x00,
②修改显示的内容
void TEST_MainPage(void)
{
GUI_ShowString(28,0,"ZHANG",16,1);
GUI_ShowCHinese(28,20,16,"张",1);
GUI_ShowString(4,48,"63190703033",16,1);
delay_ms(1500);
delay_ms(1500);
}
③main函数
int main(void)
{
delay_init();
NVIC_Configuration();
OLED_Init();
OLED_Clear(0);
while(1)
{
TEST_MainPage();
}
}
4.1.3 结果显示
4.2 配合AHT20显示温湿度
4.2.1 电路连线
以前面的连线为基础,将AHT20芯片、OLED屏与STM32F103芯片连接起来。
4.2.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,
"湿",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,
"度",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,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,
③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();
OLED_Init();
OLED_Clear(0);
while(1)
{
read_AHT20_once();
OLED_Clear(0);
delay_ms(1500);
}
}
4.2.3 结果显示
可以看到用手捂住AHT20芯片,温度升高,湿度也升高。
4.3 滚动显示长字符
4.3.1 电路连线
将AHT20芯片摘下来,其他线路保持不变。
提取字模:
"欢",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,
"迎",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,
"来",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,
"到",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,
"博",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,
"客",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,
4.3.2 代码分析
完整代码 ①水平左右移动
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);
②垂直和水平滚动
OLED_WR_Byte(0x2e,OLED_CMD);
OLED_WR_Byte(0x29,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(0x01,OLED_CMD);
OLED_WR_Byte(0x2F,OLED_CMD);
③test.c
void TEST_MainPage(void)
{
GUI_ShowCHinese(10,20,16,"欢迎来到博客",1);
delay_ms(1500);
delay_ms(1500);
}
④main.c
#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);
}
4.3.3 结果显示
可以看到文字会滚动显示,且中间留有一定间隙。
5 总结
取字模时一定要仔细一点,按照顺序来,用C51格式,没进行参数设置会导致乱码。 每一次实验都要重新烧录代码,烧录软件不成功,将串口模块取下来重新插进去,重新进行烧录。 虽然代码很多是厂家提供的,但是有些部分的代码还是要弄清楚才能知道程序出错怎么调试。
6 参考文献
stm32通过I2C接口实现温湿度(AHT20)的采集 基于STM32的温湿度采集——OLED显示 基于STM32的0.96寸OLED显示屏显示数据 0.96寸OLED在STM32f103上实现滚动显示长字符 I2C协议
|