|  | 
|  | 1 | +/* | 
|  | 2 | + * Copyright (c) 2021 Valentin Milea <[email protected]> | 
|  | 3 | + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. | 
|  | 4 | + * | 
|  | 5 | + * SPDX-License-Identifier: BSD-3-Clause | 
|  | 6 | + */ | 
|  | 7 | + | 
|  | 8 | +#include <hardware/i2c.h> | 
|  | 9 | +#include <pico/i2c_slave.h> | 
|  | 10 | +#include <pico/stdlib.h> | 
|  | 11 | +#include <stdio.h> | 
|  | 12 | +#include <string.h> | 
|  | 13 | + | 
|  | 14 | +static const uint I2C_SLAVE_ADDRESS = 0x17; | 
|  | 15 | +static const uint I2C_BAUDRATE = 100000; // 100 kHz | 
|  | 16 | + | 
|  | 17 | +#ifdef i2c_default | 
|  | 18 | +// For this example, we run both the master and slave from the same board. | 
|  | 19 | +// You'll need to wire pin GP4 to GP6 (SDA), and pin GP5 to GP7 (SCL). | 
|  | 20 | +static const uint I2C_SLAVE_SDA_PIN = PICO_DEFAULT_I2C_SDA_PIN; // 4 | 
|  | 21 | +static const uint I2C_SLAVE_SCL_PIN = PICO_DEFAULT_I2C_SCL_PIN; // 5 | 
|  | 22 | +static const uint I2C_MASTER_SDA_PIN = 6; | 
|  | 23 | +static const uint I2C_MASTER_SCL_PIN = 7; | 
|  | 24 | + | 
|  | 25 | +// The slave implements a 256 byte memory. To write a series of bytes, the master first | 
|  | 26 | +// writes the memory address, followed by the data. The address is automatically incremented | 
|  | 27 | +// for each byte transferred, looping back to 0 upon reaching the end. Reading is done | 
|  | 28 | +// sequentially from the current memory address. | 
|  | 29 | +static struct | 
|  | 30 | +{ | 
|  | 31 | +    uint8_t mem[256]; | 
|  | 32 | +    uint8_t mem_address; | 
|  | 33 | +    bool mem_address_written; | 
|  | 34 | +} context; | 
|  | 35 | + | 
|  | 36 | +// Our handler is called from the I2C ISR, so it must complete quickly. Blocking calls / | 
|  | 37 | +// printing to stdio may interfere with interrupt handling. | 
|  | 38 | +static void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { | 
|  | 39 | +    switch (event) { | 
|  | 40 | +    case I2C_SLAVE_RECEIVE: // master has written some data | 
|  | 41 | +        if (!context.mem_address_written) { | 
|  | 42 | +            // writes always start with the memory address | 
|  | 43 | +            uint8_t by = i2c_read_byte_raw(i2c); | 
|  | 44 | +            context.mem_address = by; | 
|  | 45 | +            context.mem_address_written = true; | 
|  | 46 | +        } else { | 
|  | 47 | +            // save into memory | 
|  | 48 | +            uint8_t by = i2c_read_byte_raw(i2c); | 
|  | 49 | +            context.mem[context.mem_address] = by; | 
|  | 50 | +            context.mem_address++; | 
|  | 51 | +        } | 
|  | 52 | +        break; | 
|  | 53 | +    case I2C_SLAVE_REQUEST: // master is requesting data | 
|  | 54 | +        // load from memory | 
|  | 55 | +        i2c_write_byte_raw(i2c, context.mem[context.mem_address]); | 
|  | 56 | +        context.mem_address++; | 
|  | 57 | +        break; | 
|  | 58 | +    case I2C_SLAVE_FINISH: // master has signalled Stop / Restart | 
|  | 59 | +        context.mem_address_written = false; | 
|  | 60 | +        break; | 
|  | 61 | +    default: | 
|  | 62 | +        break; | 
|  | 63 | +    } | 
|  | 64 | +} | 
|  | 65 | + | 
|  | 66 | +static void setup_slave() { | 
|  | 67 | +    gpio_init(I2C_SLAVE_SDA_PIN); | 
|  | 68 | +    gpio_set_function(I2C_SLAVE_SDA_PIN, GPIO_FUNC_I2C); | 
|  | 69 | +    gpio_pull_up(I2C_SLAVE_SDA_PIN); | 
|  | 70 | + | 
|  | 71 | +    gpio_init(I2C_SLAVE_SCL_PIN); | 
|  | 72 | +    gpio_set_function(I2C_SLAVE_SCL_PIN, GPIO_FUNC_I2C); | 
|  | 73 | +    gpio_pull_up(I2C_SLAVE_SCL_PIN); | 
|  | 74 | + | 
|  | 75 | +    i2c_init(i2c0, I2C_BAUDRATE); | 
|  | 76 | +    // configure I2C0 for slave mode | 
|  | 77 | +    i2c_slave_init(i2c0, I2C_SLAVE_ADDRESS, &i2c_slave_handler); | 
|  | 78 | +} | 
|  | 79 | + | 
|  | 80 | +static void run_master() { | 
|  | 81 | +    gpio_init(I2C_MASTER_SDA_PIN); | 
|  | 82 | +    gpio_set_function(I2C_MASTER_SDA_PIN, GPIO_FUNC_I2C); | 
|  | 83 | +    // pull-ups are already active on slave side, this is just a fail-safe in case the wiring is faulty | 
|  | 84 | +    gpio_pull_up(I2C_MASTER_SDA_PIN); | 
|  | 85 | + | 
|  | 86 | +    gpio_init(I2C_MASTER_SCL_PIN); | 
|  | 87 | +    gpio_set_function(I2C_MASTER_SCL_PIN, GPIO_FUNC_I2C); | 
|  | 88 | +    gpio_pull_up(I2C_MASTER_SCL_PIN); | 
|  | 89 | + | 
|  | 90 | +    i2c_init(i2c1, I2C_BAUDRATE); | 
|  | 91 | + | 
|  | 92 | +    for (uint8_t mem_address = 0;; mem_address = (mem_address + 32) % 256) { | 
|  | 93 | +        char msg[32]; | 
|  | 94 | +        snprintf(msg, sizeof(msg), "Hello, I2C slave! - 0x%02X", mem_address); | 
|  | 95 | +        uint8_t msg_len = strlen(msg); | 
|  | 96 | + | 
|  | 97 | +        uint8_t buf[32]; | 
|  | 98 | +        buf[0] = mem_address; | 
|  | 99 | +        memcpy(buf + 1, msg, msg_len); | 
|  | 100 | +        // write message at mem_address | 
|  | 101 | +        printf("Write at 0x%02X: '%s'\n", mem_address, msg); | 
|  | 102 | +        for(int i = 0; i < (1 + msg_len); i++) { | 
|  | 103 | +            int count; | 
|  | 104 | +            if (i < (1 + msg_len - 1)) { | 
|  | 105 | +                count = i2c_write_burst_blocking(i2c1, I2C_SLAVE_ADDRESS, &buf[i], 1); | 
|  | 106 | +                sleep_ms(1); // gratuitous sleep for demonstration purposes - don't do this in real code! | 
|  | 107 | +            } else if (i == (1 + msg_len - 1)) { | 
|  | 108 | +                count = i2c_write_blocking(i2c1, I2C_SLAVE_ADDRESS, &buf[i], 1, false); | 
|  | 109 | +            } | 
|  | 110 | +            if (count != 1) { | 
|  | 111 | +                puts("Couldn't write to slave, please check your wiring!"); | 
|  | 112 | +                return; | 
|  | 113 | +            } | 
|  | 114 | +        } | 
|  | 115 | + | 
|  | 116 | +        // seek to mem_address | 
|  | 117 | +        int count = i2c_write_blocking(i2c1, I2C_SLAVE_ADDRESS, buf, 1, true); | 
|  | 118 | +        hard_assert(count == 1); | 
|  | 119 | + | 
|  | 120 | +        // partial read | 
|  | 121 | +        for(int i = 0; i < msg_len; i++) { | 
|  | 122 | +            if (count < (msg_len - 1)) { | 
|  | 123 | +                count = i2c_read_burst_blocking(i2c1, I2C_SLAVE_ADDRESS, buf + i, 1); | 
|  | 124 | +                sleep_ms(1); // gratuitous sleep for demonstration purposes - don't do this in real code! | 
|  | 125 | +            } else { | 
|  | 126 | +                count = i2c_read_blocking(i2c1, I2C_SLAVE_ADDRESS, buf + i, 1, false); | 
|  | 127 | +            } | 
|  | 128 | +            hard_assert(count == 1); | 
|  | 129 | +        } | 
|  | 130 | +        buf[msg_len] = '\0'; | 
|  | 131 | +        printf("Read  at 0x%02X: '%s'\n", mem_address, buf); | 
|  | 132 | +        hard_assert(memcmp(buf, msg, msg_len) == 0); | 
|  | 133 | + | 
|  | 134 | +        puts(""); | 
|  | 135 | +        sleep_ms(2000); | 
|  | 136 | +    } | 
|  | 137 | +} | 
|  | 138 | +#endif | 
|  | 139 | + | 
|  | 140 | +int main() { | 
|  | 141 | +    stdio_init_all(); | 
|  | 142 | +#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) | 
|  | 143 | +#warning i2c / slave_mem_i2c example requires a board with I2C pins | 
|  | 144 | +    puts("Default I2C pins were not defined"); | 
|  | 145 | +    return 0; | 
|  | 146 | +#else | 
|  | 147 | +    puts("\nI2C slave example"); | 
|  | 148 | + | 
|  | 149 | +    setup_slave(); | 
|  | 150 | +    run_master(); | 
|  | 151 | +#endif | 
|  | 152 | +} | 
0 commit comments