打印
[PIC®/AVR®/dsPIC®产品]

【CuriosityNano测评报告】+PIC16F15244之定时器妙用

[复制链接]
773|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
今天我们用定时器做一个us/ms延时,并且保留其中断,us/ms延时均用查询的方式实现,故不影响中断额实时性。
上次用MPLAB+MCC构建了简单的点灯例程。虽然固件包里面已经帮我们做了一个软件的软延时(__delay_us()和__delay_ms()),具体在pic.h里面
#ifdef __PICCPRO__
/****************************************************************/
/* Built-in delay routines                                        */
/****************************************************************/
// NOTE: To use the macros below, YOU must have previously defined _XTAL_FREQ
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif
IDE帮我们其实做了很多工作,包括头文件的包含,一些常见的标志头定义等,好处就是易上手,不要考虑那么多,但有点小麻烦就是想查看那些头文件及源文件还是有点烦的。
下面我们看下我们的PIC16F15244的固件包里面具体有什么:




扯远了,我们开始配置定时器并且开启中断:


生成代码。我们对代码进行修改,现在它300ms的中断里面,让LED闪烁一次:
对timr0.c添加:
<font color="#ff0000">#include "pin_manager.h"</font>
......
void TMR0_CallBack(void)
{
    // Add your custom callback code here
<font color="#ff0000">    LED_Toggle(); </font>
    if(TMR0_InterruptHandler)
    {
        TMR0_InterruptHandler();
    }
}

下面我们新建我们的延时函数:
delay.h:
#ifndef DELAY_H
#define        DELAY_H

#ifdef        __cplusplus
extern "C" {
#endif

#include <xc.h>
#include "mcc_generated_files/tmr0.h"
   

void delay_us(uint32_t nus);
void delay_ms(uint32_t nms);


#ifdef        __cplusplus
}
#endif

#endif        /* DELAY_H */

delay.c:
#include "delay.h"

extern volatile uint16_t timer0ReloadVal16bit;

void delay_us(uint32_t nus)
{
    uint32_t ticks=0;
    uint32_t told,tnow,tcnt=0;
    uint32_t reload=timer0ReloadVal16bit;
    //7999cnt=1ms
    //8cnt=1us
    ticks=nus*8;
    tcnt=0;
    told=TMR0_ReadTimer();
    while(1)
    {
        tnow=TMR0_ReadTimer();       
                if(tnow!=told)
                {         
            //not flowover
                        if(tnow>told)
            {
              tcnt+=tnow-told;
            }                   
                        else //flow over tnow<told
            {
                tcnt+=(tnow-reload)+(0xffff-told);          
            }
                  
                        told=tnow;
                        if(tcnt>=ticks)break;                                //达到设定值,跳出
                }  
    }
}




void delay_ms(uint32_t nms)
{
    uint32_t ticks=0;
    uint32_t told,tnow,tcnt=0;
    uint32_t reload=timer0ReloadVal16bit;
    //7999cnt=1ms
    //8cnt=1us
    ticks=nms*7999;
    tcnt=0;
    told=TMR0_ReadTimer();
    while(1)
    {
        tnow=TMR0_ReadTimer();       
                if(tnow!=told)
                {         
            //not flowover
                        if(tnow>told)
            {
              tcnt+=tnow-told;
            }                   
                        else //flow over tnow<told
            {
                tcnt+=(tnow-reload)+(0xffff-told);          
            }
                  
                        told=tnow;
                        if(tcnt>=ticks)break;                                //达到设定值,跳出
                }  
    }
}
怎么得知,定时器1cnt代表多长时间,这里有2个办法,就是一知道时钟的时钟,然后算出,我们这里timer0时钟选的是:
(对照MCC的配置截图)
FOSC/4 通过查数据书册得知为FOSC为系统时钟,这里我们的系统时钟为32M,那么定时器0的时钟为8M,即1cnt=1/8us;
反过来就是说1us=8cnt,这样我们就把1cnt和时间的对应关系找到了。那么以此类推,1ms=8000cnt。
下面讲第二种懒人的推断方式,MCC不是配置好了1ms的reload值了么,我们知道定时器0为往上累加1计算,我们来计时
一下reload到溢出值0XFFFF到底有多少个cnt,找到定时器0的初始化配置代码:
void TMR0_Initialize(void)
{
    // Set TMR0 to the options selected in the User Interface

    // T0CS FOSC/4; T0CKPS 1:1; T0ASYNC synchronised;
    T0CON1 = 0x40;

<font color="#ff0000">    // TMR0H 224;
    TMR0H = 0xE0;

    // TMR0L 192;
    TMR0L = 0xC0;</font>

    // Load TMR0 value to the 16-bit reload variable
    timer0ReloadVal16bit = (TMR0H << 8) | TMR0L;

    // Clear Interrupt flag before enabling the interrupt
    PIR0bits.TMR0IF = 0;

    // Enabling TMR0 interrupt.
    PIE0bits.TMR0IE = 1;

    // Set Default Interrupt Handler
    TMR0_SetInterruptHandler(TMR0_DefaultInterruptHandler);

    // T0OUTPS 1:1; T0EN enabled; T016BIT 16-bit;
    T0CON0 = 0x90;
}
红色部分的 数值,即为我们需要的reload值:
0xE0C0=57536,溢出值65535-57536+1=8000,即1ms需要8000cnt,那么1us需要8cnt,与我们上面计时出的完全相符。如果不想理清
定时器的时钟,就用第二种,顶多会少算多算1cnt,无伤大雅。
void delay_us(uint32_t nus)与void delay_ms(uint32_t nms)原理一样,我们就取us延时讲解:
第一步、我们将需要的延时转换成需要的cnt数,由前面的得知:nus需要的tick数为:tick=nus*8;
第二步、我们先取出timer0的初始值作为旧值told,然后取出当前值tnow,然后计算出当前值tnow与旧值told之间的tick数,并赋给一个累加的记录总tick的变量tcnt
然后新值赋给旧值,进行下一轮循环,当总tick数cnt大于等于我们需求的tick数时,即达到延时,然后跳出。
这里新值与就值的对应位置有2总可能,一直就是未溢出的状态,那么新值是大于旧值的,还有一种可能就是溢出了,旧值大于新值。
下面用一个图表示:

这样就清楚多了。
我们在main函数里,让他每隔500ms,打印一次。
while (1)
    {
        
        //LED_Toggle();
        printf("Hello world!\r\n");
       // __delay_ms(500);
        delay_ms(500);
        
    }

下载观察现象,LED是否会300ms闪烁,和串口是否会500ms打印一次:


好了定时器就到这里了,小白文,大神勿喷~~









使用特权

评论回复
沙发
hu9jj| | 2021-1-6 15:09 | 只看该作者
    我不会那么复杂的计算,只是设定定时器中断频率为1毫秒,然后定义一个全局变量ms,在定时器中断里对这个变量加1(ms++),然后在主循环里对这个变量进行判断,当变量大于499(半秒)或者是大于999(一秒)时对这个变量清零,然后执行相应的操作。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

111

主题

627

帖子

2

粉丝