首先提醒,PIC单片机型号众多,不同型号的单片机,寄存器的每一位的作用都有可能有所不同;
TMR0
PIC单片机学习第三天--定时器/计数器
1、定时功能:所谓定时功能就是通过来自单片机内部的时钟脉冲作计数脉冲,使计数器计数,即每个机器周期计数器加1,计数值达到予置值后,定时/计数模块产生溢出。
2、计数器功能:所谓计数是指对外部事件进行计数。外部事件的发生以输入脉冲表示,因此计数功能的实质就是对外来脉冲进行计数。
PIC16F1508单片机配置了3个定时器/计数器模块,分别 为TMR0、TMR1和TMR2。
共同点:它们的核心部分都是一个由时钟信号触发,按递增方式累加工作的循环计数器;从预先设定的某一初始值开始累计,在累计到计数器产生溢出,并同时建立一个相应的溢出中断标志。
不同点:TMR0为8位宽,有一个可选的预分频器,用于通用目的,可用于定时和计数;TMR1为16位宽,附带一个可编程的预分频器和一个可选的低频时基振荡器,适合与CCP (捕捉/比较脉宽调制)模块配合使用来实现输入捕捉或输出比 较功能,也可用于定时和计数.。
定时器/计数器TMR0具有以下特点:
(1) TMR0是一个8位宽的由时钟信号上升沿触发的循环 累加计数寄存器。
(2) 有一个专用的外部触发信号输入端(T0CKI)。
(3) TMR0也是一个在文件寄存器区域内统一编址的寄存器,地址为01H或101H,用户用软件方式可直接读/写计数器的内容。
(4) 具有一个软件可编程的8位预分频器。
(5) 当使用内部触发信号,即指令周期作为时钟信号源时,模块TMR0工作于定时方式,触发方式为固定上升沿 触发有效。在计数器溢出时,相应的溢出中断标志T01F自动 置位,并可产生溢出中断。
注:一个指令周期 =4 * 时钟周期;
(6) 当外部时钟信号源时,模块TMR0工作于计数方式,触发方式可由程序设置位上升沿触发或下降触发有效。 在计数器溢出时,也可产生溢出中断。
与定时器/计数器TMR0模块相关的寄存器:
(1)选项寄存器OPTION_REG
(2)中断控制寄存器INTCON
(1)选项寄存器OPTION_REG,bit7--PBPU;bit6--INTEDG,这两位可以说用不到,直接忽略;PS0,PS1,PS2,这三位用于设置预分频器,PSA用于设置->对于前面三位所设置的预分频是用于单片机的看门狗还是用于这个定时器;
TOSE位用于选择时钟沿触发方式,是外部时钟的上升沿还是下降沿触发,这一位只有在设置为计数器的时候才有用,所以在我们用于定时器的时候,这一位也是没用的;
TOCS位,也就是把功能设置为定时器还是计数器;
(2)中断控制寄存器INTCON,主要用到GIE,T0IE,T0IF,这三位;GIE可以理解为总中断的使能位,使用中断的话,这一位一定要配置打开;T0IE就是TMR0的中断使能位;T0IF就是TMR0的中断标志位;
分析上图CLKOUT来自系统时钟频率4分频之后,TOCS=0之后,内部时钟--定时器;TOCS=1之后,外部时钟--计数器;然后来到PSA,PSA=0时,看上图,连通了TMR0定时器;然后走到8位分频器;
分频器这里解释一下,假如我们选择的是内部时钟,且是256分频,则只有当内部信号来256个信号,8选1开关那里才有一个信号,TMR0才会加1;可以理解为,分频设置的越大,延时越长;
补充分割线-------———————————————————————————————————
上面也说到,TMR0的中断功能开启位和中断溢出位在INTCON寄存器里,这里说明一下,中断溢出标志位自动置1与置0与中断开启与不开启无关,即使不开启中断,发生溢出也会自动置1的情况;所以我们编写代码可以有两种方式,查询法和中断法;查询法——即不开启中断,查询 INTCONbits.TMR0IF 的值,然后去执行相应的代码;
接下来看程序,看这个代码就是没有开启定时器0中断,只是在主函数里判断TMR0的溢出位是否置1了,置1代表发生了一次溢出,然后根据一次溢出的周期计算我们需要的延时时间;
注意:TMR0好像是没有什么开启/关闭位,只要上电它就在运行;
/RD0--一个小灯;
uint16_t intnum;
void main(void)
{
init();
while(1)
{
if(INTCONbits.TMR0IF==1)//代表溢出了
{
INTCONbits.TMR0IF=0;//每次溢出之后注意手动清0
intnum++;
if(intnum > 30)//这个要根据自己的系统时钟频率来算4/31k = 129.0323us,256次才会溢
//出;30就是1s
{
intnum = 0;
PORTDbits.RD0 = !PORTDbits.RD0; //取反,小灯闪烁
}
}
}
}
void init(void)
{
TRISDbits.TRISD0 = 0;//方向设置为输出
PORTDbits.RD0 = 0;//i/o值位清0,电平
LATDbits.LATD0 = 0;//和PORT寄存器作用差不多,这个也可以不要
//ANSELDbits.ANSD0 = 0;//设置为模拟输入口
OPTION_REG = 0x08;//不把分频设置给TMR0,选择内部指令时钟FOSC/4
}
另外一种,将分频设置给TMR0,这个设置TMR0初值,就需要我们自己考虑一个多少时间溢出一次,比如50ms溢出一次,50*20=1000ms=1s;下面这句代码可以体会一下;
TMR0 = 254;//定时器0装入初值,(256-TMR0)*256(分频)*129.0323=想要溢出的时间,比如50ms
//RD0--一个小灯;
uint16_t intnum;
void main(void)
{
init();
while(1)
{
if(INTCONbits.TMR0IF==1)//代表溢出了
{
INTCONbits.TMR0IF=0;//每次溢出之后注意手动清0
intnum++;
TMR0 = 254;
if(intnum > 20)//这个要根据自己的系统时钟频率来算4/31k = 129.0323us,20就是1s
{
intnum = 0;
PORTDbits.RD0 = !PORTDbits.RD0; //取反,小灯闪烁
}
}
}
}
void init(void)
{
TRISDbits.TRISD0 = 0;//方向设置为输出
PORTDbits.RD0 = 0;//i/o值位清0,电平
LATDbits.LATD0 = 0;//和PORT寄存器作用差不多,这个也可以不要
//ANSELDbits.ANSD0 = 0;//设置为模拟输入口
OPTION_REG = 0x07;//其实作用同下,选择内部指令时钟FOSC/4
OPTION_REGbits.PSA = 0;//把分频设置给TMRO;
OPTION_REGbits.PS0 = 1;//ps0,ps1,ps2共同设置分频,256分频
OPTION_REGbits.PS1 = 1;
OPTION_REGbits.PS2 = 1;
TMR0 = 254;//定时器0装入初值,(256-TMR0)*256(分频)*129.0323=想要溢出的时间,比如50ms
}
下面是使用中断的方法来写的;中断函数是自动调用的;
//RD0--一个小灯;
uint16_t intnum;
void main(void)
{
init();
while(1)
{
if(intnum > 20)//这个要根据自己的系统时钟频率来算4/31k = 129.0323us,20就是1s
{
intnum = 0;
PORTDbits.RD0 = !PORTDbits.RD0; //取反,小灯闪烁
}
}
}
void init(void)
{
TRISDbits.TRISD0 = 0;//方向设置为输出
PORTDbits.RD0 = 0;//i/o值位清0,电平
LATDbits.LATD0 = 0;//和PORT寄存器作用差不多,这个也可以不要
//ANSELDbits.ANSD0 = 0;//设置为模拟输入口
OPTION_REG = 0x07;//其实作用同下,选择内部指令时钟FOSC/4
OPTION_REGbits.PSA = 0;//把分频设置给TMRO;
OPTION_REGbits.PS0 = 1;//ps0,ps1,ps2共同设置分频,256分频
OPTION_REGbits.PS1 = 1;
OPTION_REGbits.PS2 = 1;
INCON = 0xA0;//TMR0溢出中断使能,使能全部中断
TMR0 = 254;//定时器0装入初值,(256-TMR0)*256(分频)*129.0323=想要溢出的时间,比如50ms
}
void Timer_0_ISR(void)
{
INTCONbits.TMR0IF = 0;//TMR0中断标志位清0
TMR0 = 254;
intnum++;
}
参考下如何设置,中断函数自动执行;
PIC16LF1508单片机的的中断配置在哪个头文件?
在使用PIC16LF1508单片机进行编程时,需要用到XC8编译器以及MPLAB X IDE集成开发环境进行开发。在这种开发环境下,中断配置通常会在 “interrupt.h” 中配置。
在 “interrupt.h” 中,可以找到相关宏定义和函数声明,可以根据需要进行修改或使用。同时,在 “xc.h” 中也有一些与中断有关的宏定义和函数声明。因此,需要在编写程序时包含这两个头文件。
我是在compiler-compat-mchip_xc8.h这个文件里看到的;
PIC16LF1508单片机的的 __interrupt();
__interrupt() 是一个特殊的函数修饰符,在使用XC8编译器进行PIC16LF1508单片机编程时,可以用它来标志一个函数是中断服务子程序(ISR)。一个使用了 __interrupt() 修饰符的函数将被编译器标记为可响应中断请求的函数,当中断请求被触发时,这个函数将被执行。
一个使用了 __interrupt() 修饰符的函数需要满足以下要求:
-
函数的返回值必须为void类型。
-
函数不能有参数。
-
函数必须定义在代码的顶层,或者使用 #pragma interrupt 和 #pragma code 等指令对中断服务子程序进行定义和初始化。
void __interrupt() Interrupt_manager(void) ;
void __interrupt() Interrupt_manager(void)
{
if( PIE1bits.TMR1IE && PIR1bits.TMR1IF )
{
Timer_1_ISR() ;
}
}
这样就通过这个指令定义了一个中断函数;就可以把自己写的中断函数弄到这个函数里面,然后中断中断触发之后,自动执行;