C++中Arduino millis()函数的等效替代方法咨询
Great question—when you’re porting your Arduino PID code to native C++, ditching millis() can feel like losing a trusty tool. Let’s walk through the best equivalent options depending on your target platform and use case:
Cross-Platform: C++11+ <chrono> Library
This is my top pick if you want your PID code to run anywhere (Windows, Linux, macOS, etc.). The steady_clock here acts just like Arduino’s millis()—it counts time since the program starts (or system boot, depending on the implementation) and doesn’t get thrown off by system time changes (like manual clock adjustments or NTP updates).
Here’s a drop-in replacement function you can use:
#include <chrono> uint64_t millis() { using namespace std::chrono; return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count(); }
Just call this millis() in your PID logic exactly how you did on Arduino. The uint64_t return type means you won’t have to worry about the 50-day rollover issue that Arduino’s 32-bit unsigned long has—this will run for years before overflowing.
Linux/UNIX-Specific: clock_gettime()
If you’re targeting Linux or other POSIX-compliant systems, clock_gettime() with the CLOCK_MONOTONIC flag is a lightweight, reliable choice. It tracks monotonic time (time since system boot, no backward jumps) which is perfect for PID timing consistency.
Here’s a simple wrapper:
#include <time.h> uint64_t millis() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return static_cast<uint64_t>(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000; }
This is low-overhead and widely supported on all modern Linux systems.
Windows-Specific: QueryPerformanceCounter() or GetTickCount64()
On Windows, you have two solid options:
QueryPerformanceCounter(): Great for high-precision timing (sub-millisecond accuracy), which can be useful for fast-acting PID controllers.#include <windows.h> uint64_t millis() { LARGE_INTEGER frequency, counter; QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&counter); return static_cast<uint64_t>((counter.QuadPart * 1000) / frequency.QuadPart); }GetTickCount64(): Simpler if you don’t need sub-millisecond precision—it directly returns milliseconds since system boot, just like Arduino’smillis().#include <windows.h> uint64_t millis() { return GetTickCount64(); }
Embedded C++ (e.g., STM32, ESP32)
If you’re working with another embedded platform (not Arduino but still C++), most SDKs include a built-in equivalent:
- STM32 HAL:
HAL_GetTick() - ESP-IDF:
esp_timer_get_time() / 1000(converts microseconds to milliseconds) - Nordic nRF:
app_timer_cnt_get()(with some math to convert to milliseconds)
These all behave just like millis()—counting time since device boot, no blocking.
Quick Note on Rollover
If you choose to use a 32-bit integer type (like unsigned long instead of uint64_t) for compatibility with your original Arduino code, make sure to handle rollover correctly in your PID logic. The uint64_t examples above avoid this problem entirely, though.
内容的提问来源于stack exchange,提问作者Cyborgshark

