STM32学习笔记(十)--I2C、IIC总线协议详解

概述:Inter Integrated Circuit,一组多从 多组多从 有应答

是一种同步(具有时钟线需要同步时钟SCL)、串行(一位一位的往一个方向发送)、半双工(发送接收存在一种)通信总线。

(1)硬件电路 所有I2C设备的SCL连接在一起,SDA连在一起
                        设备的SCL和SDA均要配置成开漏输出模式
                        SCL跟SDA各添加一个上拉电阻,阻值一般为4.7K欧姆左右

                            

(2)I2C时序基本单元
起始条件;SCL高电平期间,SDA从高电平切换到低电平
终止条件:SCL高电平期间,SDA从低电平切换到高电平
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

(3)IIC 的基本的读写通讯过程

主机首先在 IIC 总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的 数据。主机接着发送从机地址+0(写操作)组成的 8bit 数据,所有从机接收到该 8bit 数据后,行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机 在总线上接收到有应答信号后,才能继续向从机发送数据。注意:IIC 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号

主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,
都是由主机发出起始信号,接着发送从机地址+1(
读操作)组成的 8bit 数据,从机接收到数据验证是否是自身的地址。那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回 8bit 数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。

(4)软件读取I2C 的代码程序部分(主要是如何写时序复现时序的代码)

void MyI2C_W_SCL(uint8_t BitValue)//写SCL时钟电平
{GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);Delay_us(10);
}void MyI2C_W_SDA(uint8_t BitValue)//写SDA数据电平
{GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);Delay_us(10);
}uint8_t MyI2C_R_SDA(void)//读SDA数据电平
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);Delay_us(10);return BitValue;
}void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//注意是开漏输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);一定要先拉高电平
}
//在SCL为低电平时 SDA可以改变电平状态 也就是 SCL拉低时 可写
//在SCL为高电平时 SDA可以不可以改变电平状态 也就是 SCL拉高时 可读
void MyI2C_Start(void)//时序开始;SDA要由高到低,SCL拉高 拉低为了可以改变数据SDA
{MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)//时序结束;SDA由低到高,SCL拉高
{MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1);
}
//如下函数为写入数据 要从最高位开始写入 每次写入往右移一直写到最低位
void MyI2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i ++){MyI2C_W_SDA(Byte & (0x80 >> i));//由于开始的时候把SCL拉低了 所以可以直接写MyI2C_W_SCL(1);//由于写完了一位 SCL立马拉高电平保证数据稳定写入MyI2C_W_SCL(0);//写入后 SCL变回低电平 利于循环遍历 方便下次改变SDA}
}
uint8_t MyI2C_ReceiveByte(void)
{uint8_t i, Byte = 0x00;MyI2C_W_SDA(1);//设置I2C总线上的SDA线为高电平,准备接收数据。for (i = 0; i < 8; i ++){MyI2C_W_SCL(1);//SCL线为高电平,表示开始读取一个位的数据if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
//条件语句判断SDA线的状态,如果为高电平,则将对应的位(根据循环次数)置为1,使用位操作符|=将对应的位设置为1。MyI2C_W_SCL(0);//表示结束当前位的数据读取}return Byte;
}
void MyI2C_SendAck(uint8_t AckBit)//写一位
{MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1);MyI2C_W_SCL(0);
}
uint8_t MyI2C_ReceiveAck(void)//读一位
{uint8_t AckBit;MyI2C_W_SDA(1);//准备接收数据MyI2C_W_SCL(1);//开始接收数据AckBit = MyI2C_R_SDA();MyI2C_W_SCL(0);//结束数据接收return AckBit;
}
以下为I2C读取MPU6050
#define MPU6050_ADDRESS		0xD0 //模块地址 查手册
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveAck();//应答MyI2C_SendByte(RegAddress);MyI2C_ReceiveAck();//应答MyI2C_SendByte(Data);MyI2C_ReceiveAck();//应答MyI2C_Stop();
}uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{uint8_t Data;MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);MyI2C_ReceiveAck();//应答MyI2C_SendByte(RegAddress);MyI2C_ReceiveAck();//应答MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS | 0x01);//1为读 0为写MyI2C_ReceiveAck();//应答Data = MyI2C_ReceiveByte();//函数用于接收应答信号,表示设备是否成功接收到地址。MyI2C_SendAck(1);//用于发送应答信号,表示继续读取下一个字节。MyI2C_Stop();return Data;
}void MPU6050_Init(void)
{MyI2C_Init();MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);MPU6050_WriteReg(MPU6050_CONFIG, 0x06);MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//用来开启工作状态
}uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{uint8_t DataH, DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ = (DataH << 8) | DataL;
}

以下为硬件读取I2C部分
配置步骤 (主要是使用引脚本身的IIC相关功能 而不像软件模拟通过IO方式)
1.开启RCC外设时钟 开启GPIO以及I2C外设
2.初始化GPIO 配置为复用开漏
3.配置I2C初始化结构体
4.开启I2C

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

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

相关文章

【调试笔记-20240618-Windows-pnpm 更新出现 Cannot find module 问题的解决方法】

调试笔记-系列文章目录 调试笔记-20240618-Windows-pnpm 更新出现 Cannot find module 问题的解决方法 文章目录 调试笔记-系列文章目录调试笔记-20240618-Windows-pnpm 更新出现 Cannot find module 问题的解决方法 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调…

qmt量化交易策略小白学习笔记第46期【qmt编程之期货行情数据--如何获取5档盘口行情、期货结算价与持仓量】

qmt编程之获取期货数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 感谢关注&#xff0c;咨询免费开通量化回测与获取实盘权限&#xff0c;欢迎和博主联系&#xff01; 获取5档盘口行情 …

【杂记-浅谈ICMP互联网控制消息协议】

ICMP互联网控制消息协议 一、ICMP协议概述1、定义2、工作原理3、报文格式 二、ICMP协议的应用场景1、Ping命令2、Traceroute命令3、错误报告4、MTU发现 三、ICMP协议的安全问题及防护措施1、安全威胁2、防护措施 一、ICMP协议概述 1、定义 ICMP&#xff0c;Internet Control …

GeoJson 地图地理信息数据获取

效果图&#xff1a; 获取渠道&#xff1a; 通过阿里数据可视化平台获取通过Vector Maps获取通过geojson来获取 1、通过阿里数据可视化平台获取 2、通过Vector Maps获取 3、通过geojson获取

lru_cache装饰器

LRU算法原理 LRU(Least Recently Used)算法是一种缓存淘汰策略。 用于在有限的缓存空间中管理数据对象。 lru_cache用法 Python的缓存&#xff08;lru_cache&#xff09;是一种装饰在被执行的函数上&#xff0c;将其执行的结果缓存起来&#xff0c;当下次请求的时候&#xf…

数据通信与网络(五)

交换机功能&#xff1a; 地址学习&#xff08;端口/MAC地址映射表&#xff09; 通信过滤&#xff08;基于端口/MAC地址映射表&#xff09; 生成树协议&#xff08;断开环路&#xff09; 隔离冲突域 生成树协议 隔离冲突域 交换机配置模式(用不同级别的命令对交换机进行配置) 普…

如何一步一步将Python中的应用打包成安卓的APK安装包文件

一、首先&#xff0c;按照如下链接操作 Python 应用打包成 APK【全流程】_python打包成apk-CSDN博客 二、运行 buildozer init会报错buildozer命令找不到&#xff0c;明明已经安装 解决方法&#xff1a; 这里重新创建一个conda环境 Installation — Buildozer 0.11 docum…

Oracle基本语法(SQLPlus)

目录&#xff1a; 前言&#xff1a; 准备工作&#xff1a; 登录&#xff1a; 1.打开SQL Plus命令行工具 第一种方式&#xff1a; 第二种方式&#xff1a; 2.以不同用户登录 SYSTEM&#xff08;普通管理员&#xff09;&#xff1a; SYS(超级管理员)&#xff1a; 不显示…

408计算机组成原理

todo:有逻辑的分门别类的整理笔记&#xff0c;方便复习 总 理解不了就直接背下来&#xff0c;学越多就越能理解 计算机系统概述 简要目录 基本概念 字长 MAR MDR PC IR CU ALU 通用寄存器、标志寄存器、标志控制器 ACC 地址译码器 通用寄存器 PU C语言编译过程 数据通路带…

DAY10-力扣刷题

1.最后一个单词的长度(简单) 58. 最后一个单词的长度 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子…

【顺序表】05 删除有序顺序表中值在s与t之间的数

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux算法题上机准备 &#x1f618;欢迎 ❤️关注 &#x1f44d;点赞 &#x1f64c;收藏 ✍️留言 题目 从有序顺序表中删除其值在给定值s与t之间(要求s<t)的所有元素&#xff0c;若s或t不合…

deepspeed win11 安装

目录 git地址: aio报错: 编译 报错 ops已存在: 修改拷贝代码: git地址: Bug Report: Issues Building DeepSpeed on Windows Issue #5679 microsoft/DeepSpeed GitHub aio报错: setup.py 配置变量 os.environ[DISTUTILS_USE_SDK]=1 os.environ[DS_BUILD_AIO]=…

Unity3d自定义TCP消息替代UNet实现网络连接

以前使用UNet实现网络连接,Unity2018以后被弃用了。要将以前的老程序升到高版本,最开始打算使用Mirro,结果发现并不好用。那就只能自己写连接了。 1.TCP消息结构 (1). TCP消息是按流传输的,会发生粘包。那么在发射和接收消息时就需要对消息进行打包和解包。如果接收的消息…

日期工具类:获取 当前周 | 上一周 | 下一周 的第一天(周一)和最后一天(周天)

问题背景 获取 当前周 | 上一周 | 下一周 的第一天&#xff08;周一&#xff09;和最后一天&#xff08;周天&#xff09;。 例如&#xff1a; 输入&#xff1a;2024-6-21, current 输出&#xff1a;{"firstDay": "2024-6-17","lastDay": &qu…

springboot代理配置,本地访问测试环境数据库、测试环境elasticsearch

springboot代理配置&#xff0c;本地访问测试环境数据库、测试环境elasticsearch 背景 本地项目启动需要直接连接测试环境的数据库、ES以及其他资源 配置 ES和其他一些资源的访问通过springboot配置走代理访问。比如我这里 EnableAsync EnableScheduling SpringBootApplic…

Nutch爬虫在大数据采集中的应用案例

引言 在当今信息爆炸的时代&#xff0c;大数据的价值日益凸显。网络作为信息的海洋&#xff0c;蕴藏着丰富的数据资源。Nutch&#xff0c;作为一个开源的Java编写的网络爬虫框架&#xff0c;以其高效的数据采集能力和良好的可扩展性&#xff0c;成为大数据采集的重要工具。本文…

Mac安装多个jdk环境(jdk8+jdk17)保姆级

Mac安装多个jdk环境&#xff08;jdk8jdk17&#xff09;保姆级 背景&#xff1a;新机安装开发环境发现需要找很多文章&#xff0c;&#xff0c;&#xff0c;&#xff0c;这里一篇文章安装所有环境 文章目录 Mac安装多个jdk环境&#xff08;jdk8jdk17&#xff09;保姆级&#x1f…

Lambda 表达式是为了解决啥问题,语法,使用规则,c++中的常用用法示例

2024/6/21 11:20:09 Lambda 表达式的主要目的是为了简化函数对象&#xff08;或称为函数符&#xff09;的创建和使用。传统的函数对象需要定义一个具名的类或者使用函数指针&#xff0c;而Lambda 表达式可以在需要时直接定义一个匿名函数&#xff0c;从而减少代码量和提高代码…

C语言入门系列:指针入门(超详细)

文章目录 一&#xff0c;什么是指针1&#xff0c;内存2&#xff0c;指针是什么&#xff1f; 二&#xff0c;指针的声明1&#xff0c;声明指针类型变量2&#xff0c;二级指针 三&#xff0c;指针的计算1&#xff0c;两个指针运算符1.1 *运算符1.2 & 运算符1.3 &运算符与…

树莓派3b+结合 tb6600用pi4j2程序库,用java编写控制软pmw的方式控制步进电机的转速,详细解析

结论:速度控制参数,主要的不是占空比,而是pmw针脚的输出主频参数:frequency。占空比对速度的影响也有,但比较小。 在进行速度详细控制之前,我们先读取电机和电机驱动器的几个参数。 电机:步进角 =1.8度 电流:1.7A 扭矩:0.55N.M 驱动器TB6600:查看面板的数值,并设置…