TMC5160 Driver
API reference
Loading...
Searching...
No Matches
tmc51x0_multi_node.hpp
Go to the documentation of this file.
1
6#pragma once
7#include <array>
8#include <cstdint>
9#include <optional>
10
11#include "../tmc51x0.hpp"
12#include "../tmc51x0_comm_interface.hpp"
13
14namespace tmc51x0 {
15
136template <typename CommType, size_t MaxDevices = 8> class TMC51x0MultiNode {
137public:
152 explicit TMC51x0MultiNode(CommType &comm, uint8_t num_onboard_devices,
153 uint32_t f_clk = ClockFreq::DEFAULT_F_CLK) noexcept
154 : comm_(comm), num_onboard_devices_(num_onboard_devices),
155 num_active_devices_(num_onboard_devices) {
156 (void)f_clk;
157 // Validate num_onboard_devices
158 if (num_onboard_devices == 0 || num_onboard_devices > MaxDevices) {
161 }
162
163 // Create onboard TMC5160 instances, one per device
164 // Initial node address is 0 (will be programmed via ProgramSequentially())
165 // The actual programmed addresses will be 254, 253, 252, ... per datasheet
166 for (uint8_t i = 0; i < num_onboard_devices_; ++i) {
167 drivers_[i].emplace(comm_, 0, 0); // position 0, node address 0 until programmed
168 }
169
170 // Initialize extra device slots as empty
171 for (size_t i = num_onboard_devices_; i < MaxDevices; ++i) {
172 drivers_[i].reset();
173 }
174 }
175
180 [[nodiscard]] uint8_t GetNumOnboardDevices() const noexcept {
182 }
183
188 [[nodiscard]] uint8_t GetNumActiveDevices() const noexcept {
189 return num_active_devices_;
190 }
191
196 [[nodiscard]] constexpr size_t GetMaxCapacity() const noexcept {
197 return MaxDevices;
198 }
199
205 [[nodiscard]] bool IsDeviceActive(uint8_t index) const noexcept {
206 if (index >= MaxDevices) {
207 return false;
208 }
209 return drivers_[index].has_value();
210 }
211
245 Result<void> ProgramSequentially(uint8_t send_delay = 2) noexcept {
246 // Count active devices to determine starting address
247 uint8_t num_devices = 0;
248 for (size_t i = 0; i < MaxDevices; ++i) {
249 if (drivers_[i].has_value()) {
250 num_devices++;
251 }
252 }
253
254 if (num_devices == 0) {
255 return Result<void>(ErrorCode::INVALID_STATE); // No devices to program
256 }
257
258 // Program devices in forward order (index 0, 1, 2, ...)
259 // but assign addresses in reverse order (254, 253, 252, ...)
260 // First chip: accessible at address 0 (NAI=GND)
261 // After each programming, next chip becomes accessible at address 0
262
263 uint8_t device_index = 0;
264 for (size_t i = 0; i < MaxDevices; ++i) {
265 if (!drivers_[i].has_value()) {
266 continue; // Skip empty slots
267 }
268
269 // Calculate target address: start from 254 and count down
270 // Device at index 0 gets address 254, index 1 gets 253, etc.
271 uint8_t target_address = 254 - device_index;
272
273 // Program device to target address
274 // The device is currently accessible at address 0 (after previous chip's
275 // NAO went LOW) or address 0 for the first chip (NAI=GND)
276 if (!drivers_[i]->uartConfig.ConfigureUartNodeAddress(target_address,
277 send_delay)) {
278 return Result<void>(
279 ErrorCode::INVALID_STATE); // Failed to program device
280 }
281
282 // ConfigureUartNodeAddress() already moved the driver to target_address.
283 // NAO does not change by itself: drive it low (OUTPUT bit 0 = 0) so the
284 // next chip's NAI goes low and that chip answers at address 0
285 // (TMC5160A datasheet section 5.4, figure 5.1; OUTPUT register 0x04).
286 if (!drivers_[i]->io.SetSdoCfg0Polarity(false)) {
288 }
289 comm_.DelayMs(1);
290
291 device_index++;
292 }
293
294 return Result<void>();
295 }
296
309 Result<void> ProgramDevice(uint8_t index, uint8_t send_delay = 2) noexcept {
310 if (index >= MaxDevices || !drivers_[index].has_value()) {
312 }
313
314 // Calculate target address: 254 - index (per datasheet)
315 uint8_t target_address = 254 - index;
316
317 if (!drivers_[index]->uartConfig.ConfigureUartNodeAddress(target_address,
318 send_delay)) {
320 }
321
322 // Drive NAO low so the next chip answers at address 0 (datasheet figure 5.1).
323 if (!drivers_[index]->io.SetSdoCfg0Polarity(false)) {
325 }
326 return Result<void>();
327 }
328
345 Result<void> AddDevice(uint8_t index) noexcept {
346 // Validate index
347 if (index < num_onboard_devices_ || index >= MaxDevices) {
349 }
350
351 // Check if slot is already occupied
352 if (drivers_[index].has_value()) {
354 }
355
356 // Enforce sequential indexing: cannot skip indices
357 // All indices before this one (starting from num_onboard_devices) must be
358 // filled
359 for (uint8_t i = num_onboard_devices_; i < index; ++i) {
360 if (!drivers_[i].has_value()) {
361 return Result<void>(ErrorCode::INVALID_STATE); // Cannot skip indices
362 }
363 }
364
365 // Create device instance with initial address 0
366 // The actual address will be programmed via ProgramSequentially() or
367 // ProgramDevice()
368 drivers_[index].emplace(comm_, 0, 0);
370
371 return Result<void>();
372 }
373
389 Result<void> RemoveDevice(uint8_t index) noexcept {
390 // Cannot remove onboard devices
391 if (index < num_onboard_devices_) {
393 }
394
395 // Validate index
396 if (index >= MaxDevices) {
398 }
399
400 // Check if slot is actually active
401 if (!drivers_[index].has_value()) {
403 }
404
405 // Enforce sequential removal: can only remove from the end
406 // Check if there are any devices after this index
407 for (size_t i = index + 1; i < MaxDevices; ++i) {
408 if (drivers_[i].has_value()) {
409 return Result<void>(
410 ErrorCode::INVALID_STATE); // Cannot remove device in the middle of
411 // the chain
412 }
413 }
414
415 // Remove device
416 drivers_[index].reset();
418
419 return Result<void>();
420 }
421
433 [[nodiscard]] TMC51x0<CommType> &operator[](uint8_t index) noexcept {
434 return drivers_[index].value();
435 }
436
442 [[nodiscard]] const TMC51x0<CommType> &
443 operator[](uint8_t index) const noexcept {
444 return drivers_[index].value();
445 }
446
453 InitializeAll(const DriverConfig &config = DriverConfig()) noexcept {
454 Result<void> first_error{};
455 for (size_t i = 0; i < MaxDevices; ++i) {
456 if (drivers_[i].has_value()) {
457 auto result = drivers_[i]->Initialize(config);
458 if (!result && first_error) {
459 first_error = result; // keep initialising the others; report the first error
460 }
461 }
462 }
463 return first_error;
464 }
465
466private:
467 CommType &comm_;
469 uint8_t
471 std::array<std::optional<TMC51x0<CommType>>, MaxDevices>
473};
474
475} // namespace tmc51x0
Result type for operations that return a value.
Definition tmc51x0_result.hpp:90
Main class representing a TMC51x0 stepper motor driver (TMC5130 & TMC5160)
Definition tmc51x0.hpp:119
High-level manager for multiple TMC51x0 drivers in a UART multi-node configuration.
Definition tmc51x0_multi_node.hpp:136
uint8_t GetNumOnboardDevices() const noexcept
Get the number of onboard devices (fixed at construction)
Definition tmc51x0_multi_node.hpp:180
Result< void > ProgramSequentially(uint8_t send_delay=2) noexcept
Program all active devices sequentially using NAI/NAO addressing.
Definition tmc51x0_multi_node.hpp:245
Result< void > InitializeAll(const DriverConfig &config=DriverConfig()) noexcept
Initialize all active devices with the same configuration.
Definition tmc51x0_multi_node.hpp:453
const TMC51x0< CommType > & operator[](uint8_t index) const noexcept
Const access to individual TMC5160 driver by logical index.
Definition tmc51x0_multi_node.hpp:443
constexpr size_t GetMaxCapacity() const noexcept
Get the maximum capacity (onboard + extra devices)
Definition tmc51x0_multi_node.hpp:196
Result< void > AddDevice(uint8_t index) noexcept
Add an extra device at the specified logical index.
Definition tmc51x0_multi_node.hpp:345
uint8_t num_active_devices_
Total number of active devices (onboard + extra)
Definition tmc51x0_multi_node.hpp:470
CommType & comm_
Shared UART communication interface.
Definition tmc51x0_multi_node.hpp:467
TMC51x0MultiNode(CommType &comm, uint8_t num_onboard_devices, uint32_t f_clk=ClockFreq::DEFAULT_F_CLK) noexcept
Construct a multi-node manager.
Definition tmc51x0_multi_node.hpp:152
std::array< std::optional< TMC51x0< CommType > >, MaxDevices > drivers_
TMC5160 driver instances (optional for dynamic devices)
Definition tmc51x0_multi_node.hpp:472
bool IsDeviceActive(uint8_t index) const noexcept
Check if a device slot is active (has a device instance)
Definition tmc51x0_multi_node.hpp:205
Result< void > RemoveDevice(uint8_t index) noexcept
Remove an extra device at the specified logical index.
Definition tmc51x0_multi_node.hpp:389
Result< void > ProgramDevice(uint8_t index, uint8_t send_delay=2) noexcept
Program a single device at the specified logical index.
Definition tmc51x0_multi_node.hpp:309
TMC51x0< CommType > & operator[](uint8_t index) noexcept
Access individual TMC5160 driver by logical index.
Definition tmc51x0_multi_node.hpp:433
uint8_t num_onboard_devices_
Number of onboard devices (fixed)
Definition tmc51x0_multi_node.hpp:468
uint8_t GetNumActiveDevices() const noexcept
Get the total number of active devices (onboard + extra)
Definition tmc51x0_multi_node.hpp:188
constexpr uint32_t DEFAULT_F_CLK
Typical internal clock frequency in Hz (12 MHz)
Definition tmc51x0_types.hpp:16
Definition tmc51x0_config_builder.hpp:75
@ INVALID_STATE
Operation not valid in current state.
@ COMM_ERROR
Communication interface error (SPI/UART)
Driver initialization configuration structure.
Definition tmc51x0_types.hpp:2870