stm32 IIC实战

目标:1 学习IIC协议理论

           2 通过IIC协议实现与0.96屏幕通讯和与MPU6050通讯

由于不想帖子拉的太长,所以MPU6050,EEPROM和OLED屏幕的详细使用会单独开两个帖子,这一个帖子主要还是写IIC的软件和硬件通讯,写完之后链接会放在下面的。

0.96 OLED 模拟IIC:

MPU6050:

EEPROM;

如果不想看的可以直接使用git把我的代码下载出来,里面工程挺全的,后期会慢慢的补注释之类的

stm32学习笔记: stm32学习笔记源码

如果不会使用git快速下载可以选择直接下载压缩包或者去看看git的使用

git的使用(下载及上传_gitcode怎么下载文件_是小刘不是刘的博客-CSDN博客

目录

一、理论部分

 1 IIC通讯的

2 硬件电路

 3 IIC的几种信号状态

1)起始信号、终止信号

2)发送字节、接收字节

3)应答信号

4 指定地址写 

5 当前地址读

 6 指定地址读写

二 代码部分

1 软件模拟IIC

1、起始信号

2 停止信号

3 发送一个字节

4 接收一个字节

5 发送应答信号

6 接收应答

一、理论部分

 1 IIC通讯的

什么是IIC,一种通讯协议,只用两根线就能实现两个设备之间的通讯,一个SCL时钟线,一个SDA数据线,可以一主多从,多主多从。

主机享有绝对的控制权,通讯方式为同步半双工,并且会有应答位,若没有应答就重新传输。

这些可以看后面再了解。

2 硬件电路

IIC的设备没有采用直接的推挽输出,因为SDA既要输入又要输出。所以如果协调不当一个输入一个输出就会导致短路,这里采用了一种场效应管的电路设计方式。

我的模电和电基也忘完了= =后面再补详细的把。

反正场效应管这里就是0v导通-5v截至所以这个电路采用了开漏输出的模式,只有负数和0,如果有人要使用SDA或者SCL线就将这两个线拉低,这样就防止了短路,并且加了两个弱上拉电阻,可以将电路拉成所谓的高电平,当电路不控制其输出0v的时候,两个弱拉电阻就会将其拉回去。(我也不知道这里这样说准不准确,如果有错后面会改)

 3 IIC的几种信号状态

1)起始信号、终止信号

起始条件,再SCL为高电平的期间,SDA从高电平切换到低电平

停止条件。SCL为高电平期间,SDA从低切换到高电平

2)发送字节、接收字节

都是以主机的视角描述,IIC下主机享有完全的主动权

发送字节,再SCL为低的期间内,SDA线放上数据,然后释放SCL就能够发送数据位,从机就能接收到数据了

 接收数据

 SCL低电平时,从机将数据放入SDA,释放SCL线,主机就会读取数据

3)应答信号

发送应答:判断主机是否接收到了数据,再刚刚的接收数据后面加上一个发送应答,发送0位收到,如果为1就是没有收到。(主机是否收到数据

接收应答:在发送一个数据后,判断从机是否接收到了,收到了从机就将SDA拉低(从i是否收到

上面就是协议规定的信号状态,然后就是IIC协议的组成了

4 指定地址写 

对于指定的设备,在指定的地址下,写入数据

在协议中一般都需要一个设备号,然后是设备地址,最后是数据 IIC协议也不例外

每一个IIC设备都有自己不同的IIC设备号,这个号码一般为7位,前4位固定,后3位中有可变为(有些时候在一个环境下需要多台同类型设备)这个设备和还要+上一位读写位,来确定用户是想读还是写 (可以看出电平0为写入

第二个部分是地址号 是用户需要写的地址

第三个就是用户需要写入的数据,记得每次发送完一个字节加上一个应答位

5 当前地址读

读取当前指针指向的地址下的数据,但是由于在写入完成后地址指针会自动+1,所以假设我们上次写入的数据在0x01在写入完成后这个指针会跳到0x02,这就导致了我们读取不到我们写入的数据,所以这个模式我们并不经常使用

还是一样的,先发送设备号+rw模式(1为读

然后从机发送数据,主机读取数据(这个数据我们是不能选择地址的

 6 指定地址读写

这个就有用多了是吧- -想读哪里读哪里,但是时序也就比较复杂了,起始也就是把写和读组合了一下

首先第一部分和前面的写是一样的,但是少了一部分,就是后面发送的数据,这里只有起始+地址但是没有写进去东西,所以指针也就还是停留在那没有自动后移,就是直接停在了0x19这里

 然后后半段,有个SR,这个就是重复起始信号,上一张图最后的sr就是这张图的,这样裁剪让大家好连贯看图,在这重发一个起始位后,我们重复刚刚的当前地址读操作就好,重新发送设备号和读模式,然后直接接收读回来的数据,最后加上停止位,如果你想多字节,不要重复以上步骤,你可以直接先不发停止位,这个后面写代码可以测试。

二 代码部分

1 软件模拟IIC

接下来就是写代码了,我们这是模拟IIC所以我们先初始化两根引脚线,就是常用配置,这里使用了宏定义方便后期修改,初始化两个引脚为开漏输出,这个前面也讲过为什么(控制他时为0,释放他时被外部若上拉拉为高电平1)

#define  GPIO_PORT_SCL    GPIOB
#define  GPIO_PORT_SDA    GPIOB

#define  GPIO_PIN_SCL   	GPIO_Pin_10
#define  GPIO_PIN_SDA   	GPIO_Pin_11

void Myiic_GPIO_Init()
{
	  GPIO_InitTypeDef GPIO_InitStructure;
	
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
		GPIO_InitStructure.GPIO_Pin=GPIO_PIN_SCL | GPIO_PIN_SDA;
		GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
		GPIO_Init(GPIO_PORT_SCL,&GPIO_InitStructure);
	
	  GPIO_SetBits(GPIO_PORT_SCL,GPIO_PIN_SCL);
	  GPIO_SetBits(GPIO_PORT_SDA,GPIO_PIN_SDA);
}

然后我们写三个函数:读引脚电平  输出高电平 输出低电平,这里是两个写法,一个是直接使用了带参宏,一个是直接函数输入,两个其实都一样,看个人习惯,这里记得将输入强转为BitAction ,这是一个枚举量,里面不是0就是1,可以方便后期的输出,等下就能看见效果。这个writeBit函数前面没有使用过,其实就是一个改变信号线电平的函数,最后那个参数就是setbit和resetbit。

#define  OLED_W_SCL(x)    GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)(x))
#define  OLED_W_SDA(x)    GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)(x))

void MyI2c_W_SCL(uint8_t Bitvalue)
{
	 GPIO_WriteBit(GPIO_PORT_SCL,GPIO_PIN_SCL,(BitAction)Bitvalue);
	 delay_us(10);
}

void MyI2c_W_SDA(uint8_t Bitvalue)
{
	 GPIO_WriteBit(GPIO_PORT_SDA,GPIO_PIN_SDA,(BitAction)Bitvalue);
	 delay_us(10);
}

void MyI2c_R_SDA(void)
{
	u8 BitValue;
	BitValue=GPIO_ReadInputDataBit(GPIO_PORT_SDA,GPIO_Pin_11);
	delay_us(10);
	return BitValue;
}

然后就可以开始写我们刚刚的几个部分

1 起始信号

首先是起始信号根据图示来,在SCL高的时候,拉低SDA即可,这里代码我们先将SCL和SDA都拉高,虽然初始化都是拉高了的,但是有一个信号是重复起始信号,这里防止出错直接先拉高。

void Myi2c_Start(void)
{
	  MyI2c_W_SDA(1);
	  MyI2c_W_SCL(1);
    
		MyI2c_W_SDA(0);
	  MyI2c_W_SCL(0); 
}

2 停止信号

先拉低两根线,之后先释放SCK,再释放SDA

void Myi2c_End(void)
{
	  MyI2c_W_SCL(0); 
	  MyI2c_W_SDA(0);
	  MyI2c_W_SCL(1);
	  MyI2c_W_SDA(1);
}

3 发送一个字节

传入一个字节,因为这个时序是按高位先行,所以我们先想办法发送高位

代码,拿要发送的字节与上0x80取出高位,然后依次右移0x80即可,这样就能取出一整个字节,在放上数据后,就可以让主机释放时钟线了,这里SDA的传入虽然是0x80 0x40这类值,但是在上面标红的地方有说到我们这里进行了强制类型转换,然后这个类型是个枚举类型,不是0就是1.

void Myi2c_SendByte(uint8_t Byte)
{
	 uint8_t i;
	 for(i=0;i<8;i++)
	 {
				MyI2c_W_SDA(Byte & (0x80 >>i));
				MyI2c_W_SCL(1);
				MyI2c_W_SCL(0);
	 }
}

4 接收一个字节

和接收一样,在SCL低电平的时候,SDA可以改变数据,但是要记得这个数据是从机发来的,我们只需要给他发送接收命令就好,并不需要管从机设备是如何发送过来的。

直接释放SDA线让从机能够放数据,然后释放SCL保存数据到Byte,如果读的电平为1,则保存数据到Byte,这里使用按位或操作,SDA为1时,进入到if判断,让Byte的最高位为1,如果没进入这个判断,那么那一位自然就是0了。

uint8_t Myi2c_ReadByte()
{
	  uint8_t i;
	  uint8_t Byte=0x00;
		MyI2c_W_SDA(1); //释放SDA让从机有SDA控制权
	  for(i=0;i<8;i++)
		{
				MyI2c_W_SCL(1);
				if(MyI2c_R_SDA()==1)
				{
					 Byte |= (0x80>>i);
				}
				MyI2c_W_SCL(0);
		}
}

5 发送应答信号

其实和发送一个数据差不多,就是接收完数据后发送一个位给从机,表示应答。

这里我们和发送数据一样,把数据放到SDA,然后释放scl再控制scl即可

uint8_t Myi2c_SendAck(uint8_t ACK)
{
			MyI2c_W_SDA(ACK);
			MyI2c_W_SCL(1);
			MyI2c_W_SCL(0);
}

6 接收应答

主机在发送完一个字节后,从机反馈自己是不是有收到数据,等于和刚刚的主机接收数据一样

 还是和刚刚一样,先释放掉SDA让从机有控制权,然后直接释放SCL接收数据就好

uint8_t Myi2c_ReadACK(void)
{
	  uint8_t ACK;
		MyI2c_W_SDA(1); //释放SDA让从机有SDA控制权
		MyI2c_W_SCL(1);
		ACK = MyI2c_R_SDA();
		MyI2c_W_SCL(0);
		return ACK;
}

7 主函数测试\

测试是否能够跑通(直接拿主机呼叫设备号,看看设备是否会有ACK信号

这里我直接先拿了一个板载的EEPROM测试芯片是AT24C02

因为我板载的就有一个这个,现在先拿他测一下我们的IIC能不能跑先去翻手册和原理图

芯片手册上说前四位固定组成,后三位可以根据电平更改,接高为高,接低为低,板载的三根线都直接接地,然后就是最后一位为读写位,1为读 0为写,如果有匹配的设备则设备返回0

这里引脚为PB6 和PB7 我们把初始化引脚改一下测试一下

 

 主函数

int main(void)
{	
	u8 ACKBit=0;
	
  /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
  USART_Config();
	delay_init();
	//PWM_LEDInit();
  Myiic_GPIO_Init();
	
	//发送设备号测试是否有应答信号
	Myi2c_Start();
	Myi2c_SendByte(0xA2);
	ACKBit=Myi2c_ReadACK();
	Myi2c_End();
	
	printf("%d\r\n",ACKBit);
	
	
  while(1)
	{	 
	}	
}

测试打印,第一次发送0xA0 第二次发送了0xA2  测试结果如下 ,是成功了的

然后我们开始写OLED的驱动

由于不想帖子拉的太长,所以EEPROM和OLED屏幕的详细使用会单独开两个帖子。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值