From da01c80f24dc9c43ff951ea9fd283abb4628ceb7 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 39cd07a640ee87b7937738d36f17019b2865bfae 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 594724951553a882758c11497a4f03828b4f43e6 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 From f55297e7a3f004d958a49dde302cd51f6893fc48 Mon Sep 17 00:00:00 2001 From: Magnus Lundin Date: Fri, 3 May 2013 19:08:00 -0400 Subject: Use linker symbol __text_start__ to setup base of nvic vector table. Signed-off-by: Magnus Lundin Signed-off-by: Marti Bolivar --- wirish/boards.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/wirish/boards.cpp b/wirish/boards.cpp index dbc308e..a693fa6 100644 --- a/wirish/boards.cpp +++ b/wirish/boards.cpp @@ -143,13 +143,10 @@ static void setup_clocks(void) { * These addresses are where usercode starts when a bootloader is * present. If no bootloader is present, the user NVIC usually starts * at the Flash base address, 0x08000000. - * - * FIXME Let the build specify the vector table address numerically to - * avoid having these magic values -- some people have been fixing up - * the bootloader so it uses less space. */ #define USER_ADDR_ROM 0x08005000 #define USER_ADDR_RAM 0x20000C00 +extern char __text_start__; static void setup_nvic(void) { #ifdef VECT_TAB_FLASH @@ -158,8 +155,13 @@ static void setup_nvic(void) { nvic_init(USER_ADDR_RAM, 0); #elif defined VECT_TAB_BASE nvic_init((uint32)0x08000000, 0); +#elif defined VECT_TAB_ADDR + // A numerically supplied value + nvic_init((uint32)VECT_TAB_ADDR, 0); #else -#error "You must select a base address for the vector table." + // Use the __text_start__ value from the linker script; this + // should be the start of the vector table. + nvic_init((uint32)&__text_start__, 0); #endif } -- cgit v1.2.3 From 85ba23ad6ea78a72f95566a2254ea625ac3ac6f9 Mon Sep 17 00:00:00 2001 From: mlundinse Date: Sun, 12 May 2013 11:49:04 +0200 Subject: Remove unnecessaryd extra_libs.inc --- support/ld/extra_libs.inc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 support/ld/extra_libs.inc diff --git a/support/ld/extra_libs.inc b/support/ld/extra_libs.inc deleted file mode 100644 index 1216f97..0000000 --- a/support/ld/extra_libs.inc +++ /dev/null @@ -1 +0,0 @@ -/* toolchain specific stuff */ -- cgit v1.2.3 From ffa57f5f4ac77ab25cafc5cd93dbd705753790a0 Mon Sep 17 00:00:00 2001 From: Daniel Nyström Date: Mon, 27 May 2013 15:14:51 +0200 Subject: libmaple/timer.c: Sync enable_irq() declaration with it's definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Nyström --- libmaple/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmaple/timer.c b/libmaple/timer.c index 24ae7fa..c22fe9d 100644 --- a/libmaple/timer.c +++ b/libmaple/timer.c @@ -38,7 +38,7 @@ static void disable_channel(timer_dev *dev, uint8 channel); static void pwm_mode(timer_dev *dev, uint8 channel); static void output_compare_mode(timer_dev *dev, uint8 channel); -static inline void enable_irq(timer_dev *dev, uint8 interrupt); +static inline void enable_irq(timer_dev *dev, timer_interrupt_id iid); /* * Devices -- cgit v1.2.3 From ce07837e3231fb59f20219467b043a0d13652206 Mon Sep 17 00:00:00 2001 From: Daniel Nyström Date: Mon, 27 May 2013 15:16:12 +0200 Subject: wirish/syscalls.c: Replace obsolete caddr_t with void * MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit caddr_t is obsolete in POSIX and thus unavailable in most modern toolchains. caddr_t usage should be replaced by void *. stddef.h includes the size_t typedef which was missing. Signed-off-by: Daniel Nyström --- wirish/syscalls.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/wirish/syscalls.c b/wirish/syscalls.c index a3b6d2c..d5f2d9f 100644 --- a/wirish/syscalls.c +++ b/wirish/syscalls.c @@ -37,17 +37,18 @@ #include #include +#include /* If CONFIG_HEAP_START (or CONFIG_HEAP_END) isn't defined, then * assume _lm_heap_start (resp. _lm_heap_end) is appropriately set by * the linker */ #ifndef CONFIG_HEAP_START extern char _lm_heap_start; -#define CONFIG_HEAP_START ((caddr_t)&_lm_heap_start) +#define CONFIG_HEAP_START ((void *)&_lm_heap_start) #endif #ifndef CONFIG_HEAP_END extern char _lm_heap_end; -#define CONFIG_HEAP_END ((caddr_t)&_lm_heap_end) +#define CONFIG_HEAP_END ((void *)&_lm_heap_end) #endif /* @@ -56,9 +57,9 @@ extern char _lm_heap_end; * Get incr bytes more RAM (for use by the heap). malloc() and * friends call this function behind the scenes. */ -caddr_t _sbrk(int incr) { - static caddr_t pbreak = NULL; /* current program break */ - caddr_t ret; +void *_sbrk(int incr) { + static void * pbreak = NULL; /* current program break */ + void * ret; if (pbreak == NULL) { pbreak = CONFIG_HEAP_START; @@ -67,7 +68,7 @@ caddr_t _sbrk(int incr) { if ((CONFIG_HEAP_END - pbreak < incr) || (pbreak - CONFIG_HEAP_START < -incr)) { errno = ENOMEM; - return (caddr_t)-1; + return (void *)-1; } ret = pbreak; -- cgit v1.2.3 From 4ab37d27d389519a9b35b12fa1b0ee94f5fe23e3 Mon Sep 17 00:00:00 2001 From: Daniel Nyström Date: Mon, 27 May 2013 15:19:59 +0200 Subject: build-rules.mk: Add CROSS_COMPILE env var support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since toolchains other then older Code Sourcery (Mentor Graphics) might not use the arm-none-eabi triplet, make it possible to use any triplet by just setting the CROSS_COMPILE environment variable accordingly. Signed-off-by: Daniel Nyström --- support/make/build-rules.mk | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/support/make/build-rules.mk b/support/make/build-rules.mk index f852cab..eec986f 100644 --- a/support/make/build-rules.mk +++ b/support/make/build-rules.mk @@ -1,13 +1,15 @@ # Useful tools -CC := arm-none-eabi-gcc -CXX := arm-none-eabi-g++ -LD := arm-none-eabi-ld -v -AR := arm-none-eabi-ar -AS := arm-none-eabi-gcc -OBJCOPY := arm-none-eabi-objcopy -DISAS := arm-none-eabi-objdump -OBJDUMP := arm-none-eabi-objdump -SIZE := arm-none-eabi-size +CROSS_COMPILE ?= arm-none-eabi- + +CC := $(CROSS_COMPILE)gcc +CXX := $(CROSS_COMPILE)g++ +LD := $(CROSS_COMPILE)ld -v +AR := $(CROSS_COMPILE)ar +AS := $(CROSS_COMPILE)gcc +OBJCOPY := $(CROSS_COMPILE)objcopy +DISAS := $(CROSS_COMPILE)objdump +OBJDUMP := $(CROSS_COMPILE)objdump +SIZE := $(CROSS_COMPILE)size DFU := dfu-util # Suppress annoying output unless V is set -- cgit v1.2.3