本教程基于七线接口的使用
SPI
通信方式的0.96
寸OLED
单色屏进行移植,不需要考虑彩屏RGB排列顺序、权重、触摸屏等问题,移植的要点就是将官方GUI
下的画点API
函数和读点API
函数用我们自己写的驱动函数进行填充。能够让官方UI实现在我们的屏幕上画点,就基本移植成功了。因为能画点就能画一切,移植难度很低。但是例如刷屏、画多边形等情景如果用刷点方式一点点去刷,速率会很低下,能做优化尽量优化,我这里移植时没有做优化,有兴趣的朋友可以自行优化,移植是在keil5环境下没有系统的环境移植。屏幕很小、颜色不丰富,移植GUI能做什么东西想象力靠大家了。
7
线0.96
oled
一般这种屏幕引脚定义如下:
引脚名 | 功能 |
---|---|
GND | 电源地 |
VCC | 电源正极 |
D0 | SPI的CLK |
D1 | SPI的MOSI |
RES | 复位引脚 |
DC/RS | 命令数据选择引脚 |
CS | 片选引脚 |
链接: https://pan.baidu.com/s/1yN71luN5gO6sJzBkF_g5Ig
提取码: 9x86
驱动里面
SPI
通信采用电平模拟方式,这样写驱动移植起来比较方便,但是速率、可靠性稍差一点,这款oled
采用的控制IC为SSD1306
只能写,不能读。也就是屏幕本身不支持读点操作,本驱动采用的方式是将写入当前屏幕的所有点同步存储在对应大小的一块缓存区(8*128)内,屏幕显示内容与缓存区数据始终保持一致,读点的时候只需要从缓存区相应的位置获取数据信息即可。
oled.c
#include "oled.h"
#include
#include
#include "oledfont.h" //没移植gui之前可以用这里面的字库进行写文字测试
#define OLED_WriteCmd(x) OLED_WR_Byte(x,OLED_CMD)
#define OLED_WriteData(x) OLED_WR_Byte(x,OLED_DATA)
/* 下面2个宏任选 1个; 表示显示方向 */
//#define DIR_NORMAL /* 此行表示正常显示方向 */
#define DIR_180 /* 此行表示翻转180度 */
/* 12864 OLED的显存镜像,占用1K字节. 共8行,每行128像素 */
static uint8_t s_ucGRAM[8][128];
/* 为了避免刷屏拉幕感太强,引入刷屏标志。
0 表示显示函数只改写缓冲区,不写屏。1 表示直接写屏(同时写缓冲区) */
static uint8_t s_ucUpdateEn = 1;
static void OLED_BufToPanel(void); //刷新缓存
//向OLED写入一个字节。
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{
uint8_t i;
if(cmd)
{
OLED_RS_Set();
}
else
{
OLED_RS_Clr();
}
for(i=0;i<8;i++)
{
OLED_SCLK_Clr();
if(dat&0x80)
{
OLED_SDIN_Set();
}
else
{
OLED_SDIN_Clr();
}
OLED_SCLK_Set();
dat<<=1;
}
OLED_RS_Set();
}
//开启OLED显示
void OLED_Display_On(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X14,OLED_CMD); //DCDC ON
OLED_WR_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
OLED_WR_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_WR_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_WR_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
/*
*********************************************************************************************************
* 函 数 名: OLED_SetDir
* 功能说明: 设置显示方向
* 形 参: _ucDir = 0 表示正常方向,1表示翻转180度
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_SetDir(uint8_t _ucDir)
{
if (_ucDir == 0)
{
OLED_WriteCmd(0xA0); /* A0 :列地址0映射到SEG0; A1 :列地址127映射到SEG0 */
OLED_WriteCmd(0xC0); /* C0 :正常扫描,从COM0到COM63; C8 : 反向扫描, 从 COM63至 COM0 */
}
else
{
OLED_WriteCmd(0xA1); /* A0 :列地址0映射到SEG0; A1 :列地址127映射到SEG0 */
OLED_WriteCmd(0xC8); /* C0 :正常扫描,从COM0到COM63; C8 : 反向扫描, 从 COM63至 COM0 */
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_SetContrast
* 功能说明: 设置对比度
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_SetContrast(uint8_t ucValue)
{
OLED_WriteCmd(0x81); /* 设置对比度命令(双字节命令),第1个字节是命令,第2个字节是对比度参数0-255 */
OLED_WriteCmd(ucValue); /* 设置对比度参数,缺省CF */
}
/*
*********************************************************************************************************
* 函 数 名: OLED_StartDraw
* 功能说明: 开始绘图。以后绘图函数只改写缓冲区,不改写面板显存
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_StartDraw(void)
{
s_ucUpdateEn = 0;
}
/*
*********************************************************************************************************
* 函 数 名: OLED_EndDraw
* 功能说明: 结束绘图。缓冲区的数据刷新到面板显存。 OLED_StartDraw() 和 OLED_EndDraw() 必须成对使用
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_EndDraw(void)
{
s_ucUpdateEn = 1;
OLED_BufToPanel();
}
/*
*********************************************************************************************************
* 函 数 名: OLED_ClrScr
* 功能说明: 清屏
* 形 参: _ucMode : 0 表示全黑; 0xFF表示全亮
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_ClrScr(uint8_t _ucMode)
{
uint8_t i,j;
for (i = 0 ; i < 8; i++)
{
for (j = 0 ; j < 128; j++)
{
s_ucGRAM[i][j] = _ucMode;
}
}
if (s_ucUpdateEn == 1)
{
OLED_BufToPanel();
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_BufToPanel
* 功能说明: 将缓冲区中的点阵数据写入面板
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void OLED_BufToPanel(void)
{
uint8_t i,j;
for (i = 0 ; i< 8; i++)
{
OLED_WriteCmd (0xB0 + i); /* 设置页地址(0~7) */
OLED_WriteCmd (0x00); /* 设置列地址的低地址 */
OLED_WriteCmd (0x10); /* 设置列地址的高地址 */
for (j = 0 ; j < 128; j++)
{
OLED_WriteData(s_ucGRAM[i][j]);
}
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_PutPixel
* 功能说明: 画1个像素
* 形 参:
* _usX,_usY : 像素坐标
* _ucColor :像素颜色
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_PutPixel(uint16_t _usX, uint16_t _usY, uint8_t _ucColor)
{
uint8_t ucValue;
uint8_t ucPageAddr;
uint8_t ucColAddr;
const uint8_t aOrTab[8] = {0x01, 0x02, 0x04, 0x08,0x10,0x20,0x40,0x80};
const uint8_t aAndTab[8] = {0xFE, 0xFD, 0xFB, 0xF7,0xEF,0xDF,0xBF,0x7F};
ucPageAddr = _usY / 8; //计算横坐标地址属于哪一页
ucColAddr = _usX; //列地址
ucValue = s_ucGRAM[ucPageAddr][ucColAddr]; //获取显存中这一字节的数据
if (_ucColor == 0) //该点不显显示
{
ucValue &= aAndTab[_usY % 8]; //将该字节对应位清零
}
else
{
ucValue |= aOrTab[_usY % 8]; //该点对应位置位
}
s_ucGRAM[ucPageAddr][ucColAddr] = ucValue; //更新显存
if (s_ucUpdateEn == 1) //刷这个位置数据
{
OLED_WriteCmd (0xB0 + ucPageAddr); /* 设置页地址(0~7) */
OLED_WriteCmd (0x00 + (ucColAddr & 0x0F)); /* 设置列地址的低地址 */
OLED_WriteCmd (0x10 + ((ucColAddr >> 4) & 0x0F)); /* 设置列地址的高地址 */
OLED_WriteData(ucValue);
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_GetPixel
* 功能说明: 读取1个像素
* 形 参:
* _usX,_usY : 像素坐标
* 返 回 值: 颜色值 (0, 1)
*********************************************************************************************************
*/
uint8_t OLED_GetPixel(uint16_t _usX, uint16_t _usY)
{
uint8_t ucValue;
uint8_t ucPageAddr;
uint8_t ucColAddr;
ucPageAddr = _usY / 8;
ucColAddr = _usX;
ucValue = s_ucGRAM[ucPageAddr][ucColAddr];
if (ucValue & (_usY % 8))
{
return 1;
}
else
{
return 0;
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_DrawLine
* 功能说明: 采用 Bresenham 算法,在2点间画一条直线。
* 形 参:
* _usX1, _usY1 :起始点坐标
* _usX2, _usY2 :终止点Y坐标
* _ucColor :颜色
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawLine(uint16_t _usX1 , uint16_t _usY1 , uint16_t _usX2 , uint16_t _usY2 , uint8_t _ucColor)
{
int32_t dx , dy ;
int32_t tx , ty ;
int32_t inc1 , inc2 ;
int32_t d , iTag ;
int32_t x , y ;
/* 采用 Bresenham 算法,在2点间画一条直线 */
OLED_PutPixel(_usX1 , _usY1 , _ucColor);
/* 如果两点重合,结束后面的动作。*/
if ( _usX1 == _usX2 && _usY1 == _usY2 )
{
return;
}
iTag = 0 ;
/* dx = abs ( _usX2 - _usX1 ); */
if (_usX2 >= _usX1)
{
dx = _usX2 - _usX1;
}
else
{
dx = _usX1 - _usX2;
}
/* dy = abs ( _usY2 - _usY1 ); */
if (_usY2 >= _usY1)
{
dy = _usY2 - _usY1;
}
else
{
dy = _usY1 - _usY2;
}
if ( dx < dy ) /*如果dy为计长方向,则交换纵横坐标。*/
{
uint16_t temp;
iTag = 1 ;
temp = _usX1; _usX1 = _usY1; _usY1 = temp;
temp = _usX2; _usX2 = _usY2; _usY2 = temp;
temp = dx; dx = dy; dy = temp;
}
tx = _usX2 > _usX1 ? 1 : -1 ; /* 确定是增1还是减1 */
ty = _usY2 > _usY1 ? 1 : -1 ;
x = _usX1 ;
y = _usY1 ;
inc1 = 2 * dy ;
inc2 = 2 * ( dy - dx );
d = inc1 - dx ;
while ( x != _usX2 ) /* 循环画点 */
{
if ( d < 0 )
{
d += inc1 ;
}
else
{
y += ty ;
d += inc2 ;
}
if ( iTag )
{
OLED_PutPixel ( y , x , _ucColor) ;
}
else
{
OLED_PutPixel ( x , y , _ucColor) ;
}
x += tx ;
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_DrawPoints
* 功能说明: 采用 Bresenham 算法,绘制一组点,并将这些点连接起来。可用于波形显示。
* 形 参:
* x, y :坐标数组
* _ucColor :颜色
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawPoints(uint16_t *x, uint16_t *y, uint16_t _usSize, uint8_t _ucColor)
{
uint16_t i;
for (i = 0 ; i < _usSize - 1; i++)
{
OLED_DrawLine(x[i], y[i], x[i + 1], y[i + 1], _ucColor);
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_DrawRect
* 功能说明: 绘制矩形。
* 形 参:
* _usX,_usY:矩形左上角的坐标
* _usHeight :矩形的高度
* _usWidth :矩形的宽度
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawRect(uint16_t _usX, uint16_t _usY, uint8_t _usHeight, uint16_t _usWidth, uint8_t _ucColor)
{
/*
---------------->---
|(_usX,_usY) |
V V _usHeight
| |
---------------->---
_usWidth
*/
OLED_DrawLine(_usX, _usY, _usX + _usWidth - 1, _usY, _ucColor); /* 顶 */
OLED_DrawLine(_usX, _usY + _usHeight - 1, _usX + _usWidth - 1, _usY + _usHeight - 1, _ucColor); /* 底 */
OLED_DrawLine(_usX, _usY, _usX, _usY + _usHeight - 1, _ucColor); /* 左 */
OLED_DrawLine(_usX + _usWidth - 1, _usY, _usX + _usWidth - 1, _usY + _usHeight, _ucColor); /* 右 */
}
/*
*********************************************************************************************************
* 函 数 名: OLED_DrawCircle
* 功能说明: 绘制一个圆,笔宽为1个像素
* 形 参:
* _usX,_usY :圆心的坐标
* _usRadius :圆的半径
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawCircle(uint16_t _usX, uint16_t _usY, uint16_t _usRadius, uint8_t _ucColor)
{
int32_t D; /* Decision Variable */
uint32_t CurX; /* 当前 X 值 */
uint32_t CurY; /* 当前 Y 值 */
D = 3 - (_usRadius << 1);
CurX = 0;
CurY = _usRadius;
while (CurX <= CurY)
{
OLED_PutPixel(_usX + CurX, _usY + CurY, _ucColor);
OLED_PutPixel(_usX + CurX, _usY - CurY, _ucColor);
OLED_PutPixel(_usX - CurX, _usY + CurY, _ucColor);
OLED_PutPixel(_usX - CurX, _usY - CurY, _ucColor);
OLED_PutPixel(_usX + CurY, _usY + CurX, _ucColor);
OLED_PutPixel(_usX + CurY, _usY - CurX, _ucColor);
OLED_PutPixel(_usX - CurY, _usY + CurX, _ucColor);
OLED_PutPixel(_usX - CurY, _usY - CurX, _ucColor);
if (D < 0)
{
D += (CurX << 2) + 6;
}
else
{
D += ((CurX - CurY) << 2) + 10;
CurY--;
}
CurX++;
}
}
/*
*********************************************************************************************************
* 函 数 名: OLED_DrawBMP
* 功能说明: 在LCD上显示一个BMP位图,位图点阵扫描次序:从左到右,从上到下
* 形 参:
* _usX, _usY : 图片的坐标
* _usHeight :图片高度
* _usWidth :图片宽度
* _ptr :单色图片点阵指针,每个像素占用1个字节
* 返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawBMP(uint16_t _usX, uint16_t _usY, uint16_t _usHeight, uint16_t _usWidth, uint8_t *_ptr)
{
uint16_t x, y;
for (x = 0; x < _usWidth; x++)
{
for (y = 0; y < _usHeight; y++)
{
OLED_PutPixel(_usX + x, _usY + y, *_ptr);
}
}
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode)
{
uint8_t temp,t,t1;
uint8_t y0=y;
chr=chr-' ';//得到偏移后的值
for(t=0;t<size;t++)
{
if(size==12)temp=oled_asc2_1206[chr][t]; //调用1206字体
else temp=oled_asc2_1608[chr][t]; //调用1608字体
for(t1=0;t1<8;t1++)
{
if(temp&0x80)
{
OLED_PutPixel(x,y,mode);
}
else
{
OLED_PutPixel(x,y,!mode);
}
temp<<=1;
y++;
if((y-y0)==size)
{
y=y0;
x++;
break;
}
}
}
}
//m^n函数
uint32_t oled_pow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;
return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNumber(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size)
{
uint8_t t,temp;
uint8_t enshow=0;
for(t=0;t<len;t++)
{
temp=(num/oled_pow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
OLED_ShowChar(x+(size/2)*t,y,' ',size,1);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1);
}
}
//显示字符串
//x,y:起点坐标
//*p:字符串起始地址
//用16字体
void OLED_ShowString(uint8_t x,uint8_t y,const uint8_t *p)
{
#define MAX_CHAR_POSX 122
#define MAX_CHAR_POSY 58
while(*p!='\0')
{
if(x>MAX_CHAR_POSX)
{
x=0;
y+=16;
}
if(y>MAX_CHAR_POSY)
{
y=x=0;
OLED_ClrScr(0);
}
OLED_ShowChar(x,y,*p,12,1);
x+=8;
p++;
}
}
//初始化OLED
void OLED_Init(void)
{
OLED_RST_Clr();
HAL_Delay(100);
OLED_RST_Set();
/* 模块厂家提供初始化代码 */
OLED_WriteCmd(0xAE); /* 关闭OLED面板显示(休眠) */
OLED_WriteCmd(0x00); /* 设置列地址低4bit */
OLED_WriteCmd(0x10); /* 设置列地址高4bit */
OLED_WriteCmd(0x40); /* 设置起始行地址(低5bit 0-63), 硬件相关*/
OLED_WriteCmd(0x81); /* 设置对比度命令(双字节命令),第1个字节是命令,第2个字节是对比度参数0-255 */
OLED_WriteCmd(0xCF); /* 设置对比度参数,缺省CF */
#ifdef DIR_NORMAL
OLED_WriteCmd(0xA0); /* A0 :列地址0映射到SEG0; A1 :列地址127映射到SEG0 */
OLED_WriteCmd(0xC0); /* C0 :正常扫描,从COM0到COM63; C8 : 反向扫描, 从 COM63至 COM0 */
#endif
#ifdef DIR_180
OLED_WriteCmd(0xA1); /* A0 :列地址0映射到SEG0; A1 :列地址127映射到SEG0 */
OLED_WriteCmd(0xC8); /* C0 :正常扫描,从COM0到COM63; C8 : 反向扫描, 从 COM63至 COM0 */
#endif
OLED_WriteCmd(0xA6); /* A6 : 设置正常显示模式; A7 : 设置为反显模式 */
OLED_WriteCmd(0xA8); /* 设置COM路数 */
OLED_WriteCmd(0x3F); /* 1 ->(63+1)路 */
OLED_WriteCmd(0xD3); /* 设置显示偏移(双字节命令)*/
OLED_WriteCmd(0x00); /* 无偏移 */
OLED_WriteCmd(0xD5); /* 设置显示时钟分频系数/振荡频率 */
OLED_WriteCmd(0x80); /* 设置分频系数,高4bit是分频系数,低4bit是振荡频率 */
OLED_WriteCmd(0xD9); /* 设置预充电周期 */
OLED_WriteCmd(0xF1); /* [3:0],PHASE 1; [7:4],PHASE 2; */
OLED_WriteCmd(0xDA); /* 设置COM脚硬件接线方式 */
OLED_WriteCmd(0x12);
OLED_WriteCmd(0xDB); /* 设置 vcomh 电压倍率 */
OLED_WriteCmd(0x40); /* [6:4] 000 = 0.65 x VCC; 0.77 x VCC (RESET); 0.83 x VCC */
OLED_WriteCmd(0x8D); /* 设置充电泵(和下个命令结合使用) */
OLED_WriteCmd(0x14); /* 0x14 使能充电泵, 0x10 是关闭 */
OLED_WriteCmd(0xAF); /* 打开OLED面板 */
OLED_ClrScr(0X00);
}
oled.h
引脚控制部分需要自己根据自己的接线情况定义 还有引脚时钟、输出模式的初始化(全部推挽输出 50MHz即可)
#ifndef __OLED_H
#define __OLED_H
#include "stm32f10x.h"
//-----------------OLED引脚端口定义,需要自己填充----------------
//复位引脚
#define OLED_RST_Clr()
#define OLED_RST_Set()
//命令 数据选择控制引脚
#define OLED_RS_Clr()
#define OLED_RS_Set()
//时钟引脚
#define OLED_SCLK_Clr()
#define OLED_SCLK_Set()
//MOSI引脚
#define OLED_SDIN_Clr()
#define OLED_SDIN_Set()
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
//OLED控制用函数
void OLED_WR_Byte(uint8_t dat,uint8_t cmd); //写数据或者写命令
void OLED_Display_On(void); //开启OLED显示
void OLED_Display_Off(void); //关闭OLED显示
void OLED_SetDir(uint8_t _ucDir) ; //设置显示方向 0 表示正常方向,1表示翻转180度
void OLED_SetContrast(uint8_t ucValue); //设置对比度
void OLED_StartDraw(void) ; //开始绘图。以后绘图函数只改写缓冲区,不改写面板显存
void OLED_EndDraw(void) ; //结束绘图。缓冲区的数据刷新到面板显存。
void OLED_ClrScr(uint8_t _ucMode) ; //清屏
void OLED_PutPixel(uint16_t _usX, uint16_t _usY, uint8_t _ucColor) ; // 画1个像素
uint8_t OLED_GetPixel(uint16_t _usX, uint16_t _usY); // 读取1个像素
//采用 Bresenham 算法,在2点间画一条直线。
void OLED_DrawLine(uint16_t _usX1 , uint16_t _usY1 , uint16_t _usX2 , uint16_t _usY2 , uint8_t _ucColor) ;
// 采用 Bresenham 算法,绘制一组点,并将这些点连接起来。可用于波形显示。
void OLED_DrawPoints(uint16_t *x, uint16_t *y, uint16_t _usSize, uint8_t _ucColor);
//绘制矩形。
void OLED_DrawRect(uint16_t _usX, uint16_t _usY, uint8_t _usHeight, uint16_t _usWidth, uint8_t _ucColor);
//绘制一个圆,笔宽为1个像素
void OLED_DrawCircle(uint16_t _usX, uint16_t _usY, uint16_t _usRadius, uint8_t _ucColor);
//在LCD上显示一个BMP位图,位图点阵扫描次序:从左到右,从上到下
void OLED_DrawBMP(uint16_t _usX, uint16_t _usY, uint16_t _usHeight, uint16_t _usWidth, uint8_t *_ptr);
//初始化OLED
void OLED_Init(void);
void OLED_ShowString(uint8_t x,uint8_t y,const uint8_t *p) ;
#endif
自定义字库文件,如果移植成功,可以不使用这里面的字库
oledfont.h
#ifndef __OLEDFONT_H
#define __OLEDFONT_H
//常用ASCII表
//偏移量32
//ASCII字符集
//偏移量32
//大小:12*6
const unsigned char oled_asc2_1206[95][12]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
{0x00,0x00,0x30,0x00,0x40,0x00,0x30,0x00,0x40,0x00,0x00,0x00},/*""",2*/
{0x09,0x00,0x0B,0xC0,0x3D,0x00,0x0B,0xC0,0x3D,0x00,0x09,0x00},/*"#",3*/
{0x18,0xC0,0x24,0x40,0x7F,0xE0,0x22,0x40,0x31,0x80,0x00,0x00},/*"$",4*/
{0x18,0x00,0x24,0xC0,0x1B,0x00,0x0D,0x80,0x32,0x40,0x01,0x80},/*"%",5*/
{0x03,0x80,0x1C,0x40,0x27,0x40,0x1C,0x80,0x07,0x40,0x00,0x40},/*"&",6*/
{0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x80,0x20,0x40,0x40,0x20},/*"(",8*/
{0x00,0x00,0x40,0x20,0x20,0x40,0x1F,0x80,0x00,0x00,0x00,0x00},/*")",9*/
{0x09,0x00,0x06,0x00,0x1F,0x80,0x06,0x00,0x09,0x00,0x00,0x00},/*"*",10*/
{0x04,0x00,0x04,0x00,0x3F,0x80,0x04,0x00,0x04,0x00,0x00,0x00},/*"+",11*/
{0x00,0x10,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
{0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00},/*"-",13*/
{0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
{0x00,0x20,0x01,0xC0,0x06,0x00,0x38,0x00,0x40,0x00,0x00,0x00},/*"/",15*/
{0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"0",16*/
{0x00,0x00,0x10,0x40,0x3F,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"1",17*/
{0x18,0xC0,0x21,0x40,0x22,0x40,0x24,0x40,0x18,0x40,0x00,0x00},/*"2",18*/
{0x10,0x80,0x20,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"3",19*/
{0x02,0x00,0x0D,0x00,0x11,0x00,0x3F,0xC0,0x01,0x40,0x00,0x00},/*"4",20*/
{0x3C,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x23,0x80,0x00,0x00},/*"5",21*/
{0x1F,0x80,0x24,0x40,0x24,0x40,0x34,0x40,0x03,0x80,0x00,0x00},/*"6",22*/
{0x30,0x00,0x20,0x00,0x27,0xC0,0x38,0x00,0x20,0x00,0x00,0x00},/*"7",23*/
{0x1B,0x80,0x24,0x40,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"8",24*/
{0x1C,0x00,0x22,0xC0,0x22,0x40,0x22,0x40,0x1F,0x80,0x00,0x00},/*"9",25*/
{0x00,0x00,0x00,0x00,0x08,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
{0x00,0x00,0x00,0x00,0x04,0x60,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
{0x00,0x00,0x04,0x00,0x0A,0x00,0x11,0x00,0x20,0x80,0x40,0x40},/*"<",28*/
{0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x00,0x00},/*"=",29*/
{0x00,0x00,0x40,0x40,0x20,0x80,0x11,0x00,0x0A,0x00,0x04,0x00},/*">",30*/
{0x18,0x00,0x20,0x00,0x23,0x40,0x24,0x00,0x18,0x00,0x00,0x00},/*"?",31*/
{0x1F,0x80,0x20,0x40,0x27,0x40,0x29,0x40,0x1F,0x40,0x00,0x00},/*"@",32*/
{0x00,0x40,0x07,0xC0,0x39,0x00,0x0F,0x00,0x01,0xC0,0x00,0x40},/*"A",33*/
{0x20,0x40,0x3F,0xC0,0x24,0x40,0x24,0x40,0x1B,0x80,0x00,0x00},/*"B",34*/
{0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x30,0x80,0x00,0x00},/*"C",35*/
{0x20,0x40,0x3F,0xC0,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"D",36*/
{0x20,0x40,0x3F,0xC0,0x24,0x40,0x2E,0x40,0x30,0xC0,0x00,0x00},/*"E",37*/
{0x20,0x40,0x3F,0xC0,0x24,0x40,0x2E,0x00,0x30,0x00,0x00,0x00},/*"F",38*/
{0x0F,0x00,0x10,0x80,0x20,0x40,0x22,0x40,0x33,0x80,0x02,0x00},/*"G",39*/
{0x20,0x40,0x3F,0xC0,0x04,0x00,0x04,0x00,0x3F,0xC0,0x20,0x40},/*"H",40*/
{0x20,0x40,0x20,0x40,0x3F,0xC0,0x20,0x40,0x20,0x40,0x00,0x00},/*"I",41*/
{0x00,0x60,0x20,0x20,0x20,0x20,0x3F,0xC0,0x20,0x00,0x20,0x00},/*"J",42*/
{0x20,0x40,0x3F,0xC0,0x24,0x40,0x0B,0x00,0x30,0xC0,0x20,0x40},/*"K",43*/
{0x20,0x40,0x3F,0xC0,0x20,0x40,0x00,0x40,0x00,0x40,0x00,0xC0},/*"L",44*/
{0x3F,0xC0,0x3C,0x00,0x03,0xC0,0x3C,0x00,0x3F,0xC0,0x00,0x00},/*"M",45*/
{0x20,0x40,0x3F,0xC0,0x0C,0x40,0x23,0x00,0x3F,0xC0,0x20,0x00},/*"N",46*/
{0x1F,0x80,0x20,0x40,0x20,0x40,0x20,0x40,0x1F,0x80,0x00,0x00},/*"O",47*/
{0x20,0x40,0x3F,0xC0,0x24,0x40,0x24,0x00,0x18,0x00,0x00,0x00},/*"P",48*/
{0x1F,0x80,0x21,0x40,0x21,0x40,0x20,0xE0,0x1F,0xA0,0x00,0x00},/*"Q",49*/
{0x20,0x40,0x3F,0xC0,0x24,0x40,0x26,0x00,0x19,0xC0,0x00,0x40},/*"R",50*/
{0x18,0xC0,0x24,0x40,0x24,0x40,0x22,0x40,0x31,0x80,0x00,0x00},/*"S",51*/
{0x30,0x00,0x20,0x40,0x3F,0xC0,0x20,0x40,0x30,0x00,0x00,0x00},/*"T",52*/
{0x20,0x00,0x3F,0x80,0x00,0x40,0x00,0x40,0x3F,0x80,0x20,0x00},/*"U",53*/
{0x20,0x00,0x3E,0x00,0x01,0xC0,0x07,0x00,0x38,0x00,0x20,0x00},/*"V",54*/
{0x38,0x00,0x07,0xC0,0x3C,0x00,0x07,0xC0,0x38,0x00,0x00,0x00},/*"W",55*/
{0x20,0x40,0x39,0xC0,0x06,0x00,0x39,0xC0,0x20,0x40,0x00,0x00},/*"X",56*/
{0x20,0x00,0x38,0x40,0x07,0xC0,0x38,0x40,0x20,0x00,0x00,0x00},/*"Y",57*/
{0x30,0x40,0x21,0xC0,0x26,0x40,0x38,0x40,0x20,0xC0,0x00,0x00},/*"Z",58*/
{0x00,0x00,0x00,0x00,0x7F,0xE0,0x40,0x20,0x40,0x20,0x00,0x00},/*"[",59*/
{0x00,0x00,0x70,0x00,0x0C,0x00,0x03,0x80,0x00,0x40,0x00,0x00},/*"\",60*/
{0x00,0x00,0x40,0x20,0x40,0x20,0x7F,0xE0,0x00,0x00,0x00,0x00},/*"]",61*/
{0x00,0x00,0x20,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00},/*"^",62*/
{0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10},/*"_",63*/
{0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
{0x00,0x00,0x02,0x80,0x05,0x40,0x05,0x40,0x03,0xC0,0x00,0x40},/*"a",65*/
{0x20,0x00,0x3F,0xC0,0x04,0x40,0x04,0x40,0x03,0x80,0x00,0x00},/*"b",66*/
{0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x40,0x06,0x40,0x00,0x00},/*"c",67*/
{0x00,0x00,0x03,0x80,0x04,0x40,0x24,0x40,0x3F,0xC0,0x00,0x40},/*"d",68*/
{0x00,0x00,0x03,0x80,0x05,0x40,0x05,0x40,0x03,0x40,0x00,0x00},/*"e",69*/
{0x00,0x00,0x04,0x40,0x1F,0xC0,0x24,0x40,0x24,0x40,0x20,0x00},/*"f",70*/
{0x00,0x00,0x02,0xE0,0x05,0x50,0x05,0x50,0x06,0x50,0x04,0x20},/*"g",71*/
{0x20,0x40,0x3F,0xC0,0x04,0x40,0x04,0x00,0x03,0xC0,0x00,0x40},/*"h",72*/
{0x00,0x00,0x04,0x40,0x27,0xC0,0x00,0x40,0x00,0x00,0x00,0x00},/*"i",73*/
{0x00,0x10,0x00,0x10,0x04,0x10,0x27,0xE0,0x00,0x00,0x00,0x00},/*"j",74*/
{0x20,0x40,0x3F,0xC0,0x01,0x40,0x07,0x00,0x04,0xC0,0x04,0x40},/*"k",75*/
{0x20,0x40,0x20,0x40,0x3F,0xC0,0x00,0x40,0x00,0x40,0x00,0x00},/*"l",76*/
{0x07,0xC0,0x04,0x00,0x07,0xC0,0x04,0x00,0x03,0xC0,0x00,0x00},/*"m",77*/
{0x04,0x40,0x07,0xC0,0x04,0x40,0x04,0x00,0x03,0xC0,0x00,0x40},/*"n",78*/
{0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x40,0x03,0x80,0x00,0x00},/*"o",79*/
{0x04,0x10,0x07,0xF0,0x04,0x50,0x04,0x40,0x03,0x80,0x00,0x00},/*"p",80*/
{0x00,0x00,0x03,0x80,0x04,0x40,0x04,0x50,0x07,0xF0,0x00,0x10},/*"q",81*/
{0x04,0x40,0x07,0xC0,0x02,0x40,0x04,0x00,0x04,0x00,0x00,0x00},/*"r",82*/
{0x00,0x00,0x06,0x40,0x05,0x40,0x05,0x40,0x04,0xC0,0x00,0x00},/*"s",83*/
{0x00,0x00,0x04,0x00,0x1F,0x80,0x04,0x40,0x00,0x40,0x00,0x00},/*"t",84*/
{0x04,0x00,0x07,0x80,0x00,0x40,0x04,0x40,0x07,0xC0,0x00,0x40},/*"u",85*/
{0x04,0x00,0x07,0x00,0x04,0xC0,0x01,0x80,0x06,0x00,0x04,0x00},/*"v",86*/
{0x06,0x00,0x01,0xC0,0x07,0x00,0x01,0xC0,0x06,0x00,0x00,0x00},/*"w",87*/
{0x04,0x40,0x06,0xC0,0x01,0x00,0x06,0xC0,0x04,0x40,0x00,0x00},/*"x",88*/
{0x04,0x10,0x07,0x10,0x04,0xE0,0x01,0x80,0x06,0x00,0x04,0x00},/*"y",89*/
{0x00,0x00,0x04,0x40,0x05,0xC0,0x06,0x40,0x04,0x40,0x00,0x00},/*"z",90*/
{0x00,0x00,0x00,0x00,0x04,0x00,0x7B,0xE0,0x40,0x20,0x00,0x00},/*"{",91*/
{0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00},/*"|",92*/
{0x00,0x00,0x40,0x20,0x7B,0xE0,0x04,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
{0x40,0x00,0x80,0x00,0x40,0x00,0x20,0x00,0x20,0x00,0x40,0x00},/*"~",94*/
};
const unsigned char oled_asc2_1608[95][16]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
{0x00,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x00,0x00},/*""",2*/
{0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x00,0x00},/*"#",3*/
{0x00,0x00,0x0E,0x18,0x11,0x04,0x3F,0xFF,0x10,0x84,0x0C,0x78,0x00,0x00,0x00,0x00},/*"$",4*/
{0x0F,0x00,0x10,0x84,0x0F,0x38,0x00,0xC0,0x07,0x78,0x18,0x84,0x00,0x78,0x00,0x00},/*"%",5*/
{0x00,0x78,0x0F,0x84,0x10,0xC4,0x11,0x24,0x0E,0x98,0x00,0xE4,0x00,0x84,0x00,0x08},/*"&",6*/
{0x08,0x00,0x68,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",7*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x18,0x18,0x20,0x04,0x40,0x02,0x00,0x00},/*"(",8*/
{0x00,0x00,0x40,0x02,0x20,0x04,0x18,0x18,0x07,0xE0,0x00,0x00,0x00,0x00,0x00,0x00},/*")",9*/
{0x02,0x40,0x02,0x40,0x01,0x80,0x0F,0xF0,0x01,0x80,0x02,0x40,0x02,0x40,0x00,0x00},/*"*",10*/
{0x00,0x80,0x00,0x80,0x00,0x80,0x0F,0xF8,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00},/*"+",11*/
{0x00,0x01,0x00,0x0D,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*",",12*/
{0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80},/*"-",13*/
{0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*".",14*/
{0x00,0x00,0x00,0x06,0x00,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x18,0x00,0x20,0x00},/*"/",15*/
{0x00,0x00,0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"0",16*/
{0x00,0x00,0x08,0x04,0x08,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"1",17*/
{0x00,0x00,0x0E,0x0C,0x10,0x14,0x10,0x24,0x10,0x44,0x11,0x84,0x0E,0x0C,0x00,0x00},/*"2",18*/
{0x00,0x00,0x0C,0x18,0x10,0x04,0x11,0x04,0x11,0x04,0x12,0x88,0x0C,0x70,0x00,0x00},/*"3",19*/
{0x00,0x00,0x00,0xE0,0x03,0x20,0x04,0x24,0x08,0x24,0x1F,0xFC,0x00,0x24,0x00,0x00},/*"4",20*/
{0x00,0x00,0x1F,0x98,0x10,0x84,0x11,0x04,0x11,0x04,0x10,0x88,0x10,0x70,0x00,0x00},/*"5",21*/
{0x00,0x00,0x07,0xF0,0x08,0x88,0x11,0x04,0x11,0x04,0x18,0x88,0x00,0x70,0x00,0x00},/*"6",22*/
{0x00,0x00,0x1C,0x00,0x10,0x00,0x10,0xFC,0x13,0x00,0x1C,0x00,0x10,0x00,0x00,0x00},/*"7",23*/
{0x00,0x00,0x0E,0x38,0x11,0x44,0x10,0x84,0x10,0x84,0x11,0x44,0x0E,0x38,0x00,0x00},/*"8",24*/
{0x00,0x00,0x07,0x00,0x08,0x8C,0x10,0x44,0x10,0x44,0x08,0x88,0x07,0xF0,0x00,0x00},/*"9",25*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x0C,0x03,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*":",26*/
{0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*";",27*/
{0x00,0x00,0x00,0x80,0x01,0x40,0x02,0x20,0x04,0x10,0x08,0x08,0x10,0x04,0x00,0x00},/*"<",28*/
{0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x00,0x00},/*"=",29*/
{0x00,0x00,0x10,0x04,0x08,0x08,0x04,0x10,0x02,0x20,0x01,0x40,0x00,0x80,0x00,0x00},/*">",30*/
{0x00,0x00,0x0E,0x00,0x12,0x00,0x10,0x0C,0x10,0x6C,0x10,0x80,0x0F,0x00,0x00,0x00},/*"?",31*/
{0x03,0xE0,0x0C,0x18,0x13,0xE4,0x14,0x24,0x17,0xC4,0x08,0x28,0x07,0xD0,0x00,0x00},/*"@",32*/
{0x00,0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04},/*"A",33*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x11,0x04,0x0E,0x88,0x00,0x70,0x00,0x00},/*"B",34*/
{0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x08,0x1C,0x10,0x00,0x00},/*"C",35*/
{0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"D",36*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x04,0x17,0xC4,0x10,0x04,0x08,0x18,0x00,0x00},/*"E",37*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x17,0xC0,0x10,0x00,0x08,0x00,0x00,0x00},/*"F",38*/
{0x03,0xE0,0x0C,0x18,0x10,0x04,0x10,0x04,0x10,0x44,0x1C,0x78,0x00,0x40,0x00,0x00},/*"G",39*/
{0x10,0x04,0x1F,0xFC,0x10,0x84,0x00,0x80,0x00,0x80,0x10,0x84,0x1F,0xFC,0x10,0x04},/*"H",40*/
{0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x04,0x00,0x00,0x00,0x00},/*"I",41*/
{0x00,0x03,0x00,0x01,0x10,0x01,0x10,0x01,0x1F,0xFE,0x10,0x00,0x10,0x00,0x00,0x00},/*"J",42*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x03,0x80,0x14,0x64,0x18,0x1C,0x10,0x04,0x00,0x00},/*"K",43*/
{0x10,0x04,0x1F,0xFC,0x10,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x0C,0x00,0x00},/*"L",44*/
{0x10,0x04,0x1F,0xFC,0x1F,0x00,0x00,0xFC,0x1F,0x00,0x1F,0xFC,0x10,0x04,0x00,0x00},/*"M",45*/
{0x10,0x04,0x1F,0xFC,0x0C,0x04,0x03,0x00,0x00,0xE0,0x10,0x18,0x1F,0xFC,0x10,0x00},/*"N",46*/
{0x07,0xF0,0x08,0x08,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x08,0x07,0xF0,0x00,0x00},/*"O",47*/
{0x10,0x04,0x1F,0xFC,0x10,0x84,0x10,0x80,0x10,0x80,0x10,0x80,0x0F,0x00,0x00,0x00},/*"P",48*/
{0x07,0xF0,0x08,0x18,0x10,0x24,0x10,0x24,0x10,0x1C,0x08,0x0A,0x07,0xF2,0x00,0x00},/*"Q",49*/
{0x10,0x04,0x1F,0xFC,0x11,0x04,0x11,0x00,0x11,0xC0,0x11,0x30,0x0E,0x0C,0x00,0x04},/*"R",50*/
{0x00,0x00,0x0E,0x1C,0x11,0x04,0x10,0x84,0x10,0x84,0x10,0x44,0x1C,0x38,0x00,0x00},/*"S",51*/
{0x18,0x00,0x10,0x00,0x10,0x04,0x1F,0xFC,0x10,0x04,0x10,0x00,0x18,0x00,0x00,0x00},/*"T",52*/
{0x10,0x00,0x1F,0xF8,0x10,0x04,0x00,0x04,0x00,0x04,0x10,0x04,0x1F,0xF8,0x10,0x00},/*"U",53*/
{0x10,0x00,0x1E,0x00,0x11,0xE0,0x00,0x1C,0x00,0x70,0x13,0x80,0x1C,0x00,0x10,0x00},/*"V",54*/
{0x1F,0xC0,0x10,0x3C,0x00,0xE0,0x1F,0x00,0x00,0xE0,0x10,0x3C,0x1F,0xC0,0x00,0x00},/*"W",55*/
{0x10,0x04,0x18,0x0C,0x16,0x34,0x01,0xC0,0x01,0xC0,0x16,0x34,0x18,0x0C,0x10,0x04},/*"X",56*/
{0x10,0x00,0x1C,0x00,0x13,0x04,0x00,0xFC,0x13,0x04,0x1C,0x00,0x10,0x00,0x00,0x00},/*"Y",57*/
{0x08,0x04,0x10,0x1C,0x10,0x64,0x10,0x84,0x13,0x04,0x1C,0x04,0x10,0x18,0x00,0x00},/*"Z",58*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0x40,0x02,0x40,0x02,0x40,0x02,0x00,0x00},/*"[",59*/
{0x00,0x00,0x30,0x00,0x0C,0x00,0x03,0x80,0x00,0x60,0x00,0x1C,0x00,0x03,0x00,0x00},/*"\",60*/
{0x00,0x00,0x40,0x02,0x40,0x02,0x40,0x02,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00},/*"]",61*/
{0x00,0x00,0x00,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00},/*"^",62*/
{0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01},/*"_",63*/
{0x00,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",64*/
{0x00,0x00,0x00,0x98,0x01,0x24,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xFC,0x00,0x04},/*"a",65*/
{0x10,0x00,0x1F,0xFC,0x00,0x88,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"b",66*/
{0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x00},/*"c",67*/
{0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x11,0x08,0x1F,0xFC,0x00,0x04},/*"d",68*/
{0x00,0x00,0x00,0xF8,0x01,0x44,0x01,0x44,0x01,0x44,0x01,0x44,0x00,0xC8,0x00,0x00},/*"e",69*/
{0x00,0x00,0x01,0x04,0x01,0x04,0x0F,0xFC,0x11,0x04,0x11,0x04,0x11,0x00,0x18,0x00},/*"f",70*/
{0x00,0x00,0x00,0xD6,0x01,0x29,0x01,0x29,0x01,0x29,0x01,0xC9,0x01,0x06,0x00,0x00},/*"g",71*/
{0x10,0x04,0x1F,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"h",72*/
{0x00,0x00,0x01,0x04,0x19,0x04,0x19,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"i",73*/
{0x00,0x00,0x00,0x03,0x00,0x01,0x01,0x01,0x19,0x01,0x19,0xFE,0x00,0x00,0x00,0x00},/*"j",74*/
{0x10,0x04,0x1F,0xFC,0x00,0x24,0x00,0x40,0x01,0xB4,0x01,0x0C,0x01,0x04,0x00,0x00},/*"k",75*/
{0x00,0x00,0x10,0x04,0x10,0x04,0x1F,0xFC,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00},/*"l",76*/
{0x01,0x04,0x01,0xFC,0x01,0x04,0x01,0x00,0x01,0xFC,0x01,0x04,0x01,0x00,0x00,0xFC},/*"m",77*/
{0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x00,0x01,0x00,0x01,0x04,0x00,0xFC,0x00,0x04},/*"n",78*/
{0x00,0x00,0x00,0xF8,0x01,0x04,0x01,0x04,0x01,0x04,0x01,0x04,0x00,0xF8,0x00,0x00},/*"o",79*/
{0x01,0x01,0x01,0xFF,0x00,0x85,0x01,0x04,0x01,0x04,0x00,0x88,0x00,0x70,0x00,0x00},/*"p",80*/
{0x00,0x00,0x00,0x70,0x00,0x88,0x01,0x04,0x01,0x04,0x01,0x05,0x01,0xFF,0x00,0x01},/*"q",81*/
{0x01,0x04,0x01,0x04,0x01,0xFC,0x00,0x84,0x01,0x04,0x01,0x00,0x01,0x80,0x00,0x00},/*"r",82*/
{0x00,0x00,0x00,0xCC,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x24,0x01,0x98,0x00,0x00},/*"s",83*/
{0x00,0x00,0x01,0x00,0x01,0x00,0x07,0xF8,0x01,0x04,0x01,0x04,0x00,0x00,0x00,0x00},/*"t",84*/
{0x01,0x00,0x01,0xF8,0x00,0x04,0x00,0x04,0x00,0x04,0x01,0x08,0x01,0xFC,0x00,0x04},/*"u",85*/
{0x01,0x00,0x01,0x80,0x01,0x70,0x00,0x0C,0x00,0x10,0x01,0x60,0x01,0x80,0x01,0x00},/*"v",86*/
{0x01,0xF0,0x01,0x0C,0x00,0x30,0x01,0xC0,0x00,0x30,0x01,0x0C,0x01,0xF0,0x01,0x00},/*"w",87*/
{0x00,0x00,0x01,0x04,0x01,0x8C,0x00,0x74,0x01,0x70,0x01,0x8C,0x01,0x04,0x00,0x00},/*"x",88*/
{0x01,0x01,0x01,0x81,0x01,0x71,0x00,0x0E,0x00,0x18,0x01,0x60,0x01,0x80,0x01,0x00},/*"y",89*/
{0x00,0x00,0x01,0x84,0x01,0x0C,0x01,0x34,0x01,0x44,0x01,0x84,0x01,0x0C,0x00,0x00},/*"z",90*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x3E,0xFC,0x40,0x02,0x40,0x02},/*"{",91*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},/*"|",92*/
{0x00,0x00,0x40,0x02,0x40,0x02,0x3E,0xFC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"}",93*/
{0x00,0x00,0x60,0x00,0x80,0x00,0x80,0x00,0x40,0x00,0x40,0x00,0x20,0x00,0x20,0x00},/*"~",94*/
};
#endif
https://www.st.com/en/embedded-software/stemwin.html
en.stemwin.zip
,进行解压,得到STemWin_Library_V1.2.0
文件夹,可能是别的版本,关系不大。STemWin_Library_V1.2.0
文件夹内容介绍
GUIConf.c
修改GUI缓存,我们在嵌入式设备上运行,一般存储空间很小,默认数值有点大,运行GUI分配内存时容易出问题,导致移植失败,分配内存大小看自身硬件和项目需求这里我取的30。
原内容65行:GUI分配内存
#define GUI_NUMBYTES 0x200000
替换为:(这块因人而异)
#define GUI_NUMBYTES (30*1024)
GUIConf.h
主要修改GUI配置,进行裁剪,根据自身需求修改即可,下面是我的配置
/*********************************************************************
*
* Multi layer/display support
*/
#define GUI_NUM_LAYERS 2 // GUI支持最大层数
/*********************************************************************
*
* Multi tasking support
*/
#ifdef OS_SUPPORT
#define GUI_OS (0) // 增加系统支持
#else
#define GUI_OS (0)
#endif
/*********************************************************************
*
* Configuration of touch support
*/
#ifndef GUI_SUPPORT_TOUCH
#define GUI_SUPPORT_TOUCH (0) // 支持触屏
#endif
/*********************************************************************
*
* Default font
*/
#define GUI_DEFAULT_FONT &GUI_Font6x8 //默认字体及大小
/*********************************************************************
*
* Configuration of available packages
*/
#define GUI_SUPPORT_MOUSE (0) /* 鼠标支持 */
#define GUI_WINSUPPORT (1) /* 窗口管理 */
#define GUI_SUPPORT_MEMDEV (0) /* Memory device package available */
#define GUI_SUPPORT_DEVICES (1) /* Enable use of device pointers */
GUIDRV_Template.c
将自己写的画点、读点函数与系统API结合,让GUI可以在屏幕上画点,在文件内添加自己oled驱动的头文件,这个文件里面还有画矩形、画线等函数,如果你经常用到这些,可以优化,因为GUI写的这些都是基于画点绘制,针对咱们这款屏幕,刷屏速率将会很慢。
原内容:139行画点函数 178行读点函数 636行读设备数据函数这三个函数需要修改
/*********************************************************************
*
* _SetPixelIndex
*
* Purpose:
* Sets the index of the given pixel. The upper layers
* calling this routine make sure that the coordinates are in range, so
* that no check on the parameters needs to be performed.
*/
static void _SetPixelIndex(GUI_DEVICE * pDevice, int x, int y, int PixelIndex) {
//
// Convert logical into physical coordinates (Dep. on LCDConf.h)
//
#if (LCD_MIRROR_X == 1) || (LCD_MIRROR_Y == 1) || (LCD_SWAP_XY == 1)
int xPhys, yPhys;
xPhys = LOG2PHYS_X(x, y);
yPhys = LOG2PHYS_Y(x, y);
#else
#define xPhys x
#define yPhys y
#endif
GUI_USE_PARA(pDevice);
GUI_USE_PARA(x);
GUI_USE_PARA(y);
GUI_USE_PARA(PixelIndex);
{
//
// Write into hardware ... Adapt to your system
//
// TBD by customer...
//插入自己的画点函数
}
#if (LCD_MIRROR_X == 0) && (LCD_MIRROR_Y == 0) && (LCD_SWAP_XY == 0)
#undef xPhys
#undef yPhys
#endif
}
/*********************************************************************
*
* _GetPixelIndex
*
* Purpose:
* Returns the index of the given pixel. The upper layers
* calling this routine make sure that the coordinates are in range, so
* that no check on the parameters needs to be performed.
*/
static unsigned int _GetPixelIndex(GUI_DEVICE * pDevice, int x, int y) {
unsigned int PixelIndex;
//
// Convert logical into physical coordinates (Dep. on LCDConf.h)
//
#if (LCD_MIRROR_X == 1) || (LCD_MIRROR_Y == 1) || (LCD_SWAP_XY == 1)
int xPhys, yPhys;
xPhys = LOG2PHYS_X(x, y);
yPhys = LOG2PHYS_Y(x, y);
#else
#define xPhys x
#define yPhys y
#endif
GUI_USE_PARA(pDevice);
GUI_USE_PARA(x);
GUI_USE_PARA(y);
{
//
// Write into hardware ... Adapt to your system
//
// TBD by customer...
//插入自己的读点函数
PixelIndex = 0;
}
#if (LCD_MIRROR_X == 0) && (LCD_MIRROR_Y == 0) && (LCD_SWAP_XY == 0)
#undef xPhys
#undef yPhys
#endif
return PixelIndex;
}
/*********************************************************************
*
* _GetDevData
*/
static void * _GetDevData(GUI_DEVICE * pDevice, int Index) {
GUI_USE_PARA(pDevice);
#if GUI_SUPPORT_MEMDEV
switch (Index) {
case LCD_DEVDATA_MEMDEV:
return (void *)&GUI_MEMDEV_DEVICE_16; // TBD: Has to be adapted to the right memory device depending on the used color depth!
}
#else
GUI_USE_PARA(Index);
#endif
return NULL;
}
修改为:
#include "oled.h" //添加自己驱动的头文件
/*********************************************************************
*
* _SetPixelIndex
*
* Purpose:
* Sets the index of the given pixel. The upper layers
* calling this routine make sure that the coordinates are in range, so
* that no check on the parameters needs to be performed.
*/
static void _SetPixelIndex(GUI_DEVICE * pDevice, int x, int y, int PixelIndex) {
//
// Convert logical into physical coordinates (Dep. on LCDConf.h)
//
#if (LCD_MIRROR_X == 1) || (LCD_MIRROR_Y == 1) || (LCD_SWAP_XY == 1)
int xPhys, yPhys;
xPhys = LOG2PHYS_X(x, y);
yPhys = LOG2PHYS_Y(x, y);
#else
#define xPhys x
#define yPhys y
#endif
GUI_USE_PARA(pDevice);
GUI_USE_PARA(x);
GUI_USE_PARA(y);
GUI_USE_PARA(PixelIndex);
{
//插入自己的画点函数
OLED_PutPixel(x,y,PixelIndex);
}
#if (LCD_MIRROR_X == 0) && (LCD_MIRROR_Y == 0) && (LCD_SWAP_XY == 0)
#undef xPhys
#undef yPhys
#endif
}
/*********************************************************************
*
* _GetPixelIndex
*
* Purpose:
* Returns the index of the given pixel. The upper layers
* calling this routine make sure that the coordinates are in range, so
* that no check on the parameters needs to be performed.
*/
static unsigned int _GetPixelIndex(GUI_DEVICE * pDevice, int x, int y) {
unsigned int PixelIndex;
//
// Convert logical into physical coordinates (Dep. on LCDConf.h)
//
#if (LCD_MIRROR_X == 1) || (LCD_MIRROR_Y == 1) || (LCD_SWAP_XY == 1)
int xPhys, yPhys;
xPhys = LOG2PHYS_X(x, y);
yPhys = LOG2PHYS_Y(x, y);
#else
#define xPhys x
#define yPhys y
#endif
GUI_USE_PARA(pDevice);
GUI_USE_PARA(x);
GUI_USE_PARA(y);
{
//插入自己的读点函数
PixelIndex = OLED_GetPixel(x,y);
}
#if (LCD_MIRROR_X == 0) && (LCD_MIRROR_Y == 0) && (LCD_SWAP_XY == 0)
#undef xPhys
#undef yPhys
#endif
return PixelIndex;
}
/*********************************************************************
*
* _GetDevData
*/
static void * _GetDevData(GUI_DEVICE * pDevice, int Index) {
GUI_USE_PARA(pDevice);
#if GUI_SUPPORT_MEMDEV
switch (Index) {
case LCD_DEVDATA_MEMDEV:
//return (void *)&GUI_MEMDEV_DEVICE_16;
return (void *)&GUI_MEMDEV_DEVICE_1; //将16改为1
}
#else
GUI_USE_PARA(Index);
#endif
return NULL;
}
LCDConf_FlexColor_Template.c
修改屏幕尺寸和驱动相关内容 修改项在67行 164行
原内容:
//
// Physical display size
//
#define XSIZE_PHYS 240 // To be adapted to x-screen size
#define YSIZE_PHYS 320 // To be adapted to y-screen size
/*********************************************************************
*
* LCD_X_Config
*
* Function description:
* Called during the initialization process in order to set up the
* display driver configuration.
*
*/
void LCD_X_Config(void) {
GUI_DEVICE * pDevice;
CONFIG_FLEXCOLOR Config = {0};
GUI_PORT_API PortAPI = {0};
//
// Set display driver and color conversion
//
pDevice = GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_565, 0, 0);
//
// Display driver configuration, required for Lin-driver
//
LCD_SetSizeEx (0, XSIZE_PHYS , YSIZE_PHYS);
LCD_SetVSizeEx(0, VXSIZE_PHYS, VYSIZE_PHYS);
//
// Orientation
//
Config.Orientation = GUI_SWAP_XY | GUI_MIRROR_Y;
GUIDRV_FlexColor_Config(pDevice, &Config);
//
// Set controller and operation mode
//
PortAPI.pfWrite16_A0 = LcdWriteReg;
PortAPI.pfWrite16_A1 = LcdWriteData;
PortAPI.pfWriteM16_A1 = LcdWriteDataMultiple;
PortAPI.pfReadM16_A1 = LcdReadDataMultiple;
GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66708, GUIDRV_FLEXCOLOR_M16C0B16);
}
修改后:
//
// Physical display size
//
#define XSIZE_PHYS 128 // 屏幕宽度
#define YSIZE_PHYS 64 // 高度
#define VXSIZE_PHYS 128 // 虚拟内存宽度
#define VYSIZE_PHYS 64 // 虚拟内存高度
/*********************************************************************
*
* LCD_X_Config
*
* Function description:
* Called during the initialization process in order to set up the
* display driver configuration.
*
*/
void LCD_X_Config(void) {
//GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_565, 0, 0);
//采用设备自己定义初始化 单色屏幕,其余的写寄存器 写数据 连续读 连续写的操作都是基于彩屏或者其他驱动芯片的,直接删除就可以否则GUI会初始化失败
GUI_DEVICE_CreateAndLink(GUIDRV_TEMPLATE, GUICC_1, 0, 0);
LCD_SetSizeEx (0, XSIZE_PHYS , YSIZE_PHYS);
LCD_SetVSizeEx(0, VXSIZE_PHYS, VYSIZE_PHYS);
}
完成以上操作
gui
就移植完毕了,可以写一个demo
测试一下。注意在调用GUI_Init()
之前,需要包含GUI.h
头文件并使能CRC
校验时钟、相关io
引脚初始化、oled
初始化。
void LCD_X_Config(void)
LCD_X_Config
函数里面代码多余部分没有删除GUIDRV_Template.c
没有包含自己驱动的头文件GUIConf.c
分配的内存过大,初始化时分配内存失败,导致GUI
起不来程序或者硬件错误卡死CRC
时钟GUI
不做优化的话,全部操作都是基于画点函数实现的,如果画方框、圆、刷屏等函数自己写的执行效率比较高的话可以替换掉源码刷点的方式,速度能大大提升)如果需要移植到带ucos-ii系统的工程,除了库要加载带OS的版本,GUI_X.c这个文件也要换成带OS版本即GUI_X_OS.c,默认支持FreeROTS系统,支持ucos-II还需要对接口进行修改。防止多任务同时操作屏幕造成内存管理乱套。
GUI_X_OS.c
#include "GUI.h"
#include "stdio.h"
#include "ucos_ii.h"
#define TRUE 1
#define FALSE !TRUE
#define UCOS_II 1
/******************************************************************************
参数变量定义
******************************************************************************/
#if UCOS_II==1 //支持ucos
static OS_EVENT *DispSem; //信号量
static OS_EVENT *EventMbox; //邮箱
static OS_EVENT *KeySem; //键值
static int KeyPressed;
static char KeyIsInited;
#else
volatile GUI_TIMER_TIME OS_TimeMS;
#endif
/**
*****************************************************************************
* @Name : GUI获取系统时间
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : 操作系统时间值
*****************************************************************************
**/
#if UCOS_II==1 //支持ucos
int GUI_X_GetTime(void)
{
return ((int)OSTimeGet()); //获取OS时间
}
#else
GUI_TIMER_TIME GUI_X_GetTime(void)
{
return OS_TimeMS; //不开启OS时获取心跳
}
#endif
/**
*****************************************************************************
* @Name : GUI延时
*
* @Brief : none
*
* @Input : ms: 延时的ms数
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_Delay(int ms)
{
#if UCOS_II==1 //支持ucos
INT32U ticks;
ticks = (ms * 1000) / OS_TICKS_PER_SEC; //根据操作系统时钟节拍来
OSTimeDly((INT16U)ticks);
#else
int tEnd = OS_TimeMS + ms;
while ((tEnd - OS_TimeMS) > 0);
#endif
}
void GUI_X_ExecIdle(void)
{
#if UCOS_II==1 //支持ucos
GUI_X_Delay(1);
#endif
}
#if UCOS_II==1 //支持ucos
/**
*****************************************************************************
* @Name : GUI初始化OS
*
* @Brief : 主要是建立信号量、邮箱
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_InitOS(void)
{
DispSem = OSSemCreate(1); //创建一个互斥型变量
EventMbox = OSMboxCreate((void *)0); //创建一个邮箱
}
/**
*****************************************************************************
* @Name : GUI解锁
*
* @Brief : 主要是建立互斥相关函数
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_Unlock(void)
{
OSSemPost(DispSem);
}
/**
*****************************************************************************
* @Name : GUI上锁
*
* @Brief : 主要是建立互斥相关函数
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_Lock(void)
{
INT8U err;
OSSemPend(DispSem, 0, &err);
}
/**
*****************************************************************************
* @Name : GUI获取当前任务ID
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
U32 GUI_X_GetTaskId(void)
{
return ((U32)(OSTCBCur->OSTCBPrio));
}
/**
*****************************************************************************
* @Name : GUI等待事件发生
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_WaitEvent(void)
{
INT8U err;
(void)OSMboxPend(EventMbox, 0, &err);
}
/**
*****************************************************************************
* @Name : GUI等待事件发生
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_SignalEvent(void)
{
(void)OSMboxPost(EventMbox, (void *)1);
}
/******************************************************************************
对于键盘支持函数
******************************************************************************/
/**
*****************************************************************************
* @Name : GUI初始化检测
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
static void CheckInit (void)
{
if (KeyIsInited == FALSE) {
KeyIsInited = TRUE;
GUI_X_Init();
}
}
/**
*****************************************************************************
* @Name : GUI创建按键任务
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_Init (void)
{
KeySem = OSSemCreate(0);
}
/**
*****************************************************************************
* @Name : GUI得到键值
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
int GUI_X_GetKey (void)
{
int r;
r = KeyPressed;
CheckInit();
KeyPressed = 0;
return (r);
}
/**
*****************************************************************************
* @Name : GUI等待按键
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
int GUI_X_WaitKey (void)
{
int r;
INT8U err;
CheckInit();
if (KeyPressed == 0) {
OSSemPend(KeySem, 0, &err);
}
r = KeyPressed;
KeyPressed = 0;
return (r);
}
/**
*****************************************************************************
* @Name : GUI等待按键
*
* @Brief : none
*
* @Input : none
*
* @Output : none
*
* @Return : none
*****************************************************************************
**/
void GUI_X_StoreKey (int k)
{
KeyPressed = k;
OSSemPost(KeySem);
}
/******************************************************************************
OS dependent
******************************************************************************/
void GUI_X_Log (const char *s) { }
void GUI_X_Warn (const char *s) { }
void GUI_X_ErrorOut(const char *s) { }
#else
void GUI_X_Init(void) {}
/******************************************************************************
OS dependent
******************************************************************************/
void GUI_X_Log (const char *s) { GUI_USE_PARA(s); }
void GUI_X_Warn (const char *s) { GUI_USE_PARA(s); }
void GUI_X_ErrorOut(const char *s) { GUI_USE_PARA(s); }
#endif