/*
Copyright (c) [2019] [一只程序缘 jiezhuo]
[https://gitee.com/jiezhuonew/oledlib] is licensed under the Mulan PSL v1.
You can use this software according to the terms and conditions of the Mulan PSL v1.
You may obtain a copy of Mulan PSL v1 at:
http://license.coscl.org.cn/MulanPSL
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
PURPOSE.
See the Mulan PSL v1 for more details.
==========此函数是oledlib图形库的综合测试函数 基本上使用到了里面的所有内容==================
目前接线说明:
仅仅使用到0.96寸7引脚oled 使用SPI通信方式(具体见oled_drive.c)
D0 = SCK -> PA4 (屏幕批次不同丝印可能不同)
D1 = SDA -> PA5
RES = RES -> PA6
DC = DC -> PA7
CS = SCS -> PA8
软件模块:
oled spi驱动
beep 蜂鸣器 提示或作为led表示运行状态
timer3 中断1ms 可为图形库提供时间基准
uart 可作为串口连接电脑打印调试xinxi
delay 系统自带的时间设置与提供延时函数
视频演示信息:
单片机 --- STM32F103
oled --- 采用4线SPI的7引脚0.96寸黑白双色oled
图形库功能:
在oledlib文件夹下作出多个c文件的分类 每个c文件的开头都有对该文件的简要说明
包括打点 画线 矩形 三角形 圆 椭圆 圆角矩形 的边缘与填充
还包括 字符显示 汉字显示 位图显示
最后还有 oled模拟串口printf的debug
字符显示:
针对于字符有4种大小0-1-2-3 具体参见SetFontSize()
基于大家的使用习惯
0是一般大家习惯用的 此时y只能为0-7对应显示的7大行 注意:此时x为地基大行 y为第几列
2 3都是基于1的等比例放大 此时y能为0-6x对应显示的每一小行 此时为正常坐标系 x为第几小行 y为第几列
中文汉字显示:
中文汉字的大小为16x16 此时y也只能为0-7对应显示的7大行 注意:此时x为地基大行 y为第几列
取模说明:
取模软件采用PCtoLCD2002
字符 汉字 和 位图的取模方式和设置都在font.c和bmp.c中有注释
用户调用函数:
在draw_api.h中提供了所有函数的接口 可在其中查看所有函数的简要功能
移植说明:
1.移植图形库仅需要准备好底层驱动 在oled_driver.c中作出修改即可 在该c文件开头有说明
2.若需要对test.c中的动画效果进行测试 还需要为函数OledTimeMsFunc()提供时间基准
3.库函数使用的延时函数是DelayMs() 移植后由单片机型号补充调整DelayMs()
图形库本质:
该图形库本质为对一数组进行操作 然后刷新整个数组到屏幕中实现效果
因此相似点阵屏(oled/TFT/大小尺寸/点阵/液晶)也可以使用 根据像素可参见oled_config.h
详细的注释:
如果是使用为用途 了解test.c和draw_api.c即可 做出花里胡哨的UI界面
在本人阅读代码和注释的过程中 发现很多结构和函数都值得学习 仔细理解理解
GUI和本图形库的关系:
gui是一个以图形界面来进行人机交互的系统 gui是拥有窗口、菜单、控件、对话框等交互的图形接口
这个只是一个图形库 类似于c++上的easyx图形库 是没有图形接口的 只提供绘图和显示的功能
图形库可以算是gui的一部分,也可以自己以图形库继续封装为gui库
代码容量:
除去位图 Code=26254 RO-data=3754 RW-data=100 ZI-data=4108
原作的话:
为了做这个绘制图形库,我参考了很多其他的算法,花了大量时间去做移植
优化,其中参考最多的是github中的arduboy游戏机,大部分图形是移植它的,
现在是最初的初稿版本,还有很多地方需要优化改进。我想要将这个图形库做大,
后续会加入更多有趣的东西,和模块,代码都是用最简单的方式编写的,都是开源的。
后续也会加上注释说明,让更多人以单片机和oled屏来入门硬件编程,如果你
使用了该库,请标明出处。b站关注oximeterg,可获取该库的最新版本和消息。
注意:绘制填充或实心的图形,影响刷新效率(待优化中)
*/
// #include "test.h"
#include "draw_api.h"
#include "../Delay/Delay.h"
#include "stdlib.h"
#include "stdio.h"
#include "math.h"
void ShowStars(void);
void ShowWatch(void);
void ShowPolygon(void);
void ShowSnow(void);
void demo(void)
{
int i,j;
//demo演示
ClearScreen();
DrawBitmap(0,0,Panda,128,64);
UpdateScreen();
DelayMs(1000);
//字符
ClearScreen();
SetFontSize(1);
DrawString(0,0,"test");
UpdateScreen();
DelayMs(1000);
SetFontSize(2);
DrawString(0,8,"test");
UpdateScreen();
DelayMs(1000);
SetFontSize(3);
DrawString(0,24,"test");
UpdateScreen();
DelayMs(1000);
//汉字测试
ClearScreen();
OLED_ShowCHinese(0,0,"一只程序缘");
UpdateScreen();
DelayMs(1000);
OLED_ShowCHinese(2,0,"一只程序缘一只程序缘一只程序缘一只程序缘");
UpdateScreen();
DelayMs(1000);
//数字测试
ClearScreen();
for(i=0; i<300; i++)
{
SetFontSize(0);
DrawNum(0,96,i,4);
SetFontSize(1);
DrawNum(0,16,i,4);
SetFontSize(2);
DrawNum(0,24,i,4);
SetFontSize(3);
DrawNum(0,40,i,4);
UpdateScreen();
}
//串口debug
DelayMs(100);
ClearScreen();
for(i=0; i<256; i++)
{
OledPrintf("num:%d cha:%c hex:%x\r\n",i,i,i);
}
DelayMs(100);
//划线
ClearScreen();
for(i=0; i<20; i++)
{
DrawLine(0,0,i*10,63);
UpdateScreen();
}
for(i=0; i<20; i++)
{
DrawLine(128,0,128-i*10,63);
UpdateScreen();
}
DelayMs(100);
//矩形
for(j=0; j<2; j++)
{
if(j==0)
ClearScreen();
for(i=0; i<31; i+=2)
{
DrawRect2(i*2,i,128-i*4,64-2*i);//画矩形外框
UpdateScreen();
}
if(j==0)
ClearScreen();
DelayMs(100);
for(i=31; i>0; i-=2)
{
DrawFillRect2(i*2,i,128-i*4,64-2*i);//画实心矩形
UpdateScreen();
}
SetDrawColor(pix_black);//划线颜色
SetFillcolor(pix_black);//填充颜色
}
SetDrawColor(pix_white);
SetFillcolor(pix_white);
//圆角矩形
for(j=0; j<2; j++)
{
if(j==0)
ClearScreen();
for(i=0; i<25; i+=2)
{
DrawRoundRect(i*2,i,128-i*4,64-2*i,8);
UpdateScreen();
}
if(j==0)
ClearScreen();
DelayMs(100);
for(i=25; i>2; i-=2)
{
DrawfillRoundRect(i*2,i,128-i*4,64-2*i,8);
UpdateScreen();
}
SetDrawColor(pix_black);
SetFillcolor(pix_black);
}
SetDrawColor(pix_white);
SetFillcolor(pix_white);
DelayMs(100);
//椭圆
ClearScreen();
DrawEllipse(63,31,63,31);
UpdateScreen();
DelayMs(500);
ClearScreen();
DrawEllipse(63,31,16,31);
UpdateScreen();
DelayMs(500);
ClearScreen();
DrawFillEllipse(63,31,63,31);
UpdateScreen();
DelayMs(500);
ClearScreen();
DrawFillEllipse(63,31,16,31);
UpdateScreen();
DelayMs(500);
//圆
ClearScreen();
DrawCircle(63,31,30);
UpdateScreen();
DelayMs(500);
ClearScreen();
DrawFillCircle(63,31,30);
UpdateScreen();
DelayMs(500);
//三角形
ClearScreen();
DrawTriangle(5,10,100,30,60,50);
UpdateScreen();
DelayMs(500);
ClearScreen();
DrawFillTriangle(5,10,100,30,60,50);
UpdateScreen();
DelayMs(500);
//绘制图形图片 位图
ClearScreen();
DrawBitmap(0,0,BmpTest1,16,16);
UpdateScreen();
DelayMs(500);
DrawBitmap(16,0,BmpTest2,32,32);
UpdateScreen();
DelayMs(500);
DrawBitmap(48,0,BmpTest3,64,64);
UpdateScreen();
DelayMs(500);
//圆弧
ClearScreen();
for(i=0; i<360; i++)
{
DrawArc(63,31,30,0,i); //画圆弧
UpdateScreen();
DelayMs(25);
ClearScreen();
}
DrawCircle(63,31,30); //画圆
UpdateScreen();
DelayMs(100);
for(i=0; i<10; i++)
{
DrawFillCircle(63,31,i); //在中心填充圆
DrawCircle(63,31,30);
UpdateScreen();
DelayMs(25);
ClearScreen();
}
//绕点
for(i=0; i<720; i++) //转720度2圈
{
TypeXY temp;
SetAngle(i); //设置角度
SetRotateCenter(63,31); //设置圆心
temp=GetRotateXY(63,31+30); //讲已知坐标旋转角度
DrawFillCircle(temp.x,temp.y,5);//画出算出的位置
DrawCircle(63,31,30); //画出轨迹
DrawFillCircle(63,31,10); //填充中心
UpdateScreen(); //更新屏幕
ClearScreen();
}
//向右边平移
for(i=0; i<95; i++)
{
TypeXY temp; //其实就是上面继续旋转
SetAngle(720+i); //画图的时候x+偏移量
SetRotateCenter(63+i,31);
temp=GetRotateXY(63+i,31+30);
DrawFillCircle(temp.x,temp.y,5);
DrawCircle(63+i,31,30);
DrawFillCircle(63+i,31,10);
UpdateScreen();
ClearScreen();
}
//动画
ShowStars(); //星空动画
ShowWatch(); //时钟动画
ShowPolygon(); //多边形动画
// ShowSnow(); //下雪动画
DelayMs(100);
//结束
ClearScreen();
SetFontSize(2);
DrawString(8,16," Show End "); //显示字符串
UpdateScreen();
DelayMs(1000);
ClearScreen();
DrawBitmap(0,10,Like,128,40); //三连图片
UpdateScreen();
DelayMs(1000);
}
/
//星空动画
void ShowStars(void)
{
int i;
int count=0;
int fps=60;
typedef struct START
{
short x;
short y;
short speed;
unsigned char speedcount;
unsigned char isexist;
} Star;
Star star[128]= {0};
srand(2);
for(i=0; i<128; i++)
{
if(star[i].isexist==0)
{
//设置128个()星星的初始信息
star[i].x=rand()%127; //随机生成初始x坐标
star[i].y=rand()%64; //随机生成y的坐标
star[i].speedcount=0;
star[i].speed=rand()%8+1; //1-8的数
star[i].isexist=1;
}
}
while(1)
{
if(FrameRateUpdateScreen(fps)==1) //在此函数中定时刷新屏
{
count++;
if(count>=fps*10) //10秒钟时间到达之后跳出循环结束动画
return;
}
//此段函数一直在运行
//依次画出128个星星
for(i=0; i<128; i++)
{
//如果这一个星星已经移动到退出屏幕界面
//则在最左侧重新生成一颗新星星
if(star[i].isexist==0)
{
star[i].x=0;
star[i].y=rand()%64;
star[i].speed=rand()%6+1;
star[i].speedcount=0;
star[i].isexist=1;
}
else
{
star[i].speedcount++;
if(star[i].x>=124) //标记已经退出屏幕
star[i].isexist=0;
//清除上一个时刻画的星星(的尾巴) 不管有没有操作 都进行清除操作
SetDrawColor(pix_black);
DrawLine(star[i].x,star[i].y,star[i].x,star[i].y);
SetDrawColor(pix_white);
if(star[i].speedcount==star[i].speed) //运行时间到了一定的长度
{
star[i].speedcount=0; //复位运行时间并向右移一格
star[i].x+=1; //总之星星的结束需要在这经历124次
} //只不过有的更快 就能移动更快
//从头到尾画出整条星星 不管星星是否已经变化
DrawLine(star[i].x, star[i].y, star[i].x+(6/star[i].speed)-1, star[i].y);
}
}
}
}
//时钟动画
//钟 小时 分钟 秒,
void RoundClock(int hours,int minute,int sec)
{
unsigned char i=0;
TypeXY hourspoint,minutepoint,secpoint,tmp1,tmp2;
//时针
SetRotateValue(63,31,hours*30+(minute*30)/60,1);
hourspoint=GetRotateXY(63-14,31);
DrawLine(63,31,hourspoint.x,hourspoint.y);
//分针
SetRotateValue(63,31,minute*6+(sec*6)/60,1);
minutepoint=GetRotateXY(63-21,31);
DrawLine(63,31,minutepoint.x,minutepoint.y);
//秒针
SetRotateValue(63,31,sec*6,1);
secpoint=GetRotateXY(63-28,31);
DrawLine(63,31,secpoint.x,secpoint.y);
//表盘
for(i=0; i<12; i++)
{
SetRotateValue(63,31,i*30,1);
tmp1=GetRotateXY(63-29,31);
tmp2=GetRotateXY(63-24,31);
DrawLine(tmp1.x,tmp1.y,tmp2.x,tmp2.y);
}
DrawFillCircle(63,31,2);
DrawCircle(63,31,30);
UpdateScreen();
DelayMs(25);
ClearScreen();
}
void ShowWatch(void)
{
int i,j,z;
int count=0;
for(i=0; i<12; i++)
for(j=0; j<60; j++)
for(z=0; z<60; z++)
{
RoundClock(i,j,z);
count++;
if(count>=800)
return;
}
}
/
//多边形动画
void ShowPolygon(void)
{
int x0=63,y0=31; //正多边形的外接圆的圆心
unsigned char i =0,j;
int n=1,r=31; //画正n边形 大小半径31
int v=1,count=0; //每个相邻的多边形隔1 画count次后退出
int x[30],y[30];
while(1)
{
ClearScreen();
for(i=0; i<n; i++)
{
x[i]=r*cos(2*3.1415926*i/n)+x0;
y[i]=r*sin(2*3.1415926*i/n)+y0;
}
for(i=0; i<=n-2; i++)
{
for(j=i+1; j<=n-1; j++)
DrawLine(x[i],y[i],x[j],y[j]);
}
n+=v;
if(n==20||n==0)
v=-v;
UpdateScreen();
DelayMs(25); //延时停顿100ms
if(++count==90)
{
count=0;
return ;
}
}
}
//下雪的函数 和星空类似
void ShowSnow(void)
{
int a[66],i,num=0;
int count=0;
int fps=60;
struct Snow
{
short x;
short y;
short speed;
} snow[100];
srand(1);
for(i=0; i<66; i++)
a[i]=(i-2)*10;
ClearScreen();
while(1)
{
if(FrameRateUpdateScreen(fps)==1) //在此函数中定时刷新屏
{
count++;
if(count>=fps*10) //10秒钟时间到达之后跳出循环结束动画
return;
}
if(num!=100)
{
snow[num].speed=1+rand()%4;
i=rand()%66;
snow[num].x=a[i];
snow[num].y=0;
num++;
}
for(i=0; i<num; i++)
{
snow[i].y+=snow[i].speed;
DrawPixel(snow[i].x,snow[i].y+1);
DrawPixel(snow[i].x+1,snow[i].y);
DrawPixel(snow[i].x,snow[i].y);
DrawPixel(snow[i].x-1,snow[i].y);
DrawPixel(snow[i].x,snow[i].y-1);
if(snow[i].y>63)
{
snow[i].y=0;
}
}
UpdateScreen();
DelayMs(25);
ClearScreen();
}
}