物联网实战--入门篇之(五)嵌入式-IIC驱动(SHT30温湿度)

news2024/11/25 20:47:51

目录

一、IIC简介

二、IIC驱动解析

三、SHT30驱动

四、总结


一、IIC简介

        不管是IIC还是串口,亦或SPI,它们的本质区别在于有各自的规则,就是时序图;它们的相同点就是只要你理解了时序图,你就可以用最普通的IO引脚模拟出各自的通讯总线,但是一般来讲没那必要,特别是串口,模拟比较麻烦,而且速率较高,使用频率较高,很费系统资源,不合算。可以发现 我的代码里IIC驱动是用自己模拟的,主要是因为1、硬件IIC有时候会卡死;2、IIC速率比较低,且使用频率较低;3、便于在各个芯片平台上移植;4、有时候IIC的设备比较多,模拟引脚选择灵活,便于PCB设计。

        那么,下面看下模拟IIC的文件,其实并不难,就是按时序图来就行了,具体时序图就不贴了,总的就下图这几个函数,然后再根据具体IIC从设备的要求读写相应数据就行了。

二、IIC驱动解析

        接下来讲解下驱动代码,先从结构体开始,主要就是保存应用层的SDA和SCL引脚信息,还有个延时,正常默认5us,不需要改动。SDA_0等这些宏定义主要是为了程序的简洁以及驱动文件移植时便于修改,只要替换各自平台的引脚操作函数即可。

        SCL是时钟引脚,总是作为输出,而SDA有时候是输出有时候是输入,所以需要IIC_SdaInMode()和IIC_SdaOutMode()进行引脚模式的切换。

        以下是IIC驱动的引脚相关函数,移植到其他平台的时候需要修改成相应的函数。



/*		
================================================================================
描述 : IIC引脚初始化
输入 : 
输出 : 
================================================================================
*/
void IIC_GPIOInit(I2cDriverStruct *pDriver)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Pin = pDriver->pin_sda;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; 
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(pDriver->port_sda, &GPIO_InitStruct);	
	
	GPIO_InitStruct.GPIO_Pin = pDriver->pin_scl;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(pDriver->port_scl, &GPIO_InitStruct);		
  
  pDriver->delay_time=IIC_DELAY_TIME;
}


/*		
================================================================================
描述 : SDA设置成输入模式
输入 : 
输出 : 
================================================================================
*/
void IIC_SdaInMode(I2cDriverStruct *pDriver)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = pDriver->pin_sda;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(pDriver->port_sda, &GPIO_InitStruct);
}

/*		
================================================================================
描述 : SDA设置成输出模式
输入 : 
输出 : 
================================================================================
*/
void IIC_SdaOutMode(I2cDriverStruct *pDriver)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = pDriver->pin_sda;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(pDriver->port_sda, &GPIO_InitStruct);
}

        以下是根据IIC时序图写的信号代码,不同人模拟的代码首尾可能略有差别,但是最核心的信号状态是一样的,这个不用太纠结。具体的每个信号是什么作用、该怎么使用,等等结合SHT30温湿度的驱动再说明。


/*		
================================================================================
描述 : 起始信号
输入 : 
输出 : 
================================================================================
*/
void IIC_Start(I2cDriverStruct *pDriver)
{
	SCL_1;
	SDA_1;
	delay_us(pDriver->delay_time);
	SDA_0;
	delay_us(pDriver->delay_time);
	SCL_0;
	delay_us(pDriver->delay_time);
}


/*		
================================================================================
描述 : 停止信号
输入 : 
输出 : 
================================================================================
*/
void IIC_Stop(I2cDriverStruct *pDriver)
{
	SDA_0;
	SCL_1;
	delay_us(pDriver->delay_time);
	SDA_1;
	delay_us(pDriver->delay_time);
	SCL_0;
}


/*		
================================================================================
描述 : 应答
输入 : 
输出 : 
================================================================================
*/
void IIC_Ack(I2cDriverStruct *pDriver)
{
	SDA_0;
	delay_us(pDriver->delay_time);
	SCL_1;
	delay_us(pDriver->delay_time);
	SCL_0;
	delay_us(pDriver->delay_time);
	SDA_1;
	delay_us(pDriver->delay_time);
}

/*		
================================================================================
描述 : 非应答
输入 : 
输出 : 
================================================================================
*/
void IIC_NAck(I2cDriverStruct *pDriver)
{
	SCL_0;
	delay_us(pDriver->delay_time);
	SDA_1;
	SCL_1;
	delay_us(pDriver->delay_time);
	
}

/*		
================================================================================
描述 : 等待回复
输入 : 
输出 : 
================================================================================
*/
bool IIC_WaitAck(I2cDriverStruct *pDriver)
{
	u32 wait_tickets=0;
	
	SCL_0;
	IIC_SdaInMode(pDriver);
	delay_us(pDriver->delay_time);
	
	while(SDA_READ()>0)
	{
		wait_tickets++;
		if(wait_tickets>250)
		{
			IIC_SdaOutMode(pDriver);
			delay_us(pDriver->delay_time);
			IIC_Stop(pDriver);
			return false;
		}
		delay_us(1);
	}
	SCL_1;
	delay_us(pDriver->delay_time);
	SCL_0;
	delay_us(pDriver->delay_time);
	IIC_SdaOutMode(pDriver);
	return true;
}

        以下是IIC的字节读写函数,也是根据时序来就行了,传输过程是先高位后低位,读的时候SDA引脚要先设置成输入模式。

/*		
================================================================================
描述 : 读取一个字节
输入 : 
输出 : 
================================================================================
*/
u8 IIC_ReadByte(I2cDriverStruct *pDriver)
{
	u8 i, data=0;
	
	IIC_SdaInMode(pDriver);
	for(i=0;i<8;i++)
	{
		SCL_0;
		delay_us(pDriver->delay_time);
		SCL_1;
		delay_us(pDriver->delay_time);
		data=data<<1;//左移,高位先读取
		if(SDA_READ()>0)
		{
			data|=0x01;
		}
	}
	SCL_0;
	IIC_SdaOutMode(pDriver);
	delay_us(pDriver->delay_time);
	return data;
}
/*		
================================================================================
描述 : 写入一个字节
输入 : 
输出 : 
================================================================================
*/
void IIC_WriteByte(I2cDriverStruct *pDriver, u8 data)
{
	u8 i;
	for(i=0;i<8;i++)
	{
		SCL_0;
		if(data&0x80)//高位先写
		{
			SDA_1;			
		}
		else
		{
			SDA_0;
		}
		delay_us(pDriver->delay_time);
		SCL_1;
		delay_us(pDriver->delay_time);
		data=data<<1;  
	}

}

        以上基本是模拟IIC的驱动文件的全部内容了,自己看会发现每个函数输入都有一个I2cDriverStruct结构体,这样便于多个IIC设备驱动,比如2个SHT30+2个AT24C64一起使用都是没问题的,只要把各自的引脚定义清楚来就行了,互不干扰。

三、SHT30驱动

        净化器项目跟IIC相关的就是SHT30温湿度传感器了,我们一般就是读取温湿度值就行了,所以用起来比较简单,具体看下图,其中结构体Sht30WorkStruct内容就是器件地址、IIC结构体和温湿度数值,函数主要是初始化和读取温湿度,设置地址在特殊情况下才用。

        以下是初始化代码,主要是引脚初始化和配置默认的器件地址,这个器件地址是所有IIC从机设备都有的,根据芯片厂家和硬件设计来确定,比如这里的STH30,默认是0x44;如果ADDR引脚上拉则是0x45,数据手册截图如下所示。如果器件地址是0x45的话那就在应用层调用drv_sht30_set_addr进行设置即可。

/*		
================================================================================
描述 : 器件引脚初始化
输入 : 
输出 : 
================================================================================
*/
void drv_sht30_init(Sht30WorkStruct *pSht30Work)
{
	IIC_GPIOInit(&pSht30Work->tag_iic);
	pSht30Work->dev_addr=0x44;//默认器件地址
}

        核心的就是温湿度读取了,具体代码如下,其中0x2C06是温湿度所在的寄存器地址,要读取6个字节,分别是温度高8位、温度低8位、温度校验码、湿度高8位、湿度低8位和湿度校验码,按顺序先读取出来后再自己根据公式进行整合即可。

        IIC读取的常规流程是先写入器件地址,同时通过配置器件地址的最低位说明下一步是写数据,也就是写入寄存器地址,这里是两个字节,先高8位后低8位,写完后先停止并充分延时下,让SHT30做好准备,否则不能正确读取;随后再次启动传输,写入器件地址并配置读数据需求,紧接着连续读取6字节数据,最后就是根据公式转换成实际的温湿度值就行了。

        在读取温湿度过程中会发现,IIC的时序函数起到了调度指挥的作用,起始、等待回复、停止等等,都是按顺序来的,具体自己结合代码看下。


/*		
================================================================================
描述 : 读取温湿度数据
输入 : 
输出 : 
================================================================================
*/
void drv_sht30_read_th(Sht30WorkStruct *pSht30Work)
{
	u16 reg_addr=0x2C06;//温湿度的寄存器地址,由数据手册得来
	u8 dev_addr=pSht30Work->dev_addr;
	I2cDriverStruct *pIIC=&pSht30Work->tag_iic;
	
	IIC_Start(pIIC);
	IIC_WriteByte(pIIC, dev_addr<<1|0x00);//准备写入寄存器地址
	IIC_WaitAck(pIIC);
	
	IIC_WriteByte(pIIC, reg_addr>>8);//写入寄存器地址高8位
	IIC_WaitAck(pIIC);	
	IIC_WriteByte(pIIC, reg_addr&0xFF);//写入寄存器地址低8位
	IIC_WaitAck(pIIC);		
	IIC_Stop(pIIC);
	delay_ms(20);//这个延时要稍微长点20ms以上
	
	IIC_Start(pIIC);
	IIC_WriteByte(pIIC, dev_addr<<1|0x01);//准备读取数据
	IIC_WaitAck(pIIC);	
	
	u8 buff[10]={0};
	for(u8 i=0; i<6; i++)//读取温湿度和校验值状态
	{
		buff[i]=IIC_ReadByte(pIIC);
		if(i<5)IIC_Ack(pIIC);
		else IIC_NAck(pIIC);
	}
	IIC_Stop(pIIC);
	
	u16 temp=buff[0]<<8|buff[1];//温度寄存器值
	u16 humi=buff[3]<<8|buff[4];//湿度寄存器值
	
	pSht30Work->temp_value=175.f*(float)temp/65535.f-45.f ;//转换成温度-℃
	pSht30Work->humi_value=100.f*(float)humi/65535.f;//转换为湿度-%
	
	printf("temp=%.1f C, humi=%.1f%%\n", pSht30Work->temp_value, pSht30Work->humi_value);
}

        由于系统可能挂载多个温湿度传感器,所以SHT30驱动程序函数入口都有一个Sht30WorkStruct结构体。

        在应用层,主要就是定义SHT30结构体、初始化引脚和读取操作了,具体如下所示。

四、总结

        IIC模拟驱动可以用在其它各种IIC器件,比如AT24Cxx系列的EEPROM、RC522 RFID感应模块等等,底层的IIC驱动过程都是一样的,剩下的就是根据数据手册,配置不同的器件地址和操作不同的寄存器地址了,基本原理是一样的,后续有机会再多写一些IIC设备的驱动。

        

本项目的交流QQ群:701889554

   写于2024-3-30

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1559087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

P6学习:Oracle Primavera P6 OBS/责任人解析

前言 Primavera P6 EPPM 责任人用于管理 P6 企业项目组合管理 (EPPM) 系统中的项目所有权和权限。 Primavera P6 EPPM 中的所有项目都至少围绕三个结构进行组织&#xff1a;称为企业项目结构 (EPS) 的用于组织项目的结构、称为工作分解结构 (WBS) 的用于组织项目内活动的结构…

书生·浦语全链路开源开放体系 第二期

文章目录 大模型背景大模型开发流程InternLM 2.0SFT与RLHFInternLM2主要亮点 书生浦语全链路开源开放体系数据-书生万卷InternLM-Train微调 XTuner评测工具 OpenCompass部署 LMDeploy智能体 Lagent智能体工具箱 AgentLego 大模型背景 专用模型&#xff1a;针对特定的任务&…

亚马逊测评新策略:解决底层环境防关联,提升下单成功率

对于做测评的环境系统&#xff0c;确保稳定性和成功率是非常重要的。市面上有各种环境方案&#xff0c;如虚拟机、模拟机、gcs、云手机、VPS等。然而&#xff0c;这些方案不仅成本高&#xff0c;而且成功率很低。因此&#xff0c;一个好的环境系统是成功的基础。 亚马逊平台的…

算法学习——LeetCode力扣动态规划篇5(198. 打家劫舍、213. 打家劫舍 II、337. 打家劫舍 III )

算法学习——LeetCode力扣动态规划篇5 198. 打家劫舍 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统…

echarts 图表/SVG 图片指定位置截取

echarts 图表/SVG 图片指定位置截取 1.前期准备2.图片截取3.关于drawImage参数 需求&#xff1a;如下图所示&#xff0c;需要固定头部legend信息 1.前期准备 echarts dom渲染容器 <div :id"barchart id" class"charts" ref"barchart">&…

动态规划-----背包类问题(0-1背包与完全背包)详解

目录 什么是背包问题&#xff1f; 动态规划问题的一般解决办法&#xff1a; 0-1背包问题&#xff1a; 0 - 1背包类问题 分割等和子集&#xff1a; 完全背包问题&#xff1a; 完全背包类问题 零钱兑换II: 什么是背包问题&#xff1f; 背包问题(Knapsack problem)是一种…

jsp中设置动态时间

第一步 在head中写入meta <head><meta charset"UTF-8" http-equiv"Refresh" content"1"> </head> 第二步在head中写入函数 <head><meta charset"UTF-8" http-equiv"Refresh" content"…

Mac m1 Flink的HelloWorld

首先在官方下载Downloads | Apache Flink 下载好压缩包后解压&#xff0c;得到Flink文件夹 进入&#xff1a;cd flink-1.19.0 ls 查看里面的文件&#xff1a; 执行启动集群 ./bin/start-cluster.sh 输出显示它已经成功地启动了集群&#xff0c;并且正在启动 standalonesessio…

基于YOLOV8+Pyqt5光伏太阳能电池板目标检测系统

1、YOLOV8算法 YOLOv8 是当前效果较好的目标检测 算法&#xff0c;它的核心网络来源于 DarkNet-53&#xff0c;该网络初次在 YOLOv3[11] 中被引入&#xff0c;并深受 ResNet[12] 的影响。DarkNet-53 使用了残差机制&#xff0c;并连续添加了卷积模块来加强其功能性。 这 53 层…

AI 音乐的 “ChatGPT“ 时刻,SunoV3简介和升级教程

一句话总结 Suno AI音乐平台发布了V3版本&#xff0c;标志着AI音乐创作领域的一个重要进步&#xff0c;类似于ChatGPT在文本生成领域的影响。 关键信息点 Suno AI是专注于生成式AI音乐的平台&#xff0c;最新发布的V3版本在音质、咬字和节奏编排上有显著提升。V3版本的AI音乐…

集成百兆,千兆,万兆网络变压器等电子元器件的RJ45 Jack连接器在屏显控制系统中的应用

Hqst华轩盛(石门盈盛)电子导读&#xff1a;集成百兆&#xff0c;千兆&#xff0c;万兆网络变压器等电子元器件的RJ45 Jack连接器在屏显控制系统中的应用 一 ﹑集成百兆&#xff0c;千兆&#xff0c;万兆网络变压器等电子元器件的RJ45 Jack连接器在屏显控制系统中的应用前景 近年…

SQL Server 数据库常见提权总结

前面总结了linux和Windows的提权方式以及Mysql提权&#xff0c;这篇文章讲讲SQL Server数据库的提权。 目录 基础知识 权限判定 系统数据库 存储过程 常见系统存储过程 常见扩展存储过程 xp_cmdshell扩展存储过程提权 xp_dirtree写入文件提权 sp_oacreate提权 xp_re…

【吊打面试官系列】Redis篇 -Redis 如何做内存优化?

大家好&#xff0c;我是锋哥。今天分享关于 【Redis 如何做内存优化&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Redis 如何做内存优化&#xff1f; 尽可能使用散列表&#xff08;hashes&#xff09;&#xff0c;散列表&#xff08;是说散列表里面存储的数…

[ESP32]:基于esp-modbus实现serial从机

[ESP32]&#xff1a;基于esp-modbus实现serial从机 开发环境&#xff1a; esp idf 5.1esp-modbus 1.0.13 使用如下指令添加组件&#xff0c;或者访问esp-modbus idf.py add-dependency "espressif/esp-modbus^1.0.13"1.mb_register_area_descriptor_t 对于slave…

PHP远程命令执行与代码执行原理利用与常见绕过总结

PHP远程命令执行与代码执行原理利用与常见绕过总结 远程命令执行 相较于SQL注入漏洞&#xff0c;远程命令执行更加少见。由于是直接执行系统命令&#xff0c;所以相较于前者此漏洞会更加危险&#xff1a; 攻击者通过远程命令执行漏洞可以直接掌控服务器攻击者可以通过存在此…

OSCP靶场--Internal

OSCP靶场–Internal 考点(CVE-2009-3103) 1.nmap扫描 ## ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.216.40 -sV -sC -Pn --min-rate 2500 -p- Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-31 07:00 EDT Nmap scan report for 192.168.216.40 Host is up…

【微服务框架】微服务简介

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

react ts 封装搜索条件

封装 import React, { ReactNode, useImperativeHandle, forwardRef } from react; import { Card, Form, Input, Button, Row, Col } from antd;interface Iprops {formItem: any,getParams: (params: any) > void,reset?: () > void | undefined,isButton?: boolean…

算法学习16:数论03(容斥原理、博弈论)

算法学习16&#xff1a;数论03&#xff08;容斥原理、博弈论&#xff09; 文章目录 算法学习16&#xff1a;数论03&#xff08;容斥原理、博弈论&#xff09;前言一、容斥原理&#xff1a;求多个集合的并集二、博弈论1.Nim游戏&#xff1a;2.集合N-im游戏 总结 前言 提示&#…