软件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(); elseOLED_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;/elseack = 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();elseOLED_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