嵌入式通信协议——模拟IIC

IIC总线协议介绍        

IIC:Inter Integrated Circuit,集成电路总线,是一种同步 串行 半双工通信总线。

IIC总线结构图

① 由时钟线SCL和数据线SDA组成,并且都接上拉电阻,确保总线空闲状态为高电平

② 总线支持多设备连接,允许多主机存在,每个设备都有一个唯一的地址

③ 连接到总线上的数目受总线的最大电容400pf限制

④ 数据传输速率:标准模式100k bit/s  快速模式400k bit/s 高速模式3.4Mbit/s

起始信号(S):当SCL为高电平时,SDA从高电平变为低电平

停止信号(P):当SCL为高电平时,SDA从低电平变为高电平

应答信号:上拉电阻影响下SDA默认为高,而从机拉低SDA就是确认收到数据即ACK,否则NACK

IIC总线协议函数

起始信号

void iic_start(void)
{                         /* SCL为高电平期间, SDA从高电平往低电平跳变*/
    	IIC_SDA ( 1 );	
	    IIC_SCL ( 1 );
    	iic_delay( );
 	    IIC_SDA ( 0 );	
 	    iic_delay( );
    	IIC_SCL ( 0 );	
 	    iic_delay( );  /* 钳住总线, 准备发送/接收数据 */
}

停止信号

void iic_stop(void)
{ /* SCL为高电平期间, SDA从低电平往高电平跳变*/
    	IIC_SDA ( 0 );	
	    iic_delay( );
 	    IIC_SCL ( 1 );	
 	    iic_delay( );
    	IIC_SDA ( 1 ); 	/* 发送总线停止信号*/
 	    iic_delay( );
}

检测应答信号

uint8_t iic_wait_ack (void) /* return 1:fail 0:succeed*/
{	
	IIC_SDA (1);            /* 主机释放SDA线 */
	iic_delay( );
	IIC_SCL (1);            /* 从机返回ACK*/ 	
 	iic_delay( );
	if ( IIC_READ_SDA )     /* SCL高电平读取SDA状态*/ 
	{
		iic_stop();	        /* SDA高电平表示从机nack */ 
		return 1;
	}
	IIC_SCL(0);	            /* SCL低电平表示结束ACK检查 */ 
 	iic_delay( );
	return 0;
}

发送应答信号

void iic_ack(void)
{ 
    	IIC_SCL (0);	
	    iic_delay( );
 	    IIC_SDA (0);  /* 数据线为低电平,表示应答 */
 	    iic_delay( );
    	IIC_SCL (1); 	
 	    iic_delay( );
}

发送非应答信号

void iic_nack(void)
{ 
    	IIC_SCL (0);	
	    iic_delay( );
 	    IIC_SDA (1);  /* 数据线为高电平,表示非应答 */
 	    iic_delay( );
    	IIC_SCL (1); 	
 	    iic_delay( );
}

发送1字节数据

void iic_send_byte(uint8_t data)
{
	for (uint8_t t = 0; t < 8; t++)
	{	/* 高位先发 */
		IIC_SDA((data & 0x80) >> 7);
 		iic_delay( );
 		IIC_SCL ( 1 );	
 		iic_delay( );
 		IIC_SCL ( 0 );
		data <<= 1; /* 左移1位, 用于下一次发送 */
	}
	IIC_SDA ( 1 ); 	/* 发送完成,主机释放SDA线 */ 
}

读取1字节数据

uint8_t iic_read_byte (uint8_t ack) /* 1:ack 0:nack*/
{ 
	uint8_t receive = 0 ;
	for (uint8_t t = 0; t < 8; t++)
	{	/* 高位先输出,先收到的数据位要左移 */ 
		receive <<= 1;		
		IIC_SCL ( 1 );	
 		iic_delay( );
		if ( IIC_READ_SDA ) receive++;
 		IIC_SCL ( 0 );
		 iic_delay( );
	}
	if ( !ack ) iic_nack();
	else iic_ack();
	return receive; 	
}

使用模拟IIC驱动OLED屏幕

部分代码

/*全局变量*********************/

/**
  * OLED显存数组
  * 所有的显示函数,都只是对此显存数组进行读写
  * 随后调用OLED_Update函数或OLED_UpdateArea函数
  * 才会将显存数组的数据发送到OLED硬件,进行显示
  */
uint8_t OLED_DisplayBuf[8][128];

/*********************全局变量*/


/*引脚配置*********************/

/**
  * 函    数:OLED写SCL高低电平
  * 参    数:要写入SCL的电平值,范围:0/1
  * 返 回 值:无
  * 说    明:当上层函数需要写SCL时,此函数会被调用
  *           用户需要根据参数传入的值,将SCL置为高电平或者低电平
  *           当参数传入0时,置SCL为低电平,当参数传入1时,置SCL为高电平
  */
void OLED_W_SCL(uint8_t BitValue)
{
	/*根据BitValue的值,将SCL置高电平或者低电平*/
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, (GPIO_PinState)BitValue);
	
	/*如果单片机速度过快,可在此添加适量延时,以避免超出I2C通信的最大速度*/
	//...
}

/**
  * 函    数:OLED写SDA高低电平
  * 参    数:要写入SDA的电平值,范围:0/1
  * 返 回 值:无
  * 说    明:当上层函数需要写SDA时,此函数会被调用
  *           用户需要根据参数传入的值,将SDA置为高电平或者低电平
  *           当参数传入0时,置SDA为低电平,当参数传入1时,置SDA为高电平
  */
void OLED_W_SDA(uint8_t BitValue)
{
	/*根据BitValue的值,将SDA置高电平或者低电平*/
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, (GPIO_PinState)BitValue);
	
	/*如果单片机速度过快,可在此添加适量延时,以避免超出I2C通信的最大速度*/
	//...
}

/**
  * 函    数:OLED引脚初始化
  * 参    数:无
  * 返 回 值:无
  * 说    明:当上层函数需要初始化时,此函数会被调用
  *           用户需要将SCL和SDA引脚初始化为开漏模式,并释放引脚
  */
void OLED_GPIO_Init(void)
{
	uint32_t i, j;
	
	/*在初始化前,加入适量延时,待OLED供电稳定*/
	for (i = 0; i < 1000; i ++)
	{
		for (j = 0; j < 1000; j ++);
	}
	
	/*将SCL和SDA引脚初始化为开漏模式*/
	__HAL_RCC_GPIOC_CLK_ENABLE();
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
	GPIO_InitStructure.Pin = GPIO_PIN_11 | GPIO_PIN_12;
	GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	
	/*释放SCL和SDA*/
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/*********************引脚配置*/


/*通信协议*********************/

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

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

/**
  * 函    数:I2C发送一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void OLED_I2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	
	/*循环8次,主机依次发送数据的每一位*/
	for (i = 0; i < 8; i++)
	{
		/*使用掩码的方式取出Byte的指定一位数据并写入到SDA线*/
		/*两个!的作用是,让所有非零的值变为1*/
		OLED_W_SDA(!!(Byte & (0x80 >> i)));
		OLED_W_SCL(1);	//释放SCL,从机在SCL高电平期间读取SDA
		OLED_W_SCL(0);	//拉低SCL,主机开始发送下一位数据
	}
	
	OLED_W_SCL(1);		//额外的一个时钟,不处理应答信号
	OLED_W_SCL(0);
}

/**
  * 函    数:OLED写命令
  * 参    数:Command 要写入的命令值,范围:0x00~0xFF
  * 返 回 值:无
  */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_I2C_Start();				//I2C起始
	OLED_I2C_SendByte(0x78);		//发送OLED的I2C从机地址
	OLED_I2C_SendByte(0x00);		//控制字节,给0x00,表示即将写命令
	OLED_I2C_SendByte(Command);		//写入指定的命令
	OLED_I2C_Stop();				//I2C终止
}

/**
  * 函    数:OLED写数据
  * 参    数:Data 要写入数据的起始地址
  * 参    数:Count 要写入数据的数量
  * 返 回 值:无
  */
void OLED_WriteData(uint8_t *Data, uint8_t Count)
{
	uint8_t i;
	
	OLED_I2C_Start();				//I2C起始
	OLED_I2C_SendByte(0x78);		//发送OLED的I2C从机地址
	OLED_I2C_SendByte(0x40);		//控制字节,给0x40,表示即将写数量
	/*循环Count次,进行连续的数据写入*/
	for (i = 0; i < Count; i ++)
	{
		OLED_I2C_SendByte(Data[i]);	//依次发送Data的每一个数据
	}
	OLED_I2C_Stop();				//I2C终止
}

/*********************通信协议*/

  • 13
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值