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
*
* 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 {

View File

@ -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

View File

@ -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

View File

@ -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");
}

View File

@ -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 {

View File

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