09 标准库软件模拟I2C时序

引言:

你需要的I2C知识这里都有, 本文涵盖了I2C的基本知识和在各个主流平台的开发实例, 总之, 你需要的,都在这里, 后续我也会继续更新在嵌入式开发中常见的通信协议。

一、I2C的基本知识


1. I2C介绍

         IIC总线是Philips公司在八十年代初推出的一种串行、半双工总线,主要用于近距离、低速的芯片之间的通信;IIC总线有两根双向的信号线,一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步;IIC总线硬件结构简单,成本较低,因此在各个领域得到了广泛的应用

09 标准库软件模拟I2C时序_第1张图片

2 I2C的几种常见通信时序

主机向从机发送数据

从机向主机发送数据

主机先向从机发送数据,然后从机再向主机发送数据

   IIC总线是一种多主机总线,连接在IIC总线上的器件分为主机和从机 主机有权发起和结束一次通信,而从机只能被主机呼叫;当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生;

每个连接到IIC总线上的器件都有一个唯一的地址(7bit),且每个器件都可以作为主机也可以作为从机(同一时刻只能有一个主机),总线上的器件增加和删除不影响其他器件正常工作;IIC总线在通信时总线上发送数据的器件为发送器,接收数据的器件为接收器;

二、软件模拟I2C


#ifndef __MYI2C_H
#define __MYI2C_H

void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);

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

void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
}

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;         //开漏输出,,因为I2C总线默认是电阻弱上拉
	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);      //将模拟的两个引脚设置为默认高电平
}

//起始条件:在SCL为高电平的时候, 数据线由高变低
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

//结束条件:在scl的高电平期间, SDA由低变高
void MyI2C_Stop(void)          
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(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);         //scl为低电平的时候往数据线上准备数据
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;
	MyI2C_W_SDA(1);
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);         //时钟线为高的时候读取数据,这个期间需要保持稳定
		if (MyI2C_R_SDA() == 1){            //对应的哪一位为1, 就将其置1
			Byte |= (0x80 >> i);
		}
		MyI2C_W_SCL(0);
	}
	return Byte;
}


//确认信号,在SCL的高电平期间, 将sda拉低
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return AckBit;
}

以上便是软件模拟i2c的全部代码,说一下本质, 以及软件模拟i2c时序的优点, 本质就是控制GPIO引脚输出高低电平来模拟I2C通信的时序。

软件模拟I2C的优点: 因为在咱们的SOC内部虽然集成了I2C硬件控制器, 但是数量有限, 使用软件I2C就可以将具有GPIO功能的引脚改造成为i2c协议, 这也就是为什么这个协议如此强大和受欢迎。

这些代码, 我认为能看懂实在干嘛就行, 真正用的时候再来我这里复制过去, 改一下引脚, 哎呀, 忘记把引脚,时钟,封装成宏了, 算了, 大家在使用的时候全部替换一下, 替换成那么的引脚, 大家将这些经常发生变动的地方封装一下, 这样会很方便。

你可能感兴趣的:(单片机,单片机,嵌入式硬件)