IIC通信驱动硬件编程 (1)

news2025/1/10 21:02:56

1、IIC协议基础

        I2C( IIC )属于两线式串行总线,由飞利浦公司开发用于微控制器(MCU)和外围设备(从设备)进行通信的一种总线,属于一主多从即一个主设备(Master),多个从设备(Slave))的总线结构,总线上的每个设备都有一个特定的设备地址,以区分同一I2C总线上的其他设备。

        IIC有2根通信线路(SDA、SCL),SCL为时钟同步线,用于主机和从机间数据同步操作;SDA为数据传输线,用于传输通信数据。

        IIC通信是半双工通信,所谓半双工就是主机和从机间能进行双向数据传输,但同一时间只能有一个数据发送方和一个数据接收方,即主机发送数据时,从机只能处于接收状态,反之亦然。

        因IIC是一主多从通信机制,所以在IIC通信时,必须要提前提供从机的设备地址,但主机不需要设备地址,IIC通信的发起方永远是主机。从机的设备地址是由厂家设定的

        IIC设备地址由7位组成,通信时左移一位,用最低位表示为读写位。当然,也可以理解为,IIC通信地址为8位即一个字节,高7位表示设备的实际通信地址,而最低位为读写位。

        IIC设备设址:xxxx xxx(w/r)

        读写位用低电平0表示写,高电平1表示读;

        以AT24C02为例,其7位设备地址为 1010 000 = 0x50

        读模式时,AT24C02的设备地址为 1010 0001 = 0xA1

        写模式时,AT24C02的设备地址为 1010 0000 = 0xA0 

        如下图所示为IIC通信的一主多从示意图,每个从机设备都有唯一设备地址。

2、IIC协议时序图解析

        IIC通信逻辑时序图如下所示。      

①、空闲状态

        空闲状态,就是没有通信时的状态(初始状态)。I2C总线的SDA和SCL两条信号同时处于高电平时,规定为总线的空闲状态。

        此时各个器件的输出级场效管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

②、起始信号   

        SCL为高电平时SDA从高电平下降到低电平;

        起始信号结束后,SDA和SCL都处于低电平状态。

        

③、停止信号

        SCL为高电平时SDA由低电平上升到高电平;

        停止位信号结束后,SDA和SCL都处于高电平状态。

        

④、应答信号

        应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;

        应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

        

⑤、数据信号      

        发送数据和接收数据时通用。

        逻辑1:当SCL为高电平时,SDA始终为高电平表示逻辑1;

        逻辑0:当SCL为高电平时,SDA始终为低电平表示逻辑0。

        

3、IIC通信源码

        在编写程序时考虑到IIC协议代码会运用于各个平台的设备上,如STM32、ESP32、GD32、嵌入式Linux等,且即使是同一家公司生产的芯片,因型号的不同,代码也并不能直接通用。因此IIC通信协议代码必须考虑到硬件移值性的问题,即一套代码,仅仅进行非常小的改动,就可以在其他设备上运行。基于此想法,IIC协议代码实现了,对除了对SDA和SCL的GPIO引脚初始化函数需要根据平台重新编写外,其它的操作代码通过修改对应的宏定义即可让IIC协议代码正常跑起来。

①、iic.c

#include "iic.h"


/**
  * @brief IIC初始化
  * @param None
  * @retval None
  */
void IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
	__HAL_RCC_GPIOB_CLK_ENABLE();
	
    GPIO_InitStructure.Pin = SDA_Pin | SCL_Pin;
    GPIO_InitStructure.Mode = OUTPUT;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(IIC_Bus, &GPIO_InitStructure);
}

/**
  * @brief IIC SDA模式
  * @param mode:INPUT--输入模式,OUTPUT--输出模式
  * @retval None
  */
void IIC_SDA_Mode(int mode)
{
	GPIO_InitTypeDef GPIO_InitStructure = {0};
	__HAL_RCC_GPIOB_CLK_ENABLE();
	
    GPIO_InitStructure.Pin = SDA_Pin;
    GPIO_InitStructure.Mode = mode;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(IIC_Bus, &GPIO_InitStructure);
}

/**
  * @brief IIC短延时
  * @param None
  * @retval None
  */
void IIC_Delay(void)
{
	for(int i=0; i<30; i++)
	{
		;
	}
}

/**
  * @brief IIC通信开始
		   SCL为高电平时,SDA从高电平下降到低电平。
		   起始信号结束后,SDA和SCL都处于低电平状态。
  * @param None
  * @retval None
  */
void IIC_Start(void)
{
	SCL_HIGH;
	IIC_Delay();
	SDA_HIGH;
	IIC_Delay();//保证IIC开始前处于空闲状态,SDA、SCL空闲状态时为高电平
	IIC_Delay();
	SDA_LOW;
	IIC_Delay();
	SCL_LOW;
	IIC_Delay();
}

/**
  * @brief IIC通信停止
		   SCL为高电平时,SDA由低电平上升到高电平。
		   停止位信号结束后,SDA和SCL都处于高电平状态。
  * @param None

  * @retval None
  */
void IIC_Stop(void)
{
	SCL_LOW;
	SDA_LOW;
	IIC_Delay();
	
	SCL_HIGH;
	IIC_Delay();
	SDA_HIGH;
	IIC_Delay();
}

/**
  * @brief IIC写应答
		   应答信号为低电平时,规定为有效应答位
		   应答信号为高电平时,规定为非应答位
  * @param ack 应答信号 HIGH--无效应答、LOW--有效应答
  * @retval None
  */
void IIC_Write_Ack(unsigned char ack)
{
	if(ack)
	{
		SDA_HIGH;	//无效应答
	}else{
		SDA_LOW;	//有效应答
	}
	IIC_Delay();
	SCL_LOW;//
	IIC_Delay();
	SCL_HIGH;
	IIC_Delay();
	SCL_LOW;	
	IIC_Delay();
}

/**
  * @brief IIC等待应答
  * @param None
  * @retval None
  */
int IIC_Wait_Ack(void)
{
	int timeout = 10;
	IIC_SDA_Mode(INPUT);
	IIC_Delay();	
	SCL_HIGH;
	IIC_Delay();
	while(SDA_IN == HIGH)	//收到低电平有效应答时跳出循环
	{
		if(!timeout--)
		{
			IIC_SDA_Mode(OUTPUT);
			IIC_Stop();
			return -1;	//无效应答
		}
		IIC_Delay();
	}
	
	IIC_SDA_Mode(OUTPUT);
	SCL_LOW;
	IIC_Delay();
	return 0;			//有效应答
}

/**
  * @brief IIC写1个字节
		   逻辑1:当SCL为高电平时,SDA始终为高电平
		   逻辑0:当SCL为高电平时,SDA始终为低电平
  * @param None
  * @retval None
  */
void IIC_Write_Byte(unsigned char data)
{
	IIC_Delay();
	for(int i=0; i<8; i++)
	{
		SCL_LOW;
		IIC_Delay();
		if((data>>(7-i))&0x01)	//高位先发
			SDA_HIGH;
		else
			SDA_LOW;
		IIC_Delay();
		SCL_HIGH;
		IIC_Delay();
	}
	SCL_LOW;
	IIC_Delay();
}

/**
  * @brief IIC读1个字节
  * @param None
  * @retval None
  */
unsigned char IIC_Read_Byte(void)
{
	unsigned char data = 0;
	IIC_SDA_Mode(INPUT);
	SCL_LOW;

	for(int i=0; i<8; i++)
	{
		IIC_Delay();
		SCL_HIGH;
		IIC_Delay();
		if(SDA_IN == HIGH)
			data |= (0x01<<(7-i));
		SCL_LOW;
	}
	
	IIC_SDA_Mode(OUTPUT);
	//SCL_LOW;
	IIC_Delay();
	return data;
}

②、iic.h

#ifndef __IIC_H
#define __IIC_H

#include "stm32g4xx_hal.h"
#include "stdio.h"
#include "i2c_hal.h"


#define IIC_Bus		GPIOB
#define SCL_Pin		GPIO_PIN_6
#define SDA_Pin		GPIO_PIN_7

#define INPUT		GPIO_MODE_INPUT
#define OUTPUT		GPIO_MODE_OUTPUT_OD	//因为输出模式的问题,导致这个bug卡了很久!!!

#define HIGH		1
#define LOW			0

#define SCL_HIGH	HAL_GPIO_WritePin(IIC_Bus, SCL_Pin, GPIO_PIN_SET)
#define SCL_LOW		HAL_GPIO_WritePin(IIC_Bus, SCL_Pin, GPIO_PIN_RESET)
#define SDA_HIGH	HAL_GPIO_WritePin(IIC_Bus, SDA_Pin, GPIO_PIN_SET)
#define SDA_LOW		HAL_GPIO_WritePin(IIC_Bus, SDA_Pin, GPIO_PIN_RESET)

#define SDA_IN		HAL_GPIO_ReadPin(IIC_Bus, SDA_Pin) ? HIGH : LOW


void IIC_Init(void);
void IIC_SDA_Mode(int mode);
void IIC_Delay(void);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_Write_Ack(unsigned char ack);
int IIC_Wait_Ack(void);
void IIC_Write_Byte(unsigned char data);
unsigned char IIC_Read_Byte(void);

#endif

上述代码已经在AT24C02存储设备上测试通过,可以正常与AT24C02通信,并读写AT24C02的数据,代码的运行效果如下图所示。

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

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

相关文章

MR(混合现实)系统的开发流程

MR&#xff08;混合现实&#xff09;系统是一种将虚拟信息与真实世界环境相结合的技术&#xff0c;通过头戴式显示器、智能手机等设备将虚拟对象叠加在用户的真实感知中。它可以让用户与虚拟对象进行互动&#xff0c;并在真实环境中观察虚拟对象的行为。MR&#xff08;混合现实…

this的指向问题总结

this一般会出现在函数里面&#xff0c;但是一般情况下只有在函数被调用执行时&#xff0c;才能确定this指向哪个对象。一般情况下this是指调用函数的对象。 1.在全局作用域下或者普通函数中this的指向一般都是window对象 window.fn&#xff08;&#xff09;&#xff0c;普通函…

​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】

欢迎来CILMY23的博客喔&#xff0c;本期系列为​【C语言】长篇详解&#xff0c;字符系列篇3-----strstr&#xff0c;strtok&#xff0c;strerror字符串函数的使用【图文详解​】&#xff0c;图文讲解各种字符串函数&#xff0c;带大家更深刻理解C语言中各种字符串函数的应用&am…

35年的卓越成就:威步在网络安全和软件授权领域持续领先

1989-2023&#xff1a;威步成立35年里程碑。这35年中&#xff0c;公司始终致力于业务连续性、技术进步和团队凝聚力。 ​全球信任与在地优势&#xff1a;威步凭借其全球渠道合作伙伴网络&#xff0c;确保国际客户像信赖本地优质企业一样信赖他们。 传统与创新&#xff1a;威步…

Solidworks:焊件(型材)结构构件设计

焊件&#xff08;型材&#xff09;结构构件设计是Solidworks的一大特色。使用这一功能&#xff0c;能够体会到这款软件的架构设计在逐步递进增加软件功能方面做出的特别努力。 设计一个三角形框架 正面&#xff1a; 换个角度观察&#xff1a; 再来一个练习&#xff0c;包含…

顺序表详解(SeqList)

本文使用C语言进行顺序表的代码实现。 博主将使用代码和相关知识相结合的方式进行讲解&#xff0c;简单易懂&#xff0c;懵懂的大学生一听就会~ 顺序表是一种线性表的存储结构&#xff0c;它将数据元素存储在一段连续的存储空间中&#xff0c;每个元素占据一个存储单元&#x…

Spring Security 认证授权安全框架

Spring Security概述 1.什么是Spring Security? Spring Security是一个Java框架&#xff0c;用于保护应用程序的安全性。它提供了一套全面的安全解决方案&#xff0c;包括身份验证、授权、防止攻击等功能。Spring Security基于过滤器链的概念&#xff0c;可以轻松地集成到任…

白盒测试接口测试自动化测试

一、白盒测试&#xff1a;一种测试策略&#xff0c;允许我们检查程序的内部结构&#xff0c;对程序的逻辑结构进行检查&#xff0c;从中获取测试数据。白盒测试的对象基本是源程序&#xff0c;所以它又称为结构测试或逻辑驱动测试&#xff0c;白盒测试方法一般分为静态测试和动…

Cesium 问题:加载 gltf 格式的模型之后太小,如何让相机视角拉近

文章目录 问题分析问题 刚加载的模型太小,如何拉近视角放大 分析 在这里有两种方式进行拉近视角, 一种是点击复位进行视角拉近一种是刚加载就直接拉近视角// 模型三加载 this.damModel = new Cesium.Entity({name: "gltf模型",position:</

leetcode hot100零钱兑换Ⅱ

本题可以看出也是背包问题&#xff0c;但区别于之前的01背包问题&#xff0c;这个是完全背包问题的变形形式。 下面介绍01背包和完全背包的区别与联系&#xff1a; 01背包是背包中的物品只能用一次&#xff0c;不可以重复使用&#xff0c;而完全背包则是可以重复使用。01/完全…

一命通关动态规划dp

前言 这篇文章详细概括了动态规划的所有题型&#xff0c;以及各个题型的解决公式方法。如果看完这篇动态规划还是不会做题&#xff0c;我来给你补&#xff0c;我爱说点实话。 动态规划 何为动态规划&#xff1f; 动态规划&#xff0c;有一点暴力求解的感觉。用最通俗的语言来…

利用nbsp设置空格

想要实现上面效果&#xff0c;一开始直接<el-col :span"8" >{{ item.name }} </el-col> 或者<el-col :span"8" >{{ item.name }}</el-col>或者<el-col :span"8" >{{ item.name }}</el-col> 都无…

【办公类-16-07-02】“2023下学期 周计划-户外游戏 每班1周五天相同场地,6周一次循环”(python 排班表系列)

背景需求&#xff1a; 又到了开学季&#xff0c;新的自主游戏&#xff08;户外游戏&#xff09;安排表出炉了。 这张是贴在美术活动室的安排表&#xff0c;我需要转换成班级为单位的安排表&#xff0c;便于批量制作周计划。 设计思路&#xff1a; 1、一个班级每周轮到的一个场…

网工内推 | 上市公司、外企网工,厂商认证优先,最高18K*13薪

01 北京电旗通讯技术股份有限公司 招聘岗位&#xff1a;网络运维工程师 职责描述&#xff1a; 1.负责内部核心网络的运行监控、故障处理。 2.对网络性能进行监控和调优。 3.负责网络设备的规划选型。 4.设计网络架构拓扑图,制定网络规范。 5.分析处理复杂网络故障。 6.负…

sizeof()的易错点

你也可以传入一个变量的名字&#xff08;而不只是类型&#xff09;给 sizeof()&#xff0c;但在一些情况下&#xff0c;可能得不到你要的结果&#xff0c;所以要小心使用。例如&#xff0c;看看下面的代码片段&#xff1a; 在第一行&#xff0c;我们为 10 个整数的数组声明了空…

【前端】前端三要素之JavsScript基础

写在前面&#xff1a;本文仅包含JavaScript内容&#xff0c;DOM知识传送门在这里&#xff0c;BOM传送门在这里。 本文内容是假期中刷的黑马Pink老师视频&#xff08;十分感谢Pink老师&#xff09;&#xff0c;原文保存在个人的GitLab中&#xff0c;如果需要写的网页内容信息等可…

笔记:torch.roll

最近在准备写 swin transformer 的文章&#xff0c;记录下 torch.roll 的用法&#xff1a; >>> x torch.tensor([1, 2, 3, 4, 5, 6, 7, 8]).view(4, 2) >>> x tensor([[1, 2],[3, 4],[5, 6],[7, 8]]) 第0维度向下移1位&#xff0c;多出的[7,8]补充到顶部 &g…

【Flink网络通讯(一)】Flink RPC框架的整体设计

文章目录 1. Akka基本概念与Actor模型2. Akka相关demo2.1. 创建Akka系统2.2. 根据path获取Actor并与之通讯 3. Flink RPC框架与Akka的关系4.运行时RPC整体架构设计5. RpcEndpoint的设计与实现 我们从整体的角度看一下Flink RPC通信框架的设计与实现&#xff0c;了解其底层Akka通…

JS进阶——解构赋值

数组解构 基本&#xff1a; let [a, b, c] [1, 2, 3]; // a 1 // b 2 // c 3 可嵌套 let [a, [[b], c]] [1, [[2], 3]]; // a 1 // b 2 // c 3 可忽略 let [a, , b] [1, 2, 3]; // a 1 // b 3 不完全解构 let [a 1, b] []; // a 1, b undefined 剩余运…

基于RHEL8部署Zabbix6.0,监控不再困难!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…