Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,7 @@ msgstr ""
msgid "Mount point directory missing"
msgstr ""

#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c
#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c
msgid "Must be a %q subclass."
msgstr ""
Expand Down Expand Up @@ -2732,6 +2733,10 @@ msgstr ""
msgid "buffer too small for requested bytes"
msgstr ""

#: shared-bindings/busio/SPI.c
msgid "buffers must be same length"
msgstr ""

#: py/emitbc.c
msgid "bytecode overflow"
msgstr ""
Expand Down
223 changes: 221 additions & 2 deletions ports/raspberrypi/common-hal/busio/I2C.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/bitbangio/I2C.h"

#include "hardware/dma.h"
#include "hardware/gpio.h"
#include "hardware/i2c.h"
#include "supervisor/port.h"

#include <string.h>

// Synopsys DW_apb_i2c (v2.01) IP

Expand All @@ -22,7 +27,205 @@
// One second
#define BUS_TIMEOUT_US 1000000

static i2c_inst_t *i2c[2] = {i2c0, i2c1};
struct i2c_transfer_state {
uint dma_channel;
uint paired_dma_channel;
bool pending_last_command;
i2c_inst_t *i2c;
uint8_t *tx_copy;
uint32_t repeated_command;
uint32_t last_command;
};

typedef struct i2c_transfer_state i2c_transfer_state;

static i2c_transfer_state i2c_dma_channel_state[NUM_DMA_CHANNELS];

// Addresses of the form 000 0xxx or 111 1xxx are reserved. No slave should
// have these addresses.
#define i2c_reserved_addr(addr) (((addr) & 0x78) == 0 || ((addr) & 0x78) == 0x78)

static i2c_inst_t *i2c_hw_instances[2] = {i2c0, i2c1};

static i2c_transfer_state *common_hal_i2c_write_dma(i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, bool nostop) {
invalid_params_if(HARDWARE_I2C, addr >= 0x80); // 7-bit addresses
invalid_params_if(HARDWARE_I2C, i2c_reserved_addr(addr));
invalid_params_if(HARDWARE_I2C, len == 0);
invalid_params_if(HARDWARE_I2C, ((int)len) < 0);

uint dma_channel = dma_claim_unused_channel(true);
i2c_transfer_state *state = &i2c_dma_channel_state[dma_channel];
state->dma_channel = dma_channel;

i2c->hw->enable = 0;
i2c->hw->tar = addr;
i2c->hw->enable = 1;

state->paired_dma_channel = NUM_DMA_CHANNELS;
state->pending_last_command = false;
state->i2c = i2c;
state->tx_copy = NULL;
state->repeated_command = 0;
state->last_command = 0;

if (len == 1) {
uint32_t command =
bool_to_bit(i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB |
bool_to_bit(!nostop) << I2C_IC_DATA_CMD_STOP_LSB |
src[0];
i2c->hw->data_cmd = command;
state->last_command = command;
i2c->restart_on_next = nostop;
return state;
}

i2c->hw->data_cmd =
bool_to_bit(i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB |
src[0];

size_t middle_len = len - 2;
if (middle_len > 0) {
state->tx_copy = port_malloc(middle_len, true);
if (state->tx_copy == NULL) {
dma_channel_unclaim(dma_channel);
m_malloc_fail(middle_len);
}
memcpy(state->tx_copy, src + 1, middle_len);

dma_channel_config config = dma_channel_get_default_config(dma_channel);
channel_config_set_transfer_data_size(&config, DMA_SIZE_8);
channel_config_set_read_increment(&config, true);
channel_config_set_write_increment(&config, false);
channel_config_set_dreq(&config, I2C_DREQ_NUM(i2c, true));

dma_channel_configure(dma_channel, &config, &i2c->hw->data_cmd,
state->tx_copy, middle_len, true);
}

state->pending_last_command = true;
state->last_command =
bool_to_bit(!nostop) << I2C_IC_DATA_CMD_STOP_LSB |
src[len - 1];
i2c->restart_on_next = nostop;
return state;
}

static i2c_transfer_state *common_hal_i2c_read_dma(i2c_inst_t *i2c, uint8_t addr, uint8_t *dst, size_t len, bool nostop) {
invalid_params_if(HARDWARE_I2C, addr >= 0x80); // 7-bit addresses
invalid_params_if(HARDWARE_I2C, i2c_reserved_addr(addr));
invalid_params_if(HARDWARE_I2C, len == 0);
invalid_params_if(HARDWARE_I2C, ((int)len) < 0);
uint tx_dma_channel = dma_claim_unused_channel(true);
uint rx_dma_channel = dma_claim_unused_channel(true);
i2c_transfer_state *state = &i2c_dma_channel_state[tx_dma_channel];
state->dma_channel = tx_dma_channel;

i2c->hw->enable = 0;
i2c->hw->tar = addr;
i2c->hw->enable = 1;

state->paired_dma_channel = rx_dma_channel;
state->pending_last_command = false;
state->i2c = i2c;
state->tx_copy = NULL;
state->repeated_command = 0;
state->last_command = 0;

dma_channel_config rx_config = dma_channel_get_default_config(rx_dma_channel);
channel_config_set_transfer_data_size(&rx_config, DMA_SIZE_8);
channel_config_set_read_increment(&rx_config, false);
channel_config_set_write_increment(&rx_config, true);
channel_config_set_dreq(&rx_config, I2C_DREQ_NUM(i2c, false));

dma_channel_configure(rx_dma_channel, &rx_config, dst, &i2c->hw->data_cmd, len, true);

uint32_t first_command =
bool_to_bit(i2c->restart_on_next) << I2C_IC_DATA_CMD_RESTART_LSB |
I2C_IC_DATA_CMD_CMD_BITS;
uint32_t middle_command = I2C_IC_DATA_CMD_CMD_BITS;
uint32_t last_command =
bool_to_bit(!nostop) << I2C_IC_DATA_CMD_STOP_LSB |
I2C_IC_DATA_CMD_CMD_BITS;

if (len == 1) {
uint32_t command = first_command | last_command;
i2c->hw->data_cmd = command;
state->last_command = command;
} else {
i2c->hw->data_cmd = first_command;

size_t middle_len = len - 2;
if (middle_len > 0) {
dma_channel_config tx_config = dma_channel_get_default_config(tx_dma_channel);
channel_config_set_transfer_data_size(&tx_config, DMA_SIZE_32);
channel_config_set_read_increment(&tx_config, false);
channel_config_set_write_increment(&tx_config, false);
channel_config_set_dreq(&tx_config, I2C_DREQ_NUM(i2c, true));

state->repeated_command = middle_command;

dma_channel_configure(tx_dma_channel, &tx_config, &i2c->hw->data_cmd,
&state->repeated_command, middle_len, true);
}

state->pending_last_command = true;
state->last_command = last_command;
}

i2c->restart_on_next = nostop;
return state;
}

static bool common_hal_i2c_dma_is_busy(i2c_transfer_state *state) {
if (state == NULL) {
return false;
}
uint dma_channel = state->dma_channel;
if (dma_channel_is_busy(dma_channel)) {
return true;
}

if (state->pending_last_command) {
if (!i2c_get_write_available(state->i2c)) {
return true;
}

state->i2c->hw->data_cmd = state->last_command;
state->pending_last_command = false;
return true;
}

uint paired_dma_channel = state->paired_dma_channel;
if (paired_dma_channel < NUM_DMA_CHANNELS && dma_channel_is_busy(paired_dma_channel)) {
return true;
}

if (paired_dma_channel < NUM_DMA_CHANNELS) {
dma_channel_unclaim(paired_dma_channel);
state->paired_dma_channel = NUM_DMA_CHANNELS;
}

if (!(state->i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_TX_EMPTY_BITS)) {
return true;
}

if (state->last_command & I2C_IC_DATA_CMD_STOP_BITS) {
if (!(state->i2c->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_STOP_DET_BITS)) {
return true;
}
state->i2c->hw->clr_stop_det;
}

if (state->tx_copy != NULL) {
port_free(state->tx_copy);
state->tx_copy = NULL;
}

state->i2c = NULL;

dma_channel_unclaim(dma_channel);
return false;
}

void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) {
Expand All @@ -36,7 +239,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
size_t scl_instance = (scl->number / 2) % 2;
size_t sda_instance = (sda->number / 2) % 2;
if (scl->number % 2 == 1 && sda->number % 2 == 0 && scl_instance == sda_instance) {
self->peripheral = i2c[sda_instance];
self->peripheral = i2c_hw_instances[sda_instance];
}
if (self->peripheral == NULL) {
raise_ValueError_invalid_pins();
Expand Down Expand Up @@ -217,3 +420,19 @@ void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) {
never_reset_pin_number(self->scl_pin);
never_reset_pin_number(self->sda_pin);
}

i2c_transfer_state *common_hal_busio_i2c_start_read(busio_i2c_obj_t *i2c, uint8_t address, uint8_t *data, size_t len, bool nostop) {
return common_hal_i2c_read_dma(i2c->peripheral, address, data, len, nostop);
}

i2c_transfer_state *common_hal_busio_i2c_start_write(busio_i2c_obj_t *i2c, uint8_t address, const uint8_t *data, size_t len, bool nostop) {
return common_hal_i2c_write_dma(i2c->peripheral, address, data, len, nostop);
}

bool common_hal_busio_i2c_read_isbusy(i2c_transfer_state *state) {
return common_hal_i2c_dma_is_busy(state);
}

bool common_hal_busio_i2c_write_isbusy(i2c_transfer_state *state) {
return common_hal_i2c_dma_is_busy(state);
}
7 changes: 7 additions & 0 deletions ports/raspberrypi/common-hal/busio/I2C.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,10 @@ typedef struct {
uint8_t scl_pin;
uint8_t sda_pin;
} busio_i2c_obj_t;

typedef struct i2c_transfer_state i2c_transfer_state;

i2c_transfer_state *common_hal_busio_i2c_start_read(busio_i2c_obj_t *i2c, uint8_t address, uint8_t *data, size_t len, bool nostop);
i2c_transfer_state *common_hal_busio_i2c_start_write(busio_i2c_obj_t *i2c, uint8_t address, const uint8_t *data, size_t len, bool nostop);
bool common_hal_busio_i2c_read_isbusy(i2c_transfer_state *state);
bool common_hal_busio_i2c_write_isbusy(i2c_transfer_state *state);
Loading