nordic-auko / nRF5-ble-timesync-demo

nRF52 clock synchronization demo code
55 stars 50 forks source link

External sync trigger #11

Closed ShaharHD closed 2 years ago

ShaharHD commented 3 years ago

Hi,

I was successfully able to incorporate this example code into a project which requires time sync between Nordic devices.

The issue I'm having is that in my implementation the whole sync mechanism to be dependent upon an external clock input (PWM, roughly 10Hz)

What would be the best approach to implement this?

nordic-auko commented 3 years ago

Hi,

I was successfully able to incorporate this example code into a project which requires time sync between Nordic devices.

The issue I'm having is that in my implementation the whole sync mechanism to be dependent upon an external clock input (PWM, roughly 10Hz)

What would be the best approach to implement this?

Hello, glad to hear you're interested in the example!

Can you elaborate a bit more regarding this 10 Hz clock input? If I understand you correctly, one of the devices will have a ~10 Hz clock signal input from some external source, and you want to replicate this 10 Hz signal on other devices in your system (but the other nRF chips will generate the 10 Hz, instead of receiving it).

Or is the ~10 Hz clock signal something that needs to be counted, and the counter value is what needs to be transmitted to other devices in the system?

ShaharHD commented 3 years ago

Hi, I was successfully able to incorporate this example code into a project which requires time sync between Nordic devices. The issue I'm having is that in my implementation the whole sync mechanism to be dependent upon an external clock input (PWM, roughly 10Hz) What would be the best approach to implement this?

Hello, glad to hear you're interested in the example!

It's an awesome example! Goes deep into the insides of the SoftDevice radio states which makes things even more interesting...

Can you elaborate a bit more regarding this 10 Hz clock input? If I understand you correctly, one of the devices will have a ~10 Hz clock signal input from some external source, and you want to replicate this 10 Hz signal on other devices in your system (but the other nRF chips will generate the 10 Hz, instead of receiving it).

Close. The other nRF chips will sync the external 10Hz clock generator (an external MCU with TCXO and very low ppm variations) to be in sync with the transmitter device (so the devices will stay in sync for as long as possible)

I'm able to have it sync pretty easily now on the sync receivers. When I started to work on this project in December, before the PR merge, it wasn't clock phase accurate (so I started investigating this myself) but now with the latests PR and the BLE_TS_EVT_TRIGGERED and BLE_TS_EVT_SYNCHRONIZED events - works even better :)

Once it'll work good, I'll add this as a PR with #define if you think it will make the library more usable to other people as well.

The problem I'm having is that I can't seem to find an easy way to make sure the sync timer is synced with that external clock (only needs to happen once by way, and can be done before TX starts).

I tried calling timestamp_counter_start() based on the external clock (as an interrupt, once and then disable) - but I'm getting inconsistent results (as the radio has it's own timer0 at 1Mhz)

nordic-auko commented 3 years ago

Can you elaborate a bit more regarding this 10 Hz clock input? If I understand you correctly, one of the devices will have a ~10 Hz clock signal input from some external source, and you want to replicate this 10 Hz signal on other devices in your system (but the other nRF chips will generate the 10 Hz, instead of receiving it).

Close. The other nRF chips will sync the external 10Hz clock generator (an external MCU with TCXO and very low ppm variations) to be in sync with the transmitter device (so the devices will stay in sync for as long as possible)

Thanks for the details!

I'm able to have it sync pretty easily now on the sync receivers. When I started to work on this project in December, before the PR merge, it wasn't clock phase accurate (so I started investigating this myself) but now with the latests PR and the BLE_TS_EVT_TRIGGERED and BLE_TS_EVT_SYNCHRONIZED events - works even better :)

Good to hear, got a really good contribution from another github user for that set of changes :)

The problem I'm having is that I can't seem to find an easy way to make sure the sync timer is synced with that external clock (only needs to happen once by way, and can be done before TX starts).

I tried calling timestamp_counter_start() based on the external clock (as an interrupt, once and then disable) - but I'm getting inconsistent results (as the radio has it's own timer0 at 1Mhz)

If it only needs to happen once, I think the easiest approach would be to use GPIOTE and PPI to start the sync timer (edit: on the transmitter side). That is, replace this line with a PPI trigger: https://github.com/nordic-auko/nRF5-ble-timesync-demo/blob/master/nRF5_SDK_16.0.0_98a08e2/examples/common/time_sync.c#L652 "Timestamp 0" should then be aligned with a 10 Hz edge. But over time there will some drift of course.

To monitor the drift over time perhaps you could have another TIMER instance aligned with the 10 Hz signal (e.g. clearing the TIMER upon 10 Hz signal edge via GPIOTE PPI), and then use the ts_set_trigger() function to schedule a capture of this other TIME instance. You could then calculate the difference between the expected 10 Hz edge (relative to time 0) and the actual edge (well, more updated knowledge via this other TIMER that syncs more often with the 10 Hz edge).

ShaharHD commented 3 years ago

The problem I'm having is that I can't seem to find an easy way to make sure the sync timer is synced with that external clock (only needs to happen once by way, and can be done before TX starts). I tried calling timestamp_counter_start() based on the external clock (as an interrupt, once and then disable) - but I'm getting inconsistent results (as the radio has it's own timer0 at 1Mhz)

If it only needs to happen once, I think the easiest approach would be to use GPIOTE and PPI to start the sync timer (edit: on the transmitter side). That is, replace this line with a PPI trigger: https://github.com/nordic-auko/nRF5-ble-timesync-demo/blob/master/nRF5_SDK_16.0.0_98a08e2/examples/common/time_sync.c#L652 "Timestamp 0" should then be aligned with a 10 Hz edge. But over time there will some drift of course.

I tried something similar by calling sync_timer_start or simply resetting the timer counters (on the 1st interrupt I'm getting from the external GPIO), but the result I got was inconsistent - so I didn't invest the time in implementing it as a PPI.

I'll implement this as a PPI and will report back.

To monitor the drift over time perhaps you could have another TIMER instance aligned with the 10 Hz signal (e.g. clearing the TIMER upon 10 Hz signal edge via GPIOTE PPI), and then use the ts_set_trigger() function to schedule a capture of this other TIME instance. You could then calculate the difference between the expected 10 Hz edge (relative to time 0) and the actual edge (well, more updated knowledge via this other TIMER that syncs more often with the 10 Hz edge).

I have no issue with the drift as the actual PWM generated by the time sync is being used for debug right now (as after sync I'll eventually reset the nordic - in order to "forget" all the time sync timers and PPI)

The 10Hz is the "constant" (very precise MCU with 32Mhz TCXO) and is used as an interrupt for the Nordic to trigger the logic inside the Nordic.

ShaharHD commented 3 years ago

Hi,

Before investing into complex PPI, I tried the following:

Added a function named ts_reset_timers(void) which has the following

    m_params.high_freq_timer[0]->TASKS_STOP  = 1;
    m_params.high_freq_timer[0]->TASKS_CLEAR = 1;
    m_params.high_freq_timer[0]->CC[0]       = TIME_SYNC_TIMER_MAX_VAL;
    m_params.high_freq_timer[0]->CC[1]       = 0xFFFFFFFF;
    m_params.high_freq_timer[0]->CC[2]       = 0xFFFFFFFF;
    m_params.high_freq_timer[0]->CC[3]       = 0xFFFFFFFF;
    m_params.high_freq_timer[0]->CC[4]       = TIME_SYNC_TIMER_MAX_VAL / 2;

    m_params.high_freq_timer[0]->TASKS_START = 1;

I'm calling the function on the falling edge of the interrupt of the 10Hz (and disabling it afterwards), and then calling ts_tx_start(TIME_SYNC_FREQ_AUTO);

The problem, as before, is that I see the two RX units synced within 3.5us ... but the Transmitting unit 10Hz sync is fluctuating. seems totally random (between 10ms and 30ms)

I'm using the TS_EVT_TRIGGERED as my signal to sync the 10Hz (on the receiver) - and it seems to work just fine as the RX units are synced every time.

Any idea to why the fluctuations?

Edit: further more, I've used the BLE_TS_EVT_TRIGGERED to toggle a simple GPIO every 40 ticks (tick_target = evt->params.triggered.tick_target + TIME_SYNC_MSEC_TO_TICK(100);)

I'm looking at the 1st toggle I'll see if I set gpio_trigger_enable like in the example - but every time I run the code, I get a different random difference from the 10Hz which I used as the source.

Tried various values and multiple times (inside ts_gpio_trigger_enable) to see if there consistency... but no luck.

Edit: Here's a logic capture to maybe better explain (on the transmitter end) image

10Hz - that's the input 10Hz Reset Timer - that's a simple GPIO I'm putting in HIGH when I'm calling ts_reset_timers and putting in LOW when exiting. It takes around 53us to perform the timer setup.

But I can't seem to have a constant alignment of 10Hz [D1] and Channel 3 [D3] which is the BLE_TS_EVT_TRIGGERED event.

I believe that once I managed to get it sync'ed up on the transmitter - the receiver end will by in sync...

nordic-auko commented 3 years ago

Hi,

Before investing into complex PPI, I tried the following:

Added a function named ts_reset_timers(void) which has the following

    m_params.high_freq_timer[0]->TASKS_STOP  = 1;
    m_params.high_freq_timer[0]->TASKS_CLEAR = 1;
    m_params.high_freq_timer[0]->CC[0]       = TIME_SYNC_TIMER_MAX_VAL;
    m_params.high_freq_timer[0]->CC[1]       = 0xFFFFFFFF;
    m_params.high_freq_timer[0]->CC[2]       = 0xFFFFFFFF;
    m_params.high_freq_timer[0]->CC[3]       = 0xFFFFFFFF;
    m_params.high_freq_timer[0]->CC[4]       = TIME_SYNC_TIMER_MAX_VAL / 2;

    m_params.high_freq_timer[0]->TASKS_START = 1;

I'm calling the function on the falling edge of the interrupt of the 10Hz (and disabling it afterwards), and then calling ts_tx_start(TIME_SYNC_FREQ_AUTO);

The problem, as before, is that I see the two RX units synced within 3.5us ... but the Transmitting unit 10Hz sync is fluctuating. seems totally random (between 10ms and 30ms)

I'm using the TS_EVT_TRIGGERED as my signal to sync the 10Hz (on the receiver) - and it seems to work just fine as the RX units are synced every time.

Any idea to why the fluctuations?

Edit: further more, I've used the BLE_TS_EVT_TRIGGERED to toggle a simple GPIO every 40 ticks (tick_target = evt->params.triggered.tick_target + TIME_SYNC_MSEC_TO_TICK(100);)

I'm looking at the 1st toggle I'll see if I set gpio_trigger_enable like in the example - but every time I run the code, I get a different random difference from the 10Hz which I used as the source.

Tried various values and multiple times (inside ts_gpio_trigger_enable) to see if there consistency... but no luck.

Edit: Here's a logic capture to maybe better explain (on the transmitter end) image

10Hz - that's the input 10Hz Reset Timer - that's a simple GPIO I'm putting in HIGH when I'm calling ts_reset_timers and putting in LOW when exiting. It takes around 53us to perform the timer setup.

But I can't seem to have a constant alignment of 10Hz [D1] and Channel 3 [D3] which is the BLE_TS_EVT_TRIGGERED event.

I believe that once I managed to get it sync'ed up on the transmitter - the receiver end will by in sync...

Thanks for the details and logic analyzer capture!

It's not clear to me yet where the fluctuation you see comes from. Using the CPU to reset the timer (ts_reset_timers()) does certainly add some jitter compared to using the PPI, but as you write this seems to be in to microsecond range, and not tens of milliseconds.

In ts_reset_timers() I would also recommend resetting m_params.high_freq_timer[1], by adding m_params.high_freq_timer[1]->TASKS_CLEAR = 1; while m_params.high_freq_timer[1] is stopped. I don't think this is the culprit, but it could make it easier to have time 0 as the first 10 Hz edge, instead of time 0 + 2.5 ms * x.

The offset fluctuation you see between the 10 Hz edges, does it align with a 2.5 ms timer resolution, or does it seem completely random?

nordic-auko commented 2 years ago

Closing after inactivity