---
video: //player.bilibili.com/player.html?isOutside=true&aid=113305532374473&bvid=BV1zzmjYMEwz&cid=26288459353&p=1&high_quality=1&autoplay=0
---

# 128*64 OLED 屏幕（下）

当我们需要显示多个中文字符。并且提供中文字库。它可以显示任意字符而不是单一的字符。我们已经大概理解了一个字符的相关渲染方式，多个字符，我们需要将字体库和字体做出映射。然后将其写入到单片机中。

![](https://cdn.zhiyanbang.com/md18/WeChat578fc7f436d8d52a9d524d933a78a693.jpg)


## 中文字体渲染固件

因为 espruino 没有提供中文相关的渲染功能，于是老许给大家做了一个固件程序，将中文的API 调用。

- 固件下载

下载地址：<a download href="https://cdn.zhiyanbang.com/md18/espruino_2v24.1_esp32.zip" target="_blank">espruino_2v24.1_esp32.zip</a>

> [!NOTE]
> 中文固件如何编译？这里查看：<a  target="_blank" href="https://www.zhiyanbang.com/website/md/37?content_id=V0uBxu2jZtHWuINO">编译多行显示中文固件</a>

接下来老许介绍一下老许封装API

### chineseSimpleGet

```javascript

LAOXU.chineseSimpleGet(string)
```
- `string` UTF-8编码的Unicode字符序列  

可以在这里得到：  

![](https://cdn.zhiyanbang.com/md18/WeChat459fd6494a2705bf2912196cae9ac557.jpg)

这此方法用来渲染一段简单的中文，不会换行，也不会多行展示。
使用方法如下：

```javascript
  g.drawImage(LAOXU.chineseSimpleGet('\xe8\x80\x81\xe8\xae\xb8'), 0, 0);
```

### chineseGet

```javascript

LAOXU.chineseGet(string, width)

```
- string UTF-8编码的Unicode字符序列
- width 传入显示的宽度，在指定的宽度中渲染字符。

此方法支持多行字体的渲染。可以根据宽度，渲染多行的中文，当字符过多的时候，在指定宽度中，可计动态计算宽度换行。  

使用方法如下：

```javascript
LAOXU.chineseGet("\xe6\x99\xba\xe7\xa0\x94\xe5\xb8\xae\x2d\xe8\x80\x81\xe8\xae\xb8\xef\xbc\x8c\xe8\xb0\xa2\xe8\xb0\xa2\xe5\xa4\xa7\xe5\xae\xb6", 128)
.map(item => {
   g.drawImage(item.fontData, 0, item.y);
});
```
在128x64 的屏幕中渲染效果：  

![](https://cdn.zhiyanbang.com/md18/WeChat1e3236baeb7468aaf6995d21c8f6a64d.jpg)


## 准备中文字体库。

老许已经制作成功了一个中文的字体库，大家直接进行下载就可以了

下载地址：<a download href="https://cdn.zhiyanbang.com/md18/font_data.bin" target="_blank">font_data.bin</a>

将此文件上传至等下上传至Strorage 即可

![](https://cdn.zhiyanbang.com/md15/WeChat5b9301b30a49040779bddf2fc316ab50.jpg)



## 硬件连接


| 器件 | 数量 |
|--------|--------|
| ESP32 | 1 |
| 128x64 OLED | 1 |
| 杜邦线 | 1 |

![](https://cdn.zhiyanbang.com/md17/WeChat999865720e5241ee72af9c737dacc392.jpg)


## 软件代码

- 第一步软件代码库

```javascript
var C = {
    OLED_WIDTH: 128,
    OLED_CHAR: 0x40,
    OLED_CHUNK: 128
};
 
// commands sent when initialising the display
var extVcc = false; // if true, don't start charge pump 
var initCmds = new Uint8Array([
    0xAe, // 0 disp off
    0xD5, // 1 clk div
    0x80, // 2 suggested ratio
    0xA8, 63, // 3 set multiplex, height-1
    0xD3, 0x0, // 5 display offset
    0x40, // 7 start line
    0x8D, extVcc ? 0x10 : 0x14, // 8 charge pump
    0x20, 0x0, // 10 memory mode
    0xA1, // 12 seg remap 1
    0xC8, // 13 comscandec
    0xDA, 0x12, // 14 set compins, height==64 ? 0x12:0x02,
    0x81, extVcc ? 0x9F : 0xCF, // 16 set contrast
    0xD9, extVcc ? 0x22 : 0xF1, // 18 set precharge
    0xDb, 0x40, // 20 set vcom detect
    0xA4, // 22 display all on
    0xA6, // 23 display normal (non-inverted)
    0xAf // 24 disp on
]);
// commands sent when sending data to the display
var flipCmds = [
    0x21, // columns
    0, C.OLED_WIDTH - 1,
    0x22, // pages
    0, 7 /* (height>>3)-1 */
];
 
function update(options) {
    if (options) {
        if (options.height) {
            initCmds[4] = options.height - 1;
            initCmds[15] = options.height == 64 || options.height == 48 ? 0x12 : 0x02;
            flipCmds[5] = (options.height >> 3) - 1;
        }
        if (options.width) {
            C.OLED_WIDTH = options.width;
            flipCmds[1] = (128 - options.width) / 2; // 0x20 for 64, 0 for 128;
            flipCmds[2] = flipCmds[1] + options.width - 1; // 0x5f;
        }
        if (options.contrast !== undefined) initCmds[17] = options.contrast;
    }
}
 
function main(i2c, callback, options) {
    update(options);
    var oled = Graphics.createArrayBuffer(C.OLED_WIDTH, initCmds[4] + 1, 1, {
        vertical_byte: true
    });
 
    var addr = 0x3C;
    if (options) {
        if (options.address) addr = options.address;
        // reset display if 'rst' is part of options 
        if (options.rst) digitalPulse(options.rst, 0, 10);
    }
 
    setTimeout(function() {
        // configure the OLED
        initCmds.forEach(function(d) {
            i2c.writeTo(addr, [0, d]);
        });
    }, 50);
 
    // if there is a callback, call it now(ish)
    if (callback !== undefined) setTimeout(callback, 100);
 
    // write to the screen
    oled.flip = function() {
        // set how the data is to be sent (whole screen)
        flipCmds.forEach(function(d) {
            i2c.writeTo(addr, [0, d]);
        });
        var chunk = new Uint8Array(C.OLED_CHUNK + 1);
 
        chunk[0] = C.OLED_CHAR;
        for (var p = 0; p < this.buffer.length; p += C.OLED_CHUNK) {
            chunk.set(new Uint8Array(this.buffer, p, C.OLED_CHUNK), 1);
            i2c.writeTo(addr, chunk);
        }
    };
 
    // set contrast, 0..255
    oled.setContrast = function(c) {
        i2c.writeTo(addr, 0, 0x81, c);
    };
 
    // set off
    oled.off = function() {
        i2c.writeTo(addr, 0, 0xAE);
    };
 
    // set on
    oled.on = function() {
        i2c.writeTo(addr, 0, 0xAF);
    };
 
    // return graphics
    return oled;
}
```
- 第二步，准备数据， 在 https://cdn.zhiyanbang.com/font_html/getZM.html 此页面中，将需要的中文进行转换：
![](https://cdn.zhiyanbang.com/md18/WeChat269a02c6472763ac0a827ef5daf3d6e8.jpg)

```
"\xe6\x99\xba\xe7\xa0\x94\xe5\xb8\xae\x2d\xe8\x80\x81\xe8\xae\xb8\xef\xbc\x8c\xe8\xb0\xa2\xe8\xb0\xa2\xe5\xa4\xa7\xe5\xae\xb6"
```

- 第三步渲染

```javascript
function draw() {
  g.clear();

LAOXU.chineseGet("\xe6\x99\xba\xe7\xa0\x94\xe5\xb8\xae\x2d\xe8\x80\x81\xe8\xae\xb8\xef\xbc\x8c\xe8\xb0\xa2\xe8\xb0\xa2\xe5\xa4\xa7\xe5\xae\xb6", 128).map(item => {
       g.drawImage(item.fontData, 0, item.y);
   });
  g.flip();
}

var g = main(I2C1, draw, { address: 0x3C, height:64 });
```
