历史上的今天
返回首页

历史上的今天

今天是:2025年01月14日(星期二)

正在发生

2019年01月14日 | 51 软件模拟SPI驱动OLED

2019-01-14 来源:eefocus

1.OLED简介


OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。OLED由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。


OLED显示技术具有自发光的特性,采用非常薄的有机材料涂层和玻璃基板,当有电流通过时,这些有机材料就会发光,而且OLED显示屏幕可视角度大,并且能够节省电能。


2.OLED模块简介


本文采用中景园的 0.96 寸的OLED显示模块。原理图如下:


这里写图片描述


2.1模块特点:


1)0.96 寸 OLED 有黄蓝,白,蓝三种颜色可选;其中黄蓝是屏上 1/4 部分为黄光,下 3/4 为蓝;而且是固定区域显示固定颜色,颜色和显示区域均不能修改;白光则为纯白,也就是黑底白字;蓝色则为纯蓝,也就是黑底蓝字。


2)分辨率为 128*64;


3)多种接口方式;OLED 裸屏总共种接口包括:6800、8080 两种并行接口方式、3 线或 4 线的串行 SPI 接口方式、 IIC 接口方式(只需要 2 根线就可以控制 OLED 了),这五种接口是通过屏上的 BS0~BS2 来配置的。


2.2模块接口:


接口 功能

GND 电源地

VCC 电源正(3~5.5V)

D0 OLED 的 D0 脚,在 SPI 和 IIC 通信中为时钟管脚

D1 OLED 的 D1 脚,在 SPI 和 IIC 通信中为数据管脚

RES OLED 的 RES#脚,用来复位(低电平复位)

DC OLED 的 D/C#E 脚,数据和命令控制管脚

CS OLED 的 CS#脚,片选管脚


2.3 OLED 驱动 IC:


本屏所用的驱动 IC 为 SSD1306;其具有内部升压功能;所以在设计的时候不需要再专一设计升压电路;屏也可以选用外部升压,。SSD1306 的每页包含了128 个字节,总共 8 页,这样刚好是128*64 的点阵大小。


这里写图片描述


2.4 OLED 驱动方式:


此模块支持四线 SPI、三线 SPI、IIC 接口和 6800、8080 并口方式,模块的通信接口是通过 BS0,BS1,BS2 三个管脚来配置。


这里写图片描述

2.5 SSD1306的命令


这里写图片描述


0X81:设置对比度。包含两个字节,第一个0X81为命令,随后发送的一个字节为要设置的对比度的值。这个值设置得越大屏幕就越亮。


0XAE,0XAF: 0XAE为关闭显示命令;0XAF为开启显示命令。


0X8D:包含2个字节,第一个为命令字,第二个为设置值,第二个字节的BIT2表示电荷泵的开关状态,该位为1,则开启电荷泵,为0则关闭。在模块初始化的时候,这个必须要开启,否则是看不到屏幕显示的。 


命令0XB0~B7:用于设置页地址,其低三位的值对应着GRAM的页地址。


0X00~0X0F:用于设置显示时的起始列地址低四位。 

0X10~0X1F:用于设置显示时的起始列地址高四位。


3.实现程序


3.1 模拟SPI


时序:


这里写图片描述


对应时序图实现程序:


程序:


sbit OLED_SCL=P3^0;//时钟 D0(SCLK)

sbit OLED_SDIN=P3^1;//D1(MOSI) 数据

sbit OLED_RST =P3^2;//复位

sbit OLED_DC =P3^3;//数据/命令控制

sbit OLED_CS=P3^4; //片选


//Function:向OLED设备写入一个字节

//Input:dat:写入的数据/命令;cmd:写入的是数据或命令判断

//cmd=0:写入命令;cmd=1:写入数据(DC为高:数据;DC为低:命令)

//Output:无

//Return:无

//Others:无

void Spi_Write_Byte(uchar dat,uchar cmd)

{

    uchar len;


    if(cmd)

        OLED_DC = 1;

    else

        OLED_DC = 0;

    OLED_CS = 0;

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

    {

        OLED_SCL = 0;//低电平开始采样数据

        if(dat&0x80)

            OLED_SDIN = 1;

        else

            OLED_SDIN = 0;

        OLED_SCL = 1;

        dat = dat<<1;

    }

    OLED_CS = 1;

    OLED_DC = 1;

}



显示部分实现:


#include "oled_display.h"

#include "oled_font.h"



//Function:设置显示坐标

//Input:x:横坐标;y:纵坐标

//Output:无

//Return:无

//Others:无

void OLED_Set_Pos(uchar x,uchar y)

{

    Spi_Write_Byte(0xb0+y,OLED_CMD);//列偏移

    Spi_Write_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);//行高四位地址

    Spi_Write_Byte((x&0x0f)|0X01,OLED_CMD);//行低四位地址

}


//Function:开启OLED显示

//Input:无

//Output:无

//Return:无

//Others:无

void OLED_Display_On(void)

{

    Spi_Write_Byte(0X8D,OLED_CMD);//设置DCDC

    Spi_Write_Byte(0X14,OLED_CMD);//打开DCDC

    Spi_Write_Byte(0XAF,OLED_CMD);//打开OLED

}


//Function:关闭OLED显示

//Input:无

//Output:无

//Return:无

//Others:无

void OLED_Display_Off(void)

{

    Spi_Write_Byte(0X8D,OLED_CMD);//设置DCDC

    Spi_Write_Byte(0X10,OLED_CMD);//关闭DCDC

    Spi_Write_Byte(0XAE,OLED_CMD);//关闭OLED

}


//Function:清屏

//Input:无

//Output:无

//Return:无

//Others:无

void OLED_Display_Clear(void)

{

    uchar page,len;


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

    {

        Spi_Write_Byte(0xB0+page,OLED_CMD);//设置页地址(0--7)

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

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


        for(len=0;len<128;len++)

            Spi_Write_Byte(0,OLED_DATA);//写入0;屏熄灭

    }

}

//Function:在指定位置显示一个字符,包括部分字符

    //x:0~127;y:0~63;size:选择字体 16/12 

//Input:x:横坐标;y:纵坐标;str:显示的字符

//Output:无

//Return:无

//Others:无               

void OLED_Display_Onechar(uchar x,uchar y,uchar str)

{

    uchar i=0,ret=0;

    //ret = str -32;

    ret = str - ' ';//得到偏移后的值,对ASCLL码进行一个减法.即在二维数组里找它的位置  

    if(x>Max_Column-1)

    {

        x = 0;

        y = y + 2;//针对16号的字符

    }

    if(SIZE == 16 )

    {

        OLED_Set_Pos(x,y);

        //16的字体分成两部分写入

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

            Spi_Write_Byte(F8X16[ret*16+i],OLED_DATA);

        OLED_Set_Pos(x,y+1);

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

            Spi_Write_Byte(F8X16[ret*16+i+8],OLED_DATA);

    }

    else{

        OLED_Set_Pos(x,y+1);

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

            Spi_Write_Byte(F6x8[ret][i],OLED_DATA);

    }

}


//Function:显示字符串

//Input:x:横坐标;y:纵坐标;str:显示的字符串

//Output:无

//Return:无

//Others:无

void OLED_Display_String(uchar x,uchar y,uchar *str)

{

    uchar i=0;


    while(str[i]!='\0')

    {

        OLED_Display_Onechar(x,y,str[i]);

        x += 8;

        if(x>120)

        {

            x = 0;

            y += 2;

        }

        i++;

    }

}


//Function:显示中文

//Input:x:横坐标;y:纵坐标;no:显示的字的序号

//Output:无

//Return:无

//Others:无

void OLED_Display_Chinese(uchar x,uchar y,uchar no)

{

    uchar ch,addr=0;


    OLED_Set_Pos(x,y);

    for(ch=0;ch<16;ch++)//数组行列寻址

    {

        Spi_Write_Byte(TEST[2*no][ch],OLED_DATA);

        addr += 1;

    }

    OLED_Set_Pos(x,y+1);

    for(ch=0;ch<16;ch++)

    {

        Spi_Write_Byte(TEST[2*no+1][ch],OLED_DATA);

        addr += 1;

    }


}


//Function:显示图片

//Input:x0,x1:横坐标;y0,y1:纵坐标;BMP:显示的图片

//注意:y1<8,页寻址模式

//Output:无

//Return:无

//Others:无

void OLED_Display_Picture(uchar x0,uchar y0,uchar x1,uchar y1,uchar BMP[])

{

    uchar x,y;

    uint i=0;


    if(y1%8==0)

        y = y1 / 8;

    else

        y = y1 /8 + 1;

    for(y=y0;y

    {

        OLED_Set_Pos(x0,y);

        for(x=x0;x

        {

            Spi_Write_Byte(BMP[i++],OLED_DATA);

        }

    }

}


//计算m的n次方

uint OLED_Pow(uchar m,uchar n)

{

    uint ret = 1;

    while(n--)

        ret *= m;

    return ret;

}


//Function:显示数字

//Input:x:横坐标;1:纵坐标;num:显示的数字:len:数字长度;size_num:数字字体

//Output:无

//Return:无

//Others:无

void OLED_Display_Num(uchar x,uchar y,uint num,uchar len,uchar size_num)

{

    uchar t,temp;

    uchar enshow = 0;


    for(t=0;t

    {

        temp = (num/OLED_Pow(10,len-t-1))%10;//把显示的数字一位一位取出来

        if(enshow==0&&t<(len-1))

        {

            if(temp==0)

            {

                OLED_Display_Onechar(x+(size_num/2)*t,y,' ');

                continue;

            }

            else

                enshow = 1;

        }

        OLED_Display_Onechar(x+(size_num/2)*t,y,temp+'0');

    }

}


//初始化SSD1306

void OLED_Init(void)

{

    OLED_RST = 1;

    delay_ms(200);

    OLED_RST = 0;

    delay_ms(200);

    OLED_RST = 1; 


    Spi_Write_Byte(0xAE,OLED_CMD);//关闭OLED

    Spi_Write_Byte(0x00,OLED_CMD);//设置列低位地址

    Spi_Write_Byte(0x10,OLED_CMD);//设置列高位地址

    Spi_Write_Byte(0x40,OLED_CMD);//设置起始行地址及映射RAM显示起始行 (0x00~0x3F)

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

    Spi_Write_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness

    Spi_Write_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常

    Spi_Write_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常

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

    Spi_Write_Byte(0xA8,OLED_CMD);//设置驱动路数(1 to 64)

    Spi_Write_Byte(0x3f,OLED_CMD);//--1/64 duty

    Spi_Write_Byte(0xD3,OLED_CMD);//-设置显示偏移(0x00~0x3F)

    Spi_Write_Byte(0x00,OLED_CMD);//-not offset

    Spi_Write_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency

    Spi_Write_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec

    Spi_Write_Byte(0xD9,OLED_CMD);//--set pre-charge period

    Spi_Write_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock

    Spi_Write_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration

    Spi_Write_Byte(0x12,OLED_CMD);

    Spi_Write_Byte(0xDB,OLED_CMD);//--set vcomh

    Spi_Write_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level

    Spi_Write_Byte(0x20,OLED_CMD);//设置页地址模式(0x00/0x01/0x02)

    Spi_Write_Byte(0x02,OLED_CMD);//

    Spi_Write_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable

    Spi_Write_Byte(0x14,OLED_CMD);//--set(0x10) disable

    Spi_Write_Byte(0xA4,OLED_CMD);//禁用显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)(0xa4/0xa5)

    Spi_Write_Byte(0xA6,OLED_CMD);// 不使用反向显示 (0xa6/a7) 

    Spi_Write_Byte(0xAF,OLED_CMD);//打开显示


    Spi_Write_Byte(0xAF,OLED_CMD); /*display ON*/

    OLED_Display_Clear();

    OLED_Set_Pos(0,0);  

}  


断断续续总算写完了,欢迎大家积极讨论,互相学习。


推荐阅读

史海拾趣

FutureWafer公司的发展小趣事

对于冷热饮水机电路及家用电器电路,网友可能提出多种问题。以下是一些可能的问题及详细回答:

一、冷热饮水机电路相关问题

  1. 冷热饮水机的工作原理是什么?

    回答:冷热饮水机的工作原理主要分为加热和制冷两部分。加热部分通过电加热元件(如电炉丝)对水进行加热,当水温达到设定温度时,温控器自动切断电源,进入保温状态。制冷部分则通常利用半导体制冷技术,通过半导体制冷片对冷水进行降温,同时散热风扇帮助散热,以保持冷水的温度。

  2. 冷热饮水机为何会出现不加热的故障?

    回答:冷热饮水机不加热的故障可能由多种原因引起,包括但不限于:

    • 加热元件(如电炉丝)损坏或烧毁。
    • 温控器故障,导致无法正确感知水温或无法切断电源。
    • 电源线路问题,如插头松动、保险丝熔断等。
    • 加热控制电路故障,如继电器、微处理器等元件损坏。
  3. 冷热饮水机制冷效果不佳怎么办?

    回答:如果冷热饮水机制冷效果不佳,可以尝试以下解决方法:

    • 检查半导体制冷片和散热风扇是否工作正常,如有损坏需更换。
    • 清理饮水机内部的灰尘和杂物,确保空气流通顺畅。
    • 检查制冷控制电路是否有问题,如温控器、继电器等元件是否工作正常。
    • 如果以上方法均无效,可能是制冷剂泄漏或制冷系统其他部件故障,需请专业人员维修。
  4. 如何判断冷热饮水机的电路是否损坏?

    回答:判断冷热饮水机的电路是否损坏,可以通过以下步骤进行:

    • 检查电源插头和插座是否接触良好,电源是否正常。
    • 使用万用表等工具检查电路中的保险丝、继电器、微处理器等元件是否损坏。
    • 观察饮水机的指示灯和显示屏是否正常显示,以判断控制电路是否工作正常。
    • 如果以上检查均正常,但饮水机仍无法正常工作,可能是电路中存在隐性故障或需要更专业的检测手段。

二、家用电器电路相关问题

  1. 家用电器电路的常见故障有哪些?

    回答:家用电器电路的常见故障包括但不限于:

    • 电源故障,如插头松动、保险丝熔断等。
    • 控制电路故障,如遥控器失灵、控制面板按键无反应等。
    • 电机或压缩机故障,如电机不转、压缩机不制冷等。
    • 传感器故障,如温度传感器失灵导致温度控制不准确。
    • 电路板损坏,如电路板上的元件烧毁或短路。
  2. 如何预防家用电器电路故障?

    回答:预防家用电器电路故障可以从以下几个方面入手:

    • 定期清洁家用电器,避免灰尘和杂物积累导致散热不良或短路。
    • 使用合格的电源线和插座,避免使用劣质产品导致电路故障。
    • 注意家用电器的使用环境,避免潮湿、高温等恶劣环境对电路造成损害。
    • 不要超负荷使用家用电器,避免长时间连续工作导致电路过热或损坏。
    • 定期检查家用电器的电路和元件是否完好,如有问题及时维修或更换。
  3. 家用电器电路维修需要注意哪些事项?

    回答:家用电器电路维修需要注意以下事项:

    • 在维修前务必切断电源,避免触电危险。
    • 使用合适的工具和设备进行维修,避免损坏其他部件或造成更大的故障。
    • 在维修过程中要仔细检查电路和元件的损坏情况,避免遗漏或误判。
    • 对于不熟悉或复杂的电路故障,应请专业人员进行维修。
    • 维修完成后要进行测试,确保家用电器能够正常工作并符合安全要求。
DILABS公司的发展小趣事

DILABS公司成立于XX世纪初,由一群热衷于电子技术的工程师创立。在公司初创时期,他们面临了资金短缺、技术瓶颈和市场竞争等多重挑战。然而,他们坚持创新,经过无数次实验,终于在芯片封装技术上取得了突破。这一突破性的技术为DILABS赢得了业界的关注,也为公司的发展奠定了坚实的基础。

DUBILIER公司的发展小趣事

DUBILIER公司非常重视人才的培养和发展。公司建立了完善的人才培训体系,为员工提供各种培训和发展机会。通过内部培训和外部引进相结合的方式,DUBILIER公司培养了一支高素质、专业化的技术和管理团队。这些人才不仅为公司的发展提供了强大的支持,还为公司赢得了更多的商业机会和合作伙伴。

Bergquist Company公司的发展小趣事

70年代,随着电子技术的飞速发展,导热材料行业迎来了前所未有的机遇。Bergquist Company敏锐地捕捉到了这一机遇,开始涉足导热材料领域。公司投入大量资金进行研发,成功开发出了一系列具有创新性的导热产品。这些产品不仅性能卓越,而且价格合理,深受市场欢迎。凭借这些优势,Bergquist Company逐渐在导热材料行业崭露头角,成为了行业的领军企业。

C.K Magma公司的发展小趣事

在追求经济效益的同时,C.K Magma公司也积极履行社会责任。他们注重绿色发展和可持续经营,通过采用环保材料和工艺、优化生产过程等方式,降低产品对环境的影响。此外,公司还积极参与公益事业,为社会做出贡献。这些举措不仅提升了公司的社会形象,也为公司的长远发展提供了有力保障。

这五个故事展示了C.K Magma公司在电子行业中的发展历程和取得的成就。通过技术突破、市场拓展、国际合作、创新产品和绿色发展等方面的努力,公司逐渐在行业中崭露头角,成为了一家具有影响力和竞争力的企业。

Cicoil公司的发展小趣事

随着计算机技术的飞速发展,处理器成为了电子设备的核心部件。Cicoil公司看到了这一趋势,开始与处理器制造商展开合作。他们为处理器提供高质量的连接电缆,确保处理器能够稳定、高效地运行。这一合作使得Cicoil公司的电缆产品进一步深入到计算机领域,也为其带来了更多的商业机会。

问答坊 | AI 解惑

请问后缀为PCB的文件用什么软件打开呢

请问后缀为PCB的文件用什么软件打开呢请问后缀为PCB的文件用什么软件打开呢 谢谢了…

查看全部问答>

程序滤波(很多信号采集实用)

1、限幅滤波法(又称程序判断滤波法)     A、方法:         根据经验判断,确定两次采样允许的最大偏差值(设为A)         每次检测到新值时判断:    &n ...…

查看全部问答>

寻觅高手解答

设备标准: Linux version 2.4.17-uC-pre5 (root@dhome) (gcc version 2.95.3 20010315 (release)(ColdFire patches - 20010318 from http://fiddes.net/coldfire/)(uClinux XIP and shared lib patches from http://www.snapgear.com/)) #2 ?? 4?? ...…

查看全部问答>

诚招WINCE软件工程师(北京)

1、软件工程或计算机软件相关专业,本科以上学历,英语四级以上。   2、熟练掌握C++及MFC编程,有3年以上基于EVC系统的开发经验。   公司地址:北京市海淀区西三旗 如有意者,可将简历发送至:hj3850@163.com 亦可来电 ...…

查看全部问答>

5438与仿真器终于连上了

可以连上,必须把JTAG口RST脚上的电容拿掉,但又碰到了新问题:在线仿真5438时速度极慢,下载操作需1分钟左右,执行一个单步需十几秒,执行一次复位需半分钟,运算结果倒是正确的。iar是4.20.1版,操作系统是vista,仿真器是USB型的LSD-FET430UIF, ...…

查看全部问答>

Lm3S811 PLL 设置倍频到 50Mhz 之后,为什么实际只有37.5Mhz 呢?

Lm3S811 PLL 设置倍频到 50Mhz 之后,为什么实际只有37.5Mhz 呢?   SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |SYSCTL_XTAL_6MHZ);//UART 初始化SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);SysCtlPeripheral ...…

查看全部问答>

急求助:Linux无法从NFlash(K9F1208U0A)启动

自己开了一个ARM架构板, CPU: S5PC100, SDRAM: K4T1G164QQ(两颗),Nand flash: K9F1208U0A。现在系统能从SD卡启动,对FLASH擦除操作后返回OK,但将SD卡取后,从FLASH启动串口不出现任何信息。有没有人知道是哪里出了问题,急求大家的帮助,不甚 ...…

查看全部问答>

学习心得

看了视频教程,了解MSP430单片机主要侧重低功耗,应用于好多产品,像电能表,远传抄表等。这使我想起以前面试过一家电能表公司,就是用MSP430开发的。MSP430单片机是16位单片机,在常用的几类单片机中,像51,PIC,AVR,MSP430,FRESCALL,只有MSP430全 ...…

查看全部问答>

CCS使用

在使用CCS的时候,出现下图的提示,这是什么原因啊?要怎样解决呢? ,…

查看全部问答>

求介绍一款支持G.711压缩标准的语音芯片,能将语音数字化!

最近做项目用到支持G.711标准的语音压缩芯片,并且要求录音时间不小于30S,我算了一下这个要求芯片的存储空间至少是64KB*30=1920KB,大于2M的就可以。因为小弟接触的比较少,在网上也没有搜到,故求大神帮助! …

查看全部问答>