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