drivers: stepper: adi_tmc: bus: abstraction layer for bus
Now a bus abstraction for Trinamic stepper drivers with unified interface for SPI/UART communication. Separates transport logic from device-specific functionality. Signed-off-by: Dipak Shetty <shetty.dipak@gmx.com>
This commit is contained in:
parent
bceeb766fc
commit
6cc860d892
7
drivers/stepper/adi_tmc/bus/CMakeLists.txt
Normal file
7
drivers/stepper/adi_tmc/bus/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library_include_directories(include)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_SPI adi_tmc_spi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_STEPPER_ADI_TMC_UART adi_tmc_uart.c)
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include "adi_tmc_spi.h"
|
||||
#include <adi_tmc_spi.h>
|
||||
|
||||
#define BUFFER_SIZE 5U
|
||||
|
||||
163
drivers/stepper/adi_tmc/bus/adi_tmc_uart.c
Normal file
163
drivers/stepper/adi_tmc/bus/adi_tmc_uart.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <adi_tmc_uart.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(tmc_uart, CONFIG_STEPPER_LOG_LEVEL);
|
||||
|
||||
/* TMC UART standard datagram size */
|
||||
#define ADI_TMC_UART_DATAGRAM_SIZE 8
|
||||
/* TMC UART read request datagram size */
|
||||
#define ADI_TMC_UART_READ_REQ_DATAGRAM_SIZE 4
|
||||
/* TMC UART protocol constants */
|
||||
#define ADI_TMC_UART_SYNC_BYTE 0x05
|
||||
#define ADI_TMC_UART_WRITE_BIT 0x80
|
||||
|
||||
uint8_t tmc_uart_calc_crc(const uint8_t *datagram, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
uint8_t current_byte = datagram[i];
|
||||
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if ((crc >> 7) ^ (current_byte & 0x01)) {
|
||||
crc = (crc << 1) ^ 0x07;
|
||||
} else {
|
||||
crc = (crc << 1) & 0xFF;
|
||||
}
|
||||
current_byte = current_byte >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static int tmc_uart_send_byte_with_echo(const struct device *uart, uint8_t byte)
|
||||
{
|
||||
uint8_t echo_byte;
|
||||
int err;
|
||||
|
||||
uart_poll_out(uart, byte);
|
||||
|
||||
/* Wait for echo with timeout */
|
||||
k_timepoint_t end = sys_timepoint_calc(K_MSEC(5)); /* 5ms timeout for echo */
|
||||
|
||||
do {
|
||||
err = uart_poll_in(uart, &echo_byte);
|
||||
if (err >= 0 && echo_byte == byte) {
|
||||
/* Received matching echo */
|
||||
return 0;
|
||||
}
|
||||
} while (err == -1 && !sys_timepoint_expired(end));
|
||||
|
||||
LOG_ERR("Echo mismatch or timeout: sent 0x%02X", byte);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int tmc_uart_write_register(const struct device *uart, uint8_t device_addr,
|
||||
uint8_t register_address, uint32_t data)
|
||||
{
|
||||
uint8_t buffer[ADI_TMC_UART_DATAGRAM_SIZE];
|
||||
int err = 0;
|
||||
|
||||
/* Format the UART TMC datagram */
|
||||
buffer[0] = ADI_TMC_UART_SYNC_BYTE; /* Sync byte */
|
||||
buffer[1] = device_addr; /* Device address */
|
||||
buffer[2] = register_address | ADI_TMC_UART_WRITE_BIT; /* Register address with write bit */
|
||||
sys_put_be32(data, &buffer[3]); /* Write data */
|
||||
buffer[7] = tmc_uart_calc_crc(buffer, ADI_TMC_UART_DATAGRAM_SIZE - 1U); /* CRC */
|
||||
|
||||
/* Send datagram byte by byte using polling */
|
||||
for (size_t i = 0; i < ADI_TMC_UART_DATAGRAM_SIZE; i++) {
|
||||
err = tmc_uart_send_byte_with_echo(uart, buffer[i]);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to send byte %d: 0x%02X", i, buffer[i]);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tmc_uart_read_register(const struct device *uart, uint8_t device_addr, uint8_t register_address,
|
||||
uint32_t *data)
|
||||
{
|
||||
uint8_t write_buffer[ADI_TMC_UART_READ_REQ_DATAGRAM_SIZE];
|
||||
uint8_t read_buffer[ADI_TMC_UART_DATAGRAM_SIZE];
|
||||
struct uart_config uart_cfg;
|
||||
|
||||
int err = 0;
|
||||
|
||||
/* Get current UART configuration */
|
||||
err = uart_config_get(uart, &uart_cfg);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to get UART configuration: %d", err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Calculate delay based on UART baudrate (SENDDELAY: default=8 bit times))*/
|
||||
uint32_t delay_us = (8 * 1000000) / uart_cfg.baudrate;
|
||||
|
||||
/* Format the UART TMC datagram for read */
|
||||
write_buffer[0] = ADI_TMC_UART_SYNC_BYTE; /* Sync byte */
|
||||
write_buffer[1] = device_addr; /* Device address */
|
||||
write_buffer[2] = register_address; /* Register address */
|
||||
write_buffer[3] = tmc_uart_calc_crc(write_buffer, 3);
|
||||
|
||||
/* Send read request byte by byte and wait for each echo */
|
||||
for (size_t i = 0; i < ADI_TMC_UART_READ_REQ_DATAGRAM_SIZE; i++) {
|
||||
err = tmc_uart_send_byte_with_echo(uart, write_buffer[i]);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to send byte %d: 0x%02X", i, write_buffer[i]);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Small delay to allow device to prepare response */
|
||||
k_busy_wait(delay_us);
|
||||
|
||||
/* Receive response with timeout */
|
||||
for (size_t i = 0; i < sizeof(read_buffer) && (err == 0); i++) {
|
||||
k_timepoint_t end = sys_timepoint_calc(K_MSEC(1000));
|
||||
|
||||
do {
|
||||
err = uart_poll_in(uart, &read_buffer[i]);
|
||||
} while (err == -1 && !sys_timepoint_expired(end));
|
||||
|
||||
if (err == -1) {
|
||||
LOG_ERR("Timeout waiting for byte %d for register 0x%x", i,
|
||||
register_address);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (err < 0) {
|
||||
LOG_ERR("Error %d receiving byte %d for register 0x%x", err, i,
|
||||
register_address);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the received bytes */
|
||||
LOG_HEXDUMP_DBG(read_buffer, ADI_TMC_UART_DATAGRAM_SIZE, "Received bytes:");
|
||||
|
||||
/* Validate CRC */
|
||||
uint8_t crc = tmc_uart_calc_crc(read_buffer, ADI_TMC_UART_DATAGRAM_SIZE - 1U);
|
||||
|
||||
if (crc != read_buffer[7]) {
|
||||
LOG_ERR("CRC mismatch for register 0x%x: got 0x%x, expected 0x%x", register_address,
|
||||
read_buffer[7], crc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Construct a 32-bit register value from received bytes */
|
||||
*data = sys_get_be32(&read_buffer[3]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
59
drivers/stepper/adi_tmc/bus/include/adi_tmc_bus.h
Normal file
59
drivers/stepper/adi_tmc/bus/include/adi_tmc_bus.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_H_
|
||||
#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_H_
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
|
||||
/* Communication interface types */
|
||||
#define TMC_COMM_SPI 0
|
||||
#define TMC_COMM_UART 1
|
||||
|
||||
union tmc_bus {
|
||||
struct spi_dt_spec spi;
|
||||
const struct device *uart;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Function pointer type for bus check function.
|
||||
* @param bus Pointer to the bus structure.
|
||||
* @param comm_type Communication type (SPI or UART).
|
||||
* @return 0 if bus is ready, negative error code otherwise.
|
||||
*/
|
||||
typedef int (*tmc_bus_check_fn)(const union tmc_bus *bus, uint8_t comm_type);
|
||||
|
||||
/**
|
||||
* @brief Function pointer type for register read function.
|
||||
* @param dev Pointer to the device structure.
|
||||
* @param reg Register address to read.
|
||||
* @param val Pointer to store the read value.
|
||||
* @return 0 on success, negative error code otherwise.
|
||||
*/
|
||||
typedef int (*tmc_reg_read_fn)(const struct device *dev, uint8_t reg, uint32_t *val);
|
||||
|
||||
/**
|
||||
* @brief Function pointer type for register write function.
|
||||
* @param dev Pointer to the device structure.
|
||||
* @param reg Register address to write.
|
||||
* @param val Value to write to the register.
|
||||
* @return 0 on success, negative error code otherwise.
|
||||
*/
|
||||
typedef int (*tmc_reg_write_fn)(const struct device *dev, uint8_t reg, uint32_t val);
|
||||
|
||||
/**
|
||||
* @brief Structure for bus I/O operations.
|
||||
* Contains function pointers for bus check, register read, and register write.
|
||||
*/
|
||||
struct tmc_bus_io {
|
||||
tmc_bus_check_fn check; /* Function to check if the bus is ready */
|
||||
tmc_reg_read_fn read; /* Function to read a register */
|
||||
tmc_reg_write_fn write; /* Function to write to a register */
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_H_ */
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file drivers/stepper/adi/stepper.h
|
||||
* @file drivers/stepper/adi_tmc/bus/include/adi_tmc_spi.h
|
||||
*
|
||||
* @brief Private API for Trinamic SPI bus
|
||||
*
|
||||
@ -10,8 +10,8 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_
|
||||
#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_
|
||||
#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_SPI_H_
|
||||
#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_SPI_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -60,4 +60,4 @@ int tmc_spi_write_register(const struct spi_dt_spec *bus, const uint8_t write_bi
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_SPI_H_ */
|
||||
#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_SPI_H_ */
|
||||
45
drivers/stepper/adi_tmc/bus/include/adi_tmc_uart.h
Normal file
45
drivers/stepper/adi_tmc/bus/include/adi_tmc_uart.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2025 Dipak Shetty
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_UART_H_
|
||||
#define ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_UART_H_
|
||||
|
||||
#include <zephyr/drivers/uart.h>
|
||||
|
||||
/**
|
||||
* @brief Calculate CRC for TMC UART datagram
|
||||
*
|
||||
* @param datagram Pointer to datagram buffer
|
||||
* @param len Length of datagram
|
||||
* @return uint8_t CRC value
|
||||
*/
|
||||
uint8_t tmc_uart_calc_crc(const uint8_t *datagram, uint8_t len);
|
||||
|
||||
/**
|
||||
* @brief Read register via UART single-wire interface
|
||||
*
|
||||
* @param uart Pointer to UART device specification
|
||||
* @param device_addr Secondary address of TMC device
|
||||
* @param register_address Register address to read
|
||||
* @param data Pointer to store read data
|
||||
* @return int 0 on success, negative errno on failure
|
||||
*/
|
||||
int tmc_uart_read_register(const struct device *uart, uint8_t device_addr, uint8_t register_address,
|
||||
uint32_t *data);
|
||||
|
||||
/**
|
||||
* @brief Write register via UART single-wire interface
|
||||
*
|
||||
* @param uart Pointer to UART device specification
|
||||
* @param device_addr Secondary address of the TMC device
|
||||
* @param register_address Register address to write
|
||||
* @param data Data to write to register
|
||||
* @return int 0 on success, negative errno on failure
|
||||
*/
|
||||
int tmc_uart_write_register(const struct device *uart, uint8_t device_addr,
|
||||
uint8_t register_address, uint32_t data);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_STEPPER_ADI_TMC_BUS_UART_H_ */
|
||||
Loading…
Reference in New Issue
Block a user