diff --git a/CMakeLists.txt b/CMakeLists.txt index d7bd855..65a4968 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,6 @@ pico_sdk_init() add_executable(I2C_Slave main.c - i2c_slave.c ) target_link_libraries(I2C_Slave @@ -22,6 +21,7 @@ target_link_libraries(I2C_Slave hardware_pio pico_stdlib pico_multicore + pico_i2c_slave ) pico_enable_stdio_usb(I2C_Slave 1) diff --git a/i2c_fifo.h b/i2c_fifo.h deleted file mode 100644 index 805c00e..0000000 --- a/i2c_fifo.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2021 Valentin Milea - * - * SPDX-License-Identifier: MIT - */ - -#ifndef _I2C_FIFO_H_ -#define _I2C_FIFO_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** \file i2c_fifo.h - * - * \brief I2C non-blocking r/w. - */ - -/** - * \brief Pop a byte from I2C Rx FIFO. - * - * This function is non-blocking and assumes the Rx FIFO isn't empty. - * - * \param i2c I2C instance. - * \return uint8_t Byte value. - */ -static inline uint8_t i2c_read_byte(i2c_inst_t *i2c) { - i2c_hw_t *hw = i2c_get_hw(i2c); - assert(hw->status & I2C_IC_STATUS_RFNE_BITS); // Rx FIFO must not be empty - return (uint8_t)hw->data_cmd; -} - -/** - * \brief Push a byte into I2C Tx FIFO. - * - * This function is non-blocking and assumes the Tx FIFO isn't full. - * - * \param i2c I2C instance. - * \param value Byte value. - */ -static inline void i2c_write_byte(i2c_inst_t *i2c, uint8_t value) { - i2c_hw_t *hw = i2c_get_hw(i2c); - assert(hw->status & I2C_IC_STATUS_TFNF_BITS); // Tx FIFO must not be full - hw->data_cmd = value; -} - -#ifdef __cplusplus -} -#endif - -#endif // _I2C_FIFO_H_ diff --git a/i2c_slave.c b/i2c_slave.c deleted file mode 100644 index 1eb21dc..0000000 --- a/i2c_slave.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2021 Valentin Milea - * - * SPDX-License-Identifier: MIT - */ - -#include "i2c_slave.h" -#include "hardware/irq.h" - -typedef struct i2c_slave_t -{ - i2c_inst_t *i2c; - i2c_slave_handler_t handler; - bool transfer_in_progress; -} i2c_slave_t; - -static i2c_slave_t i2c_slaves[2]; - -static inline void finish_transfer(i2c_slave_t *slave) { - if (slave->transfer_in_progress) { - slave->handler(slave->i2c, I2C_SLAVE_FINISH); - slave->transfer_in_progress = false; - } -} - -static void __not_in_flash_func(i2c_slave_irq_handler)(i2c_slave_t *slave) { - i2c_inst_t *i2c = slave->i2c; - i2c_hw_t *hw = i2c_get_hw(i2c); - - uint32_t intr_stat = hw->intr_stat; - if (intr_stat == 0) { - return; - } - if (intr_stat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) { - hw->clr_tx_abrt; - finish_transfer(slave); - } - if (intr_stat & I2C_IC_INTR_STAT_R_START_DET_BITS) { - hw->clr_start_det; - finish_transfer(slave); - } - if (intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { - hw->clr_stop_det; - finish_transfer(slave); - } - if (intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) { - slave->transfer_in_progress = true; - slave->handler(i2c, I2C_SLAVE_RECEIVE); - } - if (intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS) { - hw->clr_rd_req; - slave->transfer_in_progress = true; - slave->handler(i2c, I2C_SLAVE_REQUEST); - } -} - -static void __not_in_flash_func(i2c0_slave_irq_handler)() { - i2c_slave_irq_handler(&i2c_slaves[0]); -} - -static void __not_in_flash_func(i2c1_slave_irq_handler)() { - i2c_slave_irq_handler(&i2c_slaves[1]); -} - -void i2c_slave_init(i2c_inst_t *i2c, uint8_t address, i2c_slave_handler_t handler) { - assert(i2c == i2c0 || i2c == i2c1); - assert(handler != NULL); - - uint i2c_index = i2c_hw_index(i2c); - i2c_slave_t *slave = &i2c_slaves[i2c_index]; - slave->i2c = i2c; - slave->handler = handler; - - // Note: The I2C slave does clock stretching implicitly after a RD_REQ, while the Tx FIFO is empty. - // There is also an option to enable clock stretching while the Rx FIFO is full, but we leave it - // disabled since the Rx FIFO should never fill up (unless slave->handler() is way too slow). - i2c_set_slave_mode(i2c, true, address); - - i2c_hw_t *hw = i2c_get_hw(i2c); - // unmask necessary interrupts - hw->intr_mask = I2C_IC_INTR_MASK_M_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS | I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS | I2C_IC_INTR_MASK_M_STOP_DET_BITS | I2C_IC_INTR_MASK_M_START_DET_BITS; - - // enable interrupt for current core - uint num = I2C0_IRQ + i2c_index; - irq_set_exclusive_handler(num, i2c_index == 0 ? i2c0_slave_irq_handler : i2c1_slave_irq_handler); - irq_set_enabled(num, true); -} - -void i2c_slave_deinit(i2c_inst_t *i2c) { - assert(i2c == i2c0 || i2c == i2c1); - - uint i2c_index = i2c_hw_index(i2c); - i2c_slave_t *slave = &i2c_slaves[i2c_index]; - assert(slave->i2c == i2c); // should be called after i2c_slave_init() - - slave->i2c = NULL; - slave->handler = NULL; - slave->transfer_in_progress = false; - - uint num = I2C0_IRQ + i2c_index; - irq_set_enabled(num, false); - irq_remove_handler(num, i2c_index == 0 ? i2c0_slave_irq_handler : i2c1_slave_irq_handler); - - i2c_hw_t *hw = i2c_get_hw(i2c); - hw->intr_mask = I2C_IC_INTR_MASK_RESET; - - i2c_set_slave_mode(i2c, false, 0); -} diff --git a/i2c_slave.h b/i2c_slave.h deleted file mode 100644 index 4505e66..0000000 --- a/i2c_slave.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2021 Valentin Milea - * - * SPDX-License-Identifier: MIT - */ - -#ifndef _I2C_SLAVE_H_ -#define _I2C_SLAVE_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** \file i2c_slave.h - * - * \brief I2C slave setup. - */ - -/** - * \brief I2C slave event types. - */ -typedef enum i2c_slave_event_t -{ - I2C_SLAVE_RECEIVE, /**< Data from master is available for reading. Slave must read from Rx FIFO. */ - I2C_SLAVE_REQUEST, /**< Master is requesting data. Slave must write into Tx FIFO. */ - I2C_SLAVE_FINISH, /**< Master has sent a Stop or Restart signal. Slave may prepare for the next transfer. */ -} i2c_slave_event_t; - -/** - * \brief I2C slave event handler - * - * The event handler will run from the I2C ISR, so it should return quickly (under 25 us at 400 kb/s). - * Avoid blocking inside the handler and split large data transfers across multiple calls for best results. - * When sending data to master, up to `i2c_get_write_available()` bytes can be written without blocking. - * When receiving data from master, up to `i2c_get_read_available()` bytes can be read without blocking. - * - * \param i2c Slave I2C instance. - * \param event Event type. - */ -typedef void (*i2c_slave_handler_t)(i2c_inst_t *i2c, i2c_slave_event_t event); - -/** - * \brief Configure I2C instance for slave mode. - * - * \param i2c I2C instance. - * \param address 7-bit slave address. - * \param handler Called on events from I2C master. It will run from the I2C ISR, on the CPU core - * where the slave was initialized. - */ -void i2c_slave_init(i2c_inst_t *i2c, uint8_t address, i2c_slave_handler_t handler); - -/** - * \brief Restore I2C instance to master mode. - * - * \param i2c I2C instance. - */ -void i2c_slave_deinit(i2c_inst_t *i2c); - -#ifdef __cplusplus -} -#endif - -#endif // _I2C_SLAVE_H_ diff --git a/main.c b/main.c index 7571d1a..09f819f 100644 --- a/main.c +++ b/main.c @@ -1,18 +1,26 @@ +/* + * Copyright (c) 2021 Valentin Milea + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include #include -#include "pico/stdlib.h" -#include "pico/multicore.h" -#include "hardware/i2c.h" -#include "hardware/pwm.h" -#include "i2c_fifo.h" -#include "i2c_slave.h" +#include -#define I2C0_SDA_PIN 18 -#define I2C0_SCL_PIN 19 +static const uint I2C_SLAVE_ADDRESS = 0x17; +static const uint I2C_BAUDRATE = 100000; // 100 kHz -#define I2C_SLAVE_ADDRESS 0x10 - -static const uint I2C_SLAVE_SDA_PIN = I2C0_SDA_PIN; -static const uint I2C_SLAVE_SCL_PIN = I2C0_SCL_PIN; +#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 @@ -32,17 +40,17 @@ static void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t 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(i2c); + 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(i2c); + 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(i2c, context.mem[context.mem_address]); + i2c_write_byte_raw(i2c, context.mem[context.mem_address]); context.mem_address++; break; case I2C_SLAVE_FINISH: // master has signalled Stop / Restart @@ -53,17 +61,6 @@ static void i2c_slave_handler(i2c_inst_t *i2c, i2c_slave_event_t event) { } } -void i2c_set_slave_mode_perso(i2c_inst_t *i2c, uint8_t addr) { - i2c->hw->enable = 0; - - //while( !(i2c->hw->enable_status & 0x1) ); - - i2c->hw->sar = addr; - i2c->hw->con = 0; - - i2c->hw->enable = 1; -} - static void setup_slave() { gpio_init(I2C_SLAVE_SDA_PIN); gpio_set_function(I2C_SLAVE_SDA_PIN, GPIO_FUNC_I2C); @@ -73,26 +70,28 @@ static void setup_slave() { 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); } +#endif - -void main(void) -{ +int main() { stdio_init_all(); + sleep_ms(5000); +#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(); - - gpio_set_function(0, GPIO_FUNC_PWM); - uint slice_num = pwm_gpio_to_slice_num(0); - pwm_set_wrap(slice_num, 255); - pwm_set_enabled(slice_num, true); - + //run_master(); while(1){ - printf(">adc:%d\n", context.mem[0]); - pwm_set_chan_level(slice_num, PWM_CHAN_A, context.mem[0]); - sleep_ms(10); + puts("\nI2C slave example\n"); + sleep_ms(1000); } -} - - +#endif +} \ No newline at end of file