当前位置: 首页 > ai >正文

嵌入式软件--stm32 DAY7 I2C通讯上

iic协议由飞利浦公司开发,是非常常用的一种通信协议,拥有两条总线的他支持多个设备共用同一条总线。常用于oled屏幕的外设驱动和手机PD协议通信中。了解和掌握iic协议是十分有必要的。

1.IIC概念 

1.1 IIC的两条线和协议细节

双向两线制,同步半双工

1.1.1 三种速率模式

标准模式 100kbps

快速模式 400kbps

高速模式 3.4Mbps

不占用总线,上拉电阻把总线拉成高电平。 

一般使用开漏模式而不是用推挽输出的原因是,避免多设备间冲突(多个设备共用同一条主线,如果有高有低,就会造成冲突),实现多主仲裁。

1.1.2协议细节 

起始信号:SCL保持高电平,SDA下降沿(电平由高到低变换)

数据位:SCL保持高电平,SDA维持稳定。

终止信号:SCL保持高电平,SDA上升沿(电平由低到高变换)

确认信号:IIC以字节为单位传输数据,每传输一个字节,都要向发送方发送一个确认信号。ACK NACK

数据的有效性:SDA高低电平在SCL时钟信号是低电平时才能改变,SDA线上的数据必须在SCL高电平周期保持稳定。

响应:接收方发送数据后要给发送方响应,应答响应和非应答响应。

应答响应ACK:给发送方一个低电平,非应答响应NACK:给发送方一个高电平。 

2. iic案例1:软件模拟IIC

2.1 EEPROM

型号:M24C02

M24C02的scl和sda与stm32的iic引脚相连,结合上拉电阻,构成IIC总线,通过iic进行交互。

eeprom芯片的设备地址一共七位,高四位固定1010,低三位则由E3/E2/E1信号线决定eeprom的设备地址。

R/W是读写方向控制位,与地址无关。

从设备7位地址+读写控制位

 1 0 1 0 E3 E2 E1 R/W

2.2操作时序图

2.2.1单字节写入

写入一个字节,一个字节一个字节的写入。

start 从设备地址+写操作位  ACK 写入字节地址 ACK 写入字节 ACK stop

2.2.2单字节读出

读出一个字节时序:

start 从设备地址+写操作位 ACK 写入字节地址 ACK start 从设备地址+读操作位 ACK 读字节 NACK(不应答,发送方就不会继续发) stop

单次写入多个字节时序:

start 从设备地址+写操作位 ACK 写入字节地址 ACK 写入字节1 ACK 写入字节2 ACK ····写入字节N ACK stop  

多字节写入叫页写,页写一次最多写入16个字节。超过16个字节,eeprom会重新第一个字节覆盖写入。

读的时候,要写一个字节再读一个字节,这叫假写真读。写的字节是地址,真正读的是数据。读字节没有限制,可以读取任意多个。

假写真读多个字节序:发start信号 设备地址+写操作位 ACK 写入内部地址 ACK  发start信号 设备地址加读操作位 ACK 读字节1 ACK 读字节2 ACK... 读字节N NO NACK STOP信号 

2.3 代码实战IIC

软件模拟IIC,通过向EEPROM发送一段数据,再读取出来,最后发送到串口,核对是否读写正确。

复制上一个项目文件夹,重命名为iic_software_register,意为iic软件模拟寄存器写法。

删除几个文件,只留下基本文件start,user,hardware和keil源文件.在hardware中添加文件夹IIC,新建两个文件iic.c和iic.h。

涉及到读写的EEPROM放到interface,建立两个文件m24c02.h,m24c02.c。

在keil里面将.c文件和.h文件路径添加到位。

 

关联vscode:

这是寄存器写法,用了PB10,PB11两个引脚。

通信过程:

在.h文件里写预处理函数和函数名。

加上重定向函数,方便打印看结果。

2.3.1 iic初始化配置

//初始化
void I2C_Init(void)
{//1.配置时钟RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;//2.GPIO配置,PB10\PB11通用开漏输出,MODE-11,CNF-01GPIOB->CRH|=(GPIO_CRH_MODE10|GPIO_CRH_MODE11);GPIOB->CRH&=~(GPIO_CRH_CNF10_1|GPIO_CRH_CNF11_1);GPIOB->CRH|=(GPIO_CRH_CNF10_0|GPIO_CRH_CNF11_0);
}

2.3.2 IIC时序

(1)起始信号 SCL处于高电平,SDA下降沿,先拉高后拉低

//主机发出起始信号
void I2C_Start(void)
{//1.拉高SCL/SDASCL_HIGH;SDA_HIGH;I2C_DELAY;//2.拉低SDA,产生下降沿SCL_LOW;I2C_DELAY;}

(2)SDA低,SCL高,SDA上升沿

//主机发出停止信号
void I2C_Stop(void)
{//1.拉高SCL,拉低SDASCL_HIGH;SDA_LOW;I2C_DELAY;//2.拉高SDA,产生上升沿SDA_HIGH;I2C_DELAY;
} 

(3)接收应答非应答信号

《1》拉高SDA,拉低SCL,等待SDA反转

《2》SDA拉低,翻转数据,输出应答信号

《3》SCL拉高,保持SDA信号,做数据采样

《4》SCL拉低,数据采样结束

《5》SDA拉高,应答输出结束,释放SDA

//主机发送应答/非应答信号
void I2C_ACK(void)
{//1.拉高SDA/拉低SCL,准备反转SDASDA_HIGH;SCL_LOW;I2C_DELAY;//《2》SDA拉低,翻转数据,输出应答信号
SDA_LOW;
I2C_DELAY;
//《3》SCL拉高,保持SDA信号,做数据采样
SCL_HIGH;
I2C_DELAY;
//《4》SCL拉低,数据采样结束
SCL_LOW;
I2C_DELAY;
//《5》SDA拉高,应答输出结束,释放SDA总线
SDA_HIGH;
I2C_DELAY;
}

(4)接收非应答

 

//1.拉低SCL,拉高SDA,准备发送非应答信号

//2.保持SDA,拉高SCL信号,做数据采样

//3.保持SDA,拉低SCL,数据采样结束

void I2C_NACK(void)
{//1.拉低SCL,拉高SDA,准备发送非应答信号
SCL_LOW;
SDA_HIGH;
I2C_DELAY;
//2.保持SDA,拉高SCL信号,做数据采样
SCL_HIGH;
I2C_DELAY;
//3.保持SDA,拉低SCL,数据采样结束
SCL_LOW;
I2C_DELAY;
}

(5)

//主机从SDA读取响应信号,等待应答
uint8_t I2C_Wait4ACK(void)
{//1.拉高SDA/拉低SCL,准备反转SDASDA_HIGH;SCL_LOW;I2C_DELAY;//2.SCL拉高,保持SDA信号,做数据采样
SCL_HIGH;
I2C_DELAY;//3.读取SDA上的电平
uint8_t ack=READ_SDA;//4.SCL拉低,数据采样结束
SCL_LOW;
I2C_DELAY;
return ack?NACK:ACK;
}

(6)

//主机发送一个字节
void I2C_SendByte(uint8_t byte)
{for(uint8_t i=0;i<8;i++){//1.SCL拉低,等待可能的反转SDA信号,准备发送数据SCL_LOW;I2C_DELAY;//2.将要发送的位传到SDA上,MSB先行if(byte&0x80)// 10000000 取最高位{SDA_HIGH;}else{SDA_LOW;}I2C_DELAY;//3.SCL拉高,保持SDA信号,让从设备做数据采样SCL_HIGH;I2C_DELAY;//4.SCL拉低,数据采样结束SCL_LOW;I2C_DELAY;//5.左移一位,为下一次发送做准备byte<<=1;}
}//主机从SDA读取一个字节
uint8_t I2C_ReadByte(void)
{//定义变量,用来保存读取的字节uint8_t data=0;for (size_t i = 0; i < 8; i++){//1.拉高SDA,拉低SCL,释放SDA总线SDA_HIGH;SCL_LOW;I2C_DELAY;//2.SCL拉高,保持SDA信号,做数据采样SCL_HIGH;I2C_DELAY;//3.先左移一位,为下次接收做准备data<<=1;//4.读取SDA上的电平,放到数据的最低位if (READ_SDA){data|=0x01;  }//5.SCL拉低,数据采样结束SCL_LOW;I2C_DELAY;}}

2.4 接口层

.h文件

2.4.1 初始化

接口层没有什么可以初始化的,只要收发逻辑完成即可,因此只要IIC初始化即可。

2.4.2 写入字节,字写和页写

 

2.4.3 读取字节

假写真读的过程

连续多个字节读取

//连续读取多个字节(连续读)
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size)
{//1.发出开始信号I2C_Start();//2.发送写地址(假写)I2C_SendByte(W_ADDR);//3.等待应答I2C_Wait4ACK();//4.发送内部地址I2C_SendByte(innerAddr);//5.等待应答I2C_Wait4ACK();//6.发出开始信号I2C_Start();//7.发送读地址(真读)I2C_SendByte(R_ADDR);//8.等待应答I2C_Wait4ACK();for (uint8_t i = 0; i < size; i++){//9.循环读取一个字节的数据bytes[i]= I2C_ReadByte();//10.如果没结束,发送一个应答;如果最后一个,就给非应答if(i<size-1){I2C_ACK();}else{I2C_NACK();}}//11.发送结束信号I2C_Stop();return bytes;
}

之前未考虑到,写入和读取字节,EEPROM有自己的耗时,需要5ms.在之前的代码中自行添加。

2.4.4 应用测试:读取单字节

#include "usart.h"
#include "delay.h"
#include "m24c02.h"int main(void)
{//1.初始化USART_Init();M24C02_Init();printf("WO SHI CHINESE!\n");//2.写入单个字符M24C02_WriteByte(0x00,'a');M24C02_WriteByte(0x01,'b');M24C02_WriteByte(0x02,'c');//3.读取单个字符uint8_t byte1=M24C02_ReadByte(0x00);uint8_t byte2=M24C02_ReadByte(0x01);uint8_t byte3=M24C02_ReadByte(0x02);//4.串口输出打印printf("byte1=%c\n",byte1);printf("byte2=%c\n",byte2);printf("byte3=%c\n",byte3);while(1){}}
#include "usart.h"
#include "delay.h"
#include "m24c02.h"
#include <string.h>int main(void)
{//1.初始化USART_Init();M24C02_Init();printf("WO SHI CHINESE!\n");//2.写入单个字符M24C02_WriteByte(0x00,'a');M24C02_WriteByte(0x01,'b');M24C02_WriteByte(0x02,'c');//3.读取单个字符uint8_t byte1=M24C02_ReadByte(0x00);uint8_t byte2=M24C02_ReadByte(0x01);uint8_t byte3=M24C02_ReadByte(0x02);//4.串口输出打印printf("byte1=%c\n",byte1);printf("byte2=%c\n",byte2);printf("byte3=%c\n",byte3);//5.页写M24C02_WriteBytes(0x00,"123456",6);//6.读取多个字符uint8_t buff[50];M24C02_ReadBytes(0x00,buff,6);//7.串口输出打印printf("buff=%s\n",buff);//清除缓冲区memset(buff,0,sizeof(buff));//长度超出16的页写M24C02_WriteBytes(0x00,"1234567890123456",20);//如果从0X05开始读,就可以读到尾M24C02_ReadBytes(0x00,buff,20);printf("buff=%s\n",buff);while(1){}}

http://www.xdnf.cn/news/5895.html

相关文章:

  • 码蹄集——分解、数组最大公约数、孪生质数、卡罗尔数、阶乘数
  • PY32系列单片机离线烧录器,可配置选项字节和上机台批量烧录
  • The Deep Learning Compiler: A Comprehensive Survey (深度学习编译器:全面调查)
  • milvus+flask山寨《从零构建向量数据库》第7章case2
  • FPGA图像处理(六)------ 图像腐蚀and图像膨胀
  • 【图像处理基石】遥感图像分析入门
  • stm32f103rct6中使用串口1 DMA通信程序含异常处理
  • 数据验证库pydantic的用法
  • 力扣热题——统计平衡排列的数目
  • 进程间通信分类
  • 数组练习题
  • 采购流程规范化如何实现?日事清流程自动化助力需求、采购、财务高效协作
  • 动态查找滚动容器(通用方案)
  • 故障诊断模型评估——混淆矩阵,如何使样本量一致(上)
  • 深入浅出之STL源码分析8_三个指针
  • PostgreSQL 恢复信息函数
  • Android Exoplayer多路不同时长音视频混合播放
  • window 显示驱动开发-报告图形内存(一)
  • ElasticSeach快速上手笔记-入门篇
  • VScode 的插件本地更改后怎么生效
  • 嵌入式学习--江协51单片机day5
  • React百日学习计划——Deepseek版
  • 销量预测评估指标
  • 【工作记录】Kong Gateway 入门篇之部署及简单测试
  • mpegts.js 播放flv视频报错:PIPELINE_ERROR_DECODE: video decode error!
  • FlashInfer - 安装
  • 理解 Open vSwitch (OVS)
  • 一键转换上百文件 Word 批量转 PDF 软件批量工具
  • 大模型——Crawl4AI为 LLM 和 RAG 准备高质量网页数据
  • ‌Element UI 双击事件(@cell-dblclick 与 @row-dblclick)