软件模拟IIC驱动OLED 附源码

软件模拟IIC驱动OLED 附源码

前言

CSDN上有很多的关于模拟IIC驱动的代码,都讲解的特别好。但对于想短时间理解并使用IIC的同学们而言是很枯燥困难的。所以我想由果到因,从写好的代码开始讲解IIC,希望可以帮助大家短时间掌握使用IIC进行驱动。

源代码在最下面给出

1、相关宏定义

//宏定义
#define I2C_GPIO_CLK			 	RCC_AHB1Periph_GPIOB		/* GPIO端口时钟 */
#define I2C_GPIO_PORT               GPIOB
#define I2C_SCL_PIN                 GPIO_Pin_6
#define I2C_SDA_PIN                 GPIO_Pin_5

#define I2C_SCL_H                   GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN)  	//拉高时钟线
#define I2C_SCL_L                   GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN) 	//拉低时钟线
#define I2C_SDA_H                   GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN)  	//拉高数据线
#define I2C_SDA_L                   GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN)  //拉低数据线

#define I2C_SDA_READ    GPIO_ReadInputDataBit(I2C_GPIO_PORT, I2C_SDA_PIN)	/* 读SDA口线状态 */

这就相关的宏定义,我们进行代码移植的时候需要在这里将相关的底层操作换成对应平台的就可以了。

2、初始化相应GPIO

void I2C_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(I2C_GPIO_CLK, ENABLE);	/* 打开GPIO时钟 */

	GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  	
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;  	/* 开漏输出 */
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);

	/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
	i2c_Stop();			//这是ICC的停止信号
}

各位在移植代码的时候,这一块也需要更换,因为不同平台GPIO的初始化函数是不太一样的,不过目的都只是初始化GPIO,在学习点灯的时候就已经配置过了,相信大家是很熟悉的。

3、起始信号

当 SCL 线是高电平时 SDA 线从高电平向低电平切换,表示通讯的起始。

void i2c_Start(void)
{
	/* 当SCL高电平时,SDA出现一个下降沿表示I2C总线启动信号 */
	I2C_SDA_H;		//拉高数据线
	I2C_SCL_H;		//拉高时钟线
	Delay();		//延时
	I2C_SDA_L;		//拉低数据线
	Delay();
	I2C_SCL_L;		//拉低时钟线
}

现在我们按照这个函数代码写一遍对应的IIC数据线与时钟线的状态
在这里插入图片描述

很明显我们可以发现当时钟线为高电平的时候,数据线存在一个由高到低的跳变。
这个就是IIC协议里面的起始信号。

4、停止信号

当 SCL 是高电平时 SDA 线由低电平向高电平切换,表示通讯的停止。

void i2c_Stop(void)
{
	/* 当SCL高电平时,SDA出现一个上升沿表示I2C总线停止信号 */
	I2C_SDA_L;
	I2C_SCL_H;
	Delay();
	I2C_SDA_H;
	Delay();
	I2C_SCL_L;
}

同样我们按照这个函数代码写一遍对应的IIC数据线与时钟线的状态
在这里插入图片描述

很明显我们可以发现当时钟线为高电平的时候,数据线存在一个由低到高的跳变。
这个就是IIC协议里面的停止信号。

5、发送一个字节数据

void i2c_SendByte(uint8_t _ucByte)
{
	uint8_t i;

	for (i = 0; i < 8; i++)		//循环发送8个数据,刚好一个字节
	{		
		if (_ucByte & 0x80)		//取出待发送字节_ucByte 的第8位
		{
			I2C_SDA_H;			//最高位是1就拉高数据线,是0就拉低数据线
		}
		else
		{
			I2C_SDA_L;
		}
		Delay();				
		I2C_SCL_H;				//拉高时钟线
		Delay();
		I2C_SCL_L;			    //拉低时钟线
		if (i == 7)				//i=7意味着已经发送了8位数据了
		{
			 I2C_SDA_H; // 释放总线
		}
		_ucByte <<= 1;	/左移一个bit,左移之后第7位就变成了第8位,就可以通过_ucByte & 0x80取出来
		Delay();
	}
}

在这里插入图片描述
很明显我们可以发现IIC发送数据有几个特点
1、在SCL为高电平的时候读取SDA的数据
2、在SCL为低电平的时候SDA的数据进行改变

6、读取一个字节的数据

uint8_t i2c_ReadByte(void)
{
	uint8_t i;
	uint8_t value;

	/* 读到第1个bit为数据的bit7 */
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;			//左移一位
		I2C_SCL_H;
		Delay();
		if (I2C_SDA_READ)		//I2C_SDA_READ这一个宏定义,读取并返回数据线的状态
		{
			value++;
		}
		I2C_SCL_L;
		Delay();
	}
	return value;
}

在这里插入图片描述
在这里我们可以发现IIC读取数据有两个特点
1、在SCL为低电平的时候读取SDA的数据
2、在SCL为高电平的时候SDA的数据进行改变

读取应答ACK信号

使用模拟IIC发送数据的时候,每发送一个字节的数据之后需要释放总线控制,等待从机发送响应信号。通过接收从机返回的响应信号来判断从机是否需要继续接收数据。
从机发送响应信号由从机本身决定与我们要写的代码无关

uint8_t i2c_WaitAck(void)
{
	uint8_t re;

	I2C_SDA_H;
	Delay();
	I2C_SCL_H;	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	Delay();
	if (I2C_SDA_READ)	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	I2C_SCL_L;
	Delay();
	return re;
}

在这里插入图片描述
这是一个有返回值的函数,我们依然在SCL为低电平的时候读取此时的SDA值,如果SDA为低电平,就代表从机产生了应答信号。

以上就是我们驱动OLED所需要的模拟IIC函数了,接下来我们通过上面的函数写出发给OLED的数据与命令函数。

向OLED发送命令函数

void WriteCmd(u8 command)
{
    i2c_Start();		//产生IIC时序的起始信号
    i2c_SendByte(0x78);	//发送OLED的地址
    i2c_WaitAck();     
    i2c_SendByte(0x00);//发送OLED命令寄存器地址
    i2c_WaitAck();
    i2c_SendByte(command);//发送相关指令
    i2c_WaitAck();
    i2c_Stop();		//结束IIC通讯
}

这段代码里面,OLED地址以及寄存器地址是硬件固定的,对于所有的OLED都是这个,所以大家在移植时不用改。
然后很显然应答函数i2c_WaitAck是有返回值的,但我们并没有在此处检测他的返回值。我个人理解是对于驱动OLED我们是固定要发送三个字节的数据的,所以我们不要检测它的返回值,换而言之,前两个应答信号的返回值一定是0。我通过代码检测其返回值也确实如此。

向OLED发送数据函数

void WriteDat(u8 data)
{
    i2c_Start();
    i2c_SendByte(0x78);//OLED地址
    i2c_WaitAck();      //可能需要检测返回
    i2c_SendByte(0x40);//数据寄存器地址
    if(!i2c_WaitAck())
    i2c_SendByte(data);
    i2c_WaitAck();
    i2c_Stop();
}

这个代码与发送命令函数基本一样,唯一的区别就是,发送的寄存器地址不同。这也是OLED从机判断检测接收的字节是命令还是数据的方式。

结束

好了,到这里我们就已经用模拟IIC写好的对OLED的驱动。之后我们只需要通过WriteCmd和WriteDat这两个函数对OLED进行命令与数据的发送即可以实现OLED显示了。

IIC代码

链接:https://pan.baidu.com/s/1v72Tp4gSxV-yFcisiMcH6Q
提取码:lel3

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32模拟IIC代码是在STM32单片机上实现IIC通讯协议的模拟仿真代码。下面以F103为例,以软件模拟IIC通信为主要讲解。 首先,我们需要定义IIC相关的GPIO引脚及其功能,以及一些IIC初始化函数。 ```c GPIO_InitTypeDef GPIO_InitStruct; void IIC_Init(void) { //使能GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //配置IIC_SCL引脚 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); //配置IIC_SDA引脚 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); //初始化IIC总线 IIC_Start(); IIC_Stop(); } void IIC_Start(void) { IIC_SDA_OUT(); IIC_SCL_HIGH(); IIC_SDA_HIGH(); IIC_DELAY(); IIC_SDA_LOW(); IIC_DELAY(); IIC_SCL_LOW(); } void IIC_Stop(void) { IIC_SDA_OUT(); IIC_SCL_LOW(); IIC_SDA_LOW(); IIC_DELAY(); IIC_SCL_HIGH(); IIC_DELAY(); IIC_SDA_HIGH(); } void IIC_SendByte(uint8_t data) { uint8_t i; IIC_SDA_OUT(); for (i = 0; i < 8; i++) { IIC_SCL_LOW(); if ((data & 0x80) == 0x80) IIC_SDA_HIGH(); else IIC_SDA_LOW(); data <<= 1; IIC_DELAY(); IIC_SCL_HIGH(); IIC_DELAY(); } IIC_SCL_LOW(); } uint8_t IIC_ReceiveByte(void) { uint8_t i, data = 0; IIC_SDA_IN(); for (i = 0; i < 8; i++) { IIC_SCL_LOW(); IIC_DELAY(); IIC_SCL_HIGH(); data <<= 1; if (IIC_READ_SDA()) data |= 0x01; IIC_DELAY(); } IIC_SCL_LOW(); return data; } ``` 以上代码是模拟IIC基本操作的函数,包括初始化IIC总线、启动和停止信号的发送、发送和接收数据等。其中,根据实际需求可能需要根据具体的GPIO引脚和时钟配置来修改。 在使用模拟IIC通信时,需要注意的是,STM32的I2C外设与模拟IIC通信并不相同。I2C外设直接在硬件层面上实现了I2C通信,而模拟IIC需要通过GPIO口的软件控制来模拟实现。所以在使用STM32模拟IIC时,需要禁用I2C外设,并根据实际需求修改代码中的引脚定义。 以上是关于STM32模拟IIC代码的基本讲解,通过使用这些函数,我们可以在STM32单片机上实现模拟IIC通信。当然,具体的代码实现可能会因为不同的项目需求而有所变化,需要根据实际情况进行修改与优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值