Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions docs/communication_sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
## Introduction

Synchronization (Sync) provides precise alignment of PWM signals and real-time control tasks across multiple SPIN boards.

This is particularly useful for advanced applications that require time-deterministic behavior, such as decentralized control or high-resolution PWM coordination beyond the capability of a single board.

!!! success "Synchronized PWM carriers"
- Using this synchronization features permits to have PWM carriers synched down to **25ns ± 5ns** (results obtained with 30cm CAT5 S-FTP RJ-45 cables).
- Jitter on synched control tasks is on the order of a micro second.

## How it works

**Board A** is defined as the sync Master, it effectively defines the time reference.
The PWM generator of the Master triggers its real time control task based on an internal repetition timer.

When entering the real time control task, the special sync output pin (pin number 42) is set in alternate mode.

This allows to send the next repetition event to **Board B**. Alternate mode is maintained for few microseconds in order to wait for the next incoming PWM event.

The sync pin is then returned to normal mode, effectively halting event propagation until the next real-time control task deadline.

![Working principle of the synchronization](images/communication_sync.svg)

!!! warning "A small delay is to be expected between **Board A** and **Board B** control tasks"
Current sync behavior does introduce a delay, because of the busy wait needed to control event propagation.
However PWM carrier are perfectly synched.


## How to use.

All there is to do is initialize both the Master and the Slave(s).

=== "Initialize the master board"

```
void setup_routine()
{
/* Initialize the PWM engine first */
shield.power.initBuck(ALL);
/* Set up synchronization mode as Master */
communication.sync.initMaster();

/* Declare tasks */
uint32_t background_task_number =
task.createBackground(loop_background_task);

uint32_t app_task_number = task.createBackground(application_task);

/* Make sure the real time task is based on the PWM engine */
task.createCritical(loop_critical_task, control_task_period, source_hrtim);

/* Finally, start tasks */
task.startBackground(background_task_number);
task.startBackground(app_task_number);
task.startCritical();
}
```

=== "Initialize the slave(s) board(s)"
```
void setup_routine()
{
/* Initialize the PWM engine first */
shield.power.initBuck(ALL);
/* Set up synchronization mode as Slave */
communication.sync.initSlave();

/* Declare tasks */
uint32_t background_task_number =
task.createBackground(loop_background_task);

uint32_t app_task_number = task.createBackground(application_task);

/* Make sure the real time task is based on the PWM engine */
task.createCritical(loop_critical_task, control_task_period, source_hrtim);

/* Finally, start tasks */
task.startBackground(background_task_number);
task.startBackground(app_task_number);
task.startCritical();
}
```


## Current Limitations

This synchronization feature is optimized for systems where the real-time control task frequency is significantly lower than the PWM frequency—typically by a factor of 10 or more.

However, in scenarios where the PWM frequency approaches or matches the control task frequency, the current implementation becomes ineffective. This is because:

- The Master board relies on a busy-wait mechanism to control the propagation of the synchronization signal.

- When the timing between PWM events and control tasks becomes too close, this busy-wait duration would need to be extended substantially.

- Such long blocking periods would interfere with real-time responsiveness and potentially degrade system performance.

As a result, the current design does not support synchronization in setups where the control and PWM frequencies are too close.

## API Reference
::: doxy.powerAPI.class
name: SyncCommunication
20 changes: 20 additions & 0 deletions docs/environment_setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ Before we start, make sure your machine meets all the requirements below.
- Write permission for the serial port (`/dev/ttyACM0`): See PlatformIO documentation which provides a [udev rules file](https://docs.platformio.org/en/latest/core/installation/udev-rules.html)
- **Internet connection**

=== "RaspberryPi"

- **Git:** If you do not have git installed, get it here [git for Linux](https://git-scm.com/download/linux)
- **Python3:** If you do not have python3 installed, get it here [Python3 Installers](https://docs.python-guide.org/starting/install3/linux/)
- The [pip](https://pip.pypa.io/en/stable/) package installer is needed. If using the system Python (`/usr/bin/python3`), `pip` may not be installed by default.
See [Installing pip with Linux Package Managers](https://packaging.python.org/en/latest/guides/installing-using-linux-tools/).
- The [venv](https://docs.python.org/3/library/venv.html) module is needed.
Warning if using the system Python: although `venv` is part of the Python Standard Library, some Linux distributions such as Debian and Ubuntu don't install it by default.
In that case, make sure that the `python3-venv` package is installed.
- **CMake:** If you do not have CMake installed, get it here [CMake Installer](https://cmake.org/download/)
- 64 bit Linux distribution
- Write permission for the serial port (`/dev/ttyACM0`): See PlatformIO documentation which provides a [udev rules file](https://docs.platformio.org/en/latest/core/installation/udev-rules.html)
- **Internet connection**
- From v5 onward you will need to install the following libraries
```
sudo dpkg --add-architecture armhf
sudo apt update
sudo apt install libc6:armhf libstdc++6:armhf
```


## Setup your work environment

Expand Down
4 changes: 4 additions & 0 deletions docs/images/communication_sync.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions zephyr/modules/owntech_hrtim_driver/zephyr/public_api/hrtim_enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ extern "C"
0xFFFD,
0xFFFD,
0xFFFD};

static const uint32_t SWAP_WINDOW[8] = {0x0020,
0x0010,
0x0008,
0x0004,
0x0002,
0x0002,
0x0002,
0x0002};


/**
Expand Down Expand Up @@ -439,6 +448,7 @@ extern "C"
float32_t duty_min_user_float; /* Minimum duty cycle set by the user in float */
float32_t duty_max_user_float; /* Maximum duty cycle set by the user in float */
uint8_t duty_swap; /* Detects if the duty has been swapped */
uint16_t swap_window; /* Defines the swap window based on the pre-scaler */
hrtim_pwm_mode_t pwm_mode; /* voltage mode or current mode */
hrtim_external_trigger_t external_trigger; /* event for current mode */
hrtim_burst_clk_t burst_clk; /* clock source for burst mode generator*/
Expand Down
4 changes: 4 additions & 0 deletions zephyr/modules/owntech_hrtim_driver/zephyr/src/hrtim.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,15 @@ static inline uint32_t _period_ckpsc(uint32_t freq, timer_hrtim_t *tu)
* dead-time generator which have a 868ps resolution... */

tu->pwm_conf.period = (uint16_t)period;
tu->pwm_conf.swap_window = SWAP_WINDOW[tu->pwm_conf.ckpsc];
tu->pwm_conf.max_period = period - SWAP_WINDOW[tu->pwm_conf.ckpsc];
tu->pwm_conf.min_period = min_period;

/* Stores the maximum and minimum duty cycle for the timing unit */
tu->pwm_conf.duty_max = HRTIM_MAX_PER_and_CMP_REG_VALUES[tu->pwm_conf.ckpsc];
tu->pwm_conf.duty_min = HRTIM_MIN_PER_and_CMP_REG_VALUES[tu->pwm_conf.ckpsc];


/* Stores the maximum and minimum duty cycle for the user */
tu->pwm_conf.duty_max_user = tu->pwm_conf.period * 0.9;
tu->pwm_conf.duty_min_user = tu->pwm_conf.period * 0.1;
Expand Down
5 changes: 3 additions & 2 deletions zephyr/modules/owntech_shield_api/zephyr/src/Power.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include "Power.h"
#include "SpinAPI.h"


hrtim_tu_number_t PowerAPI::spinNumberToTu(uint16_t spin_number)
{
if(spin_number == 12 || spin_number == 14)
Expand Down Expand Up @@ -218,6 +217,7 @@ void PowerAPI::setDutyCycleRaw(leg_t leg, uint16_t duty_value)
{
uint16_t period;
uint8_t swap_state;
uint16_t max_period;
hrtim_tu_number_t leg_tu;
uint16_t duty_cycle_max_raw;
uint16_t duty_cycle_min_raw;
Expand Down Expand Up @@ -257,9 +257,10 @@ void PowerAPI::setDutyCycleRaw(leg_t leg, uint16_t duty_value)

period = tu_channel[leg_tu]->pwm_conf.period;
swap_state = tu_channel[leg_tu]->pwm_conf.duty_swap;
max_period = tu_channel[leg_tu]->pwm_conf.max_period;

/* Implements a logic that allows for a duty cycle of 100% */
if (duty_value >= period-3){
if (duty_value >= max_period){
duty_value = 0;
hrtim_duty_cycle_set(leg_tu, duty_value);

Expand Down
3 changes: 2 additions & 1 deletion zephyr/modules/owntech_spin_api/zephyr/src/PwmHAL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,11 @@ void PwmHAL::setDutyCycleRaw(hrtim_tu_number_t pwmX, uint16_t duty_cycle)
}

uint16_t period = tu->pwm_conf.period; /* Get PWM period */
uint16_t max_period = tu->pwm_conf.max_period; /* Get PWM period */
bool swap_state = tu->pwm_conf.duty_swap; /* Get output swap state */

/* True if near 100% duty */
bool over_limit = (duty_cycle >= period - 3);
bool over_limit = (duty_cycle >= max_period);

/* Force 0% to avoid glitches near 100% */
duty_cycle = over_limit ? 0 : duty_cycle;
Expand Down