IIC通信(STM32)

一、IIC概念

 1、两根通信线:SCL(Serial Clock)、SDA(Serial Data) 同步,半双工

 2、带数据应答

 3、支持总线挂载多设备(一主多从、多主多从)一般使用一主多从。一主多从的模式就是,主  机产生一个起始条件,之后以广播的形式发送一个设备地址到IIC总线上进行寻址,地址相同的就被选中之后就可以进行通信了。

二、IIC特征

1、SDA SCL 都是双向线路 都通过一个电流源或上拉电阻连接到正的电源电压, 当总线空闲时
这两条线路都是高电平。连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功
能。IIC总线上数据的传输速率在标准模式下可达 100kbit/s 在快速模式下可达 400kbit/s 在高速模
式下 可达 3.4Mbit/s 连接到总线的接口数量只由总线电容是 400pF 的限制决定.
PS: IIC不能使用推挽输出,原因是推挽输出驱动能力强,电平的跳变很快,而IIC又只有SDA一条线在数据传输。如果电平驱动能力太强,IIC进行输入输出切换过程会产生冲突,就会引起短路。

2、位传输、数据的有效性

每传输一个数据位就产生 一个时钟脉冲。SDA 线上的数据必须在时钟的高电平周期保持稳定数据

线的高或低电平状态只有SCL 线的时钟信号是低电平时才能改变(时钟线是低电平时才可以切

换数据线的高低电平,时钟线在高电平的时候数据线才会发送数据此时数据上的电平在时钟线为高

电平的时候要保持稳定)这样的数据才会有效。

3、起始和停止条件

*在IIC 总线中,唯一出现的是被定义为起始 S 和停止 P 条件

*其中一种情况是在 SCL 线是高电平时 SDA 线从高电平向低电平切换这个情况表示起始条件

*当 SCL 是高电平时 SDA 线由低电平向高电平切换表示停止条件

*起始和停止条件一般由主机产生 ,总线在起始条件后被认为处于忙的状态 在停止条件的某段时间后总线被认为再次处于空闲状态

*如果产生重复起始 Sr 条件而不产生停止条件 ,总线会一直处于忙的状态 ,此时的起始条件 S 和重复起始 Sr 条件在功能上是一样的 (见图 10),因此在本文档的剩余部分,符号 S 将作为一个通用 的术语既表示起始条件又表示重复起始条件,除非有特别声明的 Sr。

*如果连接到总线的器件合并了必要的接口硬件,那么用它们检测起始和停止条件十分简便,但是 没有这种接口的微控制器在每个时钟周期至少要采样 SDA 线两次来判别有没有发生电平切换

三、数据传输

1、字节格式
发送到 SDA 线上的每个字节必须为 8 位 ,每次传输可以发送的字节数量不受限制, 每个字节后必须跟一个响应位,首先传输的是数据的最高位 MSB(见图 6)。如果 从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节 ,可以使时钟线 SCL 保持低电平迫使主机进入等待状态。 当从机准备好接收下一个数据字节并 释放时钟线 SCL 后 ,数据传输继续。
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL(高),从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,主机 需要释放SDA(高)

2、响应

数据传输必须带响应 。相关的响应时钟脉冲由主机产生,在响应的时钟脉冲期间, 发送器释放 SDA 线 (高)
在响应的时钟脉冲期间 , 接收器必须将 SDA 线拉低, 使它在这个时钟脉冲的高电平期间保持稳定的低电平
通常被寻址的接收器在接收到的 每个字节 后 ,除了用 CBUS 地址开头的报文, 必须产生一个响应
*当从机不能响应从机地址时 ,(例如它正在执行一些实时函数不能接收或发送)从机必须使 数据线保持高电平主机然后产生一个停止条件终止传输或者产生重复起始条件开始新的传输
*如果从机接收器响应了从机地址但是在传输了一段时间后不能接收更多数据字节,主机必须 再一次终止传输。 这个情况用从机在第一个字节后没有产生响应来表示 。从机使数据线保持高电平 ,主机产生一 个停止或重复起始条件。
*如果传输中有主机接收器 ,它必须通过在 从机不产生时钟的最后一个字节不产生一个响应 ,向从机发送器通知数据结束 。从机 发送器必须释放数据线 ,允许主机产生一个停止或重复起始条件
*发送应答:主机在接收完一个字节之后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答(从机释放SDA)
*接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据(此时SDA的控制权是从机),判断从机是否应答,数据0表示应答,数据1表示非应答( 主机在接收之前,需要释放SDA,目的是让SDA控制回到从机,从机要发送应答数据
PS:主机释放SDA控制权和从机获得SDA控制权的时间几乎在同一时刻,主机释放SDA,让SDA自动的回到高电平,但是如果从机应答了,从机会抓住SDA不放(这样就产生应答了),反之,从机发送数据需要主机应答也是一样

 

四、仲裁和时钟同步

1、同步

所有主机在 SCL 线上产生它们自己的时钟来传输 I 2 C 总线上的报文 ,数据只在时钟的高电平周期有效 , 因此需要一个确定的时钟进行逐位仲裁。

PS:SDA一般都是主机来控制,只有从机应答或者主机读取从机数据的时候主机才会把SDA的控制权交给从机。SCL是由主机来控制的,当有多个主机的时候,会进行仲裁,SCL线低电平周期最长的会获得仲裁权,这时就会成为主机,其它主机有从机功能的和从机设备就会成为从机。

2、仲裁

*主机只能在总线空闲的时侯启动传输 ,两个或多个主机可能在起始条件的最小持续时间 (tHD;STA) 内 产生一个起始条件 ,结果在总线上产生一个规定的起始条件。

*当 SCL 线是高电平时 ,仲裁在 SDA 线发生 ,这样在其他主机发送低电平时 ,发送高电平的主机将断开它的数据输出级 ,因为总线上的电平与它自己的电平不相同。

*如果主机也结合了从机功能 ,而且在寻址阶段丢失仲裁 ,它很可能就是赢得仲裁的主机在寻址的器件 ,因此丢失仲裁的主机必须立即切换到它的从机模式。

 I 2 C 总线的地址和数据信息由赢得仲裁的主机决定 ,在仲裁过程中不会丢失信息。

五、广播呼叫地址

广播呼叫地址是用来寻址连接到 I 2 C 总线上的每个器件 ,但是 如果器件在广播呼叫结构中不需要任何数据 ,它可以通过不发出响应来忽略这个地址 ,如果器件要求从广播呼叫地址得到数据,
它会响应这个地址并作为从机 -接收器运转 ,第二个和接下来的字节会被能处理这些数据的每个从机接收器响应

六、读写时序

从机地址有7位和10位,一般都是使用七位,下面的图也是七位。

写时序:指定地址写,对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data),ps:下面图中设备地址中高7位才是设备地址,最低位表示主机要读数据还是写数据,0(写),1(读)

指定地址写:对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data),里面的地址会自动加一

读时序:当前地址读,对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)

读时序:指定地址读 对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data),地址会自动加一 ps:指定地址读主机先发送设备地址进行寻址,从机应答之后,主机继续发送指定写的地址,从机应答后,主机会继续产生一个起始条件,之后先从机发送设备地址进行寻址,从机应答之后,主机再进行读取数据(SDA的控制权在从机),主机读取到数据之后再进行应答(SDA的控制权回到主机),最后主机产生一个终止条件。这样就是一个完整的时序了。

PS:任何不想要继续接收数据设备,可以在接收最后一个数据时,直接不应答,这样就停止接收数据了。

七、代码

硬件IIC:

#include "oled.h"
#include "codetab.h"
void I2cOledConfig(void)
{GPIO_InitTypeDef oledGpioInit;I2C_InitTypeDef oledI2cInit;//初始化时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);//初始化PB6--SCL PB7--SDAoledGpioInit.GPIO_Mode  = GPIO_Mode_AF_OD;oledGpioInit.GPIO_Pin   = GPIO_Pin_6 | GPIO_Pin_7;oledGpioInit.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&oledGpioInit);//I2c初始化I2C_DeInit(I2C1);				//将I2Cx外设寄存器重设为默认值oledI2cInit.I2C_Ack					= I2C_Ack_Enable;//从机的应答使能oledI2cInit.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//地址有效位oledI2cInit.I2C_ClockSpeed			= 400000;//400k 速度要低于400koledI2cInit.I2C_DutyCycle			= I2C_DutyCycle_2;//时钟占空比,low:high可以选2:0或者16:9oledI2cInit.I2C_Mode				= I2C_Mode_I2C;//模式oledI2cInit.I2C_OwnAddress1			= 0x30;//主机地址(随意)I2C_Init(I2C1, &oledI2cInit);I2C_Cmd(I2C1, ENABLE);				//使能I2c}
//i2C写一个字节
void I2cWriteBity(uint8_t addr,uint8_t data)
{while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));//检查I2c是否繁忙I2C_GenerateSTART(I2C1, ENABLE);//开始信号while(!I2C_CheckEvent(I2C1,  I2C_EVENT_MASTER_MODE_SELECT));//ev5(检查开始信号有没有发送成功),主模式I2C_Send7bitAddress(I2C1, OLED_ADDR, I2C_Direction_Transmitter);//发送器件地址寻找设备while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//器件等待正确的地址I2C_SendData(I2C1, addr);//器件向主机发送寄存器地址while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));I2C_SendData(I2C1, data);//器件向主机发送数据 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2c总线}//写命令
void WriteCmd(unsigned char I2cCmd)
{I2cWriteBity(0x00,I2cCmd);//0x00是oled用来写命令的}//写数据
void WriteData(unsigned char I2cData)
{I2cWriteBity(0x40,I2cData);//0x40是oled用来写数据的
}
//初始化oled,厂家提供的驱动 
void OLED_Init(void)
{ms_delay(100);WriteCmd(0xAE); //display offWriteCmd(0x20);	//Set Memory Addressing Mode	WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,InvalidWriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7WriteCmd(0xc8);	//Set COM Output Scan DirectionWriteCmd(0x00); //---set low column addressWriteCmd(0x10); //---set high column addressWriteCmd(0x40); //--set start line addressWriteCmd(0x81); //--set contrast control registerWriteCmd(0xff); //áá?èμ÷?ú 0x00~0xffWriteCmd(0xa1); //--set segment re-map 0 to 127WriteCmd(0xa6); //--set normal displayWriteCmd(0xa8); //--set multiplex ratio(1 to 64)WriteCmd(0x3F); //WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM contentWriteCmd(0xd3); //-set display offsetWriteCmd(0x00); //-not offsetWriteCmd(0xd5); //--set display clock divide ratio/oscillator frequencyWriteCmd(0xf0); //--set divide ratioWriteCmd(0xd9); //--set pre-charge periodWriteCmd(0x22); //WriteCmd(0xda); //--set com pins hardware configurationWriteCmd(0x12);WriteCmd(0xdb); //--set vcomhWriteCmd(0x20); //0x20,0.77xVccWriteCmd(0x8d); //--set DC-DC enableWriteCmd(0x14); //WriteCmd(0xaf); //--turn on oled panel
}
//设置oled起点坐标
void OLED_SetPos(unsigned char x,unsigned char y)
{WriteCmd(0xb0+y);//页地址WriteCmd((x&0xf0)>>4|0x10);//列高四位WriteCmd((x&0x0f)|0x01);//列低四位
}//全屏填充
void OLED_Fill(unsigned char FillData)
{unsigned char m,n;for(m=0;m<8;m++)//八页{WriteCmd(0xb0+m);//第几页0~7WriteCmd(0x00);//每一页第一列低四位的起始地址WriteCmd(0x10);//每一页第一列高四位的起始地址for(n = 0;n < 128;n++)//128列{WriteData(FillData);}}
}//清屏
void OLED_Clear(void)
{OLED_Fill(0x00);//00为全灭
}//OLED 打开
void OLED_OPen(void)
{WriteCmd(0x8d);//设置电荷泵WriteCmd(0x14);//开启电荷泵WriteCmd(0xaf);//OLED唤醒
}//OLED 关闭
void OLED_Close(void)
{WriteCmd(0x8d);//设置电荷泵WriteCmd(0x10);//关闭电荷泵WriteCmd(0xae);//OLED关闭
}//显示字符串,ascall码的格式
void OLED_DisplayStr(unsigned char x,unsigned char y,unsigned char ch[],unsigned char mode)
{unsigned char i = 0,c = 0,j = 0;switch(mode){case 1:{while(ch[j] != '\0'){c = ch[j] - 32;if(x>126)//判断一页是否写满{x = 0;y++;}OLED_SetPos(x,y);//一个字符用一页写8(行)*6(列)for(i = 0;i < 6 ;i++)WriteData(F6x8[c][i]);//为什么要写C,C代表的是数组里面的第几行x+=6;//写一个字符需要6列的像素j++;}}break;case 2:{while(ch[j] != '\0'){c = ch[j] - 32;if(x>120)//判断一页是否写满{x = 0;y++;}OLED_SetPos(x,y);//一个字符用两页写8(列)*16(行)for(i = 0;i < 8 ;i++)				//乘c是表示该字符在第几位WriteData(F8X16[c*16+i]);//16个十六进制数代表一个字符,前八位字符的上半部分OLED_SetPos(x,y+1);for(i = 0;i < 8 ;i++)WriteData(F8X16[c*16+i+8]);//后八位字符的上半部分x+=8;//列的像素点移动8列j++;//下一个字符}}break;}
}
//一个汉字用32个16进制数表示16*16,N表示的是第几个汉字
void displayCN(unsigned char x,unsigned char y,unsigned char N)
{unsigned char wm = 0;unsigned int addr = 32*N;if(x>125 )//判断一页是否写满{x = 0;y+=2;}OLED_SetPos(x,y);for(wm=0;wm<16;wm++)//字符的上半部分{WriteData(F16x16[addr+wm]);}OLED_SetPos(x,y+1);for(wm=0;wm<16;wm++)//写一个字符的下半部分{WriteData(F16x16[addr+wm+16]);}
}
//x0第几列,y0第几页,x1要填充的列数,y1要填充的页数,bmp填充的图片的16进制数组
void OLED_BMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{unsigned char x,y;unsigned int j = 0;//确定页数的大小
//	if(y1%8==0)
//		y = y1/8;
//	else 
//		y = y1/8+1;//每一页都进行画点for(y = y0;y<y1;y++)//每一页{OLED_SetPos(x0,y);for(x = x0;x<x1;x++)//每一列进行填充{WriteData(BMP[j++]);}}}

软件IIC:

#include "softoled.h"
#include "stm32f10x.h"
#include "SystemTick.h"
#include "softcodetab.h"
//进行gpio的初始化,pb0--SCL PB1--SDAstatic void SoftOLED_GpioInit(void)
{GPIO_InitTypeDef softOLED_Init;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);softOLED_Init.GPIO_Mode  = GPIO_Mode_Out_OD;softOLED_Init.GPIO_Pin	 = GPIO_Pin_0 | GPIO_Pin_1; softOLED_Init.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &softOLED_Init);OLED_SCL_SET();  	OLED_SDA_SET();}//软件iic的起始条件
static void softOLed_Start(void)
{ 	OLED_SDA_SET();OLED_SCL_SET(); us_delay(1);OLED_SDA_RESET();us_delay(1);	OLED_SCL_RESET();us_delay(1);
}//软件iic的停止条件
static void softOLed_Stop(void)
{	OLED_SDA_RESET();us_delay(1);OLED_SCL_SET();us_delay(1);OLED_SDA_SET();us_delay(1);
}
//软件iic的响应信号
static int softOLED_Ack(void)
{unsigned int ack;OLED_SCL_RESET();//主机时钟线拉低(时钟线拉低之后数据线才可以改变状态)us_delay(1);OLED_SDA_SET();//主机数据线拉高(数据线拉高之前准备好)us_delay(1);OLED_SCL_SET();//主机时钟线拉高us_delay(1);if(OLED_READ_SDA){ack = OLED_NO_ACK_SDA;}else {ack = OLED_ACK_SDA;}OLED_SCL_RESET();//把主机时钟线置回去(时钟线低电平的时候SDA才可以改变状态)us_delay(1);return ack;}//软件IIC写一个字节
static void softOLED_Write_Byte(unsigned char byte)
{unsigned char i;for(i = 0;i < 8;i++){OLED_SCL_RESET();//把主机时钟线拉低us_delay(1);if(byte & 0x80) //1000 0000读取最高位,高位先行OLED_SDA_SET();//最高位为1,所以将其设置成高电平elseOLED_SDA_RESET();//最高位为0,所以将其设置成低电平byte <<= 1;//数据左移1位,准备读取第7位,依次读下来us_delay(1);OLED_SCL_SET();//将时钟置高,产生上升沿,将得到1位数据发送出去us_delay(1);}OLED_SCL_RESET();//将时钟线拉低 给下一个字节发送进行做准备us_delay(1);while(softOLED_Ack());//等待从机响应
}//软件IIC写命令
static void softOLED_WriteCommand(unsigned char Command)
{softOLed_Start();//起始条件softOLED_Write_Byte(0x78);//写入oled的设备地址 去寻找对应的oled设备softOLED_Write_Byte(0x00);//0x00是oled的写命令的地址0x40是写数据的地址softOLED_Write_Byte(Command);//向0x00这个可以写命令的地址写入对应的指令softOLed_Stop();//发送停止信号//GPIO_SetBits(GPIOA,  GPIO_Pin_1);
}//软件IIC写数据
static void softOLED_WriteData(unsigned char Data)
{softOLed_Start();				//起始条件softOLED_Write_Byte(0x78);		//写入oled的设备地址 去寻找对应的oled设备softOLED_Write_Byte(0x40);		//0x00是oled的写命令的地址0x40是写数据的地址softOLED_Write_Byte(Data);	    //向0x40这个可以写数据的地址写入对应的数据softOLed_Stop();				//发送停止信号
}//对OLED写入一个字节
void SoftOLED_Write_Byte(unsigned char dat,unsigned char cmd)
{if(cmd){softOLED_WriteData(dat);//写数据}else {softOLED_WriteCommand(dat);//写命令}
}
//设置坐标x是列,y是页
void SoftOLED_SetPos(unsigned char x,unsigned char y)
{SoftOLED_Write_Byte(0xb0+y,OLED_CMD);//写入页地址SoftOLED_Write_Byte(x&0x0f,OLED_CMD);SoftOLED_Write_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
}
//打开OLED
void SoftOLED_Open(void)
{SoftOLED_Write_Byte(0x8D,OLED_CMD);//设置电荷泵SoftOLED_Write_Byte(0x14,OLED_CMD);//开启电荷泵SoftOLED_Write_Byte(0xAF,OLED_CMD);//设置显示开
}
//关闭OLED
void SoftOLED_Close(void)
{SoftOLED_Write_Byte(0x8D,OLED_CMD);//设置电荷泵SoftOLED_Write_Byte(0x10,OLED_CMD);//关闭电荷泵SoftOLED_Write_Byte(0xAE,OLED_CMD);//设置显示关
}//填充
void SoftOLED_Fill(unsigned char dat)
{unsigned char i,j;for(i = 0;i < 8;i++){SoftOLED_Write_Byte(0xb0+i,OLED_CMD);//每一页SoftOLED_Write_Byte(0x00,OLED_CMD);//每一页第一列低四位的起始地址SoftOLED_Write_Byte(0x10,OLED_CMD);//每一页第一列高四位的起始地址for(j = 0;j < 128;j++){SoftOLED_Write_Byte(dat,OLED_DATA);}}
}
//清屏
void SoftOLED_Clear(void)
{SoftOLED_Fill(0x00);
}//写一个字符
void SoftOLED_DisplayChr(unsigned char x,unsigned char y,unsigned char ch,unsigned char size)
{unsigned char c = 0,i = 0;c = ch - 32;//获取字符的偏移量
//	if(x > 120)//超过最大的列数
//	{
//		x = 0;			//回到第一列
//		y += 2;			//换两页进行写字符
//	}if(size == 16){if(x > 128)//超过最大的列数{x = 0;			//回到第一列y += 2;			//换两页进行写字符}SoftOLED_SetPos(x,y);//设置是哪一页哪一列for(i = 0;i < 8;i++){SoftOLED_Write_Byte(softF8X16[c*16+i],OLED_DATA);//字符的上半部分}SoftOLED_SetPos(x,y+1);for(i = 0;i < 8;i++)SoftOLED_Write_Byte(softF8X16[c*16+i+8],OLED_DATA);//字符的下半部分}else if(size == 8){if(x > 122)//超过最大的列数{x = 0;			//回到第一列y += 1;			//换两页进行写字符}SoftOLED_SetPos(x,y);//设置是哪一页哪一列for(i = 0;i < 6;i++)SoftOLED_Write_Byte(softF6x8[c][i],OLED_DATA);}
}
//写一个字符串
void softOLED_DisplayStr(unsigned char x,unsigned char y,unsigned char ch[],unsigned char mode)
{unsigned char j = 0;while(ch[j] != '\0'){if(mode == 1){SoftOLED_DisplayChr(x,y,ch[j],8);x += 6;j++;}else if(mode == 2){SoftOLED_DisplayChr(x,y,ch[j],16);x += 8;j++;	}}}//写一个汉字
void softOLED_DisplayCN(unsigned char x,unsigned char y,unsigned char N)
{unsigned char j = 0;unsigned int addr = N*32;if(x>110){x = 0;y += 2;}SoftOLED_SetPos(x,y);//设置是哪一页哪一列for(j = 0;j<16;j++)SoftOLED_Write_Byte(softF16x16[addr+j],OLED_DATA);SoftOLED_SetPos(x,y+1);for(j = 0;j<16;j++)SoftOLED_Write_Byte(softF16x16[addr+j+16],OLED_DATA);
}x0第几列,y0第几页,x1要填充的列数,y1要填充的页数,bmp填充的图片的16进制数组
void softOLED_DisplayPicture(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{unsigned char x,y;unsigned int j = 0;//确定页数的大小
//	if(y1%8==0)
//		y = y1/8;
//	else 
//		y = y1/8+1;//每一页都进行画点for(y = y0;y<y1;y++)//每一页{SoftOLED_SetPos(x0,y);for(x = x0;x<x1;x++)//每一列进行填充{SoftOLED_Write_Byte(BMP[j++],OLED_DATA);}}
}
//软件OLED初始化
void SoftOLED_Init(void)
{
//	ms_delay(100);
//	SoftOLED_Write_Byte(0xAE,OLED_CMD); //display off
//	SoftOLED_Write_Byte(0x20,OLED_CMD);	//Set Memory Addressing Mode	
//	SoftOLED_Write_Byte(0x10,OLED_CMD);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
//	SoftOLED_Write_Byte(0xb0,OLED_CMD);	//Set Page Start Address for Page Addressing Mode,0-7
//	SoftOLED_Write_Byte(0xc8,OLED_CMD);	//Set COM Output Scan Direction
//	SoftOLED_Write_Byte(0x00,OLED_CMD); //---set low column address
//	SoftOLED_Write_Byte(0x10,OLED_CMD); //---set high column address
//	SoftOLED_Write_Byte(0x40,OLED_CMD); //--set start line address
//	SoftOLED_Write_Byte(0x81,OLED_CMD); //--set contrast control register
//	SoftOLED_Write_Byte(0xff,OLED_CMD); //áá?èμ÷?ú 0x00~0xff
//	SoftOLED_Write_Byte(0xa1,OLED_CMD); //--set segment re-map 0 to 127
//	SoftOLED_Write_Byte(0xa6,OLED_CMD); //--set normal display
//	SoftOLED_Write_Byte(0xa8,OLED_CMD); //--set multiplex ratio(1 to 64)
//	SoftOLED_Write_Byte(0x3F,OLED_CMD); //
//	SoftOLED_Write_Byte(0xa4,OLED_CMD); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
//	SoftOLED_Write_Byte(0xd3,OLED_CMD); //-set display offset
//	SoftOLED_Write_Byte(0x00,OLED_CMD); //-not offset
//	SoftOLED_Write_Byte(0xd5,OLED_CMD); //--set display clock divide ratio/oscillator frequency
//	SoftOLED_Write_Byte(0xf0,OLED_CMD); //--set divide ratio
//	SoftOLED_Write_Byte(0xd9,OLED_CMD); //--set pre-charge period
//	SoftOLED_Write_Byte(0x22,OLED_CMD); //
//	SoftOLED_Write_Byte(0xda,OLED_CMD); //--set com pins hardware configuration
//	SoftOLED_Write_Byte(0x12,OLED_CMD);
//	SoftOLED_Write_Byte(0xdb,OLED_CMD); //--set vcomh
//	SoftOLED_Write_Byte(0x20,OLED_CMD); //0x20,0.77xVcc
//	SoftOLED_Write_Byte(0x8d,OLED_CMD); //--set DC-DC enable
//	SoftOLED_Write_Byte(0x14,OLED_CMD); //
//	SoftOLED_Write_Byte(0xaf,OLED_CMD); //--turn on oled panelSoftOLED_GpioInit();	//GPIO?ú3?ê??ˉms_delay(200);	//?ó3ù£?óéóúμ¥???úé?μ?3?ê??ˉ±èOLED?ì£??ùò?±?D??óé??ó3ù£?μè′yOLEDé??′??íê3éSoftOLED_Write_Byte(0xAE,OLED_CMD);	//1?±???ê?SoftOLED_Write_Byte(0x00,OLED_CMD);	//éè??μíáDμ??·SoftOLED_Write_Byte(0x10,OLED_CMD);	//éè????áDμ??·SoftOLED_Write_Byte(0x40,OLED_CMD);	//éè???eê?DDμ??·SoftOLED_Write_Byte(0xB0,OLED_CMD);	//éè??ò3μ??·SoftOLED_Write_Byte(0x81,OLED_CMD); 	// ??±è?èéè??£??ééè??áá?èSoftOLED_Write_Byte(0xFF,OLED_CMD);	//  265  SoftOLED_Write_Byte(0xA1,OLED_CMD);	//éè????£¨SEG£?μ??eê?ó3é?μ??·£?columnμ?127μ??·ê?SEG0μ?μ??·SoftOLED_Write_Byte(0xA6,OLED_CMD);	//?y3£??ê?£?0xa7????ê?SoftOLED_Write_Byte(0xA8,OLED_CMD);	//éè???y?ˉ?·êy£¨16~64£?SoftOLED_Write_Byte(0x3F,OLED_CMD);	//64dutySoftOLED_Write_Byte(0xC8,OLED_CMD);	//??ó3é??£ê?£?COM[N-1]~COM0é¨?èSoftOLED_Write_Byte(0xD3,OLED_CMD);	//éè????ê???ò?SoftOLED_Write_Byte(0x00,OLED_CMD);	//?T??ò?SoftOLED_Write_Byte(0xD5,OLED_CMD);	//éè???eμ′?÷·??μSoftOLED_Write_Byte(0x80,OLED_CMD);	//ê1ó???è??μSoftOLED_Write_Byte(0xD9,OLED_CMD);	//éè?? Pre-Charge PeriodSoftOLED_Write_Byte(0xF1,OLED_CMD);	//ê1ó?1ù·?í????μSoftOLED_Write_Byte(0xDA,OLED_CMD);	//éè?? com pin configuartionSoftOLED_Write_Byte(0x12,OLED_CMD);	//ê1ó???è??μSoftOLED_Write_Byte(0xDB,OLED_CMD);	//éè?? Vcomh£??éμ÷?úáá?裨??è?£?SoftOLED_Write_Byte(0x40,OLED_CMD);	ê1ó?1ù·?í????μSoftOLED_Write_Byte(0x8D,OLED_CMD);	//éè??OLEDμ?oé±?SoftOLED_Write_Byte(0x14,OLED_CMD);	//?a??ê?SoftOLED_Write_Byte(0xAF,OLED_CMD);	//?a??OLED??°???ê?SoftOLED_GpioInit();SoftOLED_Clear();        //???áSoftOLED_SetPos(0,0); 	 //éè??êy?YD′è?μ??eê?DD?¢áD
}

八、引脚图

所有I2C设备的SCL连在一起,SDA连在一起

设备的SCL和SDA均要配置成开漏输出模式

SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/16151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习】paddlets,时序数据预测

文章目录 一、环境二、题目1三、题目2四、题目3五、函数参数 资料&#xff1a; https://paddlets.readthedocs.io/zh-cn/latest/source/api/paddlets.models.base.html#paddlets.models.base.BaseModel.recursive_predict https://aistudio.baidu.com/projectdetail/5866171?…

陪跑真正值钱的不是教程,是你遇到那个挡住你的问题时,身边有个靠谱的人

今天分享两个概念&#xff0c;一个是意识决定一切&#xff0c;一个是大道至简&#xff0c;做项目就是按部就班的遵循事情发展规律去做。 先说第一个概念&#xff0c;意识决定一切。我们说的凡事预则立不预则废&#xff0c;就是计划了去做就会有结果。 给你们一个表&#xff0c;…

MySQL——索引与事务

目录 前言 一、索引 1.索引概述 &#xff08;1&#xff09;基本概念 &#xff08;2&#xff09;索引作用 &#xff08;3&#xff09;索引特点 &#xff08;4&#xff09;适用场景 2.索引的操作 &#xff08;1&#xff09;查看索引 &#xff08;2&#xff09;创建索引…

文科论文,使用AI写作时能够提供实证数据吗?

人工智能时代&#xff0c;为了撰写论文提供思路及高效&#xff0c;利用AI撰写论文已是常态&#xff0c;可撰写文科论文通常研究中都需要实证数据&#xff0c;而AI撰写论文时能够提供这样的数据吗&#xff1f; 一、什么是实证数据 实证数据是指从研究报告、财务报表、新闻报道…

计算机网络——TCP 协议的三次握手 / 四次挥手

简述 TCP / UDP 协议都是传输层的协议。 UDP 是面向无连接的协议&#xff0c;就是说发送端不在乎消息数据是否传输到接收端了&#xff0c;所以会出现数据丢失的情况&#xff0c;所以可靠性也不高。 TCP 是面向连接的、可靠的、基于字节流的传输层协议。所谓面向连接的&#…

Flink-cdc更好的流式数据集成工具

What’s Flink-cdc? Flink CDC 是基于Apache Flink的一种数据变更捕获技术&#xff0c;用于从数据源&#xff08;如数据库&#xff09;中捕获和处理数据的变更事件。CDC技术允许实时地捕获数据库中的增、删、改操作&#xff0c;将这些变更事件转化为流式数据&#xff0c;并能够…

Windows平台C#版RTSP转RTMP直播推送定制版

技术背景 前几年我们发布了C版的多路RTMP/RTSP转RTMP转发官方定制版。在秉承低延迟、灵活稳定、低资源占用的前提下&#xff0c;客户无需关注开发细节&#xff0c;只需图形化配置转发等各类参数&#xff0c;实现产品快速上线目的。 如监控类摄像机、NVR等&#xff0c;通过厂商…

【启程Golang之旅】深入解析函数的奥秘与技巧

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

【全开源】海报在线制作系统源码(ThinkPHP+FastAdmin+UniApp)

打造个性化创意海报的利器 引言 在数字化时代&#xff0c;海报作为一种重要的宣传媒介&#xff0c;其设计质量和效率直接影响着宣传效果。为了满足广大用户对于个性化、高效制作海报的需求&#xff0c;海报在线制作系统源码应运而生。本文将详细介绍海报在线制作系统源码的特…

AbMole - 肿瘤发展与免疫器官的“舞蹈”:一场细胞层面的时间赛跑

在生物医学领域&#xff0c;肿瘤与免疫系统之间的相互作用一直是研究的热点话题。肿瘤细胞不是孤立存在的&#xff0c;它们与宿主的免疫系统进行着一场复杂的“舞蹈”。 最近&#xff0c;一项发表在《Molecular & Cellular Proteomics》杂志上的研究&#xff0c;为我们揭开…

【C++】二分查找算法

1.题目 2.算法思路 暴力解法&#xff1a;可以将数组遍历一遍&#xff0c;就可以找到。时间复杂度为O(n)。不算太差&#xff0c;可以接受。 但是有更优秀的解法&#xff1a; 就是二分查找算法。 算法的特点&#xff1a;我们所查找的“数组”具有二段性。这里的二段性不一定有…

Oracle 并行和 session 数量的

这也就是为什么我们指定parallel为4&#xff0c;而实际并行度为8的原因。 insert create index&#xff0c;发现并行数都是加倍的 Indexes seem always created with parallel degree 1 during import as seen from a sqlfile. The sql file shows content like: CREATE INDE…

滑不动窗口的秘密—— “滑动窗口“算法 (Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…

《python编程从入门到实践》day39

# 昨日知识点回顾 创建主页、继承模版、显示特定主题页面 # view.py from django.shortcuts import render# 导入所需数据相关联的模型 from .models import Topic# Create your views here. def index(request):"""学习笔记的主页"""#…

Java进阶学习笔记13——抽象类

认识抽象类&#xff1a; 当我们在做子类共性功能抽取的时候&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了。在Java中&#xff0c;一个没有方法体的方法应该定义为抽象方法&#xff0c;而类中如果有抽象方法&#xff0c;该类就定义为抽象类…

ISCC2024个人挑战赛WP-迷失之门

&#xff08;非官方解&#xff0c;以下内容均互联网收集的信息和个人思路&#xff0c;仅供学习参考&#xff09; 迷失之门 方法一&#xff1a; IDA看一下 check函数逻辑 进入到check2函数 R键将ascii码转字符&#xff0c;写出逆向脚本 #include <stdio.h> #include &l…

Linux C++ Socket 套接字、select、poll、epoll 实例

文章目录 1. 概述2. TCP 网络编程实例2.1 服务器端2.2 客户端2.3 运行截图 3. I/O 模型3.1 阻塞式I/O模型3.2 非阻塞I/O模型3.3 I/O 复用模型3.4 信号驱动式I/O3.5 异步I/O模型 4. I/O复用之 select4.1 select 函数描述4.2 服务端代码4.3 客户端代码4.4 运行截图 5. I/O复用之 …

RocketMq局部顺序消息

package com.ldj.rocketmq.producer;import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message;import java.nio.charset.StandardCharsets;/*** User: ldj* Date: 2024/5/26* Time: 15:09* Description: 局部顺序消…

css卡片翻转 父元素翻转子元素不翻转效果

css卡片翻转 父元素翻转子元素不翻转效果 vue <div class"moduleBox"><div class"headTitle"><span class"headName">大额案例</span></div><div class"moduleItem"><span class"module…

three.js判断物体在人的前面,还是后面

three.js判断物体在人的前面&#xff0c;还是后面 const player new THREE.Vectors(10, 0, 5); const mesh new THREE.Vectors(15, 0, 6);上面&#xff0c;两个变量分别表示&#xff0c;玩家的位置&#xff0c;物体的位置。 从这发现&#xff0c;当玩家和物体的角度关系 小…