Merge pull request #4595 from thinkyhead/rc_i2c_slave

Add support for i2c slave address
This commit is contained in:
Scott Lahteine 2016-08-11 12:39:26 -07:00 committed by GitHub
commit 52560e257f
22 changed files with 121 additions and 63 deletions

View file

@ -205,10 +205,11 @@ script:
- opt_enable ULTIMAKERCONTROLLER FILAMENT_LCD_DISPLAY - opt_enable ULTIMAKERCONTROLLER FILAMENT_LCD_DISPLAY
- build_marlin - build_marlin
# #
# Enable BEZIER_CURVE_SUPPORT # Enable BEZIER_CURVE_SUPPORT, EXPERIMENTAL_I2CBUS, and I2C_SLAVE_ADDRESS
# #
- restore_configs - restore_configs
- opt_enable_adv BEZIER_CURVE_SUPPORT - opt_enable_adv BEZIER_CURVE_SUPPORT EXPERIMENTAL_I2CBUS
- opt_set_adv I2C_SLAVE_ADDRESS 1
- build_marlin - build_marlin
# #
# Enable COREXY # Enable COREXY

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -835,6 +835,14 @@ void servo_init() {
void enableStepperDrivers() { pinMode(STEPPER_RESET_PIN, INPUT); } // set to input, which allows it to be pulled high by pullups void enableStepperDrivers() { pinMode(STEPPER_RESET_PIN, INPUT); } // set to input, which allows it to be pulled high by pullups
#endif #endif
#if ENABLED(EXPERIMENTAL_I2CBUS) && I2C_SLAVE_ADDRESS > 0
void i2c_listener(int bytes) {
i2c.receive(bytes); // just echo all bytes received to serial
}
#endif
/** /**
* Marlin entry-point: Set up before the program loop * Marlin entry-point: Set up before the program loop
* - Set up the kill pin, filament runout, power hold * - Set up the kill pin, filament runout, power hold
@ -981,6 +989,10 @@ void setup() {
for (uint8_t i = 0; i < MIXING_STEPPERS; i++) for (uint8_t i = 0; i < MIXING_STEPPERS; i++)
mixing_virtual_tool_mix[t][i] = mixing_factor[i]; mixing_virtual_tool_mix[t][i] = mixing_factor[i];
#endif #endif
#if ENABLED(EXPERIMENTAL_I2CBUS) && I2C_SLAVE_ADDRESS > 0
i2c.onReceive(i2c_listener);
#endif
} }
/** /**
@ -5246,15 +5258,13 @@ inline void gcode_M121() { endstops.enable_globally(false); }
*/ */
inline void gcode_M155() { inline void gcode_M155() {
// Set the target address // Set the target address
if (code_seen('A')) if (code_seen('A')) i2c.address(code_value_byte());
i2c.address(code_value_byte());
// Add a new byte to the buffer // Add a new byte to the buffer
else if (code_seen('B')) if (code_seen('B')) i2c.addbyte(code_value_byte());
i2c.addbyte(code_value_int());
// Flush the buffer to the bus // Flush the buffer to the bus
else if (code_seen('S')) i2c.send(); if (code_seen('S')) i2c.send();
// Reset and rewind the buffer // Reset and rewind the buffer
else if (code_seen('R')) i2c.reset(); else if (code_seen('R')) i2c.reset();
@ -5266,11 +5276,11 @@ inline void gcode_M121() { endstops.enable_globally(false); }
* Usage: M156 A<slave device address base 10> B<number of bytes> * Usage: M156 A<slave device address base 10> B<number of bytes>
*/ */
inline void gcode_M156() { inline void gcode_M156() {
uint8_t addr = code_seen('A') ? code_value_byte() : 0; if (code_seen('A')) i2c.address(code_value_byte());
int bytes = code_seen('B') ? code_value_int() : 1;
if (addr && bytes > 0 && bytes <= 32) { uint8_t bytes = code_seen('B') ? code_value_byte() : 1;
i2c.address(addr);
if (i2c.addr > 0 && bytes > 0 && bytes <= 32) {
i2c.reqbytes(bytes); i2c.reqbytes(bytes);
} }
else { else {

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -801,5 +801,6 @@ const unsigned int dropsegments = 2; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -803,5 +803,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -797,5 +797,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -797,5 +797,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -796,5 +796,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -801,5 +801,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -797,5 +797,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -795,5 +795,6 @@ const unsigned int dropsegments = 5; //everything with less than this number of
// @section i2cbus // @section i2cbus
//#define EXPERIMENTAL_I2CBUS //#define EXPERIMENTAL_I2CBUS
#define I2C_SLAVE_ADDRESS 0 // Set non-zero to act as a slave
#endif // CONFIGURATION_ADV_H #endif // CONFIGURATION_ADV_H

View file

@ -29,25 +29,28 @@
#include <Wire.h> #include <Wire.h>
TWIBus::TWIBus() { TWIBus::TWIBus() {
Wire.begin(); // We use no address so we will join the BUS as the master #if I2C_SLAVE_ADDRESS == 0
Wire.begin(); // No address joins the BUS as the master
#else
Wire.begin(I2C_SLAVE_ADDRESS); // Join the bus as a slave
#endif
this->reset(); this->reset();
} }
void TWIBus::reset() { void TWIBus::reset() {
this->addr = 0;
this->buffer_s = 0; this->buffer_s = 0;
this->buffer[0] = 0x00; this->buffer[0] = 0x00;
} }
void TWIBus::address(uint8_t addr) { void TWIBus::address(const uint8_t addr) {
this->addr = addr; this->addr = addr;
#if ENABLED(DEBUG_TWIBUS) #if ENABLED(DEBUG_TWIBUS)
debug(PSTR("sendto"), this->addr); debug(PSTR("address"), this->addr);
#endif #endif
} }
void TWIBus::addbyte(char c) { void TWIBus::addbyte(const char c) {
if (buffer_s >= sizeof(this->buffer)) return; if (buffer_s >= sizeof(this->buffer)) return;
this->buffer[this->buffer_s++] = c; this->buffer[this->buffer_s++] = c;
@ -71,37 +74,33 @@ void TWIBus::send() {
this->reset(); this->reset();
} }
void TWIBus::reqbytes(uint8_t bytes) { void TWIBus::reqbytes(const uint8_t bytes) {
if (!this->addr) return; if (!this->addr) return;
#if ENABLED(DEBUG_TWIBUS) #if ENABLED(DEBUG_TWIBUS)
debug(PSTR("reqbytes"), bytes); debug(PSTR("reqbytes"), bytes);
#endif #endif
// requestFrom() is a blocking function
millis_t t = millis() + this->timeout; millis_t t = millis() + this->timeout;
Wire.requestFrom(this->addr, bytes); Wire.requestFrom(this->addr, bytes);
while (Wire.available() < bytes && PENDING(millis(), t)) { /*nada*/ }
// requestFrom() is a blocking function this->relaydata(bytes);
while (Wire.available() < bytes) {
if (ELAPSED(millis(), t)) break;
else continue;
}
SERIAL_ECHO_START;
SERIAL_ECHOPAIR("i2c-reply: from:", this->addr);
SERIAL_ECHOPAIR(" bytes:", Wire.available());
SERIAL_ECHOPGM (" data:");
// Protect against buffer overflows if the number of received bytes
// is less than the number of requested bytes
uint8_t wba = Wire.available();
for (int i = 0; i < wba; i++) SERIAL_CHAR(Wire.read());
SERIAL_EOL;
// Reset the buffer after sending the data // Reset the buffer after sending the data
this->reset(); this->reset();
} }
void TWIBus::relaydata(uint8_t bytes) {
SERIAL_ECHO_START;
SERIAL_ECHOPAIR("i2c-reply: from:", this->addr);
SERIAL_ECHOPAIR(" bytes:", bytes);
SERIAL_ECHOPGM (" data:");
while (bytes-- && Wire.available()) SERIAL_CHAR(Wire.read());
SERIAL_EOL;
}
#if ENABLED(DEBUG_TWIBUS) #if ENABLED(DEBUG_TWIBUS)
void TWIBus::debug(const char func[], int32_t val/*=-1*/) { void TWIBus::debug(const char func[], int32_t val/*=-1*/) {

View file

@ -25,9 +25,13 @@
#include "macros.h" #include "macros.h"
#include <Wire.h>
// Print debug messages with M111 S2 (Uses 236 bytes of PROGMEM) // Print debug messages with M111 S2 (Uses 236 bytes of PROGMEM)
//#define DEBUG_TWIBUS //#define DEBUG_TWIBUS
typedef void (*twiSlaveFunc_t)(int bytes);
/** /**
* TWIBUS class * TWIBUS class
* *
@ -49,79 +53,105 @@ class TWIBus {
private: private:
/** /**
* @brief Timeout value in milliseconds * @brief Timeout value in milliseconds
* @details For blocking operations this constant value will set the max * @details Maximum amount of time (ms) to wait for a reply.
* amount of time Marlin will keep waiting for a reply. Useful is something * Useful if something goes wrong on the bus and the
* goes wrong on the bus and the SDA/SCL lines are held up by another device. * SDA/SCL lines are held up by another device.
*/ */
const int timeout = 5; const int timeout = 5;
/**
* @brief Target device address
* @description This stores, until the buffer is flushed, the target device
* address, take not we do follow Arduino 7bit addressing.
*/
uint8_t addr = 0;
/** /**
* @brief Number of bytes on buffer * @brief Number of bytes on buffer
* @description This var holds the total number of bytes on our buffer * @description Number of bytes in the buffer waiting to be flushed to the bus.
* waiting to be flushed to the bus.
*/ */
uint8_t buffer_s = 0; uint8_t buffer_s = 0;
/** /**
* @brief Internal buffer * @brief Internal buffer
* @details This is a fixed buffer, TWI command cannot be longer than this * @details A fixed buffer. TWI commands can be no longer than this.
*/ */
char buffer[30]; char buffer[32];
public: public:
/**
* @brief Target device address
* @description The target device address. Persists until changed.
*/
uint8_t addr = 0;
/** /**
* @brief Class constructor * @brief Class constructor
* @details Initialized the TWI bus and clears the buffer * @details Initialize the TWI bus and clear the buffer
*/ */
TWIBus(); TWIBus();
/** /**
* @brief Reset the buffer * @brief Reset the buffer
* @details Brings the internal buffer to a known-empty state * @details Set the buffer to a known-empty state
*/ */
void reset(); void reset();
/** /**
* @brief Send the buffer data to the bus * @brief Send the buffer data to the bus
* @details Flushed the buffer into the bus targeting the cached slave device * @details Flush the buffer to the bus at the target address.
* address.
*/ */
void send(); void send();
/** /**
* @brief Add one byte to the buffer * @brief Add one byte to the buffer
* @details Adds the byte to the buffer in a sequential way, if buffer is full * @details Add a byte to the end of the buffer.
* the request is silently ignored. * Silently fails if the buffer is full.
* *
* @param c a data byte * @param c a data byte
*/ */
void addbyte(char c); void addbyte(const char c);
/** /**
* @brief Sets the target slave address * @brief Set the target slave address
* @details The target slave address is stored so it can be later used when * @details The target slave address for sending the full packet.
* the complete packet needs to be sent over the bus.
* *
* @param addr 7-bit integer address * @param addr 7-bit integer address
*/ */
void address(uint8_t addr); void address(const uint8_t addr);
/** /**
* @brief Request data from slave device * @brief Request data from the slave device
* @details Requests data from a slave device, when the data is received it will * @details Request a number of bytes from a slave device.
* be relayed to the serial line using a parser-friendly formatting. * This implementation simply sends the data to serial
* in a parser-friendly format.
* *
* @param bytes the number of bytes to request * @param bytes the number of bytes to request
*/ */
void reqbytes(uint8_t bytes); void reqbytes(const uint8_t bytes);
/**
* @brief Relay data from the slave device to serial
* @details Relay a number of bytes from the bus to
* serial in a parser-friendly format.
*
* @param bytes the number of bytes to request
*/
void relaydata(uint8_t bytes);
#if I2C_SLAVE_ADDRESS > 0
/**
* @brief Receive bytes (passively)
* @details Receive bytes sent to our slave address.
* and simply echo them to serial.
*/
inline void receive(uint8_t bytes) { relaydata(bytes); }
/**
* @brief Register a slave handler
* @details Set a handler to receive data from the bus,
* so we can act as a slave.
*
* @param handler A function to handle receiving bytes
*/
inline void onReceive(const twiSlaveFunc_t handler) { Wire.onReceive(handler); }
#endif
#if ENABLED(DEBUG_TWIBUS) #if ENABLED(DEBUG_TWIBUS)