[PIC®/AVR®/dsPIC®产品] 【CuriosityNano测评报告】+PIC16F15244之定时器妙用

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




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


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

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

  3. #ifdef        __cplusplus
  4. extern "C" {
  5. #endif

  6. #include <xc.h>
  7. #include "mcc_generated_files/tmr0.h"
  8.    

  9. void delay_us(uint32_t nus);
  10. void delay_ms(uint32_t nms);


  11. #ifdef        __cplusplus
  12. }
  13. #endif

  14. #endif        /* DELAY_H */

delay.c:
  1. #include "delay.h"

  2. extern volatile uint16_t timer0ReloadVal16bit;

  3. void delay_us(uint32_t nus)
  4. {
  5.     uint32_t ticks=0;
  6.     uint32_t told,tnow,tcnt=0;
  7.     uint32_t reload=timer0ReloadVal16bit;
  8.     //7999cnt=1ms
  9.     //8cnt=1us
  10.     ticks=nus*8;
  11.     tcnt=0;
  12.     told=TMR0_ReadTimer();
  13.     while(1)
  14.     {
  15.         tnow=TMR0_ReadTimer();       
  16.                 if(tnow!=told)
  17.                 {         
  18.             //not flowover
  19.                         if(tnow>told)
  20.             {
  21.               tcnt+=tnow-told;
  22.             }                   
  23.                         else //flow over tnow<told
  24.             {
  25.                 tcnt+=(tnow-reload)+(0xffff-told);          
  26.             }
  27.                   
  28.                         told=tnow;
  29.                         if(tcnt>=ticks)break;                                //达到设定值,跳出
  30.                 }  
  31.     }
  32. }




  33. void delay_ms(uint32_t nms)
  34. {
  35.     uint32_t ticks=0;
  36.     uint32_t told,tnow,tcnt=0;
  37.     uint32_t reload=timer0ReloadVal16bit;
  38.     //7999cnt=1ms
  39.     //8cnt=1us
  40.     ticks=nms*7999;
  41.     tcnt=0;
  42.     told=TMR0_ReadTimer();
  43.     while(1)
  44.     {
  45.         tnow=TMR0_ReadTimer();       
  46.                 if(tnow!=told)
  47.                 {         
  48.             //not flowover
  49.                         if(tnow>told)
  50.             {
  51.               tcnt+=tnow-told;
  52.             }                   
  53.                         else //flow over tnow<told
  54.             {
  55.                 tcnt+=(tnow-reload)+(0xffff-told);          
  56.             }
  57.                   
  58.                         told=tnow;
  59.                         if(tcnt>=ticks)break;                                //达到设定值,跳出
  60.                 }  
  61.     }
  62. }
怎么得知,定时器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的初始化配置代码:
  1. void TMR0_Initialize(void)
  2. {
  3.     // Set TMR0 to the options selected in the User Interface

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

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

  8.     // TMR0L 192;
  9.     TMR0L = 0xC0;</font>

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

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

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

  16.     // Set Default Interrupt Handler
  17.     TMR0_SetInterruptHandler(TMR0_DefaultInterruptHandler);

  18.     // T0OUTPS 1:1; T0EN enabled; T016BIT 16-bit;
  19.     T0CON0 = 0x90;
  20. }
红色部分的 数值,即为我们需要的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,打印一次。
  1. while (1)
  2.     {
  3.         
  4.         //LED_Toggle();
  5.         printf("Hello world!\r\n");
  6.        // __delay_ms(500);
  7.         delay_ms(500);
  8.         
  9.     }

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


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









本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
hu9jj 发表于 2021-1-6 15:09 | 显示全部楼层
    我不会那么复杂的计算,只是设定定时器中断频率为1毫秒,然后定义一个全局变量ms,在定时器中断里对这个变量加1(ms++),然后在主循环里对这个变量进行判断,当变量大于499(半秒)或者是大于999(一秒)时对这个变量清零,然后执行相应的操作。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

111

主题

627

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部