Esclave I2C fonctionnel, GP4/GP5
This commit is contained in:
parent
11a8a2ef5a
commit
c24a887818
@ -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)
|
||||
|
||||
53
i2c_fifo.h
53
i2c_fifo.h
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Valentin Milea <valentin.milea@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef _I2C_FIFO_H_
|
||||
#define _I2C_FIFO_H_
|
||||
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#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_
|
||||
108
i2c_slave.c
108
i2c_slave.c
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Valentin Milea <valentin.milea@gmail.com>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
65
i2c_slave.h
65
i2c_slave.h
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Valentin Milea <valentin.milea@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef _I2C_SLAVE_H_
|
||||
#define _I2C_SLAVE_H_
|
||||
|
||||
#include <hardware/i2c.h>
|
||||
|
||||
#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_
|
||||
79
main.c
79
main.c
@ -1,18 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Valentin Milea <valentin.milea@gmail.com>
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <hardware/i2c.h>
|
||||
#include <pico/i2c_slave.h>
|
||||
#include <pico/stdlib.h>
|
||||
#include <stdio.h>
|
||||
#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 <string.h>
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user