Merge pull request #12161 from AGlass0fMilk/nrf-i2c-slave

nRF52 I2CSlave Implementation
pull/12778/head
Martin Kojtal 2020-04-14 16:31:31 +02:00 committed by GitHub
commit 098c72a312
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 262 additions and 50 deletions

View File

@ -34,39 +34,95 @@ namespace mbed {
* *
* @note Synchronization level: Not protected * @note Synchronization level: Not protected
* *
* Example Simple I2C responder: * Example Simple I2C slave and master (requires two Mbed-boards):
* @code * @code
* #include <mbed.h> * #include <mbed.h>
* #include <mbed_wait_api.h>
* #include <string.h>
* *
* const int SLAVE_ADDRESS = 0xA0; * #define BUILD_I2C_SLAVE 1 // Build for slave or master of this example
* const char message[] = "Slave!"; *
* #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); * I2CSlave slave(I2C_SDA, I2C_SCL);
* *
* int main() { * int main() {
* slave.address(SLAVE_ADDRESS); *
* char buf[BUFFER_SIZE] = "ABCDE";
*
* slave.address(SLAVE_ADDR);
* while (1) { * while (1) {
* int operation = slave.receive(); * int i = slave.receive();
* switch (operation) { * switch (i) {
* case I2CSlave::ReadAddressed: * case I2CSlave::ReadAddressed:
* int status = slave.write(message, sizeof(message)); * // Write back the buffer from the master
* if (status == 0) { * slave.write(buf, BUFFER_SIZE);
* printf("Written message: %s\n", message); * printf("Written to master (addressed): %s\n", buf);
* } else {
* printf("Failed to write message.\n");
* }
* break; * break;
*
* case I2CSlave::WriteGeneral: * case I2CSlave::WriteGeneral:
* int byte_read = slave.read(); * slave.read(buf, BUFFER_SIZE);
* printf("Read General: %c (%d)\n", byte_read, byte_read); * printf("Read from master (general): %s\n", buf);
* break; * break;
*
* case I2CSlave::WriteAddressed: * case I2CSlave::WriteAddressed:
* int byte_read = slave.read(); * slave.read(buf, BUFFER_SIZE);
* printf("Read Addressed: %c (%d)\n", byte_read, byte_read); * printf("Read from master (addressed): %s\n", buf);
* break; * 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 * @endcode
*/ */
class I2CSlave { class I2CSlave {

View File

@ -3908,20 +3908,20 @@
// <e> NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver // <e> NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver
//========================================================== //==========================================================
#ifndef NRFX_TWIS_ENABLED #ifndef NRFX_TWIS_ENABLED
#define NRFX_TWIS_ENABLED 0 #define NRFX_TWIS_ENABLED 1
#endif #endif
// <q> NRFX_TWIS0_ENABLED - Enable TWIS0 instance // <q> NRFX_TWIS0_ENABLED - Enable TWIS0 instance
#ifndef NRFX_TWIS0_ENABLED #ifndef NRFX_TWIS0_ENABLED
#define NRFX_TWIS0_ENABLED 0 #define NRFX_TWIS0_ENABLED 1
#endif #endif
// <q> NRFX_TWIS1_ENABLED - Enable TWIS1 instance // <q> NRFX_TWIS1_ENABLED - Enable TWIS1 instance
#ifndef NRFX_TWIS1_ENABLED #ifndef NRFX_TWIS1_ENABLED
#define NRFX_TWIS1_ENABLED 0 #define NRFX_TWIS1_ENABLED 1
#endif #endif
// <q> NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only once // <q> NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only once

View File

@ -3908,20 +3908,20 @@
// <e> NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver // <e> NRFX_TWIS_ENABLED - nrfx_twis - TWIS peripheral driver
//========================================================== //==========================================================
#ifndef NRFX_TWIS_ENABLED #ifndef NRFX_TWIS_ENABLED
#define NRFX_TWIS_ENABLED 0 #define NRFX_TWIS_ENABLED 1
#endif #endif
// <q> NRFX_TWIS0_ENABLED - Enable TWIS0 instance // <q> NRFX_TWIS0_ENABLED - Enable TWIS0 instance
#ifndef NRFX_TWIS0_ENABLED #ifndef NRFX_TWIS0_ENABLED
#define NRFX_TWIS0_ENABLED 0 #define NRFX_TWIS0_ENABLED 1
#endif #endif
// <q> NRFX_TWIS1_ENABLED - Enable TWIS1 instance // <q> NRFX_TWIS1_ENABLED - Enable TWIS1 instance
#ifndef NRFX_TWIS1_ENABLED #ifndef NRFX_TWIS1_ENABLED
#define NRFX_TWIS1_ENABLED 0 #define NRFX_TWIS1_ENABLED 1
#endif #endif
// <q> NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only once // <q> NRFX_TWIS_ASSUME_INIT_AFTER_RESET_ONLY - Assume that any instance would be initialized only once

View File

@ -59,6 +59,10 @@
#include "app_util_platform.h" #include "app_util_platform.h"
#include "prs/nrfx_prs.h" #include "prs/nrfx_prs.h"
#if DEVICE_I2CSLAVE
#include "nrfx_twis.h"
#endif
#define TWI_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \ #define TWI_PIN_INIT(_pin) nrf_gpio_cfg((_pin), \
NRF_GPIO_PIN_DIR_INPUT, \ NRF_GPIO_PIN_DIR_INPUT, \
NRF_GPIO_PIN_INPUT_CONNECT, \ NRF_GPIO_PIN_INPUT_CONNECT, \
@ -135,6 +139,15 @@ void i2c_init(i2c_t *obj, PinName sda, PinName scl)
config->mask = 0; config->mask = 0;
#endif #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 */ /* Force reconfiguration */
config->update = true; config->update = true;
@ -164,6 +177,14 @@ void i2c_frequency(i2c_t *obj, int hz)
struct i2c_s *config = obj; struct i2c_s *config = obj;
#endif #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. */ /* Round down to nearest valid frequency. */
nrf_twi_frequency_t new_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. */ /* 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) }; 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. */ /* Forward declare interrupt handler. */
#if DEVICE_I2C_ASYNCH #if DEVICE_I2C_ASYNCH
static void nordic_nrf5_twi_event_handler(nrfx_twi_evt_t const *p_event, void *p_context); static void nordic_nrf5_twi_event_handler(nrfx_twi_evt_t const *p_event, void *p_context);
@ -632,15 +657,51 @@ 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 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 (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_disable(&nordic_nrf5_instance[instance]);
nrfx_twi_uninit(&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 /* Force resource release. This is necessary because mbed drivers don't
* deinitialize on object destruction. * deinitialize on object destruction.
*/ */
NRFX_IRQ_DISABLE((nrfx_get_irq_number((void const *)nordic_nrf5_twi_register[instance]))); NRFX_IRQ_DISABLE((nrfx_get_irq_number((void const *)nordic_nrf5_twi_register[instance])));
#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. */ /* Configure driver with new settings. */
nrfx_twi_config_t twi_config = { nrfx_twi_config_t twi_config = {
.scl = config->scl, .scl = config->scl,
@ -677,6 +738,9 @@ static void i2c_configure_driver_instance(i2c_t *obj)
/* Enable peripheral. */ /* Enable peripheral. */
nrfx_twi_enable(&nordic_nrf5_instance[instance]); 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 #endif // DEVICE_I2C_ASYNCH
#if DEVICE_I2CSLAVE #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) 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"); 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"); 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; return 0;
} }
@ -990,7 +1088,26 @@ int i2c_slave_read(i2c_t *obj, char *data, int length)
{ {
DEBUG_PRINTF("i2c_slave_read\r\n"); 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. /** 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"); 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. /** 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) 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"); DEBUG_PRINTF("i2c_slave_address\r\n");
} }

View File

@ -128,6 +128,12 @@ struct i2c_s {
uint32_t mask; uint32_t mask;
uint32_t event; uint32_t event;
#endif #endif
#if DEVICE_I2CSLAVE
bool was_slave;
bool is_slave;
uint8_t slave_addr;
#endif
}; };
struct analogin_s { struct analogin_s {

View File

@ -10584,6 +10584,7 @@
"FLASH", "FLASH",
"I2C", "I2C",
"I2C_ASYNCH", "I2C_ASYNCH",
"I2CSLAVE",
"INTERRUPTIN", "INTERRUPTIN",
"ITM", "ITM",
"LPTICKER", "LPTICKER",
@ -10968,6 +10969,7 @@
"FLASH", "FLASH",
"I2C", "I2C",
"I2C_ASYNCH", "I2C_ASYNCH",
"I2CSLAVE",
"INTERRUPTIN", "INTERRUPTIN",
"ITM", "ITM",
"LPTICKER", "LPTICKER",