【Arduino 函数练习】millis() 定时计数器


以常规的方式 实现时分秒计时

unsigned long myTime;
int Time_second = 0;//秒钟
int Time_minute = 0;//分钟
int Time_hour = 0; //小时

long Time_hour_setting = 3600000;   //1小时 = 3600000 毫秒
long Time_minute_setting = 600000; // 1分钟 = 60000 毫秒
long Time_second_setting = 1000; //1秒 = 1000 毫秒

unsigned long start_time = 0;
void setup() {
  Serial.begin(9600);
}

void loop() {
 start_time = start_time + 1000;
 Time_second = start_time/Time_second_setting;// 

if(Time_second >= 60)
{
  start_time = 1000;
  Time_minute++;
  }
if(Time_minute >= 60)
{ Time_minute = 0; 
  Time_hour++;   } 

 Serial.print("millis:\t");
 Serial.println( start_time);
 Serial.print(Time_hour);
 Serial.print(":");
 Serial.print(Time_minute);
 Serial.print(":");
 Serial.println(Time_second);

}

Arduino millis()函数应用

参考 >语言 >函数 >时间 >毫秒

返回自Arduino开发板开始运行当前程序以来经过的毫秒数。大约50天后,该数字将溢出(返回零)。


unsigned long myTime;

void setup() {
  Serial.begin(9600);
}

void loop() {
 myTime = millis();
 Serial.print("Time:\t");
 Serial.println(myTime);
 delay(1000);
}

请注意millis()的返回值为类型unsigned long,如果程序员尝试使用较小的数据类型(例如)进行算术,则可能会发生逻辑错误int。偶数签名long可能会遇到错误,因为其最大值是其未签名副本的最大值的一半。


unsigned long myTime;
int Time_second;//秒钟
int Time_minute;//分钟
int Time_hour; //小时

long Time_hour_setting = 3600000;   //1小时 = 3600000 毫秒
long Time_minute_setting = 600000; // 1分钟 = 60000 毫秒
long Time_second_setting = 1000; //1秒 = 1000 毫秒

unsigned long start_time;
void setup() {
  Serial.begin(9600);

}

void loop() {
 start_time = millis();

 Time_hour = start_time / Time_hour_setting;//小时 = millis 除以  进率3600000 
 Time_minute = (start_time % Time_hour_setting) / Time_minute_setting;//分钟数 = millis的小时取余后,除以进率60000
 Time_second = (start_time % Time_hour_setting) % Time_minute_setting / Time_second_setting;// 

 Serial.print(Time_hour);
 Serial.print(":");
 Serial.print(Time_minute);
 Serial.print(":");
 Serial.println(Time_second);
 delay(1000);

}

在这里插入图片描述
BUG1:秒数从 0,1,?,3……自动跳过了2
在这里插入图片描述
原因是:millis()函数会有误差,不稳定。999 /1000 = 0
在这里插入图片描述

BUG 2:秒数超过60后,分钟数没有变化

在这里插入图片描述
粗心,1分钟 = 60000 毫秒,写成了600000

long Time_minute_setting = 60000; // 1分钟 = 60000 毫秒


终 版:


unsigned long myTime;
unsigned int Time_second;//秒钟
unsigned int Time_minute;//分钟
unsigned int Time_hour; //小时

unsigned long Time_hour_setting = 3600000;   //1小时 = 3600000 毫秒
unsigned long Time_minute_setting = 60000; // 1分钟 = 60000 毫秒
unsigned long Time_second_setting = 1000; //1秒 = 1000 毫秒

unsigned long start_time;
void setup() {
  Serial.begin(9600);

}

void loop() {
 start_time = millis();
 Serial.print("millis:");
 Serial.println(start_time);

 Time_hour = start_time / Time_hour_setting;  //小时 = millis 除以  进率3600000 
 Time_minute = (start_time % Time_hour_setting) / Time_minute_setting;//分钟数 = millis的小时取余后,除以进率60000
 Time_second = ((start_time % Time_hour_setting) % Time_minute_setting) / Time_second_setting;  // 

 Serial.print(Time_hour);
 Serial.print(":");
 Serial.print(Time_minute);
 Serial.print(":");
 Serial.println(Time_second);
 delay(1000);

}

在这里插入图片描述

总结:用millis()函数做时钟会有误差,大概过了1分钟会有0.1秒的误差,几分钟过后误差会更大。


(2021/05/21更新)

在这里插入图片描述

unsigned long myTime;
unsigned int Time_second;//秒钟
unsigned int Time_minute;//分钟
unsigned int Time_hour; //小时

//unsigned int N_clockTime_hour;//晚上闹钟的时分秒变量
//unsigned int N_clockTime_minute;
//unsigned int N_clockTime_second;
//unsigned int M_clockTime_hour;//早上闹钟的时分秒变量
//unsigned int M_clockTime_minute;
//unsigned int M_clockTime_second;

unsigned long Time_hour_setting = 3600000;   //1小时 = 3600000 毫秒
unsigned long Time_minute_setting = 60000; // 1分钟 = 60000 毫秒
unsigned long Time_second_setting = 1000; //1秒 = 1000 毫秒

int  N_Voiceplay_pin = 3;
int  M_Voiceplay_pin = 2;


unsigned long start_time;
void setup() {
  pinMode(N_Voiceplay_pin,OUTPUT);
  pinMode(M_Voiceplay_pin,OUTPUT);
  digitalWrite(N_Voiceplay_pin,HIGH);
  digitalWrite(M_Voiceplay_pin,HIGH);
  Serial.begin(9600);
}

void loop() {
 start_time = millis();
 Serial.print("millis:");
 Serial.println(start_time);

 Time_hour = start_time / Time_hour_setting;  //小时 = millis 除以  进率3600000 
 Time_minute = (start_time % Time_hour_setting) / Time_minute_setting;//分钟数 = millis的小时取余后,除以进率60000
 Time_second = ((start_time % Time_hour_setting) % Time_minute_setting) / Time_second_setting;  // 

 Serial.print(Time_hour);
 Serial.print(":");
 Serial.print(Time_minute);
 Serial.print(":");
 Serial.println(Time_second);
// delay(1000);

 if((Time_hour == 0) && (Time_minute == 0) && (Time_second == 10))//N_clockTime_hour  8:30:00 pm
 {digitalWrite(N_Voiceplay_pin,LOW);delay(2000);digitalWrite(N_Voiceplay_pin,HIGH);}
 if((Time_hour == 0) &&( Time_minute == 1) && (Time_second == 5))//M_clockTime_hour   6:30:00  am
 {digitalWrite(M_Voiceplay_pin,LOW);delay(2000);digitalWrite(M_Voiceplay_pin,HIGH);}

}

参考资料:


Arduino 多任务系统—millis()

(2021.11.24 更新)

用Arduino millis() 函数 实现一定程度上的多任务系统,实现同时操控三组 LED 灯的开关,在测试中,黄灯以 200ms 为间隔闪烁,蓝灯以 1s 为间隔闪烁,而红灯通过 按键 控制开关,并且相互发生的情况并不受干扰。


int led1 =  6;      // led1 connected at pin 6

int led2 =  7;      // led1 connected at pin 7

int toggleLed = 5;    // push button controlled led connected at pin 5

int pushButton = 2;    // push butoon connected at pin 2 which is also interrupt pin

int ledState1 = LOW;  // to determine the states of led1 and led2

int ledState2 = LOW;

unsigned long previousMillis1 = 0;  //store last time LED1 was blinked

const long period1 = 1000;        // period at which led1 blinks in ms

unsigned long previousMillis2 = 0;  //store last time LED2 was blinked

const long period2 = 200;            // period at which led1 blinks in ms

int debouncePeriod = 20;            // debounce delay of 20ms

int debounceMillis = 0;            // similar to previousMillis

bool buttonPushed = false;    // interrupt routine button status

int ledChange = LOW;      // to track the led status last

int lastState = HIGH;    // to track last button state

void setup() {

  pinMode(led1, OUTPUT);              // define pins as input or output

  pinMode(led2, OUTPUT);

  pinMode(toggleLed, OUTPUT);

  pinMode(pushButton, INPUT);

  attachInterrupt(digitalPinToInterrupt(pushButton), pushButton_ISR, CHANGE);  // use interrupt pin2

}

void pushButton_ISR()

{

  buttonPushed = true;  // ISR should be as short as possible

}

void loop() {

//控制蓝灯闪烁

  unsigned long currentMillis = millis(); // store the current time

  if (currentMillis - previousMillis1 >= period1) {    // check if 1000ms passed

    previousMillis1 = currentMillis;  // save the last time you blinked the LED


    if (ledState1 == LOW) 
    {  // if the LED is off turn it on and vice-versa

      ledState1 = HIGH;  //change led state for next iteration

    } else {

      ledState1 = LOW;

    }

    digitalWrite(led1, ledState1);    //set LED with ledState to blink again

  }

//控制黄灯闪烁

  if (currentMillis - previousMillis2 >= period2) { // check if 200ms passed

    previousMillis2 = currentMillis;  // save the last time you blinked the LED

    if (ledState2 == LOW) { // if the LED is off turn it on and vice-versa

      ledState2 = HIGH;

    } else {

      ledState2 = LOW;

    }

    digitalWrite(led2, ledState2);//set LED with ledState to blink again

  }

  if (buttonPushed = true)    // check if ISR is called

  {

    if ((currentMillis - debounceMillis) > debouncePeriod && buttonPushed)  // generate 20ms debounce delay to avoid multiple presses

    {

      debounceMillis = currentMillis;      // save the last debounce delay time

//开关控制红灯

      if (digitalRead(pushButton) == LOW && lastState == HIGH)    // change the led after push button is pressed

      {

        ledChange = ! ledChange;

        digitalWrite(toggleLed, ledChange);   

        lastState = LOW;

      }

      else if (digitalRead(pushButton) == HIGH && lastState == LOW)   

      {

        lastState = HIGH;

      }

      buttonPushed = false;

    }

  }

}



Arduino定时计数器(T0T1T2) 的灵活使用

参考文章

单片机的几大功能组成部件中,定时计数器和中断占有重要地位。

定时/计数器

两个16位定时/计数器T0T1,可用作定时器或计数器使用,通过编程配置可工作于4种不同的工作模式下。

中断系统

五个中断源的中断控制系统,包括:两个外部中断INT0INT1)和三个内部中断(定时/计数器T0T1串口中断)。每个中断源均可设置为高优先级或低优先级。

Arduino 的定时器

  • T/C0: Pin6(OC0A)和Pin5(OC0B)

  • T/C1: Pin9(OC1A)和Pin10(OC1B)

  • T/C3: Pin11(OC2A)和Pin3(OC2B)

Arduino已经将T/C0的溢出中断运用到了delay()delayMicrosecondsmillis()micros()中,这些函数都写在了Arduino核心代码wiring.c文件中。


Arduino非 定时器0 延时,自制软件延时

参考文章

Arduino创造的delay()delayMicroseconds()等利用定时 计数器0 的延时函数,是不可以和程序并行的,不可以多任务。

在使用delay()或者delayMicroseconds()的时候,很多事情都干不成了,所以换成普通的自制软件延时可以腾出 timer0 功能做些别的事情。

在做Arduino项目定时器计数器的不够用的情况下,例如:小车有四个独立直流电机,用timer1timer2来控制 4 路 PWM 波输出,于是就只剩下timer0了。

但是timer0 被利用在了delay()和delayMicroseconds(),所以自制软件延时程序很就很有必要了。

#define NOP do { __asm__ __volatile__ ("nop"); } while (0)
#define ulong unsigned long
void setup()
{
        Serial.begin(9600);
}


void loop()
{
        ulong a=micros();
        delay_(1000);//软件延时1000ms
        ulong  b=micros();
        ulong  c;
        c=b-a;
        unsigned char x[4]={0};
        x[0]=c>>24;x[1]=c>>16;x[2]=c>>8;x[3]=c&0x000000FF;
        Serial.write(x,4);//返回4个字节,转换成十进制就是delay_(ms)的时间,单位是us
        while(1) ;
}


void delay1()//自制软件延时1ms
{
        for(ulong j=0;j<1985;j++) NOP;   
}


void delay_(int ms)//自制软件延时
{
        for(int i=0; i<ms; i++)
        {
           for(ulong j=0;j<1985;j++) NOP;
        }       
}



//第一句宏定义的 NOP 其实就是单片机汇编语言里的一个空操作,运行一个指令周期
//delay1() 是运行1ms函数,测试了一下,返回00 00 03 E8 ,正好是1000us,但实际上micros()函数有正负4us的误差。
//调试完delay1() 后,就开始做delay_(int ms)函数,我测试了一下delay_(1000),返回00 0F 3E 84 ,是999044us,0.999秒吧,看着精度还不错~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naiva

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值