Bootstrap

02 U8G2简单应用-动画

U8G2简单应用-动画


1.进度条

1.1 简单的进度条

static void show_progress(u8g2_t *u8g2)
{
#define U8G2_MARGINS	(2)
	static unsigned int ipx = 0;

#if 1		/* 重复绘制 */
	u8g2_DrawFrame(u8g2, 0, 40, u8g2_GetDisplayWidth(u8g2), 15);

	if(ipx<(u8g2_GetDisplayWidth(u8g2)-U8G2_MARGINS*2))
		u8g2_DrawBox(u8g2, U8G2_MARGINS, 40+U8G2_MARGINS, U8G2_MARGINS+ipx, 15-U8G2_MARGINS*2);

	ipx++;
	if(ipx>=(u8g2_GetDisplayWidth(u8g2)-U8G2_MARGINS*2))
		ipx = 0;
#else		/* 只绘制一次 */
	if(ipx>=(u8g2_GetDisplayWidth(u8g2)-U8G2_MARGINS*2))
		ipx = (u8g2_GetDisplayWidth(u8g2)-U8G2_MARGINS*2);

	u8g2_DrawFrame(u8g2, 0, 40, u8g2_GetDisplayWidth(u8g2), 15);
	if(ipx<(u8g2_GetDisplayWidth(u8g2)-U8G2_MARGINS*2))
		u8g2_DrawBox(u8g2, U8G2_MARGINS, 40+U8G2_MARGINS, U8G2_MARGINS+ipx, 15-U8G2_MARGINS*2);
	else
		u8g2_DrawBox(u8g2, U8G2_MARGINS, 40+U8G2_MARGINS, ipx, 15-U8G2_MARGINS*2);

	ipx++;
#endif
	return;
}

u8g2_FirstPage(&u8g2);
do
{
	show_progress(&u8g2);
} while (u8g2_NextPage(&u8g2)); 

显示效果:
在这里插入图片描述

1.2 带进度百分比的进度条

说明:

  • U8G2_BAR_R之所以定义为0是因为当有角度时,进度%1-%10显示会有问题,这个是可以理解的,当有圆角时,如果宽度太小会显示异常。
  • lcdWidth与lcdHeight定义为了浮点类型,因为要参与除法运算。
static void showLoading(u8g2_t *u8g2)
{
#define	U8G2_SPACING	(2)		// 边框到屏幕边沿的间距
#define U8G2_BOX_WIDTH	(1)		// 边框的宽度
#define U8G2_BOX_HEIGHT	(16)	// 边框的高度
#define	U8G2_BAR_HEITH	(12)	// 进度条的宽度
#define	U8G2_BAR_R		(0)		// 如果带圆角,进度条的角度
#define U8G2_BAR_MAX	(99)	// 进度最大值

	float lcdWidth = u8g2_GetDisplayWidth(u8g2);
	float lcdHeight = u8g2_GetDisplayHeight(u8g2);
	static unsigned char progress = 0;	//百度比进度
	char progressBuf[4] = {0};
	float setp = 0;				//每百分之一进度显示的宽度
	unsigned int barWidth = 0;	//进度条的宽度
	
	// 画左上和右上的边框
	u8g2_DrawBox(u8g2, U8G2_SPACING, U8G2_SPACING, lcdWidth-U8G2_SPACING*2, U8G2_BOX_WIDTH);
	u8g2_DrawBox(u8g2, U8G2_SPACING, U8G2_SPACING, U8G2_BOX_WIDTH, U8G2_BOX_HEIGHT);
	u8g2_DrawBox(u8g2, lcdWidth-U8G2_SPACING-U8G2_BOX_WIDTH, U8G2_SPACING, U8G2_BOX_WIDTH, U8G2_BOX_HEIGHT);
	
	// 画左下和右下的边框
	u8g2_DrawBox(u8g2, U8G2_SPACING, lcdHeight-U8G2_SPACING-U8G2_BOX_WIDTH, lcdWidth-U8G2_SPACING*2, U8G2_BOX_WIDTH);
	u8g2_DrawBox(u8g2, U8G2_SPACING, lcdHeight-U8G2_SPACING-U8G2_BOX_HEIGHT, U8G2_BOX_WIDTH, U8G2_BOX_HEIGHT);
	u8g2_DrawBox(u8g2, lcdWidth-U8G2_SPACING-U8G2_BOX_WIDTH, lcdHeight-U8G2_SPACING-U8G2_BOX_HEIGHT, 
		U8G2_BOX_WIDTH, U8G2_BOX_HEIGHT);
	
	// 显示Loading字符串
	u8g2_SetFont(u8g2, u8g2_font_amstrad_cpc_extended_8r);
	u8g2_DrawStr(u8g2, (lcdWidth-u8g2_GetStrWidth(u8g2, "Loading"))/2, lcdHeight*1/3, "Loading");
	
	// 画进度条边框
	u8g2_DrawRFrame(u8g2, (lcdWidth-(lcdWidth*2/3))/2, (lcdHeight/2)-(U8G2_BAR_HEITH/2), (lcdWidth*2)/3, U8G2_BAR_HEITH, U8G2_BAR_R);

	setp = ((lcdWidth*2)/3)/U8G2_BAR_MAX;
	barWidth = setp*progress;
	if(progress<=U8G2_BAR_MAX)
	{
		// 显示进度条
		u8g2_DrawRBox(u8g2, (lcdWidth-(lcdWidth*2/3))/2, ((lcdHeight/2)-(U8G2_BAR_HEITH/2)), 
			barWidth, U8G2_BAR_HEITH, U8G2_BAR_R);
		
		//显示进度百分比
		snprintf(progressBuf, sizeof(progressBuf), "%2d%s", progress, "%");
		u8g2_DrawStr(u8g2, (lcdWidth-u8g2_GetStrWidth(u8g2, progressBuf))/2, lcdHeight*3/4, progressBuf);
		progress++;
	}
	else
	{
		progress = 0;
	}	
}

int main(void)
{
	unsigned int index;
	u8g2_t u8g2;

	SysTick_Init(72);
	GPIO_Configuration();
  	u8g2_init(&u8g2);
	while (1)
	{
		index++;
		if(index%10000)
			PBout(12) = ~ PBin(12);
		delay_us(100);
		
		u8g2_FirstPage(&u8g2);
		do
		{
			showLoading(&u8g2);
		} while (u8g2_NextPage(&u8g2)); 
	}
}

显示效果:
在这里插入图片描述

2. 圆球碰撞反弹

static void random_pattern(u8g2_t *u8g2)
{
#define WIDTH 	128
#define HIGH 	64
#define R 		10
	static int Vx=5,Vy=5;						// 球移动的步长
    static int x=WIDTH/2,y=HIGH/2;             	// 表示小球的圆心坐标

	u8g2_DrawDisc(u8g2, x, y, R, U8G2_DRAW_ALL);
	x=x+Vx;
    y=y+Vy;
    if(x<=R || x>=WIDTH-R)
        Vx=-Vx;
    if(y<=R || y>=HIGH-R)
        Vy=-Vy;
	return;
}
说明:代码参考了,https://www.dotcpp.com/course/1182

u8g2_FirstPage(&u8g2);
do
{
	random_pattern(&u8g2);
	delay_ms(100);
} while (u8g2_NextPage(&u8g2)); 

显示效果:
在这里插入图片描述

3. 在随机位置生成一个图形(测试代码是矩形)

全部代码放在最后。

//random.c
#include "random.h"
#include "stdlib.h"
#include "string.h"
#include "SysTick.h"

extern u32 rand_time_us;     //用定时器提供一个动态变量

//=============================================================================
//函数名称:rand_seed
//功能概要:产生随机数
//参数说明:产生min-max之间的随机数
//函数返回:int
//=============================================================================

unsigned int rand_seed(unsigned int min, unsigned int max)
{
    unsigned int num;
	static char first = 1;

	if(first)     //只执行一次
	{
		first = 0;
		srand(rand_time_us);   //提供随机数种子
	}
	
	num = rand() % (max - min + 1) + min;

	return num;
}

//main.c
static void random_pattern(u8g2_t *u8g2)
{
	unsigned int x, y, w, h;
	
	do{
		x = rand_seed(0, 128);
		w = rand_seed(0, 128);
	}while((x+w)>128);	
	
	do{
		y = rand_seed(0, 64);
		h = rand_seed(0, 64);
	}while((y+h)>64);

	u8g2_DrawBox(u8g2, x, y, w, h);	
}

显示效果:
在这里插入图片描述

4. 线段扫描

static void random_pattern(u8g2_t *u8g2)
{
	unsigned char setp = 4;
	unsigned char x1 = 0, y1 = 0;
	static char x2 = 0, y2 = 64;

	if(x2<=128)
	{
		u8g2_DrawLine(u8g2, x1, y1, x2, y2);
		x2 = x2+setp;
	}
	else
	{
		y2 = y2 -setp;
		u8g2_DrawLine(u8g2, x1, y1, x2, y2);
		if(y2<=0)
		{
			x2 = 0;
			y2 = 64;
		}
	}	
}

u8g2_FirstPage(&u8g2);
do
{
	random_pattern(&u8g2);
	delay_ms(50);
} while (u8g2_NextPage(&u8g2)); 

显示效果:
在这里插入图片描述

5. 代码雨

代码参考链接:https://www.jb51.net/article/247902.htm
说明:

  • 参考代码是坚着刷新数据的,这里是根据屏横向刷新数据
  • 记得修改startup_stm32f10x_hd.s中的栈大小,原来只有620字节(0x400),修改为1440(0x800)。如果不修改栈大小程序跑不起来(这里我找了好久)。

5.1 静态的代码雨

修改栈大小
startup_stm32f10x_hd.s
Stack_Size      EQU     0x00000400
修改为
Stack_Size      EQU     0x00000800

main.c
static void random_pattern(u8g2_t *u8g2)
{
#define High 		64
#define Width 		128
#define CharSize	8

	int highNum=High/CharSize;
    int widthNum=Width/CharSize;

	int CharRain[High/CharSize][Width/CharSize];
    int RNum[High/CharSize];//每一行的有效字符个数
    int i,j,x,y;
	char ascii[1] = {0};

	for(j=0;j<highNum;j++)
	{
		for(i=0;i<widthNum;i++)
			CharRain[j][i] = 32;
	}

	for(j=0;j<highNum;j++)//初始化字符矩阵
    {
        RNum[j] = rand_seed(3, widthNum);//这一行的有效字符个数
        for(i=0;i<RNum[j];i++)
            CharRain[j][i] = rand_seed(0, 25)+65;//产生A~Z的随机字符
    }

	u8g2_SetFont(u8g2, u8g2_font_amstrad_cpc_extended_8r);
	u8g2_SetFontDirection(u8g2, 0);

	for(j=0;j<highNum;j++)//输出整个字符矩阵
	{
		y=(j+1)*CharSize;//当前字符的y坐标
		for(i=0;i<RNum[j];i++)
		{
			x=i*CharSize;//当前字符的x坐标
			memset(ascii, 0, sizeof(ascii));
			sprintf(ascii, "%c", CharRain[j][i]);
			u8g2_DrawStr(u8g2, x, y, ascii);//输出当前字符
		}
	}
}

u8g2_FirstPage(&u8g2);
do
{
	random_pattern(&u8g2);
	delay_ms(500);
} while (u8g2_NextPage(&u8g2));

显示效果:
在这里插入图片描述

5.2 动态的代码雨

说明:

  • 这里需要一直while循环,所以修改了u8g2的主循环
static void random_pattern(u8g2_t *u8g2)
{
#define High 		64
#define Width 		128
#define CharSize	8

	int highNum=High/CharSize;
    int widthNum=Width/CharSize;
	int charNotFull;

	int CharRain[High/CharSize][Width/CharSize];
    int RNum[High/CharSize];//每一行的有效字符个数
    int i,j,x,y;
	char ascii[1] = {0};
	
/* 全部重新显示 */
resetShow:
	u8g2_ClearBuffer(u8g2);
	for(j=0;j<highNum;j++)
	{
		for(i=0;i<widthNum;i++)
			CharRain[j][i] = 32;
	}

	for(j=0;j<highNum;j++)//初始化字符矩阵
    {
        RNum[j] = rand_seed(3, widthNum);//这一行的有效字符个数
        for(i=0;i<RNum[j];i++)
            CharRain[j][i] = rand_seed(0, 25)+65;//产生A~Z的随机字符
    }

	u8g2_SetFont(u8g2, u8g2_font_amstrad_cpc_extended_8r);
	u8g2_SetFontDirection(u8g2, 0);

	do
	{
/* 还有行没有满,重新填充显示 */
reShow:
		delay_ms(500);
		for(j=0;j<highNum;j++)//输出整个字符矩阵
		{
			if(RNum[j]<widthNum)//当这一列字符没有填满时
            {
            	for(i=RNum[j]-1;i>=0; i--)//每个字符向下移动一格
                {
                    CharRain[j][i+1]=CharRain[j][i];
                }
                CharRain[j][0] = rand_seed(0, 25)+65;//产生A~Z的随机字符
                RNum[j]=RNum[j]+1;	//这一行的字符数加1
			}
		}

		for(j=0;j<highNum;j++)//输出整个字符矩阵
		{
			y=(j+1)*CharSize;//当前字符的y坐标
			for(i=0;i<RNum[j];i++)
			{
				x=i*CharSize;//当前字符的x坐标
				memset(ascii, 0, sizeof(ascii));
				sprintf(ascii, "%c", CharRain[j][i]);
				u8g2_DrawStr(u8g2, x, y, ascii);//输出当前字符
			}
		}

		u8g2_SendBuffer(u8g2);
		for(j=0;j<highNum;j++)		// 判断是否显示满
		{
			if(RNum[j]<widthNum)	// 当这一行字符没有填满时,重新填充显示
            {
				goto reShow;	
			}
			else
			{
				if(j==highNum-1)	// 当全部行都填满时,重新显示
				{
					goto resetShow;
					break;
				}		
			}
		}
	}
	while (1);
}

int main(void)
{
	unsigned int index, x = 0, yn;
	u8g2_t u8g2;
	uint8_t val = 3;
	
	SysTick_Init(72);
	GPIO_Configuration();
  	u8g2_init(&u8g2);
	while (1)
	{
		index++;
		if(index%10000)
			PBout(12) = ~ PBin(12);
		delay_us(100);
		
		//u8g2_FirstPage(&u8g2);
		//do
		{
			random_pattern(&u8g2);
		} 
		//while (u8g2_NextPage(&u8g2)); 
	}
}

显示效果(从左边填充空白):
在这里插入图片描述

5.3 实现代码雨动画(填充空白,并且每行数据下移)

static void random_pattern(u8g2_t *u8g2)
{
#define High 		64
#define Width 		128
#define CharSize	8

	int highNum=High/CharSize;
    int widthNum=Width/CharSize;
	int charNotFull;
	int tmp[Width/CharSize], rTmp;
	
	int CharRain[High/CharSize][Width/CharSize];
    int RNum[High/CharSize];//每一行的有效字符个数
    int i,j,x,y;
	char ascii[1] = {0};

resetShow:
	u8g2_ClearBuffer(u8g2);
	for(j=0;j<highNum;j++)
	{
		for(i=0;i<widthNum;i++)
			CharRain[j][i] = 32;
	}

	for(j=0;j<highNum;j++)//初始化字符矩阵
    {
        RNum[j] = rand_seed(3, widthNum/2);//这一行的有效字符个数
        for(i=0;i<RNum[j];i++)
            CharRain[j][i] = rand_seed(0, 25)+65;//产生A~Z的随机字符
    }

	u8g2_SetFont(u8g2, u8g2_font_amstrad_cpc_extended_8r);
	u8g2_SetFontDirection(u8g2, 0);

	do
	{
reShow:
		delay_ms(500);
		for(j=0;j<highNum;j++)//输出整个字符矩阵
		{
			if(RNum[j]<widthNum)//当这一列字符没有填满时
            {
            	for(i=RNum[j]-1;i>=0; i--)//每个字符向下移动一格
                {
                    CharRain[j][i+1]=CharRain[j][i];
                }
                CharRain[j][0] = rand_seed(0, 25)+65;//产生A~Z的随机字符
                RNum[j]=RNum[j]+1;	//这一行的字符数加1
			}
		}

		for(j=0;j<highNum;j++)//输出整个字符矩阵
		{
			y=(j+1)*CharSize;//当前字符的y坐标
			for(i=0;i<RNum[j];i++)
			{
				x=i*CharSize;//当前字符的x坐标
				memset(ascii, 0, sizeof(ascii));
				sprintf(ascii, "%c", CharRain[j][i]);
				u8g2_DrawStr(u8g2, x, y, ascii);//输出当前字符
			}
		}

		memset(tmp, 32, sizeof(tmp));
		rTmp = 0;

		memcpy(tmp, CharRain[highNum-1], sizeof(tmp));
		rTmp = RNum[highNum-1];
		
		for(j=highNum-1;j>=0;j--)//输出整个字符矩阵
		{
			memcpy(CharRain[j], CharRain[j-1], sizeof(CharRain[j-1]));
			RNum[j] = RNum[j-1];
		}
		
		memcpy(CharRain[0], tmp, sizeof(tmp));
		RNum[0] = rTmp;

		u8g2_SendBuffer(u8g2);
		for(j=0;j<highNum;j++)		// 判断是否显示满
		{
			if(RNum[j]<widthNum)	// 当这一列字符没有填满时,重新填充显示
            {
				goto reShow;	
			}
			else
			{
				if(j==highNum-1)	// 当全部行都填满时,重新显示
				{
					goto resetShow;
					break;
				}		
			}
		}
	}
	while (1);
}

int main(void)
{
	unsigned int index, x = 0, yn;
	u8g2_t u8g2;
	uint8_t val = 3;
	
	SysTick_Init(72);
	GPIO_Configuration();
  	u8g2_init(&u8g2);
	while (1)
	{
		index++;
		if(index%10000)
			PBout(12) = ~ PBin(12);
		delay_us(100);
		
		//u8g2_FirstPage(&u8g2);
		//do
		{
			random_pattern(&u8g2);
		} 
		//while (u8g2_NextPage(&u8g2)); 
	}
}

显示效果:
在这里插入图片描述

6. 星空动画

引用自:https://blog.csdn.net/qq_51096702/article/details/130460827

typedef struct START
{
    uint16_t x;
    uint16_t y;
    uint16_t speed;
    uint8_t speedcount;
    uint8_t isexist;
} Star;

static Star star[128] = {0};

void Sky_Animation_show(u8g2_t *u8g2) // 星空动画
{
    uint8_t i = 0;
	unsigned int lcdWidth = u8g2_GetDisplayWidth(u8g2);
	unsigned int lcdHeight = u8g2_GetDisplayWidth(u8g2);
    for (i = 0; i < lcdWidth; i++)
    {
        if (star[i].isexist == 0)
        {
            // 设置128个()星星的初始信息
            star[i].x = rand_seed(0, lcdWidth); // 随机生成初始x坐标
            star[i].y = rand_seed(0, lcdHeight);  // 随机生成y的坐标
            star[i].speedcount = 0;
            star[i].speed = rand_seed(0, 8) + 1; // 1-8的数(长度)
            star[i].isexist = 1;
        }
    }
    for (i = 0; i < lcdWidth; i++)
    {
        // 如果这一个星星已经移动到退出屏幕界面
        // 则在最左侧重新生成一颗新星星
        if (star[i].isexist == 0)
        {
            star[i].x = 0;
            star[i].y = rand_seed(0, lcdHeight);
            star[i].speedcount = 0;
            star[i].speed = rand_seed(0, 6) + 1; // 1-6的数(长度)
            star[i].isexist = 1;
        }
        else
        {
            star[i].speedcount++;
            if (star[i].x >= (lcdWidth-4)) // 标记已经退出屏幕
                star[i].isexist = 0;
            // 清除上一个时刻画的星星(的尾巴) 不管有没有操作 都进行清除操作
            u8g2_SetDrawColor(u8g2, 0);
            u8g2_DrawLine(u8g2, star[i].x, star[i].y, star[i].x, star[i].y);
            u8g2_SetDrawColor(u8g2, 2);

            if (star[i].speedcount == star[i].speed) // 运行时间到了一定的长度
            {
                star[i].speedcount = 0; // 复位运行时间并向右移一格
                star[i].x += 1;         // 总之星星的结束需要在这经历124次
            }
            // 只不过有的更快 就能移动更快
            // 从头到尾画出整条星星 不管星星是否已经变化
            u8g2_DrawLine(u8g2, star[i].x, star[i].y, star[i].x + (6 / star[i].speed), star[i].y);
        }
    }
}

int main(void)
{
	unsigned int index, x = 0, yn;
	u8g2_t u8g2;
	uint8_t val = 3;
	
	SysTick_Init(72);
	GPIO_Configuration();
  	u8g2_init(&u8g2);
	while (1)
	{
		index++;
		if(index%10000)
			PBout(12) = ~ PBin(12);
		delay_us(100);
		
		u8g2_FirstPage(&u8g2);
		do
		{
			Sky_Animation_show(&u8g2);
		} 
		while (u8g2_NextPage(&u8g2)); 
	}
}

显示效果:
在这里插入图片描述

7. 源码

在随机位置生成一个图形(测试代码是矩形)源码:
链接:https://pan.baidu.com/s/1ODQzlX-4O6Z5gBeAOLHorA
提取码:v1ks
文件:spi_lcd_u8g2_v02.zip

视频转gif,或者gif转视频:https://www.zhihuilib.com/general/gifcompress

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;