Explain clock synchronization mechanism
This commit is contained in:
parent
76427bdede
commit
9c1d743bfd
@ -1,5 +1,116 @@
|
||||
#include "ClockSync.h"
|
||||
|
||||
/**
|
||||
* @file ClockSync.c
|
||||
* @author Jasper v. Blanckenburg (j.blanckenburg@fasttube.de)
|
||||
* @brief Clock synchronization mechanism -- slave side
|
||||
* @version 0.1
|
||||
* @date 2022-08-02
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
* OVERVIEW
|
||||
* =========
|
||||
* The slaves use the STM's internal clock (HSI), which is -- especially at
|
||||
* higher temperatures -- quite inaccurate (±4% over the STM's working
|
||||
* temperature range, according to the datasheet).
|
||||
*
|
||||
* Since the CAN bitrate is directly determined from the HSI (through prescaling
|
||||
* & time quanta), an inaccurate HSI means an inaccurate CAN bitrate. Especially
|
||||
* once the battery heats up, this leads to packet loss and ultimately the CAN
|
||||
* transceiver entering Bus-Off due to too many transmission errors.
|
||||
*
|
||||
* The easy fix would be to use an external clock (HSE), i.e. a quartz crystal.
|
||||
* Although a crystal is present on the slaves, it does not work on every one
|
||||
* and is mounted on the (inaccessible) underside. Thus, we need to make do with
|
||||
* the HSI.
|
||||
*
|
||||
* Fortunately, the HSI frequency can be trimmed through the HSITRIM bits in the
|
||||
* RCC_CR register (see STM32F412 reference manual, section 6.2.2; as well as
|
||||
* STM AN5067).
|
||||
*
|
||||
* The HSITRIM register provides the mechanism for manipulating the HSI
|
||||
* frequency, however we still need to determine what value to manipulate it to.
|
||||
* Since we don't really care about the absolute accuracy of the slaves' clocks,
|
||||
* but rather their relative accuracy to the other nodes on the CAN bus
|
||||
* (especially the master), we can synchronize the clocks to one another via
|
||||
* timed CAN frames.
|
||||
*
|
||||
* TIMED CAN FRAMES
|
||||
* ================
|
||||
* As the master is the least affected by the battery heating up, and also had
|
||||
* the most accurate clock during testing, we use it to generate the timed
|
||||
* frames.
|
||||
*
|
||||
* It sends frames from timer interrupts and with high priority (low ID) to
|
||||
* ensure minimal deviation from their intended frequency. It sends two separate
|
||||
* kinds of frames: CLOCK_SYNC and MASTER_HEARTBEAT.
|
||||
*
|
||||
* The MASTER_HEARTBEAT frames are sent every 100 ms. Their purpose is simply to
|
||||
* reliably have messages on the bus, so that the slaves can tell whether they
|
||||
* are roughly in sync with the master by checking for their reception (see the
|
||||
* FREQUENCY HOPPING section).
|
||||
*
|
||||
* The CLOCK_SYNC frames are sent every 1000 ms. They serve as the external
|
||||
* clock source. The slaves continually trim their HSI according to the time
|
||||
* they measure between to CLOCK_SYNC frames (see the NORMAL OPERATION section).
|
||||
*
|
||||
* FREQUENCY HOPPING
|
||||
* =================
|
||||
* If the HSI is very out of sync with the master's clock (e.g. because the AMS
|
||||
* was restarted with a warm battery), the slaves don't receive any CAN packets
|
||||
* from the master and thus can't rely on the CLOCK_SYNC frames for
|
||||
* synchronization. In this case, they rely on what is essentially frequency
|
||||
* hopping.
|
||||
*
|
||||
* The frequency hopping mechanism has two stages: One to get in the right
|
||||
* ballpark, and one to make the communication reliable enough for normal
|
||||
* operation.
|
||||
*
|
||||
* STAGE 1
|
||||
* -------
|
||||
* Stage 1 trims the HSI until at least one MASTER_HEARTBEAT frame has been
|
||||
* received. The frequency alternates between lower and higher values, i.e. if
|
||||
* the trim was initially 16, it will go through the following values:
|
||||
*
|
||||
* 16 -> 14 -> 18 -> 12 -> 20 -> 10 -> 22 -> ...
|
||||
*
|
||||
* Once a MASTER_HEARTBEAT frame has been received, the slave transitions to
|
||||
* stage 2.
|
||||
*
|
||||
* STAGE 2
|
||||
* -------
|
||||
* Stage 2 trims the HSI further until at least three consecutive
|
||||
* MASTER_HEARTBEAT frames have been received. The frequency alternates in the
|
||||
* same fashion as in stage 1, but now around the frequency where a
|
||||
* MASTER_HEARTBEAT frame was received in stage 1, and more slowly.
|
||||
*
|
||||
* Once three consecutive MASTER_HEARTBEAT frames have been received, the slave
|
||||
* transitions to normal operation.
|
||||
*
|
||||
* NORMAL OPERATION
|
||||
* ================
|
||||
* During normal operation, the HSI is continually trimmed so that CLOCK_SYNC
|
||||
* frames are received every 1000 ms. Since the slave measures time in
|
||||
* milliseconds (via the HAL_GetTick() function), this allows a measurement
|
||||
* accuracy of 0.1%. Each increment of HSITRIM should account for an approximate
|
||||
* 0.3% increase in the clock frequency, according to AN5067, so the 0.1%
|
||||
* accuracy is more than enough for accurate trimming.
|
||||
*
|
||||
* By counting the ticks between two CLOCK_SYNC frames, the slave determines its
|
||||
* actual HSI frequency (assuming the master clock is accurate):
|
||||
*
|
||||
* f_real = 16 MHz * measured_ticks / 1000
|
||||
*
|
||||
* If the real frequency differs from the target frequency (16 MHz) by more than
|
||||
* the trim delta, the trim is incremented or decremented accordingly.
|
||||
*
|
||||
* The trim delta is determined dynamically: It is the difference between the
|
||||
* real frequency before and after each trim.
|
||||
*
|
||||
* If the slave misses two consecutive CLOCK_SYNC frames for whatever reason, it
|
||||
* returns to (stage 1) frequency hopping.
|
||||
*/
|
||||
#include "AMS_CAN.h"
|
||||
|
||||
#include "stm32f412rx.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user