面向应用学习stm32(5)-SysTick滴答定时器-延时-计时

前导:本文的目的与,意在于面向应用的学习单片机,故不会涉及太多的原理知识,例如寄存器之类的。

主要目的在于面向应用的学习单片机,学会单片机的基础用法,开发板采取野火的指南者f103。

作者大二小白,写的不好的地方轻点喷,欢迎评论区交流

全部工程代码开源在Gitee仓库

SysTick简介

这是一个24位的系统节拍定时器system tick timer,SysTick,具有自动重载和溢出中断功能,所有基于Cortex_M3处理器的微控制器都可以由这个定时器获得一定的时间间隔。

每当重装载寄存器的值递减到0之后,系统定时器就会产生一个中断,如此往复。例如从1000递减到0后,产生一个中断,执行完中断服务函数后,再回到1000,再次减少到0,再次产生中断,进入中断处理,如此往返。

简单点说,systick就是stm32内置的一个系统中断,为的就是给操作者提供一个心跳节拍吧,当然,可以通过设置重装载计数的值,用来精确定时。

它产生一次中断的计算规则是这样的

  • 重装载计数次数:一般写成,SystemCoreCLock(主频)/值这个形式,也可以直接给定数值
  • 产生一次中断的时间:Time = 重装载计数次数 * (1/SystemCoreCLock)单位为s 。

举个例子,例如我们写入1000到重装载寄存器中。

stm32f103的默认主频是72M,那么重装载计数次数就是 72M/1000 = 72 000 ;

产生一次中断的时间就是 72000 * (1/72M) = 0.001s = 1ms

代码编写

你无法想象到,SysTick的代码编写有多么简单,真的是一行函数完成配置。

在While(1)前面加一行

SysTick_Config(SystemCoreClock / 1000);

游戏结束,现在每1ms,程序都会进入systick的中断服务函数,这个函数在it.c里。

image-20220506092427294

小实验

实验1 计算秒数

刚刚我们前面不是已经实现了配置SysTick 1ms进一次中断吗?

现在我需要计算秒数的话,是不是只需要判断进去1000次中断时,1ms*1000 = 1s,达到1s后,秒数++就可以了。

代码就是

  • 声明一个int second秒变量
  • 在it.c里,extern int second,然后声明一个count变量,每次++
  • 当count++到1000后,second++,count=0;(清零后下次才能重新从1000)

思路分析完毕,开始写代码。

照例还是复制之前LCD_Test的工程

it.c里。

extern int second;
int count=0;
void SysTick_Handler(void)
{
	if(++count==1000)
	{
		second++;
		count=0;
	}
}

main.c里

char disp[20];
int second = 0;

void LCD_Show();

int main (void)
{
	//...省略初始化和其他操作
	SysTick_Config(SystemCoreClock / 1000);
	
	while (1)
	{
		LCD_Show();
	}
}
void LCD_Show()
{
	sprintf(disp,"S:%d",second);
	ILI9341_DispStringLine_EN(LINE(1),disp);
}

实验2 时钟

在LCD实现一个简单的时钟的效果。如 12:12:45,要求是时分秒都能正常走和进位

上一个实验里,我们其实已经实现了秒数,所以在本次实验中。我们的秒数就可以拿来控制分钟了,也就是it.c里不要做任何改变,写出如下逻辑即可

void Time_Logic()
{
    //到达60s,那就是一分钟到了
	if(second==60){
		second = 0;
		minute++;
	}
    //到达60分钟,那就是1h到了
	if(minute==60){
		minute = 0;
		hour++;
	}
	if(hour==24){
		hour=0;
	}
}

修改一下LCD的显示,注意这里的占位符%2d,还是比较重要的,如果没有这个占位符的话,在液晶上

1和10的占位个数不一样,就会出一些小问题,%2d就表示了,不管是两位数字还是一位数字,都会占两格

void LCD_Show()
{
	sprintf(disp,"%2d:%2d:%2d",hour,minute,second);
	ILI9341_DispStringLine_EN(LINE(1),disp);
}

主函数如下

	while (1)
	{
		LCD_Show();
		Time_Logic();
	}

实验3 交通灯

默认情况下 亮3s绿灯,5s红灯,1s黄灯

当按键1按下,代表有紧急事件,红灯常亮,当按键1再次按下,代表紧急事件停止,恢复默认情况

我的思路是这样的

  • 3s+5s+1s为9s,9s = 9000ms,那就是以9000ms为一个来回。
  • 在it.c中,分为三段,每段对应一个颜色
  • 声明一个mode变量,mode为1的时候正常情况,mode=0的时候红灯常亮

当然也有其他写法,比如it.c里计算秒数,主函数里用这个秒数在1-9的情况里亮灯,到9后清零,就看各位想怎么做了

所以我们先这样写,main.c里声明一个全局变量color

it.c中

extern int color;
extern int mode;
int count;
void SysTick_Handler(void)
{
	if(mode==1)
	{
		count++;
		if(count<=3000)
		{
			color = LED_GREEN;
		}
		else if(count>=3000 && count<=8000)
		{
			color = LED_RED;
		}
		else if(count>=8000 && count<=9000)
		{
			color = LED_YELLOW;
		}
		else{
			count=0;
		}
	}
}

然后在主函数main.c里,我们用这个color变量控制灯的颜色,按键控制模式

void LED_Control()
{
	if(mode==1)
	{
		LED_Color(color);//这里的color会在it.c里改变
	}
	if(mode==0)
	{
		LED_Color(LED_RED);
	}
}

void KEY_Control()
{
	char value = KEY_Scan();
	if(value=='1')
	{
		mode = !mode;
	}
}

最后while(1)里就变成这样了

	while (1)
	{
		LED_Control();
		KEY_Control();
	}

实验4 准确延时

思考一下,我们之前的延时怎么实现的?

void Delay(int i){
	for(;i>0;i--);
}

事实上这种延时是不准确的,你并不知道它到底能延时多久。所以我们可以利用systick实现准确的延时。

在delay.c里写如下代码,并在delay.h里声明

void Delay_ms(int time)
{
	TimeDelay = time;
	while(TimeDelay!=0);
}

这里的TimeDelay在it.c里面extern引入。

extern int TimeDelay;
void SysTick_Handler(void)
{
	TimeDelay--;
}

然后主函数这样就可以尝试了

	while (1)
	{
		LED_Color(LED_PURPLE);
		Delay_ms(500);
		LED_Color(LED_OFF);
		Delay_ms(500);
	}

然后我们可以改造我们之前的LED_Flashing闪烁函数和LED_Stream流水灯函数,让他们指定精确时间

void LED_Flashing(int color,int speed)
{
		LED_Color(color);
		Delay_ms(speed);
		LED_Color(LED_OFF);
		Delay_ms(speed);
}
void LED_Stream(int speed)
{
	int i;
	for(i = 0; i<8; i++)
	{
		LED_Color(i);
		Delay_ms(speed);
	}
}

然后做如下测试,

while (1)
{
		LED_Flashing(LED_BLUE,3000);
		LED_Stream(200);
}

同理,按键的消抖也可以这么改了

char KEY_Scan()
{
	char key = '0';
	if(KEY1)
	{
        //这里的延时需要根据每个人板子的按键自己调整,因为消抖是在处理硬件问题,每个人按键体质不一样
		//但至少比你看着之前那种奇奇怪怪的数值号,这个延时你知道大概能延时多久
        Delay_ms(200);
		key = '1';
	}
	if(KEY2)
	{
		Delay_ms(200);
		key = '2';
	}
	return key;
}

然后可以做这个测试

int count = 0;
void Test_Delay2()
{
	char key = KEY_Scan();
	if(key=='1')
	{
		count++;
	}
	sprintf(disp,"Count:%d",count);
	ILI9341_DispStringLine_EN(LINE(1),disp);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这里煤球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值