From ef5c4b9f30da2f4b9a0edd4231e95b3ef8a88d43 Mon Sep 17 00:00:00 2001 From: Barry Carter Date: Mon, 3 Sep 2012 17:45:58 +0100 Subject: - tx and rx callbacks for each module - Callbacks can be called after each read/write cycle or per byte - Each I2C module can have different callbacks - General call support also working - Supports master and slave at same time. Also works with multimaster Usage: i2c_msg msg; char buffer[255]; main() { i2c_slave_enable(I2C1, I2C_FAST_MODE | I2C_SLAVE_DUAL_ADDRESS | I2C_SLAVE_GENERAL_CALL | I2C_SLAVE_USE_RX_BUFFER); // init slave mode. Enables master too i2c_slave_attach_recv_handler(I2C1, pmsg, funcrx); // attach receive handler i2c_slave_attach_transmit_handler(I2C1, pmsg, functx); // attach transmit handler i2c_slave_set_own_address(I2C1, 0x10); // set addresss 1 i2c_slave_set_own_address2(I2C1, 0x20); // set addresss 2 } void funcrx(i2c_msg *msg) { printf("length is %d.\n", msg->length); char return_data = msg0>data[0]; } void functx(i2c_msg *dev) { msg->data[0] = 0x01; msg->data[1] = 0x02; msg->data[2] = 0x03; msg->data[3] = 0x04; msg->data[4] = 0x05; msg->length = 5; } All code derived from datasheets and libmaple. Signed-off-by:- Barry Carter --- CREDITS | 6 + libmaple/i2c.c | 235 ++++++++++++++++++++++++++++++++- libmaple/include/libmaple/i2c.h | 52 ++++++++ libmaple/include/libmaple/i2c_common.h | 14 ++ 4 files changed, 306 insertions(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index 2b803b1..5918e01 100644 --- a/CREDITS +++ b/CREDITS @@ -81,3 +81,9 @@ W: http://sarup.dk/index.html N: Andy Scott ("xttocs") E: andy.g.scott@gmail.com D: LiquidCrystal library + +N: Barry Carter ("ginge") +E: barry.carter@gmail.com +D: Added i2C slave support +W: headfuzz.co.uk + \ No newline at end of file diff --git a/libmaple/i2c.c b/libmaple/i2c.c index 9c93d3f..b8e622d 100644 --- a/libmaple/i2c.c +++ b/libmaple/i2c.c @@ -154,6 +154,7 @@ void i2c_bus_reset(const i2c_dev *dev) { void i2c_init(i2c_dev *dev) { rcc_reset_dev(dev->clk_id); rcc_clk_enable(dev->clk_id); + _i2c_irq_priority_fixup(dev); } /* Hack for deprecated bit of STM32F1 functionality */ @@ -198,12 +199,30 @@ void i2c_master_enable(i2c_dev *dev, uint32 flags) { nvic_irq_enable(dev->er_nvic_line); i2c_enable_irq(dev, I2C_IRQ_EVENT | I2C_IRQ_BUFFER | I2C_IRQ_ERROR); + /* Configure the slave unit */ + if (flags & I2C_SLAVE_DUAL_ADDRESS) { + i2c_slave_dual_address_enable(dev); + } + + if (flags & I2C_SLAVE_GENERAL_CALL) { + i2c_slave_general_call_enable(dev); + } + + /* store all of the flags */ + dev->config_flags = flags; + /* Make it go! */ i2c_peripheral_enable(dev); + i2c_enable_ack(dev); dev->state = I2C_STATE_IDLE; } +void i2c_slave_enable(i2c_dev *dev, uint32 flags) { + i2c_disable(dev); + i2c_master_enable(dev, dev->config_flags | flags); +} + /** * @brief Process an i2c transaction. * @@ -303,6 +322,152 @@ void _i2c_irq_handler(i2c_dev *dev) { */ dev->timestamp = systick_uptime(); + /* Add Slave support + * Barry Carter 2012 + * barry.carter@gmail.com + */ + + /* Check to see if MSL master slave bit is set */ + if ((sr2 & I2C_SR2_MSL) != I2C_SR2_MSL) { /* 0 = slave mode 1 = master */ + + /* Check for address match */ + if (sr1 & I2C_SR1_ADDR) { + /* Find out which address was matched */ + /* Check the general call address first */ + if (sr2 & I2C_SR2_GENCALL) { + dev->i2c_slave_msg->addr = 0; + } + /* We matched the secondary address */ + else if (sr2 & I2C_SR2_DUALF) { + dev->i2c_slave_msg->addr = dev->regs->OAR2 & 0xFE; + } + /* We matched the primary address */ + else if ((sr2 & I2C_SR2_DUALF) != I2C_SR2_DUALF) { + dev->i2c_slave_msg->addr = dev->regs->OAR1 & 0xFE; + } + /* Shouldn't get here */ + else { + dev->i2c_slave_msg->addr = -1; /* uh oh */ + } + + /* if we have buffered io */ + if ((dev->config_flags & I2C_SLAVE_USE_RX_BUFFER) || + (dev->config_flags & I2C_SLAVE_USE_TX_BUFFER)) { + + /* if receiving then this would be a repeated start + * + *if we have some bytes already + */ + if ((dev->state == I2C_STATE_SL_RX) && + (dev->i2c_slave_msg->xferred > 0) && + (dev->config_flags & I2C_SLAVE_USE_RX_BUFFER)) { + /* Call the callback with the contents of the data */ + if (dev->i2c_slave_recv_callback != NULL) { + (*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg); + } + } + + /* Reset the message back to defaults. + * We are starting a new message + */ + dev->i2c_slave_msg->flags = 0; + dev->i2c_slave_msg->length = 0; + dev->i2c_slave_msg->xferred = 0; + dev->msgs_left = 0; + dev->timestamp = systick_uptime(); + + /* We have been addressed with SLA+R so + * the master wants us to transmit + */ + if ((sr1 & I2C_SR1_TXE) && + (dev->config_flags & I2C_SLAVE_USE_TX_BUFFER)) { + /* Call the transmit callback so it can populate the msg + * data with the bytes to go + */ + if (dev->i2c_slave_transmit_callback != NULL) { + (*(dev->i2c_slave_transmit_callback))(dev->i2c_slave_msg); + } + } + dev->state = I2C_STATE_BUSY; + } + + sr1 = sr2 = 0; + } + + /* EV3: Master requesting data from slave. Transmit a byte*/ + if (sr1 & I2C_SR1_TXE) { + if (dev->config_flags & I2C_SLAVE_USE_TX_BUFFER) { + if (dev->i2c_slave_msg->xferred >= dev->i2c_slave_msg->length) { + /* End of the transmit buffer? If so we NACK */ + i2c_disable_ack(dev); + /* We have to either issue a STOP or write something here. + * STOP here seems to screw up some masters, + * For now padding with 0 + */ + i2c_write(dev, 0); + /*i2c_stop_condition(dev); // This is causing bus lockups way more than it should !? Seems some I2C master devices freak out here*/ + } + else + { + /* NACk the last byte */ + if (dev->i2c_slave_msg->xferred == dev->i2c_slave_msg->length-1) { + i2c_disable_ack(dev); + } + else { + i2c_enable_ack(dev); + } + i2c_write(dev, dev->i2c_slave_msg->data[dev->i2c_slave_msg->xferred++]); + } + } + else + { + /* Call the callback to get the data we need. + * The callback is expected to write using i2c_write(...) + * If the slave is going to terminate the transfer, this function should + * also do a NACK on the last byte! + */ + if (dev->i2c_slave_transmit_callback != NULL) (*(dev->i2c_slave_transmit_callback))(dev->i2c_slave_msg); + } + + dev->state = I2C_STATE_BUSY; + sr1 = sr2 = 0; + } + + /* EV2: Slave received data from a master. Get from DR */ + if (sr1 & I2C_SR1_RXNE) { + if (dev->config_flags & I2C_SLAVE_USE_RX_BUFFER) { + /* Fill the buffer with the contents of the data register */ + dev->i2c_slave_msg->data[dev->i2c_slave_msg->xferred++] = dev->regs->DR; + dev->i2c_slave_msg->length++; + } + else { + /* Call the callback with the contents of the data */ + dev->i2c_slave_msg->data[0] = dev->regs->DR; + if (dev->i2c_slave_recv_callback != NULL) (*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg); + } + dev->state = I2C_STATE_SL_RX; + sr1 = sr2 = 0; + } + + /* EV4: Slave has detected a STOP condition on the bus */ + if (sr1 & I2C_SR1_STOPF) { + dev->regs->CR1 |= I2C_CR1_PE; + + if ((dev->config_flags & I2C_SLAVE_USE_RX_BUFFER) || + (dev->config_flags & I2C_SLAVE_USE_TX_BUFFER)) { + + /* The callback with the data will happen on a NACK of the last data byte. + * This is handled in the error IRQ (AF bit) + */ + } + + sr1 = sr2 = 0; + dev->state = I2C_STATE_IDLE; + } + + return; + } + /* * EV5: Start condition sent */ @@ -447,6 +612,7 @@ void _i2c_irq_handler(i2c_dev *dev) { } } } + } /* @@ -456,10 +622,49 @@ void _i2c_irq_handler(i2c_dev *dev) { void _i2c_irq_error_handler(i2c_dev *dev) { I2C_CRUMB(ERROR_ENTRY, dev->regs->SR1, dev->regs->SR2); - dev->error_flags = dev->regs->SR2 & (I2C_SR1_BERR | + dev->error_flags = dev->regs->SR1 & (I2C_SR1_BERR | I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_OVR); + + /* Are we in slave mode? */ + if ((dev->regs->SR2 & I2C_SR2_MSL) != I2C_SR2_MSL) { + /* Check to see if the master device did a NAK on the last bit + * This is perfectly valid for a master to do this on the bus. + * We ignore this. Any further error processing takes us into dead + * loop waiting for the stop condition that will never arrive + */ + if (dev->regs->SR1 & I2C_SR1_AF) { + /* Clear flags */ + dev->regs->SR1 = 0; + dev->regs->SR2 = 0; + /* We need to write something to CR1 to clear the flag. + * This isn't really mentioned but seems important */ + i2c_enable_ack(dev); + + if (dev->state == I2C_STATE_SL_RX && + dev->config_flags & I2C_SLAVE_USE_RX_BUFFER && + dev->i2c_slave_msg->xferred > 0) { + /* Call the callback with the contents of the data */ + if (dev->i2c_slave_recv_callback != NULL) (*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg); + } + + dev->state = I2C_STATE_IDLE; + return; + } + /* Catch any other strange errors while in slave mode. + * I have seen BERR caused by an over fast master device + * as well as several overflows and arbitration failures. + * We are going to reset SR flags and carry on at this point which + * is not the best thing to do, but stops the bus locking up completely + * If we carry on below and send the stop bit, the code spins forever */ + /* Clear flags */ + dev->regs->SR1 = 0; + dev->regs->SR2 = 0; + dev->state = I2C_STATE_IDLE; + return; + } + /* Clear flags */ dev->regs->SR1 = 0; dev->regs->SR2 = 0; @@ -507,3 +712,31 @@ static void set_ccr_trise(i2c_dev *dev, uint32 flags) { i2c_set_clk_control(dev, ccr); i2c_set_trise(dev, trise); } + + +/** + * @brief callback for when the device acts as a slave. If using an rx buffer, this is triggered + * after the last byte, otherwise it is called for every incoming packet. + * @param dev I2C device + * @param msg The dev_msg to pass to the slave init code + * @param func The function pointer to call + */ +void i2c_slave_attach_recv_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_recv_callback_func func) { + dev->i2c_slave_recv_callback = func; + dev->i2c_slave_msg = msg; + msg->xferred = 0; +} + + +/** + * @brief callback for when the device acts as a slave. If using a tx buffer, this is triggered + * after the device is successsfully addressed with SLA+R. + * @param dev I2C device + * @param msg The dev_msg to pass to the slave init code + * @param func The function pointer to call + */ +void i2c_slave_attach_transmit_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_transmit_callback_func func) { + dev->i2c_slave_transmit_callback = func; + dev->i2c_slave_msg = msg; + msg->xferred = 0; +} diff --git a/libmaple/include/libmaple/i2c.h b/libmaple/include/libmaple/i2c.h index ff1c313..d4eac61 100644 --- a/libmaple/include/libmaple/i2c.h +++ b/libmaple/include/libmaple/i2c.h @@ -93,6 +93,7 @@ typedef struct i2c_msg { #define I2C_MSG_READ 0x1 #define I2C_MSG_10BIT_ADDR 0x2 + /** * Bitwise OR of: * - I2C_MSG_READ (write is default) @@ -197,6 +198,10 @@ typedef struct i2c_msg { #define I2C_DUTY_16_9 0x2 // 16/9 duty ratio /* Flag 0x4 is reserved; DO NOT USE. */ #define I2C_BUS_RESET 0x8 // Perform a bus reset +#define I2C_SLAVE_USE_RX_BUFFER 0x10 // Use a buffered message when doing a slave recv +#define I2C_SLAVE_USE_TX_BUFFER 0x20 // Use a buffered message when doing a slave transmit +#define I2C_SLAVE_DUAL_ADDRESS 0x40 // Enable the dual slave address scheme +#define I2C_SLAVE_GENERAL_CALL 0x80 // Enable the dual slave address scheme void i2c_master_enable(i2c_dev *dev, uint32 flags); #define I2C_ERROR_PROTOCOL (-1) @@ -406,6 +411,53 @@ static inline void i2c_set_trise(i2c_dev *dev, uint32 trise) { dev->regs->TRISE = trise; } +/* Barry Carter + * Slave support + */ + +/** + * @brief Enable Dual addressing mode to allow peripheral to have 2 addresses + * @param dev I2C device + */ +static inline void i2c_slave_dual_address_enable(i2c_dev *dev) { + dev->regs->OAR2 |= I2C_OAR2_ENDUAL; +} + +/** + * @brief Enable General Call to allow the unit to respond on addr 0x00 + * @param dev I2C device + */ +static inline void i2c_slave_general_call_enable(i2c_dev *dev) { + dev->regs->CR1 |= I2C_CR1_ENGC; +} + +/* callback functions */ +/* Callback handler for data received over the bus */ +void i2c_slave_attach_recv_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_recv_callback_func func); + +/* Callback handler for data being requested over the bus + * The callback function must call i2c_write to get the data over the bus + */ +void i2c_slave_attach_transmit_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_transmit_callback_func func); + +/** + * @brief Set the primary I2c slave address + * @param dev I2C device + * @param address the 8 or 10 bit i2c address + */ +static inline void i2c_slave_set_own_address(i2c_dev *dev, uint16 address) { + dev->regs->OAR1 = address <<1; +} + +/** + * @brief Set the secondary I2c slave address + * @param dev I2C device + * @param address the 8 or 10 bit i2c address + */ +static inline void i2c_slave_set_own_address2(i2c_dev *dev, uint16 address) { +dev->regs->OAR2 = (address <<1 ) | I2C_OAR2_ENDUAL; +} + #ifdef __cplusplus } #endif diff --git a/libmaple/include/libmaple/i2c_common.h b/libmaple/include/libmaple/i2c_common.h index 17cabe3..5debcb8 100644 --- a/libmaple/include/libmaple/i2c_common.h +++ b/libmaple/include/libmaple/i2c_common.h @@ -52,9 +52,12 @@ typedef enum i2c_state { I2C_STATE_IDLE = 1, /**< Idle */ I2C_STATE_XFER_DONE = 2, /**< Done with transfer */ I2C_STATE_BUSY = 3, /**< Busy */ + I2C_STATE_SL_RX = 4, /**< Slave receiving */ I2C_STATE_ERROR = -1 /**< Error occurred */ } i2c_state; +typedef void (*i2c_slave_recv_callback_func)(struct i2c_msg *); +typedef void (*i2c_slave_transmit_callback_func)(struct i2c_msg *); /** * @brief I2C device type. */ @@ -88,6 +91,17 @@ typedef struct i2c_dev { nvic_irq_num ev_nvic_line; /**< Event IRQ number */ nvic_irq_num er_nvic_line; /**< Error IRQ number */ volatile i2c_state state; /**< Device state */ + uint32 config_flags; /**< Configuration flags */ + + /* Barry Carter + * Slave implementation. Callback functions in this struct allow + * for a separate callback function for each I2C unit available onboard + */ + i2c_slave_transmit_callback_func i2c_slave_transmit_callback; + i2c_slave_recv_callback_func i2c_slave_recv_callback; + + struct i2c_msg *i2c_slave_msg; /* the message that the i2c slave will use */ + } i2c_dev; #endif -- cgit v1.2.3 From 576457f5477597c3bc88f06cbae01c01a459c32e Mon Sep 17 00:00:00 2001 From: Barry Carter Date: Tue, 4 Sep 2012 22:24:25 +0100 Subject: I2C slave support cleanups. Added fix for corner case where badly behaving master doesn't NACK and we don't get the callbacks fires. Removed my own name from several places and added attribution to the correct place. Updated include comments to reference the fact it now supports I2C slave Signed-off-by:- Barry Carter --- libmaple/i2c.c | 40 +++++++++++++++++++++++++++++----- libmaple/include/libmaple/i2c.h | 13 ++++++++--- libmaple/include/libmaple/i2c_common.h | 2 +- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/libmaple/i2c.c b/libmaple/i2c.c index b8e622d..6c609d9 100644 --- a/libmaple/i2c.c +++ b/libmaple/i2c.c @@ -28,9 +28,11 @@ /** * @file libmaple/i2c.c * @author Perry Hung + * @author Barry Carter * @brief Inter-Integrated Circuit (I2C) support. * - * Currently, only master mode is supported. + * Master and Slave supported + * Slave code added Barry Carter 2012 */ #include "i2c_private.h" @@ -218,6 +220,26 @@ void i2c_master_enable(i2c_dev *dev, uint32 flags) { dev->state = I2C_STATE_IDLE; } +/** + * @brief Initialize an I2C device as slave (and master) + * @param dev Device to enable + * @param flags Bitwise or of the following I2C options: + * I2C_FAST_MODE: 400 khz operation, + * I2C_DUTY_16_9: 16/9 Tlow/Thigh duty cycle (only applicable for + * fast mode), + * I2C_BUS_RESET: Reset the bus and clock out any hung slaves on + * initialization, + * I2C_10BIT_ADDRESSING: Enable 10-bit addressing, + * I2C_REMAP: (deprecated, STM32F1 only) Remap I2C1 to SCL/PB8 + * SDA/PB9. + * I2C_SLAVE_DUAL_ADDRESS: Slave can respond on 2 i2C addresses + * I2C_SLAVE_GENERAL_CALL: SLA+W broadcast to all general call + * listeners on bus. Addr 0x00 + * I2C_SLAVE_USE_RX_BUFFER: Use a buffer to receive the incoming + * data. Callback at end of recv + * I2C_SLAVE_USE_TX_BUFFER: Use a buffer to transmit data. + * Callback will be called before tx + */ void i2c_slave_enable(i2c_dev *dev, uint32 flags) { i2c_disable(dev); i2c_master_enable(dev, dev->config_flags | flags); @@ -322,11 +344,10 @@ void _i2c_irq_handler(i2c_dev *dev) { */ dev->timestamp = systick_uptime(); - /* Add Slave support - * Barry Carter 2012 - * barry.carter@gmail.com + /* + * Add Slave support */ - + /* Check to see if MSL master slave bit is set */ if ((sr2 & I2C_SR2_MSL) != I2C_SR2_MSL) { /* 0 = slave mode 1 = master */ @@ -459,6 +480,15 @@ void _i2c_irq_handler(i2c_dev *dev) { /* The callback with the data will happen on a NACK of the last data byte. * This is handled in the error IRQ (AF bit) */ + /* Handle the case where the master misbehaves by sending no NACK */ + if (dev->state != I2C_STATE_IDLE) { + if (dev->state == I2C_STATE_SL_RX) { + if (dev->i2c_slave_recv_callback != NULL) (*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg); + } + else { + if (dev->i2c_slave_transmit_callback != NULL) (*(dev->i2c_slave_transmit_callback))(dev->i2c_slave_msg); + } + } } sr1 = sr2 = 0; diff --git a/libmaple/include/libmaple/i2c.h b/libmaple/include/libmaple/i2c.h index d4eac61..5a9da58 100644 --- a/libmaple/include/libmaple/i2c.h +++ b/libmaple/include/libmaple/i2c.h @@ -29,12 +29,19 @@ * @file libmaple/include/libmaple/i2c.h * @brief Inter-Integrated Circuit (I2C) peripheral support * - * Currently master-only. Usage notes: + * Supports Master and Slave. + * Master Usage notes: * * - Enable an I2C device with i2c_master_enable(). * - Initialize an array of struct i2c_msg to suit the bus * transactions (reads/writes) you wish to perform. * - Call i2c_master_xfer() to do the work. + * + * Slave Usage notes: + * - Enable I2C slave by calling i2c_slave_enable(). + * Check flags for usage. Enabling master also enabled slave. + * - initialise the i2c_msg struct and the data buffer + * - initialise the callback functions */ #ifndef _LIBMAPLE_I2C_H_ @@ -201,7 +208,7 @@ typedef struct i2c_msg { #define I2C_SLAVE_USE_RX_BUFFER 0x10 // Use a buffered message when doing a slave recv #define I2C_SLAVE_USE_TX_BUFFER 0x20 // Use a buffered message when doing a slave transmit #define I2C_SLAVE_DUAL_ADDRESS 0x40 // Enable the dual slave address scheme -#define I2C_SLAVE_GENERAL_CALL 0x80 // Enable the dual slave address scheme +#define I2C_SLAVE_GENERAL_CALL 0x80 // Enable the general call on address 0x00 void i2c_master_enable(i2c_dev *dev, uint32 flags); #define I2C_ERROR_PROTOCOL (-1) @@ -411,7 +418,7 @@ static inline void i2c_set_trise(i2c_dev *dev, uint32 trise) { dev->regs->TRISE = trise; } -/* Barry Carter +/* * Slave support */ diff --git a/libmaple/include/libmaple/i2c_common.h b/libmaple/include/libmaple/i2c_common.h index 5debcb8..93e17e2 100644 --- a/libmaple/include/libmaple/i2c_common.h +++ b/libmaple/include/libmaple/i2c_common.h @@ -93,7 +93,7 @@ typedef struct i2c_dev { volatile i2c_state state; /**< Device state */ uint32 config_flags; /**< Configuration flags */ - /* Barry Carter + /* * Slave implementation. Callback functions in this struct allow * for a separate callback function for each I2C unit available onboard */ -- cgit v1.2.3 From 0d8f8210e5decb4870f77b5cd0e5325cb803a3af Mon Sep 17 00:00:00 2001 From: Barry Carter Date: Wed, 5 Sep 2012 00:08:10 +0100 Subject: Added I2C slave echo example in examples folder. Using another maple, write a byte and then read. Slight tidy up. Reformatted CREDITS file to be in correct order. Added a note about buffer overrun Signed-off-by:- Barry Carter --- CREDITS | 12 ++--- examples/i2c_slave_example.cpp | 110 ++++++++++++++++++++++++++++++++++++++++ libmaple/i2c.c | 4 ++ libmaple/include/libmaple/i2c.h | 1 + 4 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 examples/i2c_slave_example.cpp diff --git a/CREDITS b/CREDITS index 5918e01..0fa915a 100644 --- a/CREDITS +++ b/CREDITS @@ -18,6 +18,11 @@ E: mbolivar@leaflabs.com D: Current libmaple maintainer D: Random libmaple hacks +N: Barry Carter ("ginge") +E: barry.carter@gmail.com +D: Added I2C slave support +W: headfuzz.co.uk + N: Anton Eltchaninov E: anton.eltchaninov@gmail.com D: STM32F1 value line support @@ -81,9 +86,4 @@ W: http://sarup.dk/index.html N: Andy Scott ("xttocs") E: andy.g.scott@gmail.com D: LiquidCrystal library - -N: Barry Carter ("ginge") -E: barry.carter@gmail.com -D: Added i2C slave support -W: headfuzz.co.uk - \ No newline at end of file + diff --git a/examples/i2c_slave_example.cpp b/examples/i2c_slave_example.cpp new file mode 100644 index 0000000..98da58c --- /dev/null +++ b/examples/i2c_slave_example.cpp @@ -0,0 +1,110 @@ +/* + * i2c_slave example.cpp + * + * Created on: 4 Sep 2012 + * Author: Barry Carter + */ +#include +#include + +#define USE_BUFFERED_EXAMPLE 1 + +i2c_msg msg; +i2c_msg *pmsg = &msg; +uint8 buffer[255]; + +uint8 value_to_print = 'A'; + +void funcrx(i2c_msg *msg) +{ + // Received length will be in msg->length + char return_data = msg->data[0]; + value_to_print = return_data; +} + +#if USE_BUFFERED_EXAMPLE == 1 +/* We ARE using a buffer to transmit the data out. + * Make sure you fill the buffer with the data AND you set the length correctly + */ +void functx(i2c_msg *msg) +{ + // Cheeky. We are using the received byte of the data which is currently in + // byte 0 to echo it back to the master device + //msg->data[0] = 0x01; // We are re-using the rx buffer here to echo the request back + msg->data[1] = 0x02; + msg->data[2] = 0x03; + msg->data[3] = 0x04; + msg->data[4] = 0x05; + msg->length = 5; +} + +#else + +/* We are NOT using the buffered data transmission + * We will get this callback for each outgoing packet. Make sure to call i2c_write + * Strickly speaking, we should be sending a NACk on the last byte we want to send + * but for this test example I am going to assume the master will NACK it when it + * wants to stop. + */ +void functx(i2c_msg *msg) +{ + i2c_write(I2C1, msg->data[0]); +} + +#endif + + +void setup() { + SerialUSB.begin(); + SerialUSB.println("I2C Slave example"); + + // attach the buffer + msg.data = buffer; + + /* Init slave mode. Enables master too + * We are going to configure the slave device to + * - enable fast I2C (400khz) + * - dual addresses (can have 2 addresses per module) + * general call (accepts data writes to 0x00 on a broadcast basis) + * + * If the buffered example is enabled, then we also enable the + * buffer for rx and tx. + * Note you can independently enable/disable RX and TX buffers to + * allow a buffered read and direct writes. Useful if you don't know how + * much the master will read. + */ +#if USE_BUFFERED_EXAMPLE == 1 + i2c_slave_enable(I2C1, I2C_FAST_MODE | I2C_SLAVE_DUAL_ADDRESS | I2C_SLAVE_GENERAL_CALL | I2C_SLAVE_USE_RX_BUFFER | I2C_SLAVE_USE_TX_BUFFER); +#else + i2c_slave_enable(I2C1, I2C_FAST_MODE | I2C_SLAVE_DUAL_ADDRESS | I2C_SLAVE_GENERAL_CALL); +#endif + + // attach receive handler + i2c_slave_attach_recv_handler(I2C1, &msg, funcrx); + // attach transmit handler + i2c_slave_attach_transmit_handler(I2C1, &msg, functx); + + // set addresss 1 to 16 + i2c_slave_set_own_address(I2C1, 0x10); + // set addresss 2 to 32 + i2c_slave_set_own_address2(I2C1, 0x20); +} + +void loop() { + SerialUSB.print("Last byte: "); + SerialUSB.println(value_to_print); +} + +// Force init() to be called before anything else. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/libmaple/i2c.c b/libmaple/i2c.c index 6c609d9..bbbf123 100644 --- a/libmaple/i2c.c +++ b/libmaple/i2c.c @@ -458,6 +458,10 @@ void _i2c_irq_handler(i2c_dev *dev) { if (sr1 & I2C_SR1_RXNE) { if (dev->config_flags & I2C_SLAVE_USE_RX_BUFFER) { /* Fill the buffer with the contents of the data register */ + /* These is potential for buffer overflow here, so we should + * really store the size of the array. This is expensive in + * the ISR so left out for now. We must trust the implementor! + */ dev->i2c_slave_msg->data[dev->i2c_slave_msg->xferred++] = dev->regs->DR; dev->i2c_slave_msg->length++; } diff --git a/libmaple/include/libmaple/i2c.h b/libmaple/include/libmaple/i2c.h index 5a9da58..fbb4c09 100644 --- a/libmaple/include/libmaple/i2c.h +++ b/libmaple/include/libmaple/i2c.h @@ -210,6 +210,7 @@ typedef struct i2c_msg { #define I2C_SLAVE_DUAL_ADDRESS 0x40 // Enable the dual slave address scheme #define I2C_SLAVE_GENERAL_CALL 0x80 // Enable the general call on address 0x00 void i2c_master_enable(i2c_dev *dev, uint32 flags); +void i2c_slave_enable(i2c_dev *dev, uint32 flags); #define I2C_ERROR_PROTOCOL (-1) #define I2C_ERROR_TIMEOUT (-2) -- cgit v1.2.3