基于STM32F103C8T6的电子沙漏

应客户需求,制作一款可以在显示屏上显示出沙漏的剩余时间,而且可以选择自定义时间精准一分一秒。

一、显示模块

采用0.96寸四针OLED显示屏IIC接口。

OLED的数据存储格式:
  纵向8点,高位在下,先从左到右,再从上到下,每一个Bit对应一个像素点

  *      B0 B0                  B0 B0
  *      B1 B1                  B1 B1
  *      B2 B2                  B2 B2
  *      B3 B3  ---------->  B3 B3 
  *      B4 B4                  B4 B4  |
  *      B5 B5                  B5 B5  |
  *      B6 B6                  B6 B6  |
  *      B7 B7                  B7 B7  |
  *                                              |
  *  -----------------------------------
  *  |   
  *  |   B0 B0                  B0 B0
  *  |   B1 B1                  B1 B1
  *  |   B2 B2                  B2 B2
  *  --> B3 B3  --------->  B3 B3
  *      B4 B4                  B4 B4
  *      B5 B5                  B5 B5
  *      B6 B6                  B6 B6
  *      B7 B7                  B7 B7

OLED三角形绘制函数,调用此函数后,要想真正地呈现在屏幕上,还需调用更新函数

void OLED_DrawTriangle(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1, uint8_t X2, uint8_t Y2, uint8_t IsFilled)
{
	uint8_t minx = X0, miny = Y0, maxx = X0, maxy = Y0;
	uint8_t i, j;
	int16_t vx[] = {X0, X1, X2};
	int16_t vy[] = {Y0, Y1, Y2};
	
	if (!IsFilled)			//指定三角形不填充
	{
		/*调用画线函数,将三个点用直线连接*/
		OLED_DrawLine(X0, Y0, X1, Y1);
		OLED_DrawLine(X0, Y0, X2, Y2);
		OLED_DrawLine(X1, Y1, X2, Y2);
	}
	else					//指定三角形填充
	{
		/*找到三个点最小的X、Y坐标*/
		if (X1 < minx) {minx = X1;}
		if (X2 < minx) {minx = X2;}
		if (Y1 < miny) {miny = Y1;}
		if (Y2 < miny) {miny = Y2;}
		
		/*找到三个点最大的X、Y坐标*/
		if (X1 > maxx) {maxx = X1;}
		if (X2 > maxx) {maxx = X2;}
		if (Y1 > maxy) {maxy = Y1;}
		if (Y2 > maxy) {maxy = Y2;}
		
		/*最小最大坐标之间的矩形为可能需要填充的区域*/
		/*遍历此区域中所有的点*/
		/*遍历X坐标*/		
		for (i = minx; i <= maxx; i ++)
		{
			/*遍历Y坐标*/	
			for (j = miny; j <= maxy; j ++)
			{
				/*调用OLED_pnpoly,判断指定点是否在指定三角形之中*/
				/*如果在,则画点,如果不在,则不做处理*/
				if (OLED_pnpoly(3, vx, vy, i, j)) {OLED_DrawPoint(i, j);}
			}
		}
	}
}

二、整体思路

时间设定思路:利用串口将所需要设定的时间发送给单片机,然后在对时间数据进行沙漏动画处理。对于串口发送过来的设定时间以30秒为基础进行取整和取余,得到30秒动画的循环次数和最后一步动画的剩余时间数。

if (Serial_GetRxFlag() == 1)	//如果接收到数据包
{
	Data_Flag = 1;
	Beg_Flag = 1;
			
	Serial_TxPacket[0] = Serial_RxPacket[0];	//测试数据是否接收到
	Serial_TxPacket[1] = Serial_RxPacket[1];
	Serial_TxPacket[2] = Serial_RxPacket[2];
	Serial_TxPacket[3] = Serial_RxPacket[3];
	Serial_SendPacket();
}
	if(Data_Flag == 1)
	{
	/*****设置各个参数的初始值*****/
		ALL_TIME = ((Serial_TxPacket[0]*60)+Serial_TxPacket[1]);//设定时间
		y1=0;
		y2=0;
		y3=63;
		Number=0;             //当前循环数

		n = ALL_TIME / 30;    //总循环数
		s = ALL_TIME % 30;    //剩余时间
		Data_Flag = 0;
	}
		if(Beg_Flag == 1)
		{
			/*****沙漏开始工作*****/
			OLED_Clear();
			
			Min = ALL_TIME / 60;//显示分
			Sec = ALL_TIME % 60;//显示秒
			
			if((y1==30)&&(Number != n))//30秒动画完成一次
			{
				y1=0;
				Number++;
			}
			if(Number == n)//当前循环数=总循环数
			{
				y2 = 30 - s;
				Number++;
			}
			if((y2==30)&&(Number != n))//最后一组动画完成
			{
				
				Number++;
			}

动画设定思路:首先定义坐标轴,左上角为(0, 0)点,横向向右为X轴,取值范围:0~127;纵向向下为Y轴,取值范围:0~63;
       

以线段(63,0)、(63,63)为分界线,左侧为剩余时间显示,右侧为沙漏动画显示

画出沙漏的框架:

OLED_DrawLine(63, 0, 127, 63);       //在(63, 0)和(127, 63)位置之间画直线
OLED_DrawLine(63, 63, 127, 0);            
OLED_DrawLine(63, 0, 127, 0);        //在(63, 63)和(127, 0)位置之间画直线
OLED_DrawLine(63, 63, 127, 63);

沙漏动画思路:设定一个沙漏漏完为30秒,对于沙漏的动画处理,以30秒为循环进行显示,最后不满30秒的,对沙漏上方进行实际像素点的填充,再进行下漏操作。 

30秒一组的循环动画:

制作上面的沙子越来越少的动画,简单来说就是上面的那个实心三角形越来越小,Y1的值越来越大,三个顶点中,有一个是不变的(95,31),上面部分的三角形为等腰直角三角形,我们根据三角函数可以得到另外两个顶点的坐标(63+Y1,Y1)(127-Y1,Y1)

制作下面沙子越来越多的程序,下面的沙子越来越多,Y1的值越来越大,相对就比较简单,有两个点永远不变,那就是 (63,63)和(127,63),变的只是最上面那个顶点,而且上面那个顶点的X坐标一直没变,就只是Y坐标一直在变(95,63-Y1)

最后在加入中间沙子流动的动画,实际就是一根线.

代码部分:

if( (Number <= n)&&(Number != n) )
    {
		OLED_DrawLine(63, 0, 127, 63);   //在(63, 0)和(127, 63)位置之间画直线
		OLED_DrawLine(63, 63, 127, 0);   
		OLED_DrawLine(63, 0, 127, 0);    //在(63, 63)和(127, 0)位置之间画直线
		OLED_DrawLine(63, 63, 127, 63);
				
		OLED_DrawTriangle(63+y1, y1, 95, 31, 127-y1, y1, OLED_FILLED);//上层	
		OLED_DrawTriangle(63, 63, 95, 63-y1, 127, 63, OLED_FILLED);//下层
		OLED_DrawLine(95, 31, 95, 63);   //在(95, 31)和(95, 63)位置之间画直线
	}

不满30秒的动画:

在时间设定的时候,每次设定的时间不一定都是30的整数倍,所以最后一组动画时需要特殊处理。 即上半部分沙漏的Y2需要有一个初始值,Y2=30-剩余时间数,使得Y2++到30时,正好对应到剩余时间数倒数完成。下半部分沙漏的沙子增加动画,使用变量Y3,初值为63,Y3--,当Y3=Y2时,即表示上半部分沙子漏完,下半部分沙子全部接收完成,动画结束。

代码部分:

if(Number == n+1)
	{
		OLED_DrawLine(63, 0, 127, 63);    //在(63, 0)和(127, 63)位置之间画直线
		OLED_DrawLine(63, 63, 127, 0);    
		OLED_DrawLine(63, 0, 127, 0);     //在(63, 63)和(127, 0)位置之间画直线
		OLED_DrawLine(63, 63, 127, 63);
				
		OLED_DrawTriangle(63+y2, y2, 95, 31, 127-y2, y2, OLED_FILLED);//上层	
		OLED_DrawTriangle(63, 63, 95, y3, 127, 63, OLED_FILLED);//下层
		OLED_DrawLine(95, 31, 95, 63);    //在(95, 31)和(95, 63)位置之间画直线
	}

定时器函数 :实现沙漏精确的时间计数,采用中断定时器的方法每秒对变量实行加/减操作。

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//1秒钟进入一次中断
	{
	    if(Beg_Flag == 1)//动画开始
		{
		    if( ALL_TIME >=0 )//得到设定时间,开始倒数
			{
		        ALL_TIME --;
			}
			
			if(y1<=30)//30秒循环组动画,y1每秒+1
			{
			    y1++;
			    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
			}
			if((y2<=30)&&(Number == n+1)&&(y2!=y3))//不满30秒的动画
			{
			    y2++;
			    y3--;
		        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
			}
		 }
	}
}

三、实物效果

电子沙漏动画

 

该项目主要需要去了解OLED屏是怎么进行数据存储的,IIC通信原理,较为简单的一个小项目,硬件较少,代码逻辑一般,适合新手练手。

  • 32
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值