蓝桥杯51单片机设计

发布于:2025-07-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

#STC15系列单片机定时器高阶#

STC15系列单片机:

定时器2相关知识点

代码使用

滴答定时器

每次水滴从高处落下的时间相同,只需要记录落下的次数,就可以计时

定义ms级的滴答定时器, unsigned long类型,4个字节,

范围0~4294967295,每过1ms去加1,要运行49天才会溢出

应用1.计时器

普通计时器:if(count==20) count=0.....

可以转换成:ms++; if(ms%100==0).....

定时器注意事项

.优先级问题

优先级从高到低

外部中断0→定时器中断0→外部中断1→定时器中断1→串口中断1→串口中断2→外部中断2→外部中断3→定时器中断2

2.实现多个计时,需要多个定时器吗?

不需要,利用多个变量自加,使用一个定时器即可

3.在一个1ms的定时器中断中进行count++,在main.c中判断

if(count==100)count=0

L1闪烁,为什么没有正常执行?

这种判断是常见的错误:因为,如果main.c中有延时函数,且总是延时>1ms,与判断函数执行时间不匹配,所以判断不成立

解决方法:将代码放进定时器中断函数即可

4.定时器中断的定时时间以及中断中代码量该如何权衡?

最简单点就是定时时间长一点,中断中代码少一点

若定时时间短,中断代码写的多,会有的问题:

①中断代码内执行时间大于定时时间,则会一直卡在定时器中断

②中断中代码执行时间小于定时时间,但中断代码量过多仍有弊处

eg.若主函数调用延时函数时触发中断,若延时500微秒,中断执行时间位100微秒,则实际延时时间位600微秒,对一些对延时严格的外设影响很大(DS18B20)用延时产生的方波也不准确

如果遇到DS18B20用定时器不能正常工作,可以每次读取温度前TR1=0定时器关闭,读完后TR1=1定时器打开

#定时器任务调度#

①单片机框架:

顺序执行;定时器任务调度;操作系统

顺序执行

在编码过程中,一部分操作加上delay延时函数

eg.点灯+delay;数码管+delay(消隐);按键+delay(消抖);传感器采集+delay

缺点:浪费时间,会在delay延时函数内做没有用的编程

问题一.数码管显示时间中间间隔过大,颜色显示不均匀

问题二:按键响应慢

问题三:传感器采集太快

优化方案:定时器调度

定时器计时:给各个模块规定刷新时间,到刷新时间给标志位

主函数:判断标志位,来决定执行哪个模块

定时器1用来给工程设置时间

注意:在主函数EA=1

SMG【】数组内容为数码管数字显示的下标

dot[]数组代表着数码管第几位是否携带小数点

LED【】控制灯的位置和亮灭情况

数码管和灯显示是一样的,亮为低电平,灭为高电平

所以显示的时候

亮为temp &=~0x01<<pos)

灭为temp|=0X01<<pos

②smg/led/relay函数

#include "smg.h"
extern uchar state_relay;
code uchar segment[]={
0xc0,  
0xf9,  
0xa4,  
0xb0,  
0x99,  
0x92,  
0x82,  
0xf8,  
0x80,  
0x90,
0xff,
0xbf};
/*
数码管的余辉效应的delay_ms舍去

pos用定时器传送,每一毫秒,pos+1,最终成为稳定状态,哪一个灯亮或者哪一个数码管显示

dot是小数点显示
dot[]数组代表着数码管第几位是否携带小数点

temp[pos]表示第几位数码管,指向主文件中SMG数组内容
SMG[]数组内容为数码管数字显示的下标

*/
void smg(uchar *SMG,uchar pos,uchar *dot)
{
	P0=0xff;
	hc573(7);
	P0=0x01<<pos;
	hc573(6);
	if(dot[pos]==0)
	{
		P0=segment[SMG[pos]];
	}
	else
	{
		P0=segment[SMG[pos]]&0x7f;
	}
	hc573(7);
}

/*
static uchar temp 静态变量,进入程序,保留上一次temp赋予的值

数组LED中,0为灯灭,1为灯亮
LED[]={}控制灯的位置和亮灭情况
pos从0开始递增,跟数组下标无差别

灯1灭0亮
所以&=~为亮  |=为灭
而蜂鸣器继电器1为运作,0为停止
所以|=为运作  &=~为停止

最终的结果就是LED[0]=1,就是第一个灯亮起,其他无关
*/
void led(uchar *LED,uchar pos)
{
	static uchar temp=0xff;
	if(LED[pos])
	{
		temp &=~(0x01<<pos);
	}
	else
	{
		temp |=0x01<<pos;
	}
	P0=temp;
	hc573(4);
}

void relay(state_relay)
{
	uchar temp;
	if(state_relay)
	{
		temp |=0x01;
	}
	else
	{
		temp &=~0x01;
	}
	P0=temp;
	hc573(5);
}

显示任务:

 LED任务,数码管任务,蜂鸣器继电器任务

if(display_dly<500)  return;

display_dly = 0;

在中断函数内调用,才能延时,都是通过P0来实现 

采集任务:

 温度采集,距离采集,时间采集,AD采集,EERTROM采集,频率采集,键值采集

if(collect_dly<500)  return;

collect_dly = 0;

采集任务跟数码管分开,因为采集任务时间可以慢点 

按键处理任务

 在key.c文件中,delay_ms延时函数省去 

每隔十毫秒进行一次,代替按键消抖过程(时间短  

为什么每个标志位都是取<而不是=?

因为若主函数执行采集任务,执行时间>1ms

而中断函数也是每隔1ms加一

主函数执行时间>1s,=不会执行,已经错过=

 

主函数每次只需要执行一次任务,节省单片机资源

③主函数编写

#include "sys.h"
#include "key.h"
#include "smg.h"
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"

uchar SMG[]={10,10,10,10,10,10,10,10};
uchar LED[]={0,0,0,0,0,0,0,0};
uchar dot[]={0,0,0,0,0,0,0,0};
uchar pos;
uint fre;
uchar key_dly;
uint collect_dly;
uchar smg_dly;
uint num;
uint count;
uint systick_ms;
uchar state_relay;

void Timer0_Isr(void) interrupt 1
{
	smg(SMG,pos,dot);
	led(LED,pos);
	relay(state_relay);
	key_dly++;
	collect_dly++;
	systick_ms++;//滴答定时器
	smg_dly++;
	if(++pos==8)pos=0;
	//虽然是一个个显示,但是POS是每1微秒自加,最终显示是稳定状态
	//不可以写成pos=(pos+1)%8
	//执行时间久,在中断服务函数里面,不要让程序运行很久

	if(++count==1000)//1s中断
	{
		fre=(TH0<<8)|TL0;
		TH0=TL0=0;
		count=0;
	}
}

void Timer0_Init(void)		//1微秒@12.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0xFF;				//设置定时初始值
	TH0 = 0xFF;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;				//使能定时器0中断
}

void led_pro()
{
	
}

void key_pro()
{
	uchar key;
	if(key_dly<10)key_dly=0;
	else return;
	key=key_scan();
	switch(key)
	{
		case 1: num++;break;
	}
}

void collect_pro()
{
	if(collect_dly<500)collect_dly=0;
	else return;
}

void display()
{
	if(smg_dly<200)smg_dly=0;
	else return;

}

void main()
{
	init();
	Timer0_Init();
	EA=1;
	while(1)
	{
		led_pro();
		key_pro();
		display();
		collect_pro();
	}
}

④超声波模块,减少定时器2的使用

#include "wave.h"
sbit TX=P1^0;
sbit RX=P1^1;

//5个周期12us
void Delay12us(void)	//@12.000MHz
{
	unsigned char data i;

	_nop_();
	_nop_();
	i = 33;
	while (--i);
}

void wave()
{
	uchar i;
	for(i=0;i<5;i++)
	{
		TX=1;
		Delay12us();
		TX=0;
		Delay12us();
	}
}

uchar read_distance()
{
	uchar distance;
	CMOD=0;
	CH=CL=0;
	wave();
	CR=1;
	while(RX &&!CF);
	CR=0;
	if(CF)
	{
		CF=0;
		distance=255;
	}
	else
	{
		distance=((CH<<8)|CL)*0.017;
	}
	return distance;
}

⑤串口通信,使用定时器2

#include "uart.h"

//发送接收
void Uart1_Isr(void) interrupt 4
{
	if (RI)				//检测串口1接收中断
	{
		uart_flag=1;
		uart_systick=0;
		uart_buf[index++]=SBUF;
		RI = 0;			//清除串口1接收中断请求位
		if(index>2)index=0;
	}
}

void Uart1_Init(void)	//9600bps@12.000MHz
{
	SCON = 0x50;		//8位数据,可变波特率
	AUXR |= 0x40;		//定时器时钟1T模式
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0xC7;			//设置定时初始值
	TH1 = 0xFE;			//设置定时初始值
	ET1 = 0;			//禁止定时器中断
	TR1 = 1;			//定时器1开始计时
	ES = 1;				//使能串口1中断
}

char putchar(char ch)
{
	ch=SBUF;
	while(TI==0);
	TI=0;
	return ch;
}


网站公告

今日签到

点亮在社区的每一天
去签到