From 5e1c1ca7b6891a672ccaf59dfe12d81eaf01f413 Mon Sep 17 00:00:00 2001 From: Peter Harper <77111776+peterharperuk@users.noreply.github.com> Date: Tue, 7 Feb 2023 14:03:00 +0000 Subject: [PATCH] Add i2c slave example (#320) * Add i2c slave example from https://github.com/vmilea/pico_i2c_slave To test you need to wire pin GP4 to GP6 (SDA), and pin GP5 to GP7 (SCL) --- README.md | 1 + i2c/CMakeLists.txt | 1 + i2c/slave_mem_i2c/CMakeLists.txt | 10 +++ i2c/slave_mem_i2c/slave_mem_i2c.c | 134 ++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 i2c/slave_mem_i2c/CMakeLists.txt create mode 100644 i2c/slave_mem_i2c/slave_mem_i2c.c diff --git a/README.md b/README.md index d45c71c..12143fd 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ App|Description [pa1010d_i2c](i2c/pa1010d_i2c) | Read GPS location data, parse and display data via I2C. [pcf8523_i2c](i2c/pcf8523_i2c) | Read time and date values from a real time clock. Set current time and alarms on it. [ht16k33_i2c](i2c/ht16k33_i2c) | Drive a 4 digit 14 segment LED with an HT16K33. +[slave_mem_i2c](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory ### Interpolator diff --git a/i2c/CMakeLists.txt b/i2c/CMakeLists.txt index 977a3d2..4b57e7f 100644 --- a/i2c/CMakeLists.txt +++ b/i2c/CMakeLists.txt @@ -11,4 +11,5 @@ if (NOT PICO_NO_HARDWARE) add_subdirectory(pa1010d_i2c) add_subdirectory(pcf8523_i2c) add_subdirectory(ht16k33_i2c) + add_subdirectory(slave_mem_i2c) endif () diff --git a/i2c/slave_mem_i2c/CMakeLists.txt b/i2c/slave_mem_i2c/CMakeLists.txt new file mode 100644 index 0000000..26eb3e4 --- /dev/null +++ b/i2c/slave_mem_i2c/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(slave_mem_i2c + slave_mem_i2c.c + ) +target_link_libraries(slave_mem_i2c + pico_i2c_slave + hardware_i2c + pico_stdlib + ) +pico_add_extra_outputs(slave_mem_i2c) +example_auto_set_url(slave_mem_i2c) diff --git a/i2c/slave_mem_i2c/slave_mem_i2c.c b/i2c/slave_mem_i2c/slave_mem_i2c.c new file mode 100644 index 0000000..776e459 --- /dev/null +++ b/i2c/slave_mem_i2c/slave_mem_i2c.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 Valentin Milea + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static const uint I2C_SLAVE_ADDRESS = 0x17; +static const uint I2C_BAUDRATE = 100000; // 100 kHz + +// For this example, we run both the master and slave from the same board. +// You'll need to wire pin GP4 to GP6 (SDA), and pin GP5 to GP7 (SCL). +static const uint I2C_SLAVE_SDA_PIN = PICO_DEFAULT_I2C_SDA_PIN; // 4 +static const uint I2C_SLAVE_SCL_PIN = PICO_DEFAULT_I2C_SCL_PIN; // 5 +static const uint I2C_MASTER_SDA_PIN = 6; +static const uint I2C_MASTER_SCL_PIN = 7; + +// The slave implements a 256 byte memory. To write a series of bytes, the master first +// writes the memory address, followed by the data. The address is automatically incremented +// for each byte transferred, looping back to 0 upon reaching the end. Reading is done +// sequentially from the current memory address. +static struct +{ + uint8_t mem[256]; + uint8_t mem_address; + bool mem_address_written; +} context; + +// Our handler is called from the I2C ISR, so it must complete quickly. Blocking calls / +// printing to stdio may interfere with interrupt handling. +static void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { + switch (event) { + case I2C_SLAVE_RECEIVE: // master has written some data + if (!context.mem_address_written) { + // writes always start with the memory address + context.mem_address = i2c_read_byte_raw(i2c); + context.mem_address_written = true; + } else { + // save into memory + context.mem[context.mem_address] = i2c_read_byte_raw(i2c); + context.mem_address++; + } + break; + case I2C_SLAVE_REQUEST: // master is requesting data + // load from memory + i2c_write_byte_raw(i2c, context.mem[context.mem_address]); + context.mem_address++; + break; + case I2C_SLAVE_FINISH: // master has signalled Stop / Restart + context.mem_address_written = false; + break; + default: + break; + } +} + +static void setup_slave() { + gpio_init(I2C_SLAVE_SDA_PIN); + gpio_set_function(I2C_SLAVE_SDA_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SLAVE_SDA_PIN); + + gpio_init(I2C_SLAVE_SCL_PIN); + gpio_set_function(I2C_SLAVE_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SLAVE_SCL_PIN); + + i2c_init(i2c0, I2C_BAUDRATE); + // configure I2C0 for slave mode + i2c_slave_init(i2c0, I2C_SLAVE_ADDRESS, &i2c_slave_handler); +} + +static void run_master() { + gpio_init(I2C_MASTER_SDA_PIN); + gpio_set_function(I2C_MASTER_SDA_PIN, GPIO_FUNC_I2C); + // pull-ups are already active on slave side, this is just a fail-safe in case the wiring is faulty + gpio_pull_up(I2C_MASTER_SDA_PIN); + + gpio_init(I2C_MASTER_SCL_PIN); + gpio_set_function(I2C_MASTER_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_MASTER_SCL_PIN); + + i2c_init(i2c1, I2C_BAUDRATE); + + for (uint8_t mem_address = 0;; mem_address = (mem_address + 32) % 256) { + char msg[32]; + snprintf(msg, sizeof(msg), "Hello, I2C slave! - 0x%02X", mem_address); + uint8_t msg_len = strlen(msg); + + uint8_t buf[32]; + buf[0] = mem_address; + memcpy(buf + 1, msg, msg_len); + // write message at mem_address + printf("Write at 0x%02X: '%s'\n", mem_address, msg); + int count = i2c_write_blocking(i2c1, I2C_SLAVE_ADDRESS, buf, 1 + msg_len, false); + if (count < 0) { + puts("Couldn't write to slave, please check your wiring!"); + return; + } + hard_assert(count == 1 + msg_len); + + // seek to mem_address + count = i2c_write_blocking(i2c1, I2C_SLAVE_ADDRESS, buf, 1, true); + hard_assert(count == 1); + // partial read + uint8_t split = 5; + count = i2c_read_blocking(i2c1, I2C_SLAVE_ADDRESS, buf, split, true); + hard_assert(count == split); + buf[count] = '\0'; + printf("Read at 0x%02X: '%s'\n", mem_address, buf); + hard_assert(memcmp(buf, msg, split) == 0); + // read the remaining bytes, continuing from last address + count = i2c_read_blocking(i2c1, I2C_SLAVE_ADDRESS, buf, msg_len - split, false); + hard_assert(count == msg_len - split); + buf[count] = '\0'; + printf("Read at 0x%02X: '%s'\n", mem_address + split, buf); + hard_assert(memcmp(buf, msg + split, msg_len - split) == 0); + + puts(""); + sleep_ms(2000); + } +} + +int main() { + stdio_init_all(); + puts("\nI2C slave example"); + + setup_slave(); + run_master(); +}