基于STM32F103的“口袋”嵌入式学习/控制平台的模拟时钟
基于STM32F103R8T6的可设置时间、整点报时的模拟时钟(精确到分钟即可),整点的时候可以通过板上的蜂鸣器以声音报时;转动板子,LCD屏上的时钟自动跟着旋转,这要用到板上的姿态传感器来感测。
标签
嵌入式系统
Y__
更新2021-03-01
1280

一、项目完成功能

1.时间显示功能:上电后显示初始在代码中写入的时间,并且随着时间的变化,表盘上的指针也会发生相应变化。

2.整点报时功能:当到达一个整点时,蜂鸣器会鸣叫一声。

3.设置时间功能:可以根据按键,对显示的时间进行设置。

4.重力调整姿态功能:根据板子的朝向,表盘也会发生相应的变化。

二、所用板上资源

1.定时器2

2.LCD——SPI协议

3.按键——外部中断

4.MPU6050——I2C协议

三、制作过程

1.表盘

①画圆,使用Bresenham算法画圆。

void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color)
{
	int a,b;
	int di;
	a=0;b=r;	  
	di=3-(r<<1);             //判断下个点位置的标志  
	while(a<=b)
	{
		LCD_DrawPoint(x0-b,y0-a,color);             //3           
		LCD_DrawPoint(x0+b,y0-a,color);             //0           
		LCD_DrawPoint(x0-a,y0+b,color);             //1                
		LCD_DrawPoint(x0-a,y0-b,color);             //2             
		LCD_DrawPoint(x0+b,y0+a,color);             //4               
		LCD_DrawPoint(x0+a,y0-b,color);             //5
		LCD_DrawPoint(x0+a,y0+b,color);             //6 
		LCD_DrawPoint(x0-b,y0+a,color);             //7
		a++;
		//使用Bresenham算法画圆     
		if(di<0)di +=4*a+6;	  
		else
		{
			di+=10+4*(a-b);   
			b--;
		} 	
	}
}
Draw_Circle(120,120,115, WHITE);//画圆

②画刻度,使用画线函数

void LCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color)
{
	u16 t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance;
	int incx,incy,uRow,uCol;
	delta_x=x2-x1; //计算坐标增量 
	delta_y=y2-y1;
	uRow=x1;//画线起点坐标
	uCol=y1;
	if(delta_x>0)incx=1; //设置单步方向 
	else if (delta_x==0)incx=0;//垂直线 
	else {incx=-1;delta_x=-delta_x;}
	if(delta_y>0)incy=1;
	else if (delta_y==0)incy=0;//水平线 
	else {incy=-1;delta_y=-delta_y;}
	if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
	else distance=delta_y;
	for(t=0;t<distance+1;t++)
	{
		LCD_DrawPoint(uRow,uCol,color);//画点
		xerr+=delta_x;
		yerr+=delta_y;
		if(xerr>distance)
		{
			xerr-=distance;
			uRow+=incx;
		}
		if(yerr>distance)
		{
			yerr-=distance;
			uCol+=incy;
		}
	}
}
for(i = 0; i <60; i++)//画刻度			 
   LCD_DrawLine(Frame1_X[i],Frame1_Y[i],Frame2_X[i],Frame2_Y[i],WHITE);
		

③画指针,使用画线函数

LCD_DrawLine(120,120,Sec_X[flag1], Sec_Y[flag1],RED);
LCD_DrawLine(120,120,Min_X[flag2], Min_Y[flag2],BLUE);
LCD_DrawLine(120,120,Hou_X[flag3], Hou_Y[flag3],GREEN);

在表盘制作过程中,本想使用软件对图片取模,再导入图片到LCD上,尝试多次失败后换了该方案,至此,表盘制作完成。

2.指针转动及整点报时

在此我设置一个1s的定时器与3个分别对应秒针、分针、时针的变量,当时间发生改变,变量也发生改变,变量对应着不同的画线函数,故三根指针也会随着时间发生转动。该板上的蜂鸣器为无缘蜂鸣器,故在此使用PWM波进行驱动,当时针变量发生改变时,驱动蜂鸣器进行鸣叫。

void TIM3_Int_Init(u16 arr, u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSture;
	NVIC_InitTypeDef NVIC_InitSturcture;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能定时器3时钟
	
	TIM_TimeBaseInitSture.TIM_Period = arr;
	TIM_TimeBaseInitSture.TIM_Prescaler = psc;
	TIM_TimeBaseInitSture.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitSture.TIM_CounterMode = TIM_CounterMode_Up;

	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitSture);
	
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
	
	NVIC_InitSturcture.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitSturcture.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitSturcture.NVIC_IRQChannelPreemptionPriority = 3; //子优先级为3
	NVIC_InitSturcture.NVIC_IRQChannelSubPriority = 0; //抢占优先级为0
	NVIC_Init(&NVIC_InitSturcture);
	
	TIM_Cmd(TIM3, ENABLE);
}
void BEEP_PWM(void)
{
	u16 i;
	for(i=0;i<200;i++)
	{
		GPIO_ResetBits(GPIOC,GPIO_Pin_10);//buzzer output low
		delay_us(500);
		GPIO_SetBits(GPIOC,GPIO_Pin_10);
		delay_us(500);
	}
}

3.按键控制时间

三个按键对应三个变量,当按键按下,触发外部中断,相应变量+1,使指针发生转动。

void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line9)!=RESET )//通道9
	{
		delay_ms(10);				 //消抖
		if(KEY1 == 1)	  		 //WK_UP按键
		{				 
			flag1++;
			if(flag1 >= 61)
				flag1 = 1;			
			BEEP_PWM();
		}
		EXTI_ClearITPendingBit(EXTI_Line9);  //清除LINE9上的中断标志位
	}	

	if(EXTI_GetITStatus(EXTI_Line8)!=RESET )//通道8
	{
		delay_ms(10);				//消抖
		if(KEY2 == 1) 			//按键KEY1
		{
			flag2++;
			if(flag2 >= 61)
				flag2 = 1;	
			BEEP_PWM();
		}
		EXTI_ClearITPendingBit(EXTI_Line8);  //消除LINE8上的中断标志位
	}
}

//外部中断11服务程序
void EXTI15_10_IRQHandler(void)
{
	delay_ms(10);				//消抖
	if(KEY3 == 1)	 				//按键KEY0
	{
		flag3++;
		if(flag3 >= 13)
			flag3 = 1;
		BEEP_PWM();
	}		 
	EXTI_ClearITPendingBit(EXTI_Line11);  //清除LINE4上的中断标志位  
}

4.姿态传感器

板上的MPU6050连接了I2C接口,在此我参考正点原子的MPU6050实验代码进行编写,获取陀螺仪值,再由官方的DMP eMPL库获取欧拉角值。

u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
    u8 buf[6],res;  
	res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);
	if(res==0)
	{
		*gx=((u16)buf[0]<<8)|buf[1];  
		*gy=((u16)buf[2]<<8)|buf[3];  
		*gz=((u16)buf[4]<<8)|buf[5];
	} 	
    return res;;
}
u8 mpu_dmp_init(void)
{
	u8 res=0;
	MPU_IIC_Init(); 	//初始化IIC总线
	if(mpu_init()==0)	//初始化MPU6050
	{	 
		res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器
		if(res)return 1; 
		res=mpu_configure_fifo(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置FIFO
		if(res)return 2; 
		res=mpu_set_sample_rate(DEFAULT_MPU_HZ);	//设置采样率
		if(res)return 3; 
		res=dmp_load_motion_driver_firmware();		//加载dmp固件
		if(res)return 4; 
		res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
		if(res)return 5; 
		res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP|	//设置dmp功能
		    DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|
		    DMP_FEATURE_GYRO_CAL);
		if(res)return 6; 
		res=dmp_set_fifo_rate(DEFAULT_MPU_HZ);	//设置DMP输出速率(最大不超过200Hz)
		if(res)return 7;   
		res=run_self_test();		//自检
		if(res)return 8;    
		res=mpu_set_dmp_state(1);	//使能DMP
		if(res)return 9;     
	}else return 10;
	return 0;
}

四、不足之处

1.程序烧录后MPU6050初始化较慢,进入正常运作需要较长时间。

2.设置时间时,若按下按键过快,局部刷新速度跟不上会导致指针残留。

3.表盘制作没有使用图片导入,而是自己编写代码制作表盘。

在之后的时间里,我会尽力解决以上问题,并且尽量优化自己的代码。

五、总结

非常感谢此次硬禾学堂的寒假一起学,在制作项目的过程中,我对单片机的理解有了新的突破,对于STM32F103上资源有了更多的了解,这对我在今后的学习中有着非常大的帮助,再次感谢硬禾学堂举办的此次活动,希望在今后还能参与到这些活动中来。

附件下载
模拟时钟.rar
团队介绍
南昌大学信息工程学院电赛基地
团队成员
余斯吉
南昌大学_2019级_信息工程学院_测控技术与仪器
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号