OLED显示实验源程序

http://www.openedv.com/forum.php?mod=viewthread&tid=11969&highlight=OLED%CF%D4%CA%BE%CA%B5%D1%E9

软件设计我们依旧在之前的工程上面增加,首先在HARDWARE文件夹下新建一个OLED的文件夹。然后打开USER文件夹下的工程,新建一个oled.c的文件和oled.h的头文件,保存在OLED文件夹下,并将OLED文件夹加入头文件包含路径。

oled.c的代码,由于比较长,这里我们就不贴出来了,仅介绍几个比较重要的函数。首先是OLED_Init函数,该函数的结构比较简单,开始是对IO口的初始化,这里我们用了宏定义OLED_MODE来决定要设置的IO口,其他就是一些初始化序列了,我们按照厂家提供的资料来做就可以。最后要说明一点的是,因为OLED是无背光的,在初始化之后,我们把显存都清空了,所以我们在屏幕上是看不到任何内容的,跟没通电一个样,不要以为这就是初始化失败,要写入数据模块才会显示的。OLED_Init函数代码如下:

//初始化SSD1306

void OLED_Init(void)

{

   RCC->APB2ENR|=1<<4;    //使能PORTC时钟

   RCC->APB2ENR|=1<<5;    //使能PORTD时钟

   RCC->APB2ENR|=1<<8;    //使能PORTG时钟

  GPIOD->CRL&=0XF0FF0FFF;//PD3,6推挽输出

  GPIOD->CRL|=0X03003000;      

  GPIOD->ODR|=1<<3;

  GPIOD->ODR|=1<<6;      

#if OLED_MODE==1

   GPIOC->CRL=0X33333333; //PC0~7 OUT

   GPIOC->ODR|=0X00FF;            

  GPIOG->CRH&=0X000FFFFF;//PG13,14,15 OUT

  GPIOG->CRH|=0X33300000;     

   GPIOG->ODR|=7<<13;

#else

   GPIOC->CRL&=0XFFFFFF00; //PC0,1 OUT

   GPIOC->CRL|=0X00000033;          

  GPIOC->ODR|=3<<0;

  GPIOG->CRH&=0X0FFFFFFF;//RST   

  GPIOG->CRH|=0X30000000;     

   GPIOG->ODR|=1<<15;

#endif

   OLED_RST=0;                                 //复位

   delay_ms(100);

   OLED_RST=1;                                   

   OLED_WR_Byte(0xAE,OLED_CMD);//关闭显示

   OLED_WR_Byte(0xD5,OLED_CMD);//设置时钟分频因子,震荡频率

   OLED_WR_Byte(80,OLED_CMD);  //[3:0],分频因子;[7:4],震荡频率

   OLED_WR_Byte(0xA8,OLED_CMD);//设置驱动路数

   OLED_WR_Byte(0X3F,OLED_CMD);//默认0X3F(1/64)

   OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移

   OLED_WR_Byte(0X00,OLED_CMD);//默认为0

   OLED_WR_Byte(0x40,OLED_CMD);//设置显示开始行 [5:0],行数.                              

   OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵设置

   OLED_WR_Byte(0x14,OLED_CMD);//bit2,开启/关闭

   OLED_WR_Byte(0x20,OLED_CMD);//设置内存地址模式

   OLED_WR_Byte(0x02,OLED_CMD);

//[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;

   OLED_WR_Byte(0xA1,OLED_CMD);//段重定义设置,bit0:0,0->0;1,0->127;

   OLED_WR_Byte(0xC0,OLED_CMD);

//设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数

   OLED_WR_Byte(0xDA,OLED_CMD);//设置COM硬件引脚配置

   OLED_WR_Byte(0x12,OLED_CMD);//[5:4]配置            

   OLED_WR_Byte(0x81,OLED_CMD);//对比度设置

   OLED_WR_Byte(0xEF,OLED_CMD);//1~255;默认0X7F (亮度设置,越大越亮)

   OLED_WR_Byte(0xD9,OLED_CMD);//设置预充电周期

   OLED_WR_Byte(0xf1,OLED_CMD);//[3:0],PHASE 1;[7:4],PHASE 2;

   OLED_WR_Byte(0xDB,OLED_CMD);//设置VCOMH 电压倍率

   OLED_WR_Byte(0x30,OLED_CMD);//[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;

   OLED_WR_Byte(0xA4,OLED_CMD);//全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)

   OLED_WR_Byte(0xA6,OLED_CMD);//设置显示方式;bit0:1,反相显示;0,正常显示       

   OLED_WR_Byte(0xAF,OLED_CMD);//开启显示    

   OLED_Clear();

}

接着,要介绍的是OLED_Refresh_Gram函数。我们在STM32内部定义了一个块GRAM:u8 OLED_GRAM[128][8];此部分GRAM对应OLED模块上的GRAM。在操作的时候,我们只要修改STM32内部的GRAM就可以了,然后通过OLED_Refresh_Gram函数把GRAM一次刷新到OLED 的GRAM上。该函数代码如下:

//更新显存到LCD

void OLED_Refresh_Gram(void)

{

   u8 i,n;               

   for(i=0;i<8;i++) 

   { 

          OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)

          OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址

          OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址  

          for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n],OLED_DATA);

   }  

}

OLED_Refresh_Gram函数先设置页地址,然后写入列地址(也就是纵坐标),然后从0开始写入128个字节,写满该页,最后循环把8页的内容都写入,就实现了整个从STM32显存到OLED显存的拷贝。

OLED_Refresh_Gram函数还用到了一个外部函数,也就是我们接着要介绍的函数:OLED_WR_Byte,该函数直接和硬件相关,函数代码如下:

#if OLED_MODE==1

//向SSD1306写入一个字节。

//dat:要写入的数据/命令

//cmd:数据/命令标志 0,表示命令;1,表示数据;

void OLED_WR_Byte(u8 dat,u8 cmd)

{

   DATAOUT(dat);        

  OLED_RS=cmd;

   OLED_CS=0;   

   OLED_WR=0;      

   OLED_WR=1;

   OLED_CS=1;  

   OLED_RS=1;

}

#else

//向SSD1306写入一个字节。

//dat:要写入的数据/命令

//cmd:数据/命令标志 0,表示命令;1,表示数据;

void OLED_WR_Byte(u8 dat,u8 cmd)

{

   u8 i;                

   OLED_RS=cmd; //写命令

   OLED_CS=0;         

   for(i=0;i<8;i++)

   {                    

          OLED_SCLK=0;

          if(dat&0x80)OLED_SDIN=1;

          else OLED_SDIN=0;

          OLED_SCLK=1;

          dat<<=1;  

   }                         

   OLED_CS=1;         

   OLED_RS=1;        

}

#endif

这里有2个一样的函数,通过宏定义OLED_MODE来决定使用哪一个。如果OLED_MODE=1,就定义为并口模式,选择第一个函数,而如果为0,则为4线串口模式,选择第二个函数。这两个函数输入参数均为2个:dat和cmd,dat为要写入的数据,cmd则表明该数据是命令还是数据。这两个函数的时序操作就是根据上面我们对8080接口以及4线SPI接口的时序来编写的。

OLED_GRAM[128][8]中的128代表列数(x坐标),而8代表的是页,每页又包含8行,总共64行(y坐标)。从高到低对应行数从小到大。比如,我们要在x=100,y=29这个点写入1,则可以用这个句子实现:

                                      OLED_GRAM[100][4]|=1<<2;

一个通用的在点(x,y)置1表达式为:

                                      OLED_GRAM[x][7-y/8]|=1<<(7-y%8);

其中x的范围为:0127;y的范围为:063。

因此,我们可以得出下一个将要介绍的函数:画点函数,void OLED_DrawPoint(u8 x,u8 y,u8 t);函数代码如下:

void OLED_DrawPoint(u8 x,u8 y,u8 t)

{

   u8 pos,bx,temp=0;

   if(x>127||y>63)return;//超出范围了.

   pos=7-y/8;

   bx=y%8;

   temp=1<<(7-bx);

   if(t)OLED_GRAM[x][pos]|=temp;

   else OLED_GRAM[x][pos]&=~temp;     

}

该函数有3个参数,前两个是坐标,第三个t为要写入1还是0。该函数实现了我们在OLED模块上任意位置画点的功能。

在介绍完画点函数之后,我们介绍一下显示字符函数,OLED_ShowChar,在介绍之前,我们来介绍一下字符(ASCII字符集)是怎么显示在OLED模块上去的。要显示字符,我们先要有字符的点阵数据,ASCII常用的字符集总共有95个,从空格符开始,分别为: !"#$%&’()*+,-0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~.

我们先要得到这个字符集的点阵数据,这里我们介绍一个款很好的字符提取软件:PCtoLCD2002完美版。该软件可以提供各种字符,包括汉字(字体和大小都可以自己设置)阵提取,且取模方式可以设置好几种,常用的取模方式,该软件都支持。该软件还支持图形模式,也就是用户可以自己定义图片的大小,然后画图,根据所画的图形再生成点阵数据,这功能在制作图标或图片的时候很有用。

然后我们选择设置,在设置里面设置取模方式

设置的取模方式,在右上角的取模说明里面有,即:从第一列开始向下每取8个点作为一个字节,如果最后不足8个点就补满8位。取模顺序是从高到低,即第一个点作为最高位。如*-------取为10000000。其实就是按如图17.3.3所示的这种方式:

图17.3.3 取模方式图解

从上到下,从左到右,高位在前。我们按这样的取模方式,然后把ASCII字符集按126大小和160大小取模出来(对应汉字大小为1212和1616,字符的只有汉字的一半大!),保存在oledfont.h里面,每个126的字符占用12个字节,每个168的字符占用16个字节。具体见oledfont.h部分代码(该部分我们不再这里列出来了,请大家参考光盘里面的代码)。

在知道了取模方式之后,我们就可以根据取模的方式来编写显示字符的代码了,这里我们针对以上取模方式的显示字符代码如下:

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode)

{

   u8 temp,t,t1;

   u8 y0=y;

   chr=chr-' ';//得到偏移后的值                              

for(t=0;t

{  

          if(size==12)temp=oled_asc2_1206[chr][t];  //调用1206字体

          else temp=oled_asc2_1608[chr][t];              //调用1608字体                                  

   for(t1=0;t1<8;t1++)

          {

                 if(temp&0x80)OLED_DrawPoint(x,y,mode);

                 else OLED_DrawPoint(x,y,!mode);

                 temp<<=1;

                 y++;

                 if((y-y0)==size)

                 {

                        y=y0;

                        x++;

                        break;

                 }

          }     

}         

}

该函数为字符以及字符串显示的核心部分,函数中chr=chr-’ ';这句是要得到在字符点阵数据里面的实际地址,因为我们的取模是从空格键开始的,例如oled_asc2_1206[0][0],代表的是空格符开始的点阵码。在接下来的代码,我们也是按照从上到小,从左到右的取模方式来编写的,先得到最高位,然后判断是写1还是0,画点;接着读第二位,如此循环,直到一个字符的点阵全部取完为止。这其中涉及到列地址和行地址的自增,根据取模方式来理解,就不难了。

oled.c的内容就为大家介绍到这里,将oled.c保存,然后加入到HARDWARE组下。接下来我们在oled.h中输入如下代码:

#ifndef __OLED_H

#define __OLED_H

#include “sys.h”

#include “stdlib.h”

//OLED模式设置

//0:4线串行模式

//1:并行8080模式

#define OLED_MODE 1

//---------------------------OLED端口定义--------------------------

#define OLED_CS PDout(6)

#define OLED_RST PGout(15)

#define OLED_RS PDout(3)

#define OLED_WR PGout(14)

#define OLED_RD PGout(13)

//PC0~7,作为数据线

#define DATAOUT(x) GPIOC->ODR=(GPIOC->ODR&0xff00)|(x&0x00FF); //输出

//使用4线串行接口时使用

#define OLED_SCLK PCout(0)

#define OLED_SDIN PCout(1)

#define OLED_CMD 0 //写命令

#define OLED_DATA 1 //写数据

//OLED控制用函数

void OLED_WR_Byte(u8 dat,u8 cmd);

void OLED_Display_On(void);

void OLED_Display_Off(void);

void OLED_Refresh_Gram(void);

void OLED_Init(void);

void OLED_Clear(void);

void OLED_DrawPoint(u8 x,u8 y,u8 t);

void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);

void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);

void OLED_ShowString(u8 x,u8 y,const u8 *p);

#endif

该部分比较简单,OLED_MODE的定义也在这个文件里面,我们必须根据自己OLED模块BS0~2的设置(目前代码仅支持8080和4线SPI)来确定OLED_MODE的值。

保存好oled.h之后,我们就可以在主程序里面编写我们的应用层代码了,该部分代码如下:

int main(void)

{

  u8 t;

  Stm32_Clock_Init(9);    //系统时钟设置

   uart_init(72,9600);      //串口初始化为9600

   delay_init(72);                  //延时初始化

   LED_Init();                //初始化与LED连接的硬件接口

  OLED_Init();                //初始化液晶     

  OLED_ShowString(0,0, "0.96' OLED TEST"); 

  OLED_ShowString(0,16,"ATOM@ALIENTEK"); 

  OLED_ShowString(0,32,"2010/06/3"); 

  OLED_ShowString(0,48,"ASCII:"); 

  OLED_ShowString(63,48,"CODE:"); 

   OLED_Refresh_Gram();      

   t=' ';

   while(1)

   {           

          OLED_ShowChar(48,48,t,16,1);//显示ASCII字符      

          OLED_Refresh_Gram();

          t++;

          if(t>'~')t=' ';

          OLED_ShowNum(103,48,t,3,16);//显示ASCII字符的码值

          delay_ms(300);

          LED0=!LED0;

   }      

}

该部分代码用于在OLED上显示一些字符,然后从空格键开始不停的循环显示ASCII字符集,并显示该字符的ASCII值。注意在test.c文件里面包含oled.h头文件,同时把oled.c文件加入到HARDWARE组下,然后我们编译此工程,直到编译成功为止。

你可能感兴趣的:(OLED显示实验源程序)