ARM I2C通信

1.概念

	I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线,主要用于连接整体电路

2.IIC总线硬件连接

在这里插入图片描述

1.IIC总线支持多主机多从机,但是在实际开发过程中,大多数采用单主机多从机模式
2.挂接到IIC总线上,每个从机设备都有自己的7bit从机地址
3.在总线上,发送数据的叫做发送器,接收数据叫做接收器
4.主动发起数据的叫做主机,只能被动接收数据的叫做从机
5.时钟信号由主机产生,作用:给从机,为了IIC总线上传输数据同步

3.IIC总线时序

3.1起始信号

在这里插入图片描述

在SCL为高电平期间,SDA从高到低的变化(下降沿),属于起始信号
起始信号由主机产生,起始信号产生之后,总线占用状态

3.2停止信号

在这里插入图片描述

在SCL为高电平期间,SDA从低到高的变化(上升沿),属于终止信号
停止信号由主机产生,停止信号产生之后,总线空闲状态

3.3数据传输信号(读写)

在这里插入图片描述

1.在SCL为高电平期间,数据线上的数据保持稳定,接收器从数据线上读取数据
2.在SCL为低电平期间,数据线上的数据允许变化,发送器向数据线上写入数据

3.4应答信号

在这里插入图片描述

1.每一个字节必须保证是8位长度。数据传送时,先传送高位,在发送低位,每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)
2.发送器在发送完8位数据位之后,接收器在第9个时钟周期,返回一个应答信号(0),或者非应答信号(1)在第9个时钟周期,接收器向数据线上写入数据在第9个时钟周期,发送器从数据线上读取数据如果读取到0,代表应答信号如果读取到1,代表非应答信号

3.5寻址

在这里插入图片描述

1.IIC总线上传输数据是广义的,可以传输地址信号,也可以传输数据信号
2.主机在产生起始信号之后,必须传送7位从机地址,加上读写位
3.用0表示写,用1表示读

4.IIC框图

在这里插入图片描述

5.IIC总线协议

5.1主机给从机发送一个字节

在这里插入图片描述

5.2主机给从机发送多个连续字节

在这里插入图片描述

5.3主机从从机读一个字节

在这里插入图片描述

5.4主机从从机读多个连续字节

在这里插入图片描述

6.GPIO模拟IIC协议

在这里插入图片描述

7.分析SI7006芯片手册

7.1思路

1.分析SI7006芯片手册内部实现框图
2.分析SI7006从机地址
3.分析SI7006芯片通信协议
4.分析找到采集温湿度传感器命令码
5.找到将采集的模拟量转换为数字量的公式
6.分析SI7006初始化的值

7.2从机地址

在这里插入图片描述

通过以上分析可知,si7006芯片从机地址0x40
从机地址 + 读:0x40 << 1 | 1
从机地址 + 写:0x40 << 1 | 0

7.3分析命令码

在这里插入图片描述

7.4协议

在这里插入图片描述

7.5公式

在这里插入图片描述
在这里插入图片描述

7.6初始化值

在这里插入图片描述

8.代码

// si7006.h
#ifndef __SI7006_H__
#define __SI7006_H__#include "iic.h"
#define SI7006_SLAVE 0x40void si7006_init(void);unsigned short si7006_read_hum_data(unsigned char slave_addr, unsigned char cmd_code);
short si7006_read_temp_data(unsigned char slave_addr, unsigned char cmd_code);#endif //__SI7006_H__
// si7006.c
#include "iic.h"
#include "si7006.h"
/** 函数名:si7006_init* 函数功能:SI7006芯片的初始化* 函数参数:无* 函数返回值:无*/
extern void delay(int ms);
// 往SI7006芯片0XE6写入0X3A
void si7006_init(void)
{// I2初始化i2c_init();// 发送起始信号i2c_start();// 主机发送7位从机地址+1位写位i2c_write_byte(0X40 << 1 | 0);// 等待从机回应i2c_wait_ack();// 发送寄存器地址i2c_write_byte(0XE6);// 等待从机回应i2c_wait_ack();// 发送要写的数据i2c_write_byte(0X3A);// 等待从机回应i2c_wait_ack();// 发送终止信号i2c_stop();
}
/** 函数名:si7006_read_hum_data* 函数功能:读取SI7006的湿度转换结果* 函数参数:*     slave_addr : 从机地址*     cmd_code : 命令码* 函数返回值:湿度测量的数字量*/
unsigned short si7006_read_hum_data(unsigned char slave_addr,unsigned char cmd_code)
{unsigned short dat;			// 保存读取到的湿度数据unsigned char dat_h, dat_l; // 保存读取到的数据的高八位和低八位// 发送起始信号i2c_start();// 主机发送7位从机地址+1位写位i2c_write_byte(slave_addr << 1 | 0);// 等待从机回应i2c_wait_ack();// 发送寄存器地址i2c_write_byte(cmd_code);// 等待从机回应i2c_wait_ack();// 发送第二次起始信号i2c_start();// 主机发送7位从机地址+1位写位i2c_write_byte(slave_addr << 1 | 1);// 等待从机回应i2c_wait_ack();// 延时等待从机测量数据delay(100);// 读取数据的高8位dat_h = i2c_read_byte(0); // 读取完毕发送应答信号// 读取数据的低8位dat_l = i2c_read_byte(1); // 读取完毕发送非应答信号// 发送停止信号i2c_stop();// 将读取到的数据整合到一起dat = (dat_h << 8) | dat_l;return dat;
}
/** 函数名:si7006_read_temp_data* 函数功能:读取SI7006的温度转换结果* 函数参数:*     slave_addr : 从机地址*     cmd_code : 命令码* 函数返回值:温度测量的数字量*/
short si7006_read_temp_data(unsigned char slave_addr,unsigned char cmd_code)
{short dat;		   // 保存读取到的温度数据char dat_h, dat_l; // 保存读取到的数据的高八位和低八位// 发送起始信号i2c_start();// 主机发送7位从机地址+1位写位i2c_write_byte(slave_addr << 1 | 0);// 等待从机回应i2c_wait_ack();// 发送寄存器地址i2c_write_byte(cmd_code);// 等待从机回应i2c_wait_ack();// 发送第二次起始信号i2c_start();// 主机发送7位从机地址+1位写位i2c_write_byte(slave_addr << 1 | 1);// 等待从机回应i2c_wait_ack();// 延时等待从机测量数据delay(100);// 读取数据的高8位dat_h = i2c_read_byte(0); // 读取完毕发送应答信号// 读取数据的低8位dat_l = i2c_read_byte(1); // 读取完毕发送非应答信号// 发送停止信号i2c_stop();// 将读取到的数据整合到一起dat = (dat_h << 8) | dat_l;return dat;
}
// iic.h
#ifndef __IIC_H__
#define __IIC_H__
#include "stm32mp1xx_gpio.h"
#include "stm32mp1xx_rcc.h"
// #include "gpio.h"
/* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4* I2C1_SCL ---> PF14* I2C1_SDA ---> PF15** */#define SET_SDA_OUT                     \do                                  \{                                   \GPIOF->MODER &= (~(0x3 << 30)); \GPIOF->MODER |= (0x1 << 30);    \} while (0)
#define SET_SDA_IN                      \do                                  \{                                   \GPIOF->MODER &= (~(0x3 << 30)); \} while (0)#define I2C_SCL_H                   \do                              \{                               \GPIOF->BSRR |= (0x1 << 14); \} while (0)
#define I2C_SCL_L                  \do                             \{                              \GPIOF->BRR |= (0x1 << 14); \} while (0)#define I2C_SDA_H                   \do                              \{                               \GPIOF->BSRR |= (0x1 << 15); \} while (0)
#define I2C_SDA_L                  \do                             \{                              \GPIOF->BRR |= (0x1 << 15); \} while (0)#define I2C_SDA_READ (GPIOF->IDR & (0x1 << 15))void delay_us(void);
void i2c_init(void);
void i2c_start(void);
void i2c_stop(void);
void i2c_write_byte(unsigned char dat);
unsigned char i2c_read_byte(unsigned char ack);
unsigned char i2c_wait_ack(void);
void i2c_ack(void);
void i2c_nack(void);#endif
// icc.h
#include "iic.h"extern void printf(const char *fmt, ...);
/** 函数名 : delay_us* 函数功能:延时函数* 函数参数:无* 函数返回值:无* */
void delay_us(void)
{unsigned int i = 2000;while (i--);
}
/** 函数名 : i2c_init* 函数功能: i2C总线引脚的初始化, 通用输出,推挽输出,输出速度,* 函数参数:无* 函数返回值:无* */
void i2c_init(void)
{// 使能GPIOF端口的时钟RCC->MP_AHB4ENSETR |= (0x1 << 5);// 设置PF14,PF15引脚为通用的输出功能GPIOF->MODER &= (~(0xF << 28));GPIOF->MODER |= (0x5 << 28);// 设置PF14, PF15引脚为推挽输出GPIOF->OTYPER &= (~(0x3 << 14));// 设置PF14, PF15引脚为高速输出GPIOF->OSPEEDR |= (0xF << 28);// 设置PF14, PF15引脚的禁止上拉和下拉GPIOF->PUPDR &= (~(0xF << 28));// 空闲状态SDA和SCL拉高I2C_SCL_H;I2C_SDA_H;
}/** 函数名:i2c_start* 函数功能:模拟i2c开始信号的时序* 函数参数:无* 函数返回值:无* */
void i2c_start(void)
{/** 开始信号:时钟在高电平期间,数据线从高到低的变化*     --------* SCL         \*              --------*     ----* SDA     \*          --------* */// 确保SDA是输出状态 PF15输出SET_SDA_OUT;// 空闲状态SDA和SCL拉高I2C_SCL_H;I2C_SDA_H;delay_us(); // 延时等待一段时间I2C_SDA_L;	// 数据线拉低delay_us(); // 延时等待一段时间I2C_SCL_L;	// 时钟线拉低,让总线处于占用状态
}/** 函数名:i2c_stop* 函数功能:模拟i2c停止信号的时序* 函数参数:无* 函数返回值:无* */void i2c_stop(void)
{/** 停止信号 : 时钟在高电平期间,数据线从低到高的变化*             ----------* SCL        /*    --------*    ---         -------* SDA   X       /*    --- -------* */// 确保SDA是输出状态 PF15输出SET_SDA_OUT;// 时钟线拉低I2C_SCL_L;delay_us(); // 延时等待一段时间I2C_SDA_L;	// 数据线拉低delay_us(); // 延时等待一段时间// 时钟线拉高I2C_SCL_H;delay_us(); // 延时等待一段时间I2C_SDA_H;	// 数据线拉高
}/** 函数名: i2c_write_byte* 函数功能:主机向i2c总线上的从设备写8bits数据* 函数参数:dat : 等待发送的字节数据* 函数返回值: 无* */void i2c_write_byte(unsigned char dat)
{/** 数据信号:时钟在低电平期间,发送器向数据线上写入数据*          时钟在高电平期间,接收器从数据线上读取数据*      ----          --------*  SCL     \        /        \*           --------          --------*      -------- ------------------ ---*  SDA         X                  X*      -------- ------------------ ---**      先发送高位在发送低位* */// 确保SDA是输出状态 PF15输出SET_SDA_OUT;unsigned int i;for (i = 0; i < 8; i++){// 时钟线拉低I2C_SCL_L;delay_us(); // 延时// 0X3A->0011 1010   0X80->10000000if (dat & 0X80) // 最高位为1{// 发送1I2C_SDA_H;}else // 最高位为0{I2C_SDA_L; // 发送0}delay_us(); // 延时// 时钟线拉高,接收器接收I2C_SCL_H;delay_us(); // 延时,用于等待接收器接收数据delay_us(); // 延时// 将数据左移一位,让原来第6位变为第7位dat = dat << 1;}
}/** 函数名:i2c_read_byte* 函数功能: 主机从i2c总线上的从设备读8bits数据,*          主机发送一个应答或者非应答信号* 函数参数: 0 : 应答信号   1 : 非应答信号* 函数返回值:读到的有效数据** */
unsigned char i2c_read_byte(unsigned char ack)
{/** 数据信号:时钟在低电平期间,发送器向数据线上写入数据*          时钟在高电平期间,接收器从数据线上读取数据*      ----          --------*  SCL     \        /        \*           --------          --------*      -------- ------------------ ---*  SDA         X                  X*      -------- ------------------ ---**      先接收高位, 在接收低位* */unsigned int i;unsigned char dat; // 保存接受的数据// 将数据线设置为输入SET_SDA_IN;for (i = 0; i < 8; i++){// 先把时钟线拉低,等一段时间,保证发送器发送完毕数据I2C_SCL_L;delay_us();delay_us(); // 保证发送器发送完数据// 时钟线拉高,读取数据I2C_SCL_H;delay_us();dat = dat << 1;	  // 数值左移  0000 0000if (I2C_SDA_READ) // pf15管脚得到了一个高电平输入{dat |= 1; // 0000 0110}else{dat &= (~0X1);}delay_us();}if (ack){i2c_nack(); // 发送非应答信号,不再接收下一次数据}else{i2c_ack(); // 发送应答信号}return dat;
}
/** 函数名: i2c_wait_ack* 函数功能: 主机作为发送器时,等待接收器返回的应答信号* 函数参数:无* 函数返回值:*                  0:接收到的应答信号*                  1:接收到的非应答信号* */
unsigned char i2c_wait_ack(void)
{/** 主机发送一个字节之后,从机给主机返回一个应答信号**                   -----------* SCL              /   M:读    \*     -------------             --------*     --- ---- --------------------* SDA    X    X*     ---      --------------------*     主  释   从机    主机*     机  放   向数据  读数据线*         总   线写    上的数据*         线   数据* */// 时钟线拉低,接收器可以发送信号I2C_SCL_L;I2C_SDA_H; // 先把数据线拉高,当接收器回应应答信号时,数据线会拉低delay_us();SET_SDA_IN; // 设置数据线为输入delay_us();delay_us();I2C_SCL_H;		  // 用于读取数据线数据if (I2C_SDA_READ) // PF15得到一个高电平输入,收到非应答信号return 1;I2C_SCL_L; // 时钟线拉低,让数据线处于占用状态return 0;
}
/** 函数名: iic_ack* 函数功能: 主机作为接收器时,给发送器发送应答信号* 函数参数:无* 函数返回值:无* */
void i2c_ack(void)
{/*            --------* SCL       /        \*    -------          ------*    ---* SDA   X*    --- -------------* */// 保证数据线是输出SET_SDA_OUT;I2C_SCL_L; // 拉低时钟线delay_us();I2C_SDA_L; // 数据线拉低,表示应答信号delay_us();I2C_SCL_H; // 时钟线拉高,等待发送器读取应答信号delay_us();delay_us();I2C_SCL_L; // 数据线处于占用状态,发送器发送下一次数据
}
/** 函数名: iic_nack* 函数功能: 主机作为接收器时,给发送器发送非应答信号* 函数参数:无* 函数返回值:无* */
void i2c_nack(void)
{/*            --------* SCL       /        \*    -------          ------*    --- ---------------* SDA   X*    ---* */// 保证数据线是输出SET_SDA_OUT;I2C_SCL_L; // 拉低时钟线delay_us();I2C_SDA_H; // 数据线拉高,表示非应答信号delay_us();I2C_SCL_H; // 时钟线拉高,等待发送器读取应答信号delay_us();delay_us();I2C_SCL_L; // 数据线处于占用状态,发送器发送下一次数据
}
// main.c
#include "si7006.h"void delay(int ms)
{int i, j;for (i = 0; i < ms; i++){for (j = 0; j < 2000; j++);}
}
int main()
{unsigned short hum;short tem;// 进行si7006的初始化si7006_init();while (1){// 读取湿度hum = si7006_read_hum_data(0X40, 0XE5);// 读取温度tem = si7006_read_temp_data(0X40, 0XE3);// 将温度数据和湿度数据按照转换公式进行转换hum = 125 * hum / 65536 - 6;tem = 175.72 * tem / 65536 - 46.85;delay(1000); // 延时打印// 将获取到的数据打印到串口printf("hum:%d\n", hum);printf("tem:%d\n", tem);}return 0;
}

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

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

相关文章

现代雷达车载应用——第2章 汽车雷达系统原理 2.4节 雷达波形和信号处理

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.4 雷达波形和信号处理 对于连续波雷达来说&#xff0c;波形决定了其基本信号处理流程以及一些关键功能。本节将以FMCW波形为例&#xff0c;讨论信号…

EasyRecovery2024苹果电脑mac破解版安装包下载

EasyRecovery是一款操作安全、价格便宜、用户自主操作的非破坏性的只读应用程序&#xff0c;它不会往源驱上写任何东西&#xff0c;也不会对源驱做任何改变。它支持从各种各样的存储介质恢复删除或者丢失的文件&#xff0c;其支持的媒体介质包括&#xff1a;硬盘驱动器、光驱、…

VS Code串口监视插件Serial Monitor

文章目录 初步使用参数设置VS Code插件 初步使用 Serial Monitor&#xff0c;即串行监视器&#xff0c;提供串口和TCP协议的通信监控功能。在插件栏搜索安装之后&#xff0c;按下Ctrl打开终端&#xff0c;终端界面会多出一个串行监视器选项卡&#xff0c;进入之后&#xff0c;…

golang反射(reflect)虽爽,但很贵

标准库 reflect 为 Go 语言提供了运行时动态获取对象的类型和值以及动态创建对象的能力。反射可以帮助抽象和简化代码&#xff0c;提高开发效率。 但是使用反射势必会多出大量的操作指令&#xff0c;导致性能下降 案例 字段赋值方式对比 type Student struct {Name string…

如何使用 Redis 快速实现分布式锁?

本文我们来讨论如何使用 Redis 快速实现分布式锁。 分布式锁有很多种解决方案&#xff0c;前面简单介绍过&#xff0c;Redis 可以通过 set key 方式来实现分布式锁&#xff0c;但实际情况要更加复杂&#xff0c;比如如何确保临界资源的串行执行&#xff0c;如何及时释放&#…

用Flask搭建简单的web模型部署服务

目录结构如下&#xff1a; 分类模型web部署 classification.py import os import cv2 import numpy as np import onnxruntime from flask import Flask, render_template, request, jsonifyapp Flask(__name__)onnx_session onnxruntime.InferenceSession("mobilen…

Tomcat部署Activiti官方 流程设计器【数据库更换为Mysql !!!】

一、官网下载activiti6 解压后结构如下: database&#xff1a; 存放数据库对象相关脚本&#xff0c;包含不同的数据库脚本 libs&#xff1a; 包含activiti开发过程中需要用到的jar包和源码&#xff0c;不建议通过jar包直接引用&#xff0c;建议通过maven进行管理 wars&am…

大模型应用_FastGPT

1 功能 整体功能&#xff0c;想解决什么问题 官方说明&#xff1a;FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01;个人体会…

ubuntu将本机的wifi网络通过网线分享给另一台机器(用于没有有线网络,重装系统后无wifi驱动或者另一台设备没有wifi网卡)

1.将两台机器通过网线连接 2.在pci ethernet中设置选择另一台机器的mac address&#xff0c;ipv4中选择share to other computer&#xff0c;另一台机器上设置为动态ip&#xff0c;连接上之后另一台机器即可上网。

大数据机器学习深度解读DBSCAN聚类算法:技术与实战全解析

大数据机器学习深度解读DBSCAN聚类算法&#xff1a;技术与实战全解析 一、简介 在机器学习的众多子领域中&#xff0c;聚类算法一直占据着不可忽视的地位。它们无需预先标注的数据&#xff0c;就能将数据集分组&#xff0c;组内元素相似度高&#xff0c;组间差异大。这种无监…

Github 2023-12-14开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2023-12-14统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量非开发语言项目5TypeScript项目2JavaScript项目1Jupyter Notebook项目1PHP项目1 基于项目的学习 创建周期&a…

Python进阶(一)

1.Python中一切皆对象 1.1 Python中一切皆对象 JAVA中有class和object这两个概念&#xff0c;object只是class的一个实例。 而在Python中面向对象更加的彻底&#xff0c;class和函数都是对象。代码也是对象&#xff0c;模块也是对象。 函数和类也是对象&#xff0c;对象有四…

AUTOSAR_SWS_LogAndTrace文档中文翻译

1 Introduction and functional overview 本规范规定了AUTOSAR自适应平台日志和跟踪的功能。 日志和跟踪为AA提供接口&#xff0c;以便将日志信息转发到通信总线、控制台或文件系统。 提供的每个日志记录信息都有自己的严重性级别。对于每个严重级别&#xff0c;都提供了一个单…

bugku--source

dirsearch扫一下 题目提示源代码&#xff08;source&#xff09; 也就是源代码泄露&#xff0c;然后发现有.git 猜到是git泄露 拼接后发现有文件 但是点开啥也没有 kali里面下载下来 wegt -r 下载网站的所有内容 ls 查看目录 cd 进入到目录里面 gie reflog 引用日志使用…

如何用python编写抢票软件,python爬虫小程序抢购

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python小程序抢购脚本怎么写&#xff0c;如何用python编写抢票软件&#xff0c;现在让我们一起来看看吧&#xff01; 大家好&#xff0c;小编来为大家解答以下问题&#xff0c;python小程序抢购脚本怎么写&#xff0c;如…

【洛谷算法题】P1422-小玉家的电费【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P1422-小玉家的电费【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…

diag_service的GLINK_IST是怎么来的

背景 平台&#xff1a;SA8155,QA 1.2.1 8155上集成了很多IP核&#xff0c;其中有不少的IP本质上是arm M核或者R核&#xff0c;这些模块在开发或者使用过程中也是需要监控和诊断的&#xff0c;但是他们并没有外部的调试接口&#xff0c;高通设计了整套诊断框架通过APSS&#x…

OpenHarmony应用开发——实现Toast提示功能-鸿蒙物联网应用开发-HarmonyOs应用开发

一、前言 本文我们将实现Toast样式的功能&#xff0c;以便于和用户进行简单、基本的信息交互。需要注意的是&#xff0c;本专栏&#xff08;OpenHarmony应用开发&#xff09;不阐述UI设计内容&#xff0c;而主要介绍大家开发中常遇到、常使用的功能问题&#xff0c;以及在物联网…

基于Dockerfile创建LNMP

实验组件 172.111.0.10&#xff1a;nginx docker-nginx 172.111.0.20&#xff1a;mysql docker-mysql 172.111.0.30&#xff1a;php docker-php 实验步骤 1.建立nginx-lnmp镜像及容器 cd /opt mkdir nginx cd nginx/ --上传nginx-1.22.0.tar.gz和wordpress-6.4.2-zh_C…

Android13适配所有文件管理权限

Android13适配所有文件管理权限 前言&#xff1a; 很早之前在Android11上面就适配过所有文件管理权限&#xff0c;这次是海外版升级到Android13&#xff0c;由于选择相册用的是第三方库&#xff0c;组内的同事没有上架Google的经验直接就提交代码&#xff0c;虽然功能没有问题…