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.pswp.cn/bicheng/16151.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/16151.shtml
英文地址,請注明出處:http://en.pswp.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;…

Linux簡單通過Minicom命令操作串口設備(linux串口操作命令)

Minicom是一個在Linux系統中廣泛使用的串行通信程序。它類似于Windows下的超級終端,允許用戶通過串口與外部硬件設備進行通信。Minicom不僅功能強大,而且完全免費,帶有源代碼,可以在大多數Unix系統下運行。 安裝Minicom 在大多數Linux發行版中,Minicom可能沒有預裝。可以…

【前端】面試八股文——BFC

面試八股文——BFC 在前端開發的面試中&#xff0c;BFC&#xff08;Block Formatting Context&#xff0c;塊級格式化上下文&#xff09;常常是一個高頻出現的考點。它不僅考察應聘者對CSS布局的理解深度&#xff0c;也是面試官判斷候選人解決實際問題能力的重要依據之一。因此…

python接口自動化測試中為什么用yaml文件進行用例管理而不是json文件

在Python接口自動化測試中&#xff0c;使用YAML文件進行用例管理而不是JSON文件&#xff0c;主要基于以下幾個原因&#xff1a; 可讀性&#xff1a;YAML文件使用縮進和冒號來表示層級結構&#xff0c;使得文件內容更加清晰易讀。相比之下&#xff0c;JSON文件則使用大括號和中…

MySQL——索引與事務

目錄 前言 一、索引 1.索引概述 &#xff08;1&#xff09;基本概念 &#xff08;2&#xff09;索引作用 &#xff08;3&#xff09;索引特點 &#xff08;4&#xff09;適用場景 2.索引的操作 &#xff08;1&#xff09;查看索引 &#xff08;2&#xff09;創建索引…

LeetCode399觸發求值

題目描述 給你一個變量對數組 equations 和一個實數值數組 values 作為已知條件&#xff0c;其中 equations[i] [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi values[i] 。每個 Ai 或 Bi 是一個表示單個變量的字符串。另有一些以數組 queries 表示的問題&#xff0c;其中 que…

文科論文,使用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;我們所查找的“數組”具有二段性。這里的二段性不一定有…

頭歌OpenGauss數據庫-L.應用開發(Python)-選做

第1關:簡單查詢 編程要求 正確使用 psycopg2 ,查詢金融應用場景數據庫 finance 的 client 表(客戶表)中郵箱不為空的客戶信息,列出客戶姓名,郵箱和電話.一個展示結果的示例如下(字體顏色不是編程要求): 注意:你要連接到finance數據庫上(后面第2-6關也是連接這個數據庫)…

【C/C++】詳解關聯容器map的使用

&#x1f517; 運行環境&#xff1a;Matlab &#x1f6a9; 撰寫作者&#xff1a;左手の明天 &#x1f947; 精選專欄&#xff1a;《python》 &#x1f525; 推薦專欄&#xff1a;《算法研究》 &#x1f510;#### 防偽水印——左手の明天 ####&#x1f510; &#x1f497; 大家…

mpv常用快捷鍵

1 mpv mpv是Linux下的一個開源視頻播放器&#xff0c;使用Manjaro的話安裝方式如下&#xff1a; paru -S mpv2 常用快捷鍵 q&#xff1a;推出w/e&#xff1a;視頻縮放r/t&#xff1a;調整字幕位置u&#xff1a;開啟/關閉ass/ssa字幕覆蓋i&#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…

求平方數 1 到 N 之間所有正整數的平方數

概念&#xff1a; 平方數的概念&#xff1a; 平方數是指一個數的平方等于另一個數的數&#xff0c;具有正平方數和負平方數&#xff0c;其性質和運用在多領域中具有重要意義&#xff0c;如幾何、自然科學、計算機科學和物理學。平方數的計算和運用在多領域中常見&#xff0c;例…