STM32模擬4線 SPI驅動OLED(SSD1306)

1.定義引腳對應的IO

  OLED    --    STM32    --    SPI   

    D0    --    PA5    --    SPI_SCK

    D1    --    PA7    --    SPI_MOSI

    RES    --    PB0    --    RES#(OLED的RESET腳,低電平有效)

    DC    --    PB1    --    DC#(OLED的DC腳,數據和命令控制管腳)

    CS    --    PA4    --    SPI_CS

2.SSD1306使用4Wire SPI寫操作的時序圖 

CS#--片選--拉低CS#選中SSD1306

D/C#-- 高電平爲寫數據,低電平爲寫命令

在SCLK(D0)爲低電平時,SSD1306把SDIN(D0)作爲信號線,在SCLK(D0)的每個上升沿,SDIN(D1)將發出1bit數據,在每一個DBn將會發送8bit共1byte(字節)的數據,SDIN(D1)從高位到低位發送(D7-D0),連續發送8次後拉高CS#,結束本次通訊。

SDIN is shifted into an 8-bit shift register on every rising edge of SCLK in the order of D7, D6, ... D0. D/C#
is sampled on every eighth clock and the data byte in the shift register is written to the Graphic Display Data
RAM (GDDRAM) or command register in the same clock.
Under serial mode, only write operations are allowed.

 

3.

3.1  oled.h  OLED相關的引腳定義

/*--------------------引腳定義--------------------------*/
#define SPI_SCK_HIGH		GPIO_SetBits(GPIOA,GPIO_Pin_5)	        //PA5(DO)輸出高
#define	SPI_SCK_LOW		GPIO_ResetBits(GPIOA,GPIO_Pin_5)	//PA5(DO)輸出低

#define SPI_MOSI_HIGH		GPIO_SetBits(GPIOA,GPIO_Pin_7)	        //PA7(D1)輸出高
#define	SPI_MOSI_LOW		GPIO_ResetBits(GPIOA,GPIO_Pin_7)	//PA7(D1)輸出低

#define OLED_RES_HIGH		GPIO_SetBits(GPIOB,GPIO_Pin_0)	        //PB0(RES)輸出高
#define OLED_RES_LOW		GPIO_ResetBits(GPIOB,GPIO_Pin_0)	//PB0(RES)輸出低

#define OLED_DC_HIGH		GPIO_SetBits(GPIOB,GPIO_Pin_1)	        //PB1(DC)輸出高
#define OLED_DC_LOW		GPIO_ResetBits(GPIOB,GPIO_Pin_1)	//PB1(DC)輸出低

#define SPI_CS_HIGH		GPIO_SetBits(GPIOA,GPIO_Pin_4)	        //PA4(CS)輸出高
#define SPI_CS_LOW		GPIO_ResetBits(GPIOA,GPIO_Pin_4)	//PA4(CS)輸出低

/*definition--------------------------------------------*/
#define OLED_CMD  0	//寫命令爲低
#define OLED_DATA 1	//寫數據爲高

#define SIZE 		16		//定義顯示字符的大小
#define Max_Column	128		//定義最大列數
#define Max_Row		64		//定義最大行數
#define X_WIDTH 	128		//定義X軸的寬度
#define Y_WIDTH 	64	        //定義Y軸的寬度		

3.2 初始化OLED對應的IO

static void OLED_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;	                                        //定義一個GPIO_InitTypeDef類型的結構體
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);	 //使能A端口時鐘
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7;	                 //初始化相關的引腳
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		                 //配置爲推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                //速度50MHz
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	                                          //初始化GPIOA
 	GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7|GPIO_Pin_4);	
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_8;	                 //初始化相關引腳
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		                 //配置爲推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                                //速度50MHz
 	GPIO_Init(GPIOB, &GPIO_InitStructure);	                                         //初始化GPIOB
 	GPIO_SetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1);	
}

3.3 模擬SPI發送字節 

    data爲將要發送的的數據

static void SPI_Write_Byte(unsigned char data)
{
	unsigned char i;	        //定義變量
	for(i = 0; i < 8; i++)	        //循環8次
	{
		SPI_SCK_LOW;	        //將時鐘線拉低
		delay(1);	        //延遲1us
		
		if(data & 0x80)	            //數據從高位-->低位依次發送
			SPI_MOSI_HIGH;	//數據爲爲1
		else
			SPI_MOSI_LOW;	//數據位爲0
			
		data <<= 1;	        //數據左移1位
		
		delay(1);	        //延遲1us
		SPI_SCK_HIGH;	        //時鐘線拉高,把數據發送出去
		delay(1);	        //延遲1us
	}
	
}

3.4 對OLED寫數據/命令 

    dat:爲所需寫入的數據

void OLED_WR_Byte(unsigned char dat,unsigned char cmd)
{
	if(cmd)                 //如果cmd爲高,則發送的是數據
	  	OLED_DC_HIGH;	//將DC拉高
	else                    //如果cmd爲低,則發送的是命令
		OLED_DC_LOW;	//將DC拉低
		
	SPI_CS_LOW;             //片選拉低,選通器件
		
	SPI_Write_Byte(dat);    //發送數據
		
	SPI_CS_HIGH;            //片選拉高,關閉器件
	OLED_DC_HIGH;           //DC拉高,空閒時爲高電平
}

3.5    顯示字符

void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';                             //獲取字符的偏移量	
		if(x>Max_Column-1){x=0;y=y+2;}         //如果列數超出了範圍,就從下2頁的第0列開始

		if(SIZE ==16)             //字符大小如果爲 16 = 8*16
			{
				OLED_Set_Pos(x,y);	                     //從x y 開始畫點
				for(i=0;i<8;i++)                             //循環8次 佔8列
				OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);       //找出字符 c 的數組位置,先在第一頁把列畫完
				OLED_Set_Pos(x,y+1);                         //頁數加1
				for(i=0;i<8;i++)                             //循環8次
				OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);     //把第二頁的列數畫完
			}
		else 	                //字符大小爲 6 = 6*8
			{	
				OLED_Set_Pos(x,y+1);                         //一頁就可以畫完
				for(i=0;i<6;i++)                             //循環6次 ,佔6列
				OLED_WR_Byte(F6x8[c][i],OLED_DATA);          //把字符畫完
			}
}

3.6顯示數字

void OLED_ShowNum(unsigned char x,unsigned char y,unsigned int num,unsigned char len,unsigned char size)
{         	
	unsigned char t,temp;                  //定義變量
	unsigned char enshow=0;		       //定義變量

	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;                  //取出輸入數的每個位,由高到低
		if(enshow==0&&t<(len-1))                             //enshow:是否爲第一個數;t<(len-1):判斷是否爲最後一個數
		{
			if(temp==0)                                  //如果該數爲0 
			{
				OLED_ShowChar(x+(size/2)*t,y,' ');   //顯示 0 ;x+(size2/2)*t根據字體大小偏移的列數(8)
				continue;                            //跳過剩下語句,繼續重複循環(避免重複顯示)
			}else enshow=1; 
		}
	 	OLED_ShowChar(x+(size/2)*t,y,temp+'0');               //顯示一個位;x+(size2/2)*t根據字體大小偏移的列數(8)
	}
} 

3.7 顯示字符串

void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *chr)
{
	unsigned char j=0;             //定義變量

	while (chr[j]!='\0')             //如果不是最後一個字符
	{		
		OLED_ShowChar(x,y,chr[j]);    //顯示字符
		x+=8;                         //列數加8 ,一個字符的列數佔8
		if(x>=128){x=0;y+=2;}         //如果x大於等於128,切換頁,從該頁的第一列顯示
		j++;                          //下一個字符
	}
}

3.8顯示中文

需要進行取模

void OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char no)
{      			    
	unsigned char t,adder=0;     //定義變量

	OLED_Set_Pos(x,y);	    //從 x y 開始畫點,先畫第一頁
    for(t=0;t<16;t++)               //循環16次,畫第一頁的16列
		{
			OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);    //畫no在數組位置的第一頁16列的點
			adder+=1;                                //數組地址加1
     	}	
		OLED_Set_Pos(x,y+1);                             //畫第二頁
    for(t=0;t<16;t++)                                            //循環16次,畫第二頁的16列
		{	
			OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);  //畫no在數組位置的第二頁16列的點
			adder+=1;                                //數組地址加1
        }					
}

3.9 顯示圖片

需要進行取模

void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ 	
 	unsigned int j=0;         //定義變量
 	unsigned char x,y;        //定義變量
  
 	if(y1%8==0) y=y1/8;       //判斷終止頁是否爲8的整數倍
 	 else y=y1/8+1;

		for(y=y0;y<y1;y++)         //從起始頁開始,畫到終止頁
		{
			OLED_Set_Pos(x0,y);     //在頁的起始列開始畫
   			for(x=x0;x<x1;x++)      //畫x1 - x0 列
	    		{
	    			OLED_WR_Byte(BMP[j++],OLED_DATA);	//畫圖片的點    	
	    		}
		}
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章