# 如何处理millis（）转换？

73

``````void loop()
{
unsigned long currentMillis = millis();

// Read the sensor when needed.
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
readSensor();
}

// Do other stuff...
}
``````

5

Edgar Bonet 2015年

2

4
@Jasen：是的！`previousMillis += interval`如果您要保持恒定的频率，并确保处理所需的时间少于`interval`，则可确保`previousMillis = currentMillis`最小延迟为`interval`
Edgar Bonet

frarugi87 '18

Answers:

95

## 不比较时间戳

``````unsigned long t1 = millis();
delay(3000);
unsigned long t2 = millis();
if (t2 > t1) { ... }
``````

1. `later_timestamp - earlier_timestamp`产生持续时间，即在较早的时刻和较晚的时刻之间经过的时间量。这是涉及时间戳的最有用的算术运算。
2. `timestamp ± duration`产生一个时间戳，该时间戳在初始时间戳之后（如果使用+）或之前（如果-）。听起来不那么有用，因为生成的时间戳只能用于两种计算中...

## 比较持续时间很好

``````void myDelay(unsigned long ms) {          // ms: duration
unsigned long start = millis();       // start: timestamp
unsigned long finished = start + ms;  // finished: timestamp
for (;;) {
unsigned long now = millis();     // now: timestamp
if (now >= finished)              // comparing timestamps: BUG!
return;
}
}
``````

``````void myDelay(unsigned long ms) {              // ms: duration
unsigned long start = millis();           // start: timestamp
for (;;) {
unsigned long now = millis();         // now: timestamp
unsigned long elapsed = now - start;  // elapsed: duration
if (elapsed >= ms)                    // comparing durations: OK
return;
}
}
``````

``while (millis() < start + ms) ;  // BUGGY version``

``while (millis() - start < ms) ;  // CORRECT version``

## 如果我真的需要比较时间戳怎么办？

``````unsigned long reference_instant = t2 - LONG_ENOUGH_DURATION;
unsigned long from_reference_until_t1 = t1 - reference_instant;
unsigned long from_reference_until_t2 = t2 - reference_instant;
if (from_reference_until_t1 < from_reference_until_t2)
// t1 is before t2
``````

``````if (t1 - t2 + LONG_ENOUGH_DURATION < LONG_ENOUGH_DURATION)
// t1 is before t2
``````

``````if ((signed long)(t1 - t2) < 0)  // works with gcc
// t1 is before t2
``````

`signed`上面的关键字是多余的（平原`long`总是带符号的），但是它有助于使意图更清晰。转换为有符号长等于设置为`LONG_ENOUGH_DURATION`等于24.85天。该技巧不可移植，因为根据C标准，结果是实现定义的。但是由于gcc编译器承诺会做正确的事，因此它可以在Arduino上可靠地工作。如果我们希望避免实现定义的行为，则上述签名的比较在数学上等效于此：

``````#include <limits.h>

if (t1 - t2 > LONG_MAX)  // too big to be believed
// t1 is before t2
``````

``````if ((t1 - t2) & 0x80000000)  // test the "sign" bit
// t1 is before t2
``````

## 我如何针对米利斯翻转测试我的草图

``````#include <util/atomic.h>

void setMillis(unsigned long ms)
{
extern unsigned long timer0_millis;
ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
timer0_millis = ms;
}
}
``````

``````// 6-second time loop starting at rollover - 3 seconds
if (millis() - (-3000) >= 6000)
setMillis(-3000);
``````

## 如果我真的需要追踪很长的时间怎么办？

``````uint64_t millis64() {
static uint32_t low32, high32;
uint32_t new_low32 = millis();
if (new_low32 < low32) high32++;
low32 = new_low32;
return (uint64_t) high32 << 32 | low32;
}
``````

2

3
@Jasen：是的！人们似乎似乎不止一次尝试“解决”最初不存在的问题。
Edgar Bonet

2

1
StackExchange上最好和最有用的答案之一！非常感谢！：）

17

TL; DR简版：

An `unsigned long`是0到4,294,967,295（2 ^ 32-1）。

`Serial.println( ( unsigned long ) ( 10 - 4294967290 ) ); // 16`

ps95

@ prakharsingh95 10ms-15ms将变为〜49.7天-5ms，这是正确的差值。数学运算直到`millis()`翻转两次为止，但是所讨论的代码极不可能发生这种情况。
BrettAM 2015年

ps95 2015年

@ prakharsingh95 `previousMillis`必须先测量其中存储的一个`currentMillis`，所以如果`currentMillis`小于`previousMillis`一个发生了翻转。通过数学计算得出，除非发生两次翻转，否则您甚至都无需考虑。
BrettAM 2015年

1

ps95 2015年

1

`millis()`课程包起来！

1. 使用ID代替`millis()`直接使用。
2. 使用ID比较冲销。这是干净且无翻转的。
3. 对于特定的应用程序，要计算两个id之间的确切差异，请跟踪冲销和标记。计算差异。

1. 定期更新本地戳的速度比快`millis()`。这将帮助您找出是否`millis()`溢出。
2. 计时器的周期决定精度
``````class Timer {

public:
static long last_stamp;
static long *stamps;
static int *reversals;
static int count;
static int reversal_count;

static void setup_timer() {
// Setup Timer2 overflow to fire every 8ms (125Hz)
//   period [sec] = (1 / f_clock [sec]) * prescale * (255-count)
//                  (1/16000000)  * 1024 * (255-130) = .008 sec

TCCR2B = 0x00;        // Disable Timer2 while we set it up

TCNT2  = 130;         // Reset Timer Count  (255-130) = execute ev 125-th T/C clock
TIFR2  = 0x00;        // Timer2 INT Flag Reg: Clear Timer Overflow Flag
TIMSK2 = 0x01;        // Timer2 INT Reg: Timer2 Overflow Interrupt Enable
TCCR2A = 0x00;        // Timer2 Control Reg A: Wave Gen Mode normal
TCCR2B = 0x07;        // Timer2 Control Reg B: Timer Prescaler set to 1024

count = 0;
stamps = new long[50];
reversals = new int [10];
reversal_count =0;
}

static long get_stamp () {
stamps[count++] = millis();
return count-1;
}

static bool compare_stamps_by_id(int s1, int s2) {
return s1 > s2;
}

static long long get_stamp_difference(int s1, int s2) {
int no_of_reversals = 0;
for(int j=0; j < reversal_count; j++)
if(reversals[j] < s2 && reversals[j] > s1)
no_of_reversals++;
return stamps[s2]-stamps[s1] + 49.7 * 86400 * 1000;
}

};

long Timer::last_stamp;
long *Timer::stamps;
int *Timer::reversals;
int Timer::count;
int Timer::reversal_count;

ISR(TIMER2_OVF_vect) {

long stamp = millis();
if(stamp < Timer::last_stamp) // reversal
Timer::reversals[Timer::reversal_count++] = Timer::count;
else
; // no reversal
Timer::last_stamp = stamp;
TCNT2 = 130;     // reset timer ct to 130 out of 255
TIFR2 = 0x00;    // timer2 int flag reg: clear timer overflow flag
};

// Usage

void setup () {
Timer::setup_timer();

long s1 = Timer::get_stamp();
delay(3000);
long s2 = Timer::get_stamp();

Timer::compare_stamps_by_id(s1, s2); // true

Timer::get_stamp_difference(s1, s2); // return true difference, taking into account reversals
}
``````

9

Edgar Bonet 2015年

1

``````volatile unsigned long long timer0_millis = 0;      // BFB: need 64-bit resolution

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long long m = timer0_millis;       // BFB: need 64-bit resolution
unsigned char f = timer0_fract;

m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}

timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}

// BFB: 64-bit version
unsigned long long millis64()
{
unsigned long long m;
uint8_t oldSREG = SREG;

// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;

return m;
}
``````

1

Edgar Bonet
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.