#STC15系列单片机定时器高阶#
STC15系列单片机:
定时器2相关知识点
代码使用
滴答定时器
每次水滴从高处落下的时间相同,只需要记录落下的次数,就可以计时
定义ms级的滴答定时器, unsigned long类型,4个字节,
范围0~4294967295,每过1ms去加1,要运行49天才会溢出
应用1.计时器
普通计时器:if(count==20) count=0.....
可以转换成:ms++; if(ms%100==0).....
定时器注意事项
1.优先级问题
优先级从高到低
外部中断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;
}