HF-TMC51x0 Driver (TMC5130 & TMC5160) 0.1.0-dev
Hardware Agnostic C++ Driver for the TMC51x0 (TMC5130 & TMC5160)
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"
13
14namespace tmc51x0 {
15
136template <typename CommType, size_t MaxDevices = 8> class TMC51x0MultiNode {
137public:
151 explicit TMC51x0MultiNode(CommType &comm, uint8_t num_onboard_devices,
152 uint32_t f_clk = ClockFreq::DEFAULT_F_CLK) noexcept
153 : comm_(comm), num_onboard_devices_(num_onboard_devices),
154 num_active_devices_(num_onboard_devices), f_clk_(f_clk) {
155 // Validate num_onboard_devices
156 if (num_onboard_devices == 0 || num_onboard_devices > MaxDevices) {
159 }
160
161 // Create onboard TMC5160 instances, one per device
162 // Initial node address is 0 (will be programmed via ProgramSequentially())
163 // The actual programmed addresses will be 254, 253, 252, ... per datasheet
164 for (uint8_t i = 0; i < num_onboard_devices_; ++i) {
165 drivers_[i] = std::make_optional<TMC51x0<CommType>>(comm_, f_clk_, 0, 0);
166 }
167
168 // Initialize extra device slots as empty
169 for (size_t i = num_onboard_devices_; i < MaxDevices; ++i) {
170 drivers_[i] = std::nullopt;
171 }
172 }
173
178 [[nodiscard]] uint8_t GetNumOnboardDevices() const noexcept {
180 }
181
186 [[nodiscard]] uint8_t GetNumActiveDevices() const noexcept {
187 return num_active_devices_;
188 }
189
194 [[nodiscard]] constexpr size_t GetMaxCapacity() const noexcept {
195 return MaxDevices;
196 }
197
203 [[nodiscard]] bool IsDeviceActive(uint8_t index) const noexcept {
204 if (index >= MaxDevices) {
206 }
207 return drivers_[index].has_value();
208 }
209
243 Result<void> ProgramSequentially(uint8_t send_delay = 2) noexcept {
244 // Count active devices to determine starting address
245 uint8_t num_devices = 0;
246 for (size_t i = 0; i < MaxDevices; ++i) {
247 if (drivers_[i].has_value()) {
248 num_devices++;
249 }
250 }
251
252 if (num_devices == 0) {
253 return Result<void>(ErrorCode::INVALID_STATE); // No devices to program
254 }
255
256 // Program devices in forward order (index 0, 1, 2, ...)
257 // but assign addresses in reverse order (254, 253, 252, ...)
258 // First chip: accessible at address 0 (NAI=GND)
259 // After each programming, next chip becomes accessible at address 0
260
261 uint8_t device_index = 0;
262 for (size_t i = 0; i < MaxDevices; ++i) {
263 if (!drivers_[i].has_value()) {
264 continue; // Skip empty slots
265 }
266
267 // Calculate target address: start from 254 and count down
268 // Device at index 0 gets address 254, index 1 gets 253, etc.
269 uint8_t target_address = 254 - device_index;
270
271 // Program device to target address
272 // The device is currently accessible at address 0 (after previous chip's
273 // NAO went LOW) or address 0 for the first chip (NAI=GND)
274 if (!drivers_[i]->uartConfig.ConfigureUartNodeAddress(target_address,
275 send_delay)) {
276 return Result<void>(
277 ErrorCode::INVALID_STATE); // Failed to program device
278 }
279
280 // Update the driver's node address to the programmed address
281 drivers_[i]->SetUartNodeAddress(target_address);
282
283 // After programming, the chip's NAO automatically goes LOW (per
284 // datasheet) This makes the next chip accessible at address 0 Small delay
285 // to ensure NAO settles
286 comm_.DelayMs(10);
287
288 device_index++;
289 }
290
291 return Result<void>();
292 }
293
306 Result<void> ProgramDevice(uint8_t index, uint8_t send_delay = 2) noexcept {
307 if (index >= MaxDevices || !drivers_[index].has_value()) {
309 }
310
311 // Calculate target address: 254 - index (per datasheet)
312 uint8_t target_address = 254 - index;
313
314 if (!drivers_[index]->uartConfig.ConfigureUartNodeAddress(target_address,
315 send_delay)) {
317 }
318
319 drivers_[index]->SetUartNodeAddress(target_address);
320 return Result<void>();
321 }
322
339 Result<void> AddDevice(uint8_t index) noexcept {
340 // Validate index
341 if (index < num_onboard_devices_ || index >= MaxDevices) {
343 }
344
345 // Check if slot is already occupied
346 if (drivers_[index].has_value()) {
348 }
349
350 // Enforce sequential indexing: cannot skip indices
351 // All indices before this one (starting from num_onboard_devices) must be
352 // filled
353 for (uint8_t i = num_onboard_devices_; i < index; ++i) {
354 if (!drivers_[i].has_value()) {
355 return Result<void>(ErrorCode::INVALID_STATE); // Cannot skip indices
356 }
357 }
358
359 // Create device instance with initial address 0
360 // The actual address will be programmed via ProgramSequentially() or
361 // ProgramDevice()
362 drivers_[index] =
363 std::make_optional<TMC51x0<CommType>>(comm_, f_clk_, 0, 0);
365
366 return Result<void>();
367 }
368
384 Result<void> RemoveDevice(uint8_t index) noexcept {
385 // Cannot remove onboard devices
386 if (index < num_onboard_devices_) {
388 }
389
390 // Validate index
391 if (index >= MaxDevices) {
393 }
394
395 // Check if slot is actually active
396 if (!drivers_[index].has_value()) {
398 }
399
400 // Enforce sequential removal: can only remove from the end
401 // Check if there are any devices after this index
402 for (size_t i = index + 1; i < MaxDevices; ++i) {
403 if (drivers_[i].has_value()) {
404 return Result<void>(
405 ErrorCode::INVALID_STATE); // Cannot remove device in the middle of
406 // the chain
407 }
408 }
409
410 // Remove device
411 drivers_[index] = std::nullopt;
413
414 return Result<void>();
415 }
416
428 [[nodiscard]] TMC51x0<CommType> &operator[](uint8_t index) noexcept {
429 return drivers_[index].value();
430 }
431
437 [[nodiscard]] const TMC51x0<CommType> &
438 operator[](uint8_t index) const noexcept {
439 return drivers_[index].value();
440 }
441
448 InitializeAll(const DriverConfig &config = DriverConfig()) noexcept {
449 bool all_success = true;
450 for (size_t i = 0; i < MaxDevices; ++i) {
451 if (drivers_[i].has_value()) {
452 if (!drivers_[i]->Initialize(config)) {
453 all_success = false;
454 }
455 }
456 }
457 return all_success;
458 }
459
460private:
461 CommType &comm_;
463 uint8_t
465 uint32_t f_clk_;
466 std::array<std::optional<TMC51x0<CommType>>, MaxDevices>
468};
469
470} // namespace tmc51x0
Result type for operations that return a value.
Definition tmc51x0_result.hpp:90
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:178
Result< void > ProgramSequentially(uint8_t send_delay=2) noexcept
Program all active devices sequentially using NAI/NAO addressing.
Definition tmc51x0_multi_node.hpp:243
Result< void > InitializeAll(const DriverConfig &config=DriverConfig()) noexcept
Initialize all active devices with the same configuration.
Definition tmc51x0_multi_node.hpp:448
const TMC51x0< CommType > & operator[](uint8_t index) const noexcept
Const access to individual TMC5160 driver by logical index.
Definition tmc51x0_multi_node.hpp:438
constexpr size_t GetMaxCapacity() const noexcept
Get the maximum capacity (onboard + extra devices)
Definition tmc51x0_multi_node.hpp:194
Result< void > AddDevice(uint8_t index) noexcept
Add an extra device at the specified logical index.
Definition tmc51x0_multi_node.hpp:339
uint8_t num_active_devices_
Total number of active devices (onboard + extra)
Definition tmc51x0_multi_node.hpp:464
CommType & comm_
Shared UART communication interface.
Definition tmc51x0_multi_node.hpp:461
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:151
std::array< std::optional< TMC51x0< CommType > >, MaxDevices > drivers_
TMC5160 driver instances (optional for dynamic devices)
Definition tmc51x0_multi_node.hpp:467
bool IsDeviceActive(uint8_t index) const noexcept
Check if a device slot is active (has a device instance)
Definition tmc51x0_multi_node.hpp:203
Result< void > RemoveDevice(uint8_t index) noexcept
Remove an extra device at the specified logical index.
Definition tmc51x0_multi_node.hpp:384
uint32_t f_clk_
TMC5160 clock frequency.
Definition tmc51x0_multi_node.hpp:465
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:306
TMC51x0< CommType > & operator[](uint8_t index) noexcept
Access individual TMC5160 driver by logical index.
Definition tmc51x0_multi_node.hpp:428
uint8_t num_onboard_devices_
Number of onboard devices (fixed)
Definition tmc51x0_multi_node.hpp:462
uint8_t GetNumActiveDevices() const noexcept
Get the total number of active devices (onboard + extra)
Definition tmc51x0_multi_node.hpp:186
Main class representing a TMC51x0 stepper motor driver (TMC5130 & TMC5160)
Definition tmc51x0.hpp:119
constexpr uint32_t DEFAULT_F_CLK
Typical internal clock frequency in Hz (12 MHz)
Definition tmc51x0_types.hpp:16
Definition tmc51x0_register_defs.cpp:10
@ INVALID_STATE
Operation not valid in current state.
Driver initialization configuration structure.
Definition tmc51x0_types.hpp:2870
Main TMC51x0 stepper motor driver interface and subsystem classes.
Communication interfaces for TMC51x0 stepper motor driver using SPI and UART.