STM中的I2C

常见的几种通信接口

I2C总线定义

定义

I2C - Inter-Integrated Circuit:两线式 串行总线:说明处理器和外设之间只需两根信号线,分别是SCL时钟控制信号线和SDA数据线

SCL(serial clock line)

时钟控制信号线,永远只能由CPU控制,用于实现数据的同步,就四个字:低放高取

  • SCL为低电平的时候将数据放到SDA数据线上
  • SCL为高电平的时候从数据线SDA上获取数据

SDA

数据线,用于传输数据,双方都可以控制
  • 如果处理器给外设发送数据,SDA由处理器控制
  • 如果外设给处理器发送数据,SDA由外设控制
注意:SCL和SDA必须要分别连接一个上拉电阻,所以他们默认的电平都是高电平

架构

    I2C总线基于主从架构 

  •     其中一个设备作为主设备(master)

            负责发起通信控制总线时序

  •     其它的设备为从设备(slave)

            负责响应和数据传输 

速率

  •     标准模式 - 100kbps
  •     快速模式 - 400kbps
  •     高速模式 - 3.4Mbps 

串行

由于数据线就一根SDA,必然是串行,又由于有时钟控制信号线SCL,所以数据传输是一个时钟周期传输一个bit位,I2C数据传输从高位开始,I2C数据传输一次传输一个字节,如果传输多个字节,需要分拆着来传
传输特点:
  • 一位一周期
  • 一次一字节
  • 传输从高位
  • 速度看时钟(SCL)
  • 时钟看外设

I2C总线的应用领域

I2C总线协议相关概念 

  • START信号:又称起始信号,此信号永远只能由CPU发起,表示CPU开始要访问外设

                              时序为:SCL为高电平,SDA由高电平向低电平跳变产生START信号

  • STOP信号:又称结束信号,此信号永远只能由CPU发起,表示CPU结束对总线的访问

                              时序为:SCL为高电平,SDA由低电平向高电平跳变产生STOP信号

  •  R/W读写位:用于表示CPU到底是向外设写入数据还是从外设读取数据

                             有效位数为1个bit位,CPU读取数据:R/W=1,CPU向外设写入数据:R/W=0

  • 设备地址:用于表示外设在总线上的唯一性,也就是同一个I2C总线上,不同的外设具有唯一的一个设备地址,也就是如果CPU要想访问某个外设,CPU只需向总线上发送这个外设的设备地址即可设备地址的有效位数为7位或者10位(极其少见),设备地址不包含读写位!
  • 设备地址由原理图和芯片手册共同来决定: 

 

I2C总线数据传输的流程(协议)

设备地址

  • 读设备地址=设备地址<<1|R/W=1
  • 写设备地址=设备地址<<1|R/W=0
问:为何需要读或者写设备地址呢?
答:I2C总线协议规定,数据传输一次一个字节(8位),而设备地址本身7位不够1字节,正巧R/W为一个bit位,所以报团取暖凑够一字节,将来CPU要想访问某个外设只需发送读或者写设备地址,即可找到这个外 设也可以告诉外设到底读还是写!一箭双雕!

ACK信号

又称应答信号,表示双方数据传输的反馈,有效位数为1个bit位,低电平有效

片内寄存器

切记:任何I2C外设芯片内部都集成了一堆的寄存器,此类寄存器又称片内寄存器
  • 这些寄存器同样也有地址,地址编号从0x00开始
  • 虽然这些寄存器都有唯一的地址,但是CPU不能直接以指针的形式访问, 必须要严格按照,读写时序进行访问

结论

CPU访问I2C外设本质就是访问I2C外设内部的寄存器!

所以I2C外设本身只需关注三点:
  • I2C外设片内寄存器的特性
  • I2C外设片内寄存器的基地址
  • I2C外设片内寄存器的读写时序

以CPU访问MMA8653三轴加速度传感器为例

读取单字节数据

写入单字节数据

读取多字节数据

写入多字节数据

 总结

AT24C02的访问操作

以CPU访问AT24C02存储器为例


写数据

 

读数据

时序图

AT24C02 

概况

AT24C02是一个2K位串行EEPROM, 内部含有256个8位字节的存储单元,掉电数据不丢失
AT24C02的存储容量分成32页,每页8Byte,共256Byte
AT24C02寻址范围为00~FF,共256个寻址单位

 硬件设计

SCL和SDA均接有上拉电阻,连接到STM32的PB6和PB7管脚上

 时序细节

IIC代码编写 

STM32中无直接调用IIC的底层库,需要手撸代码实现IIC的数据收发

初始化

void IIC_Init(void)
{// 1.打开SCL/SDA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);// 2.配置SCL - 推挽输出, 50MHzGPIO_InitTypeDef GPIO_Config;GPIO_Config.GPIO_Pin = IIC_SCL_PIN;GPIO_Config.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Config.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出GPIO_Init(IIC_SCL_PORT, &GPIO_Config);// 3.配置SDA - 推挽输出, 50MHzGPIO_Config.GPIO_Pin = IIC_SDA_PIN;GPIO_Config.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Config.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出GPIO_Init(IIC_SDA_PORT, &GPIO_Config);// 4.拉高SCL/SDA IIC_SDA = 1;IIC_SCL = 1;
}

配置SDA为推挽输出, 50MHz

// 配置SDA为推挽输出, 50MHz
static void SDA_OUT(void){GPIO_InitTypeDef GPIO_Config;GPIO_Config.GPIO_Pin = IIC_SDA_PIN;GPIO_Config.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Config.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出GPIO_Init(IIC_SDA_PORT, &GPIO_Config);
}

配置SDA为上拉输入

// 配置SDA为上拉输入
static void  SDA_IN(void){GPIO_InitTypeDef GPIO_Config;GPIO_Config.GPIO_Pin = IIC_SDA_PIN;GPIO_Config.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_Init(IIC_SDA_PORT, &GPIO_Config);
}

起始和终止条件

 开始步骤
  •     1.配置SDA为输出模式 
  •     2.配置SDA/SCL为高电平 
  •     3.保持至少4.7us
  •     4.拉低SDA
  •     5.保持至少4us 

    ------>已经完成发送开始信号

void  IIC_Start(void){SDA_OUT();// 配置SDA为输出模式 IIC_SCL = 1; // 时钟线拉高IIC_SDA = 1; // 数据线拉高 delay_us(6); // 延时6us, >=4.7us IIC_SDA = 0; // 拉低SDAdelay_us(6); // 延时6us, >= 4us//-------->发送了开始信号 IIC_SCL = 0; // 将SCL拉低,便于下一次数据数据
}
终止步骤
  •     1.配置SDA为输出模式
  •     2.配置SCL为高电平,SDA为低电平
  •     3.保持至少4us
  •     4.拉高SDA
  •     5.保持至少4.7us 
void IIC_Stop(void){SDA_OUT(); // 配置SDA为输出模式 IIC_SDA = 0; // 数据线拉低 IIC_SCL = 1; // 时钟线拉高 delay_us(6); // 延时6us IIC_SDA = 1; // 数据线拉高 delay_us(6); // 延时>4.7us 
}

处理ack

CPU等待ack
  • CPU等待外设发送ack信号 
  • 返回值 - 判断是否收到了ack
  • 收到ack ,0; 没收到ack, 返回1
u8 IIC_Wait_Ack(void){u32 tempTime = 0; // 等待的次数 // 将时钟拉低, 方便外设放入数据 IIC_SCL = 0; delay_us(6); // 保持6us SDA_IN(); // 配置输入模式 // 将时钟线拉高, 为了让CPU来读取数据 IIC_SCL = 1;delay_us(6); // 如何判断SDA的高低电平呢? - READ_SDA // 如果外设发送了ack, 发送低电平, SDA = 0// 如果外设没发送ack, 上拉输入,   SDA = 1while(READ_SDA){tempTime++;if(tempTime > 250){// 没有收到ack,结束传输 IIC_Stop(); return 1;// 没收到ack }}IIC_SCL = 0; //准备下一次数据传输return 0; // 收到ack
}
发送ack信号
void  IIC_Ack(void){IIC_SCL = 0; // 将SCL拉低SDA_OUT(); // 配置为输出模式 IIC_SDA = 0; // 将低电平放到SDA, 发送ackdelay_us(6); // 保持低电平的周期 IIC_SCL = 1; // 将时钟线拉高, 让外设在此时读取SDA数据 delay_us(6); IIC_SCL = 0; // 拉低准备下一次数据传输
}
发送nack信号
void  IIC_NAck(void){IIC_SCL = 0; // 将SCL拉低SDA_OUT(); // 配置为输出模式 IIC_SDA = 1; // 将高电平放到SDA, 发送nackdelay_us(6); // 保持低电平的周期 IIC_SCL = 1; // 将时钟线拉高, 让外设在此时读取SDA数据 delay_us(6); IIC_SCL = 0; // 拉低准备下一次数据传输
}

CPU收发数据 

CPU发送单字节
void IIC_Send_Byte(u8 TxData){u8 i;SDA_OUT(); // 配置为输出模式IIC_SCL = 0; // 为了将数据放到SDA上 for(i = 0; i < 8; i++){if(TxData & 0x80)IIC_SDA = 1;else IIC_SDA = 0;TxData <<= 1;delay_us(6); // 低电平的时钟周期 IIC_SCL = 1; // 拉高,让外设读delay_us(6); IIC_SCL = 0; }
}
CPU读取单字节
// 返回读取到的数据 
// 参数 :
// 		1, 回复ack信号; 
//		0, 回复nack信号;
u8 IIC_Read_Byte(u8 ack){u8 i = 0, data = 0; // data保存读取到的数据 SDA_IN(); // 配置为输入模式 for(i = 0; i < 8; i++){IIC_SCL = 0; // 拉低SCL为了让外设放入数据 delay_us(6);IIC_SCL = 1; // 拉高为了获取数据data |= READ_SDA << (7 - i);delay_us(6); }// 回复ack/nackif(!ack)IIC_NAck();elseIIC_Ack();return data; // 返回读取到的数据 
}

 AT24C02代码

// @file at24c02.c
#include "at24c02.h"
#include "iic.h"
#include "systick.h"
#include "stdio.h" // printfvoid AT24C02_Init(void){IIC_Init(); 
}// 参数:要读取的寄存器的地址
// 返回值 : 返回读取到的数据
// AT24C02_ID
u8   AT24C02_ReadByte(u16 ReadAddr){u8 temp;// 1.发送开始信号IIC_Start();// 2.发送写设备地址IIC_Send_Byte(AT24C02_ID << 1 | 0);// 3.等待ackIIC_Wait_Ack();// 4.发送要读取的寄存器地址IIC_Send_Byte(ReadAddr);// 5.等待ackIIC_Wait_Ack();// 6.发送开始信号IIC_Start();// 7.发送读设备地址IIC_Send_Byte(AT24C02_ID << 1 | 1);// 8.等待ackIIC_Wait_Ack();// 9.读取外设数据 + 回复nacktemp = IIC_Read_Byte(0);// 10.发送结束信号IIC_Stop();return temp;
}
// 功能 : 发送单字节数据
// 参数 : 
//		WriteAddr : 要写入的寄存器地址 
//		data : 要写入的数据
void AT24C02_WriteByte(u16 WriteAddr, u8 data){// 1.发送开始信号IIC_Start();// 2.发送写设备地址IIC_Send_Byte(AT24C02_ID << 1 | 0);// 3.等待ackIIC_Wait_Ack();// 4.发送要写入的寄存器地址IIC_Send_Byte(WriteAddr);// 5.等待ackIIC_Wait_Ack();// 6.发送要写入的数据IIC_Send_Byte(data);// 7.等待ackIIC_Wait_Ack();// 8.发送结束信号 IIC_Stop();
}
// 功能 : 读取多字节
// 参数 :
//	ReadAddr : 要读取的寄存器的首地址
//  pBuffer : 要读取数据存储的首地址
//	Len : 要读取的数据个数 
//  char buf[1024]; char* pBuffer = buf;
//  11 12 13 14  寄存器地址
void AT24C02_ReadBlockData(u16 ReadAddr, u8* pBuffer, u16 Len){while(Len){*pBuffer++ =  AT24C02_ReadByte(ReadAddr++);Len--;}
}
// 功能 : 写入多字节
// 参数 :
//	WriteAddr : 要写入的寄存器的首地址
//  pBuffer : 要写入数据存储的首地址
//	Len : 要写入的数据个数 
//  char buf[1024]; char* pBuffer = buf; 
//  buf数组 : xx xx xx xx
//  11 12 13 14  寄存器地址
void AT24C02_WriteBlockData(u16 WriteAddr, u8* pBuffer, u16 Len){while(Len){AT24C02_WriteByte(WriteAddr, *pBuffer);WriteAddr++;pBuffer++;Len--;}delay_us(20);
}// 测试函数 : 后续进行命令匹配使用 	
void AT24C02_ReadOne(void){// 读取地址0x00寄存器数据 printf("READ DATA : %#X\n", AT24C02_ReadByte(0x00));
}
void AT24C02_WriteOne(void){// 将数据0XAA写入到地址0x00寄存器中 AT24C02_WriteByte(0X00, 0XAA);
}
void AT24C02_ReadMul(void){u8 data[5] = {0};// 从地址0x00开始连续读取5个数据到data数组中 AT24C02_ReadBlockData(0x00, data, 5);// 打印输出 u8 i;for(i = 0; i < 5; i++)printf("ADDR[%d], DATA[%#x]\n", i, data[i]);
}
void AT24C02_WriteMul(void){u8 Data[5] = {1, 2, 3, 4, 5};// 将5个数据分别写入到地址0 1 2 3 4寄存器中 AT24C02_WriteBlockData(0x00, Data, 5);
}

实验结果

 通过串口工具向内部写入单字节,读取单字节

通过串口工具向内部写入多字节,读取多字节

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

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

相关文章

fastadmin 安装

环境要求&#xff0c;大家可以参考官方文档的&#xff0c;我这里使用的是phpstudy&#xff0c;很多已经集成了。 注意一点&#xff0c;PHP 版本&#xff1a;PHP 7.4 。 第二步&#xff1a;下载 下载地址&#xff1a;https://www.fastadmin.net/download.html 进入下载地址后…

【嵌入式裸机开发】智能家居入门7:最新ONENET,MQTT协议接入,最全最新(微信小程序、MQTT协议、ONENET云平台、STM32)

智能家居入门7 前言一、ONENET云平台创建产品与设备二、STM32端连接服务器前的准备三、STM32端实现四、微信小程序端连接服务器前的准备五、微信小程序端实现六、最终测试 前言 本篇文章介绍最新ONENET云平台的MQTT协议接入方法&#xff0c;在STM32上实现数据上云与服务器下发…

TCP和UDP编程的学习

UDP编程特点&#xff1a;c/s模型 不可靠 &#xff1b; 无链接 &#xff1b;数据报形式&#xff08;效率高&#xff09;&#xff1b; TCP编程特点&#xff1a; 面向链接&#xff1b; 可靠传输&#xff1b;&#xff08;保证数据准确可靠&#xff09;&#xff1b; 面向字节流&a…

基于x86 平台opencv的图像采集和seetaface6的口罩人脸检测识别功能

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 配置资源文件3.2 验证功能一、概述 本文档是针对x86 平台opencv的图像采集和seetaface6的口罩人脸检测识别功能,opencv通过摄像头采集视频图像,将采集的视频图像送给seetaface6的口罩人脸检测…

【blender】一个汉堡包

第一次建模&#xff0c;哈哈汉堡包样式大差不差。至于这么做的&#xff0c;那么多快捷键完蛋&#xff0c;忘得差不多了&#xff01; 官网地址&#xff1a;https://www.blender.org/download/ 我下载的是4.2版本

DHCP协议-CSP认证

文章目录 DHCP协议 DHCP协议 stringstream的用法 应用实践 #include<bits/stdc.h>using namespace std;signed main() {string s"wo shi wwl, also wlw";stringstream ss;ss<<s;while(ss>>s){if(s[s.size()-1],) s[s.size()-1] ;cout<<s<…

Excel的使用总结2

目录 1、数据的分割 2、根据某个条件筛选出来的数据&#xff0c;想要直接复制&#xff0c;并粘贴到与筛选数据对应的位置 3、几个单元格中的内容连接 4、如果一个表格数据很多&#xff0c;但是你想查看一列中是否有数据&#xff0c;如何查看 1、数据的分割 首先&#xff0c…

SwapPrompt(论文解读): Test-Time Prompt Adaptation for Vision-Language Models

2023(Neural IPS) 摘要 测试自适应 &#xff08;TTA&#xff09; 是无监督域自适应&#xff08;UDA&#xff09;中一种特殊且实用的设置&#xff0c;它允许源域中的预训练模型去适应另一个目标域中的未标记测试数据。为了避免计算密集型的骨干网络微调过程&#xff0c;因此利…

JavaScript(31)——内置构造函数

构造函数 构造函数是一种特殊的函数&#xff0c;主要用于快速初始化对象 用大写字母开头只能由new操作符来执行 function Stu(name, age) {this.name namethis.age age}const xiaom new Stu(小明, 18)const xiaoh new Stu(小红, 19)console.log(xiaom);console.log(xiaoh…

使用 Visual Studio 编辑器作为 DailyNotes 的 markdown 编辑器

DailyNotes 是我使用过的最优秀的日常笔记管理工具&#xff0c;为它配置一个好的 markdown 编辑器&#xff0c;可以大幅提升效率。 除了使用 Typora 作为 markdown 编辑器&#xff0c;Visual Studio Code 也是一个非常不错的选择&#xff0c;令人惊喜的是&#xff0c;它也支持…

ts枚举类型校验,提示枚举信息

ts 类型验证&#xff0c;提示枚举信息 const eventTypeList [MOUSE_MOVE, // 鼠标移动LEFT_CLICK // 左键单击// MOUSE_OUT, ] as consttype EventTypeList typeof eventTypeList[number]

Pod基础使用

POD基本操作 1.Pod生命周期 在Kubernetes中&#xff0c;Pod的生命周期经历了几个重要的阶段。下面是Pod生命周期的详细介绍&#xff1a; Pending&#xff08;待处理&#xff09;: 调度: Pod被创建后&#xff0c;首先进入“Pending”状态。此时&#xff0c;Kubernetes的调度器…

uniapp中 使用 VUE3 组合式API 怎么接收上一个页面传递的参数

项目是uniapp &#xff0c;使用了vue3 vite // 使用的组合式API 的 语法糖 <script setup> // 无法使用 onLoad <script> 使用不了下面方法获得上一个页面参数传递 onLoad(options){ } 解决方案1&#xff08;亲测Ok&#xff09;&#xff1a;消息通知与监听…

虹软科技25届校招笔试算法 A卷

目录 1. 第一题2. 第二题3. 论述题 ⏰ 时间&#xff1a;2024/08/18 &#x1f504; 输入输出&#xff1a;ACM格式 ⏳ 时长&#xff1a;2h 本试卷分为不定项选择&#xff0c;编程题&#xff0c;必做论述题和选做论述题&#xff0c;这里只展示编程题和必做论述题&#xff0c;一共三…

Win11搭建Angular开发环境

作为一名后端程序员&#xff0c;无论当前的工作是否需要&#xff0c;会一点点前端无疑对自己是有帮助的。今天就来介绍一下如何搭建Angular的开发环境。我也是摸着石头过河&#xff0c;所以很多东西也不熟悉&#xff0c;先按照Angular官网的介绍来配置吧。 这个是Angular最新版…

mmyolo训练模型报错:ValueError: Key img_path is not in available keys解决办法

使用mmyolo训练模型 的时候报错&#xff1a;ValueError: Key img_path is not in available keys. Traceback (most recent call last): File “tools/train.py”, line 123, in main() File “tools/train.py”, line 119, in main runner.train() File “/root/anaconda3/en…

erlang学习:erlang学习:书上案例22.6练习题3

初步实现了书上案例第二&#xff0c;三问的要求&#xff0c;对输出结果有部分偏差&#xff0c;没有实现对已完成任务状态的记录&#xff0c;因此已完成任务输出无论如何都是0&#xff0c;明天会在record中加一个字段进行已完成任务状态的记录 (2) 添加一个名为job_centre:stati…

服务器备份

服务器备份 一、方案 FreeFileSync freeSSHd Windows任务计划程序 FreeFileSync&#xff1a;设置文件备份方案&#xff08;双向同步、镜像同步、更新同步、自定义同步&#xff09;&#xff0c;适用于本地的文件同步之外&#xff0c;还支持 Google Driver、SFTP 和 FTP 三种…

泛微基于华为仓颉编程语言开发公文交换系统 推动办公软件全面国产化

2024年6月21日下午&#xff0c;华为终端BG软件部总裁龚体先生在华为开发者大会主题演讲《鸿蒙原生应用&#xff0c;全新出发&#xff01;》中向全球开发者介绍了华为自研仓颉编程语言&#xff0c;并发布了HarmonyOS NEXT仓颉语言开发者预览版。这是华为首次公开发布仓颉编程语言…

Windows—UDP编程

Client骨架&#xff1a; #include <iostream> #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib")int main() {//启动Winsock DLLWORD wVersionRequested MAKEWORD(2, 2);WSADATA lpWSAData;WSAStartup(wVersionRequested, &lpWSAData);//…