使用stm32f103c8实现一个简单的运动会计时器

没事做突然想写博客,然后发现没有刷什么有价值的题,就写个做实验的记录吧。
首先是构思的思维导图
使用stm32f103c8实现一个简单的运动会计时器_第1张图片
可以看到,我用到了按键结合中断作为输入,使用定时中断来计数,同时显示使用的是oled屏幕,大小为128*64个像素点。
屏幕图片源水印淘宝店( ̄▽ ̄)"
使用stm32f103c8实现一个简单的运动会计时器_第2张图片

ok开始第一步,配置管脚图

oled管脚*7,一个3v3,一个GND,还有五个GPIO输出。
这边用的是串行通信的方法,各个管脚功能分别是
CS:OLED 片选信号。
RST(RES):硬复位 OLED。
DC:命令/数据标志(0,读写命令;1,读写数据)。
SCLK:串行时钟线。在 4 线串行模式下,D0 信号线作为串行时钟线 SCLK。
SDIN:串行数据线。在 4 线串行模式下,D1 信号线作为串行数据线 SDIN。
oled管脚:
CS~PA0
DC/RS~PA2 //这边的DC管脚在程序里面被记作了RS,都一样
RST~PA1
SCLK/D0~PB5
SDIN/D1~PB6

按键管脚*5,一个5v,一个GND,3个GPIO输入。
按键功能为
key0:开始和暂停计时
key1:重置计时
key2:计次,多人跑步的时候记下各个人情况
按键管脚:
key0~PB0
key1~PB1
key2~PB2

ok,既然心里有B数了,那么就可以开始设置初始化函数了

KEY_INIT(),我设置为上拉输入了,管脚改一下,就不多说了。
EXTI_INIT(),从按键设置可以看到,应该选中断线0,1,2,配置管脚为下降沿触发,设置NVIC中断优先级,中断服务程序先留着。
计时器初始化,这边用三号定时中断,TIM3_Int_Init(100,7199),解释一下,这个板子的时钟频率为72Mhz,我们不需要这么高的频率来做定时器中断,所以将他分频,72M除一下设置的分频值7199,得到的结果是1W,也就是现在我们的定时器中断为1Whz,我们想要计时器可以精确到0.01s,所以设置为每数100次触发一个定时器中断。然后中断服务程序就可以用来累加我们的时间了。
oled_init(),这个也就是按照之前的管脚配置做就行了,注意选择串行通信模式

初始化完成,回过头来想一下,我觉得中文显示可以嗦一嗦

先找个软件来,将中文转为16进制的点阵图,放到oled_font里面,我用了四个中文字
时间第名,做成了四个数组,一个数组里面有32个8位的16进制数。这边用了一个OLED_DrawPoint(x,y,1),x和y为像素点的位置,1表示这个点要点亮,否则用0。
另外,一个字为16*16个像素点,这边写入的方式为,先(0,0)->(0,1)->(0,2)…(0,15)->(1,0)->(1,2)…(15,15)。也就是先写完一列16个像素,再换下一列。但是,我们数组中的数是8位的,也就是只保存了8个像素点的状态,所以一列要两个数来表示。

void OLED_ShowMyChinese(u8 x,u8 y,u8 n)
{
	u8 temp,t,t1;//c语言要先声明变量,t,t1用来for循环,temp是取数组值
	u8 y0=y;     //保存列开头位置
	for(t=0;t<32;t++)   //遍历数组中32个数
	{
		temp=oled_chinese_1616[n][t];    //第n个汉字的第t个数
		for(t1=0;t1<8;t1++)              //遍历一个数中的八个位
		{
			if(temp&0x80)            //查看temp最高位,看像素点状态
				OLED_DrawPoint(x,y,1);    //如果最高位是1,就点亮
			else
				OLED_DrawPoint(x,y,0);    //否则暗着
			temp<<=1;                      //左移查看下一个像素
			y++;                     //移动到下一个像素点的物理位置
			if((y-y0)==16)  //这边是判断是否写完一列
			{               //画完第一个8位数字后y不变回0
				y=y0;   //画完两个8位数字后y变回0
				x++;
				break;
			}
		}
	}
}

阿西吧,终于到了最重要的部分了,计时部分开始啦

首先想一下,时间怎么保存呢?当然是用结构体啦

struct StuTime{
	u8 m,s;//八位的分钟,秒
	u16 ms;//16位的毫秒
}MyTime={0,0,0};//初始化为0:0.00

写在timer.c 这个文件最合适啦。这样定时中断服务就可以直接累加时间了。

void TIM3_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) 
	{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  
		if(!MyFlag)      //flag用来看有不有暂停计时
			return;
		MyTime.ms+=10;   //累加毫秒
		if(MyTime.ms>=1000)//累加秒
		{
			MyTime.ms%=1000;
			MyTime.s++;
			if(MyTime.s>=60)//累加分钟
			{
				MyTime.s%=60;
				MyTime.m++;;
			}
		}
	}
}

那么暂停的方法上面也说了,用一个flag就行了

void MyTime_GO(void)
{
	MyFlag=!MyFlag;
}

机智呀使用stm32f103c8实现一个简单的运动会计时器_第3张图片
这个函数将被外部中断服务0程序调用

void EXTI0_IRQHandler(void)
{
	delay_ms(10);
	MyTime_GO();
	EXTI_ClearITPendingBit(EXTI_Line0);
}

另外三个按键功能也是这样的,所以主要的功能函数都在timer.c这个文件里面了
重置计时功能

void MyTime_RST(void)
{
	MyFlag=0;   //先把flag设为0,暂停计时
	MyTime.m=0; //再将时间全重置为0
	MyTime.s=0; //这样以后呢,就不多忙活了
	MyTime.ms=0;

}

然后是计次功能,显示的样式为三行,这个oled可以写四行字,这边选择下三行,第一行留给实时的时间。
这边显示的一行为第nn名: nn:nn.nn,这个n就是到时候要替换的数字了。

void MyTime_CNT(void)
{
	u8 static MyCntFlag=1;//给这个函数加个锁,防止连续触发
	u8 static MyCnt=0;    //打算用三行来计次这个记录我这是第几次计次了,改写哪行
	u8 MyPageY;
	if(!MyCntFlag)      //查看有不有锁上
		return ;
	MyCntFlag=0;        //锁上
	MyPageY=(MyCnt%3+1)*16;		//计算我改写哪一行,也就是y的值
	//显示中文了
	OLED_ShowMyChinese(0,MyPageY,2);//显示我中文字库的第二个字,就是"第"
	OLED_ShowMyChinese(32,MyPageY,3);//显示"名",和下面数字和在一起就是‘第n名’了
	OLED_ShowNum(16,MyPageY,MyCnt+1,2,16);
	//显示分隔符
	OLED_ShowChar(48,MyPageY,':',16,1);
	OLED_ShowChar(80,MyPageY,':',16,1);
	OLED_ShowChar(104,MyPageY,'.',16,1);
	//替换数字,都是两位的数字
	OLED_ShowNum(64,MyPageY,MyTime.m,2,16);//分钟
	OLED_ShowNum(88,MyPageY,MyTime.s,2,16);//秒钟
	OLED_ShowNum(112,MyPageY,MyTime.ms,2,16);//毫秒
	MyCnt++;//记下我写过这行了,下次写下一行
	MyCntFlag=1;//记得把锁打开
}

啊哈,到这边计时已经实现了,可以暂停和重置还有计次,但是第一行的实时时间还没有显示,也没有看到过刷新GRAM的函数,这样是不行的,oled啥玩意都不会显示

所以现在开始搞定最后一个main函数
首先,把七大姨八大叔全部叫来初始化一下,调用一个他们的init函数
然后进入永不退出的while(1)循环啦
第一行实时显示的样式为时 间: nn:nn.nn,同上,n为要替换的数字
这边用到了一个GetTime函数,先看一下这个函数,这个函数在timer.c里面

u32 GetTime(void)
{
	return (u32)((MyTime.m<<24)+(MyTime.s<<16)+(MyTime.ms));
}

还记的我们的时间结构体吗,8位的分钟,8位的秒,16位的毫秒,加一起不就可以组成一个32位的数啦,这边就用一个32位的数返回给要查看时间的同学了,前8位为分钟,后面8位为秒,再最后16位就是毫秒了。

	while(1)
	{
		u8 tmp;
		u32 reVal=GetTime();          //从timer.c获取时间
		OLED_ShowMyChinese(0,0,0);    //显示‘时’
		OLED_ShowMyChinese(32,0,1);   //显示‘间’
		OLED_ShowChar(48,0,':',16,1); //显示分隔的冒号
		OLED_ShowChar(80,0,':',16,1); 
		OLED_ShowChar(104,0,'.',16,1);
		tmp=(reVal&0xff000000)>>24;//根据前面GetTime的规则取出分钟
		OLED_ShowNum(64,0,tmp,2,16);//显示分钟
		tmp=(reVal&0x00ff0000)>>16;//根据前面GetTime的规则取出秒
		OLED_ShowNum(88,0,tmp,2,16);//显示秒
		tmp=(reVal&0x0000ffff);//根据前面GetTime的规则取出毫秒
		OLED_ShowNum(112,0,tmp,2,16);//显示毫秒
		OLED_Refresh_Gram();//刷新oled,注意,整个项目中不要有多个刷新调用
		delay_ms(10);       //否则你的屏幕会乱掉,错位
		                    //这样看来他的刷新函数应该没有锁保护了
	}
 }

附上全文代码,IDE是uv4,和现在的新版本有点不兼容,但不影响阅读和移植。
https://pan.baidu.com/s/15ojkzdAZskfZgF5cXR7anA

好了,到这里也就写完了,连个线试一下,然后就可以开始debug了使用stm32f103c8实现一个简单的运动会计时器_第4张图片

你可能感兴趣的:(stm32,计时器,单片机)