diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 10a9375146..4b40c3a632 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -1,3 +1,7 @@ +ifneq (,$(filter arduino,$(USEMODULE))) + FEATURES_OPTIONAL += periph_i2c +endif + ifneq (,$(filter eepreg,$(USEMODULE))) FEATURES_REQUIRED += periph_eeprom endif diff --git a/sys/arduino/include/Arduino.h b/sys/arduino/include/Arduino.h new file mode 100644 index 0000000000..a151793d6f --- /dev/null +++ b/sys/arduino/include/Arduino.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_arduino + * @brief Wrapper to keep source code compatibility for Arduino.h + * @author Gunar Schorcht + * @file + * @{ + */ + +#ifndef ARDUINO_H +#define ARDUINO_H + +#ifdef __cplusplus +#include "arduino.hpp" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ARDUINO_H */ +/** @} */ diff --git a/sys/arduino/include/Wire.h b/sys/arduino/include/Wire.h new file mode 100644 index 0000000000..0f546e8661 --- /dev/null +++ b/sys/arduino/include/Wire.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_arduino + * @brief Wrapper to keep source code compatibility for Wire.h + * @author Gunar Schorcht + * @file + * @{ + */ + +#ifndef WIRE_H +#define WIRE_H + +#ifndef MODULE_PERIPH_I2C +#error "No I2C support on your board" +#endif + +#ifdef __cplusplus +#include "wireport.hpp" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WIRE_H */ +/** @} */ diff --git a/sys/arduino/include/arduino.hpp b/sys/arduino/include/arduino.hpp index 536f7fbbf8..6953a694f1 100644 --- a/sys/arduino/include/arduino.hpp +++ b/sys/arduino/include/arduino.hpp @@ -18,8 +18,8 @@ * @author Hauke Petersen */ -#ifndef ARDUINO_H -#define ARDUINO_H +#ifndef ARDUINO_HPP +#define ARDUINO_HPP extern "C" { #include "periph/gpio.h" @@ -122,5 +122,5 @@ unsigned long millis(); int analogRead(int pin); #endif -#endif /* ARDUINO_H */ +#endif /* ARDUINO_HPP */ /** @} */ diff --git a/sys/arduino/include/wireport.hpp b/sys/arduino/include/wireport.hpp new file mode 100644 index 0000000000..497a363023 --- /dev/null +++ b/sys/arduino/include/wireport.hpp @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_arduino_api + * @{ + * + * @author Gunar Schorcht + * @file + * + * @brief Definition of the Arduino 'Wire Library' for TwoWire interfaces + * + * This library is the implementation of the [Arduino Wire Library] + * (https://www.arduino.cc/en/Reference/Wire) for the I2C peripheral + * interfaces in RIOT. It supports only I2C master mode and the functions + * that are documented in the official [Arduino Reference] + * (https://www.arduino.cc/en/Reference/Wire) of this library. + * + * The implementation is an adaptation of the original Arduino Wire Library + * which is published under the following copyright: + * + * ``` + * TwoWire.h - TWI/I2C library for Arduino & Wiring + * Copyright (c) 2006 Nicholas Zambetti. All right reserved. + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts + * ``` + * + * The documentation in this file is partially extracted from the original + * [Arduino Reference](https://www.arduino.cc/en/Reference/Wire) of this + * library which is published under the + * [Creative Commons Attribution-ShareAlike 3.0 License] + * (https://creativecommons.org/licenses/by-sa/3.0/). + */ + +#ifndef WIREPORT_HPP +#define WIREPORT_HPP + +#include +#include + +/** Default Arduino I2C interface */ +#ifndef ARDUINO_I2C_DEV +#define ARDUINO_I2C_DEV (I2C_DEV(0)) +#endif + +/** Buffer length used by the Arduino Wire library implementation */ +#define WIREPORT_BUFFER_LENGTH 32 + +/** Class definition for the Arduino Wire library implementation */ +class TwoWire +{ + private: + + static uint8_t rxBuffer[]; /**< RX buffer */ + static uint8_t rxBufferIndex; /**< index for RX buffer read */ + static uint8_t rxBufferLength; /**< number of bytes in RX buffer */ + + static uint8_t txAddress; /**< adress for transfer */ + static uint8_t txBuffer[]; /**< TX buffer */ + static uint8_t txBufferIndex; /**< index for TX buffer write */ + static uint8_t txBufferLength; /**< number of bytes in TX buffer */ + static uint8_t txError; /**< error code in write operations */ + + static uint8_t transmitting; /**< set by #beginTransmission and reset + by #endTransmission to indicate an + ongoing transmission */ + public: + + /** + * @brief Constructor + */ + TwoWire(void); + + /** + * @brief Initializes the I2C device defined by #ARDUINO_I2C_DEV as master + */ + void begin(void); + + /** + * @brief Initializes the I2C device defined by #ARDUINO_I2C_DEV as slave + * + * @note Since slave mode is not yet supported by the RIOT port of the + * Arduino Wire library, calling this method leads to a core panic. + * + * @param[in] addr Address of the device initialized as slave + */ + void begin(uint8_t addr); + + /** + * @brief Set the clock speed of the I2C device defined by + * #ARDUINO_I2C_DEV. + * + * @note In RIOT, the I2C bus clock speed is determined by the board + * definition. This method does therefore nothing. It is just realized + * for compatibility reasons. + * + * @param[in] clk I2C clock speed in Hz + */ + void setClock(uint32_t clk); + + /** + * @brief Begin a transmission to a I2C slave device + * + * This method begins a transmission to the I2C slave device with the + * given address. Subsequently, queue bytes for transmission with the + * #write method and transmit them by calling #endTransmission. + * + * Copied from https://www.arduino.cc/en/Reference/WireBeginTransmission + * + * @param[in] addr Address of the slave device + */ + void beginTransmission(uint8_t addr); + + /** + * @brief End a transmission to a I2C slave device + * + * Ends a transmission to a slave device that was begun by + * #beginTransmission() and transmits the bytes that were queued by #write. + * Sends always a STOP condition after the request. + * + * Copied from https://www.arduino.cc/en/Reference/WireEndTransmission + * + * @retval 0 success + * @retval 1 data too long to fit in transmit buffer + * @retval 2 received NACK on transmit of address + * @retval 3 received NACK on transmit of data + * @retval 4 other error + */ + uint8_t endTransmission(void); + + /** + * @brief End a transmission to a I2C slave device + * + * Ends a transmission to a slave device that was begun by + * #beginTransmission() and transmits the bytes that were queued by #write. + * + * Copied from https://www.arduino.cc/en/Reference/WireEndTransmission + * + * @param stop Send STOP condition after transmission if true or + * nothing if false. + * + * @retval 0 success + * @retval 1 data too long to fit in transmit buffer + * @retval 2 received NACK on transmit of address + * @retval 3 received NACK on transmit of data + * @retval 4 other error + */ + uint8_t endTransmission(uint8_t stop); + + /** + * @brief Request bytes from a I2C slave device + * + * Used by the master to request bytes from a slave device. The bytes may + * then be retrieved with the #available and #read methods. Sends always + * a STOP condition after the request. + * + * Copied from https://www.arduino.cc/en/Reference/WireRequestFrom + * + * @param[in] addr 7-bit address of the device to request bytes from + * @param[in] size Number of bytes to request + * + * @return number of bytes returned from the slave device + */ + uint8_t requestFrom(uint8_t addr, uint8_t size); + + /** + * @brief Request bytes from a I2C slave device + * + * Used by the master to request bytes from a slave device. The bytes may + * then be retrieved with the #available and #read methods. + * + * @param[in] addr 7-bit address of the device to request bytes from + * @param[in] size Number of bytes to request + * @param[in] stop Send STOP condition after the request if true or + * nothing if false. + * + * Copied from https://www.arduino.cc/en/Reference/WireRequestFrom + * + * @return number of bytes returned from the slave device + */ + uint8_t requestFrom(uint8_t addr, uint8_t size, uint8_t stop); + + /** + * @brief Queue a byte for transmission from a master to slave device + * + * The method queues a byte for transmission from a master to slave device + * in-between calls to #beginTransmission and #endTransmission. + * + * @param[in] data Data byte + * + * Copied from https://www.arduino.cc/en/Reference/WireWrite + * + * @return number of bytes queued + */ + virtual size_t write(uint8_t data); + + /** + * @brief Queue bytes for transmission from a master to slave device + * + * The method queues bytes for transmission from a master to slave device + * in-between calls to #beginTransmission and #endTransmission. + * + * @param[in] data Array of data to send as bytes + * @param[in] size Number of bytes to transmit + * + * Copied from https://www.arduino.cc/en/Reference/WireWrite + * + * @return number of bytes queued + */ + virtual size_t write(const uint8_t *data, size_t size); + + /** + * @brief Return the number of bytes available for retrieval + * + * Returns the number of bytes available for retrieval with #read. This + * should be called on a master device after a call to #requestFrom. + * + * Copied from https://www.arduino.cc/en/Reference/WireAvailable + * + * @return number of bytes available for retrieval + */ + virtual int available(void); + + /** + * @brief Reads one byte transmitted from slave device to the master + * + * Reads a byte that was transmitted from a slave device to the master after + * a call to #requestFrom and removes it from receive buffer. + * + * Copied from https://www.arduino.cc/en/Reference/WireRead + * + * @return next byte received, or -1 if none is available. + */ + virtual int read(void); + + /** + * @brief Read bytes transmitted from slave device to the master + * + * Reads a number of bytes that were transmitted from a slave device to the + * master after a call to #requestFrom and removes them from receive buffer. + * + * @param[out] buffer buffer to store the bytes + * @param[in] length number of bytes to read + * + * @return number of bytes placed in the buffer + */ + virtual size_t readBytes(uint8_t *buffer, size_t length); + /** + * @brief Peeks one byte transmitted from slave device to the master + * + * Reads a byte that was transmitted from a slave device to the master after + * a call to #requestFrom without advancing to the next one. That is, + * successive calls to #peek will return the same value, as will the + * next call to read. + * + * Copied from https://www.arduino.cc/en/Reference/WireRead and + * https://www.arduino.cc/en/Reference/StreamPeek + * + * @return next byte received, or -1 if none is available. + */ + virtual int peek(void); + + /** + * @brief Flush the RX and TX buffer + * + * This method clears the RX as well as the TX buffer. It is not necessary + * to call this method explicitly. RX buffer is flushed implicitly when + * method #requestFrom is called. Tx buffer is flushed implicitly when + * method #beginTransmission is called. + */ + virtual void flush(void); +}; + +extern TwoWire Wire; + +#endif /* WIREPORT_HPP */ +/** @} */ diff --git a/sys/arduino/wireport.cpp b/sys/arduino/wireport.cpp new file mode 100644 index 0000000000..5cff54d350 --- /dev/null +++ b/sys/arduino/wireport.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_arduino_api + * @{ + * + * @author Gunar Schorcht + * @file + * + * @brief Definition of the Arduino 'Wire Library' for TwoWire interfaces + * @} + */ + +#if MODULE_PERIPH_I2C + +extern "C" { +#include +#include +#include +#include +} + +#include "Wire.h" + +#include "log.h" +#include "panic.h" +#include "periph/i2c.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define WIRE_PORT_OK (0) +#define WIRE_PORT_ERROR_DATA_TO_LONG (1) +#define WIRE_PORT_ERROR_ADDR_NACK (2) +#define WIRE_PORT_ERROR_DATA_NACK (3) +#define WIRE_PORT_ERROR_OTHER (4) + +/* Initialize Class Variables */ + +uint8_t TwoWire::rxBuffer[WIREPORT_BUFFER_LENGTH]; +uint8_t TwoWire::rxBufferIndex = 0; +uint8_t TwoWire::rxBufferLength = 0; + +uint8_t TwoWire::txAddress = 0; +uint8_t TwoWire::txBuffer[WIREPORT_BUFFER_LENGTH]; +uint8_t TwoWire::txBufferIndex = 0; +uint8_t TwoWire::txBufferLength = 0; +uint8_t TwoWire::txError = 0; + +uint8_t TwoWire::transmitting = 0; + +/* Constructors */ + +TwoWire::TwoWire(void) +{ + DEBUG("[wire] %s\n", __func__); +} + +/* Public Methods */ + +void TwoWire::begin(void) +{ + DEBUG("[wire] %s\n", __func__); + + rxBufferIndex = 0; + rxBufferLength = 0; + + txBufferIndex = 0; + txBufferLength = 0; + + i2c_init(ARDUINO_I2C_DEV); +} + +void TwoWire::begin(uint8_t addr) +{ + (void)addr; + DEBUG("[wire] %s\n", __func__); + core_panic(PANIC_GENERAL_ERROR, "[wire] slave mode is not supported"); +} + +void TwoWire::setClock(uint32_t clk) +{ + (void)clk; + /* not possible, bus speed is defined by the board definition */ + DEBUG("[wire] %s: clock is defined by board definition\n", __func__); +} + +uint8_t TwoWire::requestFrom(uint8_t addr, uint8_t size, uint8_t stop) +{ + DEBUG("[wire] %s: addr %02x, size %d, stop %d\n", __func__, addr, size, stop); + + if (size > WIREPORT_BUFFER_LENGTH) { + size = WIREPORT_BUFFER_LENGTH; + } + + uint8_t read = 0; + + if (i2c_acquire(ARDUINO_I2C_DEV) == 0) { + if (i2c_read_bytes(ARDUINO_I2C_DEV, addr, rxBuffer, size, + stop ? 0 : I2C_NOSTOP) == 0) { + read = size; + } + i2c_release(ARDUINO_I2C_DEV); + } + + rxBufferIndex = 0; + rxBufferLength = read; + + return read; +} + +uint8_t TwoWire::requestFrom(uint8_t addr, uint8_t size) { + return requestFrom(addr, size, 1); +} + +void TwoWire::beginTransmission(uint8_t addr) +{ + DEBUG("[wire] %s: addr %02x\n", __func__, addr); + transmitting = 1; + txAddress = addr; + txBufferIndex = 0; + txBufferLength = 0; +} + +uint8_t TwoWire::endTransmission(uint8_t stop) +{ + DEBUG("[wire] %s: stop %d\n", __func__, stop); + + if (txError) { + return txError; + } + + if (i2c_acquire(ARDUINO_I2C_DEV) != 0) { + return WIRE_PORT_ERROR_OTHER; + } + + int res = i2c_write_bytes(ARDUINO_I2C_DEV, + txAddress, txBuffer, txBufferLength, + stop ? 0 : I2C_NOSTOP); + switch (res) { + case 0: break; + case ENXIO: res = WIRE_PORT_ERROR_ADDR_NACK; + break; + case EIO: res = WIRE_PORT_ERROR_DATA_NACK; + break; + default: res = WIRE_PORT_ERROR_OTHER; + } + + i2c_release(ARDUINO_I2C_DEV); + + txBufferIndex = 0; + txBufferLength = 0; + txError = 0; + transmitting = 0; + + return res; +} + +uint8_t TwoWire::endTransmission(void) +{ + return endTransmission(true); +} + +size_t TwoWire::write(uint8_t data) +{ + DEBUG("[wire] %s: data %02x\n", __func__, data); + + if (!transmitting || txBufferLength >= WIREPORT_BUFFER_LENGTH) { + txError = WIRE_PORT_ERROR_DATA_TO_LONG; + return 0; + } + + txBuffer[txBufferIndex++] = data; + txBufferLength = txBufferIndex; + + return 1; +} + +size_t TwoWire::write(const uint8_t *data, size_t size) +{ + DEBUG("[wire] %s: data %p, size %d\n", __func__, data, size); + for (size_t i = 0; i < size; i++) { + if (!write(data[i])) { + return i; + } + } + + return size; +} + +int TwoWire::available(void) +{ + DEBUG("[wire] %s: return %d\n", __func__, rxBufferLength - rxBufferIndex); + return rxBufferLength - rxBufferIndex; +} + +int TwoWire::read(void) +{ + DEBUG("[wire] %s\n", __func__); + + int value = -1; + if (rxBufferIndex < rxBufferLength) { + value = rxBuffer[rxBufferIndex]; + ++rxBufferIndex; + } + return value; +} + +size_t TwoWire::readBytes(uint8_t *buffer, size_t length) +{ + DEBUG("[wire] %s\n", __func__); + + for (size_t i = 0; i < length; i++) { + int byte = read(); + if (byte == -1) { + return i; + } + buffer[i] = byte; + } + return length; +} + +int TwoWire::peek(void) +{ + DEBUG("[wire] %s\n", __func__); + + int value = -1; + if (rxBufferIndex < rxBufferLength) { + value = rxBuffer[rxBufferIndex]; + } + return value; +} + +void TwoWire::flush(void) +{ + DEBUG("[wire] %s\n", __func__); + + rxBufferIndex = 0; + rxBufferLength = 0; + txBufferIndex = 0; + txBufferLength = 0; +} + +/* single instance */ + +TwoWire Wire; + +#else /* MODULE_PERIPH_I2C */ + +typedef int dont_be_pedantic; + +#endif /* MODULE_PERIPH_I2C */