屏幕参数
1.I2C接口(GND,VCC,SCL ,SDA(双向数据线))
2.分辨率128*64
3.超逛可视角度 160
4.超低功耗正常显示0.04w
5.宽供电范围3.3-5V
6、工业级温度:-30---70C
7、体积28.65mm---27.8mm
8.亮度、对比度可以通过程序指令控制
9.使用寿命不少于16000小时
10、OLED内部驱动SSD1306
连接方式如下图
代码
main.cpp
/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-01-28 Damon first version */ #include <rtthread.h> #include <rtdevice.h> #include "pico/stdlib.h" #include "include/ss_oled.hpp" // RPI Pico #define SDA_PIN 4 #define SCL_PIN 5 #define PICO_I2C i2c0 #define I2C_SPEED 100 * 1000 #define OLED_WIDTH 128 #define OLED_HEIGHT 64 #define LED_PIN 25 int main(void) { rt_kprintf("Hello, RT-Thread!\n"); rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT); static uint8_t ucBuffer[1024]; uint8_t uc[8]; int i, j, rc; char szTemp[32]; picoSSOLED myOled(OLED_128x64, 0x3c, 0, 0, PICO_I2C, SDA_PIN, SCL_PIN, I2C_SPEED); rc = myOled.init() ; myOled.set_back_buffer(ucBuffer); while (1) { if (rc != OLED_NOT_FOUND) { rt_pin_write(LED_PIN, 1); rt_thread_mdelay(1000); myOled.fill(0,1); myOled.set_contrast(255); myOled.write_string(0,0,0,(char *)"**************** ", FONT_8x8, 0, 1); myOled.write_string(0,4,1,(char *)"RT-Pico SS_OLED", FONT_6x8, 0, 1); myOled.write_string(0,8,2,(char *)"running on the", FONT_8x8, 0, 1); myOled.write_string(0,8,3,(char *)"SSD1306 128x64", FONT_8x8, 0, 1); myOled.write_string(0,4,4,(char *)"monochrome OLED", FONT_8x8, 0, 1); myOled.write_string(0,0,5,(char *)"Written by Damon", FONT_8x8, 0, 1); //myOled.write_string(0,4,6,(char *), FONT_6x8, 0, 1); myOled.write_string(0,0,7,(char *)"**************** ", FONT_8x8, 0, 1); sleep_ms(10000); } myOled.fill(0,1); myOled.write_string(0,0,0,(char *)"Now with 5 font sizes", FONT_6x8, 0, 1); myOled.write_string(0,0,1,(char *)"6x8 8x8 16x16", FONT_8x8, 0, 1); myOled.write_string(0,0,2,(char *)"16x32 and a new", FONT_8x8, 0, 1); myOled.write_string(0,0,3,(char *)"Stretched", FONT_12x16, 0, 1); myOled.write_string(0,0,5,(char *)"from 6x8", FONT_12x16, 0, 1); sleep_ms(10000); int x, y; myOled.fill(0, 1); myOled.write_string(0,0,0,(char *)"Backbuffer Test", FONT_NORMAL,0,1); myOled.write_string(0,0,1,(char *)"96 lines", FONT_NORMAL,0,1); sleep_ms(10000); for (x=0; x<OLED_WIDTH-1; x+=2) { myOled.draw_line(x, 0, OLED_WIDTH-x, OLED_HEIGHT-1, 1); }; for (y=0; y<OLED_HEIGHT-1; y+=2) { myOled.draw_line(OLED_WIDTH-1,y, 0,OLED_HEIGHT-1-y, 1); }; sleep_ms(10000); myOled.write_string(0,0,1,(char *)"Without backbuffer", FONT_SMALL,0,1); sleep_ms(10000); myOled.fill(0,1); for (x=0; x<OLED_WIDTH-1; x+=2) { myOled.draw_line(x, 0, OLED_WIDTH-1-x, OLED_HEIGHT-1, 0); } for (y=0; y<OLED_HEIGHT-1; y+=2) { myOled.draw_line(OLED_WIDTH-1,y, 0,OLED_HEIGHT-1-y, 0); } myOled.dump_buffer(ucBuffer); myOled.write_string(0,0,1,(char *)"With backbuffer", FONT_SMALL,0,1); sleep_ms(10000); rt_pin_write(LED_PIN, 0); rt_thread_mdelay(1000); } return 0; }
ss_oled.cpp
// // ss_oled (Small, Simple OLED library) // Copyright (c) 2017-2019 BitBank Software, Inc. // Written by Larry Bank (bitbank@pobox.com) // Project started 1/15/2017 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // #include <stdio.h> #include <string.h> #include <stdlib.h> #include "pico/stdlib.h" #include "include/ss_oled.h" const uint8_t ucFont[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x5f, 0x5f, 0x06, 0x00, 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x14, 0x7f, 0x7f, 0x14, 0x7f, 0x7f, 0x14, 0x24, 0x2e, 0x2a, 0x6b, 0x6b, 0x3a, 0x12, 0x46, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x62, 0x30, 0x7a, 0x4f, 0x5d, 0x37, 0x7a, 0x48, 0x00, 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x3e, 0x63, 0x41, 0x00, 0x00, 0x00, 0x41, 0x63, 0x3e, 0x1c, 0x00, 0x00, 0x08, 0x2a, 0x3e, 0x1c, 0x3e, 0x2a, 0x08, 0x00, 0x08, 0x08, 0x3e, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x80, 0xe0, 0x60, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x3e, 0x7f, 0x59, 0x4d, 0x47, 0x7f, 0x3e, 0x40, 0x42, 0x7f, 0x7f, 0x40, 0x40, 0x00, 0x62, 0x73, 0x59, 0x49, 0x6f, 0x66, 0x00, 0x22, 0x63, 0x49, 0x49, 0x7f, 0x36, 0x00, 0x18, 0x1c, 0x16, 0x53, 0x7f, 0x7f, 0x50, 0x27, 0x67, 0x45, 0x45, 0x7d, 0x39, 0x00, 0x3c, 0x7e, 0x4b, 0x49, 0x79, 0x30, 0x00, 0x03, 0x03, 0x71, 0x79, 0x0f, 0x07, 0x00, 0x36, 0x7f, 0x49, 0x49, 0x7f, 0x36, 0x00, 0x06, 0x4f, 0x49, 0x69, 0x3f, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe6, 0x66, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x63, 0x36, 0x1c, 0x08, 0x00, 0x00, 0x02, 0x03, 0x59, 0x5d, 0x07, 0x02, 0x3e, 0x7f, 0x41, 0x5d, 0x5d, 0x5f, 0x0e, 0x7c, 0x7e, 0x13, 0x13, 0x7e, 0x7c, 0x00, 0x41, 0x7f, 0x7f, 0x49, 0x49, 0x7f, 0x36, 0x1c, 0x3e, 0x63, 0x41, 0x41, 0x63, 0x22, 0x41, 0x7f, 0x7f, 0x41, 0x63, 0x3e, 0x1c, 0x41, 0x7f, 0x7f, 0x49, 0x5d, 0x41, 0x63, 0x41, 0x7f, 0x7f, 0x49, 0x1d, 0x01, 0x03, 0x1c, 0x3e, 0x63, 0x41, 0x51, 0x33, 0x72, 0x7f, 0x7f, 0x08, 0x08, 0x7f, 0x7f, 0x00, 0x00, 0x41, 0x7f, 0x7f, 0x41, 0x00, 0x00, 0x30, 0x70, 0x40, 0x41, 0x7f, 0x3f, 0x01, 0x41, 0x7f, 0x7f, 0x08, 0x1c, 0x77, 0x63, 0x41, 0x7f, 0x7f, 0x41, 0x40, 0x60, 0x70, 0x7f, 0x7f, 0x0e, 0x1c, 0x0e, 0x7f, 0x7f, 0x7f, 0x7f, 0x06, 0x0c, 0x18, 0x7f, 0x7f, 0x1c, 0x3e, 0x63, 0x41, 0x63, 0x3e, 0x1c, 0x41, 0x7f, 0x7f, 0x49, 0x09, 0x0f, 0x06, 0x1e, 0x3f, 0x21, 0x31, 0x61, 0x7f, 0x5e, 0x41, 0x7f, 0x7f, 0x09, 0x19, 0x7f, 0x66, 0x26, 0x6f, 0x4d, 0x49, 0x59, 0x73, 0x32, 0x03, 0x41, 0x7f, 0x7f, 0x41, 0x03, 0x00, 0x7f, 0x7f, 0x40, 0x40, 0x7f, 0x7f, 0x00, 0x1f, 0x3f, 0x60, 0x60, 0x3f, 0x1f, 0x00, 0x3f, 0x7f, 0x60, 0x30, 0x60, 0x7f, 0x3f, 0x63, 0x77, 0x1c, 0x08, 0x1c, 0x77, 0x63, 0x07, 0x4f, 0x78, 0x78, 0x4f, 0x07, 0x00, 0x47, 0x63, 0x71, 0x59, 0x4d, 0x67, 0x73, 0x00, 0x7f, 0x7f, 0x41, 0x41, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x41, 0x41, 0x7f, 0x7f, 0x00, 0x00, 0x08, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x20, 0x74, 0x54, 0x54, 0x3c, 0x78, 0x40, 0x41, 0x7f, 0x3f, 0x48, 0x48, 0x78, 0x30, 0x38, 0x7c, 0x44, 0x44, 0x6c, 0x28, 0x00, 0x30, 0x78, 0x48, 0x49, 0x3f, 0x7f, 0x40, 0x38, 0x7c, 0x54, 0x54, 0x5c, 0x18, 0x00, 0x48, 0x7e, 0x7f, 0x49, 0x03, 0x06, 0x00, 0x98, 0xbc, 0xa4, 0xa4, 0xf8, 0x7c, 0x04, 0x41, 0x7f, 0x7f, 0x08, 0x04, 0x7c, 0x78, 0x00, 0x44, 0x7d, 0x7d, 0x40, 0x00, 0x00, 0x60, 0xe0, 0x80, 0x84, 0xfd, 0x7d, 0x00, 0x41, 0x7f, 0x7f, 0x10, 0x38, 0x6c, 0x44, 0x00, 0x41, 0x7f, 0x7f, 0x40, 0x00, 0x00, 0x7c, 0x7c, 0x18, 0x78, 0x1c, 0x7c, 0x78, 0x7c, 0x78, 0x04, 0x04, 0x7c, 0x78, 0x00, 0x38, 0x7c, 0x44, 0x44, 0x7c, 0x38, 0x00, 0x84, 0xfc, 0xf8, 0xa4, 0x24, 0x3c, 0x18, 0x18, 0x3c, 0x24, 0xa4, 0xf8, 0xfc, 0x84, 0x44, 0x7c, 0x78, 0x4c, 0x04, 0x0c, 0x18, 0x48, 0x5c, 0x54, 0x74, 0x64, 0x24, 0x00, 0x04, 0x04, 0x3e, 0x7f, 0x44, 0x24, 0x00, 0x3c, 0x7c, 0x40, 0x40, 0x3c, 0x7c, 0x40, 0x1c, 0x3c, 0x60, 0x60, 0x3c, 0x1c, 0x00, 0x3c, 0x7c, 0x60, 0x30, 0x60, 0x7c, 0x3c, 0x44, 0x6c, 0x38, 0x10, 0x38, 0x6c, 0x44, 0x9c, 0xbc, 0xa0, 0xa0, 0xfc, 0x7c, 0x00, 0x4c, 0x64, 0x74, 0x5c, 0x4c, 0x64, 0x00, 0x08, 0x08, 0x3e, 0x77, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x41, 0x41, 0x77, 0x3e, 0x08, 0x08, 0x00, 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x70, 0x78, 0x4c, 0x46, 0x4c, 0x78, 0x70 }; const uint8_t ucBigFont[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0xfc, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0xfc, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0xf0, 0xf0, 0xc3, 0xc3, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x3f, 0x3f, 0x3f, 0x3f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0xff, 0xff, 0xc3, 0xc3, 0xff, 0xff, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x3f, 0x3f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0xcc, 0xcc, 0xff, 0xff, 0x3f, 0x3f, 0x3f, 0x3f, 0xff, 0xff, 0xcc, 0xcc, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x3f, 0x3f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x0f, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x3c, 0x3c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0x3c, 0x3c, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x3f, 0x3f, 0xf3, 0xf3, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x3c, 0x3c, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf3, 0xf3, 0x3f, 0x3f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3f, 0x3f, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x3c, 0x3c, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0xc3, 0xc3, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0xc3, 0xc3, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0xff, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x3f, 0x3f, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf0, 0xf0, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf0, 0xf0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0xff, 0xff, 0xc3, 0xc3, 0x03, 0x03, 0x03, 0x03, 0x3f, 0x3f, 0x3c, 0x3c, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0x3f, 0xff, 0xff, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xfc, 0xfc, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0xff, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x3f, 0x3f, 0xfc, 0xfc, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xcf, 0xcf, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xcf, 0xcf, 0xcf, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xcf, 0xcf, 0xcf, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x3c, 0x3c, 0xff, 0xff, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0xff, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x30, 0x30, 0xf0, 0xf0, 0xc3, 0xc3, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xfc, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x03, 0x03, 0xc3, 0xc3, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 5x7 font (in 6x8 cell) const uint8_t ucSmallFont[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x5f, 0x06, 0x00, 0x07, 0x03, 0x00, 0x07, 0x03, 0x24, 0x7e, 0x24, 0x7e, 0x24, 0x24, 0x2b, 0x6a, 0x12, 0x00, 0x63, 0x13, 0x08, 0x64, 0x63, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x07, 0x03, 0x00, 0x00, 0x00, 0x3e, 0x41, 0x00, 0x00, 0x00, 0x41, 0x3e, 0x00, 0x00, 0x08, 0x3e, 0x1c, 0x3e, 0x08, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0xe0, 0x60, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x60, 0x60, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x51, 0x49, 0x45, 0x3e, 0x00, 0x42, 0x7f, 0x40, 0x00, 0x62, 0x51, 0x49, 0x49, 0x46, 0x22, 0x49, 0x49, 0x49, 0x36, 0x18, 0x14, 0x12, 0x7f, 0x10, 0x2f, 0x49, 0x49, 0x49, 0x31, 0x3c, 0x4a, 0x49, 0x49, 0x30, 0x01, 0x71, 0x09, 0x05, 0x03, 0x36, 0x49, 0x49, 0x49, 0x36, 0x06, 0x49, 0x49, 0x29, 0x1e, 0x00, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0xec, 0x6c, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3e, 0x41, 0x5d, 0x55, 0x1e, 0x7e, 0x11, 0x11, 0x11, 0x7e, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x3e, 0x41, 0x41, 0x41, 0x22, 0x7f, 0x41, 0x41, 0x41, 0x3e, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x7f, 0x09, 0x09, 0x09, 0x01, 0x3e, 0x41, 0x49, 0x49, 0x7a, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x41, 0x7f, 0x41, 0x00, 0x30, 0x40, 0x40, 0x40, 0x3f, 0x7f, 0x08, 0x14, 0x22, 0x41, 0x7f, 0x40, 0x40, 0x40, 0x40, 0x7f, 0x02, 0x04, 0x02, 0x7f, 0x7f, 0x02, 0x04, 0x08, 0x7f, 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x7f, 0x09, 0x09, 0x09, 0x06, 0x3e, 0x41, 0x51, 0x21, 0x5e, 0x7f, 0x09, 0x09, 0x19, 0x66, 0x26, 0x49, 0x49, 0x49, 0x32, 0x01, 0x01, 0x7f, 0x01, 0x01, 0x3f, 0x40, 0x40, 0x40, 0x3f, 0x1f, 0x20, 0x40, 0x20, 0x1f, 0x3f, 0x40, 0x3c, 0x40, 0x3f, 0x63, 0x14, 0x08, 0x14, 0x63, 0x07, 0x08, 0x70, 0x08, 0x07, 0x71, 0x49, 0x45, 0x43, 0x00, 0x00, 0x7f, 0x41, 0x41, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x7f, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x03, 0x07, 0x00, 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, 0x7f, 0x44, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x44, 0x7f, 0x38, 0x54, 0x54, 0x54, 0x08, 0x08, 0x7e, 0x09, 0x09, 0x00, 0x18, 0xa4, 0xa4, 0xa4, 0x7c, 0x7f, 0x04, 0x04, 0x78, 0x00, 0x00, 0x00, 0x7d, 0x40, 0x00, 0x40, 0x80, 0x84, 0x7d, 0x00, 0x7f, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x7f, 0x40, 0x00, 0x7c, 0x04, 0x18, 0x04, 0x78, 0x7c, 0x04, 0x04, 0x78, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0xfc, 0x44, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0xfc, 0x44, 0x78, 0x44, 0x04, 0x08, 0x08, 0x54, 0x54, 0x54, 0x20, 0x04, 0x3e, 0x44, 0x24, 0x00, 0x3c, 0x40, 0x20, 0x7c, 0x00, 0x1c, 0x20, 0x40, 0x20, 0x1c, 0x3c, 0x60, 0x30, 0x60, 0x3c, 0x6c, 0x10, 0x10, 0x6c, 0x00, 0x9c, 0xa0, 0x60, 0x3c, 0x00, 0x64, 0x54, 0x54, 0x4c, 0x00, 0x08, 0x3e, 0x41, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x41, 0x3e, 0x08, 0x02, 0x01, 0x02, 0x01, 0x00, 0x3c, 0x26, 0x23, 0x26, 0x3c }; // Initialization sequences const unsigned char oled128_initbuf[] = { 0x00, 0xae, 0xdc, 0x00, 0x81, 0x40, 0xa1, 0xc8, 0xa8, 0x7f, 0xd5, 0x50, 0xd9, 0x22, 0xdb, 0x35, 0xb0, 0xda, 0x12, 0xa4, 0xa6, 0xaf }; const unsigned char oled64_initbuf[] = { 0x00, 0xae, 0xa8, 0x3f, 0xd3, 0x00, 0x40, 0xa1, 0xc8, 0xda, 0x12, 0x81, 0xff, 0xa4, 0xa6, 0xd5, 0x80, 0x8d, 0x14, 0xaf, 0x20, 0x02 }; const unsigned char oled32_initbuf[] = { 0x00, 0xae, 0xd5, 0x80, 0xa8, 0x1f, 0xd3, 0x00, 0x40, 0x8d, 0x14, 0xa1, 0xc8, 0xda, 0x02, 0x81, 0x7f, 0xd9, 0xf1, 0xdb, 0x40, 0xa4, 0xa6, 0xaf }; const unsigned char oled72_initbuf[] = { 0x00, 0xae, 0xa8, 0x3f, 0xd3, 0x00, 0x40, 0xa1, 0xc8, 0xda, 0x12, 0x81, 0xff, 0xad, 0x30, 0xd9, 0xf1, 0xa4, 0xa6, 0xd5, 0x80, 0x8d, 0x14, 0xaf, 0x20, 0x02 }; // some globals static void __oledWriteCommand(SSOLED *pOLED, unsigned char c); void __InvertBytes(uint8_t *pData, uint8_t bLen); // wrapper/adapter functions to make the code work on Linux static uint8_t pgm_read_byte(uint8_t *ptr) { return *ptr; } static int16_t pgm_read_word(uint8_t *ptr) { return ptr[0] + (ptr[1] << 8); } static void _I2CWrite(SSOLED *pOLED, unsigned char *pData, int iLen) { I2CWrite(&pOLED->bbi2c, pOLED->oled_addr, pData, iLen); } /* _I2CWrite() */ // // Initializes the OLED controller into "page mode" // int __oledInit(SSOLED *pOLED, int bInvert, int32_t iSpeed) { unsigned char uc[4]; int rc = OLED_NOT_FOUND; int iAddr = pOLED->oled_addr; int bFlip = (int) pOLED->oled_flip; int iType = pOLED->oled_type; pOLED->ucScreen = NULL; // reset backbuffer; user must provide one later pOLED->oled_wrap = 0; // default - disable text wrap I2CInit(&pOLED->bbi2c, iSpeed); // on Linux, SDA = bus number, SCL = device address // find the device address if requested if (iAddr == -1 || iAddr == 0 || iAddr == 0xff) // find it { I2CTest(&pOLED->bbi2c, 0x3c); if (I2CTest(&pOLED->bbi2c, 0x3c)) pOLED->oled_addr = 0x3c; else if (I2CTest(&pOLED->bbi2c, 0x3d)) pOLED->oled_addr = 0x3d; else return rc; // no display found! } else { pOLED->oled_addr = iAddr; I2CTest(&pOLED->bbi2c, iAddr); if (!I2CTest(&pOLED->bbi2c, iAddr)) return rc; // no display found } // Detect the display controller (SSD1306, SH1107 or SH1106) uint8_t u = 0; I2CReadRegister(&pOLED->bbi2c, pOLED->oled_addr, 0x00, &u, 1); // read the status register u &= 0x0f; // mask off power on/off bit if (u == 0x7 || u == 0xf) // SH1107 { pOLED->oled_type = OLED_128x128; rc = OLED_SH1107_3C; bFlip = !bFlip; // SH1107 seems to have this reversed from the usual direction } else if (u == 0x8) // SH1106 { rc = OLED_SH1106_3C; pOLED->oled_type = OLED_132x64; // needs to be treated a little differently } else if (u == 3 || u == 6) // 6=128x64 display, 3=smaller { rc = OLED_SSD1306_3C; } if (pOLED->oled_addr == 0x3d) rc++; // return the '3D' version of the type if (iType == OLED_128x32 || iType == OLED_96x16) _I2CWrite(pOLED, (unsigned char *) oled32_initbuf, sizeof(oled32_initbuf)); else if (iType == OLED_128x128) _I2CWrite(pOLED, (unsigned char *) oled128_initbuf, sizeof(oled128_initbuf)); else if (iType == OLED_72x40) _I2CWrite(pOLED, (unsigned char *) oled72_initbuf, sizeof(oled72_initbuf)); else // 132x64, 128x64 and 64x32 _I2CWrite(pOLED, (unsigned char *) oled64_initbuf, sizeof(oled64_initbuf)); if (bInvert) { uc[0] = 0; // command uc[1] = 0xa7; // invert command _I2CWrite(pOLED, uc, 2); } if (bFlip) // rotate display 180 { uc[0] = 0; // command uc[1] = 0xa0; _I2CWrite(pOLED, uc, 2); uc[1] = 0xc0; _I2CWrite(pOLED, uc, 2); } pOLED->oled_x = 128; // assume 128x64 pOLED->oled_y = 64; if (iType == OLED_96x16) { pOLED->oled_x = 96; pOLED->oled_y = 16; } else if (iType == OLED_128x32) pOLED->oled_y = 32; else if (iType == OLED_128x128) pOLED->oled_y = 128; else if (iType == OLED_64x32) { pOLED->oled_x = 64; pOLED->oled_y = 32; } else if (iType == OLED_72x40) { pOLED->oled_x = 72; pOLED->oled_y = 40; } return rc; } /* oledInit() */ // // Sends a command to turn on or off the OLED display // void __oledPower(SSOLED *pOLED, uint8_t bOn) { if (bOn) __oledWriteCommand(pOLED, 0xaf); // turn on OLED else __oledWriteCommand(pOLED, 0xae); // turn off OLED } /* oledPower() */ // Send a single byte command to the OLED controller static void __oledWriteCommand(SSOLED *pOLED, unsigned char c) { unsigned char buf[2]; buf[0] = 0x00; // command introducer buf[1] = c; _I2CWrite(pOLED, buf, 2); } /* oledWriteCommand() */ static void __oledWriteCommand2(SSOLED *pOLED, unsigned char c, unsigned char d) { unsigned char buf[3]; buf[0] = 0x00; buf[1] = c; buf[2] = d; _I2CWrite(pOLED, buf, 3); } /* oledWriteCommand2() */ // // Sets the brightness (0=off, 255=brightest) // void __oledSetContrast(SSOLED *pOLED, unsigned char ucContrast) { __oledWriteCommand2(pOLED, 0x81, ucContrast); } /* oledSetContrast() */ // // Scroll the internal buffer by 1 scanline (up/down) // width is in pixels, lines is group of 8 rows // int __oledScrollBuffer(SSOLED *pOLED, int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp) { uint8_t b, *s; int col, row; if (iStartCol < 0 || iStartCol > 127 || iEndCol < 0 || iEndCol > 127 || iStartCol > iEndCol) // invalid return -1; if (iStartRow < 0 || iStartRow > 7 || iEndRow < 0 || iEndRow > 7 || iStartRow > iEndRow) return -1; if (bUp) { for (row = iStartRow; row <= iEndRow; row++) { s = &pOLED->ucScreen[(row * 128) + iStartCol]; for (col = iStartCol; col <= iEndCol; col++) { b = *s; b >>= 1; // scroll pixels 'up' if (row < iEndRow) b |= (s[128] << 7); // capture pixel of row below, except for last row *s++ = b; } // for col } // for row } // up else // down { for (row = iEndRow; row >= iStartRow; row--) { s = &pOLED->ucScreen[(row * 128) + iStartCol]; for (col = iStartCol; col <= iEndCol; col++) { b = *s; b <<= 1; // scroll down if (row > iStartRow) b |= (s[-128] >> 7); // capture pixel of row above *s++ = b; } // for col } // for row } return 0; } /* oledScrollBuffer() */ // // Send commands to position the "cursor" (aka memory write address) // to the given row and column // static void __oledSetPosition(SSOLED *pOLED, int x, int y, int bRender) { unsigned char buf[4]; pOLED->iScreenOffset = (y * 128) + x; if (!bRender) return; // don't send the commands to the OLED if we're not rendering the graphics now if (pOLED->oled_type == OLED_64x32) // visible display starts at column 32, row 4 { x += 32; // display is centered in VRAM, so this is always true if (pOLED->oled_flip == 0) // non-flipped display starts from line 4 y += 4; } else if (pOLED->oled_type == OLED_132x64) // SH1106 has 128 pixels centered in 132 { x += 2; } else if (pOLED->oled_type == OLED_96x16) // visible display starts at line 2 { // mapping is a bit strange on the 96x16 OLED if (pOLED->oled_flip) x += 32; else y += 2; } else if (pOLED->oled_type == OLED_72x40) // starts at x=28,y=3 { x += 28; if (!pOLED->oled_flip) { y += 3; } } buf[0] = 0x00; // command introducer buf[1] = 0xb0 | y; // set page to Y buf[2] = x & 0xf; // lower column address buf[3] = 0x10 | (x >> 4); // upper column addr _I2CWrite(pOLED, buf, 4); } /* oledSetPosition() */ // // Write a block of pixel data to the OLED // Length can be anything from 1 to 1024 (whole display) // static void __oledWriteDataBlock(SSOLED *pOLED, unsigned char *ucBuf, int iLen, int bRender) { unsigned char ucTemp[129]; ucTemp[0] = 0x40; // data command // Copying the data has the benefit in SPI mode of not letting // the original data get overwritten by the SPI.transfer() function if (bRender) { memcpy(&ucTemp[1], ucBuf, iLen); _I2CWrite(pOLED, ucTemp, iLen + 1); } // Keep a copy in local buffer if (pOLED->ucScreen) { memcpy(&pOLED->ucScreen[pOLED->iScreenOffset], ucBuf, iLen); pOLED->iScreenOffset += iLen; pOLED->iScreenOffset &= 1023; // we use a fixed stride of 128 no matter what the display size } } // // Byte operands for compressing the data // The first 2 bits are the type, followed by the counts #define OP_MASK 0xc0 #define OP_SKIPCOPY 0x00 #define OP_COPYSKIP 0x40 #define OP_REPEATSKIP 0x80 #define OP_REPEAT 0xc0 // // Write a block of flash memory to the display // void __oledWriteFlashBlock(SSOLED *pOLED, uint8_t *s, int iLen) { int j; int iWidthMask = pOLED->oled_x - 1; int iSizeMask = ((pOLED->oled_x * pOLED->oled_y) / 8) - 1; int iWidthShift = (pOLED->oled_x == 128) ? 7 : 6; // assume 128 or 64 wide uint8_t ucTemp[128]; while (((pOLED->iScreenOffset & iWidthMask) + iLen) >= pOLED->oled_x) // if it will hit the page end { j = pOLED->oled_x - (pOLED->iScreenOffset & iWidthMask); // amount we can write in one shot memcpy(ucTemp, s, j); __oledWriteDataBlock(pOLED, ucTemp, j, 1); s += j; iLen -= j; pOLED->iScreenOffset = (pOLED->iScreenOffset + j) & iSizeMask; __oledSetPosition(pOLED, pOLED->iScreenOffset & iWidthMask, (pOLED->iScreenOffset >> iWidthShift), 1); } // while it needs some help memcpy(ucTemp, s, iLen); __oledWriteDataBlock(pOLED, ucTemp, iLen, 1); pOLED->iScreenOffset = (pOLED->iScreenOffset + iLen) & iSizeMask; } /* oledWriteFlashBlock() */ // // Write a repeating byte to the display // void __oledRepeatByte(SSOLED *pOLED, uint8_t b, int iLen) { int j; int iWidthMask = pOLED->oled_x - 1; int iWidthShift = (pOLED->oled_x == 128) ? 7 : 6; // assume 128 or 64 pixels wide int iSizeMask = ((pOLED->oled_x * pOLED->oled_y) / 8) - 1; uint8_t ucTemp[128]; memset(ucTemp, b, (iLen > 128) ? 128 : iLen); while (((pOLED->iScreenOffset & iWidthMask) + iLen) >= pOLED->oled_x) // if it will hit the page end { j = pOLED->oled_x - (pOLED->iScreenOffset & iWidthMask); // amount we can write in one shot __oledWriteDataBlock(pOLED, ucTemp, j, 1); iLen -= j; pOLED->iScreenOffset = (pOLED->iScreenOffset + j) & iSizeMask; __oledSetPosition(pOLED, pOLED->iScreenOffset & iWidthMask, (pOLED->iScreenOffset >> iWidthShift), 1); } // while it needs some help __oledWriteDataBlock(pOLED, ucTemp, iLen, 1); pOLED->iScreenOffset += iLen; } /* oledRepeatByte() */ // // Play a frame of animation data // The animation data is assumed to be encoded for a full frame of the display // Given the pointer to the start of the compressed data, // it returns the pointer to the start of the next frame // Frame rate control is up to the calling program to manage // When it finishes the last frame, it will start again from the beginning // uint8_t * __oledPlayAnimFrame(SSOLED *pOLED, uint8_t *pAnimation, uint8_t *pCurrent, int iLen) { uint8_t *s; int i, j; unsigned char b, bCode; int iBufferSize = (pOLED->oled_x * pOLED->oled_y) / 8; // size in bytes of the display devce int iWidthMask, iWidthShift; iWidthMask = pOLED->oled_x - 1; iWidthShift = (pOLED->oled_x == 128) ? 7 : 6; // 128 or 64 pixels wide if (pCurrent == NULL || pCurrent > pAnimation + iLen) return NULL; // invalid starting point s = (uint8_t *) pCurrent; // start of animation data i = 0; __oledSetPosition(pOLED, 0, 0, 1); while (i < iBufferSize) // run one frame { bCode = pgm_read_byte(s++); switch (bCode & OP_MASK) // different compression types { case OP_SKIPCOPY: // skip/copy if (bCode == OP_SKIPCOPY) // big skip { b = pgm_read_byte(s++); i += b + 1; __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } else // skip/copy { if (bCode & 0x38) { i += ((bCode & 0x38) >> 3); // skip amount __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } if (bCode & 7) { __oledWriteFlashBlock(pOLED, s, bCode & 7); s += (bCode & 7); i += bCode & 7; } } break; case OP_COPYSKIP: // copy/skip if (bCode == OP_COPYSKIP) // big copy { b = pgm_read_byte(s++); j = b + 1; __oledWriteFlashBlock(pOLED, s, j); s += j; i += j; } else { j = ((bCode & 0x38) >> 3); if (j) { __oledWriteFlashBlock(pOLED, s, j); s += j; i += j; } if (bCode & 7) { i += (bCode & 7); // skip __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } } break; case OP_REPEATSKIP: // repeat/skip j = (bCode & 0x38) >> 3; // repeat count b = pgm_read_byte(s++); __oledRepeatByte(pOLED, b, j); i += j; if (bCode & 7) { i += (bCode & 7); // skip amount __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } break; case OP_REPEAT: j = (bCode & 0x3f) + 1; b = pgm_read_byte(s++); __oledRepeatByte(pOLED, b, j); i += j; break; } // switch on code type } // while rendering a frame if (s >= pAnimation + iLen) // we've hit the end, restart from the beginning s = pAnimation; return s; // return pointer to start of next frame } /* oledPlayAnimFrame() */ // // Draw a sprite of any size in any position // If it goes beyond the left/right or top/bottom edges // it's trimmed to show the valid parts // This function requires a back buffer to be defined // The priority color (0 or 1) determines which color is painted // when a 1 is encountered in the source image. // void __oledDrawSprite(SSOLED *pOLED, uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority) { int tx, ty, dx, dy, iStartX; uint8_t *s, *d, uc, pix, ucSrcMask, ucDstMask; if (x + cx < 0 || y + cy < 0 || x >= pOLED->oled_x || y >= pOLED->oled_y || pOLED->ucScreen == NULL) return; // no backbuffer or out of bounds dy = y; // destination y if (y < 0) // skip the invisible parts { cy += y; y = -y; pSprite += (y * iPitch); dy = 0; } if (y + cy > pOLED->oled_y) cy = pOLED->oled_y - y; iStartX = 0; dx = x; if (x < 0) { cx += x; x = -x; iStartX = x; dx = 0; } if (x + cx > pOLED->oled_x) cx = pOLED->oled_x - x; for (ty = 0; ty < cy; ty++) { s = &pSprite[iStartX >> 3]; d = &pOLED->ucScreen[(dy >> 3) * pOLED->oled_x + dx]; ucSrcMask = 0x80 >> (iStartX & 7); pix = *s++; ucDstMask = 1 << (dy & 7); if (iPriority) // priority color is 1 { for (tx = 0; tx < cx; tx++) { uc = d[0]; if (pix & ucSrcMask) // set pixel in source, set it in dest d[0] = (uc | ucDstMask); d++; // next pixel column ucSrcMask >>= 1; if (ucSrcMask == 0) // read next byte { ucSrcMask = 0x80; pix = *s++; } } // for tx } // priorty color 1 else { for (tx = 0; tx < cx; tx++) { uc = d[0]; if (pix & ucSrcMask) // clr pixel in source, clr it in dest d[0] = (uc & ~ucDstMask); d++; // next pixel column ucSrcMask >>= 1; if (ucSrcMask == 0) // read next byte { ucSrcMask = 0x80; pix = *s++; } } // for tx } // priority color 0 dy++; pSprite += iPitch; } // for ty } /* oledDrawSprite() */ // // Draw a 16x16 tile in any of 4 rotated positions // Assumes input image is laid out like "normal" graphics with // the MSB on the left and 2 bytes per line // On AVR, the source image is assumed to be in FLASH memory // The function can draw the tile on byte boundaries, so the x value // can be from 0 to 112 and y can be from 0 to 6 // void __oledDrawTile(SSOLED *pOLED, const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender) { uint8_t ucTemp[32]; // prepare LCD data here uint8_t i, j, k, iOffset, ucMask, uc, ucPixels; uint8_t bFlipX = 0, bFlipY = 0; if (x < 0 || y < 0 || y > 6 || x > 112) return; // out of bounds if (pTile == NULL) return; // bad pointer; really? :( if (iRotation == ANGLE_180 || iRotation == ANGLE_270 || iRotation == ANGLE_FLIPX) bFlipX = 1; if (iRotation == ANGLE_180 || iRotation == ANGLE_270 || iRotation == ANGLE_FLIPY) bFlipY = 1; memset(ucTemp, 0, sizeof(ucTemp)); // we only set white pixels, so start from black if (iRotation == ANGLE_0 || iRotation == ANGLE_180 || iRotation == ANGLE_FLIPX || iRotation == ANGLE_FLIPY) { for (j = 0; j < 16; j++) // y { for (i = 0; i < 16; i += 8) // x { ucPixels = pgm_read_byte((uint8_t*) pTile++); ucMask = 0x80; // MSB is the first source pixel for (k = 0; k < 8; k++) { if (ucPixels & ucMask) // translate the pixel { if (bFlipY) uc = 0x80 >> (j & 7); else uc = 1 << (j & 7); iOffset = i + k; if (bFlipX) iOffset = 15 - iOffset; iOffset += (j & 8) << 1; // top/bottom half of output if (bFlipY) iOffset ^= 16; ucTemp[iOffset] |= uc; } ucMask >>= 1; } // for k } // for i } // for j } else // rotated 90/270 { for (j = 0; j < 16; j++) // y { for (i = 0; i < 16; i += 8) // x { ucPixels = pgm_read_byte((uint8_t*) pTile++); ucMask = 0x80; // MSB is the first source pixel for (k = 0; k < 8; k++) { if (ucPixels & ucMask) // translate the pixel { if (bFlipY) uc = 0x80 >> k; else uc = 1 << k; iOffset = 15 - j; if (bFlipX) iOffset = 15 - iOffset; iOffset += i << 1; // top/bottom half of output if (bFlipY) iOffset ^= 16; ucTemp[iOffset] |= uc; } ucMask >>= 1; } // for k } // for i } // for j } if (bInvert) __InvertBytes(ucTemp, 32); // Send the data to the display __oledSetPosition(pOLED, x, y, bRender); __oledWriteDataBlock(pOLED, ucTemp, 16, bRender); // top half __oledSetPosition(pOLED, x, y + 1, bRender); __oledWriteDataBlock(pOLED, &ucTemp[16], 16, bRender); // bottom half } /* oledDrawTile() */ // Set (or clear) an individual pixel // The local copy of the frame buffer is used to avoid // reading data from the display controller int __oledSetPixel(SSOLED *pOLED, int x, int y, unsigned char ucColor, int bRender) { int i; unsigned char uc, ucOld; i = ((y >> 3) * 128) + x; if (i < 0 || i > 1023) // off the screen return -1; __oledSetPosition(pOLED, x, y >> 3, bRender); if (pOLED->ucScreen) uc = ucOld = pOLED->ucScreen[i]; else if (pOLED->oled_type == OLED_132x64 || pOLED->oled_type == OLED_128x128) // SH1106/SH1107 can read data { uint8_t ucTemp[3]; ucTemp[0] = 0x80; // one command ucTemp[1] = 0xE0; // read_modify_write ucTemp[2] = 0xC0; // one data _I2CWrite(pOLED, ucTemp, 3); // read a dummy byte followed by the data byte we want I2CRead(&pOLED->bbi2c, pOLED->oled_addr, ucTemp, 2); uc = ucOld = ucTemp[1]; // first byte is garbage } else uc = ucOld = 0; uc &= ~(0x1 << (y & 7)); if (ucColor) { uc |= (0x1 << (y & 7)); } if (uc != ucOld) // pixel changed { // oledSetPosition(x, y>>3); if (pOLED->ucScreen) { __oledWriteDataBlock(pOLED, &uc, 1, bRender); pOLED->ucScreen[i] = uc; } else if (pOLED->oled_type == OLED_132x64 || pOLED->oled_type == OLED_128x128) // end the read_modify_write operation { uint8_t ucTemp[4]; ucTemp[0] = 0xc0; // one data ucTemp[1] = uc; // actual data ucTemp[2] = 0x80; // one command ucTemp[3] = 0xEE; // end read_modify_write operation _I2CWrite(pOLED, ucTemp, 4); } } return 0; } /* oledSetPixel() */ // // Invert font data // void __InvertBytes(uint8_t *pData, uint8_t bLen) { uint8_t i; for (i = 0; i < bLen; i++) { *pData = ~(*pData); pData++; } } /* InvertBytes() */ // // Load a 128x64 1-bpp Windows bitmap // Pass the pointer to the beginning of the BMP file // First pass version assumes a full screen bitmap // int __oledLoadBMP(SSOLED *pOLED, uint8_t *pBMP, int bInvert, int bRender) { int16_t i16; int iOffBits, q, y, j; // offset to bitmap data int iPitch; uint8_t x, z, b, *s; uint8_t dst_mask; uint8_t ucTemp[16]; // process 16 bytes at a time uint8_t bFlipped = false; i16 = pgm_read_word(pBMP); if (i16 != 0x4d42) // must start with 'BM' return -1; // not a BMP file i16 = pgm_read_word(pBMP + 18); if (i16 != 128) // must be 128 pixels wide return -1; i16 = pgm_read_word(pBMP + 22); if (i16 != 64 && i16 != -64) // must be 64 pixels tall return -1; if (i16 == 64) // BMP is flipped vertically (typical) bFlipped = true; i16 = pgm_read_word(pBMP + 28); if (i16 != 1) // must be 1 bit per pixel return -1; iOffBits = pgm_read_word(pBMP + 10); iPitch = 16; if (bFlipped) { iPitch = -16; iOffBits += (63 * 16); // start from bottom } // rotate the data and send it to the display for (y = 0; y < 8; y++) // 8 lines of 8 pixels { __oledSetPosition(pOLED, 0, y, bRender); for (j = 0; j < 8; j++) // do 8 sections of 16 columns { s = &pBMP[iOffBits + (j * 2) + (y * iPitch * 8)]; // source line memset(ucTemp, 0, 16); // start with all black for (x = 0; x < 16; x += 8) // do each block of 16x8 pixels { dst_mask = 1; for (q = 0; q < 8; q++) // gather 8 rows { b = pgm_read_byte(s + (q * iPitch)); for (z = 0; z < 8; z++) // gather up the 8 bits of this column { if (b & 0x80) ucTemp[x + z] |= dst_mask; b <<= 1; } // for z dst_mask <<= 1; } // for q s++; // next source byte } // for x if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, ucTemp, 16, bRender); } // for j } // for y return 0; } /* oledLoadBMP() */ // // Set the current cursor position // The column represents the pixel column (0-127) // The row represents the text row (0-7) // void __oledSetCursor(SSOLED *pOLED, int x, int y) { pOLED->iCursorX = x; pOLED->iCursorY = y; } /* oledSetCursor() */ // // Turn text wrap on or off for the oldWriteString() function // void __oledSetTextWrap(SSOLED *pOLED, int bWrap) { pOLED->oled_wrap = bWrap; } /* oledSetTextWrap() */ // // Draw a string of normal (8x8), small (6x8) or large (16x32) characters // At the given col+row // int __oledWriteString(SSOLED *pOLED, int iScroll, int x, int y, char *szMsg, int iSize, int bInvert, int bRender) { int i, iFontOff, iLen, iFontSkip; unsigned char c, *s, ucTemp[40]; if (x == -1 || y == -1) // use the cursor position { x = pOLED->iCursorX; y = pOLED->iCursorY; } else { pOLED->iCursorX = x; pOLED->iCursorY = y; // set the new cursor position } if (pOLED->iCursorX >= pOLED->oled_x || pOLED->iCursorY >= pOLED->oled_y / 8) return -1; // can't draw off the display __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); if (iSize == FONT_8x8) // 8x8 font { i = 0; iFontSkip = iScroll & 7; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && szMsg[i] != 0 && pOLED->iCursorY < pOLED->oled_y / 8) { if (iScroll < 8) // only display visible characters { c = (unsigned char) szMsg[i]; iFontOff = (int) (c - 32) * 7; // we can't directly use the pointer to FLASH memory, so copy to a local buffer ucTemp[0] = 0; memcpy(&ucTemp[1], &ucFont[iFontOff], 7); if (bInvert) __InvertBytes(ucTemp, 8); // oledCachedWrite(ucTemp, 8); iLen = 8 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 7 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY++; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } iFontSkip = 0; } iScroll -= 8; i++; } // while // oledCachedFlush(); // write any remaining data return 0; } // 8x8 #ifndef __AVR__ else if (iSize == FONT_16x32) // 16x32 font { i = 0; iFontSkip = iScroll & 15; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) - 3 && szMsg[i] != 0) { if (iScroll < 16) // if characters are visible { s = (unsigned char *) &ucBigFont[(unsigned char) (szMsg[i] - 32) * 64]; iLen = 16 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; // we can't directly use the pointer to FLASH memory, so copy to a local buffer __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); memcpy(ucTemp, s, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 1, bRender); memcpy(ucTemp, s + 16, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern if (pOLED->iCursorY <= 5) { __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 2, bRender); memcpy(ucTemp, s + 32, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern } if (pOLED->iCursorY <= 4) { __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 3, bRender); memcpy(ucTemp, s + 48, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern } pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 15 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY += 4; } iFontSkip = 0; } // if character visible from scrolling iScroll -= 16; i++; } // while return 0; } // 16x32 #endif // !__AVR__ else if (iSize == FONT_12x16) // 6x8 stretched to 12x16 { i = 0; iFontSkip = iScroll % 12; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) - 1 && szMsg[i] != 0) { // stretch the 'normal' font instead of using the big font if (iScroll < 12) // if characters are visible { int tx, ty; c = szMsg[i] - 32; unsigned char uc1, uc2, ucMask, *pDest; s = (unsigned char *) &ucSmallFont[(int) c * 5]; ucTemp[0] = 0; // first column is blank memcpy(&ucTemp[1], s, 6); if (bInvert) __InvertBytes(ucTemp, 6); // Stretch the font to double width + double height memset(&ucTemp[6], 0, 24); // write 24 new bytes for (tx = 0; tx < 6; tx++) { ucMask = 3; pDest = &ucTemp[6 + tx * 2]; uc1 = uc2 = 0; c = ucTemp[tx]; for (ty = 0; ty < 4; ty++) { if (c & (1 << ty)) // a bit is set uc1 |= ucMask; if (c & (1 << (ty + 4))) uc2 |= ucMask; ucMask <<= 2; } pDest[0] = uc1; pDest[1] = uc1; // double width pDest[12] = uc2; pDest[13] = uc2; } // smooth the diagonal lines for (tx = 0; tx < 5; tx++) { uint8_t c0, c1, ucMask2; c0 = ucTemp[tx]; c1 = ucTemp[tx + 1]; pDest = &ucTemp[6 + tx * 2]; ucMask = 1; ucMask2 = 2; for (ty = 0; ty < 7; ty++) { if (((c0 & ucMask) && !(c1 & ucMask) && !(c0 & ucMask2) && (c1 & ucMask2)) || (!(c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask2) && !(c1 & ucMask2))) { if (ty < 3) // top half { pDest[1] |= (1 << ((ty * 2) + 1)); pDest[2] |= (1 << ((ty * 2) + 1)); pDest[1] |= (1 << ((ty + 1) * 2)); pDest[2] |= (1 << ((ty + 1) * 2)); } else if (ty == 3) // on the border { pDest[1] |= 0x80; pDest[2] |= 0x80; pDest[13] |= 1; pDest[14] |= 1; } else // bottom half { pDest[13] |= (1 << (2 * (ty - 4) + 1)); pDest[14] |= (1 << (2 * (ty - 4) + 1)); pDest[13] |= (1 << ((ty - 3) * 2)); pDest[14] |= (1 << ((ty - 3) * 2)); } } else if (!(c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask2) && !(c1 & ucMask2)) { if (ty < 4) // top half { pDest[1] |= (1 << ((ty * 2) + 1)); pDest[2] |= (1 << ((ty + 1) * 2)); } else { pDest[13] |= (1 << (2 * (ty - 4) + 1)); pDest[14] |= (1 << ((ty - 3) * 2)); } } ucMask <<= 1; ucMask2 <<= 1; } } iLen = 12 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); __oledWriteDataBlock(pOLED, &ucTemp[6 + iFontSkip], iLen, bRender); __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 1, bRender); __oledWriteDataBlock(pOLED, &ucTemp[18 + iFontSkip], iLen, bRender); pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 11 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY += 2; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } iFontSkip = 0; } // if characters are visible iScroll -= 12; i++; } // while return 0; } // 12x16 else if (iSize == FONT_16x16) // 8x8 stretched to 16x16 { i = 0; iFontSkip = iScroll & 15; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) - 1 && szMsg[i] != 0) { // stretch the 'normal' font instead of using the big font if (iScroll < 16) // if characters are visible { int tx, ty; c = szMsg[i] - 32; unsigned char uc1, uc2, ucMask, *pDest; s = (unsigned char *) &ucFont[(int) c * 7]; ucTemp[0] = 0; memcpy(&ucTemp[1], s, 7); if (bInvert) __InvertBytes(ucTemp, 8); // Stretch the font to double width + double height memset(&ucTemp[8], 0, 32); // write 32 new bytes for (tx = 0; tx < 8; tx++) { ucMask = 3; pDest = &ucTemp[8 + tx * 2]; uc1 = uc2 = 0; c = ucTemp[tx]; for (ty = 0; ty < 4; ty++) { if (c & (1 << ty)) // a bit is set uc1 |= ucMask; if (c & (1 << (ty + 4))) uc2 |= ucMask; ucMask <<= 2; } pDest[0] = uc1; pDest[1] = uc1; // double width pDest[16] = uc2; pDest[17] = uc2; } iLen = 16 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); __oledWriteDataBlock(pOLED, &ucTemp[8 + iFontSkip], iLen, bRender); __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 1, bRender); __oledWriteDataBlock(pOLED, &ucTemp[24 + iFontSkip], iLen, bRender); pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 15 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY += 2; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } iFontSkip = 0; } // if characters are visible iScroll -= 16; i++; } // while return 0; } // 16x16 else if (iSize == FONT_6x8) // 6x8 font { i = 0; iFontSkip = iScroll % 6; while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) && szMsg[i] != 0) { if (iScroll < 6) // if characters are visible { c = szMsg[i] - 32; // we can't directly use the pointer to FLASH memory, so copy to a local buffer ucTemp[0] = 0; memcpy(&ucTemp[1], &ucSmallFont[(int) c * 5], 5); if (bInvert) __InvertBytes(ucTemp, 6); iLen = 6 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern // oledCachedWrite(ucTemp, 6); pOLED->iCursorX += iLen; iFontSkip = 0; if (pOLED->iCursorX >= pOLED->oled_x - 5 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY++; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } } // if characters are visible iScroll -= 6; i++; } // oledCachedFlush(); // write any remaining data return 0; } // 6x8 return -1; // invalid size } /* oledWriteString() */ // // Render a sprite/rectangle of pixels from a provided buffer to the display. // The row values refer to byte rows, not pixel rows due to the memory // layout of OLEDs. // returns 0 for success, -1 for invalid parameter // int __oledDrawGFX(SSOLED *pOLED, uint8_t *pBuffer, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch) { int y; if (iSrcCol < 0 || iSrcCol > 127 || iSrcRow < 0 || iSrcRow > 7 || iDestCol < 0 || iDestCol >= pOLED->oled_x || iDestRow < 0 || iDestRow >= (pOLED->oled_y >> 3) || iSrcPitch <= 0) return -1; // invalid for (y = iSrcRow; y < iSrcRow + iHeight; y++) { uint8_t *s = &pBuffer[(y * iSrcPitch) + iSrcCol]; __oledSetPosition(pOLED, iDestCol, iDestRow, 1); __oledWriteDataBlock(pOLED, s, iWidth, 1); pBuffer += iSrcPitch; iDestRow++; } // for y return 0; } /* oledDrawGFX() */ // // Dump a screen's worth of data directly to the display // Try to speed it up by comparing the new bytes with the existing buffer // void __oledDumpBuffer(SSOLED *pOLED, uint8_t *pBuffer) { int x, y; int iLines, iCols; uint8_t bNeedPos; uint8_t *pSrc = pOLED->ucScreen; if (pBuffer == NULL) // dump the internal buffer if none is given pBuffer = pOLED->ucScreen; if (pBuffer == NULL) return; // no backbuffer and no provided buffer iLines = pOLED->oled_y >> 3; iCols = pOLED->oled_x >> 4; for (y = 0; y < iLines; y++) { bNeedPos = 1; // start of a new line means we need to set the position too for (x = 0; x < iCols; x++) // wiring library has a 32-byte buffer, so send 16 bytes so that the data prefix (0x40) can fit { if (pOLED->ucScreen == NULL || pBuffer == pSrc || memcmp(pSrc, pBuffer, 16) != 0) // doesn't match, need to send it { if (bNeedPos) // need to reposition output cursor? { bNeedPos = 0; __oledSetPosition(pOLED, x * 16, y, 1); } __oledWriteDataBlock(pOLED, pBuffer, 16, 1); } else { bNeedPos = 1; // we're skipping a block, so next time will need to set the new position } pSrc += 16; pBuffer += 16; } // for x pSrc += (128 - pOLED->oled_x); // for narrow displays, skip to the next line pBuffer += (128 - pOLED->oled_x); } // for y } /* oledDumpBuffer() */ // // Fill the frame buffer with a byte pattern // e.g. all off (0x00) or all on (0xff) // void __oledFill(SSOLED *pOLED, unsigned char ucData, int bRender) { uint8_t x, y; uint8_t iLines, iCols; unsigned char temp[16]; iLines = pOLED->oled_y >> 3; iCols = pOLED->oled_x >> 4; memset(temp, ucData, 16); pOLED->iCursorX = pOLED->iCursorY = 0; for (y = 0; y < iLines; y++) { __oledSetPosition(pOLED, 0, y, bRender); // set to (0,Y) for (x = 0; x < iCols; x++) // wiring library has a 32-byte buffer, so send 16 bytes so that the data prefix (0x40) can fit { __oledWriteDataBlock(pOLED, temp, 16, bRender); } // for x // 72 isn't evenly divisible by 16, so fix it if (pOLED->oled_type == OLED_72x40) __oledWriteDataBlock(pOLED, temp, 8, bRender); } // for y if (pOLED->ucScreen) memset(pOLED->ucScreen, ucData, (pOLED->oled_x * pOLED->oled_y) / 8); } /* oledFill() */ // // Provide or revoke a back buffer for your OLED graphics // This allows you to manage the RAM used by ss_oled on tiny // embedded platforms like the ATmega series // Pass NULL to revoke the buffer. Make sure you provide a buffer // large enough for your display (e.g. 128x64 needs 1K - 1024 bytes) // void __oledSetBackBuffer(SSOLED *pOLED, uint8_t *pBuffer) { pOLED->ucScreen = pBuffer; } /* oledSetBackBuffer() */ void __oledDrawLine(SSOLED *pOLED, int x1, int y1, int x2, int y2, int bRender) { int temp; int dx = x2 - x1; int dy = y2 - y1; int error; uint8_t *p, *pStart, mask, bOld, bNew; int xinc, yinc; int y, x; if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || x1 >= pOLED->oled_x || x2 >= pOLED->oled_x || y1 >= pOLED->oled_y || y2 >= pOLED->oled_y) return; if (abs(dx) > abs(dy)) { // X major case if (x2 < x1) { dx = -dx; temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } y = y1; dy = (y2 - y1); error = dx >> 1; yinc = 1; if (dy < 0) { dy = -dy; yinc = -1; } p = pStart = &pOLED->ucScreen[x1 + ((y >> 3) << 7)]; // point to current spot in back buffer mask = 1 << (y & 7); // current bit offset for (x = x1; x1 <= x2; x1++) { *p++ |= mask; // set pixel and increment x pointer error -= dy; if (error < 0) { error += dx; if (yinc > 0) mask <<= 1; else mask >>= 1; if (mask == 0) // we've moved outside the current row, write the data we changed { __oledSetPosition(pOLED, x, y >> 3, bRender); __oledWriteDataBlock(pOLED, pStart, (int) (p - pStart), bRender); // write the row we changed x = x1 + 1; // we've already written the byte at x1 y1 = y + yinc; p += (yinc > 0) ? 128 : -128; pStart = p; mask = 1 << (y1 & 7); } y += yinc; } } // for x1 if (p != pStart) // some data needs to be written { __oledSetPosition(pOLED, x, y >> 3, bRender); __oledWriteDataBlock(pOLED, pStart, (int) (p - pStart), bRender); } } else { // Y major case if (y1 > y2) { dy = -dy; temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } p = &pOLED->ucScreen[x1 + ((y1 >> 3) * 128)]; // point to current spot in back buffer bOld = bNew = p[0]; // current data at that address mask = 1 << (y1 & 7); // current bit offset dx = (x2 - x1); error = dy >> 1; xinc = 1; if (dx < 0) { dx = -dx; xinc = -1; } for (x = x1; y1 <= y2; y1++) { bNew |= mask; // set the pixel error -= dx; mask <<= 1; // y1++ if (mask == 0) // we're done with this byte, write it if necessary { if (bOld != bNew) { p[0] = bNew; // save to RAM __oledSetPosition(pOLED, x, y1 >> 3, bRender); __oledWriteDataBlock(pOLED, &bNew, 1, bRender); } p += 128; // next line bOld = bNew = p[0]; mask = 1; // start at LSB again } if (error < 0) { error += dy; if (bOld != bNew) // write the last byte we modified if it changed { p[0] = bNew; // save to RAM __oledSetPosition(pOLED, x, y1 >> 3, bRender); __oledWriteDataBlock(pOLED, &bNew, 1, bRender); } p += xinc; x += xinc; bOld = bNew = p[0]; } } // for y if (bOld != bNew) // write the last byte we modified if it changed { p[0] = bNew; // save to RAM __oledSetPosition(pOLED, x, y2 >> 3, bRender); __oledWriteDataBlock(pOLED, &bNew, 1, bRender); } } // y major case } /* oledDrawLine() */ // // For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction // This function draws a single pixel and scales its position based on the x/y fraction of the ellipse // static void __DrawScaledPixel(SSOLED *pOLED, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) { uint8_t *d, ucMask; if (iXFrac != 0x10000) x = ((x * iXFrac) >> 16); if (iYFrac != 0x10000) y = ((y * iYFrac) >> 16); x += iCX; y += iCY; if (x < 0 || x >= pOLED->oled_x || y < 0 || y >= pOLED->oled_y) return; // off the screen d = &pOLED->ucScreen[((y >> 3) * 128) + x]; ucMask = 1 << (y & 7); if (ucColor) *d |= ucMask; else *d &= ~ucMask; } /* DrawScaledPixel() */ // // For drawing filled ellipses // static void __DrawScaledLine(SSOLED *pOLED, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) { int iLen, x2; uint8_t *d, ucMask; if (iXFrac != 0x10000) x = ((x * iXFrac) >> 16); if (iYFrac != 0x10000) y = ((y * iYFrac) >> 16); iLen = x * 2; x = iCX - x; y += iCY; x2 = x + iLen; if (y < 0 || y >= pOLED->oled_y) return; // completely off the screen if (x < 0) x = 0; if (x2 >= pOLED->oled_x) x2 = pOLED->oled_x - 1; iLen = x2 - x + 1; // new length d = &pOLED->ucScreen[((y >> 3) * 128) + x]; ucMask = 1 << (y & 7); if (ucColor) // white { for (; iLen > 0; iLen--) *d++ |= ucMask; } else // black { for (; iLen > 0; iLen--) *d++ &= ~ucMask; } } /* DrawScaledLine() */ // // Draw the 8 pixels around the Bresenham circle // (scaled to make an ellipse) // static void BresenhamCircle(SSOLED *pOLED, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor, uint8_t bFill) { if (bFill) // draw a filled ellipse { // for a filled ellipse, draw 4 lines instead of 8 pixels __DrawScaledLine(pOLED, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); __DrawScaledLine(pOLED, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); __DrawScaledLine(pOLED, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); __DrawScaledLine(pOLED, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); } else // draw 8 pixels around the edges { __DrawScaledPixel(pOLED, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -x, y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -x, -y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -y, x, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -y, -x, iXFrac, iYFrac, ucColor); } } /* BresenhamCircle() */ // // Draw an outline or filled ellipse // void __oledEllipse(SSOLED *pOLED, int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled) { int32_t iXFrac, iYFrac; int iRadius, iDelta, x, y; if (pOLED == NULL || pOLED->ucScreen == NULL) return; // must have back buffer defined if (iRadiusX <= 0 || iRadiusY <= 0) return; // invalid radii if (iRadiusX > iRadiusY) // use X as the primary radius { iRadius = iRadiusX; iXFrac = 65536; iYFrac = (iRadiusY * 65536) / iRadiusX; } else { iRadius = iRadiusY; iXFrac = (iRadiusX * 65536) / iRadiusY; iYFrac = 65536; } iDelta = 3 - (2 * iRadius); x = 0; y = iRadius; while (x <= y) { BresenhamCircle(pOLED, iCenterX, iCenterY, x, y, iXFrac, iYFrac, ucColor, bFilled); x++; if (iDelta < 0) { iDelta += (4 * x) + 6; } else { iDelta += 4 * (x - y) + 10; y--; } } } /* oledEllipse() */ // // Draw an outline or filled rectangle // void __oledRectangle(SSOLED *pOLED, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled) { uint8_t *d, ucMask, ucMask2; int tmp, iOff; if (pOLED == NULL || pOLED->ucScreen == NULL) return; // only works with a back buffer if (x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0 || x1 >= pOLED->oled_x || y1 >= pOLED->oled_y || x2 >= pOLED->oled_x || y2 >= pOLED->oled_y) return; // invalid coordinates // Make sure that X1/Y1 is above and to the left of X2/Y2 // swap coordinates as needed to make this true if (x2 < x1) { tmp = x1; x1 = x2; x2 = tmp; } if (y2 < y1) { tmp = y1; y1 = y2; y2 = tmp; } if (bFilled) { int x, y, iMiddle; iMiddle = (y2 >> 3) - (y1 >> 3); ucMask = 0xff << (y1 & 7); if (iMiddle == 0) // top and bottom lines are in the same row ucMask &= (0xff >> (7 - (y2 & 7))); d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1]; // Draw top for (x = x1; x <= x2; x++) { if (ucColor) *d |= ucMask; else *d &= ~ucMask; d++; } if (iMiddle > 1) // need to draw middle part { ucMask = (ucColor) ? 0xff : 0x00; for (y = 1; y < iMiddle; y++) { d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1 + (y * 128)]; for (x = x1; x <= x2; x++) *d++ = ucMask; } } if (iMiddle >= 1) // need to draw bottom part { ucMask = 0xff >> (7 - (y2 & 7)); d = &pOLED->ucScreen[(y2 >> 3) * 128 + x1]; for (x = x1; x <= x2; x++) { if (ucColor) *d++ |= ucMask; else *d++ &= ~ucMask; } } } else // outline { // see if top and bottom lines are within the same byte rows d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1]; if ((y1 >> 3) == (y2 >> 3)) { ucMask2 = 0xff << (y1 & 7); // L/R end masks ucMask = 1 << (y1 & 7); ucMask |= 1 << (y2 & 7); ucMask2 &= (0xff >> (7 - (y2 & 7))); if (ucColor) { *d++ |= ucMask2; // start x1++; for (; x1 < x2; x1++) *d++ |= ucMask; if (x1 <= x2) *d++ |= ucMask2; // right edge } else { *d++ &= ~ucMask2; x1++; for (; x1 < x2; x1++) *d++ &= ~ucMask; if (x1 <= x2) *d++ &= ~ucMask2; // right edge } } else { int y; // L/R sides iOff = (x2 - x1); ucMask = 1 << (y1 & 7); for (y = y1; y <= y2; y++) { if (ucColor) { *d |= ucMask; d[iOff] |= ucMask; } else { *d &= ~ucMask; d[iOff] &= ~ucMask; } ucMask <<= 1; if (ucMask == 0) { ucMask = 1; d += 128; } } // T/B sides ucMask = 1 << (y1 & 7); ucMask2 = 1 << (y2 & 7); x1++; d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1]; iOff = (y2 >> 3) - (y1 >> 3); iOff *= 128; for (; x1 < x2; x1++) { if (ucColor) { *d |= ucMask; d[iOff] |= ucMask2; } else { *d &= ~ucMask; d[iOff] &= ~ucMask2; } d++; } } } // outline } /* oledRectangle() */
bit_bangiic.cpp
// // ss_oled (Small, Simple OLED library) // Copyright (c) 2017-2019 BitBank Software, Inc. // Written by Larry Bank (bitbank@pobox.com) // Project started 1/15/2017 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // #include <stdio.h> #include <string.h> #include <stdlib.h> #include "pico/stdlib.h" #include "include/ss_oled.h" const uint8_t ucFont[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x5f, 0x5f, 0x06, 0x00, 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x14, 0x7f, 0x7f, 0x14, 0x7f, 0x7f, 0x14, 0x24, 0x2e, 0x2a, 0x6b, 0x6b, 0x3a, 0x12, 0x46, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x62, 0x30, 0x7a, 0x4f, 0x5d, 0x37, 0x7a, 0x48, 0x00, 0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x3e, 0x63, 0x41, 0x00, 0x00, 0x00, 0x41, 0x63, 0x3e, 0x1c, 0x00, 0x00, 0x08, 0x2a, 0x3e, 0x1c, 0x3e, 0x2a, 0x08, 0x00, 0x08, 0x08, 0x3e, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x80, 0xe0, 0x60, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x3e, 0x7f, 0x59, 0x4d, 0x47, 0x7f, 0x3e, 0x40, 0x42, 0x7f, 0x7f, 0x40, 0x40, 0x00, 0x62, 0x73, 0x59, 0x49, 0x6f, 0x66, 0x00, 0x22, 0x63, 0x49, 0x49, 0x7f, 0x36, 0x00, 0x18, 0x1c, 0x16, 0x53, 0x7f, 0x7f, 0x50, 0x27, 0x67, 0x45, 0x45, 0x7d, 0x39, 0x00, 0x3c, 0x7e, 0x4b, 0x49, 0x79, 0x30, 0x00, 0x03, 0x03, 0x71, 0x79, 0x0f, 0x07, 0x00, 0x36, 0x7f, 0x49, 0x49, 0x7f, 0x36, 0x00, 0x06, 0x4f, 0x49, 0x69, 0x3f, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe6, 0x66, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x63, 0x36, 0x1c, 0x08, 0x00, 0x00, 0x02, 0x03, 0x59, 0x5d, 0x07, 0x02, 0x3e, 0x7f, 0x41, 0x5d, 0x5d, 0x5f, 0x0e, 0x7c, 0x7e, 0x13, 0x13, 0x7e, 0x7c, 0x00, 0x41, 0x7f, 0x7f, 0x49, 0x49, 0x7f, 0x36, 0x1c, 0x3e, 0x63, 0x41, 0x41, 0x63, 0x22, 0x41, 0x7f, 0x7f, 0x41, 0x63, 0x3e, 0x1c, 0x41, 0x7f, 0x7f, 0x49, 0x5d, 0x41, 0x63, 0x41, 0x7f, 0x7f, 0x49, 0x1d, 0x01, 0x03, 0x1c, 0x3e, 0x63, 0x41, 0x51, 0x33, 0x72, 0x7f, 0x7f, 0x08, 0x08, 0x7f, 0x7f, 0x00, 0x00, 0x41, 0x7f, 0x7f, 0x41, 0x00, 0x00, 0x30, 0x70, 0x40, 0x41, 0x7f, 0x3f, 0x01, 0x41, 0x7f, 0x7f, 0x08, 0x1c, 0x77, 0x63, 0x41, 0x7f, 0x7f, 0x41, 0x40, 0x60, 0x70, 0x7f, 0x7f, 0x0e, 0x1c, 0x0e, 0x7f, 0x7f, 0x7f, 0x7f, 0x06, 0x0c, 0x18, 0x7f, 0x7f, 0x1c, 0x3e, 0x63, 0x41, 0x63, 0x3e, 0x1c, 0x41, 0x7f, 0x7f, 0x49, 0x09, 0x0f, 0x06, 0x1e, 0x3f, 0x21, 0x31, 0x61, 0x7f, 0x5e, 0x41, 0x7f, 0x7f, 0x09, 0x19, 0x7f, 0x66, 0x26, 0x6f, 0x4d, 0x49, 0x59, 0x73, 0x32, 0x03, 0x41, 0x7f, 0x7f, 0x41, 0x03, 0x00, 0x7f, 0x7f, 0x40, 0x40, 0x7f, 0x7f, 0x00, 0x1f, 0x3f, 0x60, 0x60, 0x3f, 0x1f, 0x00, 0x3f, 0x7f, 0x60, 0x30, 0x60, 0x7f, 0x3f, 0x63, 0x77, 0x1c, 0x08, 0x1c, 0x77, 0x63, 0x07, 0x4f, 0x78, 0x78, 0x4f, 0x07, 0x00, 0x47, 0x63, 0x71, 0x59, 0x4d, 0x67, 0x73, 0x00, 0x7f, 0x7f, 0x41, 0x41, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x41, 0x41, 0x7f, 0x7f, 0x00, 0x00, 0x08, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x20, 0x74, 0x54, 0x54, 0x3c, 0x78, 0x40, 0x41, 0x7f, 0x3f, 0x48, 0x48, 0x78, 0x30, 0x38, 0x7c, 0x44, 0x44, 0x6c, 0x28, 0x00, 0x30, 0x78, 0x48, 0x49, 0x3f, 0x7f, 0x40, 0x38, 0x7c, 0x54, 0x54, 0x5c, 0x18, 0x00, 0x48, 0x7e, 0x7f, 0x49, 0x03, 0x06, 0x00, 0x98, 0xbc, 0xa4, 0xa4, 0xf8, 0x7c, 0x04, 0x41, 0x7f, 0x7f, 0x08, 0x04, 0x7c, 0x78, 0x00, 0x44, 0x7d, 0x7d, 0x40, 0x00, 0x00, 0x60, 0xe0, 0x80, 0x84, 0xfd, 0x7d, 0x00, 0x41, 0x7f, 0x7f, 0x10, 0x38, 0x6c, 0x44, 0x00, 0x41, 0x7f, 0x7f, 0x40, 0x00, 0x00, 0x7c, 0x7c, 0x18, 0x78, 0x1c, 0x7c, 0x78, 0x7c, 0x78, 0x04, 0x04, 0x7c, 0x78, 0x00, 0x38, 0x7c, 0x44, 0x44, 0x7c, 0x38, 0x00, 0x84, 0xfc, 0xf8, 0xa4, 0x24, 0x3c, 0x18, 0x18, 0x3c, 0x24, 0xa4, 0xf8, 0xfc, 0x84, 0x44, 0x7c, 0x78, 0x4c, 0x04, 0x0c, 0x18, 0x48, 0x5c, 0x54, 0x74, 0x64, 0x24, 0x00, 0x04, 0x04, 0x3e, 0x7f, 0x44, 0x24, 0x00, 0x3c, 0x7c, 0x40, 0x40, 0x3c, 0x7c, 0x40, 0x1c, 0x3c, 0x60, 0x60, 0x3c, 0x1c, 0x00, 0x3c, 0x7c, 0x60, 0x30, 0x60, 0x7c, 0x3c, 0x44, 0x6c, 0x38, 0x10, 0x38, 0x6c, 0x44, 0x9c, 0xbc, 0xa0, 0xa0, 0xfc, 0x7c, 0x00, 0x4c, 0x64, 0x74, 0x5c, 0x4c, 0x64, 0x00, 0x08, 0x08, 0x3e, 0x77, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x41, 0x41, 0x77, 0x3e, 0x08, 0x08, 0x00, 0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x70, 0x78, 0x4c, 0x46, 0x4c, 0x78, 0x70 }; const uint8_t ucBigFont[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0xfc, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0xfc, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0xf0, 0xf0, 0xc3, 0xc3, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x3f, 0x3f, 0x3f, 0x3f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0xff, 0xff, 0xc3, 0xc3, 0xff, 0xff, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x3f, 0x3f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0xcc, 0xcc, 0xff, 0xff, 0x3f, 0x3f, 0x3f, 0x3f, 0xff, 0xff, 0xcc, 0xcc, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x3f, 0x3f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x0f, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x3c, 0x3c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0x3c, 0x3c, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x3f, 0x3f, 0xf3, 0xf3, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x3c, 0x3c, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf3, 0xf3, 0x3f, 0x3f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3f, 0x3f, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x3c, 0x3c, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0xc3, 0xc3, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0xc3, 0xc3, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0xff, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x3f, 0x3f, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf0, 0xf0, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf0, 0xf0, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0f, 0x0f, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0xff, 0xff, 0xc3, 0xc3, 0x03, 0x03, 0x03, 0x03, 0x3f, 0x3f, 0x3c, 0x3c, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x03, 0x03, 0x03, 0x03, 0x0f, 0x0f, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0x3f, 0xff, 0xff, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xfc, 0xfc, 0xc0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0xff, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xff, 0xff, 0x0f, 0x0f, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x0f, 0x0f, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x3f, 0x3f, 0xfc, 0xfc, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xfc, 0xfc, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc3, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xcf, 0xcf, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xcf, 0xcf, 0xcf, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xcf, 0xcf, 0xcf, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x3c, 0x3c, 0xff, 0xff, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0xff, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x3c, 0x3c, 0x30, 0x30, 0xf0, 0xf0, 0xc3, 0xc3, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xfc, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x0f, 0x0f, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xff, 0xff, 0x3f, 0x3f, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x03, 0x03, 0xc3, 0xc3, 0xf0, 0xf0, 0x3c, 0x3c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x0f, 0x0f, 0x0c, 0x0c, 0x0f, 0x0f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0xff, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 5x7 font (in 6x8 cell) const uint8_t ucSmallFont[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x5f, 0x06, 0x00, 0x07, 0x03, 0x00, 0x07, 0x03, 0x24, 0x7e, 0x24, 0x7e, 0x24, 0x24, 0x2b, 0x6a, 0x12, 0x00, 0x63, 0x13, 0x08, 0x64, 0x63, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x07, 0x03, 0x00, 0x00, 0x00, 0x3e, 0x41, 0x00, 0x00, 0x00, 0x41, 0x3e, 0x00, 0x00, 0x08, 0x3e, 0x1c, 0x3e, 0x08, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0xe0, 0x60, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x60, 0x60, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x51, 0x49, 0x45, 0x3e, 0x00, 0x42, 0x7f, 0x40, 0x00, 0x62, 0x51, 0x49, 0x49, 0x46, 0x22, 0x49, 0x49, 0x49, 0x36, 0x18, 0x14, 0x12, 0x7f, 0x10, 0x2f, 0x49, 0x49, 0x49, 0x31, 0x3c, 0x4a, 0x49, 0x49, 0x30, 0x01, 0x71, 0x09, 0x05, 0x03, 0x36, 0x49, 0x49, 0x49, 0x36, 0x06, 0x49, 0x49, 0x29, 0x1e, 0x00, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0xec, 0x6c, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3e, 0x41, 0x5d, 0x55, 0x1e, 0x7e, 0x11, 0x11, 0x11, 0x7e, 0x7f, 0x49, 0x49, 0x49, 0x36, 0x3e, 0x41, 0x41, 0x41, 0x22, 0x7f, 0x41, 0x41, 0x41, 0x3e, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x7f, 0x09, 0x09, 0x09, 0x01, 0x3e, 0x41, 0x49, 0x49, 0x7a, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x41, 0x7f, 0x41, 0x00, 0x30, 0x40, 0x40, 0x40, 0x3f, 0x7f, 0x08, 0x14, 0x22, 0x41, 0x7f, 0x40, 0x40, 0x40, 0x40, 0x7f, 0x02, 0x04, 0x02, 0x7f, 0x7f, 0x02, 0x04, 0x08, 0x7f, 0x3e, 0x41, 0x41, 0x41, 0x3e, 0x7f, 0x09, 0x09, 0x09, 0x06, 0x3e, 0x41, 0x51, 0x21, 0x5e, 0x7f, 0x09, 0x09, 0x19, 0x66, 0x26, 0x49, 0x49, 0x49, 0x32, 0x01, 0x01, 0x7f, 0x01, 0x01, 0x3f, 0x40, 0x40, 0x40, 0x3f, 0x1f, 0x20, 0x40, 0x20, 0x1f, 0x3f, 0x40, 0x3c, 0x40, 0x3f, 0x63, 0x14, 0x08, 0x14, 0x63, 0x07, 0x08, 0x70, 0x08, 0x07, 0x71, 0x49, 0x45, 0x43, 0x00, 0x00, 0x7f, 0x41, 0x41, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x7f, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x03, 0x07, 0x00, 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, 0x7f, 0x44, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x44, 0x7f, 0x38, 0x54, 0x54, 0x54, 0x08, 0x08, 0x7e, 0x09, 0x09, 0x00, 0x18, 0xa4, 0xa4, 0xa4, 0x7c, 0x7f, 0x04, 0x04, 0x78, 0x00, 0x00, 0x00, 0x7d, 0x40, 0x00, 0x40, 0x80, 0x84, 0x7d, 0x00, 0x7f, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x7f, 0x40, 0x00, 0x7c, 0x04, 0x18, 0x04, 0x78, 0x7c, 0x04, 0x04, 0x78, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0xfc, 0x44, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0xfc, 0x44, 0x78, 0x44, 0x04, 0x08, 0x08, 0x54, 0x54, 0x54, 0x20, 0x04, 0x3e, 0x44, 0x24, 0x00, 0x3c, 0x40, 0x20, 0x7c, 0x00, 0x1c, 0x20, 0x40, 0x20, 0x1c, 0x3c, 0x60, 0x30, 0x60, 0x3c, 0x6c, 0x10, 0x10, 0x6c, 0x00, 0x9c, 0xa0, 0x60, 0x3c, 0x00, 0x64, 0x54, 0x54, 0x4c, 0x00, 0x08, 0x3e, 0x41, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x41, 0x3e, 0x08, 0x02, 0x01, 0x02, 0x01, 0x00, 0x3c, 0x26, 0x23, 0x26, 0x3c }; // Initialization sequences const unsigned char oled128_initbuf[] = { 0x00, 0xae, 0xdc, 0x00, 0x81, 0x40, 0xa1, 0xc8, 0xa8, 0x7f, 0xd5, 0x50, 0xd9, 0x22, 0xdb, 0x35, 0xb0, 0xda, 0x12, 0xa4, 0xa6, 0xaf }; const unsigned char oled64_initbuf[] = { 0x00, 0xae, 0xa8, 0x3f, 0xd3, 0x00, 0x40, 0xa1, 0xc8, 0xda, 0x12, 0x81, 0xff, 0xa4, 0xa6, 0xd5, 0x80, 0x8d, 0x14, 0xaf, 0x20, 0x02 }; const unsigned char oled32_initbuf[] = { 0x00, 0xae, 0xd5, 0x80, 0xa8, 0x1f, 0xd3, 0x00, 0x40, 0x8d, 0x14, 0xa1, 0xc8, 0xda, 0x02, 0x81, 0x7f, 0xd9, 0xf1, 0xdb, 0x40, 0xa4, 0xa6, 0xaf }; const unsigned char oled72_initbuf[] = { 0x00, 0xae, 0xa8, 0x3f, 0xd3, 0x00, 0x40, 0xa1, 0xc8, 0xda, 0x12, 0x81, 0xff, 0xad, 0x30, 0xd9, 0xf1, 0xa4, 0xa6, 0xd5, 0x80, 0x8d, 0x14, 0xaf, 0x20, 0x02 }; // some globals static void __oledWriteCommand(SSOLED *pOLED, unsigned char c); void __InvertBytes(uint8_t *pData, uint8_t bLen); // wrapper/adapter functions to make the code work on Linux static uint8_t pgm_read_byte(uint8_t *ptr) { return *ptr; } static int16_t pgm_read_word(uint8_t *ptr) { return ptr[0] + (ptr[1] << 8); } static void _I2CWrite(SSOLED *pOLED, unsigned char *pData, int iLen) { I2CWrite(&pOLED->bbi2c, pOLED->oled_addr, pData, iLen); } /* _I2CWrite() */ // // Initializes the OLED controller into "page mode" // int __oledInit(SSOLED *pOLED, int bInvert, int32_t iSpeed) { unsigned char uc[4]; int rc = OLED_NOT_FOUND; int iAddr = pOLED->oled_addr; int bFlip = (int) pOLED->oled_flip; int iType = pOLED->oled_type; pOLED->ucScreen = NULL; // reset backbuffer; user must provide one later pOLED->oled_wrap = 0; // default - disable text wrap I2CInit(&pOLED->bbi2c, iSpeed); // on Linux, SDA = bus number, SCL = device address // find the device address if requested if (iAddr == -1 || iAddr == 0 || iAddr == 0xff) // find it { I2CTest(&pOLED->bbi2c, 0x3c); if (I2CTest(&pOLED->bbi2c, 0x3c)) pOLED->oled_addr = 0x3c; else if (I2CTest(&pOLED->bbi2c, 0x3d)) pOLED->oled_addr = 0x3d; else return rc; // no display found! } else { pOLED->oled_addr = iAddr; I2CTest(&pOLED->bbi2c, iAddr); if (!I2CTest(&pOLED->bbi2c, iAddr)) return rc; // no display found } // Detect the display controller (SSD1306, SH1107 or SH1106) uint8_t u = 0; I2CReadRegister(&pOLED->bbi2c, pOLED->oled_addr, 0x00, &u, 1); // read the status register u &= 0x0f; // mask off power on/off bit if (u == 0x7 || u == 0xf) // SH1107 { pOLED->oled_type = OLED_128x128; rc = OLED_SH1107_3C; bFlip = !bFlip; // SH1107 seems to have this reversed from the usual direction } else if (u == 0x8) // SH1106 { rc = OLED_SH1106_3C; pOLED->oled_type = OLED_132x64; // needs to be treated a little differently } else if (u == 3 || u == 6) // 6=128x64 display, 3=smaller { rc = OLED_SSD1306_3C; } if (pOLED->oled_addr == 0x3d) rc++; // return the '3D' version of the type if (iType == OLED_128x32 || iType == OLED_96x16) _I2CWrite(pOLED, (unsigned char *) oled32_initbuf, sizeof(oled32_initbuf)); else if (iType == OLED_128x128) _I2CWrite(pOLED, (unsigned char *) oled128_initbuf, sizeof(oled128_initbuf)); else if (iType == OLED_72x40) _I2CWrite(pOLED, (unsigned char *) oled72_initbuf, sizeof(oled72_initbuf)); else // 132x64, 128x64 and 64x32 _I2CWrite(pOLED, (unsigned char *) oled64_initbuf, sizeof(oled64_initbuf)); if (bInvert) { uc[0] = 0; // command uc[1] = 0xa7; // invert command _I2CWrite(pOLED, uc, 2); } if (bFlip) // rotate display 180 { uc[0] = 0; // command uc[1] = 0xa0; _I2CWrite(pOLED, uc, 2); uc[1] = 0xc0; _I2CWrite(pOLED, uc, 2); } pOLED->oled_x = 128; // assume 128x64 pOLED->oled_y = 64; if (iType == OLED_96x16) { pOLED->oled_x = 96; pOLED->oled_y = 16; } else if (iType == OLED_128x32) pOLED->oled_y = 32; else if (iType == OLED_128x128) pOLED->oled_y = 128; else if (iType == OLED_64x32) { pOLED->oled_x = 64; pOLED->oled_y = 32; } else if (iType == OLED_72x40) { pOLED->oled_x = 72; pOLED->oled_y = 40; } return rc; } /* oledInit() */ // // Sends a command to turn on or off the OLED display // void __oledPower(SSOLED *pOLED, uint8_t bOn) { if (bOn) __oledWriteCommand(pOLED, 0xaf); // turn on OLED else __oledWriteCommand(pOLED, 0xae); // turn off OLED } /* oledPower() */ // Send a single byte command to the OLED controller static void __oledWriteCommand(SSOLED *pOLED, unsigned char c) { unsigned char buf[2]; buf[0] = 0x00; // command introducer buf[1] = c; _I2CWrite(pOLED, buf, 2); } /* oledWriteCommand() */ static void __oledWriteCommand2(SSOLED *pOLED, unsigned char c, unsigned char d) { unsigned char buf[3]; buf[0] = 0x00; buf[1] = c; buf[2] = d; _I2CWrite(pOLED, buf, 3); } /* oledWriteCommand2() */ // // Sets the brightness (0=off, 255=brightest) // void __oledSetContrast(SSOLED *pOLED, unsigned char ucContrast) { __oledWriteCommand2(pOLED, 0x81, ucContrast); } /* oledSetContrast() */ // // Scroll the internal buffer by 1 scanline (up/down) // width is in pixels, lines is group of 8 rows // int __oledScrollBuffer(SSOLED *pOLED, int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp) { uint8_t b, *s; int col, row; if (iStartCol < 0 || iStartCol > 127 || iEndCol < 0 || iEndCol > 127 || iStartCol > iEndCol) // invalid return -1; if (iStartRow < 0 || iStartRow > 7 || iEndRow < 0 || iEndRow > 7 || iStartRow > iEndRow) return -1; if (bUp) { for (row = iStartRow; row <= iEndRow; row++) { s = &pOLED->ucScreen[(row * 128) + iStartCol]; for (col = iStartCol; col <= iEndCol; col++) { b = *s; b >>= 1; // scroll pixels 'up' if (row < iEndRow) b |= (s[128] << 7); // capture pixel of row below, except for last row *s++ = b; } // for col } // for row } // up else // down { for (row = iEndRow; row >= iStartRow; row--) { s = &pOLED->ucScreen[(row * 128) + iStartCol]; for (col = iStartCol; col <= iEndCol; col++) { b = *s; b <<= 1; // scroll down if (row > iStartRow) b |= (s[-128] >> 7); // capture pixel of row above *s++ = b; } // for col } // for row } return 0; } /* oledScrollBuffer() */ // // Send commands to position the "cursor" (aka memory write address) // to the given row and column // static void __oledSetPosition(SSOLED *pOLED, int x, int y, int bRender) { unsigned char buf[4]; pOLED->iScreenOffset = (y * 128) + x; if (!bRender) return; // don't send the commands to the OLED if we're not rendering the graphics now if (pOLED->oled_type == OLED_64x32) // visible display starts at column 32, row 4 { x += 32; // display is centered in VRAM, so this is always true if (pOLED->oled_flip == 0) // non-flipped display starts from line 4 y += 4; } else if (pOLED->oled_type == OLED_132x64) // SH1106 has 128 pixels centered in 132 { x += 2; } else if (pOLED->oled_type == OLED_96x16) // visible display starts at line 2 { // mapping is a bit strange on the 96x16 OLED if (pOLED->oled_flip) x += 32; else y += 2; } else if (pOLED->oled_type == OLED_72x40) // starts at x=28,y=3 { x += 28; if (!pOLED->oled_flip) { y += 3; } } buf[0] = 0x00; // command introducer buf[1] = 0xb0 | y; // set page to Y buf[2] = x & 0xf; // lower column address buf[3] = 0x10 | (x >> 4); // upper column addr _I2CWrite(pOLED, buf, 4); } /* oledSetPosition() */ // // Write a block of pixel data to the OLED // Length can be anything from 1 to 1024 (whole display) // static void __oledWriteDataBlock(SSOLED *pOLED, unsigned char *ucBuf, int iLen, int bRender) { unsigned char ucTemp[129]; ucTemp[0] = 0x40; // data command // Copying the data has the benefit in SPI mode of not letting // the original data get overwritten by the SPI.transfer() function if (bRender) { memcpy(&ucTemp[1], ucBuf, iLen); _I2CWrite(pOLED, ucTemp, iLen + 1); } // Keep a copy in local buffer if (pOLED->ucScreen) { memcpy(&pOLED->ucScreen[pOLED->iScreenOffset], ucBuf, iLen); pOLED->iScreenOffset += iLen; pOLED->iScreenOffset &= 1023; // we use a fixed stride of 128 no matter what the display size } } // // Byte operands for compressing the data // The first 2 bits are the type, followed by the counts #define OP_MASK 0xc0 #define OP_SKIPCOPY 0x00 #define OP_COPYSKIP 0x40 #define OP_REPEATSKIP 0x80 #define OP_REPEAT 0xc0 // // Write a block of flash memory to the display // void __oledWriteFlashBlock(SSOLED *pOLED, uint8_t *s, int iLen) { int j; int iWidthMask = pOLED->oled_x - 1; int iSizeMask = ((pOLED->oled_x * pOLED->oled_y) / 8) - 1; int iWidthShift = (pOLED->oled_x == 128) ? 7 : 6; // assume 128 or 64 wide uint8_t ucTemp[128]; while (((pOLED->iScreenOffset & iWidthMask) + iLen) >= pOLED->oled_x) // if it will hit the page end { j = pOLED->oled_x - (pOLED->iScreenOffset & iWidthMask); // amount we can write in one shot memcpy(ucTemp, s, j); __oledWriteDataBlock(pOLED, ucTemp, j, 1); s += j; iLen -= j; pOLED->iScreenOffset = (pOLED->iScreenOffset + j) & iSizeMask; __oledSetPosition(pOLED, pOLED->iScreenOffset & iWidthMask, (pOLED->iScreenOffset >> iWidthShift), 1); } // while it needs some help memcpy(ucTemp, s, iLen); __oledWriteDataBlock(pOLED, ucTemp, iLen, 1); pOLED->iScreenOffset = (pOLED->iScreenOffset + iLen) & iSizeMask; } /* oledWriteFlashBlock() */ // // Write a repeating byte to the display // void __oledRepeatByte(SSOLED *pOLED, uint8_t b, int iLen) { int j; int iWidthMask = pOLED->oled_x - 1; int iWidthShift = (pOLED->oled_x == 128) ? 7 : 6; // assume 128 or 64 pixels wide int iSizeMask = ((pOLED->oled_x * pOLED->oled_y) / 8) - 1; uint8_t ucTemp[128]; memset(ucTemp, b, (iLen > 128) ? 128 : iLen); while (((pOLED->iScreenOffset & iWidthMask) + iLen) >= pOLED->oled_x) // if it will hit the page end { j = pOLED->oled_x - (pOLED->iScreenOffset & iWidthMask); // amount we can write in one shot __oledWriteDataBlock(pOLED, ucTemp, j, 1); iLen -= j; pOLED->iScreenOffset = (pOLED->iScreenOffset + j) & iSizeMask; __oledSetPosition(pOLED, pOLED->iScreenOffset & iWidthMask, (pOLED->iScreenOffset >> iWidthShift), 1); } // while it needs some help __oledWriteDataBlock(pOLED, ucTemp, iLen, 1); pOLED->iScreenOffset += iLen; } /* oledRepeatByte() */ // // Play a frame of animation data // The animation data is assumed to be encoded for a full frame of the display // Given the pointer to the start of the compressed data, // it returns the pointer to the start of the next frame // Frame rate control is up to the calling program to manage // When it finishes the last frame, it will start again from the beginning // uint8_t * __oledPlayAnimFrame(SSOLED *pOLED, uint8_t *pAnimation, uint8_t *pCurrent, int iLen) { uint8_t *s; int i, j; unsigned char b, bCode; int iBufferSize = (pOLED->oled_x * pOLED->oled_y) / 8; // size in bytes of the display devce int iWidthMask, iWidthShift; iWidthMask = pOLED->oled_x - 1; iWidthShift = (pOLED->oled_x == 128) ? 7 : 6; // 128 or 64 pixels wide if (pCurrent == NULL || pCurrent > pAnimation + iLen) return NULL; // invalid starting point s = (uint8_t *) pCurrent; // start of animation data i = 0; __oledSetPosition(pOLED, 0, 0, 1); while (i < iBufferSize) // run one frame { bCode = pgm_read_byte(s++); switch (bCode & OP_MASK) // different compression types { case OP_SKIPCOPY: // skip/copy if (bCode == OP_SKIPCOPY) // big skip { b = pgm_read_byte(s++); i += b + 1; __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } else // skip/copy { if (bCode & 0x38) { i += ((bCode & 0x38) >> 3); // skip amount __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } if (bCode & 7) { __oledWriteFlashBlock(pOLED, s, bCode & 7); s += (bCode & 7); i += bCode & 7; } } break; case OP_COPYSKIP: // copy/skip if (bCode == OP_COPYSKIP) // big copy { b = pgm_read_byte(s++); j = b + 1; __oledWriteFlashBlock(pOLED, s, j); s += j; i += j; } else { j = ((bCode & 0x38) >> 3); if (j) { __oledWriteFlashBlock(pOLED, s, j); s += j; i += j; } if (bCode & 7) { i += (bCode & 7); // skip __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } } break; case OP_REPEATSKIP: // repeat/skip j = (bCode & 0x38) >> 3; // repeat count b = pgm_read_byte(s++); __oledRepeatByte(pOLED, b, j); i += j; if (bCode & 7) { i += (bCode & 7); // skip amount __oledSetPosition(pOLED, i & iWidthMask, (i >> iWidthShift), 1); } break; case OP_REPEAT: j = (bCode & 0x3f) + 1; b = pgm_read_byte(s++); __oledRepeatByte(pOLED, b, j); i += j; break; } // switch on code type } // while rendering a frame if (s >= pAnimation + iLen) // we've hit the end, restart from the beginning s = pAnimation; return s; // return pointer to start of next frame } /* oledPlayAnimFrame() */ // // Draw a sprite of any size in any position // If it goes beyond the left/right or top/bottom edges // it's trimmed to show the valid parts // This function requires a back buffer to be defined // The priority color (0 or 1) determines which color is painted // when a 1 is encountered in the source image. // void __oledDrawSprite(SSOLED *pOLED, uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority) { int tx, ty, dx, dy, iStartX; uint8_t *s, *d, uc, pix, ucSrcMask, ucDstMask; if (x + cx < 0 || y + cy < 0 || x >= pOLED->oled_x || y >= pOLED->oled_y || pOLED->ucScreen == NULL) return; // no backbuffer or out of bounds dy = y; // destination y if (y < 0) // skip the invisible parts { cy += y; y = -y; pSprite += (y * iPitch); dy = 0; } if (y + cy > pOLED->oled_y) cy = pOLED->oled_y - y; iStartX = 0; dx = x; if (x < 0) { cx += x; x = -x; iStartX = x; dx = 0; } if (x + cx > pOLED->oled_x) cx = pOLED->oled_x - x; for (ty = 0; ty < cy; ty++) { s = &pSprite[iStartX >> 3]; d = &pOLED->ucScreen[(dy >> 3) * pOLED->oled_x + dx]; ucSrcMask = 0x80 >> (iStartX & 7); pix = *s++; ucDstMask = 1 << (dy & 7); if (iPriority) // priority color is 1 { for (tx = 0; tx < cx; tx++) { uc = d[0]; if (pix & ucSrcMask) // set pixel in source, set it in dest d[0] = (uc | ucDstMask); d++; // next pixel column ucSrcMask >>= 1; if (ucSrcMask == 0) // read next byte { ucSrcMask = 0x80; pix = *s++; } } // for tx } // priorty color 1 else { for (tx = 0; tx < cx; tx++) { uc = d[0]; if (pix & ucSrcMask) // clr pixel in source, clr it in dest d[0] = (uc & ~ucDstMask); d++; // next pixel column ucSrcMask >>= 1; if (ucSrcMask == 0) // read next byte { ucSrcMask = 0x80; pix = *s++; } } // for tx } // priority color 0 dy++; pSprite += iPitch; } // for ty } /* oledDrawSprite() */ // // Draw a 16x16 tile in any of 4 rotated positions // Assumes input image is laid out like "normal" graphics with // the MSB on the left and 2 bytes per line // On AVR, the source image is assumed to be in FLASH memory // The function can draw the tile on byte boundaries, so the x value // can be from 0 to 112 and y can be from 0 to 6 // void __oledDrawTile(SSOLED *pOLED, const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender) { uint8_t ucTemp[32]; // prepare LCD data here uint8_t i, j, k, iOffset, ucMask, uc, ucPixels; uint8_t bFlipX = 0, bFlipY = 0; if (x < 0 || y < 0 || y > 6 || x > 112) return; // out of bounds if (pTile == NULL) return; // bad pointer; really? :( if (iRotation == ANGLE_180 || iRotation == ANGLE_270 || iRotation == ANGLE_FLIPX) bFlipX = 1; if (iRotation == ANGLE_180 || iRotation == ANGLE_270 || iRotation == ANGLE_FLIPY) bFlipY = 1; memset(ucTemp, 0, sizeof(ucTemp)); // we only set white pixels, so start from black if (iRotation == ANGLE_0 || iRotation == ANGLE_180 || iRotation == ANGLE_FLIPX || iRotation == ANGLE_FLIPY) { for (j = 0; j < 16; j++) // y { for (i = 0; i < 16; i += 8) // x { ucPixels = pgm_read_byte((uint8_t*) pTile++); ucMask = 0x80; // MSB is the first source pixel for (k = 0; k < 8; k++) { if (ucPixels & ucMask) // translate the pixel { if (bFlipY) uc = 0x80 >> (j & 7); else uc = 1 << (j & 7); iOffset = i + k; if (bFlipX) iOffset = 15 - iOffset; iOffset += (j & 8) << 1; // top/bottom half of output if (bFlipY) iOffset ^= 16; ucTemp[iOffset] |= uc; } ucMask >>= 1; } // for k } // for i } // for j } else // rotated 90/270 { for (j = 0; j < 16; j++) // y { for (i = 0; i < 16; i += 8) // x { ucPixels = pgm_read_byte((uint8_t*) pTile++); ucMask = 0x80; // MSB is the first source pixel for (k = 0; k < 8; k++) { if (ucPixels & ucMask) // translate the pixel { if (bFlipY) uc = 0x80 >> k; else uc = 1 << k; iOffset = 15 - j; if (bFlipX) iOffset = 15 - iOffset; iOffset += i << 1; // top/bottom half of output if (bFlipY) iOffset ^= 16; ucTemp[iOffset] |= uc; } ucMask >>= 1; } // for k } // for i } // for j } if (bInvert) __InvertBytes(ucTemp, 32); // Send the data to the display __oledSetPosition(pOLED, x, y, bRender); __oledWriteDataBlock(pOLED, ucTemp, 16, bRender); // top half __oledSetPosition(pOLED, x, y + 1, bRender); __oledWriteDataBlock(pOLED, &ucTemp[16], 16, bRender); // bottom half } /* oledDrawTile() */ // Set (or clear) an individual pixel // The local copy of the frame buffer is used to avoid // reading data from the display controller int __oledSetPixel(SSOLED *pOLED, int x, int y, unsigned char ucColor, int bRender) { int i; unsigned char uc, ucOld; i = ((y >> 3) * 128) + x; if (i < 0 || i > 1023) // off the screen return -1; __oledSetPosition(pOLED, x, y >> 3, bRender); if (pOLED->ucScreen) uc = ucOld = pOLED->ucScreen[i]; else if (pOLED->oled_type == OLED_132x64 || pOLED->oled_type == OLED_128x128) // SH1106/SH1107 can read data { uint8_t ucTemp[3]; ucTemp[0] = 0x80; // one command ucTemp[1] = 0xE0; // read_modify_write ucTemp[2] = 0xC0; // one data _I2CWrite(pOLED, ucTemp, 3); // read a dummy byte followed by the data byte we want I2CRead(&pOLED->bbi2c, pOLED->oled_addr, ucTemp, 2); uc = ucOld = ucTemp[1]; // first byte is garbage } else uc = ucOld = 0; uc &= ~(0x1 << (y & 7)); if (ucColor) { uc |= (0x1 << (y & 7)); } if (uc != ucOld) // pixel changed { // oledSetPosition(x, y>>3); if (pOLED->ucScreen) { __oledWriteDataBlock(pOLED, &uc, 1, bRender); pOLED->ucScreen[i] = uc; } else if (pOLED->oled_type == OLED_132x64 || pOLED->oled_type == OLED_128x128) // end the read_modify_write operation { uint8_t ucTemp[4]; ucTemp[0] = 0xc0; // one data ucTemp[1] = uc; // actual data ucTemp[2] = 0x80; // one command ucTemp[3] = 0xEE; // end read_modify_write operation _I2CWrite(pOLED, ucTemp, 4); } } return 0; } /* oledSetPixel() */ // // Invert font data // void __InvertBytes(uint8_t *pData, uint8_t bLen) { uint8_t i; for (i = 0; i < bLen; i++) { *pData = ~(*pData); pData++; } } /* InvertBytes() */ // // Load a 128x64 1-bpp Windows bitmap // Pass the pointer to the beginning of the BMP file // First pass version assumes a full screen bitmap // int __oledLoadBMP(SSOLED *pOLED, uint8_t *pBMP, int bInvert, int bRender) { int16_t i16; int iOffBits, q, y, j; // offset to bitmap data int iPitch; uint8_t x, z, b, *s; uint8_t dst_mask; uint8_t ucTemp[16]; // process 16 bytes at a time uint8_t bFlipped = false; i16 = pgm_read_word(pBMP); if (i16 != 0x4d42) // must start with 'BM' return -1; // not a BMP file i16 = pgm_read_word(pBMP + 18); if (i16 != 128) // must be 128 pixels wide return -1; i16 = pgm_read_word(pBMP + 22); if (i16 != 64 && i16 != -64) // must be 64 pixels tall return -1; if (i16 == 64) // BMP is flipped vertically (typical) bFlipped = true; i16 = pgm_read_word(pBMP + 28); if (i16 != 1) // must be 1 bit per pixel return -1; iOffBits = pgm_read_word(pBMP + 10); iPitch = 16; if (bFlipped) { iPitch = -16; iOffBits += (63 * 16); // start from bottom } // rotate the data and send it to the display for (y = 0; y < 8; y++) // 8 lines of 8 pixels { __oledSetPosition(pOLED, 0, y, bRender); for (j = 0; j < 8; j++) // do 8 sections of 16 columns { s = &pBMP[iOffBits + (j * 2) + (y * iPitch * 8)]; // source line memset(ucTemp, 0, 16); // start with all black for (x = 0; x < 16; x += 8) // do each block of 16x8 pixels { dst_mask = 1; for (q = 0; q < 8; q++) // gather 8 rows { b = pgm_read_byte(s + (q * iPitch)); for (z = 0; z < 8; z++) // gather up the 8 bits of this column { if (b & 0x80) ucTemp[x + z] |= dst_mask; b <<= 1; } // for z dst_mask <<= 1; } // for q s++; // next source byte } // for x if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, ucTemp, 16, bRender); } // for j } // for y return 0; } /* oledLoadBMP() */ // // Set the current cursor position // The column represents the pixel column (0-127) // The row represents the text row (0-7) // void __oledSetCursor(SSOLED *pOLED, int x, int y) { pOLED->iCursorX = x; pOLED->iCursorY = y; } /* oledSetCursor() */ // // Turn text wrap on or off for the oldWriteString() function // void __oledSetTextWrap(SSOLED *pOLED, int bWrap) { pOLED->oled_wrap = bWrap; } /* oledSetTextWrap() */ // // Draw a string of normal (8x8), small (6x8) or large (16x32) characters // At the given col+row // int __oledWriteString(SSOLED *pOLED, int iScroll, int x, int y, char *szMsg, int iSize, int bInvert, int bRender) { int i, iFontOff, iLen, iFontSkip; unsigned char c, *s, ucTemp[40]; if (x == -1 || y == -1) // use the cursor position { x = pOLED->iCursorX; y = pOLED->iCursorY; } else { pOLED->iCursorX = x; pOLED->iCursorY = y; // set the new cursor position } if (pOLED->iCursorX >= pOLED->oled_x || pOLED->iCursorY >= pOLED->oled_y / 8) return -1; // can't draw off the display __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); if (iSize == FONT_8x8) // 8x8 font { i = 0; iFontSkip = iScroll & 7; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && szMsg[i] != 0 && pOLED->iCursorY < pOLED->oled_y / 8) { if (iScroll < 8) // only display visible characters { c = (unsigned char) szMsg[i]; iFontOff = (int) (c - 32) * 7; // we can't directly use the pointer to FLASH memory, so copy to a local buffer ucTemp[0] = 0; memcpy(&ucTemp[1], &ucFont[iFontOff], 7); if (bInvert) __InvertBytes(ucTemp, 8); // oledCachedWrite(ucTemp, 8); iLen = 8 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 7 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY++; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } iFontSkip = 0; } iScroll -= 8; i++; } // while // oledCachedFlush(); // write any remaining data return 0; } // 8x8 #ifndef __AVR__ else if (iSize == FONT_16x32) // 16x32 font { i = 0; iFontSkip = iScroll & 15; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) - 3 && szMsg[i] != 0) { if (iScroll < 16) // if characters are visible { s = (unsigned char *) &ucBigFont[(unsigned char) (szMsg[i] - 32) * 64]; iLen = 16 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; // we can't directly use the pointer to FLASH memory, so copy to a local buffer __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); memcpy(ucTemp, s, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 1, bRender); memcpy(ucTemp, s + 16, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern if (pOLED->iCursorY <= 5) { __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 2, bRender); memcpy(ucTemp, s + 32, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern } if (pOLED->iCursorY <= 4) { __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 3, bRender); memcpy(ucTemp, s + 48, 16); if (bInvert) __InvertBytes(ucTemp, 16); __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern } pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 15 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY += 4; } iFontSkip = 0; } // if character visible from scrolling iScroll -= 16; i++; } // while return 0; } // 16x32 #endif // !__AVR__ else if (iSize == FONT_12x16) // 6x8 stretched to 12x16 { i = 0; iFontSkip = iScroll % 12; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) - 1 && szMsg[i] != 0) { // stretch the 'normal' font instead of using the big font if (iScroll < 12) // if characters are visible { int tx, ty; c = szMsg[i] - 32; unsigned char uc1, uc2, ucMask, *pDest; s = (unsigned char *) &ucSmallFont[(int) c * 5]; ucTemp[0] = 0; // first column is blank memcpy(&ucTemp[1], s, 6); if (bInvert) __InvertBytes(ucTemp, 6); // Stretch the font to double width + double height memset(&ucTemp[6], 0, 24); // write 24 new bytes for (tx = 0; tx < 6; tx++) { ucMask = 3; pDest = &ucTemp[6 + tx * 2]; uc1 = uc2 = 0; c = ucTemp[tx]; for (ty = 0; ty < 4; ty++) { if (c & (1 << ty)) // a bit is set uc1 |= ucMask; if (c & (1 << (ty + 4))) uc2 |= ucMask; ucMask <<= 2; } pDest[0] = uc1; pDest[1] = uc1; // double width pDest[12] = uc2; pDest[13] = uc2; } // smooth the diagonal lines for (tx = 0; tx < 5; tx++) { uint8_t c0, c1, ucMask2; c0 = ucTemp[tx]; c1 = ucTemp[tx + 1]; pDest = &ucTemp[6 + tx * 2]; ucMask = 1; ucMask2 = 2; for (ty = 0; ty < 7; ty++) { if (((c0 & ucMask) && !(c1 & ucMask) && !(c0 & ucMask2) && (c1 & ucMask2)) || (!(c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask2) && !(c1 & ucMask2))) { if (ty < 3) // top half { pDest[1] |= (1 << ((ty * 2) + 1)); pDest[2] |= (1 << ((ty * 2) + 1)); pDest[1] |= (1 << ((ty + 1) * 2)); pDest[2] |= (1 << ((ty + 1) * 2)); } else if (ty == 3) // on the border { pDest[1] |= 0x80; pDest[2] |= 0x80; pDest[13] |= 1; pDest[14] |= 1; } else // bottom half { pDest[13] |= (1 << (2 * (ty - 4) + 1)); pDest[14] |= (1 << (2 * (ty - 4) + 1)); pDest[13] |= (1 << ((ty - 3) * 2)); pDest[14] |= (1 << ((ty - 3) * 2)); } } else if (!(c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask2) && !(c1 & ucMask2)) { if (ty < 4) // top half { pDest[1] |= (1 << ((ty * 2) + 1)); pDest[2] |= (1 << ((ty + 1) * 2)); } else { pDest[13] |= (1 << (2 * (ty - 4) + 1)); pDest[14] |= (1 << ((ty - 3) * 2)); } } ucMask <<= 1; ucMask2 <<= 1; } } iLen = 12 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); __oledWriteDataBlock(pOLED, &ucTemp[6 + iFontSkip], iLen, bRender); __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 1, bRender); __oledWriteDataBlock(pOLED, &ucTemp[18 + iFontSkip], iLen, bRender); pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 11 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY += 2; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } iFontSkip = 0; } // if characters are visible iScroll -= 12; i++; } // while return 0; } // 12x16 else if (iSize == FONT_16x16) // 8x8 stretched to 16x16 { i = 0; iFontSkip = iScroll & 15; // number of columns to initially skip while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) - 1 && szMsg[i] != 0) { // stretch the 'normal' font instead of using the big font if (iScroll < 16) // if characters are visible { int tx, ty; c = szMsg[i] - 32; unsigned char uc1, uc2, ucMask, *pDest; s = (unsigned char *) &ucFont[(int) c * 7]; ucTemp[0] = 0; memcpy(&ucTemp[1], s, 7); if (bInvert) __InvertBytes(ucTemp, 8); // Stretch the font to double width + double height memset(&ucTemp[8], 0, 32); // write 32 new bytes for (tx = 0; tx < 8; tx++) { ucMask = 3; pDest = &ucTemp[8 + tx * 2]; uc1 = uc2 = 0; c = ucTemp[tx]; for (ty = 0; ty < 4; ty++) { if (c & (1 << ty)) // a bit is set uc1 |= ucMask; if (c & (1 << (ty + 4))) uc2 |= ucMask; ucMask <<= 2; } pDest[0] = uc1; pDest[1] = uc1; // double width pDest[16] = uc2; pDest[17] = uc2; } iLen = 16 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); __oledWriteDataBlock(pOLED, &ucTemp[8 + iFontSkip], iLen, bRender); __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY + 1, bRender); __oledWriteDataBlock(pOLED, &ucTemp[24 + iFontSkip], iLen, bRender); pOLED->iCursorX += iLen; if (pOLED->iCursorX >= pOLED->oled_x - 15 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY += 2; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } iFontSkip = 0; } // if characters are visible iScroll -= 16; i++; } // while return 0; } // 16x16 else if (iSize == FONT_6x8) // 6x8 font { i = 0; iFontSkip = iScroll % 6; while (pOLED->iCursorX < pOLED->oled_x && pOLED->iCursorY < (pOLED->oled_y / 8) && szMsg[i] != 0) { if (iScroll < 6) // if characters are visible { c = szMsg[i] - 32; // we can't directly use the pointer to FLASH memory, so copy to a local buffer ucTemp[0] = 0; memcpy(&ucTemp[1], &ucSmallFont[(int) c * 5], 5); if (bInvert) __InvertBytes(ucTemp, 6); iLen = 6 - iFontSkip; if (pOLED->iCursorX + iLen > pOLED->oled_x) // clip right edge iLen = pOLED->oled_x - pOLED->iCursorX; __oledWriteDataBlock(pOLED, &ucTemp[iFontSkip], iLen, bRender); // write character pattern // oledCachedWrite(ucTemp, 6); pOLED->iCursorX += iLen; iFontSkip = 0; if (pOLED->iCursorX >= pOLED->oled_x - 5 && pOLED->oled_wrap) // word wrap enabled? { pOLED->iCursorX = 0; // start at the beginning of the next line pOLED->iCursorY++; __oledSetPosition(pOLED, pOLED->iCursorX, pOLED->iCursorY, bRender); } } // if characters are visible iScroll -= 6; i++; } // oledCachedFlush(); // write any remaining data return 0; } // 6x8 return -1; // invalid size } /* oledWriteString() */ // // Render a sprite/rectangle of pixels from a provided buffer to the display. // The row values refer to byte rows, not pixel rows due to the memory // layout of OLEDs. // returns 0 for success, -1 for invalid parameter // int __oledDrawGFX(SSOLED *pOLED, uint8_t *pBuffer, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch) { int y; if (iSrcCol < 0 || iSrcCol > 127 || iSrcRow < 0 || iSrcRow > 7 || iDestCol < 0 || iDestCol >= pOLED->oled_x || iDestRow < 0 || iDestRow >= (pOLED->oled_y >> 3) || iSrcPitch <= 0) return -1; // invalid for (y = iSrcRow; y < iSrcRow + iHeight; y++) { uint8_t *s = &pBuffer[(y * iSrcPitch) + iSrcCol]; __oledSetPosition(pOLED, iDestCol, iDestRow, 1); __oledWriteDataBlock(pOLED, s, iWidth, 1); pBuffer += iSrcPitch; iDestRow++; } // for y return 0; } /* oledDrawGFX() */ // // Dump a screen's worth of data directly to the display // Try to speed it up by comparing the new bytes with the existing buffer // void __oledDumpBuffer(SSOLED *pOLED, uint8_t *pBuffer) { int x, y; int iLines, iCols; uint8_t bNeedPos; uint8_t *pSrc = pOLED->ucScreen; if (pBuffer == NULL) // dump the internal buffer if none is given pBuffer = pOLED->ucScreen; if (pBuffer == NULL) return; // no backbuffer and no provided buffer iLines = pOLED->oled_y >> 3; iCols = pOLED->oled_x >> 4; for (y = 0; y < iLines; y++) { bNeedPos = 1; // start of a new line means we need to set the position too for (x = 0; x < iCols; x++) // wiring library has a 32-byte buffer, so send 16 bytes so that the data prefix (0x40) can fit { if (pOLED->ucScreen == NULL || pBuffer == pSrc || memcmp(pSrc, pBuffer, 16) != 0) // doesn't match, need to send it { if (bNeedPos) // need to reposition output cursor? { bNeedPos = 0; __oledSetPosition(pOLED, x * 16, y, 1); } __oledWriteDataBlock(pOLED, pBuffer, 16, 1); } else { bNeedPos = 1; // we're skipping a block, so next time will need to set the new position } pSrc += 16; pBuffer += 16; } // for x pSrc += (128 - pOLED->oled_x); // for narrow displays, skip to the next line pBuffer += (128 - pOLED->oled_x); } // for y } /* oledDumpBuffer() */ // // Fill the frame buffer with a byte pattern // e.g. all off (0x00) or all on (0xff) // void __oledFill(SSOLED *pOLED, unsigned char ucData, int bRender) { uint8_t x, y; uint8_t iLines, iCols; unsigned char temp[16]; iLines = pOLED->oled_y >> 3; iCols = pOLED->oled_x >> 4; memset(temp, ucData, 16); pOLED->iCursorX = pOLED->iCursorY = 0; for (y = 0; y < iLines; y++) { __oledSetPosition(pOLED, 0, y, bRender); // set to (0,Y) for (x = 0; x < iCols; x++) // wiring library has a 32-byte buffer, so send 16 bytes so that the data prefix (0x40) can fit { __oledWriteDataBlock(pOLED, temp, 16, bRender); } // for x // 72 isn't evenly divisible by 16, so fix it if (pOLED->oled_type == OLED_72x40) __oledWriteDataBlock(pOLED, temp, 8, bRender); } // for y if (pOLED->ucScreen) memset(pOLED->ucScreen, ucData, (pOLED->oled_x * pOLED->oled_y) / 8); } /* oledFill() */ // // Provide or revoke a back buffer for your OLED graphics // This allows you to manage the RAM used by ss_oled on tiny // embedded platforms like the ATmega series // Pass NULL to revoke the buffer. Make sure you provide a buffer // large enough for your display (e.g. 128x64 needs 1K - 1024 bytes) // void __oledSetBackBuffer(SSOLED *pOLED, uint8_t *pBuffer) { pOLED->ucScreen = pBuffer; } /* oledSetBackBuffer() */ void __oledDrawLine(SSOLED *pOLED, int x1, int y1, int x2, int y2, int bRender) { int temp; int dx = x2 - x1; int dy = y2 - y1; int error; uint8_t *p, *pStart, mask, bOld, bNew; int xinc, yinc; int y, x; if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || x1 >= pOLED->oled_x || x2 >= pOLED->oled_x || y1 >= pOLED->oled_y || y2 >= pOLED->oled_y) return; if (abs(dx) > abs(dy)) { // X major case if (x2 < x1) { dx = -dx; temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } y = y1; dy = (y2 - y1); error = dx >> 1; yinc = 1; if (dy < 0) { dy = -dy; yinc = -1; } p = pStart = &pOLED->ucScreen[x1 + ((y >> 3) << 7)]; // point to current spot in back buffer mask = 1 << (y & 7); // current bit offset for (x = x1; x1 <= x2; x1++) { *p++ |= mask; // set pixel and increment x pointer error -= dy; if (error < 0) { error += dx; if (yinc > 0) mask <<= 1; else mask >>= 1; if (mask == 0) // we've moved outside the current row, write the data we changed { __oledSetPosition(pOLED, x, y >> 3, bRender); __oledWriteDataBlock(pOLED, pStart, (int) (p - pStart), bRender); // write the row we changed x = x1 + 1; // we've already written the byte at x1 y1 = y + yinc; p += (yinc > 0) ? 128 : -128; pStart = p; mask = 1 << (y1 & 7); } y += yinc; } } // for x1 if (p != pStart) // some data needs to be written { __oledSetPosition(pOLED, x, y >> 3, bRender); __oledWriteDataBlock(pOLED, pStart, (int) (p - pStart), bRender); } } else { // Y major case if (y1 > y2) { dy = -dy; temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } p = &pOLED->ucScreen[x1 + ((y1 >> 3) * 128)]; // point to current spot in back buffer bOld = bNew = p[0]; // current data at that address mask = 1 << (y1 & 7); // current bit offset dx = (x2 - x1); error = dy >> 1; xinc = 1; if (dx < 0) { dx = -dx; xinc = -1; } for (x = x1; y1 <= y2; y1++) { bNew |= mask; // set the pixel error -= dx; mask <<= 1; // y1++ if (mask == 0) // we're done with this byte, write it if necessary { if (bOld != bNew) { p[0] = bNew; // save to RAM __oledSetPosition(pOLED, x, y1 >> 3, bRender); __oledWriteDataBlock(pOLED, &bNew, 1, bRender); } p += 128; // next line bOld = bNew = p[0]; mask = 1; // start at LSB again } if (error < 0) { error += dy; if (bOld != bNew) // write the last byte we modified if it changed { p[0] = bNew; // save to RAM __oledSetPosition(pOLED, x, y1 >> 3, bRender); __oledWriteDataBlock(pOLED, &bNew, 1, bRender); } p += xinc; x += xinc; bOld = bNew = p[0]; } } // for y if (bOld != bNew) // write the last byte we modified if it changed { p[0] = bNew; // save to RAM __oledSetPosition(pOLED, x, y2 >> 3, bRender); __oledWriteDataBlock(pOLED, &bNew, 1, bRender); } } // y major case } /* oledDrawLine() */ // // For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction // This function draws a single pixel and scales its position based on the x/y fraction of the ellipse // static void __DrawScaledPixel(SSOLED *pOLED, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) { uint8_t *d, ucMask; if (iXFrac != 0x10000) x = ((x * iXFrac) >> 16); if (iYFrac != 0x10000) y = ((y * iYFrac) >> 16); x += iCX; y += iCY; if (x < 0 || x >= pOLED->oled_x || y < 0 || y >= pOLED->oled_y) return; // off the screen d = &pOLED->ucScreen[((y >> 3) * 128) + x]; ucMask = 1 << (y & 7); if (ucColor) *d |= ucMask; else *d &= ~ucMask; } /* DrawScaledPixel() */ // // For drawing filled ellipses // static void __DrawScaledLine(SSOLED *pOLED, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor) { int iLen, x2; uint8_t *d, ucMask; if (iXFrac != 0x10000) x = ((x * iXFrac) >> 16); if (iYFrac != 0x10000) y = ((y * iYFrac) >> 16); iLen = x * 2; x = iCX - x; y += iCY; x2 = x + iLen; if (y < 0 || y >= pOLED->oled_y) return; // completely off the screen if (x < 0) x = 0; if (x2 >= pOLED->oled_x) x2 = pOLED->oled_x - 1; iLen = x2 - x + 1; // new length d = &pOLED->ucScreen[((y >> 3) * 128) + x]; ucMask = 1 << (y & 7); if (ucColor) // white { for (; iLen > 0; iLen--) *d++ |= ucMask; } else // black { for (; iLen > 0; iLen--) *d++ &= ~ucMask; } } /* DrawScaledLine() */ // // Draw the 8 pixels around the Bresenham circle // (scaled to make an ellipse) // static void BresenhamCircle(SSOLED *pOLED, int iCX, int iCY, int x, int y, int32_t iXFrac, int32_t iYFrac, uint8_t ucColor, uint8_t bFill) { if (bFill) // draw a filled ellipse { // for a filled ellipse, draw 4 lines instead of 8 pixels __DrawScaledLine(pOLED, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); __DrawScaledLine(pOLED, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); __DrawScaledLine(pOLED, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); __DrawScaledLine(pOLED, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); } else // draw 8 pixels around the edges { __DrawScaledPixel(pOLED, iCX, iCY, x, y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -x, y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, x, -y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -x, -y, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, y, x, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -y, x, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, y, -x, iXFrac, iYFrac, ucColor); __DrawScaledPixel(pOLED, iCX, iCY, -y, -x, iXFrac, iYFrac, ucColor); } } /* BresenhamCircle() */ // // Draw an outline or filled ellipse // void __oledEllipse(SSOLED *pOLED, int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled) { int32_t iXFrac, iYFrac; int iRadius, iDelta, x, y; if (pOLED == NULL || pOLED->ucScreen == NULL) return; // must have back buffer defined if (iRadiusX <= 0 || iRadiusY <= 0) return; // invalid radii if (iRadiusX > iRadiusY) // use X as the primary radius { iRadius = iRadiusX; iXFrac = 65536; iYFrac = (iRadiusY * 65536) / iRadiusX; } else { iRadius = iRadiusY; iXFrac = (iRadiusX * 65536) / iRadiusY; iYFrac = 65536; } iDelta = 3 - (2 * iRadius); x = 0; y = iRadius; while (x <= y) { BresenhamCircle(pOLED, iCenterX, iCenterY, x, y, iXFrac, iYFrac, ucColor, bFilled); x++; if (iDelta < 0) { iDelta += (4 * x) + 6; } else { iDelta += 4 * (x - y) + 10; y--; } } } /* oledEllipse() */ // // Draw an outline or filled rectangle // void __oledRectangle(SSOLED *pOLED, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled) { uint8_t *d, ucMask, ucMask2; int tmp, iOff; if (pOLED == NULL || pOLED->ucScreen == NULL) return; // only works with a back buffer if (x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0 || x1 >= pOLED->oled_x || y1 >= pOLED->oled_y || x2 >= pOLED->oled_x || y2 >= pOLED->oled_y) return; // invalid coordinates // Make sure that X1/Y1 is above and to the left of X2/Y2 // swap coordinates as needed to make this true if (x2 < x1) { tmp = x1; x1 = x2; x2 = tmp; } if (y2 < y1) { tmp = y1; y1 = y2; y2 = tmp; } if (bFilled) { int x, y, iMiddle; iMiddle = (y2 >> 3) - (y1 >> 3); ucMask = 0xff << (y1 & 7); if (iMiddle == 0) // top and bottom lines are in the same row ucMask &= (0xff >> (7 - (y2 & 7))); d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1]; // Draw top for (x = x1; x <= x2; x++) { if (ucColor) *d |= ucMask; else *d &= ~ucMask; d++; } if (iMiddle > 1) // need to draw middle part { ucMask = (ucColor) ? 0xff : 0x00; for (y = 1; y < iMiddle; y++) { d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1 + (y * 128)]; for (x = x1; x <= x2; x++) *d++ = ucMask; } } if (iMiddle >= 1) // need to draw bottom part { ucMask = 0xff >> (7 - (y2 & 7)); d = &pOLED->ucScreen[(y2 >> 3) * 128 + x1]; for (x = x1; x <= x2; x++) { if (ucColor) *d++ |= ucMask; else *d++ &= ~ucMask; } } } else // outline { // see if top and bottom lines are within the same byte rows d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1]; if ((y1 >> 3) == (y2 >> 3)) { ucMask2 = 0xff << (y1 & 7); // L/R end masks ucMask = 1 << (y1 & 7); ucMask |= 1 << (y2 & 7); ucMask2 &= (0xff >> (7 - (y2 & 7))); if (ucColor) { *d++ |= ucMask2; // start x1++; for (; x1 < x2; x1++) *d++ |= ucMask; if (x1 <= x2) *d++ |= ucMask2; // right edge } else { *d++ &= ~ucMask2; x1++; for (; x1 < x2; x1++) *d++ &= ~ucMask; if (x1 <= x2) *d++ &= ~ucMask2; // right edge } } else { int y; // L/R sides iOff = (x2 - x1); ucMask = 1 << (y1 & 7); for (y = y1; y <= y2; y++) { if (ucColor) { *d |= ucMask; d[iOff] |= ucMask; } else { *d &= ~ucMask; d[iOff] &= ~ucMask; } ucMask <<= 1; if (ucMask == 0) { ucMask = 1; d += 128; } } // T/B sides ucMask = 1 << (y1 & 7); ucMask2 = 1 << (y2 & 7); x1++; d = &pOLED->ucScreen[(y1 >> 3) * 128 + x1]; iOff = (y2 >> 3) - (y1 >> 3); iOff *= 128; for (; x1 < x2; x1++) { if (ucColor) { *d |= ucMask; d[iOff] |= ucMask2; } else { *d &= ~ucMask; d[iOff] &= ~ucMask2; } d++; } } } // outline } /* oledRectangle() */
include 文件夹包含的文件
bitbang_iic.h
// // Bit Bang I2C library // Copyright (c) 2018 BitBank Software, Inc. // Written by Larry Bank (bitbank@pobox.com) // Project started 10/12/2018 // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef __BITBANG_I2C__ #define __BITBANG_I2C__ #include "pico/stdlib.h" #include "hardware/i2c.h" // supported devices enum { DEVICE_UNKNOWN = 0, DEVICE_SSD1306, DEVICE_SH1106, DEVICE_VL53L0X, DEVICE_BMP180, DEVICE_BMP280, DEVICE_BME280, DEVICE_MPU6000, DEVICE_MPU9250, DEVICE_MCP9808, DEVICE_LSM6DS3, DEVICE_ADXL345, DEVICE_ADS1115, DEVICE_MAX44009, DEVICE_MAG3110, DEVICE_CCS811, DEVICE_HTS221, DEVICE_LPS25H, DEVICE_LSM9DS1, DEVICE_LM8330, DEVICE_DS3231, DEVICE_LIS3DH, DEVICE_LIS3DSH, DEVICE_INA219, DEVICE_SHT3X, DEVICE_HDC1080, DEVICE_MPU6886, DEVICE_BME680, DEVICE_AXP202, DEVICE_AXP192, DEVICE_24AAXXXE64, DEVICE_DS1307 }; #ifndef LOW #define LOW 0 #define HIGH 1 #endif typedef struct mybbi2c { uint8_t iSDA, iSCL; // pin numbers (0xff = disabled) i2c_inst_t * picoI2C; // used pico I2C } BBI2C; #ifdef __cplusplus extern "C" { #endif // // Read N bytes // int I2CRead(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen); // // Read N bytes starting at a specific I2C internal register // int I2CReadRegister(BBI2C *pI2C, uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen); // // Write I2C data // quits if a NACK is received and returns 0 // otherwise returns the number of bytes written // int I2CWrite(BBI2C *pI2C, uint8_t iAddr, uint8_t *pData, int iLen); // // Scans for I2C devices on the bus // returns a bitmap of devices which are present (128 bits = 16 bytes, LSB first) // // Test if an address responds // returns 0 if no response, 1 if it responds // uint8_t I2CTest(BBI2C *pI2C, uint8_t addr); // A set bit indicates that a device responded at that address // void I2CScan(BBI2C *pI2C, uint8_t *pMap); // // Initialize the I2C BitBang library // Pass the pin numbers used for SDA and SCL // as well as the clock rate in Hz // void I2CInit(BBI2C *pI2C, uint32_t iClock); // // Figure out what device is at that address // returns the enumerated value // int I2CDiscoverDevice(BBI2C *pI2C, uint8_t i); #ifdef __cplusplus } #endif #endif //__BITBANG_I2C__
ss_oled.h
#ifndef __SS_OLED_H__ #define __SS_OLED_H__ #include <include/bitbang_iic.h> typedef struct ssoleds { uint8_t oled_addr; // requested address or 0xff for automatic detection uint8_t oled_wrap, oled_flip, oled_type; uint8_t *ucScreen; uint8_t iCursorX, iCursorY; uint8_t oled_x, oled_y; int iScreenOffset; BBI2C bbi2c; } SSOLED; // Make the Linux library interface C instead of C++ #if defined(__cplusplus) extern "C" { #endif // 4 possible font sizes: 8x8, 16x32, 6x8, 16x16 (stretched from 8x8) enum { FONT_6x8 = 0, FONT_8x8, FONT_12x16, FONT_16x16, FONT_16x32 }; #define FONT_NORMAL FONT_8x8 #define FONT_SMALL FONT_6x8 #define FONT_LARGE FONT_16x32 #define FONT_STRETCHED FONT_16x16 // OLED type for init function enum { OLED_128x128 = 1, OLED_128x32, OLED_128x64, OLED_132x64, OLED_64x32, OLED_96x16, OLED_72x40 }; // Rotation and flip angles to draw tiles enum { ANGLE_0=0, ANGLE_90, ANGLE_180, ANGLE_270, ANGLE_FLIPX, ANGLE_FLIPY }; // Return value from oledInit() enum { OLED_NOT_FOUND = -1, // no display found OLED_SSD1306_3C, // SSD1306 found at 0x3C OLED_SSD1306_3D, // SSD1306 found at 0x3D OLED_SH1106_3C, // SH1106 found at 0x3C OLED_SH1106_3D, // SH1106 found at 0x3D OLED_SH1107_3C, // SH1107 OLED_SH1107_3D }; // // Initializes the OLED controller into "page mode" on I2C // If SDAPin and SCLPin are not -1, then bit bang I2C on those pins // Otherwise use the Wire library. // If you don't need to use a separate reset pin, set it to -1 // int __oledInit(SSOLED *pOLED, int bInvert, int32_t iSpeed); // // Provide or revoke a back buffer for your OLED graphics // This allows you to manage the RAM used by ss_oled on tiny // embedded platforms like the ATmega series // Pass NULL to revoke the buffer. Make sure you provide a buffer // large enough for your display (e.g. 128x64 needs 1K - 1024 bytes) // void __oledSetBackBuffer(SSOLED *pOLED, uint8_t *pBuffer); // // Sets the brightness (0=off, 255=brightest) // void __oledSetContrast(SSOLED *pOLED, unsigned char ucContrast); // // Load a 128x64 1-bpp Windows bitmap // Pass the pointer to the beginning of the BMP file // First pass version assumes a full screen bitmap // int __oledLoadBMP(SSOLED *pOLED, uint8_t *pBMP, int bInvert, int bRender); // // Power up/down the display // useful for low power situations // void __oledPower(SSOLED *pOLED, uint8_t bOn); // // Set the current cursor position // The column represents the pixel column (0-127) // The row represents the text row (0-7) // void __oledSetCursor(SSOLED *pOLED, int x, int y); // // Turn text wrap on or off for the oldWriteString() function // void __oledSetTextWrap(SSOLED *pOLED, int bWrap); // // Draw a string of normal (8x8), small (6x8) or large (16x32) characters // At the given col+row with the given scroll offset. The scroll offset allows you to // horizontally scroll text which does not fit on the width of the display. The offset // represents the pixels to skip when drawing the text. An offset of 0 starts at the beginning // of the text. // The system remembers where the last text was written (the cursor position) // To continue writing from the last position, set the x,y values to -1 // The text can optionally wrap around to the next line by calling oledSetTextWrap(true); // otherwise text which would go off the right edge will not be drawn and the cursor will // be left "off screen" until set to a new position explicitly // // Returns 0 for success, -1 for invalid parameter // int __oledWriteString(SSOLED *pOLED, int iScrollX, int x, int y, char *szMsg, int iSize, int bInvert, int bRender); // // Fill the frame buffer with a byte pattern // e.g. all off (0x00) or all on (0xff) // void __oledFill(SSOLED *pOLED, unsigned char ucData, int bRender); // // Set (or clear) an individual pixel // The local copy of the frame buffer is used to avoid // reading data from the display controller // (which isn't possible in most configurations) // This function needs the USE_BACKBUFFER macro to be defined // otherwise, new pixels will erase old pixels within the same byte // int __oledSetPixel(SSOLED *pOLED, int x, int y, unsigned char ucColor, int bRender); // // Dump an entire custom buffer to the display // useful for custom animation effects // void __oledDumpBuffer(SSOLED *pOLED, uint8_t *pBuffer); // // Render a window of pixels from a provided buffer or the library's internal buffer // to the display. The row values refer to byte rows, not pixel rows due to the memory // layout of OLEDs. Pass a src pointer of NULL to use the internal backing buffer // returns 0 for success, -1 for invalid parameter // int __oledDrawGFX(SSOLED *pOLED, uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch); // // Draw a line between 2 points // void __oledDrawLine(SSOLED *pOLED, int x1, int y1, int x2, int y2, int bRender); // // Play a frame of animation data // The animation data is assumed to be encoded for a full frame of the display // Given the pointer to the start of the compressed data, // it returns the pointer to the start of the next frame // Frame rate control is up to the calling program to manage // When it finishes the last frame, it will start again from the beginning // uint8_t * __oledPlayAnimFrame(SSOLED *pOLED, uint8_t *pAnimation, uint8_t *pCurrent, int iLen); // // Scroll the internal buffer by 1 scanline (up/down) // width is in pixels, lines is group of 8 rows // Returns 0 for success, -1 for invalid parameter // int __oledScrollBuffer(SSOLED *pOLED, int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp); // // Draw a sprite of any size in any position // If it goes beyond the left/right or top/bottom edges // it's trimmed to show the valid parts // This function requires a back buffer to be defined // The priority color (0 or 1) determines which color is painted // when a 1 is encountered in the source image. // e.g. when 0, the input bitmap acts like a mask to clear // the destination where bits are set. // void __oledDrawSprite(SSOLED *pOLED, uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority); // // Draw a 16x16 tile in any of 4 rotated positions // Assumes input image is laid out like "normal" graphics with // the MSB on the left and 2 bytes per line // On AVR, the source image is assumed to be in FLASH memory // The function can draw the tile on byte boundaries, so the x value // can be from 0 to 112 and y can be from 0 to 6 // void __oledDrawTile(SSOLED *pOLED, const uint8_t *pTile, int x, int y, int iRotation, int bInvert, int bRender); // // Draw an outline or filled ellipse // void __oledEllipse(SSOLED *pOLED, int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, uint8_t bFilled); // // Draw an outline or filled rectangle // void __oledRectangle(SSOLED *pOLED, int x1, int y1, int x2, int y2, uint8_t ucColor, uint8_t bFilled); #if defined(__cplusplus) } #endif #endif // __SS_OLED_H__
ss_oled.hpp
#pragma once #include <include/bitbang_iic.h> #include "pico/stdlib.h" #include "hardware/i2c.h" #include "ss_oled.h" class picoSSOLED { private: SSOLED oled; bool invert; int32_t speed; public: //constructor picoSSOLED(int iType, int iAddr, bool bFlip, bool bInvert, i2c_inst_t * pI2C, int sda, int scl, int32_t iSpeed) { oled.oled_type = iType; oled.oled_addr = iAddr; oled.oled_flip = (int) bFlip; invert = bInvert; oled.bbi2c.picoI2C = pI2C; oled.bbi2c.iSDA = sda; oled.bbi2c.iSCL = scl; speed = iSpeed; } ; int init() { return __oledInit(&oled, (int) invert, (int32_t) speed); } ; // // Provide or revoke a back buffer for your OLED graphics // This allows you to manage the RAM used by ss_oled on tiny // embedded platforms like the ATmega series // Pass NULL to revoke the buffer. Make sure you provide a buffer // large enough for your display (e.g. 128x64 needs 1K - 1024 bytes) // void set_back_buffer(uint8_t * pBuffer) { __oledSetBackBuffer(&oled, pBuffer); } ; // // Sets the brightness (0=off, 255=brightest) // void set_contrast(uint ucContrast) { __oledSetContrast(&oled, (unsigned char) ucContrast); } ; // // Load a 128x64 1-bpp Windows bitmap // Pass the pointer to the beginning of the BMP file // First pass version assumes a full screen bitmap // int load_bmp(uint8_t *pBMP, bool bInvert, bool bRender) { return __oledLoadBMP(&oled, pBMP, (int) bInvert, (int) bRender); } ; // // Power up/down the display // useful for low power situations // void power(bool bON) { __oledPower(&oled, (uint8_t) bON); } ; // // Set the current cursor position // The column represents the pixel column (0-127) // The row represents the text row (0-7) // void set_cursor(int x, int y) { __oledSetCursor(&oled, x, y); } ; // // Turn text wrap on or off for the oldWriteString() function // void set_textWrap(bool bWrap) { __oledSetTextWrap(&oled, (int) bWrap); } ; // // Draw a string of normal (8x8), small (6x8) or large (16x32) characters // At the given col+row with the given scroll offset. The scroll offset allows you to // horizontally scroll text which does not fit on the width of the display. The offset // represents the pixels to skip when drawing the text. An offset of 0 starts at the beginning // of the text. // The system remembers where the last text was written (the cursor position) // To continue writing from the last position, set the x,y values to -1 // The text can optionally wrap around to the next line by calling oledSetTextWrap(true); // otherwise text which would go off the right edge will not be drawn and the cursor will // be left "off screen" until set to a new position explicitly // // Returns 0 for success, -1 for invalid parameter // int write_string(int iScrollX, int x, int y, char *szMsg, int iSize, bool bInvert, bool bRender) { return __oledWriteString(&oled, iScrollX, x, y, szMsg, iSize, (int) bInvert, (int) bRender); } ; // // Fill the frame buffer with a byte pattern // e.g. all off (0x00) or all on (0xff) // void fill(unsigned char ucData, bool bRender) { __oledFill(&oled, ucData, (int) bRender); } // // Set (or clear) an individual pixel // The local copy of the frame buffer is used to avoid // reading data from the display controller // (which isn't possible in most configurations) // This function needs the USE_BACKBUFFER macro to be defined // otherwise, new pixels will erase old pixels within the same byte // int set_pixel(int x, int y, unsigned char ucColor, bool bRender) { return __oledSetPixel(&oled, x, y, ucColor, (int) bRender); } ; // // Dump an entire custom buffer to the display // useful for custom animation effects // void dump_buffer(uint8_t *pBuffer) { __oledDumpBuffer(&oled, pBuffer); } ; // // Render a window of pixels from a provided buffer or the library's internal buffer // to the display. The row values refer to byte rows, not pixel rows due to the memory // layout of OLEDs. Pass a src pointer of NULL to use the internal backing buffer // returns 0 for success, -1 for invalid parameter // int draw_GFX(uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch) { return __oledDrawGFX(&oled, pSrc, iSrcCol, iSrcRow, iDestCol, iDestRow, iWidth, iHeight, iSrcPitch); } ; // // Draw a line between 2 points // void draw_line(int x1, int y1, int x2, int y2, bool bRender) { __oledDrawLine(&oled, x1, y1, x2, y2, (int) bRender); } ; // // Play a frame of animation data // The animation data is assumed to be encoded for a full frame of the display // Given the pointer to the start of the compressed data, // it returns the pointer to the start of the next frame // Frame rate control is up to the calling program to manage // When it finishes the last frame, it will start again from the beginning // uint8_t * play_anim_frame(uint8_t *pAnimation, uint8_t *pCurrent, int iLen) { return __oledPlayAnimFrame(&oled, pAnimation, pCurrent, iLen); } ; // // Scroll the internal buffer by 1 scanline (up/down) // width is in pixels, lines is group of 8 rows // Returns 0 for success, -1 for invalid parameter // int scroll_buffer(int iStartCol, int iEndCol, int iStartRow, int iEndRow, bool bUp) { return __oledScrollBuffer(&oled, iStartCol, iEndCol, iStartRow, iEndRow, (int) bUp); } ; // // Draw a sprite of any size in any position // If it goes beyond the left/right or top/bottom edges // it's trimmed to show the valid parts // This function requires a back buffer to be defined // The priority color (0 or 1) determines which color is painted // when a 1 is encountered in the source image. // e.g. when 0, the input bitmap acts like a mask to clear // the destination where bits are set. // void draw_sprite(uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority) { __oledDrawSprite(&oled, pSprite, cx, cy, iPitch, x, y, iPriority); } ; // // Draw a 16x16 tile in any of 4 rotated positions // Assumes input image is laid out like "normal" graphics with // the MSB on the left and 2 bytes per line // On AVR, the source image is assumed to be in FLASH memory // The function can draw the tile on byte boundaries, so the x value // can be from 0 to 112 and y can be from 0 to 6 // void draw_tile(const uint8_t *pTile, int x, int y, int iRotation, bool bInvert, bool bRender) { __oledDrawTile(&oled, (const uint8_t *) pTile, x, y, iRotation, (int) bInvert, (int) bRender); } ; // // Draw an outline or filled ellipse // void draw_ellipse(int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, bool bFilled) { __oledEllipse(&oled, iCenterX, iCenterY, iRadiusX, iRadiusY, ucColor, (uint8_t) bFilled); } ; // // Draw an outline or filled rectangle // void draw_rectangle(int x1, int y1, int x2, int y2, uint8_t ucColor, bool bFilled) { __oledRectangle(&oled, x1, y1, x2, y2, ucColor, (uint8_t) bFilled); } ; };
stripp.hpp
//class, constructor & methods definitions class picoSSOLED { private: [...] public: //constructor picoSSOLED(int iType, int iAddr, bool bFlip, bool bInvert, i2c_inst_t * pUART, int sda, int scl, int32_t iSpeed) { //methods int init (); // // Provide or revoke a back buffer for your OLED graphics // This allows you to manage the RAM used by ss_oled on tiny // embedded platforms like the ATmega series // Pass NULL to revoke the buffer. Make sure you provide a buffer // large enough for your display (e.g. 128x64 needs 1K - 1024 bytes) // void set_back_buffer(uint8_t * pBuffer); // // Sets the brightness (0=off, 255=brightest) // void set_contrast(uint ucContrast); // // Load a 128x64 1-bpp Windows bitmap // Pass the pointer to the beginning of the BMP file // First pass version assumes a full screen bitmap // int load_bmp(uint8_t *pBMP, bool bInvert, bool bRender); // // Power up/down the display // useful for low power situations // void power(bool bON); // // Set the current cursor position // The column represents the pixel column (0-127) // The row represents the text row (0-7) // void set_cursor(int x, int y); // // Turn text wrap on or off for the oldWriteString() function // void set_textWrap(bool bWrap); // // Draw a string of normal (8x8), small (6x8) or large (16x32) characters // At the given col+row with the given scroll offset. The scroll offset allows you to // horizontally scroll text which does not fit on the width of the display. The offset // represents the pixels to skip when drawing the text. An offset of 0 starts at the beginning // of the text. // The system remembers where the last text was written (the cursor position) // To continue writing from the last position, set the x,y values to -1 // The text can optionally wrap around to the next line by calling oledSetTextWrap(true); // otherwise text which would go off the right edge will not be drawn and the cursor will // be left "off screen" until set to a new position explicitly // // Returns 0 for success, -1 for invalid parameter // int write_string(int iScrollX, int x, int y, char *szMsg, int iSize, bool bInvert, bool bRender); // // Fill the frame buffer with a byte pattern // e.g. all off (0x00) or all on (0xff) // void fill(unsigned char ucData, bool bRender); // // Set (or clear) an individual pixel // The local copy of the frame buffer is used to avoid // reading data from the display controller // (which isn't possible in most configurations) // This function needs the USE_BACKBUFFER macro to be defined // otherwise, new pixels will erase old pixels within the same byte // int set_pixel(int x, int y, unsigned char ucColor, bool bRender); // // Dump an entire custom buffer to the display // useful for custom animation effects // void dump_buffer(uint8_t *pBuffer); // // Render a window of pixels from a provided buffer or the library's internal buffer // to the display. The row values refer to byte rows, not pixel rows due to the memory // layout of OLEDs. Pass a src pointer of NULL to use the internal backing buffer // returns 0 for success, -1 for invalid parameter // int draw_GFX(uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch); // // Draw a line between 2 points // void draw_line(int x1, int y1, int x2, int y2, bool bRender); // // Play a frame of animation data // The animation data is assumed to be encoded for a full frame of the display // Given the pointer to the start of the compressed data, // it returns the pointer to the start of the next frame // Frame rate control is up to the calling program to manage // When it finishes the last frame, it will start again from the beginning // uint8_t * play_anim_frame(uint8_t *pAnimation, uint8_t *pCurrent, int iLen); // // Scroll the internal buffer by 1 scanline (up/down) // width is in pixels, lines is group of 8 rows // Returns 0 for success, -1 for invalid parameter // int scroll_buffer(int iStartCol, int iEndCol, int iStartRow, int iEndRow, bool bUp); // // Draw a sprite of any size in any position // If it goes beyond the left/right or top/bottom edges // it's trimmed to show the valid parts // This function requires a back buffer to be defined // The priority color (0 or 1) determines which color is painted // when a 1 is encountered in the source image. // e.g. when 0, the input bitmap acts like a mask to clear // the destination where bits are set. // void draw_sprite(uint8_t *pSprite, int cx, int cy, int iPitch, int x, int y, uint8_t iPriority); // // Draw a 16x16 tile in any of 4 rotated positions // Assumes input image is laid out like "normal" graphics with // the MSB on the left and 2 bytes per line // On AVR, the source image is assumed to be in FLASH memory // The function can draw the tile on byte boundaries, so the x value // can be from 0 to 112 and y can be from 0 to 6 // void draw_tile(const uint8_t *pTile, int x, int y, int iRotation, bool bInvert, bool bRender); // // Draw an outline or filled ellipse // void draw_ellipse(int iCenterX, int iCenterY, int32_t iRadiusX, int32_t iRadiusY, uint8_t ucColor, bool bFilled); // // Draw an outline or filled rectangle // void draw_rectangle(int x1, int y1, int x2, int y2, uint8_t ucColor, bool bFilled); };