分类: 嵌入式
2015-04-08 13:29:21
本次设计选用的单片机是STC的STC15W402AS,sop16封装。
1:首先单片机也属于嵌入式行业,当然它没有驱动那么复杂,也没有操作系统。但是简单不等于容易,没有了操作系统对于我们的限制,我们可以尽最大的能力去发挥硬件的优势,不同的单片机产品的功能,硬件架构可能不同,导致硬件参数不同,但软件上的操作大多类似,接下来我们看看单片机中要学习的几大重点吧!
2:时钟,对于当今的任何机器,只要它是按部就班的工作的它就需要时钟,就好像学校的铃声,没有规矩不成方圆,时钟是所有规矩的基础
首先我们来看一看对于一个时钟,我们是怎样对它进行初始化,进行设置的呢?
程序如下(以time0为例,其他时钟类似)!
//********************* Timer0初始化函数************************/
void Timer0_Inilize(void)
{
TR0 = 0; //停止计数
EA = 1; //开放所有中断
ET0 = 1; //准许T0中断
PT0 = 0; //低优先级中断
TMOD = (TMOD & ~0x03);//16自动重装定时器
AUXR &= ~0x80; //定时器0是传统速度,12分频
INT_CLKO |= 0x01;//不准许p3.5管脚被配置为定时器0的时钟输出
TH0 = 0x94;//(u8)((65536UL - (MAIN_Fosc / 100UL)) >> 8);
TL0 = 0x00;//(u8)(65536UL - (MAIN_Fosc / 100UL));
TR0 = 1;//开始计数
}
/********************* Timer0中断函数************************/
//30ms 中断一次
//按住/释放0.6秒以下为无效操作,按住0.6到3秒为短按,3秒以上为长按,连按要求两次按下间隔不低于0.6秒不高于1.5秒
void timer0_int (void) interrupt TIMER0_VECTOR
{
//这里添加中断处理
}
接下来我们围绕单片机的时钟操作看看时钟到底是怎么成功运行的
首先声明各个寄存器的地址值都在头文件中进行声明,如:
//别问我0x88怎么来的,这里的地址都是从芯片手册中查到的
sfr TCON = 0x88;
sbit TR0 = TCON^4; //定时器0运行控制位
问:那么TCON这个寄存器时干什么的呢?
答:芯片手册上有,这里做一些简要介绍,TCON:定时器/计数器中断控制寄存器(可位寻址),这里的TR0:定时器T0的运行控制位,该位由软件置位和清零(该程序已经表明这一点),当GATE(TMOD.3) = 0,TR0 = 1时就准许T0开始计数,TR0 = 0时就禁止T0计数,当GATE(TMOD.3) = 1,TR0 = 1且INT0输入高电平时,才准许T0计数,TR0 = 0 时禁止T0计数。
说到这里,应该懂得开始和结束的地方TR = 0, TR = 1是什么意思了吧,
你可能还会问:那GATE这个你没有设置啊!
回答:真的没有设置吗?我们来看看芯片手册
这就是GATE所在的位置,你发现什么了,对,它的复位值时00H,所以它就是0啊!当然如果你愿意也可以自己去设置,只是徒劳无功而已。那么GATE到底是什么作用呢?GATA:TMOD.3控制定时器0,置1时只有在INT0脚为高及TR0控制位置1时才可以打开定时器/计数器0.
当然我们这里的GATE为0,所以不用关心INT0,但为了让你更加透彻,我还是来说说吧!
INT0:字面意思是外部中断,如果你学过操作系统,那你一定知道外部中断的意思了!简单而形象地说,就是硬件提供一个引脚,当这个引脚上有信号时,芯片正在执行的程序会停下来,转而去执行某个地址上的程序,执行完以后又回到原来的地方继续执行原来的程序,当然,有人不会理解为什么引脚有信号,原来的程序会停止,这个问题是由硬件完成的,学单片机的人了解原理即可,不需太较真。现在你已经懂得怎么开启关闭定时器/计数器了!
因为我们用的是定时器,所以我们还要把时间到了这样的信息告诉芯片,所以我们需要中断,当然不是上面的外部中断0,而是我们定时器自己自带的中断,所以接下来是ET0 的设置。
从手册上可以发现,这个字段属于IE寄存器
IE:中断准许寄存器
EA:CPU的总中断准许控制位,EA = 1,CPU开放中断,EA = 0,CPU屏蔽所有的中断请求。
EA的作用是使中断形成多级控制,即各中断源首先受EA控制,其次还受各中断源自己的中断准许控制位控制。
ET0:T0的溢出中断准许位,ET0 = 1准许T0中断,ET0 = 0禁止T0中断
很明显:这里的作用就是打开大门在打开小门,这样中断信号就能进入芯片内部咯!
既然上面说到中断有优先级,那么我们的定时器的优先级是多少呢?接下来就是对T0优先级的设置:如下图可以看出PT0在IP这个寄存器中。
IP:中断优先级控制寄存器
PT0:定时器0中断优先级控制位。
当PT0 = 0时,定时器0中断为最低优先级中断(优先级0)
当PT0 = 1时,定时器0中断为最高优先级中断(优先级1)
为了体验单片机的实时性,我们这里用到了最高优先级,即优先级1,PT0 = 1;
现在定时器/计数器打开,中断打开,优先级设置好,可是我们到底用的是定时器还是计数器呢?接下来我们设置的模式寄存器就可以解决这一点。
TMOD:定时器/计数器工作模式寄存器(这个寄存器有点复杂)
TMOD.7(GATE):控制定时器1,置1时只有在INT1脚为高及TR1控制位置1时 才可以打开定时器/计数器1。
TMOD.3(GATE):控制定时器0,置1时只有在INT0脚为高及TR0控制位置1时 才可以打开定时器/计数器1。
TMOD.6(C/T) : 控制定时器1用作定时器或计数器清0则用作定时器(对内部系 统时钟进行技术),置1用作计数器(对引脚T1/P3.5外部脉冲 进行计数)
TMOD.2(C/T): 控制定时器0用作定时器或计数器清0则用作定时器(对内部系 统时钟进行技术),置1用作计数器(对引脚T1/P3.4外部脉冲 进行计数)
TMOD.5/TMOD.4(M1,M0):定时器/计数器1模式选择
0 0 :16位自动重装定时器,当溢出时将RL_TH1和RL_TL1 存放的值自动重装如TH1和TL1中
0 1 :16位不可重装载模式,TL1,TH1全用
1 0 :8位自动重装载定时器,当溢出时将TH1存放的值自 动重装入TL1
1 1 : 定时器/计数器1此时无效(停止计数)
TMOD.1/TMOD.0(M1,M0):定时器/计数器0模式选择
0 0 :16位自动重装定时器,当溢出时将RL_TH0和RL_TL0 存放的值自动重装如TH0和TL0中
0 1 :16位不可重装载模式,TL0,TH0全用
1 0 :8位自动重装载定时器,当溢出时将TH1存放的值自 动重装入TL0
1 1 : 定时器/计数器0此时无效(停止计数)
接下来我们回到程序,
TMOD = (TMOD & ~0x03);//16自动重装定时器
这句话的意思就是将TMOD的最低2位置0,其余为保持不变(如果你不知道&,~这些符号的意思,你可以去搬砖头了),对照上面写的就可以知道它为16位自动重装定时器。
现在我们又确定了模式,可是有个很重要的问题没有解决,那就是到底多少次计数是1ms呢?或者说我们的定时器的频率是多少呢?当然这里的频率指的是每秒多少次,
现在我就来带大家看看怎么设置频率!
AUXR寄存器的篇幅较长,请大家自己参考芯片手册去看,这里省略
AUXR &= ~0x80; //定时器0是传统速度,12分频
由表达式可知,AUXR的第7位被置0,其余位不变,即T0x12被置0
T0x12:定时器0速度控制位
0:定时器0是传统8051速度,12分频
1:定时器0的速度是传统8051的12倍,不分频
INT_CLKO:外部中断准许和时钟寄存器(具体请查看芯片手册)
INT_CLKO |= 0x01;//不准许p3.5管脚被配置为定时器0的时钟输出
即将最低为置位1即T0CLKO = 1;
T0CLKO:是否准许将P3.5/T1脚配置为定时器0(T0)的时钟输出T0CLKO
1 :将P3.5/T1管脚配置为定时器0的时钟输出T0CLKO,输出时钟频率 = T0溢出率/2
若定时器/计数器T0工作在定时器模式0(16位自动重装载模式)时,
如果C/T = 0,定时器计数器T0是内部系统时钟计数,则:
T0工作在1T模式(AUXR.7/T0x12 = 1)时的输出频率 = (SYSclk)/(65536 -[RL_TH0,RL_TL0])/2
T0工作在12T模式(AUXR.7/T0x12 = 0)时的输出频率 =(SYSclk)/12/(65536 - [RL_TH0,RL_TL0])/2
如果C/T = 1,定时器/计数器T0是对外部脉冲(P3.4/T0)输入计数,则:
输出时钟频率 = (T0_Pin_CLK)/(65536-[RL_TH0,RL_TL0])/2
若定时器/计数器T0工作在定时器模式2(8位自动重装模式),
如果C/T = 0,定时器/计数器T0是对内部系统时钟计数,则:
T0工作在1T模式(AUXR.7/T0x12 = 1)时的输出频率 = (SYSclk)/(256 - TH0)/2
T0工作在12T模式(AUXR.7/T0x12 = 1)时的输出频率 = (SYSclk)/12/(256 - TH0)/2
如果C/T = 1,定时器/计数器T0是对外部脉冲输入(P3.4/T0)计数,则:
输出时钟频率 = (T0_pin_clk)/(256-TH0)/2
0 不准许P3.5/T1管脚被配置为定时器0的时钟输出
由此可见我们的任务也完成百分之九十了!接下来是怎么设置定时时间,1ms,1min,1year?当然一年就有点过于忽悠了!那怎么设置时间呢?上面说过定时值放在TH0,TH1中。
TH0 = 0x94;//(u8)((65536UL - (MAIN_Fosc / 100UL)) >> 8);
TL0 = 0x00;//(u8)(65536UL - (MAIN_Fosc / 100UL));
问:65536UL - (MAIN_Fosc / 100UL是什么意思,为什么这么写?
答:我们的定时器不可能永远不停地去计数,那样的话总数会越来越大,达到一定数目就会溢出,假如我们的计数寄存器只有8位,那最大计数就是256,当然这里我们的溢出计数是65536,所以自然是16位了!那为什么这样运算呢?我们假设你的定时只有30ms而定时器的定时是65536ms,你怎么办。也许你会说,给他一个初始值65536-30,对了,这里就是这个意思,至于为什么要左移8位,那是因为TH0,TL1只有8位不够存放,所以分开存放咯!
好了,万事俱备只欠东风,那定时器时间到了怎么办?在哪进行相应的处理呢?
没错,还是中断,前面我们设置了中断,就是为了现在的运用
第二个函数就是个中断函数
void timer0_int (void) interrupt TIMER0_VECTOR
void timer0_int (void) :这是中断函数的函数名
Interrupt :这是单片机特有的关键字,用来说明它修饰的函数是中断函数
TIMER0_VECTOR :这是一个中断向量。前面说过,发生中断会出现一个信号,中断向 量就是用来发现这个信号的。
至此,我们的定时器的程序写完了!由于个人能力有限,里面难免有些错误,还请各位及时纠正,
学高为师,就到这里,谢谢各位!