diff --git a/Adafruit_BusIO_Register.cpp b/Adafruit_BusIO_Register.cpp index a28193f..f3e8187 100644 --- a/Adafruit_BusIO_Register.cpp +++ b/Adafruit_BusIO_Register.cpp @@ -88,6 +88,26 @@ Adafruit_BusIO_Register::Adafruit_BusIO_Register( _width = width; } +/*! + * @brief Create a register we access over a GenericDevice + * @param genericdevice Generic device to use + * @param reg_addr Register address we will read/write + * @param width Width of the register in bytes (1-4) + * @param byteorder Byte order of register data (LSBFIRST or MSBFIRST) + * @param address_width Width of the register address in bytes (1 or 2) + */ +Adafruit_BusIO_Register::Adafruit_BusIO_Register( + Adafruit_GenericDevice *genericdevice, uint16_t reg_addr, uint8_t width, + uint8_t byteorder, uint8_t address_width) { + _i2cdevice = nullptr; + _spidevice = nullptr; + _genericdevice = genericdevice; + _addrwidth = address_width; + _address = reg_addr; + _byteorder = byteorder; + _width = width; +} + /*! * @brief Write a buffer of data to the register location * @param buffer Pointer to data to write @@ -96,17 +116,14 @@ Adafruit_BusIO_Register::Adafruit_BusIO_Register( * uncheckable) */ bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) { - uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF), (uint8_t)(_address >> 8)}; - if (_i2cdevice) { return _i2cdevice->write(buffer, len, true, addrbuffer, _addrwidth); } if (_spidevice) { if (_spiregtype == ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE) { // very special case! - // pass the special opcode address which we set as the high byte of the // regaddr addrbuffer[0] = @@ -116,7 +133,6 @@ bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) { // the address appears to be a byte longer return _spidevice->write(buffer, len, addrbuffer, _addrwidth + 1); } - if (_spiregtype == ADDRBIT8_HIGH_TOREAD) { addrbuffer[0] &= ~0x80; } @@ -129,6 +145,9 @@ bool Adafruit_BusIO_Register::write(uint8_t *buffer, uint8_t len) { } return _spidevice->write(buffer, len, addrbuffer, _addrwidth); } + if (_genericdevice) { + return _genericdevice->writeRegister(addrbuffer, _addrwidth, buffer, len); + } return false; } @@ -192,23 +211,20 @@ uint32_t Adafruit_BusIO_Register::read(void) { uint32_t Adafruit_BusIO_Register::readCached(void) { return _cached; } /*! - * @brief Read a buffer of data from the register location - * @param buffer Pointer to data to read into - * @param len Number of bytes to read - * @return True on successful write (only really useful for I2C as SPI is - * uncheckable) - */ + @brief Read a number of bytes from a register into a buffer + @param buffer Buffer to read data into + @param len Number of bytes to read into the buffer + @return true on successful read, otherwise false +*/ bool Adafruit_BusIO_Register::read(uint8_t *buffer, uint8_t len) { uint8_t addrbuffer[2] = {(uint8_t)(_address & 0xFF), (uint8_t)(_address >> 8)}; - if (_i2cdevice) { return _i2cdevice->write_then_read(addrbuffer, _addrwidth, buffer, len); } if (_spidevice) { if (_spiregtype == ADDRESSED_OPCODE_BIT0_LOW_TO_WRITE) { // very special case! - // pass the special opcode address which we set as the high byte of the // regaddr addrbuffer[0] = @@ -230,6 +246,9 @@ bool Adafruit_BusIO_Register::read(uint8_t *buffer, uint8_t len) { } return _spidevice->write_then_read(addrbuffer, _addrwidth, buffer, len); } + if (_genericdevice) { + return _genericdevice->readRegister(addrbuffer, _addrwidth, buffer, len); + } return false; } diff --git a/Adafruit_BusIO_Register.h b/Adafruit_BusIO_Register.h index c6d58de..ee7a57e 100644 --- a/Adafruit_BusIO_Register.h +++ b/Adafruit_BusIO_Register.h @@ -6,6 +6,7 @@ #if !defined(SPI_INTERFACES_COUNT) || \ (defined(SPI_INTERFACES_COUNT) && (SPI_INTERFACES_COUNT > 0)) +#include #include #include @@ -57,6 +58,11 @@ class Adafruit_BusIO_Register { uint8_t width = 1, uint8_t byteorder = LSBFIRST, uint8_t address_width = 1); + Adafruit_BusIO_Register(Adafruit_GenericDevice *genericdevice, + uint16_t reg_addr, uint8_t width = 1, + uint8_t byteorder = LSBFIRST, + uint8_t address_width = 1); + bool read(uint8_t *buffer, uint8_t len); bool read(uint8_t *value); bool read(uint16_t *value); @@ -77,6 +83,7 @@ class Adafruit_BusIO_Register { private: Adafruit_I2CDevice *_i2cdevice; Adafruit_SPIDevice *_spidevice; + Adafruit_GenericDevice *_genericdevice; Adafruit_BusIO_SPIRegType _spiregtype; uint16_t _address; uint8_t _width, _addrwidth, _byteorder; diff --git a/Adafruit_GenericDevice.cpp b/Adafruit_GenericDevice.cpp new file mode 100644 index 0000000..2fab4f0 --- /dev/null +++ b/Adafruit_GenericDevice.cpp @@ -0,0 +1,78 @@ +/* + Written with help by Claude! + https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c (at this time + chats are not shareable :( +*/ + +#include "Adafruit_GenericDevice.h" + +/*! @brief Create a Generic device with the provided read/write functions + @param read_func Function pointer for reading raw data + @param write_func Function pointer for writing raw data + @param readreg_func Function pointer for reading registers (optional) + @param writereg_func Function pointer for writing registers (optional) */ +Adafruit_GenericDevice::Adafruit_GenericDevice( + busio_genericdevice_read_t read_func, + busio_genericdevice_write_t write_func, + busio_genericdevice_readreg_t readreg_func, + busio_genericdevice_writereg_t writereg_func) { + _read_func = read_func; + _write_func = write_func; + _readreg_func = readreg_func; + _writereg_func = writereg_func; + _begun = false; +} + +/*! @brief Initializes the device + @return true if initialization was successful, otherwise false */ +bool Adafruit_GenericDevice::begin(void) { + _begun = true; + return true; +} + +/*! @brief Write a buffer of data + @param buffer Pointer to buffer of data to write + @param len Number of bytes to write + @return true if write was successful, otherwise false */ +bool Adafruit_GenericDevice::write(const uint8_t *buffer, size_t len) { + if (!_begun) + return false; + return _write_func(buffer, len); +} + +/*! @brief Read data into a buffer + @param buffer Pointer to buffer to read data into + @param len Number of bytes to read + @return true if read was successful, otherwise false */ +bool Adafruit_GenericDevice::read(uint8_t *buffer, size_t len) { + if (!_begun) + return false; + return _read_func(buffer, len); +} + +/*! @brief Read from a register location + @param addr_buf Buffer containing register address + @param addrsiz Size of register address in bytes + @param buf Buffer to store read data + @param bufsiz Size of data to read in bytes + @return true if read was successful, otherwise false */ +bool Adafruit_GenericDevice::readRegister(uint8_t *addr_buf, uint8_t addrsiz, + uint8_t *buf, uint16_t bufsiz) { + if (!_begun || !_readreg_func) + return false; + return _readreg_func(addr_buf, addrsiz, buf, bufsiz); +} + +/*! @brief Write to a register location + @param addr_buf Buffer containing register address + @param addrsiz Size of register address in bytes + @param buf Buffer containing data to write + @param bufsiz Size of data to write in bytes + @return true if write was successful, otherwise false */ +bool Adafruit_GenericDevice::writeRegister(uint8_t *addr_buf, uint8_t addrsiz, + const uint8_t *buf, + uint16_t bufsiz) { + if (!_begun || !_writereg_func) + return false; + return _writereg_func(addr_buf, addrsiz, buf, bufsiz); +} diff --git a/Adafruit_GenericDevice.h b/Adafruit_GenericDevice.h new file mode 100644 index 0000000..7dd032f --- /dev/null +++ b/Adafruit_GenericDevice.h @@ -0,0 +1,50 @@ +#ifndef ADAFRUIT_GENERICDEVICE_H +#define ADAFRUIT_GENERICDEVICE_H + +#include + +typedef bool (*busio_genericdevice_read_t)(uint8_t *buffer, size_t len); +typedef bool (*busio_genericdevice_write_t)(const uint8_t *buffer, size_t len); +typedef bool (*busio_genericdevice_readreg_t)(uint8_t *addr_buf, + uint8_t addrsiz, uint8_t *buf, + uint16_t bufsiz); +typedef bool (*busio_genericdevice_writereg_t)(uint8_t *addr_buf, + uint8_t addrsiz, + const uint8_t *buf, + uint16_t bufsiz); + +/*! + * @brief Class for communicating with a device via generic read/write functions + */ +class Adafruit_GenericDevice { +public: + Adafruit_GenericDevice( + busio_genericdevice_read_t read_func, + busio_genericdevice_write_t write_func, + busio_genericdevice_readreg_t readreg_func = nullptr, + busio_genericdevice_writereg_t writereg_func = nullptr); + + bool begin(void); + + bool read(uint8_t *buffer, size_t len); + bool write(const uint8_t *buffer, size_t len); + bool readRegister(uint8_t *addr_buf, uint8_t addrsiz, uint8_t *buf, + uint16_t bufsiz); + bool writeRegister(uint8_t *addr_buf, uint8_t addrsiz, const uint8_t *buf, + uint16_t bufsiz); + +protected: + /*! @brief Function pointer for reading raw data from the device */ + busio_genericdevice_read_t _read_func; + /*! @brief Function pointer for writing raw data to the device */ + busio_genericdevice_write_t _write_func; + /*! @brief Function pointer for reading a 'register' from the device */ + busio_genericdevice_readreg_t _readreg_func; + /*! @brief Function pointer for writing a 'register' to the device */ + busio_genericdevice_writereg_t _writereg_func; + + bool _begun; ///< whether we have initialized yet (in case the function needs + ///< to do something) +}; + +#endif // ADAFRUIT_GENERICDEVICE_H diff --git a/README.md b/README.md index 1cc06a1..59712ff 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Adafruit Bus IO Library [![Build Status](https://github.com/adafruit/Adafruit_BusIO/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_BusIO/actions) -This is a helper library to abstract away I2C & SPI transactions and registers +This is a helper library to abstract away I2C, SPI, and 'generic transport' (e.g. UART) transactions and registers Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! diff --git a/examples/genericdevice_uartregtest/.uno.test.skip b/examples/genericdevice_uartregtest/.uno.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/examples/genericdevice_uartregtest/genericdevice_uartregtest.ino b/examples/genericdevice_uartregtest/genericdevice_uartregtest.ino new file mode 100644 index 0000000..d9f3fab --- /dev/null +++ b/examples/genericdevice_uartregtest/genericdevice_uartregtest.ino @@ -0,0 +1,246 @@ +/* + Advanced example of using bstracted transport for reading and writing + register data from a UART-based device such as a TMC2209 + + Written with help by Claude! https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c + (at this time chats are not shareable :( +*/ + + +#include "Adafruit_BusIO_Register.h" +#include "Adafruit_GenericDevice.h" + +// Debugging macros +//#define DEBUG_SERIAL Serial + +#ifdef DEBUG_SERIAL +#define DEBUG_PRINT(x) DEBUG_SERIAL.print(x) +#define DEBUG_PRINTLN(x) DEBUG_SERIAL.println(x) +#define DEBUG_PRINT_HEX(x) \ + do { \ + if (x < 0x10) \ + DEBUG_SERIAL.print('0'); \ + DEBUG_SERIAL.print(x, HEX); \ + DEBUG_SERIAL.print(' '); \ + } while (0) +#else +#define DEBUG_PRINT(x) +#define DEBUG_PRINTLN(x) +#define DEBUG_PRINT_HEX(x) +#endif + +// Add IOIN register definition +#define TMC2209_IOIN 0x06 + +class TMC2209_UART { +private: + static TMC2209_UART *_instance; + Stream *_uart_stream; + uint8_t _addr; + + static bool uart_read_impl(uint8_t *buffer, size_t len) { + return _instance->uart_read_fn(buffer, len); + } + + static bool uart_write_impl(const uint8_t *buffer, size_t len) { + return _instance->uart_write_fn(buffer, len); + } + + static bool uart_readreg_impl(uint8_t *addr_buf, uint8_t addrsiz, + uint8_t *data, uint16_t datalen) { + return _instance->uart_readreg_fn(addr_buf, addrsiz, data, datalen); + } + + static bool uart_writereg_impl(uint8_t *addr_buf, uint8_t addrsiz, + const uint8_t *data, uint16_t datalen) { + return _instance->uart_writereg_fn(addr_buf, addrsiz, data, datalen); + } + + bool uart_read_fn(uint8_t *buffer, size_t len) { + uint16_t timeout = 100; + while (_uart_stream->available() < len && timeout--) { + delay(1); + } + if (timeout == 0) { + DEBUG_PRINTLN("Read timeout!"); + return false; + } + + DEBUG_PRINT("Reading: "); + for (size_t i = 0; i < len; i++) { + buffer[i] = _uart_stream->read(); + DEBUG_PRINT_HEX(buffer[i]); + } + DEBUG_PRINTLN(""); + + return true; + } + + bool uart_write_fn(const uint8_t *buffer, size_t len) { + DEBUG_PRINT("Writing: "); + for (size_t i = 0; i < len; i++) { + DEBUG_PRINT_HEX(buffer[i]); + } + DEBUG_PRINTLN(""); + + _uart_stream->write(buffer, len); + return true; + } + + bool uart_readreg_fn(uint8_t *addr_buf, uint8_t addrsiz, uint8_t *data, + uint16_t datalen) { + while (_uart_stream->available()) + _uart_stream->read(); + + uint8_t packet[4] = {0x05, uint8_t(_addr << 1), addr_buf[0], 0x00}; + + packet[3] = calcCRC(packet, 3); + if (!uart_write_impl(packet, 4)) + return false; + + // Read back echo + uint8_t echo[4]; + if (!uart_read_impl(echo, 4)) + return false; + + // Verify echo + for (uint8_t i = 0; i < 4; i++) { + if (echo[i] != packet[i]) { + DEBUG_PRINTLN("Echo mismatch"); + return false; + } + } + + uint8_t response[8]; // sync + 0xFF + reg + 4 data bytes + CRC + if (!uart_read_impl(response, 8)) + return false; + + // Verify response + if (response[0] != 0x05) { + DEBUG_PRINTLN("Invalid sync byte"); + return false; + } + + // Verify 0xFF address byte + if (response[1] != 0xFF) { + DEBUG_PRINTLN("Invalid reply address"); + return false; + } + + // Verify register address matches our request + if (response[2] != addr_buf[0]) { + DEBUG_PRINTLN("Register mismatch"); + return false; + } + + // Verify CRC + uint8_t crc = calcCRC(response, 7); // Calculate CRC of all but last byte + if (crc != response[7]) { + DEBUG_PRINTLN("CRC mismatch"); + return false; + } + + // Copy the data bytes + memcpy(data, &response[3], 4); + + return true; + } + + bool uart_writereg_fn(uint8_t *addr_buf, uint8_t addrsiz, const uint8_t *data, + uint16_t datalen) { + while (_uart_stream->available()) + _uart_stream->read(); + + uint8_t packet[8] = {0x05, + uint8_t(_addr << 1), + uint8_t(addr_buf[0] | 0x80), + data[0], + data[1], + data[2], + data[3], + 0x00}; + + packet[7] = calcCRC(packet, 7); + if (!uart_write_impl(packet, 8)) + return false; + + // Read and verify echo + uint8_t echo[8]; + if (!uart_read_impl(echo, 8)) + return false; + + // Verify echo matches what we sent + for (uint8_t i = 0; i < 8; i++) { + if (echo[i] != packet[i]) { + DEBUG_PRINTLN("Write echo mismatch"); + return false; + } + } + + return true; + } + + static uint8_t calcCRC(uint8_t *data, uint8_t length) { + uint8_t crc = 0; + for (uint8_t i = 0; i < length; i++) { + uint8_t currentByte = data[i]; + for (uint8_t j = 0; j < 8; j++) { + if ((crc >> 7) ^ (currentByte & 0x01)) { + crc = (crc << 1) ^ 0x07; + } else { + crc = crc << 1; + } + currentByte = currentByte >> 1; + } + } + return crc; + } + +public: + TMC2209_UART(Stream *serial, uint8_t addr) + : _uart_stream(serial), _addr(addr) { + _instance = this; + } + + Adafruit_GenericDevice *createDevice() { + return new Adafruit_GenericDevice(uart_read_impl, uart_write_impl, + uart_readreg_impl, uart_writereg_impl); + } +}; + +TMC2209_UART *TMC2209_UART::_instance = nullptr; + +void setup() { + Serial.begin(115200); + while (!Serial) + ; + delay(100); + Serial.println("TMC2209 Generic Device register read/write test!"); + + Serial1.begin(115200); + + TMC2209_UART uart(&Serial1, 0); + Adafruit_GenericDevice *device = uart.createDevice(); + device->begin(); + + // Create register object for IOIN + Adafruit_BusIO_Register ioin_reg(device, + TMC2209_IOIN, // device and register address + 4, // width = 4 bytes + MSBFIRST, // byte order + 1); // address width = 1 byte + Serial.print("IOIN = 0x"); + Serial.println(ioin_reg.read(), HEX); + + // Create RegisterBits for VERSION field (bits 28:24) + Adafruit_BusIO_RegisterBits version_bits( + &ioin_reg, 8, 24); // 8 bits wide, starting at bit 24 + + Serial.println("Reading VERSION..."); + uint8_t version = version_bits.read(); + + Serial.print("VERSION = 0x"); + Serial.println(version, HEX); +} + +void loop() { delay(1000); } diff --git a/examples/genericdevice_uarttest/.uno.test.skip b/examples/genericdevice_uarttest/.uno.test.skip new file mode 100644 index 0000000..e69de29 diff --git a/examples/genericdevice_uarttest/genericdevice_uarttest.ino b/examples/genericdevice_uarttest/genericdevice_uarttest.ino new file mode 100644 index 0000000..bc4175c --- /dev/null +++ b/examples/genericdevice_uarttest/genericdevice_uarttest.ino @@ -0,0 +1,75 @@ +/* + Abstracted transport for reading and writing data from a UART-based + device such as a TMC2209 + + Written with help by Claude! https://claude.ai/chat/335f50b1-3dd8-435e-9139-57ec7ca26a3c + (at this time chats are not shareable :( +*/ + +#include "Adafruit_GenericDevice.h" + +Stream *uart_stream; // Will hold the pointer to our Stream object + +Adafruit_GenericDevice *create_uart_device(Stream *serial_port) { + uart_stream = serial_port; // Store the Stream pointer + + auto uart_write = [](const uint8_t *buffer, size_t len) -> bool { + uart_stream->write(buffer, len); + return true; + }; + + auto uart_read = [](uint8_t *buffer, size_t len) -> bool { + uint16_t timeout = 100; + while (uart_stream->available() < len && timeout--) { + delay(1); + } + if (timeout == 0) { + return false; + } + for (size_t i = 0; i < len; i++) { + buffer[i] = uart_stream->read(); + } + return true; + }; + + return new Adafruit_GenericDevice(uart_read, uart_write); +} + +void setup() { + Serial.begin(115200); + while (!Serial) + ; + delay(100); + + Serial.println("Generic Device test!"); + + Serial1.begin(115200); + + Adafruit_GenericDevice *device = create_uart_device(&Serial1); + device->begin(); + + uint8_t write_buf[4] = {0x5, 0x0, 0x0, 0x48}; + uint8_t read_buf[8]; + + Serial.println("Writing data..."); + if (!device->write(write_buf, 4)) { + Serial.println("Write failed!"); + return; + } + + Serial.println("Reading response..."); + if (!device->read(read_buf, 8)) { + Serial.println("Read failed!"); + return; + } + + Serial.print("Got response: "); + for (int i = 0; i < 8; i++) { + Serial.print("0x"); + Serial.print(read_buf[i], HEX); + Serial.print(" "); + } + Serial.println(); +} + +void loop() { delay(1000); } diff --git a/library.properties b/library.properties index 27e536a..bdff9e1 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=Adafruit BusIO -version=1.16.3 +version=1.17.0 author=Adafruit maintainer=Adafruit -sentence=This is a library for abstracting away I2C and SPI interfacing -paragraph=This is a library for abstracting away I2C and SPI interfacing +sentence=This is a library for abstracting away UART, I2C and SPI interfacing +paragraph=This is a library for abstracting away UART, I2C and SPI interfacing category=Signal Input/Output url=https://github.com/adafruit/Adafruit_BusIO architectures=*