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

小实战项目-第二章2.1-IIC协议讲解? 什么是软件IIC 什么是硬件IIC 有什么区别如何编写代码--这章节主要讲解软件IIC,下一章节讲解硬件IIC协议

这篇讲解软件IIC和一些基础,硬件IIC在下一篇章,添加链接描述
https://blog.csdn.net/qq_46187594/article/details/141643049

第二章-IIC协议

2.1-协议概述

说IIC特点

IIC时序图例子

或者仿真图

2.2-软件模式IIC

2.2.1-STM32模拟I2C

SDA必须设置开漏输出+硬件上拉电阻

SCL可以开漏输出+硬件上拉电阻 也可以推挽输出

如何通过STM32 io 模拟出I2C时序

我们这里还使用第0章的工程

在这里插入图片描述

设置上拉输入开漏输出、然后硬件也要上拉电阻

防止优化,我们要关闭优化选项

在这里插入图片描述

IIC.c 文件

#include "MyApp.h"
//讲IIC通信引脚设置为开漏上拉输出,引脚还要连接上拉电阻//SDA电平设置
#define SDA_High	HAL_GPIO_WritePin(GY30_SDA_GPIO_Port,GY30_SDA_Pin,GPIO_PIN_SET)
#define SDA_Low		HAL_GPIO_WritePin(GY30_SDA_GPIO_Port,GY30_SDA_Pin,GPIO_PIN_RESET)	//SCL电平设置
#define SCL_Hige	HAL_GPIO_WritePin(GY30_SCL_GPIO_Port,GY30_SCL_Pin,GPIO_PIN_SET)
#define SCL_Low		HAL_GPIO_WritePin(GY30_SCL_GPIO_Port,GY30_SCL_Pin,GPIO_PIN_RESET)//STM32在开漏输出模式可以直接读GPIO 电平
#define SDA_Read    HAL_GPIO_ReadPin(GY30_SDA_GPIO_Port, GY30_SDA_Pin)/* Private variables----------------------------------------------------------*//* Public variables-----------------------------------------------------------*/
/* Private function prototypes------------------------------------------------*/ 
/*******************
*  @brief  这是us 级延时函数
*  @param  
*  @return  
*
*******************/
void I2C_Delay_us(uint8_t us)
{uint8_t i = 0;while(us --){for(i = 0;i<7;i++);}
}
/*******************
*  @brief  起始信号S
*  @param  
*  @return  
*
*******************/
void I2C_Start(void)
{SCL_Hige;SDA_High;I2C_Delay_us(5);SDA_Low;I2C_Delay_us(5);SCL_Low;
}
/*******************
*  @brief  停止信号
*  @param  
*  @return  
*
*******************/
void I2C_Stop(void)
{SDA_Low;I2C_Delay_us(5);SCL_Hige;I2C_Delay_us(5);SDA_High;}
/*******************
*  @brief  读是否有应答
*  @param  
*  @return  1-没有收到应答位,0-收到应答位
*
*******************/
uint8_t I2C_Read_Ack(void)
{uint8_t AckTime = 0;//超时计数SCL_Hige;//先拉高SCL、SCL为高的时候检测SDAI2C_Delay_us(5);while(SDA_Read){if(++AckTime > 250){//超时判断 如果长时间没有应答I2C_Stop();//发送停止信号return 1;//退出}}SCL_Low;I2C_Delay_us(4);return 0;	//
}
/*******************
*  @brief  写一个字节数据
*  @param  要写的一个字节
*  @return  
*
*******************/
void I2C_Write_Byte(uint8_t Data)
{SCL_Low;//设置SCL低电平 ,允许SDA改变I2C_Delay_us(4);for(uint8_t i = 0;i<8;i++){if((Data << i) & 0x80) SDA_High;//循环发送,先发送高位 ,然后发低位else SDA_Low;SCL_Hige;//设置SCL为高,后面要保持SDA不变I2C_Delay_us(4);SCL_Low;I2C_Delay_us(4);}
}
/*******************
*  @brief  读一个字节数据
*  @param  
*  @return  返回读的数据
*
*******************/
uint8_t I2C_Read_Data(void)
{uint8_t Data;//暂存读的数据for(uint8_t i=0;i<8;i++){SCL_Hige;//设置SCL高,延时后SDA稳定 从SDA读数据I2C_Delay_us(4);Data <<= 1;	//收的数据先存低位,然后用这个左移8次if(SDA_Read){//如果SDA为高电平就置位Data |= 0x01;//就置为最后一位}SCL_Low;//拉低SCL,期间SDA可能变化I2C_Delay_us(4);}return Data;//返回读的数据
}
/*******************
*  @brief  发送应答或者非应答信号
*  @param  1- 非应答、0- 应答
*  @return  无
*
*******************/
void Sende_Ack(uint8_t Ack)
{if(Ack)SDA_High;//如果输入1 SDA设置为高else SDA_Low;SCL_Hige;//SCL设置为高保持4毫秒I2C_Delay_us(4);SCL_Low;//然后释放掉I2C_Delay_us(4);
}/********************************************************End Of File
********************************************************/

I2C.h

#ifndef __I2C_H__
#define __I2C_H__#include "MyApp.h"
/* extern variables-----------------------------------------------------------*//* extern function prototypes-------------------------------------------------*/void I2C_Delay_us(uint8_t us);
/*******************
*  @brief  起始信号S
*  @param  
*  @return  
*
*******************/
void I2C_Start(void);
/*******************
*  @brief  停止信号
*  @param  
*  @return  
*
*******************/
void I2C_Stop(void);
/*******************
*  @brief  读是否有应答
*  @param  
*  @return  1-没有收到应答位,0-收到应答位
*
*******************/
uint8_t I2C_Read_Ack(void);
/*******************
*  @brief  写一个字节数据
*  @param  要写的一个字节
*  @return  
*
*******************/
void I2C_Write_Byte(uint8_t Data);
/*******************
*  @brief  读一个字节数据
*  @param  
*  @return  返回读的数据
*
*******************/
uint8_t I2C_Read_Data(void);
/*******************
*  @brief  发送应答或者非应答信号
*  @param  1- 非应答、0- 应答
*  @return  无
*
*******************/
void Sende_Ack(uint8_t Ack);
#endif/********************************************************End Of File
********************************************************/

2.2.2模拟I2C驱动传感器

光照传感器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

GY30.c

#include "MyApp.h"#define GY30	0xB8
//ADDR接高地址为OxB8(1011100)然后写就是最后一位0 就是0xB8 如果读就最后一位1 就是0xB9
//ADDR接低地址为0x46(0100011)然后写操作最后一位是0 就是0x46 如果是读最后一位1 就是0x47/* Private variables----------------------------------------------------------*//* Public variables-----------------------------------------------------------*/uint8_t DATA[2];/* Private function prototypes------------------------------------------------*/ 
/*******************
*  @brief  写一个Byte
*  @param  
*  @return  
*
*******************/
void GY30_Write_Byte(uint8_t Byte){I2C_Start();//主机发送起始信号I2C_Write_Byte(GY30|0x00);//发送地址+0表示写 0xB8I2C_Read_Ack();//主机等待收到应答信号I2C_Write_Byte(Byte);//主机写数据过去I2C_Read_Ack();//主机等待从机的应答信号I2C_Stop();//主机发送停止信号}
/*******************
*  @brief  读光照数据
*  @param  
*  @return  
*
*******************/
void GY30_Read_Byte(void){I2C_Start();//发送起始信号I2C_Write_Byte(GY30|0x01);//0xB9发送过去,表示地址0XB8和1(读数据)I2C_Read_Ack();//主机等待收到应答信号for(uint8_t i = 0;i<2;i++){DATA[i] = I2C_Read_Data();//读数据保持if(i == 2)  			///觉得这里有问题,I2C_Sende_Ack(1);//如果读两个数据 主机发送不应答elseI2C_Sende_Ack(0);}I2C_Stop();//主机发送停止信号HAL_Delay(5);//延时5ms 时间
}
/********************************************************End Of File
********************************************************/

GY30.h

#ifndef __GY30_H__
#define __GY30_H__
#include "MyApp.h"/* extern variables-----------------------------------------------------------*//* extern function prototypes-------------------------------------------------*//*******************
*  @brief  写一个Byte
*  @param  
*  @return  
*
*******************/
void GY30_Write_Byte(uint8_t Byte);
/*******************
*  @brief  读光照数据
*  @param  
*  @return  
*
*******************/
void GY30_Read_Byte(void);#endif

补充一个点

在这里插入图片描述

添加一下串口的初始化

在这里插入图片描述

增加映射

在这里插入图片描述

/**
* @brief 重定向printf (重定向fputc),使用时候记得勾选上魔法棒->Target->UseMicro LIB 可能需要在C文件加typedef struct __FILE FILE;包含这个文件#include "stdio.h"
* @param 
* @return 
*/
int fputc(int ch,FILE *stream)
{HAL_UART_Transmit(&huart1,( uint8_t *)&ch,1,0xFFFF);return ch;
}

勾选微库

在这里插入图片描述

调试之后发现无法读出光照数据、光照高位和低位都是0 ,然后我们猜测是延时函数不准确,下面测试延时函数、

在这里插入图片描述

在这里插入图片描述

把延时函数调整一下

在这里插入图片描述

这里接接好传感器还是不能读出数据

我们烧录一下历程,然后看一下例程的波形

三个部分的波形

在这里插入图片描述

第一部分波形

波形就是:起始位+从机地址(0xB8)+从机ACK+通电指令(0X01)+从机ACK+SP停止

然后下一个:起始位+从机地址(0XB8)+从机ACK+通电指令(0X10)+从机ACK+SP停止

在这里插入图片描述

第二部分

起始信号+0xB9(地址0xB9+1这个表示读)+光照高八位+从机ACK+光照低八位+从机ACK

在这里插入图片描述

我们把没有问题的程序数据和有问题的都采集到逻辑分析仪上
这是有问题的波形

在这里插入图片描述

这是没有问题的波形

在这里插入图片描述

2.2.3-切换状态解决问题

还是没有发现问题,我们把IO设置推挽输出,然后在需要设置输入的时候再切换模式
设置GPIO状态

在这里插入图片描述

代码增加了SDA状态改变,我把所有代码贴出来了

iic.c

#include "MyApp.h"
//讲IIC通信引脚设置为开漏上拉输出,引脚还要连接上拉电阻//SDA电平设置
#define SDA_High	HAL_GPIO_WritePin(GY30_SDA_GPIO_Port,GY30_SDA_Pin,GPIO_PIN_SET)
#define SDA_Low		HAL_GPIO_WritePin(GY30_SDA_GPIO_Port,GY30_SDA_Pin,GPIO_PIN_RESET)	//SCL电平设置
#define SCL_Hige	HAL_GPIO_WritePin(GY30_SCL_GPIO_Port,GY30_SCL_Pin,GPIO_PIN_SET)
#define SCL_Low		HAL_GPIO_WritePin(GY30_SCL_GPIO_Port,GY30_SCL_Pin,GPIO_PIN_RESET)//STM32在开漏输出模式可以直接读GPIO 电平
#define SDA_Read    HAL_GPIO_ReadPin(GY30_SDA_GPIO_Port, GY30_SDA_Pin)/* Private variables----------------------------------------------------------*//* Public variables-----------------------------------------------------------*/
/* Private function prototypes------------------------------------------------*/ 
//软件先设置GPIO为推挽输出、然后SDA读的时候设置
/*******************
*  @brief  设置IIC的模式
*  @param  
*  @return  
*
*******************/
void I2C_SDA_Mode(uint8_t Addr)
{GPIO_InitTypeDef GPIO_InitStruct;if(Addr){	// 1 设置输出模式GPIO_InitStruct.Pin = GY30_SDA_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GY30_SDA_GPIO_Port, &GPIO_InitStruct);}else{//0   设置输入模式GPIO_InitStruct.Pin = GY30_SDA_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GY30_SDA_GPIO_Port, &GPIO_InitStruct);}}/*******************
*  @brief  这是us 级延时函数
*  @param  
*  @return  
*
*******************/
void I2C_Delay_us(uint8_t us)
{uint8_t i = 0;while(us --){for(i = 0;i<8;i++);}
}
/*******************
*  @brief  起始信号S
*  @param  
*  @return  
*
*******************/
void I2C_Start(void)
{I2C_SDA_Mode(OUT);SCL_Hige;SDA_High;I2C_Delay_us(5);SDA_Low;I2C_Delay_us(5);SCL_Low;
}
/*******************
*  @brief  停止信号
*  @param  
*  @return  
*
*******************/
void I2C_Stop(void)
{I2C_SDA_Mode(OUT);SDA_Low;
//	I2C_Delay_us(5);//调整SCL_Hige;I2C_Delay_us(5);SDA_High;I2C_Delay_us(5);//调整
}
/*******************
*  @brief  读是否有应答
*  @param  
*  @return  1-没有收到应答位,0-收到应答位
*
*******************/
uint8_t I2C_Read_Ack(void)
{uint8_t AckTime ;//超时计数I2C_SDA_Mode(INPUT);//设置输入模式SCL_Hige;//先拉高SCL、SCL为高的时候检测SDAI2C_Delay_us(4);while(SDA_Read == GPIO_PIN_SET){if(++AckTime > 250){//超时判断 如果长时间没有应答I2C_Stop();//发送停止信号return 1;//退出}}SCL_Low;I2C_Delay_us(4);return 0;	//
}
/*******************
*  @brief  写一个字节数据
*  @param  要写的一个字节
*  @return  
*
*******************/
void I2C_Write_Byte(uint8_t Data)
{SCL_Low;//设置SCL低电平 ,允许SDA改变I2C_Delay_us(4);for(uint8_t i = 0;i<8;i++){I2C_SDA_Mode(OUT);//设置输出if((Data << i) & 0x80) SDA_High;//循环发送,先发送高位 ,然后发低位else SDA_Low;SCL_Hige;//设置SCL为高,后面要保持SDA不变I2C_Delay_us(4);SCL_Low;I2C_Delay_us(4);}
}
/*******************
*  @brief  读一个字节数据
*  @param  
*  @return  返回读的数据
*
*******************/
uint8_t I2C_Read_Data(void)
{uint8_t Data;//暂存读的数据for(uint8_t i=0;i<8;i++){I2C_SDA_Mode(INPUT);//设置输入SCL_Hige;//设置SCL高,延时后SDA稳定 从SDA读数据I2C_Delay_us(4);Data <<= 1;	//收的数据先存低位,然后用这个左移8次if(SDA_Read == GPIO_PIN_SET){//如果SDA为高电平就置位Data |= 0x01;//就置为最后一位}SCL_Low;//拉低SCL,期间SDA可能变化I2C_Delay_us(4);}return Data;//返回读的数据
}
/*******************
*  @brief  发送应答或者非应答信号
*  @param  1- 非应答、0- 应答
*  @return  无
*
*******************/
void I2C_Sende_Ack(uint8_t Ack)
{I2C_SDA_Mode(OUT);//设置输出if(Ack)SDA_High;//如果输入1 SDA设置为高else SDA_Low;SCL_Hige;//SCL设置为高保持4毫秒I2C_Delay_us(4);SCL_Low;//然后释放掉I2C_Delay_us(4);
}/********************************************************End Of File
********************************************************/

iic.h

#ifndef __I2C_H__
#define __I2C_H__#include "MyApp.h"
/* extern variables-----------------------------------------------------------*/#define OUT 1
#define INPUT 0
/* extern function prototypes-------------------------------------------------*/void I2C_Delay_us(uint8_t us);
/*******************
*  @brief  起始信号S
*  @param  
*  @return  
*
*******************/
void I2C_Start(void);
/*******************
*  @brief  停止信号
*  @param  
*  @return  
*
*******************/
void I2C_Stop(void);
/*******************
*  @brief  读是否有应答
*  @param  
*  @return  1-没有收到应答位,0-收到应答位
*
*******************/
uint8_t I2C_Read_Ack(void);
/*******************
*  @brief  写一个字节数据
*  @param  要写的一个字节
*  @return  
*
*******************/
void I2C_Write_Byte(uint8_t Data);
/*******************
*  @brief  读一个字节数据
*  @param  
*  @return  返回读的数据
*
*******************/
uint8_t I2C_Read_Data(void);
/*******************
*  @brief  发送应答或者非应答信号
*  @param  1- 非应答、0- 应答
*  @return  无
*
*******************/
void I2C_Sende_Ack(uint8_t Ack);
#endif/********************************************************End Of File
********************************************************/

GY30.c

#include "MyApp.h"//#define GY30	0XB8
#define GY30	0X46
//ADDR接高地址为OxB8(1011100)然后写就是最后一位0 就是0xB8 如果读就最后一位1 就是0xB9
//ADDR接低地址为0x46(0100011)然后写操作最后一位是0 就是0x46 如果是读最后一位1 就是0x47/* Private variables----------------------------------------------------------*//* Public variables-----------------------------------------------------------*/uint8_t DATA[2];/* Private function prototypes------------------------------------------------*/ 
/*******************
*  @brief  写一个Byte
*  @param  
*  @return  
*
*******************/
void GY30_Write_Byte(uint8_t Byte){I2C_Start();//主机发送起始信号I2C_Write_Byte(GY30|0x00);//发送地址+0表示写 0xB8I2C_Read_Ack();//主机等待收到应答信号I2C_Write_Byte(Byte);//主机写数据过去I2C_Read_Ack();//主机等待从机的应答信号I2C_Stop();//主机发送停止信号}
/*******************
*  @brief  读光照数据
*  @param  
*  @return  
*
*******************/
void GY30_Read_Byte(void){I2C_Start();//发送起始信号I2C_Write_Byte(GY30|0x01);//0xB9发送过去,表示地址0XB8和1(读数据)I2C_Read_Ack();//主机等待收到应答信号for(uint8_t i = 0;i<2;i++){DATA[i] = I2C_Read_Data();//读数据保持if(i == 2)  			///觉得这里有问题,I2C_Sende_Ack(1);//如果读两个数据 主机发送不应答elseI2C_Sende_Ack(0);}I2C_Stop();//主机发送停止信号HAL_Delay(5);//延时5ms 时间
}
/********************************************************End Of File
********************************************************/

GY30.h

#ifndef __GY30_H__
#define __GY30_H__
#include "MyApp.h"/* extern variables-----------------------------------------------------------*//* extern function prototypes-------------------------------------------------*//*******************
*  @brief  写一个Byte
*  @param  
*  @return  
*
*******************/
void GY30_Write_Byte(uint8_t Byte);
/*******************
*  @brief  读光照数据
*  @param  
*  @return  
*
*******************/
void GY30_Read_Byte(void);#endif

然后主函数还是那样的

在这里插入图片描述

  while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///这个测试设置两个都是推挽,然后SDA读的时候设置上拉输入GY30_Write_Byte(0x01);//通电指令GY30_Write_Byte(0x10);//连续高分辨模式120msHAL_Delay(120);//延时GY30_Read_Byte();//读数据printf("数值:%d\r\n%d\r\n",DATA[0],DATA[1]);HAL_Delay(500);HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);//	DATA[0] //这就是高位和低位了
//	DATA[1]}

2.3-硬件I2C

硬件IIC我们下一篇再讲解

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 哈夫曼树例题
  • Matlab R2022b使用Camera Calibrator工具箱张正友标定法进行相机标定附带标定前后对比代码
  • 论文翻译:Multi-step Jailbreaking Privacy Attacks on ChatGPT
  • 设计模式(四)
  • 掌握 Rust 中的 YAML 魔法:Serde_yaml 使用指南
  • 【前端开发】国际化开发工具i18n的使用教程
  • MySQL 数据库深度解析:安装、语法与高级查询实战
  • BMC解决方案丨服务器故障诊断与预测平台方案设计与实现
  • 计算机毕业设计选题推荐-医疗就诊平台-在线医疗问诊系统-Java/Python项目实战
  • 【Android】根据URI获取文件扩展名或MimeType
  • Android SurfaceFlinger——共享内存读写流程(五十五)
  • 探索微服务架构中的动态服务发现与调用:使用 Nacos 与 Spring Cloud OpenFeign 打造高效订单管理系统
  • 如何将 Parallels Desktop 许可证密钥移至新的 Mac?
  • Android 获取通话记录
  • 【Verilog 数字系统设计教程】Verilog 基础:硬件描述语言入门指南
  • 30秒的PHP代码片段(1)数组 - Array
  • angular2 简述
  • co.js - 让异步代码同步化
  • CSS盒模型深入
  • github指令
  • java取消线程实例
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Magento 1.x 中文订单打印乱码
  • Markdown 语法简单说明
  • Mithril.js 入门介绍
  • OSS Web直传 (文件图片)
  • WePY 在小程序性能调优上做出的探究
  • 给新手的新浪微博 SDK 集成教程【一】
  • 开源SQL-on-Hadoop系统一览
  • 如何进阶一名有竞争力的程序员?
  • 如何胜任知名企业的商业数据分析师?
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 手写一个CommonJS打包工具(一)
  • 昨天1024程序员节,我故意写了个死循环~
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​VRRP 虚拟路由冗余协议(华为)
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (1)(1.13) SiK无线电高级配置(六)
  • (层次遍历)104. 二叉树的最大深度
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • (力扣)1314.矩阵区域和
  • (七)Appdesigner-初步入门及常用组件的使用方法说明
  • (十) 初识 Docker file
  • (转载)Linux 多线程条件变量同步
  • .apk文件,IIS不支持下载解决
  • .dwp和.webpart的区别
  • .net core使用EPPlus设置Excel的页眉和页脚
  • .NET NPOI导出Excel详解
  • .NET 中选择合适的文件打开模式(CreateNew, Create, Open, OpenOrCreate, Truncate, Append)
  • .NET/ASP.NETMVC 大型站点架构设计—迁移Model元数据设置项(自定义元数据提供程序)...
  • .netcore 获取appsettings
  • /*在DataTable中更新、删除数据*/
  • /etc/sudoer文件配置简析
  • @PostConstruct 注解的方法用于资源的初始化