[Dprglist] Comparing Raspberry Pi's hardware PWM vs. external clock

Murray Altheim murray18 at altheim.com
Thu Mar 4 13:17:43 PST 2021


Here's my report comparing an external clock vs. an internal hardware
clock on a Raspberry Pi.


# Executive Summary ...................................................

There doesn't seem to be any notable difference between using the
internal (PWM) hardware clock of a PI vs. an external microcontroller
in terms of either precision or accuracy, not that would affect usage
of either as a system clock for a robot.

The internal hardware clock may be considered a more elegant solution,
but requires more significant Linux skills to set up and configure.
This can be ameliorated by providing suitable documentation and
example code. This solution was shown to display intermittently but
very infrequent lags whose source has not yet been determined -- these
may be entirely in the test harness, so have been discounted.

As hardware, the external clock is relatively simple to set up but as
has been mentioned here previously, kinda "wastes" an entire micro-
controller for what is admittedly a simple task. Given the hardware
cost is very low this waste is mostly aesthetic, but also includes the
physical space taken on a robot, additional power usage and hardware
complexity.

Given their respective pros and cons, both seem to be viable solutions.
Design elegance is an important consideration, but must be balanced
against the respective hardware and software skills and experience of
the person doing the implementation.


# Detailed Summary ....................................................

The tests were run on my KD01 robot using a Raspberry Pi 3 B+ and a
bench power supply.

I ran two separate types of tests, one with a hardware (Raspberry Pi PWM)
clock, the second with an external clock whose 20Hz pulse comes from a
C++ program running on a Cortex-M4 (Adafruit Itsy Bitsy M4 Express) mounted
on the robot. The details on setting up the hardware clock can be found
inside the hw_clock.py file (listed below).

Each of these test types was run three ways:

   1. no system load (apart from the ssh session)
   2. light system load (running 'sudo apt update' once during the test)
   3. high load, running "stress --cpu 4", a temperature monitor with fan
      script, the 'top' system monitor, and three ssh sessions

Each test was run 5 times (the hardware clock low load 10x), at 1000 clock
cycles per test, each cycle a 20Hz/50ms clock loop. The output is log-printed
to sysout. The test results are listed below.

The external clock seems to consistently run about 0.1ms slow, but with very
low variability even under stress.

The internal hardware clock reacts more to system load and on some occasions
(particularly under the 'sudo apt update' load) sometimes gets a significant
bump (extreme case was 16ms), but very infrequently and so far not repeatable.
Over many runs of 1000 cycles this happened maybe three or four times, but
it's notable that this did occur.

The external clock is more more precise but less accurate. The C++ clock
loop is actually set for 50000 microseconds (and this of course shows up on
the M4's serial monitor as "50000ns") but something between that clock and
it showing up in the Python program is consistently creating that 0.1ms lag.
I'm not going to worry about this as it would not cause any problem when used
as a system clock. The external clock solution shows no discernible reaction
to system load that can't be ascribed to the test harness. The downside of
the external clock is that it requires the setup of a separate microcontroller
board, and C++ programming of that board.

The internal hardware clock is, to a very small degree, more accurate but
less precise, and more prone to system loads, though in general probably
not problematic. I haven't determined what causes the infrequent, intermittent
lags in timing.

The downside of using the internal hardware clock is that its configuration
is lost upon each reboot, meaning that it's (software-wise) more complicated
to set up and maintain. There are a number of ways of running a root-permission
program on boot (e.g., rc.local, systemd, etc.); if someone has the Linux OS
skills to do this it's not hard to do. OTOH, for someone without the software
skills but having hardware skills, setting up an external microcontroller with
a simple 20Hz timing loop in C++ is not hard to do. YMMV.

I suspect that for both external and internal clocks, the displayed timing
lags are likely due to the test program being affected by the system load,
not the clocks themselves, since both the external and internal clocks
display some lag and we know that the external clock is entirely independent.

So this last point is what I was really trying to determine: that the internal
hardware clock of the Pi is (so far as I can ascertain) entirely independent
on system load. I could use either external or internal clock and be assured
of reasonable, reliable timing. I've already got the Itsy Bitsy installed and
functional so I could leave it there. Or figure out how to run a short Python
or bash script to re-establish the hardware PWM timer on reboot. I've written
two methods into the HardwareClock class, check_boot_config() and configure(),
which resp. checks to see if /boot/config.txt has been modified to support a
PWM clock, and then to see if the PWM clock is actually running. The latter
must be run as sudo, but this call could be made by a startup script. I'll
need to make a change so that if called in that way it actually exits rather
than running the clock, but I'll be doing that in an update soon.

The code for the tests and library classes may be found at:

    https://github.com/ifurusato/ros/blob/master/ext_clock_test.py
    https://github.com/ifurusato/ros/blob/master/lib/ext_clock.py
    https://github.com/ifurusato/ros/blob/master/hw_clock_test.py
    https://github.com/ifurusato/ros/blob/master/lib/hw_clock.py

As noted above, when the hw_clock.py is run as root it will establish and
start the clock, which currently keeps running rather than exiting. I've not
determined whether I like this behaviour or not, but this may change in the
near future.


# test results .................................................................

# external clock, no load:  ....................................................
Δ 50.10200; err:  0.10200; max err:  0.26000; mean: 50.10078; max vari:  0.00216
Δ 50.11900; err:  0.11900; max err:  0.69500; mean: 50.09706; max vari:  0.01953
Δ 50.00400; err:  0.00400; max err:  0.63300; mean: 50.09290; max vari:  0.01522
Δ 50.09700; err:  0.09700; max err:  0.24800; mean: 50.09582; max vari:  0.00346
Δ 50.11000; err:  0.11000; max err:  0.80300; mean: 50.10728; max vari:  0.02050

# external clock, low load:  ...................................................
Δ 50.09900; err:  0.09900; max err:  0.42900; mean: 50.09946; max vari:  0.00766
Δ 50.10700; err:  0.10700; max err:  1.74600; mean: 50.08970; max vari:  0.10963
Δ 50.09900; err:  0.09900; max err:  0.74500; mean: 50.09298; max vari:  0.03481
Δ 50.08800; err:  0.08800; max err:  0.75500; mean: 50.09696; max vari:  0.01975
Δ 50.09500; err:  0.09500; max err:  0.68500; mean: 50.09910; max vari:  0.01608

# external clock, high load:  ..................................................
Δ 50.11500; err:  0.11500; max err:  0.15700; mean: 50.11342; max vari:  0.00039
Δ 50.11500; err:  0.11500; max err:  0.15500; mean: 50.11678; max vari:  0.00010
Δ 50.08400; err:  0.08400; max err:  0.19500; mean: 50.11086; max vari:  0.00043
Δ 50.12000; err:  0.12000; max err:  0.97100; mean: 50.11776; max vari:  0.02985
Δ 50.09600; err:  0.09600; max err:  0.20100; mean: 50.10798; max vari:  0.00045

# hardware clock, no load:  ....................................................
Δ 50.00400; err:  0.00400; max err:  0.35500; mean: 50.00110; max vari:  0.01178
Δ 50.00200; err:  0.00200; max err:  0.78000; mean: 50.00150; max vari:  0.06192
Δ 50.03500; err:  0.03500; max err:  1.55600; mean: 50.00242; max vari:  0.11057
Δ 50.00000; err:  0.00000; max err:  0.23600; mean: 50.00090; max vari:  0.00706
Δ 50.00800; err:  0.00800; max err:  0.21400; mean: 50.00118; max vari:  0.00805

# hardware clock, low load:  ...................................................
Δ 49.96800; err: -0.03200; max err:  0.21500; mean: 50.00224; max vari:  0.00586
Δ 50.10900; err:  0.10900; max err:  6.71900; mean: 50.00814; max vari:  1.91167
Δ 50.04900; err:  0.04900; max err:  1.35400; mean: 50.00202; max vari:  0.09144
Δ 49.99100; err: -0.00900; max err:  1.05200; mean: 50.00136; max vari:  0.09587
Δ 49.96600; err: -0.03400; max err:  0.68800; mean: 50.00162; max vari:  0.02198
Δ 50.00300; err:  0.00300; max err:  2.26600; mean: 50.00174; max vari:  0.21424
Δ 49.99200; err: -0.00800; max err:  9.11900; mean: 50.00222; max vari:  3.45226
Δ 50.00700; err:  0.00700; max err:  0.64700; mean: 50.00004; max vari:  0.03155
Δ 49.90500; err: -0.09500; max err:  0.49400; mean: 50.00238; max vari:  0.01838
Δ 49.98100; err: -0.01900; max err:  0.38800; mean: 50.00296; max vari:  0.01134

worst case (unexplained):
Δ 49.90700; err: -0.09300; max err: 19.67400; mean: 50.00384; max vari: 15.84047

# hardware clock, high load:  ..................................................
Δ 49.99300; err: -0.00700; max err:  0.85000; mean: 50.00248; max vari:  0.02417
Δ 50.01000; err:  0.01000; max err:  0.24200; mean: 50.00284; max vari:  0.00341
Δ 50.01100; err:  0.01100; max err:  4.27400; mean: 50.00352; max vari:  0.72376
Δ 50.00500; err:  0.00500; max err:  0.27800; mean: 49.99622; max vari:  0.00419
Δ 50.00500; err:  0.00500; max err:  0.18600; mean: 50.00202; max vari:  0.00250

# legend:  ................................................................
Δ:          delta: elapsed time (ms) for this cycle
err:        error for this cycle
max err:    maximum error over 1000 cycles
mean:       mean delta over 1000 cycles
max vari:   maximum variance over 1000 cycles

...........................................................................
Murray Altheim <murray18 at altheim dot com>                       = =  ===
http://www.altheim.com/murray/                                     ===  ===
                                                                    = =  ===
     In the evening
     The rice leaves in the garden
     Rustle in the autumn wind
     That blows through my reed hut.
            -- Minamoto no Tsunenobu



More information about the DPRGlist mailing list