diff --git a/README.md b/README.md index 7170139fe..b33af27b4 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ App|Description [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 +[slave_mem_i2c_burst](i2c/slave_mem_i2c) | i2c slave example where the slave implements a 256 byte memory. This version inefficiently writes each byte in a separate call to demonstrate read and write burst mode. ### Interpolator diff --git a/i2c/slave_mem_i2c/CMakeLists.txt b/i2c/slave_mem_i2c/CMakeLists.txt index 26eb3e4f2..449744447 100644 --- a/i2c/slave_mem_i2c/CMakeLists.txt +++ b/i2c/slave_mem_i2c/CMakeLists.txt @@ -8,3 +8,14 @@ target_link_libraries(slave_mem_i2c ) pico_add_extra_outputs(slave_mem_i2c) example_auto_set_url(slave_mem_i2c) + +add_executable(slave_mem_i2c_burst + slave_mem_i2c_burst.c + ) +target_link_libraries(slave_mem_i2c_burst + pico_i2c_slave + hardware_i2c + pico_stdlib + ) +pico_add_extra_outputs(slave_mem_i2c_burst) +example_auto_set_url(slave_mem_i2c_burst) diff --git a/i2c/slave_mem_i2c/slave_mem_i2c_burst.c b/i2c/slave_mem_i2c/slave_mem_i2c_burst.c new file mode 100644 index 000000000..375fa1ed4 --- /dev/null +++ b/i2c/slave_mem_i2c/slave_mem_i2c_burst.c @@ -0,0 +1,152 @@ +/* + * 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 + +#ifdef i2c_default +// 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 + uint8_t by = i2c_read_byte_raw(i2c); + context.mem_address = by; + context.mem_address_written = true; + } else { + // save into memory + uint8_t by = i2c_read_byte_raw(i2c); + context.mem[context.mem_address] = by; + 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); + for(int i = 0; i < (1 + msg_len); i++) { + int count; + if (i < (1 + msg_len - 1)) { + count = i2c_write_burst_blocking(i2c1, I2C_SLAVE_ADDRESS, &buf[i], 1); + sleep_ms(1); // gratuitous sleep for demonstration purposes - don't do this in real code! + } else if (i == (1 + msg_len - 1)) { + count = i2c_write_blocking(i2c1, I2C_SLAVE_ADDRESS, &buf[i], 1, false); + } + if (count != 1) { + puts("Couldn't write to slave, please check your wiring!"); + return; + } + } + + // seek to mem_address + int count = i2c_write_blocking(i2c1, I2C_SLAVE_ADDRESS, buf, 1, true); + hard_assert(count == 1); + + // partial read + for(int i = 0; i < msg_len; i++) { + if (count < (msg_len - 1)) { + count = i2c_read_burst_blocking(i2c1, I2C_SLAVE_ADDRESS, buf + i, 1); + sleep_ms(1); // gratuitous sleep for demonstration purposes - don't do this in real code! + } else { + count = i2c_read_blocking(i2c1, I2C_SLAVE_ADDRESS, buf + i, 1, false); + } + hard_assert(count == 1); + } + buf[msg_len] = '\0'; + printf("Read at 0x%02X: '%s'\n", mem_address, buf); + hard_assert(memcmp(buf, msg, msg_len) == 0); + + puts(""); + sleep_ms(2000); + } +} +#endif + +int main() { + stdio_init_all(); +#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) +#warning i2c / slave_mem_i2c example requires a board with I2C pins + puts("Default I2C pins were not defined"); + return 0; +#else + puts("\nI2C slave example"); + + setup_slave(); + run_master(); +#endif +}