Created
January 19, 2022 11:06
-
-
Save vmilea/5820c2d08d3392d639b1fbc8c015c53b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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