STM32模拟4 Wire 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);	//画图片的点    	
	    		}
		}
} 

猜你喜欢

转载自blog.csdn.net/u013002186/article/details/81051547