熊仔 发表于 2023-7-21 10:51:57

STC32 定时器0做滴答,设计millis(),micros(),delay_ms()函数

本帖最后由 熊仔 于 2023-7-21 12:10 编辑

用过arduino的人都知道,millis()是获取系统运行时间单位ms;micros()是获取系统运行时间单位us。
当然delay_ms()看名字就知道了,毫秒延时函数。这个根据micros()获取系统us时间,延时挺准的。
delay_us(),微秒延时函数,自适应系统时钟。
while(t--) 翻译汇编需要6T时钟,所以时钟最好选6的倍数:12,24,30,36,42,48,54

注意:delay_ms() 不能在中断中使用。
#include "config.h"

/*
2023-07-13 修复定时器读取计数值不准的问题。
*/



uint32_t volatile edata timer0_millis = 0;

//定时器0初始化
void Timer0_Init(void)                //1毫秒
{
    TM0PS = MAIN_Fosc / 1000000 - 1; //分频,定时器1M时钟
    AUXR |= 0x80;                        //定时器时钟1T模式
    TMOD &= 0xF0;                        //设置定时器模式
    TL0 = (uint8_t)(65536 - 1000);                                //设置定时初始值
    TH0 = (uint8_t)((65536 - 1000) / 256);                                //设置定时初始值
    TF0 = 0;                                //清除TF0标志
    TR0 = 1;                                //定时器0开始计时
    ET0 = 1;                //开启中断
}

//定时器溢出中断
void Timer0_Isr(void) interrupt 1
{
    timer0_millis++;
    P20 = ~P20;
}

//获取系统运行时间单位ms
uint32_t millis()
{
    return timer0_millis;
}

//获取系统运行时间单位us
uint32_t micros()
{
    uint32_t ms, us;
    uint8_t th, tl, tf;

    F0 = EA;
    EA = 0;
    /*
    动态读取运行中的计数值
    一种可避免读错的方法是:先读 THx,后读TLx,将两次读得的THx 进行比较,
    若两次读得的值相等,则可确定读的值是正确的,否则重复上述过程,
    重复读得的值一般不会再错。
    */
    do
    {
      th = TH0;
      tl = TL0;
    }
    while(th != TH0);

    tf = TF0;
    ms = timer0_millis;
    EA = F0;

    if(tf)
    {
      ms++;
      us = ms * 1000;
    }
    else
    {
      us = ms * 1000 + th * 256 + tl - 64536;
    }

    return us;
}

//毫秒延时函数
void delay_ms(uint32_t ms)
{
    uint32_t start, current;
    start = micros();
    while (ms > 0)
    {
      current = micros();
      if (( current - start) >= 1000)
      {
            ms--;
            start += 1000;
      }
    }
}


//while(t--) 翻译汇编需要6T时钟,所以时钟最好选6的倍数:12,24,30,36,42,48,54
#define US_6T(MAIN_Fosc/6000000UL)
void delay_us(uint16_t us)
{
    uint32_t t;

    t =us * US_6T;   //3T
    t -= 2;         //1T
    while(t--);   //6T*t
    //最后退出只需要1T,所以-2T
    //RET 3T参数 1T lcall 3T
}


//由于micros()需要时间,导致不是很准
/*
void delay_us(uint32_t us)
{
        uint32_t end,current;

    end = micros()+us;
    #if(MAIN_Fosc>=40000000UL)
    end--;
    #endif
    #if(MAIN_Fosc<40000000UL)
    end-=2;
    #endif
    do{
      current = micros();
    }while (current < end) ;

}
*/


熊仔 发表于 2023-7-21 11:43:38

这个代码调试了很久,才弄对。
调试过程中,遇到2个难题:
1,动态读取运行中的计数值,读不准的问题。请教了STC技术才解决。
2,必须读取溢出标志位,溢出的时候tl不是马上重载的,这个要注意,计算的时候去掉th和tl的数值。

aa520520 发表于 2024-9-10 16:18:36

版主,我这也遇到这样同样的问题了,一个定时器中断里自增的一个值,之后读出来的值有时候反而小于一开始读出来的值,请问你是怎么解决的呢

liuzonggong 发表于 2024-9-10 17:10:50

aa520520 发表于 2024-9-10 16:18
版主,我这也遇到这样同样的问题了,一个定时器中断里自增的一个值,之后读出来的值有时候反而小于一开始读 ...

你要在程序里写上,如果后读入的值小于前读入的值,后面的值要加上进制,这样,后面的值就会永远大于前面的值了。问题就是,变量是循环的,int型到了65535,再来一次就变成0了。我的办法就是在中断中看看次数到了30000吗,到了30000就清零,这样如果后面读到的数小于前面的数,直接加30000,总数也没有超过int的范围。

熊仔 发表于 2024-11-1 08:31:15

liuzonggong 发表于 2024-9-10 17:10
你要在程序里写上,如果后读入的值小于前读入的值,后面的值要加上进制,这样,后面的值就会永远大于前面 ...

读的时候需要关闭中断
页: [1]
查看完整版本: STC32 定时器0做滴答,设计millis(),micros(),delay_ms()函数