C51实现定时器0高效率获取开机的微秒数及实现多任务执行无阻塞延时

类似arduino里的micros()函数, 可以获取开机到现在的微秒数,  不能1us中断一次吧, 这样效率太低, 直接获取定时器0的 TH0 和 TL0的值及溢出次数来计时就比较好

这里我用的芯片的是stc32g, 主频为24M, 设置定时器初始为 TH0, TL0的值为0, 最大计时为 32768 us, 计时器每计时一次所用的时间为0.5us, 再次添加一个溢出中断函数来记录溢出了多少次

uint32 globeTime = 0;

void T_IRQ0(void) interrupt 1 using 1 { globeTime++; }

void Timer0_Init(void) // 32768微秒@24.000MHz
{
  AUXR &= 0x7F; // 定时器时钟12T模式
  TMOD &= 0xF0; // 设置定时器模式
  TL0 = 0x00;   // 设置定时初始值
  TH0 = 0x00;   // 设置定时初始值
  TF0 = 0;      // 清除TF0标志
  TR0 = 1;      // 定时器0开始计时
  ET0 = 1;      // 打开定时器0中断
}
uint32 micros() {
  // 系统微秒计时
  return (globeTime << 15) + ((TH0 << 8 | TL0) >> 1);
}

则此时获取开机到现在的运行时间计算为,

(globeTime*65536+(TH0-0)*256+(TL0-0))/2, 

c51计算乘除法比较慢, 为计算效率高采用位操作简化为, 此时需要满足条件为主频为24M, 定时器初始为0
(globeTime << 15) + ((TH0 << 8 | TL0) >> 1)

既然有了能够直接获取开机到现在的时间, 那么利用这个就能实现类似javascript的setTimeout和setInterval, 实现分时多任务执行的效果

JSTime.h

#define JSTimeSize 20 //初始化定时的个数

unsigned long globeTime = 0;
struct JSTimeStruct {
  unsigned long id;         //定时器id,用来取消定时器
  unsigned long startTime;  // 开始执行的时间
  unsigned long periodTime; // 延时的时间 或者 间隔执行的时间
  void (*callback)();       // 回调函数
};
struct JSTimeStruct JSTime_arr[JSTimeSize];
unsigned long JSTime_createTimeId = 0x80000000;
unsigned long JSTime_createIntervalTimeId = 1;

unsigned long micros() {
  // 系统微秒计时
  return (globeTime << 15) + ((TH0 << 8 | TL0) >> 1);
}
void T_IRQ0(void) interrupt 1 using 1 { globeTime++; }
void Timer0_Init(void) // 32768微秒@24.000MHz
{
  AUXR &= 0x7F; // 定时器时钟12T模式
  TMOD &= 0xF0; // 设置定时器模式
  TL0 = 0x00;   // 设置定时初始值
  TH0 = 0x00;   // 设置定时初始值
  TF0 = 0;      // 清除TF0标志
  TR0 = 1;      // 定时器0开始计时
  ET0 = 1;      // 打开定时器0中断
}

// 定时器初始化函数
void JSTime_init() {
  unsigned char i = 0;
  for (i = 0; i < JSTimeSize; i++) {
    JSTime_arr[i].callback = 0;
    JSTime_arr[i].periodTime = 0;
    JSTime_arr[i].startTime = 0;
    JSTime_arr[i].id = 0;
  }
  Timer0_Init();
}

// loop循环中不断刷新定时器
// JSTime_refresh中的局部变量变成全局变量
// 我发现这几个变量声明为JSTime_refresh函数内局部变量时,
// 在执行JSTime_arr[i].callback()回调函数时(这个回调函数中声明了一个局部变量的数组,
// 并且对这个数组进行赋值时),
// 会出现这几个局部变量的值有可能被莫名奇妙修改的情况, 纳尼??? 这是什么原因呢???
// C51编译器的bug吗? C51是真的难以理解啊!
unsigned long JSTime_cacheId = 0;
unsigned long JSTime_currentTime = 0;
unsigned char JSTime_i = 0;
unsigned char JSTime_isFree = 0;
void JSTime_refresh() {
  JSTime_currentTime = micros();
  for (JSTime_i = 0; JSTime_i < JSTimeSize; JSTime_i++) {
    if (JSTime_arr[JSTime_i].id != 0) {
      if (JSTime_currentTime - JSTime_arr[JSTime_i].startTime >=
          JSTime_arr[JSTime_i].periodTime) {
        JSTime_cacheId = JSTime_arr[JSTime_i].id;
        if (JSTime_cacheId >= 0x80000000) {
          // setTimeout 执行完毕就销毁
          JSTime_isFree = 1;
        } else {
          JSTime_isFree = 0;
          // setInteval 不断进行
          JSTime_arr[JSTime_i].startTime = JSTime_currentTime;
        }
        if (JSTime_arr[JSTime_i].callback) {
          JSTime_arr[JSTime_i].callback();
          JSTime_currentTime = micros();
        }
        // 防止在回调函数里调用了 clearTime 而引发bug
        if (JSTime_isFree == 1 && JSTime_arr[JSTime_i].id == JSTime_cacheId) {
          // setTimeout 执行完毕就销毁
          JSTime_arr[JSTime_i].id = 0;
        }
      }
    }
  }
}

// 延时执行, delayTime单位为ms
unsigned long setTimeout(void (*callback)(), float delayTime) {
  unsigned char i = 0;
  for (i = 0; i < JSTimeSize; i++) {
    // 找出失效的 结构体
    if (JSTime_arr[i].id == 0) {
      JSTime_arr[i].callback = callback;
      JSTime_arr[i].periodTime = delayTime * 1000;
      JSTime_arr[i].startTime = micros();
      if (JSTime_createTimeId > 0xfffffff0) {
        JSTime_createTimeId = 0x80000000;
      }
      JSTime_createTimeId++;
      JSTime_arr[i].id = JSTime_createTimeId;
      return JSTime_createTimeId;
    }
  }
  return 0;
}

// 间隔时间执行, intervalTime单位为ms
unsigned long setInterval(void (*callback)(), float intervalTime) {
  unsigned char i = 0;
  for (i = 0; i < JSTimeSize; i++) {
    // 找出失效的 结构体
    if (JSTime_arr[i].id == 0) {
      JSTime_arr[i].startTime = micros();
      JSTime_arr[i].callback = callback;
      JSTime_arr[i].periodTime = intervalTime * 1000;
      if (JSTime_createIntervalTimeId > 0x7ffffff0) {
        JSTime_createIntervalTimeId = 1;
      }
      JSTime_createIntervalTimeId++;
      JSTime_arr[i].id = JSTime_createIntervalTimeId;
      return JSTime_createIntervalTimeId;
    }
  }
  return 0;
}

// 停止计时
void clearTime(unsigned long timeId) {
  unsigned char i = 0;
  for (i = 0; i < JSTimeSize; i++) {
    if (timeId == JSTime_arr[i].id) {
      JSTime_arr[i].id = 0;
    }
  }
}

// 停止计时
void clearAllTime() {
  unsigned char i = 0;
  for (i = 0; i < JSTimeSize; i++) {
    JSTime_arr[i].id = 0;
  }
}

这个是基于天问block开发工具的代码

demo

#define IRC_24M
#define PLL_NO
#define boolean uint8
#define true 1
#define false 0
#define HIGH 1
#define LOW 0

#include <STC32G.h>
#include <stdio.h>
uint32 sys_clk = 24000000; // 设置PWM、定时器、串口、EEPROM频率参数
#include "lib/rcclock.h"
#include "lib/UART.h"
#include "lib/ADC.h"
#include "lib/delay.h"
#include "myLib/JSTime.h"

#define LED_PIN P2_1
#define LED2_PIN P2_0

void mytask1()
{
  LED_PIN = !LED_PIN;
}

void mytask2()
{
  LED2_PIN = !LED2_PIN;
}

void setup()
{
  rcclock_set_irc(1);
  P2M1 &= ~0x01;
  P2M0 |= 0x01; // 推挽输出
  P2M1 &= ~0x02;
  P2M0 |= 0x02; // 推挽输出
  P2M1 &= ~0x04;
  P2M0 |= 0x04; // 推挽输出
  P2M1 &= ~0x08;
  P2M0 |= 0x08; // 推挽输出
  P3M1 |= 0x04;
  P3M0 &= ~0x04;                                                 // P3.2 高阻输入
  uart_init(UART_1, UART1_RX_P30, UART1_TX_P31, 1000000, TIM_1); // 初始化串口
  ET0 = 1; // 控制定时器中断
  EA = 1;
  JSTime_init(); // JSTime初始化
  
  setInterval(mytask1, 1000); // 添加任务1
  setInterval(mytask2, 200); // 添加任务2
}

void ispDowbload()
{
  if (P3_2 == LOW)
  {
    if (P3_2 == LOW)
    {
      IAP_CONTR = 0x60; // 进入ISP下载
    }
  }
}

void loop()
{
  JSTime_refresh();
  ispDowbload();
}

void main(void)
{
  setup();
  while (1)
  {
    loop();
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值