软件模拟IIC程序代码详解
文章目录
概述:
通过stm32模拟IIC协议读取传感器86BSD压力传感器的压力值和温度值数据,利用通信波形来深入理解IIC协议。
MCU-STM32F103,从设备地址0x28,利用逻辑分析仪进行波形分析。
1.IIC协议简介
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
1.1 IIC接口介绍
1.2 IIC总线特征
2. IIC总线协议详解
● IIC总线协议的构成
IIC总线协议构成主要构成如下图所示:

用逻辑分析仪捕捉的传输2Byte数据的IIC协议如下图所示:
下面依次分解。
2.1 起始条件
总线上必须以一个起始条件作为数据传输的起点,当SCL为高电平时,SDA由高电平变为低电平时为起始条件,如下图所示。

说明:起始信号和停止信号总是由主设备发出的。
2.2 从设备地址+R/W位
之前介绍过每个连接到IIC总线上的器件都有唯一的地址,主设备要想与从设备通信就必须先校验从设备地址,7bit从设备地址+1bit读/写位共同构成了1Byte的数据,这1Byte数据在起始条件之后,并在SCL的8个周期内将其放置在了SDA数据线上,如下图所示。

★★★注意:8bit地址数据的D0位位读/写位,D0为1时为主设备向从设备读模式,D0为0时为主设备向从设备写模式,上图中可以看出从设备地址为0x51(0101_0001),实际上该传感器设备的出厂地址为0x28(0010_1000),由此看出D7-D1为设备的出厂地址D0为1,所以此时的模式是向传感器读取数据,同时观察逻辑分析仪第三通道同样识别为Read模式。
2.3 ACK应答
2.4 数据传输
2.5 停止条件
总线上必须以一个停止条件作为数据传输的终点,当SCL为高电平时,SDA由低电平变为高电平时为停止条件,如下图所示。

说明:起始信号和停止信号总是由主设备发出的。
3.利用软件模拟IIC协议读取传感器内部的数据
3.1 起始条件
代码:
void I2C_START(void) {
/* 当SCL高电平时,SDA出现一个下降沿表示I2C总线启动信号 */ I2C_SDA_H; I2C_SCL_H; i2c_Delay(); I2C_SDA_L;//SDA先产生下降沿 i2c_Delay(); I2C_SCL_L; i2c_Delay(); }
3.2 停止条件
void I2C_STOP(void) {
/* 当SCL高电平时,SDA出现一个上升沿表示I2C总线停止信号 */ I2C_SDA_L; I2C_SCL_H; i2c_Delay(); I2C_SDA_H;//SDA先产生上升沿 }
3.3 主设备等待来自从设备的应答
//返回值:1:接收应答失败,0:接收应答成功 uint8_t I2C_Wait_Ack(void) {
uint8_t re; I2C_SDA_H; /* CPU释放SDA总线 */ i2c_Delay(); I2C_SCL_H; /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */ i2c_Delay(); if (I2C_SDA_STATE !=0) /* CPU读取SDA口线状态 */ {
re = 1; } else {
re = 0;//从设备主动将SDA拉低,则接收到应答 } I2C_SCL_L; i2c_Delay(); return re; }
3.4 主设备应答
代码:
void I2C_ACK(void) {
I2C_SDA_L; /* CPU驱动SDA = 0 */ i2c_Delay(); I2C_SCL_H; /* CPU产生1个时钟 */ i2c_Delay(); I2C_SCL_L; i2c_Delay(); I2C_SDA_H; /* CPU释放SDA总线 */ }
3.5 主设备非应答
代码:
void I2C_NACK(void) {
I2C_SDA_H; i2c_Delay(); I2C_SCL_H; i2c_Delay(); I2C_SCL_L; i2c_Delay(); }
3.6 时钟信号的产生
通过以上程序中的
i2c_Delay();
延时的使用产生了周期性的SCL时钟。
static void i2c_Delay(void) {
uint8_t i; //循环次数为10时,SCL频率 = 240KHz for (i = 0; i < 10; i++); }
3.7 发送一个字节
void I2C_SendByte(uint8_t _ucByte) {
uint8_t i; /* 先发送字节的高位bit7 */ for (i = 0; i < 8; i++) {
if (_ucByte & 0x80) {
I2C_SDA_H; } else {
I2C_SDA_L; } i2c_Delay(); I2C_SCL_H; i2c_Delay(); I2C_SCL_L; if (i == 7) {
I2C_SDA_H; // 释放总线 } _ucByte <<= 1; /* 左移一个bit */ i2c_Delay(); } }
3.8 接收一个字节并作出应答
uint8_t I2C_ReceiveByte(u8 ack) {
uint8_t i; uint8_t value; /* 读到第1个bit为数据的bit7 */ value = 0; for (i = 0; i < 8; i++) {
value <<= 1; I2C_SCL_H; i2c_Delay(); if (I2C_SDA_STATE !=0) {
value++; } I2C_SCL_L; i2c_Delay(); } if(ack==0) I2C_NACK(); else I2C_ACK(); return value; }
4. IIC实践
————–手动分割————————-以下为主设备对从设备一次数据的获取流程————-这才是重点↓———————-
数据提取
模式1:
测试传感器是否应答,地址信息D0为1(主设备向从设备读数据)。

主机发送从设备地址后等待应答:
void _86BSD_ReadData(unsigned char*Read) {
unsigned char i; I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack();//等待从机响应 I2C_STOP(); }
模式2: 主设备向从设备读取两个字节的数据。

void _86BSD_ReadData(unsigned char*Read) {
unsigned char i; I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack(); //等待从机响应 /*气压*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(0);//0 主机非应答 I2C_STOP(); }
模式3: 主设备向从设备读取三个字节的数据。

I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack();//等待从机应答 /*气压*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(0);//0 主机非应答 I2C_STOP();
逻辑分析仪捕捉波形:
观察波形,从机收到地址信息后产生应答ACK,随之返回数据0x1E,主机收到数据后产生应答ACK,从机又返回数据0x1C,主机收到数据后产生应答ACK,从机又返回数据0x64,主机收到数据后不再需要从机的数据所以产生非应答NACK。
模式4: 主设备向从设备读取四个字节的数据。

void _86BSD_ReadData(unsigned char*Read) {
unsigned char i; I2C_START(); I2C_SendByte(0x51);//从机的地址 //0101 0001 I2C_Wait_Ack(); /*气压*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; /*温度*/ *Read=I2C_ReceiveByte(1);//1 主机应答 Read++; *Read=I2C_ReceiveByte(0);//0 主机非应答 I2C_STOP(); }
★★★如有错误欢迎指导。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/212877.html原文链接:https://javaforall.net
