Skip to content

RFC: defining an I2C slave class and behaviour #3935

@dpgeorge

Description

@dpgeorge

I2C slave mode is an often-requested feature. See eg #3680 and #3858, and #3114 for a previous discussion that was tied to esp8266 implementation. It would be good to define an I2C slave class and how it would behave, and then implementation for various ports could follow.

stm32 recently got some I2C slave code and the interface is in this header: https://github.com/micropython/micropython/blob/master/ports/stm32/i2cslave.h . In this C implementation there are 4 external functions that should be provided and which are essentially callbacks from the low-level I2C slave driver:

// the slave's address was matched, and the master requested a read/write (via rw param)
// return 0 to ack address or 1 to nack it
int i2c_slave_process_addr_match(int rw);

// master sent a byte, slave received it (val param)
// return 0 to ack the byte or 1 to nack it
int i2c_slave_process_rx_byte(uint8_t val);

// master stopped sending data via a stop or repeated start condition
void i2c_slave_process_rx_end(void);

// master requests data, slave needs to send it
// return the byte to send back to the master
uint8_t i2c_slave_process_tx_byte(void);

This describes a very low level interface, at the byte level. With a bus at 100kHz bytes are transferred at about 10kHz. Python code might be able to respond at this rate to individual bytes, but only on fast hardware. And a 400kHz SCL will make it more difficult to respond to individual bytes. So, while the above interface allows to implement (almost) any I2C device, it's not really a solution for MicroPython.

We need to define a I2C slave API at a higher level than bytes. A callback for each full bus transaction would be the way to go.

The most obvious I2C behaviour to implement is a memory-mapped device, eg I2CSlaveMemory, where the master send the memory address (1 or 2 bytes) then either reads or writes the data. For example:

mem_buf = bytearray(32)
i2c_mem = I2CSlaveMemory(scl=scl, sda=sda, addr=67, mem_addr_len=8, mem=mem_buf)
# master can now access the mem_buf array
while True:
    mem_buf[0] += 1
    time.sleep(1)

For simple cases that doesn't even need a callback. But I2CSlaveMemory would support callbacks like memory_read(offset, len) and memory_written(offset, len) to signal when the master did a transaction.

Other types of high-level devices include:

  • A FIFO/stream: the master can write bytes to the slave which buffer up and the slave reads them in a FIFO manner. Similarly with data going from slave to master (with first byte read by the master indicating how many bytes are in the buffer to read). This would make I2C very similar to UART and could be the basis of an I2C REPL.
  • A packet interface: the master can write packets (just a bunch of bytes in one write transaction) and the slave gets a callback when a new packet comes in, with the packet data as the arg to the callback (or they get queued in a RX buffer). For the other direction the slave could queue up packets to go out to the master when it does a read.

In addition to defining the tx/rx behaviour (memory/stream/packet/etc) it's also important to consider whether a callback interface should be used for events as they happen, or something more like polling (eg like sockets). For polling the I2C slave device would have methods read() and write() and work with select.poll() to (efficiently) query when it was ready to read/write (or have a blocking behaviour, similar to sockets). Polling behaviour would probably need buffering of some sort.

Metadata

Metadata

Assignees

No one assigned

    Labels

    rfcRequest for Comment

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy