软件i2c通过模拟i2c时序配置普通的gpio引脚,首先封装一个函数
static void OLED_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_pp;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
OLED_SCLK_Set();
OLED_SDIN_Set();
}
在起始条件未开始之前把SDA,SCL拉高
接着封装模拟起始信号函数,在SCL高电平时,SDA从高电平向低电平跳变,发出起始信号,最后时钟线置低数据开始准备传输。
static void OLED_IIC_Start(void)
{
OLED_SCLK_Set();
OLED_SDIN_Set();
delay_us(1);
OLED_SDIN_CLr();
delay_us(1);
OLED_SCLK_CLr();
delay_us(1);
}
然后是封装i2c停止信号,在时钟线为高电平期间,SDA由低变高
static void OLED_IIC_Stop(void)
{
OLED_SDIN_CLr();
delay_us(1);
OLED_SCLK_Set(); / * 时钟线置高 */
delay_us(1);
OLED_SDIN_Set();
delay_us(1);
}
4.应答信号
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号,就是由从机去拉低SDA线。
当主机发送了8位数据后,会再产生一个时钟,此时主机放开SDA的控制,读取SDA电平,在上拉电阻的影响下,
此时SDA默认为高,必须从机拉低,以确认收到数据。
对于反馈有效应答位ACK的要求是,接收器在第九个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平,也就是说在时钟线为高期间数据线为低电平则为有效反馈。
static unsigned char IIC_Wait_Ack(void)
{
unsigned char ack;
OLED_SCLK_CLr();
delay_us(1);
OLED_SDIN_Set();
delay_us(1);
OLED_SCLK_Set();
delay_us(1);
if(OLED_READ_SDIN())
ack = IIC_NO_ACK;
delay_us(1);
return ack;
}
发送一个字节i2c数据,数据高位先行
此涉及数据有效性
SDA的高或低电平状态只有在 SCL 线的时钟信号是低电平时才能改变。
SDA在SCLSCL的上升沿到来前准备好,并在下降沿到来之前必须稳定。
为高电平时表示有效数据,SDA为高电平表示“1”,低电平表示“0”;SCL为低电平时表示无效数据,此时
SDA会进行电平切换,为下次数据表示做准备。数据有效性示意图如图所示。
static void Write_IIC_Byte(unsigned char IIC_Byte)
{
unsigned char i; //定义变量
for(i=0;i<8;i++)
{
OLED_SCLK_CLr();
delay_us(1);
if(IIC_Byte & 0x80)
OLED_SDIN_Set();
else
OLED_SDIN_CLr();
IIC_Byte <<= 1;
delay_us(1);
OLED_SCLK_Set();
delay_us(1);
}
OLED_SCLK_CLr();
delay_us(1);
while(IIC_Wait_Ack());
}
I2c写命令
static void Write_IIC_Command(unsigned char IIC_Command)
{
OLED_IIC_Start();
Write_IIC_Byte(0x78);
Write_IIC_Byte(0x00);
Write_IIC_Byte(IIC_Command);
OLED_IIC_Stop();
}
I2c写数据
static void Write_IIC_Data(unsigned char IIC_Data)
{
OLED_IIC_Start();
Write_IIC_Byte(0x78);
Write_IIC_Byte(0x40);
Write_IIC_Byte(IIC_Data);
OLED_IIC_Stop();
}
代码整合
.c代码
#include "stm32f10x.h"
#include "oled.h"
#include "oledfont.h"
#include "delay.h"
static void OLED_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
OLED_SCLK_Set();
OLED_SDIN_Set();
}
static void OLED_IIC_Start(void)
{
OLED_SCLK_Set();
OLED_SDIN_Set();
delay_us(1);
OLED_SDIN_CLr();
delay_us(1);
OLED_SCLK_CLr();
delay_us(1);
}
static void OLED_IIC_Stop(void)
{
OLED_SDIN_CLr();
delay_us(1);
OLED_SCLK_Set();
delay_us(1);
OLED_SDIN_Set();
delay_us(1);
}
static unsigned char IIC_Wait_Ack(void)
{
unsigned char ack;
OLED_SCLK_CLr();
delay_us(1);
OLED_SDIN_Set();
delay_us(1);
OLED_SCLK_Set();
delay_us(1);
if(OLED_READ_SDIN())
ack = IIC_NO_ACK; /
else
ack = IIC_ACK;
OLED_SCLK_CLr();
delay_us(1);
return ack;
}
static void Write_IIC_Byte(unsigned char IIC_Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OLED_SCLK_CLr();
delay_us(1);
if(IIC_Byte & 0x80)
OLED_SDIN_Set();
else
OLED_SDIN_CLr();
IIC_Byte <<= 1;
delay_us(1);
OLED_SCLK_Set();
delay_us(1);
}
OLED_SCLK_CLr();
delay_us(1);
while(IIC_Wait_Ack());
}
static void Write_IIC_Command(unsigned char IIC_Command)
{
OLED_IIC_Start();
Write_IIC_Byte(0x78);
Write_IIC_Byte(0x00);
Write_IIC_Byte(IIC_Command);
OLED_IIC_Stop();
}
static void Write_IIC_Data(unsigned char IIC_Data)
{
OLED_IIC_Start();
Write_IIC_Byte(0x78);
Write_IIC_Byte(0x40);
Write_IIC_Byte(IIC_Data);
OLED_IIC_Stop();
.h代码
#ifndef _OLED_H_ #define _OLED_H_ #include "stm32f10x.h" #define OLED_SCLK_Set() GPIO_SetBits(GPIOB,GPIO_Pin_0) #define OLED_SCLK_CLr() GPIO_ResetBits(GPIOB,GPIO_Pin_0) #define OLED_SDIN_Set() GPIO_SetBits(GPIOB,GPIO_Pin_1) #define OLED_SDIN_CLr() GPIO_ResetBits(GPIOB,GPIO_Pin_1) #define OLED_READ_SDIN() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) #define IIC_ACK 0 #define IIC_NO_ACK 1



