mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #12161 from AGlass0fMilk/nrf-i2c-slave
nRF52 I2CSlave Implementationpull/12778/head
commit
098c72a312
|
@ -34,39 +34,95 @@ namespace mbed {
|
|||
*
|
||||
* @note Synchronization level: Not protected
|
||||
*
|
||||
* Example Simple I2C responder:
|
||||
* Example Simple I2C slave and master (requires two Mbed-boards):
|
||||
* @code
|
||||
* #include <mbed.h>
|
||||
* #include <mbed_wait_api.h>
|
||||
* #include <string.h>
|
||||
*
|
||||
* const int SLAVE_ADDRESS = 0xA0;
|
||||
* const char message[] = "Slave!";
|
||||
* #define BUILD_I2C_SLAVE 1 // Build for slave or master of this example
|
||||
*
|
||||
* #define SLAVE_ADDR 0xA0
|
||||
* #define BUFFER_SIZE 6
|
||||
*
|
||||
* #if BUILD_I2C_SLAVE
|
||||
*
|
||||
* // Slave side of the example
|
||||
*
|
||||
* #if !DEVICE_I2CSLAVE
|
||||
* #error [NOT_SUPPORTED] I2C Slave is not supported
|
||||
* #endif
|
||||
*
|
||||
* I2CSlave slave(I2C_SDA, I2C_SCL);
|
||||
*
|
||||
* int main() {
|
||||
* slave.address(SLAVE_ADDRESS);
|
||||
*
|
||||
* char buf[BUFFER_SIZE] = "ABCDE";
|
||||
*
|
||||
* slave.address(SLAVE_ADDR);
|
||||
* while (1) {
|
||||
* int operation = slave.receive();
|
||||
* switch (operation) {
|
||||
* int i = slave.receive();
|
||||
* switch (i) {
|
||||
* case I2CSlave::ReadAddressed:
|
||||
* int status = slave.write(message, sizeof(message));
|
||||
* if (status == 0) {
|
||||
* printf("Written message: %s\n", message);
|
||||
* } else {
|
||||
* printf("Failed to write message.\n");
|
||||
* }
|
||||
* // Write back the buffer from the master
|
||||
* slave.write(buf, BUFFER_SIZE);
|
||||
* printf("Written to master (addressed): %s\n", buf);
|
||||
* break;
|
||||
*
|
||||
* case I2CSlave::WriteGeneral:
|
||||
* int byte_read = slave.read();
|
||||
* printf("Read General: %c (%d)\n", byte_read, byte_read);
|
||||
* slave.read(buf, BUFFER_SIZE);
|
||||
* printf("Read from master (general): %s\n", buf);
|
||||
* break;
|
||||
*
|
||||
* case I2CSlave::WriteAddressed:
|
||||
* int byte_read = slave.read();
|
||||
* printf("Read Addressed: %c (%d)\n", byte_read, byte_read);
|
||||
* slave.read(buf, BUFFER_SIZE);
|
||||
* printf("Read from master (addressed): %s\n", buf);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* #else
|
||||
*
|
||||
* // Master side of the example
|
||||
*
|
||||
* I2C master(I2C_SDA, I2C_SCL);
|
||||
*
|
||||
* static const char* to_send[] = { "abcde", "12345", "EFGHI" };
|
||||
*
|
||||
* int main() {
|
||||
* char buf[BUFFER_SIZE];
|
||||
* int send_index = 0;
|
||||
*
|
||||
* while (1) {
|
||||
* strcpy(buf, to_send[send_index]);
|
||||
*
|
||||
* // Write the new message to the slave
|
||||
* if (master.write(SLAVE_ADDR, buf, BUFFER_SIZE)) {
|
||||
* printf("Failed to write to slave!\n");
|
||||
* } else {
|
||||
* printf("Written to slave: %s\n", buf);
|
||||
* }
|
||||
*
|
||||
* // Read what the slave has (should be identical)
|
||||
* if (master.read(SLAVE_ADDR, buf, BUFFER_SIZE)) {
|
||||
* printf("Failed to read from slave!\n");
|
||||
* } else {
|
||||
* printf("Read from slave: %s\n", buf);
|
||||
* }
|
||||
*
|
||||
* // Change the message we're writing to the slave
|
||||
* send_index++;
|
||||
* if (send_index > 2) {
|
||||
* send_index = 0;
|
||||
* }
|
||||
*
|
||||
* wait_us(500000); // Wait 0.5s
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* #endif
|
||||
*
|
||||
* @endcode
|
||||
*/
|
||||
class I2CSlave {
|
||||
|
|
|
@ -3908,20 +3908,20 @@
|
|||
// <e> NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver
|
||||
//==========================================================
|
||||
#ifndef NRFX_TWIS_ENABLED
|
||||
#define NRFX_TWIS_ENABLED 0
|
||||
#define NRFX_TWIS_ENABLED 1
|
||||
#endif
|
||||
// <q> NRFX_TWIS0_ENABLED - Enable TWIS0 instance
|
||||
|
||||
|
||||
#ifndef NRFX_TWIS0_ENABLED
|
||||
#define NRFX_TWIS0_ENABLED 0
|
||||
#define NRFX_TWIS0_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> NRFX_TWIS1_ENABLED - Enable TWIS1 instance
|
||||
|
||||
|
||||
#ifndef NRFX_TWIS1_ENABLED
|
||||
#define NRFX_TWIS1_ENABLED 0
|
||||
#define NRFX_TWIS1_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only once
|
||||
|
|
|
@ -3908,20 +3908,20 @@
|
|||
// <e> NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver
|
||||
//==========================================================
|
||||
#ifndef NRFX_TWIS_ENABLED
|
||||
#define NRFX_TWIS_ENABLED 0
|
||||
#define NRFX_TWIS_ENABLED 1
|
||||
#endif
|
||||
// <q> NRFX_TWIS0_ENABLED - Enable TWIS0 instance
|
||||
|
||||
|
||||
#ifndef NRFX_TWIS0_ENABLED
|
||||
#define NRFX_TWIS0_ENABLED 0
|
||||
#define NRFX_TWIS0_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> NRFX_TWIS1_ENABLED - Enable TWIS1 instance
|
||||
|
||||
|
||||
#ifndef NRFX_TWIS1_ENABLED
|
||||
#define NRFX_TWIS1_ENABLED 0
|
||||
#define NRFX_TWIS1_ENABLED 1
|
||||
#endif
|
||||
|
||||
// <q> NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only once
|
||||
|
|
|
@ -59,6 +59,10 @@
|
|||
#include "app_util_platform.h"
|
||||
#include "prs/nrfx_prs.h"
|
||||
|
||||
#if DEVICE_I2CSLAVE
|
||||
#include "nrfx_twis.h"
|
||||
#endif
|
||||
|
||||
#define TWI_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \
|
||||
NRF_GPIO_PIN_DIR_INPUT, \
|
||||
NRF_GPIO_PIN_INPUT_CONNECT, \
|
||||
|
@ -135,6 +139,15 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl)
|
|||
config->mask = 0;
|
||||
#endif
|
||||
|
||||
#if DEVICE_I2CSLAVE
|
||||
/** was_slave is used to decide which driver call we need
|
||||
* to use when uninitializing a given instance
|
||||
*/
|
||||
config->was_slave = false;
|
||||
config->is_slave = false;
|
||||
config->slave_addr = 0;
|
||||
#endif
|
||||
|
||||
/* Force reconfiguration */
|
||||
config->update = true;
|
||||
|
||||
|
@ -164,6 +177,14 @@ void i2c_frequency(i2c_t *obj, int hz)
|
|||
struct i2c_s *config = obj;
|
||||
#endif
|
||||
|
||||
|
||||
#if DEVICE_I2CSLAVE
|
||||
/* Slaves automatically get frequency from master, may be 100kHz or 400kHz */
|
||||
if(config->is_slave) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Round down to nearest valid frequency. */
|
||||
nrf_twi_frequency_t new_frequency;
|
||||
|
||||
|
@ -588,6 +609,10 @@ void i2c_reset(i2c_t *obj)
|
|||
/* Global array holding driver configuration for easy access. */
|
||||
static const nrfx_twi_t nordic_nrf5_instance[2] = { NRFX_TWI_INSTANCE(0), NRFX_TWI_INSTANCE(1) };
|
||||
|
||||
#if DEVICE_I2CSLAVE
|
||||
static const nrfx_twis_t nordic_nrf5_twis_instance[2] = { NRFX_TWIS_INSTANCE(0), NRFX_TWIS_INSTANCE(1) };
|
||||
#endif
|
||||
|
||||
/* Forward declare interrupt handler. */
|
||||
#if DEVICE_I2C_ASYNCH
|
||||
static void nordic_nrf5_twi_event_handler(nrfx_twi_evt_t const *p_event, void *p_context);
|
||||
|
@ -632,51 +657,90 @@ static void i2c_configure_driver_instance(i2c_t *obj)
|
|||
|
||||
/* If the peripheral is already running, then disable it and use the driver API to uninitialize it.*/
|
||||
if (nordic_nrf5_instance[instance].p_twi->ENABLE) {
|
||||
#if DEVICE_I2CSLAVE
|
||||
/* If it was a slave, we should disable it with the appropriate driver */
|
||||
if(config->was_slave) {
|
||||
nrfx_twis_disable(&nordic_nrf5_twis_instance[instance]);
|
||||
nrfx_twis_uninit(&nordic_nrf5_twis_instance[instance]);
|
||||
}
|
||||
else {
|
||||
nrfx_twi_disable(&nordic_nrf5_instance[instance]);
|
||||
nrfx_twi_uninit(&nordic_nrf5_instance[instance]);
|
||||
}
|
||||
|
||||
#else
|
||||
nrfx_twi_disable(&nordic_nrf5_instance[instance]);
|
||||
nrfx_twi_uninit(&nordic_nrf5_instance[instance]);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* Force resource release. This is necessary because mbed drivers don't
|
||||
* deinitialize on object destruction.
|
||||
*/
|
||||
NRFX_IRQ_DISABLE((nrfx_get_irq_number((void const *)nordic_nrf5_twi_register[instance])));
|
||||
|
||||
/* Configure driver with new settings. */
|
||||
nrfx_twi_config_t twi_config = {
|
||||
.scl = config->scl,
|
||||
.sda = config->sda,
|
||||
.frequency = config->frequency,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_LOWEST,
|
||||
.hold_bus_uninit = false
|
||||
};
|
||||
#if DEVICE_I2CSLAVE
|
||||
if(config->is_slave) {
|
||||
/* Configure driver as slave with new settings. */
|
||||
nrfx_twis_config_t twis_config = {
|
||||
.addr = { (config->slave_addr >> 1), 0 },
|
||||
.scl = config->scl,
|
||||
.sda = config->sda,
|
||||
.scl_pull = NRF_GPIO_PIN_NOPULL,
|
||||
.sda_pull = NRF_GPIO_PIN_NOPULL,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_LOWEST
|
||||
};
|
||||
/* Initialze driver in blocking mode. */
|
||||
nrfx_twis_init(&nordic_nrf5_twis_instance[instance],
|
||||
&twis_config,
|
||||
NULL);
|
||||
|
||||
/* Enable peripheral (nrfx_twi_t and nrfx_twis_t are interchangeable). */
|
||||
nrfx_twis_enable(&nordic_nrf5_twis_instance[instance]);
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
|
||||
/* Configure driver with new settings. */
|
||||
nrfx_twi_config_t twi_config = {
|
||||
.scl = config->scl,
|
||||
.sda = config->sda,
|
||||
.frequency = config->frequency,
|
||||
.interrupt_priority = APP_IRQ_PRIORITY_LOWEST,
|
||||
.hold_bus_uninit = false
|
||||
};
|
||||
|
||||
#if DEVICE_I2C_ASYNCH
|
||||
/* Set callback handler in asynchronous mode. */
|
||||
if (config->handler) {
|
||||
/* Set callback handler in asynchronous mode. */
|
||||
if (config->handler) {
|
||||
|
||||
/* Initialze driver in non-blocking mode. */
|
||||
nrfx_twi_init(&nordic_nrf5_instance[instance],
|
||||
&twi_config,
|
||||
nordic_nrf5_twi_event_handler,
|
||||
obj);
|
||||
} else {
|
||||
/* Initialze driver in non-blocking mode. */
|
||||
nrfx_twi_init(&nordic_nrf5_instance[instance],
|
||||
&twi_config,
|
||||
nordic_nrf5_twi_event_handler,
|
||||
obj);
|
||||
} else {
|
||||
|
||||
/* Initialze driver in blocking mode. */
|
||||
nrfx_twi_init(&nordic_nrf5_instance[instance],
|
||||
&twi_config,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
#else
|
||||
/* Initialze driver in blocking mode. */
|
||||
nrfx_twi_init(&nordic_nrf5_instance[instance],
|
||||
&twi_config,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
#else
|
||||
/* Initialze driver in blocking mode. */
|
||||
nrfx_twi_init(&nordic_nrf5_instance[instance],
|
||||
&twi_config,
|
||||
NULL,
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
/* Enable peripheral. */
|
||||
nrfx_twi_enable(&nordic_nrf5_instance[instance]);
|
||||
/* Enable peripheral. */
|
||||
nrfx_twi_enable(&nordic_nrf5_instance[instance]);
|
||||
#if DEVICE_I2CSLAVE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -945,7 +1009,6 @@ void i2c_abort_asynch(i2c_t *obj)
|
|||
#endif // DEVICE_I2C_ASYNCH
|
||||
|
||||
#if DEVICE_I2CSLAVE
|
||||
#warning DEVICE_I2CSLAVE
|
||||
|
||||
/***
|
||||
* _____ ___ _____ _____ _
|
||||
|
@ -965,6 +1028,25 @@ void i2c_abort_asynch(i2c_t *obj)
|
|||
*/
|
||||
void i2c_slave_mode(i2c_t *obj, int enable_slave)
|
||||
{
|
||||
#if DEVICE_I2C_ASYNCH
|
||||
struct i2c_s *config = &obj->i2c;
|
||||
#else
|
||||
struct i2c_s *config = obj;
|
||||
#endif
|
||||
|
||||
/* Reconfigure this instance as an I2C slave */
|
||||
|
||||
/* Not configured yet, so this instance is and was a slave */
|
||||
if(config->state == NRFX_DRV_STATE_UNINITIALIZED) {
|
||||
config->was_slave = enable_slave;
|
||||
} else {
|
||||
config->was_slave = config->is_slave;
|
||||
}
|
||||
config->is_slave = enable_slave;
|
||||
config->update = true;
|
||||
|
||||
i2c_configure_driver_instance(obj);
|
||||
|
||||
DEBUG_PRINTF("i2c_slave_mode\r\n");
|
||||
}
|
||||
|
||||
|
@ -977,6 +1059,22 @@ int i2c_slave_receive(i2c_t *obj)
|
|||
{
|
||||
DEBUG_PRINTF("i2c_slave_receive\r\n");
|
||||
|
||||
#if DEVICE_I2C_ASYNCH
|
||||
struct i2c_s *config = &obj->i2c;
|
||||
#else
|
||||
struct i2c_s *config = obj;
|
||||
#endif
|
||||
|
||||
const nrfx_twis_t* twis_instance = &nordic_nrf5_twis_instance[config->instance];
|
||||
|
||||
if(nrfx_twis_is_waiting_rx_buff(twis_instance)) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(nrfx_twis_is_waiting_tx_buff(twis_instance)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -990,7 +1088,26 @@ int i2c_slave_read(i2c_t *obj, char *data, int length)
|
|||
{
|
||||
DEBUG_PRINTF("i2c_slave_read\r\n");
|
||||
|
||||
return 0;
|
||||
#if DEVICE_I2C_ASYNCH
|
||||
struct i2c_s *config = &obj->i2c;
|
||||
#else
|
||||
struct i2c_s *config = obj;
|
||||
#endif
|
||||
|
||||
const nrfx_twis_t* twis_instance = &nordic_nrf5_twis_instance[config->instance];
|
||||
|
||||
/* Wait until the master is trying to write data */
|
||||
while(!nrfx_twis_is_waiting_rx_buff(twis_instance)) { }
|
||||
|
||||
/* Master is attempting to write, now we prepare the rx buffer */
|
||||
nrfx_twis_rx_prepare(twis_instance,
|
||||
data,
|
||||
length);
|
||||
|
||||
/* Wait until the transaction is over */
|
||||
while(nrfx_twis_is_pending_rx(twis_instance)) { }
|
||||
|
||||
return nrfx_twis_rx_amount(twis_instance);
|
||||
}
|
||||
|
||||
/** Configure I2C as slave or master.
|
||||
|
@ -1003,7 +1120,26 @@ int i2c_slave_write(i2c_t *obj, const char *data, int length)
|
|||
{
|
||||
DEBUG_PRINTF("i2c_slave_write\r\n");
|
||||
|
||||
return 0;
|
||||
#if DEVICE_I2C_ASYNCH
|
||||
struct i2c_s *config = &obj->i2c;
|
||||
#else
|
||||
struct i2c_s *config = obj;
|
||||
#endif
|
||||
|
||||
const nrfx_twis_t* twis_instance = &nordic_nrf5_twis_instance[config->instance];
|
||||
|
||||
/* Wait until the master is trying to read data */
|
||||
while(!nrfx_twis_is_waiting_tx_buff(twis_instance)) { }
|
||||
|
||||
/* Master is attempting to read, now we prepare the tx buffer */
|
||||
nrfx_twis_tx_prepare(twis_instance,
|
||||
data,
|
||||
length);
|
||||
|
||||
/* Wait until the transaction is over */
|
||||
while(nrfx_twis_is_pending_tx(twis_instance)) { }
|
||||
|
||||
return nrfx_twis_tx_amount(twis_instance);
|
||||
}
|
||||
|
||||
/** Configure I2C address.
|
||||
|
@ -1014,6 +1150,18 @@ int i2c_slave_write(i2c_t *obj, const char *data, int length)
|
|||
*/
|
||||
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
|
||||
{
|
||||
#if DEVICE_I2C_ASYNCH
|
||||
struct i2c_s *config = &obj->i2c;
|
||||
#else
|
||||
struct i2c_s *config = obj;
|
||||
#endif
|
||||
|
||||
/* Reconfigure this instance as an I2C slave with given address */
|
||||
config->update = true;
|
||||
config->slave_addr = (uint8_t) address;
|
||||
|
||||
i2c_configure_driver_instance(obj);
|
||||
|
||||
DEBUG_PRINTF("i2c_slave_address\r\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,12 @@ struct i2c_s {
|
|||
uint32_t mask;
|
||||
uint32_t event;
|
||||
#endif
|
||||
|
||||
#if DEVICE_I2CSLAVE
|
||||
bool was_slave;
|
||||
bool is_slave;
|
||||
uint8_t slave_addr;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct analogin_s {
|
||||
|
|
|
@ -10584,6 +10584,7 @@
|
|||
"FLASH",
|
||||
"I2C",
|
||||
"I2C_ASYNCH",
|
||||
"I2CSLAVE",
|
||||
"INTERRUPTIN",
|
||||
"ITM",
|
||||
"LPTICKER",
|
||||
|
@ -10968,6 +10969,7 @@
|
|||
"FLASH",
|
||||
"I2C",
|
||||
"I2C_ASYNCH",
|
||||
"I2CSLAVE",
|
||||
"INTERRUPTIN",
|
||||
"ITM",
|
||||
"LPTICKER",
|
||||
|
|
Loading…
Reference in New Issue