STM32学习(十三)——IIC总线

基础知识

一主多从模型:任何时候SCL被主机控制,主机可将SDA权限下方给从机

这样可以避免电平冲突产生电源短路现象

使用开漏输出,强下拉(直接接地)弱上拉(接电阻上拉)

会有“线与”现象 

起始条件和终止条件都是由主机控制,实线指主机控制,虚线指从机控制

 

一般芯片的地址高电平都一样,低电平各有不同

当前地址读,读取当前指针地址的数据,读完了指针加1

指定地址读:先写地址,再转换为读模式

MPU6050简介

这是6轴,9轴还包括括磁场强度(3个),10轴包括气压强度(高度)

姿态角:俯仰角,滚转角,偏航角 

加速度计:静态稳定,动态不稳定

陀螺仪:动态稳定,静态不稳定

0X68<<1 作为从机地址,需要再或上读写位,从机地址相当于0xD0

0xD0融入了读写位的 

XCL和XDA可以拓展方位角,偏航角等

IIC软件代码

可能有不知名故障

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
/*采用宏定义的方式
#define SCL_PORT   GPIOB
#define SCL_PIN    GPIO_Pin_10
#define SDA_PIN    GPIO_Pin_11
*/
void My_I2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);//使用BitAction强制转换可移植
	Delay_us(10);//单片机主频太快需要延时
}
void My_I2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);//使用BitAction强制转换可移植
	Delay_us(10);//单片机主频太快需要延时
}
/*实际上用不到读SCL的函数
uint8_t My_I2C_R_SCL(void)
{
	uint8_t BitValue;
	BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10);
	Delay_us(10);
	return BitValue;
}
*/
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}
void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;//都要配置成开漏输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);//宏定义的方式方便初始化
	
}

void MyI2C_Start(void)
{
	My_I2C_W_SDA(1);//释放SCL,SDA使之输出1
	My_I2C_W_SCL(1);

	My_I2C_W_SDA(0);//先拉低SDA,再拉低SCL
	My_I2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	My_I2C_W_SDA(0);
	My_I2C_W_SCL(1);//除了终值条件,其他情况都保证SCL以低电平结束
	My_I2C_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for(i=0;i<1;i++)
	{
	My_I2C_W_SDA(Byte& (0x80>>i));//取出高位,考虑到BitAction强制转换,非0即1,0x80等效于1
	My_I2C_W_SCL(1);
	My_I2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t Byte=0x00;//初始值
	uint8_t i=0;
	My_I2C_W_SDA(1);
	for(i=0;i<8;i++)
	{
	My_I2C_W_SCL(1);
	if (MyI2C_R_SDA()==1){ Byte|=(0x80>>i);}//先高位后低位
	My_I2C_W_SCL(0);
  }
	return Byte;
}
//发送,接收应答就是发收一位
void MyI2C_SendAck(uint8_t AckBit)
{
	My_I2C_W_SDA(AckBit);
	My_I2C_W_SCL(1);
	My_I2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;//初始值
	My_I2C_W_SDA(1);//先释放,开漏输出模式下,主机置1释放1而不是置1,也就是意味着这个操作后SDA可能是0,因为接下来从机有动作
	My_I2C_W_SCL(1);
	AckBit=MyI2C_R_SDA();
	My_I2C_W_SCL(0);
	return AckBit;
}

下面一个是标准版

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/*引脚配置层*/

/**
  * 函    数:I2C写SCL引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  */
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C写SDA引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平
  */
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C读SDA引脚电平
  * 参    数:无
  * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  */
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);		//读取SDA电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
	return BitValue;											//返回SDA电平
}

/**
  * 函    数:I2C初始化
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化
  */
void MyI2C_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为开漏输出
	
	/*设置默认电平*/
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}

/*协议层*/

/**
  * 函    数:I2C起始
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);							//释放SDA,确保SDA为高电平
	MyI2C_W_SCL(1);							//释放SCL,确保SCL为高电平
	MyI2C_W_SDA(0);							//在SCL高电平期间,拉低SDA,产生起始信号
	MyI2C_W_SCL(0);							//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}

/**
  * 函    数:I2C终止
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平
	MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平
	MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号
}

/**
  * 函    数:I2C发送一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));	//使用掩码的方式取出Byte的指定一位数据并写入到SDA线
		MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDA
		MyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据
	}
}

/**
  * 函    数:I2C接收一个字节
  * 参    数:无
  * 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
  */
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	for (i = 0; i < 8; i ++)				//循环8次,主机依次接收数据的每一位
	{
		MyI2C_W_SCL(1);						//释放SCL,主机机在SCL高电平期间读取SDA
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量
														//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
		MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA
	}
	return Byte;							//返回接收到的一个字节数据
}

/**
  * 函    数:I2C发送应答位
  * 参    数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
  * 返 回 值:无
  */
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线
	MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}

/**
  * 函    数:I2C接收应答位
  * 参    数:无
  * 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
  */
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;							//定义应答位变量
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	MyI2C_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDA
	AckBit = MyI2C_R_SDA();					//将应答位存储到变量里
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
	return AckBit;							//返回定义应答位变量
}

I2C硬件时序

硬件框图

端口:I2C2的端口 PB10 PB11 I2C1的端口PB7 PB6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值