Skip to content

Instantly share code, notes, and snippets.

@vmilea
Created January 19, 2022 11:06
Show Gist options
  • Save vmilea/5820c2d08d3392d639b1fbc8c015c53b to your computer and use it in GitHub Desktop.
Save vmilea/5820c2d08d3392d639b1fbc8c015c53b to your computer and use it in GitHub Desktop.
/*
* Copyright (c) 2022 Valentin Milea <[email protected]>
*
* SPDX-License-Identifier: MIT
*/
#include <i2c_fifo.h>
#include <i2c_slave.h>
#include <pico/multicore.h> // add pico_multicore to target_link_libraries
#include <pico/stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_TRANSFER_SIZE (4 + 128)
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;
static struct
{
critical_section_t crit_sec;
bool has_request;
} slave;
// 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) {
critical_section_enter_blocking(&slave.crit_sec);
switch (event) {
case I2C_SLAVE_RECEIVE: // master has written some data
break;
case I2C_SLAVE_REQUEST: // master is requesting data
slave.has_request = true;
__sev(); // notify slave transmitter
break;
case I2C_SLAVE_FINISH: // master has signalled Stop / Restart
break;
default:
break;
}
critical_section_exit(&slave.crit_sec);
}
static void i2c_slave_transmit_blocking(i2c_inst_t *i2c, const uint8_t *src, size_t len) {
critical_section_enter_blocking(&slave.crit_sec);
// wait until master has requested data
while (!slave.has_request) {
critical_section_exit(&slave.crit_sec);
__wfe();
critical_section_enter_blocking(&slave.crit_sec);
}
// clear flag
slave.has_request = false;
i2c_write_raw_blocking(i2c, src, len);
critical_section_exit(&slave.crit_sec);
}
static void setup_slave(void ) {
critical_section_init(&slave.crit_sec);
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 master_thread() {
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);
static uint32_t data[MAX_TRANSFER_SIZE / sizeof(uint32_t)];
do {
puts(" Read header...");
size_t len = 0;
int count = i2c_read_blocking(i2c1, I2C_SLAVE_ADDRESS, (uint8_t*)&len, sizeof(uint32_t), true);
if (count != sizeof(uint32_t)) {
puts(" Couldn't read from slave, please check your wiring!");
return;
}
printf(" len: %u\n", len);
size_t payload_size = len * sizeof(uint32_t);
hard_assert(payload_size <= MAX_TRANSFER_SIZE);
puts(" Read payload...");
count = i2c_read_blocking(i2c1, I2C_SLAVE_ADDRESS, (uint8_t*)data, payload_size, false);
hard_assert(count == payload_size);
for (size_t i = 0; i < len; i++) {
printf(" %u\n", (uint)data[i]);
}
puts("");
sleep_ms(100);
} while (true);
}
int main() {
stdio_init_all();
puts("\nI2C slave - transmit blocking");
setup_slave();
multicore_launch_core1(master_thread);
size_t processed_data_len = MAX_TRANSFER_SIZE / sizeof(uint32_t) - 1;
uint32_t *processed_data = malloc(processed_data_len * sizeof(uint32_t));
uint32_t counter = 0;
do {
// prepare next data set
for (size_t i = 0; i < processed_data_len; i++) {
processed_data[i] = counter++;
}
// simulate processing lag; master tries to read immediately, but I2C bus will be
// stalled until slave starts transmitting
sleep_ms(1000);
// transmit header
i2c_slave_transmit_blocking(i2c0, (uint8_t*)&processed_data_len, sizeof(uint32_t));
// transmit payload
i2c_slave_transmit_blocking(i2c0, (uint8_t*)processed_data, processed_data_len * sizeof(uint32_t));
} while (true);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment