首页 > 其他分享 >03 OLED显示屏实现

03 OLED显示屏实现

时间:2024-04-25 23:24:26浏览次数:24  
标签:03 0x08 显示屏 0x20 0x00 write OLED 0x80 GPIO

目录

前言

这一章主要是上一节没有讲完的项目的一个编写,上一章我们说道单片机的输入和输出,包括四种单片机与外设之间的数据交换使用到的方式,这一章就利用前面说到的这几种方式来编写一个OLED显示屏的驱动。

一、软件模拟IIC协议

这使用的OLED屏幕是用的4引脚IIC协议的,在编写相关代码之前我们需要先了解一下什么是IIC协议,在单片机中,IIC通常指的是Inter-Integrated Circuit,也称为I2C(Inter-Integrated Circuit)总线,它是一种用于在集成电路之间进行通信的串行通信协议。

I2C总线由飞利浦公司(现在的恩智浦半导体公司)开发,用于在数字集成电路之间进行通信。它通常用于连接微控制器和外部设备,如传感器、存储器、扩展模块等。

I2C总线使用两根线进行通信:串行数据线(SDA)和串行时钟线(SCL)。通过这两根线,多个设备可以在同一总线上进行通信。每个设备都有一个唯一的地址,因此可以通过地址来选择要通信的特定设备。

I2C总线采用主从结构,主设备负责发起通信并控制总线,而从设备则响应主设备的命令。这种通信协议简单而灵活,适用于连接多种类型的设备。

简单来说IIC协议就是一种串行通讯协议,我们只需要对两根数据线进行操作就可以了,在32中有对应的IIC的函数库,我们可以直接调用来实现,但是那个内容我安排在后面了,我们实现先学习上一节用输入和输出来进行模拟的方式,学习完后我们才可以用一些传感器来观察效果并实现更高级的内容。

1.开启IIC协议

我们在使用IIC协议之前需要发送一个开始信号的,这个开始信号的时序图如下:

3.png

可以看到左边是起始条件,我们要开启IIC协议必须得先发送其实条件,这里的代码很容易就可以编写,这里我用了一下宏定义和位操作:

void IIC_Start(void){
    GPIO_SetBits(GPIO, SDA);
    GPIO_SetBits(GPIO, SCL);
    GPIO_ResetBits(GPIO, SDA);
    GPIO_ResetBits(GPIO, SCL);
}

这个函数就可以让IIC协议开始,当开始后我们就可以参数数据和一些命令了。

2.结束IIC协议

当一条IIC协议传输完成后需要发送一条协议结束信号,这个信号的写法也很简单,代码如下:

void IIC_Stop(void){
    GPIO_ResetBits(GPIO, SDA);
    GPIO_SetBits(GPIO, SCL);
    GPIO_SetBits(GPIO, SDA);
}

这样就可以结束上一次的IIC数据传输。

3.传输数据

这个的时序图也可以参考上面的时序图

3.png

看中间的位置,这个位置就是传输一位数据时的时序图,那么我们就可以根据这个图写出传输一个字节的数据了,这里需要注意一下,我们传输一条字节数据时是需要先传递高位数据,在SCL从高电平到低电平的时候才会将数据传递过去,代码如下:

void IIC_WriteBit(u8 byte){
    u8 i;
    for (i = 0; i < 8; i++){
        if (byte & 0x80){
            GPIO_SetBits(GPIO, SDA);
        }
        else{
            GPIO_ResetBits(GPIO, SDA);
        }
        byte <<= 1;
        GPIO_SetBits(GPIO, SCL);
        GPIO_ResetBits(GPIO, SCL);
    }
    // 跳过一个ACK信号
    GPIO_SetBits(GPIO, SCL);
    GPIO_ResetBits(GPIO, SCL);
}

这里就是向IIC传递一个字节数据的函数,最后这里写了一个跳过ACK响应,这个ACK响应是什么呢?

我们可以看这个时序图:

3.jpg

可以看到WORD数据后有一个ACK信号,这个ACK信号是响应信号,就是当我们数据传递到IIC设备后,它如果接受到数据后就会发送一个接受信号,这个就是接受信号,我们不需要这个信号,所以直接忽略即可。

好了,模拟IIC的操作就是这些了,下面我们就介绍一下OLED的操作了。

二、OLED的操作

1.传输数据的准备

在用IIC协议向OLED传输数据前需要做的操作有下面这几步,我们只有操作好了才能正确的传输命令或者数据:

1.开启IIC协议

2.向OLED传入从地址

3.传输是写入的是命令还是数据

4.开始传输数据

5.结束IIC协议

在传输数据或者命令之前要先向OLED传入它的地址,相当于一个片选,当选中了才可以进行操作,每个OLED的地址都是固定的,都是为0x78,如果要更改需要联系厂家

4.png

这个就是时序图,在右边的SSD1306 Slave Address就是它的地址,我们就将这个先传入即可,在下面的写入命令会有这个代码。

然后就是传入是数据还是命令,在words这个数据块中可以告诉你该如何传入,第一个Co不用管,默认0皆可,然后第二个是选择命令还是数据,1是数据,0是命令,所以就可以得到下面的内容,当我们要写命令的时候,传入完地址后再传入0x00。如果要写数据得到的时候,要传入0x40

2.写入命令

知道了上面的过程后我们就可以写写入命令的函数了:

void write_command(u8 cmd){
    IIC_Start();        // 开启IIC协议
    IIC_Write(0x78);    // 传入地址
    IIC_Write(0x00);    // 传入0x00代表要传入命令
    IIC_Write(cmd);     // 传入命令
    IIC_Stop();         // 结束IIC协议
}

这样就完成了一次写入命令。

3.写入数据

写入数据的函数和上面的函数一样,只不过就是传入地址后传入的内容不同:

void write_data(u8 data){
    IIC_Start();
    IIC_Write(0x78);
    IIC_Write(0x40);
    IIC_Write(data);
    IIC_Stop();
}

这样就可以完成一次数据的传送。

这些基础功能讲完了,后面的操作是需要结合命令列表来进行操作了。

4.初始化函数

首先我们要操作需要先进行初始化,这里初始化的功能不会全部说明,大家如果有问题可以查一下OLED的数据手册即可。

初始化传入的命令步骤如下:

  1. 关闭显示屏

  2. 设置显示时钟分频比/振荡器频率

  3. 设置多路复用率

  4. 显示开始行

  5. 设置左右方向

  6. 设置上下方向

  7. 设置COM引脚硬件配置

  8. 设置对比度

  9. 设置预充电周期

  10. 设置VCOMH取消选择级别

  11. 设置整个显示打开/关闭

  12. 设置正常/倒转显示

  13. 设置充电泵

  14. 开启显示

这个内容需要查看手册来慢慢写,我这里就不一个一个介绍了,我这写好了,你们可以直接复制和粘贴

#define CMD 0x00
#define DATA 0x40
#define CLOSESHOW 0xAE
#define SETCLS 0xD5
#define RATE    0xA8
#define OFFSET  0xD3
#define START   0x40
#define LEFTRIG 0xA1
#define UPDOWN  0xC8
#define HARD    0xDA
#define RATION  0x81
#define  CYCLE  0xD9
#define VCOM    0xDB
#define SHOWUP  0xA4
#define BEN     0x8D
#define SHOWOK  0xAF
void OLED_Init(){
    write_command(CLOSESHOW);
    write_command(SETCLS);
    write_command(0x80);
    write_command(RATE);
    write_command(0x3F);
    write_command(OFFSET);
    write_command(0x00);
    write_command(START);
    write_command(LEFTRIG);
    write_command(UPDOWN);
    write_command(HARD);
    write_command(0x12);
    write_command(RATION);
    write_command(0xCF);
    write_command(CYCLE);
    write_command(0xF1);
    write_command(VCOM);
    write_command(0x30);
    write_command(SHOWUP);
    write_command(0xA6);
    write_command(BEN);
    write_command(0x14);
    write_command(SHOWOK);
}

5.设置光标

在屏幕中有一个光标,只不过我们在初始化的时候就把这个光标给隐藏了,但是你要有这个概念,设置光标也是需要使用命令来进行操作,这里也需要参考一下命令手册,但是,我这已经写好了,直接用就可以了:

void set_currsor(u8 row, u8 col){
    write_command(0xB0 |row);
    write_command(0x10 | ((col & 0xF0) >> 4));
    write_command(0x00 | (col & 0x0F));
}

row就是光标所在的行,col是光标所在的列。

6.显示字符

要让OLED显示字符需要使用到取摸3工具取出来,因为我们显示其实就是让那些液晶块亮,哪些不亮,所以需要取摸,这里我没找到Linux下的取摸工具,等后面我有时间后我写一个,然后下面是我之前取好的ASCII后32个字符对应的字摸,大家可以直接拿来用:

static unsigned char ASCII1[][16] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},

{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00},/*"!",0*/

{0x00,0x10,0x0C,0x02,0x10,0x0C,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*""",1*/

{0x00,0x40,0xC0,0x78,0x40,0xC0,0x78,0x00,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x00},/*"#",2*/

{0x00,0x70,0x88,0x88,0xFC,0x08,0x30,0x00,0x00,0x18,0x20,0x20,0xFF,0x21,0x1E,0x00},/*"$",3*/

{0xF0,0x08,0xF0,0x80,0x60,0x18,0x00,0x00,0x00,0x31,0x0C,0x03,0x1E,0x21,0x1E,0x00},/*"%",4*/

{0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x2C,0x19,0x27,0x21,0x10},/*"&",5*/

{0x00,0x12,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",6*/

{0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},/*"(",7*/

{0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},/*")",8*/

{0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},/*"*",9*/

{0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x0F,0x01,0x01,0x01},/*"+",10*/

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x70,0x00,0x00,0x00,0x00,0x00},/*",",11*/

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00},/*"-",12*/

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},/*".",13*/

{0x00,0x00,0x00,0x00,0xC0,0x38,0x04,0x00,0x00,0x60,0x18,0x07,0x00,0x00,0x00,0x00},/*"/",14*/

{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",0*/

{0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"1",1*/

{0x00,0x70,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",2*/

{0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x20,0x21,0x21,0x22,0x1C,0x00},/*"3",3*/

{0x00,0x00,0x80,0x40,0x30,0xF8,0x00,0x00,0x00,0x06,0x05,0x24,0x24,0x3F,0x24,0x24},/*"4",4*/

{0x00,0xF8,0x88,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x20,0x20,0x20,0x11,0x0E,0x00},/*"5",5*/

{0x00,0xE0,0x10,0x88,0x88,0x90,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x20,0x1F,0x00},/*"6",6*/

{0x00,0x18,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x00},/*"7",7*/

{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",8*/

{0x00,0xF0,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x01,0x12,0x22,0x22,0x11,0x0F,0x00},/*"9",9*/

{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},/*":",0*/

{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00},/*";",1*/

{0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},/*"<",2*/

{0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x00},/*"=",3*/

{0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},/*">",4*/

{0x00,0x70,0x48,0x08,0x08,0x88,0x70,0x00,0x00,0x00,0x00,0x30,0x37,0x00,0x00,0x00},/*"?",5*/

{0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x28,0x2F,0x28,0x17,0x00},/*"@",6*/

{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},/*"A",0*/

{0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},/*"B",1*/

{0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},/*"C",2*/

{0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},/*"D",3*/

{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},/*"E",4*/

{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},/*"F",5*/

{0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},/*"G",6*/

{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},/*"H",7*/

{0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"I",8*/

{0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},/*"J",9*/

{0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},/*"K",10*/

{0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},/*"L",11*/

{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x01,0x3E,0x01,0x3F,0x20,0x00},/*"M",12*/

{0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},/*"N",13*/

{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},/*"O",14*/

{0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},/*"P",15*/

{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x28,0x28,0x30,0x50,0x4F,0x00},/*"Q",16*/

{0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},/*"R",17*/

{0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},/*"S",18*/

{0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"T",19*/

{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"U",20*/

{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},/*"V",21*/

{0x08,0xF8,0x00,0xF8,0x00,0xF8,0x08,0x00,0x00,0x03,0x3E,0x01,0x3E,0x03,0x00,0x00},/*"W",22*/

{0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},/*"X",23*/

{0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"Y",24*/

{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},/*"Z",25*/

{0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},/*"[",0*/

{0x00,0x04,0x38,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},/*"\",1*/

{0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},/*"]",2*/

{0x00,0x00,0x04,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"^",3*/

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},/*"_",4*/

{0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",5*/

{0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x19,0x24,0x24,0x12,0x3F,0x20,0x00},/*"a",0*/

{0x10,0xF0,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},/*"b",1*/

{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},/*"c",2*/

{0x00,0x00,0x80,0x80,0x80,0x90,0xF0,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},/*"d",3*/

{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x24,0x24,0x24,0x24,0x17,0x00},/*"e",4*/

{0x00,0x80,0x80,0xE0,0x90,0x90,0x20,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"f",5*/

{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},/*"g",6*/

{0x10,0xF0,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"h",7*/

{0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"i",8*/

{0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},/*"j",9*/

{0x10,0xF0,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x06,0x29,0x30,0x20,0x00},/*"k",10*/

{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"l",11*/

{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},/*"m",12*/

{0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"n",13*/

{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"o",14*/

{0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0x91,0x20,0x20,0x11,0x0E,0x00},/*"p",15*/

{0x00,0x00,0x00,0x80,0x80,0x00,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0x91,0xFF,0x80},/*"q",16*/

{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},/*"r",17*/

{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},/*"s",18*/

{0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x10,0x00},/*"t",19*/

{0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},/*"u",20*/

{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x03,0x0C,0x30,0x0C,0x03,0x00,0x00},/*"v",21*/

{0x80,0x80,0x00,0x80,0x80,0x00,0x80,0x80,0x01,0x0E,0x30,0x0C,0x07,0x38,0x06,0x01},/*"w",22*/

{0x00,0x80,0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x20,0x31,0x0E,0x2E,0x31,0x20,0x00},/*"x",23*/

{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x81,0x86,0x78,0x18,0x06,0x01,0x00},/*"y",24*/

{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},/*"z",25*/
};

static unsigned char number1[][16] = {
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",0*/

{0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"1",1*/

{0x00,0x70,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",2*/

{0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x20,0x21,0x21,0x22,0x1C,0x00},/*"3",3*/

{0x00,0x00,0x80,0x40,0x30,0xF8,0x00,0x00,0x00,0x06,0x05,0x24,0x24,0x3F,0x24,0x24},/*"4",4*/

{0x00,0xF8,0x88,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x20,0x20,0x20,0x11,0x0E,0x00},/*"5",5*/

{0x00,0xE0,0x10,0x88,0x88,0x90,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x20,0x1F,0x00},/*"6",6*/

{0x00,0x18,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x00},/*"7",7*/

{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",8*/

{0x00,0xF0,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x01,0x12,0x22,0x22,0x11,0x0F,0x00},/*"9",9*/
};

然后根据这个字摸,我们可以显示字符了,这里写一个显示字符的函数:

void show_char(u8 row, u8 col, u8 charr){
    u8 i;     // 遍历变量
    set_currsor(row, col + 8);    // 设置显示位置
    for (i = 0; i < 8; i++){
        IIC_Write(ASCII1[charr - 32][i]);
    }
    set_currsor(row + 1, col + 8);  // 设置下一行显示的位置
    for (i = 0; i < 8; i++){
        IIC_Write(ASCII1[charr - 32][i + 8]);
    }
}

字摸的长度是16位,而我们显示的这个ASCII是8*8大小的,我们在第一次设置光标的时候要加上8是为了错过前面的一个字符,然后在显示字符的时候选择的是逐列式。

也就是先将第一个字节的字摸先显示,然后转到下一列再显示第二个字摸,依次下去,这样如果只有一半,也就是只有4*8,还需要转到下一行补上那4行才可以变成8*8。

在写这个函数的时候,我发现了一个好玩的,我这如果让列不跳过一个字符,而是跳过一位,那这个字符就会扭动,虽然没有什么用,但是感觉很有趣,代码如下,大家可以来试试:

// 扭动字符显示
// 只能显示一个,显示多个就不流畅了
void show_Twistchar(u8 row, u8 col, u8 word){
    u8 i;
    set_currsor(row, col * 8);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i]);
    }
    set_currsor(row + 1, col * 8);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i + 8]);
    }
    // 下面这些可以删掉,然后在主函数调用的时候调用两遍,要一样的字符
    delay_us(5); 
    set_currsor(row, col * 8 + 1);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i]);
    }
    set_currsor(row + 1, col * 8 + 1);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i + 8]);
    }
}

7.显示字符串

这个就得看大家对于串操作的熟练程度了,就是用到对字符串的操作,但是呢,这个需要考虑到换行,代码如下:

void show_string(u8 row, u8 col, u8* str){
    u8 i, j = 0;
    for (i = 0; str[i] != '\0'; i++, j++){
        if (j >= 16){
            row += 1;
            col = 0;
            j = 0;
        }
        show_char(row, col + j, str[i]);
    }
}

这里我用一个变量作为判断条件,因为一行有128个像素点,我们显示8*8的字符,那一行只能显示16个8*8的字符,所以当j变量到了16个字符,我们就让行换一行,列清零,这样就可以实现换行操作了。

8.清屏

这个操作很简单,就是将每一行和每一列的所有像素点全部变为0x00就可以了,代码如下:

void clear(){
    u8 i, j;
    for (i = 0; i < 8; i++){
        set_currsor(i, 0);
        for (j = 0; j < 128; j++){
            write_data(0x00);
        }
    }
}

9.显示汉字

这个也是需要使用取摸得到,然后每个汉字是16*16的点阵,我们只需要显示即可:

// 现实汉字
void show_hz(u8 row, u8 col, u8 len){
    u8 i, j, x, y;
    y = row;
    x = col;
    for (j = 0; j < len * 2; j += 2, y+= 2){
        if ((x + y) * 8 >= 128){
            y = 0;
            row = 2;
        }
        set_currsor(row, (x + y) * 8);
        for (i = 0; i < 16; i++){
            write_data(hz[j][i]);
        }
        set_currsor(row + 1, (x + y) * 8);
        for (i = 0; i < 16; i++){
            write_data(hz[j + 1][i]);
        }
    }
}

这个也是可以自动换行的,但在使用前需要将字摸放入到hz这个数组中,这个数组的格式如下:

static unsigned char hz[][16] = {
	{0x20,0x24,0x24,0x24,0xFE,0x23,0x22,0x20,0x20,0xFF,0x20,0x22,0x2C,0xA0,0x20,0x00},
	{0x00,0x08,0x48,0x84,0x7F,0x02,0x41,0x40,0x20,0x13,0x0C,0x14,0x22,0x41,0xF8,0x00},/*"我",0*/

	{0x00,0x80,0x60,0xF8,0x07,0x00,0xF8,0x01,0x06,0x00,0x04,0x04,0x04,0xFC,0x00,0x00},
	{0x01,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x40,0x80,0x7F,0x00,0x00},/*"们",1*/

	{0x20,0x24,0x24,0xA4,0x7F,0x24,0x34,0x28,0x26,0x20,0xFE,0x02,0x22,0xDA,0x06,0x00},
	{0x04,0x02,0xFF,0x49,0x49,0x49,0x49,0xFF,0x00,0x00,0xFF,0x08,0x10,0x08,0x07,0x00},/*"都",2*/

	{0x04,0x04,0x04,0x84,0xE4,0x3C,0x27,0x24,0x24,0x24,0x24,0xE4,0x04,0x04,0x04,0x00},
	{0x04,0x02,0x01,0x00,0xFF,0x09,0x09,0x09,0x09,0x49,0x89,0x7F,0x00,0x00,0x00,0x00},/*"有",3*/

	{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"一",4*/

	{0x80,0x80,0x40,0x20,0x10,0x08,0x04,0xC3,0x04,0x08,0x10,0x20,0x40,0x80,0x80,0x00},
	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"个",5*/

	{0x40,0x44,0x24,0x14,0x7F,0x94,0x24,0x00,0x24,0x14,0x7F,0x14,0x24,0x44,0x40,0x00},
	{0x00,0x80,0x88,0x84,0x42,0x45,0x29,0x11,0x11,0x09,0x05,0x03,0x00,0x00,0x00,0x00},/*"梦",6*/

	{0x00,0x08,0x88,0x68,0xFF,0x48,0x88,0x00,0xFE,0x52,0x52,0x52,0xFE,0x00,0x00,0x00},
	{0x40,0x31,0x00,0x00,0x3B,0x40,0x40,0x44,0x5B,0x42,0x42,0x72,0x03,0x08,0x30,0x00},/*"想",7*/

	{0x00,0x08,0x88,0x68,0xFF,0x48,0x88,0x00,0xFE,0x52,0x52,0x52,0xFE,0x00,0x00,0x00},
	{0x40,0x31,0x00,0x00,0x3B,0x40,0x40,0x44,0x5B,0x42,0x42,0x72,0x03,0x08,0x30,0x00},/*"想",7*/

};

这里面有一些测试的汉字,大家可以测试一下。

10.显示图片

显示图片的代码我没有写,其实和显示字符和汉字一样,然后用取摸工具取出,然后看一下行有多少,列有多少,然后显示即可,这里我后面写完后会放入gitee上的,github的话因为我最近资金不足,所以没工具,懂得都懂。

11.显示动图

这个其实和GIF的原理一样,图片上覆盖图片,这样就会让图片动起来。

三、完整代码

我这个项目是分文件编写的,分为oled.c函数原型,oled.h函数声明和头文件引入,oledfont.h这个是字符库,sys.c是延时函数和系统操作,gpio.h是gpio口的初始化函数的声明

// gpio.c
#include "gpio.h"

void MX_GPIOInit(){
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStruct.GPIO_Pin = CLK;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIO, &GPIO_InitStruct);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStruct.GPIO_Pin = SDA;
    GPIO_Init(GPIO, &GPIO_InitStruct);
}
// gpio.h
#ifndef __GPIO_H
#define __GPIO_H

#include "stm32f10x.h"

#define CLK GPIO_Pin_8
#define SDA GPIO_Pin_9
#define GPIO GPIOB

void MX_GPIOInit();

#endif

// oled.c
#include "oled.h"

// I2C协议开始
void IIC_Start(){
    GPIO_SetBits(GPIO, SDA);
    GPIO_SetBits(GPIO, CLK);
    GPIO_ResetBits(GPIO, SDA);
    GPIO_ResetBits(GPIO, CLK);
}

// IIC协议结束
void IIC_Stop(){
    GPIO_ResetBits(GPIO, SDA);
    GPIO_SetBits(GPIO, CLK);
    GPIO_SetBits(GPIO, SDA);
}

// 通过IIC写入数据
void IIC_Write(u8 data){
    u8 i;
    for (i = 0; i < 8; i++){
        
        if (data & 0x80){
            GPIO_SetBits(GPIO, SDA);
        }
        else{
            GPIO_ResetBits(GPIO, SDA);
        }
        GPIO_SetBits(GPIO, CLK);
        GPIO_ResetBits(GPIOB, CLK);
        data <<= 1;
        // GPIO_ResetBits(GPIO, CLK);
        // GPIO_SetBits(GPIO, CLK);
    }
    GPIO_SetBits(GPIO, CLK);
    GPIO_ResetBits(GPIO, CLK);
}

// 写入命令
void write_command(u8 cmd){
    IIC_Start();
    IIC_Write(ADDR);
    IIC_Write(0x00);
    IIC_Write(cmd);
    IIC_Stop();
}

// 写入数据
void write_data(u8 data){
    IIC_Start();
    IIC_Write(ADDR);
    IIC_Write(0x40);
    IIC_Write(data);
    IIC_Stop();
}

// 设置光标
void set_currsor(u8 row, u8 col){
    write_command(0xB0 |row);
    write_command(0x10 | ((col & 0xF0) >> 4));
    write_command(0x00 | (col & 0x0F));
}

// 扭动字符显示
// 只能显示一个,显示多个就不流畅了
void show_Twistchar(u8 row, u8 col, u8 word){
    u8 i;
    set_currsor(row, col * 8);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i]);
    }
    set_currsor(row + 1, col * 8);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i + 8]);
    }
    delay_us(5);
    set_currsor(row, col * 8 + 1);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i]);
    }
    set_currsor(row + 1, col * 8 + 1);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i + 8]);
    }
}

// 显示一个字符
void show_char(u8 row, u8 col, u8 word){
    u8 i;
    set_currsor(row * 2, col * 8);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i]);
    }
    set_currsor(row * 2 + 1, col * 8);
    for (i = 0; i < 8; i++){
        write_data(ASCII1[word - 32][i + 8]);
    }
}

// 显示字符串
void show_string(u8 row, u8 col, u8* str){
    u8 i, j = 0;
    for (i = 0; str[i] != '\0'; i++, j++){
        if (j >= 16){
            row += 1;
            col = 0;
            j = 0;
        }
        show_char(row, col + j, str[i]);
    }
}

// 现实汉字
void show_hz(u8 row, u8 col, u8 len){
    u8 i, j, x, y;
    y = row;
    x = col;
    for (j = 0; j < len * 2; j += 2, y+= 2){
        // x = (x + j) * 8;
        if ((x + y) * 8 >= 128){
            y = 0;
            row = 2;
        }
        set_currsor(row, (x + y) * 8);
        for (i = 0; i < 16; i++){
            write_data(hz[j][i]);
        }
        set_currsor(row + 1, (x + y) * 8);
        for (i = 0; i < 16; i++){
            write_data(hz[j + 1][i]);
        }
    }
}

// 清屏
void clear(){
    u8 i, j;
    for (i = 0; i < 8; i++){
        set_currsor(i, 0);
        for (j = 0; j < 128; j++){
            write_data(0x00);
        }
    }
}

// 初始化函数
void OLED_Init(){
    u32 i, j;
    for (i = 0; i < 1000; i++){
        for (j = 0; j < 1000; j++);
    }
    write_command(CLOSESHOW);
    write_command(SETCLS);
    write_command(0x80);
    write_command(RATE);
    write_command(0x3F);
    write_command(OFFSET);
    write_command(0x00);
    write_command(START);
    write_command(LEFTRIG);
    write_command(UPDOWN);
    write_command(HARD);
    write_command(0x12);
    write_command(RATION);
    write_command(0xCF);
    write_command(CYCLE);
    write_command(0xF1);
    write_command(VCOM);
    write_command(0x30);
    write_command(SHOWUP);
    write_command(0xA6);
    write_command(BEN);
    write_command(0x14);
    write_command(SHOWOK);
    clear();
}
// oled.h
#ifndef __OLED_H
#define __OLED_H

#include "stm32f10x.h"
#include "gpio.h"
#include "oledfont.h"
#include "sys.h"

#define ADDR 0x78

#define CMD 0x00
#define DATA 0x40
#define CLOSESHOW 0xAE
#define SETCLS 0xD5
#define RATE    0xA8
#define OFFSET  0xD3
#define START   0x40
#define LEFTRIG 0xA1
#define UPDOWN  0xC8
#define HARD    0xDA
#define RATION  0x81
#define  CYCLE  0xD9
#define VCOM    0xDB
#define SHOWUP  0xA4
#define BEN     0x8D
#define SHOWOK  0xAF

void OLED_Init();
void clear();
void show_char(u8 row, u8 col, u8 word);
void set_currsor(u8 row, u8 col);
void show_Twistchar(u8 row, u8 col, u8 word);
void show_string(u8 row, u8 col, u8* str);
void show_hz(u8 row, u8 col, u8 len);

#endif

// sys.c
#include "sys.h"

void delay_us(u32 time){
    u32 temp;
    SysTick -> LOAD = 9 * time;
    SysTick -> CTRL = 0x01;
    SysTick -> VAL = 0;
    do{
        temp = SysTick -> CTRL;
    }while((temp & 0x01) && (!(temp & (1 << 16))));
    SysTick -> CTRL = 0;
    SysTick -> VAL = 0;
}
// sys.h
#ifndef __SYS_H
#define __SYS_H

#include "stm32f10x.h"

void delay_us(u32 time);

#endif

总结

学会了这个显示屏如何显示后后面我们可以对传感器进行操作了,到时候传感器接收到的数据就可以直接显示在OLED上,这样就能很好的观察其中的变化了。

下一章准备介绍一下显示屏的一些操作,比如显示进度条,显示动图或者其他内容。

标签:03,0x08,显示屏,0x20,0x00,write,OLED,0x80,GPIO
From: https://www.cnblogs.com/Lavender-edgar/p/18158900

相关文章

  • 20230330 专项训练 4
    Tajan/序列问题专项save原题链接煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援......
  • 03_make menuconfig图形化配置
    makemenuconfig图形化配置1.怎么进入到makemenuconfig图形化界面?首先进入到内核源码的路径下,然后输入makemenuconfig即可打开这个界面。2.makemenuconfig图形化界面的操作。1.搜索功能输入“”/“”即可弹出搜索界面,然后输入我们想要搜索的内容即可。2.配置驱动的状态。......
  • docker.from_env() 获取docker守护进程时出现 TypeError: load_config() got an unexp
    某天使用python重启docker容器时,出现了一个令人费解的BUG,我的代码为1defrestart_docker(container_name):2#连接到docker守护进程3client=docker.from_env()4try:5#获取容器对象6container=client.containers.get(containe......
  • The Emperor's New Clothes
    TheStoryIChoose:TheEmperor'sNewClothesThepointofviewofthestory:ThethirdpersonpointofviewThepointwillIusedinthestory.:Firstpersonpointofview**Thescript:Iamtheemperorofthiscountry.Iparticularlyenjoywearingb......
  • 03、应急响应流程
    应急响应流程1.应急响应准备1.1.获取当前网络安全事件信息事件发生前,做好日常运维检测,收集各类故障信息区分系统自身故障和人为破坏区分一般事件和应急响应事件充分获取当前事件信息,从而启动相应的预案事件上报,确认应急事件类型和应急事件的等级通知相关人员,启动应急预......
  • centos8.2报错Failed to download metadata for repo 'BaseOS': Cannot prepare inter
    报错CentOS-8-Base68B/s|38B00:00错误:Failedtodownloadmetadataforrepo'BaseOS':......
  • Python3.8.4 解决 ImportError: urllib3 v2 only supports OpenSSL 1.1.1+, currently
    系统版本:CentOSLinuxrelease7.6.1810(Core)编译安装Python3.8.4[root@hankyoon~]#tar-xvfPython-3.8.4.tgz[root@hankyoon~]#cdPython-3.8.4/[root@hankyoon~]#./configure--prefix=/usr/local/python3.8[root@hankyoon~]#make&&makeinstall[......
  • 超低功耗三通道低频无线唤醒 ASK 接收芯片DP20RF003
    DP20RF003是一款三通道、超低功耗的ASK接收芯片,可检测30~300KHz范围的LF(低频)载波频率数据并触发唤醒信号,唤醒之后MCU可通过IO实时采集后续接收到的数据,也可以通过SPI或I2C直接从寄存器读取(最多保存8字节数据)。三个独立通道可以配置成不同的唤醒模式,每个通道都具......
  • Git runner 返回报错: status=couldn't execute POST against dial tcp: lookup gitlab
    当发现Gitlab上的runner显示出runneroffline的问题时1查一下gitrunner的报错runner=xxxxstatus=couldn'texecutePOSTagainsthttps://gitlab/api/v4/jobs/request:Posthttps://gitlab/api/v4/jobs/request:dialtcp:lookupgitonx.x.x.x:53:servermisbehaving......
  • 实时动态规则(55)规则发布平台后端开发(5) 规则模型开发(4)rulemodel_03_涉及事件时间
    0涉及架构 注意:以下代码,都是根据一个特定规则模型: rulemodel_03_caculator 来进行开发的不同的规则模型,如下功能代码需要进行不同的开发RuleModel_03 这个规则模型的特点是:拥有事件间隔时间1规则参数结构规范{"ruleModelId":"3","ruleId":"m3-r01",......