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的AHT20温度采集器及SPI协议OLED显示 -> 正文阅读

[嵌入式]基于I2C的AHT20温度采集器及SPI协议OLED显示

目录

?

一.相关协议

1 关于I2C协议

2 关于SPI协议???

3 了解AHT20芯片的相关信息

4 OLED屏的滚屏命令

5 AHT20引脚接法

二、采集温度传到电脑

1 完整代码

2 打开工程文件

3 编译加烧录

4 结果

三、基于SPI的OLED显示

1修改代码

?2汉字取模点阵软件

3烧录

4结果

?5显示AHT20的温度和湿度??

结果

6显示左右滑动的字符?

结果?

?四.总结


一.相关协议

1 关于I2C协议

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

??协议层

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

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

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

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

软件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可能通信更加快,更加稳定。

2 关于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 及通讯模式
?

3 了解AHT20芯片的相关信息

具体信息请到官方下载对应产品介绍文档

4 OLED屏的滚屏命令

水平左右移

OLED_WR_Byte(0x2E,OLED_CMD);        //关闭滚动
OLED_WR_Byte(0x26,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);        //虚拟字节
OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动

垂直和水平滚动

OLED_WR_Byte(0x2e,OLED_CMD);        //关闭滚动
OLED_WR_Byte(0x29,OLED_CMD);        //水平垂直和水平滚动左右 29/2a
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);        //终止页 1
OLED_WR_Byte(0x01,OLED_CMD);        //垂直滚动偏移量
OLED_WR_Byte(0x2F,OLED_CMD);        //开启滚动

?本OLED屏的芯片类型:SSD1306
详细命令的介绍,请查找SSD1306-OLED驱动中文手册进行了解

5 AHT20引脚接法

SCL-->PB6? ? ??SDA-->PB7?

本文代码主要使用PB6,PB7引脚,如果需要使用其它引脚可自行修改。

1.6 OLED引脚接法

二、采集温度传到电脑

1 完整代码

主要的代码

main.c

#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include "bsp_i2c.h"
 
 
int main(void)
{	
	//延时初始化
	delay_init();
	//串口初始化
	uart_init(115200);
	//
	IIC_Init();
	
	while(1)
	{
		read_AHT20_once();
		delay_ms(1500);
  }
}
 
/*********************************************END OF FILE**********************/

bsp_i2c.c

#include "bsp_i2c.h"
#include "delay.h"
#include "string.h"
 
uint8_t   ack_status=0;
uint8_t   readByte[6];
 
uint32_t  H1=0;  //Humility
uint32_t  T1=0;  //Temperature
 
uint8_t  AHT20_OutData[4];
 
/****************
 *初始化 I2C 函数
 ****************/
void IIC_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	//启用高速 APB (APB2) 外围时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );	
	
	//GPIO 定义
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	//初始化 SCL(Pin6)高电平
	IIC_SCL=1;
	//初始化 SDA(Pin7)高电平
	IIC_SDA=1;
}
 
/*********************
 *AHT20 数据操作总函数
 *********************/
void read_AHT20_once(void)
{
	printf("读取数据中");
	
	//延时 10 微妙
	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 信号
	ack_status = Receive_ACK();
	//判断 ACK 信号
	if(ack_status)
	{
		printf(">");
	}
	else
		printf("×");
	
	//发送软复位命令(重启传感器系统)
	I2C_WriteByte(0xBA);
	//接收 ACK 信号
	ack_status = Receive_ACK();
	//判断 ACK 信号
	if(ack_status)
		printf(">");
	else
		printf("×");
	
	//停止 I2C 协议
	I2C_Stop();
}
 
//0x70 —> 0111 0000 前七位表示 I2C 地址,第八位为0,表示 write
//0xE1 —> 看状态字的校准使能位Bit[3]是否为 1
//0x08 0x00 —> 0xBE 命令的两个参数,详见 AHT20 参考手册
void init_AHT20(void)
{
	//传输开始
	I2C_Start();
 
	//写入 0x70 数据
	I2C_WriteByte(0x70);
	//接收 ACK 信号
	ack_status = Receive_ACK();
	//判断 ACK 信号
	if(ack_status)
		printf(">");
	else
		printf("×");
	
	//写入 0xE1 数据
	I2C_WriteByte(0xE1);
	ack_status = Receive_ACK();
	if(ack_status)
		printf(">");
	else
		printf("×");
	
	//写入 0x08 数据
	I2C_WriteByte(0x08);
	ack_status = Receive_ACK();
	if(ack_status)
		printf(">");
	else 
		printf("×");
	
	//写入 0x00 数据
	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();
	if(ack_status) 
		printf(">");
	else 
		printf("×");
	
	//停止 I2C 协议
	I2C_Stop();
}
 
//0x70 —> 0111 0000 前七位表示 I2C 地址,第八位为0,表示 write
//0xAC —> 触发测量
//0x33 0x00 —> 0xAC 命令的两个参数,详见 AHT20 参考手册
void startMeasure_AHT20(void)
{
	//启动 I2C 协议
	I2C_Start();
	
	I2C_WriteByte(0x70);
	ack_status = Receive_ACK();
	if(ack_status)
		printf(">");
	else 
		printf("×");
	
	I2C_WriteByte(0xAC);
	ack_status = Receive_ACK();
	if(ack_status) 
		printf(">");
	else 
		printf("×");
	
	I2C_WriteByte(0x33);
	ack_status = Receive_ACK();
	if(ack_status) 
		printf(">");
	else 
		printf("×");
	
	I2C_WriteByte(0x00);
	ack_status = Receive_ACK();
	if(ack_status) 
		printf(">");
	else 
		printf("×");
	
	I2C_Stop();
}
 
 
void read_AHT20(void)
{
	uint8_t i;
 
	//初始化 readByte 数组
	for(i=0; i<6; i++)
	{
		readByte[i]=0;
	}
 
	I2C_Start();
 
	//通过发送 0x71 可以获取一个字节的状态字
	I2C_WriteByte(0x71);
	ack_status = Receive_ACK();
	
	//接收 6 个 8 bit的数据
	readByte[0]= I2C_ReadByte();
	//发送 ACK 信号
	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();
	//发送 NACK 信号
	SendNot_Ack();
 
	I2C_Stop();
 
	//温湿度的二进制数据处理
	//0x68 = 0110 1000
  //0x08 = 0000 1000	
	if( (readByte[0] & 0x68) == 0x08 )
	{
		H1 = readByte[1];
		//H1 左移 8 位并与 readByte[2] 相或 
		H1 = (H1<<8) | readByte[2];
		H1 = (H1<<8) | readByte[3];
		//H1 右移 4 位
		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("完成!\n");
	printf("----温度:%d%d.%d °C\n",T1/100,(T1/10)%10,T1%10);
	printf("----湿度:%d%d.%d %%",H1/100,(H1/10)%10,H1%10);
	printf("\n\n");
}
 
//接收 ACK 信号
uint8_t Receive_ACK(void)
{
	uint8_t result=0;
	uint8_t cnt=0;
 
	//置 SCL 低电平
	IIC_SCL = 0;
	//设置 SDA 为读取数据模式
	SDA_IN();
	delay_us(4);
 
	//置 SCL 高电平
	IIC_SCL = 1;
	delay_us(4);
 
	//等待从机发送 ACK 信号,等待时间为 100 个循环
	while(READ_SDA && (cnt<100))
	{
		cnt++;
	}
 
	IIC_SCL = 0;
	delay_us(4);
 
	//如果在等待时间内,则结果为 1
	if(cnt<100)
	{
		result=1;
	}
	
	return result;
}
 
//发送 ACK 信号
void Send_ACK(void)
{
	//设置 SDA 为写数据模式
	SDA_OUT();
	IIC_SCL = 0;
	delay_us(4);
 
	//置 SDA 为低电平
	IIC_SDA = 0;
	delay_us(4);
 
	IIC_SCL = 1;
	delay_us(4);
	IIC_SCL = 0;
	delay_us(4);
 
	SDA_IN();
}
 
//发送 NACK 信号
void SendNot_Ack(void)
{
	//设置 SDA 为写数据模式
	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 为写数据模式
	SDA_OUT();
	
	//循环左移发送 8 bit数据
	for(i=0; i<8; i++)
	{
		IIC_SCL = 0;
		delay_ms(5);
 
		if(input & 0x80)
		{
			IIC_SDA = 1;
		}
		else
		{
			IIC_SDA = 0;
		}
 
		IIC_SCL = 1;
		delay_ms(5);
 
		input = (input<<1);
	}
 
	IIC_SCL = 0;
	delay_us(4);
 
	SDA_IN();
	delay_us(4);
}	
 
//循环检测 SDA 的电平状态并存储起来
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 << 1) | a;
 
		IIC_SCL = 0;
		delay_ms(3);
	}
 
	SDA_IN();
	delay_ms(10);
 
	return   resultByte;
}
 
//设置 I2C 协议开始
void I2C_Start(void)
{
	SDA_OUT();
	
	IIC_SCL = 1;
	delay_ms(4);
 
	//SDA 从 1 跳变为 0 的这个过程
	//表示起始信号
	IIC_SDA = 1;
	delay_ms(4);
	IIC_SDA = 0;
	delay_ms(4);
 
	//SCL 变为 0
	//表示 SDA 数据无效,此时 SDA 可以进行电平切换
	IIC_SCL = 0;
	delay_ms(4);
}
 
//设置 I2C 协议停止
void I2C_Stop(void)
{
	SDA_OUT();
	
	//SCL 高电平,SDA 高电平
	//停止时序
	IIC_SDA = 0;
	delay_ms(4);
	IIC_SCL = 1;
	delay_ms(4);
 
	//SDA 切换到高电平
	IIC_SDA = 1;
	delay_ms(4);
}

2 打开工程文件

3 编译加烧录

在该文件下找到对应的hex文件进行烧录

文件烧录?

4 结果

三、基于SPI的OLED显示

1修改代码

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

?2汉字取模点阵软件

?使用汉字取模点阵软件将自己需要的字

?需要将设置点阵的取模走向改为顺向,方式改为逐行?

然后将对应的字模点阵加入到oledfont.h里?

3烧录

烧录跟上面的文件一样的操作方式

4结果

?5显示AHT20的温度和湿度??

????????过程大同小异

结果

6显示左右滑动的字符?

更改test.c可改变显示

用点阵字取模工具将对应字的字模取出来添加到oledfont中去

结果?

?四.总结

经过这次实验对I2c协议和SPI协议有了部分了解,代码是多方借鉴的,许多原理还不清楚,还需多加练习。

参考:

基于I2C的AHT20温度采集器以及基于SPI协议OLED显示_Laul Ken-Yi的博客-CSDN博客

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

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:51:12-

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