模拟IIC协议--结合四轴学习(利用MPU9250 WHO AM I寄存器验证通信)

						模拟IIC协议

通信总线在嵌入式系统有着及其重要的地位,他是单片机与单片机通信,单片机与外围设备通信的一个协议,如果没有这协议,我们就不能保证数据能有效,稳定,准确的在两个设备之间通信。我们常用的总线有USART、IIC、SPI、CAN等,各有各的优势后面会讲解。今天我们着重讲解一下I2C总线,DragonFly四轴学习平台与MPU9250、FBM320通信都是用的IIC,因此本节内容尤为重要。
本节任务

IIC协议简介

IIC通讯协议是 Philips 公司开发的,由于引脚少,硬件实现简单,可拓展性强,不需要 USART,CAN等通讯协议等外部接收发设备,现在被广泛地应用在系统内多个集成电路(IC)间的通讯。下面我们分别对 IIC 协议的物理层及协议层进行讲解。

物理层

IIC通讯连接
物理层特点:

  1. 它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个IIC通讯总线中,可连接多个IIC通讯设备,支持多个通讯主机及多个通讯从机。(多对多)
  2. 一个IIC总线只使用两条总线线路,一条双向串行数据线(SDA),一条串行时钟线(SCL)。数据线用于传输数据,时钟线用于数据收发同步
  3. 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
  4. 总线通过上拉电阻接到电源。当IIC设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
  5. 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
  6. 具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s, 高速模式下可达3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
  7. 连接到相同总线的IC数量受到总线的最大电容400pF限制。

协议层

IIC的协议定义了通讯的其实信号,停止信号,数据有效性,响应,仲裁,时钟同步和地址广播等环节

1.IIC基本读写流程

IIC的通信流程可分为三部分,通信流程见下图:

IIC通信

黑色:数据由主机传输至主机 白色:数据由从机传至主机
**S:**开始信号 **SLAVE ADDRESS:**从机地址 **R/W:**传输方向选择位,1读,0写 **A/-A:**应答(ACK)与非应答(NACK)信号 P:停止信号
上图表示的是主机和从机通信时,数据线SDA的数据包序列。
起始信号(S):表示由主机的IIC接口产生的传输,这时连接到IIC总线上的所有从机都会接收到这个信号。
从机地址(SLAVEADDRESS):起始信号产生后,所有从机就开始等待主机紧接下来广播的从机地址信号(SLAVEADDRESS)。在IIC总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。IIC协议规定从机地址可以是
7位或10位

传输方向选择位 (R/W―): 在地址位之后是传输方向的选择位,该位为0时,表示后面的数据传输方向是由主机写数据到从机。该位为1时,即主机由从机读数据。
应答信号(A):第一种:从机接收到匹配的地址,从机返回应答(ACK)信号表示找到对应地址的从设备。第二种:当从机被寻址后,主机写数据到从机时:要求从机每收到一个字节都需给主机一个应答(ACK),主机只有接收到应答信号后,才能继续发送数据。主机由从机读数据时:主机每接收一个字节时都需给从机一个应答(ACK),从机只有接收到应答信号后,才能继续发送数据。从机发送ACK
非应答信号(NACK):主机由从机读取数据时:主机接收完最后一个字节,必须产生一个非应答(NACK)芯片信号,用来通知从机不要再发数据了,这样从机也就释放了SDA。这里的NACK是主机发送给从机的
停止信号§:所有数据都传输完成时,主机会给出一个停止信号,用于结束此次通信。
总结:IIC通讯中,SDA和SCL都是由主机控制的,从设备只是能够将SDA线拉低而已。对于SCL线,从机是没有任何能力去控制的。从机只能被动跟随SCL。时钟跟随主机,数据从机可以参与

若配置的方向传输位为**“写”方向,情况如图10-2:广播完地址,接收到应答信号后,主机开始正式向从机传输数据(DATA),数据包的大小为8位,主机每发送完一个字节数据,都要等待从机的应答信号(ACK),重复这个过程,可以向从机传输N个数据,这个N没有大小限制。当数据传输结束时,主机向从机发送一个停止传输信号§,表示不再传输数据。
若配置的方向传输位为
“读”**方向,情况如图10-3:该传输过程中有两次起始信号(S).一般在第一次传输中,主机通过SLAVE_ADDRESS寻找到从设备后,发送一段“数据” ,这段数据通常用在表示从设备内部的寄存器或储存器地址(注意区分它与SLAVE_ADDRESS的区别);在第二次的传输中,对改地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次是读写的实际内容。

IIC通信各信号分解

起始和停止信号
当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通信的起始。当 SCL 是高电平时 SDA 线由低电平向高电平切换,表示通信的停止。起始和停止信号一般由主机产生。
地址与数据方向
IIC总线上的每个设备都有自己的独立地址,主机发起通讯时,通过SDA信号线发送设备地址(SLAVE ADDRESS)来查找从机。IIC 协议规定设备地址可以是7位或10位,实际中7位的地址应用比较广泛。紧跟设备地址的一位数据位来表示数据的传输方向,他是数据方向位(R/W),第8位或第11位。若用7位的地址,➕1位的数据读写位,这样就构成一个字节的数据。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
设备地址与数据传输方向
数据有效性
IIC使用SDA信号线来传输数据,使用SCL信号线进行数据同步。SDA数据线在SCL的每个时钟周期传输一位数据。传输时,SCL为高电平时SDA的数据有效,即此时的SDA为高电平时表示数据“1”,为低电平时表示数据“0”。当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平切换,为下一次数据做好准备。
数据有效性
应答与非应答
IIC 的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。作为数据接收端时,当设备(无论主从机)接收到IIC传输的一个字节数据或地址后,若希望对方继续发送数据,就需要向对方发送应答信号,发送方会继续发送下一个数据。若接收端希望结束数据传输,则向对方发送非应答,发送方接收到该信号后就会停止,结束信号传输。
应答与非应答信号
传输时主机产生时钟信号,在第9个时钟时,数据发送端会释放SDA的控制,由数据接收端控制SDA来反映是否应答。

GPIO模拟IIC时序

对于STM32F4自带IIC硬件接口,通信速度最高能达到400KHz。但是也有其缺点就是通用性不是很好,不同芯片的配置不同,引脚固定。因此我们选择用IO口来模拟IIC,它具有通用性强,移植方便,可以用任意IO引脚模拟等特点。下面我们来看看怎么用IO模拟IIC

GPIO初始化模式

我们知道IIC一共就两条线分别为SDA和SCL,所以我们需要用两个IO口来进行模拟。其中,SDA数据线需要可以受主从机的控制这个特点,所以模拟时需要有IO口的输入输出切换。SCL为时钟线,仅由主机控制输出,所以只需要配置为输出模式。
/*移植代码修改区*/
#define RCC_IIC_SCL 	RCC_AHB1Periph_GPPIOB	//端口时钟
#define IIC_SCL_PORT 	GPIOB					//端口
#define IIC_SCL 		GPIO_Pin_6				//引脚

#define RCC_IIC_SDA		RCC_AHB1Periph_GPIOB	//端口时钟
#define IIC_SDA_PORT 	GPIOB					//端口
#define IIC_SDA 		GPIO_Pin_7				//引脚
/************/

平时我们在借用GPIO模拟IIC时候都可以宏定义一下,方便理解和移植。

/**********************
函数:void IIC_Init(void)
功能:IIC初始化
/**********************/
void IIC_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_IIC_SDA,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_IIC_SCL,ENABLE);

	GPIO_IinitStrucure.GPIO_Pin = IIC_SCL;
	GPIO_IinitStrucure.GPIO_Mode = GPIO_Mode_OUT; //选择模式
	GPIO_IinitStrucure.GPIO_OType = GPIO_OType_OD;	//开漏输出类型
	GPIO_IinitStrucure.GPIO_Speed = GPIO_Speed_50Mhz;
	GPIO_Init(IIC_SCL_PORT,&GPIO_InitStructure);

	GPIO_IinitStrucure.GPIO_Pin = IIC_SDA;
	GPIO_IinitStrucure.GPIO_Mode = GPIO_Mode_OUT;//选择模式
	GPIO_IinitStrucure.GPIO_OType = GPIO_OType_OD;//开漏输出类型
	GPIO_IinitStrucure.GPIO_Speed = GPIO_Speed_50Mhz;
	GPIO_IinitStrucure.GPIO_PuPd = GPIO_PuPd_UP;//仅仅对配置成输入时有效
	GPIO_Init(IIC_SCL_PORT,&GPIO_InitStructure);

	IIC_SCL_H;
	IIC_SDA_H;
}

SDA线输入输出模式转换

上面说了SDA线需要输出/输入,所以我们就需要有一个快速切换 IO 模式的操作。最快改变 IO 模式的方法,也就是直接操作寄存器了。由STM32F4参考手册可知 GPIOx-MODER 寄存器是用来配置IO模式的。
代码操作如下:

//求偏移量
#define IIC_SCL_OFFSET (uint8_t)(log(IIC_SCL)/log(2))
#define	IIC_SDA_OFFSET (uint8_t)(log(IIC_SDA)/log(2))

//模式切换
#define SDA_IN()	{IIC_SDA_PORT->MODER&=~(3<<(IIC_SDA_OFFSET*2));IIC_SDA_PORT->MODER|=0<<IIC_SDA_OFFSET*2;}
#define SDA_OUT()	{IIC_SDA_PORT->MODER&=~(3<<(IIC_SDA_OFFSET*2));IIC_SDA_PORT->MODER|=1<<IIC_SDA_OFFSET*2;}

IIC_SDA_OFFSET 是对应 IO 在GPIOx-MODER 模式寄存器里的偏移量,IIC_SDA_PORT->MODER&=~(3<<(IIC_SDA_OFFSET2));是清除上一次设置,IIC_SDA_PORT->MODER|=0<<IIC_SDA_OFFSET2;这句是设置SDA为输入模式,另一个一样,设置为输出模式。模拟SDA和SCL的电平转换,需要电平反转速度足够快,所以我选择了直接操作寄存器。电平反转宏定义在下:

#define IIC_SCL_H		IIC_SCL_PORT->BSRRL |= IIC_SCL //SCL
#define IIC_SCL_L		IIC_SCL_PORT->BSRRL |= IIC_SCL //SCL
#define IIC_SDA_H		IIC_SDA_PORT->BSRRL |= IIC_SDA //SDA
#define IIC_SDA_H		IIC_SDA_PORT->BSRRL |= IIC_SDA //SDA
#define READ_SDA		(IIC_SDA_PORT->IDR&IIC_SDA)//输入SDA

需要注意的是 READ_SDA 这个宏,前面IIC_SCL_H,IIC_SCL_L,IIC_SDA_H,IIC_SDA_L都是注意输出到从机,READ_SDA为主机读取从机发来的电平,也就是用这个宏时,SDA对应的IO口模式为输入模式
具体每一个信号怎么模拟,可以私信我,给你分享源码

读取MPU9250 WHO AM I寄存器

前面说那么多,那么我们怎么快速验证我们的模拟是否正确呢?
在MPU9250里面有一个WHO AM I 寄存器就能帮我们完成此验证,寄存器位图如下:
WHO AM I寄存器
这个寄存器用于验证设备的标识。WHO AM I 的内容是一个8位设备ID,寄存器默认值是0x71。这也就是说我们用MCU主机通过IIC总线读取MPU9250的WHO AM I寄存器的值,如果读取的值为0x71,则说明MCU与MPU9250通信成功, 也就是间接证明了我们模拟的IIC时序是正确的。
其实大部分支持IIC通信的传感器或其他器件都会有这样一个类似功能的寄存器,用于确认通信是否成功。
读取主机由从机读取一个字节的数据可调用源码中的:uint8_tIIC_ReadByteFromSlave(uint8_tI2C_Addr,uint8_t reg,uint8_t* buf);该函数的参数I2C_Addr:传入MPU9250的从机地址0x69; reg:传入WHOAMI寄存器的地址0x75;*buff:传入的是保存读取寄存器值的地址。代码如下:
IIC_ReadByteFromSlave(0x69,0x75,&ID);
WHO AM I寄存器下一章介绍

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值