14. STM32——软件IIC

6 篇文章 1 订阅

IIC协议层

在这里插入图片描述

空闲状态

当IIC总线SDA以及SCL均处于高电平时,规定此状态为空闲状态,对应输出状态为高阻态(各场器件输出效应管截止,导致场效应管电阻很大),由上拉电阻将电平拉高。

起始信号

起始条件:SCL线是高电平时,SDA线从高电平向低电平切换

在这里插入图片描述

在这里插入图片描述

先写SDA,容易写程序,容易理解

//模拟IIC起始信号
static void I2C_START(void)
{
	I2C_SDA_SET();
	I2C_SCL_SET(); //将时钟线和数据线全部拉高,让其处于空闲状态
	delay_us(1);
	
	I2C_SDA_CLR(); //拉低数据线,产生下降沿
	delay_us(1);
	
	I2C_SCL_CLR(); //拉低时钟线
	delay_us(1);
}

停止信号

停止条件:SCL线是高电平时,SDA线从低电平向高电平切换

在这里插入图片描述

在这里插入图片描述

先写SDA,容易写程序,容易理解

//模拟IIC停止信号
static void I2C_STOP(void)
{
	I2C_SDA_CLR(); //拉低数据线,准备好
	delay_us(1);
	
	I2C_SCL_SET(); //拉高时钟线
	delay_us(1);
	
	I2C_SDA_SET(); //拉高数据线,产生上升沿
	delay_us(1);
}

数据有效性

SDA的高或低电平状态只有在 SCL 线的时钟信号是低电平时才能改变。

SDA在SCL的上升沿到来前准备好,并在下降沿到来之前必须稳定。
在这里插入图片描述

在这里插入图片描述

//写入一位数据
static void I2C_WriteByte(unsigned char I2C_Byte)
{
	unsigned char i = 0;
	
	for(i=0;i<8;i++) //共有8个字节,循环8次
	{
		I2C_SCL_CLR(); //拉低时钟线,为数据线的跳变提供条件
		delay_us(1);
		
		if( I2C_Byte & 0x80 ) //从最高位传输,XXXX XXXX & 1000 0000,1&X=X
		{
			I2C_SDA_SET(); //为1则拉高数据线
		}
		else
		{
			I2C_SDA_CLR(); //为0则拉低数据线
		}
		
		I2C_Byte <<= 1; //左移一位,下一位
		delay_us(1);
		
		I2C_SCL_SET(); //每传输完一位数据,拉高时钟线
		delay_us(1);
	}
	
	I2C_SCL_CLR(); //拉低时钟线
	delay_us(1);
	
	I2C_Wait_Ack(); //接收应答信号
}

应答信号

对于反馈有效应答位ACK的要求是,接收器在第九个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。

在这里插入图片描述

//IIC模拟应答信号
static unsigned char I2C_Wait_Ack(void)
{
	unsigned char Ack;
	
	I2C_SCL_CLR(); //拉低时钟线,准备好传输第9个时钟脉冲
	delay_us(1);
	
	I2C_SDA_SET(); //拉高数据线
	delay_us(1);
	
	I2C_SCL_SET(); //拉高时钟线,传输第9个时钟脉冲
	delay_us(1);
	
	if( I2C_SDA_Read() ) //判断从机数据应答信号,1则为无效应答,0则为有效应答
	{
		Ack = I2C_NO_Ack; //如果从机拉拉高数据线,反馈无效应答位Ack
	}
	else
	{
		Ack = I2C_Ack; //如果从机拉低数据线,反馈有效应答位Ack
	}
	
	I2C_SCL_SLR(); //拉低时钟线,9个脉冲传输完毕
	delay_us(1);
	
	return Ack; //返回应答
}

配置引脚初始化

void OLED_GPIO_Init(void)
{
	GPIO_InitTypeDef I2C_GPIOStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能APB2时钟
	
	//PB0 -> SCL    PB1 -> SDA
	I2C_GPIOStructure.GPIO_Mode  = GPIO_Mode_Out_OD; //连接到总线的器件必须配置为开漏输出
	I2C_GPIOStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1;
	I2C_GPIOStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &I2C_GPIOStructure); //初始化GPIOB
	
	I2C_SDA_SET();
	I2C_SCL_SET(); //将时钟线和数据线全部拉高,让其处于空闲状态
	
}

整合软件IIC代码

oled.c

#include "stm32f10x.h"
#include "oled.h"
#include "SysTick.h"
#include "oledfront.h"

void OLED_GPIO_Init(void)
{
	GPIO_InitTypeDef I2C_GPIOStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	I2C_GPIOStructure.GPIO_Mode  = GPIO_Mode_Out_OD;
	I2C_GPIOStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1;
	I2C_GPIOStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &I2C_GPIOStructure);
	
	I2C_SDA_SET();
	I2C_SCL_SET();
	
}

static void I2C_Start(void)
{
	I2C_SDA_SET();
	I2C_SCL_SET();
	delay_us(1);
	
	I2C_SDA_CLR();
	delay_us(1);
	
	I2C_SCL_CLR();
	delay_us(1);
}

static void I2C_Stop(void)
{
	I2C_SDA_CLR();
	delay_us(1);
	
	I2C_SCL_SET();
	delay_us(1);
	
	I2C_SDA_SET();
	delay_us(1);
}

static unsigned char I2C_Wait_Ack(void)
{
	unsigned char Ack;
	
	I2C_SCL_CLR();
	delay_us(1);
	
	I2C_SDA_SET();
	delay_us(1);
	
	I2C_SCL_SET();
	delay_us(1);
	
	if( I2C_SDA_Read() )
	{
		Ack = I2C_NO_Ack;
	}
	else
	{
		Ack = I2C_Ack;
	}
	
	I2C_SCL_CLR();
	delay_us(1);
	
	return Ack;
}

static void I2C_WriteByte(unsigned char I2C_Byte)
{
	unsigned char i = 0;
	
	for(i=0;i<8;i++)
	{
		I2C_SCL_CLR();
		delay_us(1);
		
		if(I2C_Byte & 0x80)
		{
			I2C_SDA_SET();
		}
		else
		{
			I2C_SDA_CLR();
		}
		
		I2C_Byte <<= 1;
		delay_us(1);
		
		I2C_SCL_SET();
		delay_us(1);
	}
	
	I2C_SCL_CLR();
	delay_us(1);
	
	I2C_Wait_Ack();
}

oled.h

#ifndef _OLED_H
#define _OLED_H

#define	I2C_SCL_SET()  GPIO_SetBits(GPIOB, GPIO_Pin_0)
#define I2C_SCL_CLR()  GPIO_ResetBits(GPIOB, GPIO_Pin_0)
#define I2C_SDA_SET()  GPIO_SetBits(GPIOB, GPIO_Pin_1)
#define I2C_SDA_CLR()  GPIO_ResetBits(GPIOB, GPIO_Pin_1)
#define I2C_SDA_Read() GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_1)

#define I2C_Ack    0
#define I2C_NO_Ack 1

void OLED_GPIO_Init(void);
static void I2C_Start(void);
static void I2C_Stop(void);
static unsigned char I2C_Wait_Ack(void);
static void I2C_WriteByte(unsigned char I2C_Byte);

#endif
  • 17
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是基于STM32F103的软件I2C代码示例: ```c #include "stm32f10x.h" #define I2C_SPEED 100000 // I2C速度设置为100kHz // I2C总线上设备的地址 #define DEVICE_ADDRESS 0xA0 // I2C总线上的数据缓存 uint8_t i2c_data_buf[256]; // I2C总线上的数据长度 uint16_t i2c_data_len = 0; // I2C总线的初始化函数 void I2C_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; // 打开I2C时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 打开GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置I2C_SCL和I2C_SDA的GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出 GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置I2C I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED; I2C_Init(I2C1, &I2C_InitStructure); // 使能I2C I2C_Cmd(I2C1, ENABLE); } // 向I2C总线上发送一个字节 void I2C_SendByte(uint8_t byte) { // 等待I2C总线空闲 while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // 发送START信号 I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 发送设备地址 I2C_Send7bitAddress(I2C1, DEVICE_ADDRESS, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 发送数据 I2C_SendData(I2C1, byte); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 发送STOP信号 I2C_GenerateSTOP(I2C1, ENABLE); } // 从I2C总线上接收一个字节 uint8_t I2C_RecvByte(void) { uint8_t byte; // 等待I2C总线空闲 while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // 发送START信号 I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 发送设备地址 I2C_Send7bitAddress(I2C1, DEVICE_ADDRESS, I2C_Direction_Receiver); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // 接收数据 while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); byte = I2C_ReceiveData(I2C1); // 发送STOP信号 I2C_GenerateSTOP(I2C1, ENABLE); return byte; } // 向I2C总线上发送一段数据 void I2C_SendData(uint8_t *data, uint16_t len) { uint16_t i; // 发送数据 for (i = 0; i < len; i++) { I2C_SendByte(data[i]); } } // 从I2C总线上接收一段数据 void I2C_RecvData(uint8_t *data, uint16_t len) { uint16_t i; // 接收数据 for (i = 0; i < len; i++) { data[i] = I2C_RecvByte(); } } // 从I2C总线上读取一段数据 void I2C_ReadData(uint8_t addr, uint8_t *data, uint16_t len) { // 发送读取命令 I2C_SendByte(addr); // 接收数据 I2C_RecvData(data, len); } // 向I2C总线上写入一段数据 void I2C_WriteData(uint8_t addr, uint8_t *data, uint16_t len) { // 发送写入命令 I2C_SendByte(addr); // 发送数据 I2C_SendData(data, len); } ``` 这是一个简单的I2C总线驱动代码,可以根据需要进行修改和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值