arch/arm/src/stm32/stm32_i2cslave_v2.c: add STM32 I2C Slave support for the v2 ip core
This commit adds the lowerhalf driver support for the I2C Slave. While not currently ideal, it is compatible with the upperhalf i2c slave driver. A workqueue can be used to delegate the isr work to the upperhalf driver. But keep in mind wq introduces a lot of delay and in certain scenarios, it is better to write your own better upperhalf driver. Signed-off-by: Stepan Pressl <pressl.stepan@gmail.com>
This commit is contained in:
parent
3d37e85b13
commit
55bef681e1
6 changed files with 934 additions and 5 deletions
|
|
@ -2,7 +2,7 @@
|
|||
I2C Device Drivers
|
||||
==================
|
||||
|
||||
- ``include/nuttx/i2c/i2c_master.h`` and ``include/nuttx/i2c/i2c_slave.h``.
|
||||
- ``include/nuttx/i2c/i2c_master.h``
|
||||
All structures and APIs needed to work with I2C drivers are provided in
|
||||
this header file.
|
||||
|
||||
|
|
@ -22,10 +22,59 @@ I2C Device Drivers
|
|||
- **Examples**: ``arch/z80/src/ez80/ez80_i2c.c``,
|
||||
``arch/z80/src/z8/z8_i2c.c``, etc.
|
||||
|
||||
- ``struct i2c_slaveops_s``. Each I2C slave device driver must implement
|
||||
|
||||
========================
|
||||
I2C Slave Device Drivers
|
||||
========================
|
||||
|
||||
- ``include/nuttx/i2c/i2c_slave.h``
|
||||
Declaration of all macros and ops and ``int i2c_slave_register``,
|
||||
used to bind the lowerhalf driver to the upper one and register an
|
||||
I2C slave device.
|
||||
|
||||
- **Binding I2C Slave Drivers**. Use ``int i2c_slave_register`` in your BSP
|
||||
to register your slave device. Before that, you need to get the instance
|
||||
of ``struct i2c_slave_s`` from the hardware-specific I2C Slave driver.
|
||||
|
||||
- **Using I2C Slave in your Application**. I2C slave drivers are normally directly
|
||||
accessed by user code, We can read and write to device nodes using posix
|
||||
interfaces. The device is registered as ``/dev/i2cslv%d``, where ``%d``
|
||||
is a number provided in the BSP initialization phase.
|
||||
|
||||
- **BSP initialization example (STM32 I2C Slave)**:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int stm32_i2cs_setup(void)
|
||||
{
|
||||
i2cs = stm32_i2cbus_slaveinitialize(1);
|
||||
if (i2cs != NULL)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
return i2c_slave_register(i2cs, 1, 0x01, 7);
|
||||
}
|
||||
|
||||
I2C Slave Driver API
|
||||
--------------------
|
||||
|
||||
.. c:function:: int i2c_slave_register(FAR struct i2c_slave_s *dev, int bus, int addr, int nbit);
|
||||
|
||||
Bind the lowerhalf ``dev`` driver to the upperhalf driver and register
|
||||
a device. The name of the device is ``/dev/i2cslv%d``, where ``%d`` is
|
||||
bus. The address the I2C slave is specified in ``addr``.
|
||||
``nbit`` is either 7 or 10 and it specifies the address format.
|
||||
|
||||
|
||||
- ``struct i2c_slaveops_s``. Each I2C slave lowerhalf driver must implement
|
||||
an instance of ``struct i2c_slaveops_s``. That structure defines a call
|
||||
table with the following methods:
|
||||
|
||||
- **Binding I2C Slave Drivers**. I2C slave drivers are normally directly
|
||||
accessed by user code, We can read and write to device nodes using posix
|
||||
interfaces.
|
||||
- ``setownaddress``: the address the slave responds to,
|
||||
- ``write``: sets the tx buffer pointer,
|
||||
- ``read``: sets the rx buffer pointer,
|
||||
- ``registercallback``: registers the callback function which should
|
||||
be called from a service routine. Signals the received or fully transferred
|
||||
I2C packet.
|
||||
- ``setup``: initializes the peripheral,
|
||||
- ``shutdown``: shutdowns the peripheral.
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ if(CONFIG_STM32_HAVE_IP_I2C_V1)
|
|||
endif()
|
||||
elseif(CONFIG_STM32_HAVE_IP_I2C_V2)
|
||||
list(APPEND SRCS stm32_i2c_v2.c)
|
||||
if(CONFIG_STM32_I2C_SLAVE)
|
||||
list(APPEND SRCS stm32_i2cslave_v2.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_USBDEV)
|
||||
|
|
|
|||
|
|
@ -3024,6 +3024,24 @@ config STM32_I2C3
|
|||
depends on STM32_HAVE_I2C3
|
||||
select STM32_I2C
|
||||
|
||||
config STM32_I2C1_SLAVE
|
||||
bool "I2C1 Slave"
|
||||
default n
|
||||
depends on !STM32_I2C1 && I2C_SLAVE
|
||||
select STM32_I2C_SLAVE
|
||||
|
||||
config STM32_I2C2_SLAVE
|
||||
bool "I2C2 Slave"
|
||||
default n
|
||||
depends on STM32_HAVE_I2C2 && !STM32_I2C2 && I2C_SLAVE
|
||||
select STM32_I2C_SLAVE
|
||||
|
||||
config STM32_I2C3_SLAVE
|
||||
bool "I2C3 Slave"
|
||||
default n
|
||||
depends on STM32_HAVE_I2C3 && !STM32_I2C3 && I2C_SLAVE
|
||||
select STM32_I2C_SLAVE
|
||||
|
||||
config STM32_LPTIM1
|
||||
bool "LPTIM1"
|
||||
default n
|
||||
|
|
@ -3421,6 +3439,10 @@ config STM32_I2C
|
|||
bool
|
||||
default n
|
||||
|
||||
config STM32_I2C_SLAVE
|
||||
bool
|
||||
default n
|
||||
|
||||
config STM32_CAN
|
||||
bool
|
||||
default n
|
||||
|
|
@ -10720,6 +10742,31 @@ config STM32_I2C_DMA
|
|||
|
||||
endmenu
|
||||
|
||||
menu "I2C Slave Configuration"
|
||||
depends on STM32_I2C_SLAVE
|
||||
|
||||
config STM32_I2C_SLAVE_DEFAULT_TX
|
||||
hex "Default TX byte to be sent when the TX buffer is empty"
|
||||
default 0xFF
|
||||
|
||||
config STM32_I2C_SLAVE_USEWQ
|
||||
bool "Use work queue to delegate the isr completion status"
|
||||
default n
|
||||
---help---
|
||||
With the current upperhalf I2C slave driver implementation, the user
|
||||
should delegate the callback completion status using a work queue.
|
||||
However, work queues introduce a delay, so in certain scenarios
|
||||
it is better to use a custom driver without using a work queue.
|
||||
|
||||
config STM32_I2C_SLAVE_RETRANSFER
|
||||
bool "The frame is retransferred when stop is issued beforehand"
|
||||
default n
|
||||
---help---
|
||||
If stop is issued before the whole frame is transferred,
|
||||
the tx pointer is reset to 0.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "SDIO Configuration"
|
||||
depends on STM32_SDIO
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ CHIP_CSRCS += stm32_i2c.c
|
|||
endif
|
||||
else ifeq ($(CONFIG_STM32_HAVE_IP_I2C_V2),y)
|
||||
CHIP_CSRCS += stm32_i2c_v2.c
|
||||
ifeq ($(CONFIG_STM32_I2C_SLAVE),y)
|
||||
CHIP_CSRCS += stm32_i2cslave_v2.c
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_USBDEV),y)
|
||||
|
|
|
|||
|
|
@ -88,4 +88,38 @@ struct i2c_master_s *stm32_i2cbus_initialize(int port);
|
|||
|
||||
int stm32_i2cbus_uninitialize(struct i2c_master_s *dev);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2cbus_slaveinitialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize the selected I2C port as a slave. Return an unique
|
||||
* instance of struct i2c_slave_s.
|
||||
*
|
||||
* Input Parameters:
|
||||
* Port number (for hardware that has multiple I2C interfaces)
|
||||
*
|
||||
* Returned Value:
|
||||
* Valid I2C device structure reference on success; a NULL on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
struct i2c_slave_s *stm32_i2cbus_slaveinitialize(int port);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2cbus_uninitialize
|
||||
*
|
||||
* Description:
|
||||
* De-initialize the selected I2C port, and power down the device.
|
||||
*
|
||||
* Input Parameters:
|
||||
* Device structure as returned by the stm32_i2cbus_initialize()
|
||||
*
|
||||
* Returned Value:
|
||||
* OK on success, ERROR when internal reference count mismatch or dev
|
||||
* points to invalid hardware device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_i2cbus_uninitialize(struct i2c_master_s *dev);
|
||||
|
||||
#endif /* __ARCH_ARM_SRC_STM32_STM32_I2C_H */
|
||||
|
|
|
|||
793
arch/arm/src/stm32/stm32_i2cslave_v2.c
Normal file
793
arch/arm/src/stm32/stm32_i2cslave_v2.c
Normal file
|
|
@ -0,0 +1,793 @@
|
|||
/****************************************************************************
|
||||
* arch/arm/src/stm32/stm32_i2cslave_v2.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* TODO
|
||||
* - DMA
|
||||
* - SMBus adaptation
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/irq.h>
|
||||
#include <nuttx/mutex.h>
|
||||
#include <nuttx/clock.h>
|
||||
#include <nuttx/i2c/i2c_slave.h>
|
||||
|
||||
#include <arch/board/board.h>
|
||||
|
||||
#include "arm_internal.h"
|
||||
#include "hardware/stm32_pinmap.h"
|
||||
#include "hardware/stm32_i2c_v2.h"
|
||||
#include "stm32_gpio.h"
|
||||
#include "stm32_rcc.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define I2C_CR1_ALLINTS (I2C_CR1_RXIE | I2C_CR1_TXIE | I2C_CR1_ADDRIE | \
|
||||
I2C_CR1_STOPIE)
|
||||
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_USEWQ
|
||||
# ifdef CONFIG_SCHED_HPWORK
|
||||
# define I2CSWORK HPWORK
|
||||
# endif
|
||||
|
||||
# ifndef I2CSWORK
|
||||
# ifdef CONFIG_SCHED_LPWORK
|
||||
# define I2CSWORK LPWORK
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifndef I2CSWORK
|
||||
# error "For correct operation, you should define LPWORK or HPWORK."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct stm32_i2cslave_s
|
||||
{
|
||||
const struct i2c_slaveops_s *ops;
|
||||
int refs;
|
||||
mutex_t lock;
|
||||
uint32_t frequency;
|
||||
uint32_t base;
|
||||
uint32_t clk_bit;
|
||||
uint32_t reset_bit;
|
||||
uint32_t ev_irq;
|
||||
uint32_t scl_pin;
|
||||
uint32_t sda_pin;
|
||||
const uint8_t *tx_buffer;
|
||||
uint8_t *rx_buffer;
|
||||
int rx_buflen;
|
||||
int rx_curptr;
|
||||
int tx_buflen;
|
||||
int tx_curptr;
|
||||
int rx_received;
|
||||
i2c_slave_callback_t *callback;
|
||||
void *callback_arg;
|
||||
bool read;
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_USEWQ
|
||||
struct work_s irqwork; /* For deferring interrupt work to the wq */
|
||||
#endif
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_setup(struct i2c_slave_s *dev);
|
||||
static int stm32_i2c_shutdown(struct i2c_slave_s *dev);
|
||||
static int stm32_i2c_setownaddress(struct i2c_slave_s *dev, int addr,
|
||||
int nbits);
|
||||
static int stm32_i2c_write(struct i2c_slave_s *dev, const uint8_t *buffer,
|
||||
int buflen);
|
||||
static int stm32_i2c_read(struct i2c_slave_s *dev, uint8_t *buffer,
|
||||
int buflen);
|
||||
static int stm32_i2c_registercallback(struct i2c_slave_s *dev,
|
||||
i2c_slave_callback_t *callback,
|
||||
void *arg);
|
||||
static int stm32_i2c_init(struct stm32_i2cslave_s *priv);
|
||||
static int stm32_i2c_deinit(struct stm32_i2cslave_s *priv);
|
||||
static int stm32_i2c_isr(int irq, void *context, void *arg);
|
||||
static int stm32_i2c_isr_impl(struct stm32_i2cslave_s *priv);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const struct i2c_slaveops_s stm32_i2cslave_ops =
|
||||
{
|
||||
.setownaddress = stm32_i2c_setownaddress,
|
||||
.write = stm32_i2c_write,
|
||||
.read = stm32_i2c_read,
|
||||
.registercallback = stm32_i2c_registercallback,
|
||||
.setup = stm32_i2c_setup,
|
||||
.shutdown = stm32_i2c_shutdown
|
||||
};
|
||||
|
||||
#ifdef CONFIG_STM32_I2C1_SLAVE
|
||||
static struct stm32_i2cslave_s stm32_i2c1_priv =
|
||||
{
|
||||
.ops = &stm32_i2cslave_ops,
|
||||
.refs = 0,
|
||||
.lock = NXMUTEX_INITIALIZER,
|
||||
.frequency = 0,
|
||||
.base = STM32_I2C1_BASE,
|
||||
.clk_bit = RCC_APB1ENR_I2C1EN,
|
||||
.reset_bit = RCC_APB1RSTR_I2C1RST,
|
||||
.ev_irq = STM32_IRQ_I2C1EV,
|
||||
.scl_pin = GPIO_I2C1_SCL,
|
||||
.sda_pin = GPIO_I2C1_SDA,
|
||||
.tx_buffer = NULL,
|
||||
.rx_buffer = NULL,
|
||||
.rx_buflen = 0,
|
||||
.rx_curptr = 0,
|
||||
.tx_curptr = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_STM32_I2C2_SLAVE
|
||||
static struct stm32_i2cslave_s stm32_i2c2_priv =
|
||||
{
|
||||
.ops = &stm32_i2cslave_ops,
|
||||
.refs = 0,
|
||||
.lock = NXMUTEX_INITIALIZER,
|
||||
.frequency = 0,
|
||||
.base = STM32_I2C2_BASE,
|
||||
.clk_bit = RCC_APB1ENR_I2C2EN,
|
||||
.reset_bit = RCC_APB1RSTR_I2C2RST,
|
||||
.ev_irq = STM32_IRQ_I2C2EV,
|
||||
.scl_pin = GPIO_I2C2_SCL,
|
||||
.sda_pin = GPIO_I2C2_SDA,
|
||||
.tx_buffer = NULL,
|
||||
.rx_buffer = NULL,
|
||||
.rx_buflen = 0,
|
||||
.rx_curptr = 0,
|
||||
.tx_curptr = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_STM32_I2C3_SLAVE
|
||||
static struct stm32_i2cslave_s stm32_i2c3_priv =
|
||||
{
|
||||
.ops = &stm32_i2cslave_ops,
|
||||
.refs = 0,
|
||||
.lock = NXMUTEX_INITIALIZER,
|
||||
.frequency = 0,
|
||||
.base = STM32_I2C3_BASE,
|
||||
.clk_bit = RCC_APB1ENR_I2C3EN,
|
||||
.reset_bit = RCC_APB1RSTR_I2C3RST,
|
||||
.ev_irq = STM32_IRQ_I2C3EV,
|
||||
.scl_pin = GPIO_I2C3_SCL,
|
||||
.sda_pin = GPIO_I2C3_SDA,
|
||||
.tx_buffer = NULL,
|
||||
.rx_buffer = NULL,
|
||||
.rx_buflen = 0,
|
||||
.rx_curptr = 0,
|
||||
.tx_curptr = 0,
|
||||
};
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_getreg
|
||||
*
|
||||
* Description:
|
||||
* Get a 16-bit register value by offset
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline uint16_t stm32_i2c_getreg(struct stm32_i2cslave_s *priv,
|
||||
uint8_t offset)
|
||||
{
|
||||
return getreg16(priv->base + offset);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_getreg32
|
||||
*
|
||||
* Description:
|
||||
* Get a 32-bit register value by offset
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline uint32_t stm32_i2c_getreg32(struct stm32_i2cslave_s *priv,
|
||||
uint8_t offset)
|
||||
{
|
||||
return getreg32(priv->base + offset);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_putreg
|
||||
*
|
||||
* Description:
|
||||
* Put a 16-bit register value by offset
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline void stm32_i2c_putreg(struct stm32_i2cslave_s *priv,
|
||||
uint8_t offset, uint16_t value)
|
||||
{
|
||||
putreg16(value, priv->base + offset);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_putreg32
|
||||
*
|
||||
* Description:
|
||||
* Put a 32-bit register value by offset
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static inline void stm32_i2c_putreg32(struct stm32_i2cslave_s *priv,
|
||||
uint8_t offset, uint32_t value)
|
||||
{
|
||||
putreg32(value, priv->base + offset);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_isr
|
||||
*
|
||||
* Description:
|
||||
* Common I2C interrupt service routine
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_isr(int irq, void *context, void *arg)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)arg;
|
||||
DEBUGASSERT(priv != NULL);
|
||||
return stm32_i2c_isr_impl(priv);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_rxdone_work
|
||||
*
|
||||
* Description:
|
||||
* A routine for delegating frame reception information
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_USEWQ
|
||||
static void stm32_i2c_rxdone_work(void *arg)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)arg;
|
||||
priv->callback(priv->callback_arg, I2CS_RX_COMPLETE,
|
||||
priv->rx_curptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_rxdone_work
|
||||
*
|
||||
* Description:
|
||||
* A routine for delegating tx frame information
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_USEWQ
|
||||
static void stm32_i2c_txdone_work(void *arg)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)arg;
|
||||
priv->callback(priv->callback_arg, I2CS_TX_COMPLETE,
|
||||
priv->tx_curptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_isr_impl
|
||||
*
|
||||
* Description:
|
||||
* Interrupt handler
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_isr_impl(struct stm32_i2cslave_s *priv)
|
||||
{
|
||||
volatile uint32_t isr;
|
||||
volatile uint8_t rx;
|
||||
volatile uint8_t tx;
|
||||
|
||||
/* Get the status register first. */
|
||||
|
||||
isr = stm32_i2c_getreg32(priv, STM32_I2C_ISR_OFFSET);
|
||||
|
||||
/* Was the TX completed? */
|
||||
|
||||
if ((isr & I2C_ISR_TXIS) != 0)
|
||||
{
|
||||
/* Check, if anything must be sent */
|
||||
|
||||
if (priv->tx_curptr < priv->tx_buflen - 1)
|
||||
{
|
||||
/* Yes... */
|
||||
|
||||
priv->tx_curptr++;
|
||||
tx = priv->tx_buffer[priv->tx_curptr];
|
||||
}
|
||||
else if (priv->tx_curptr == priv->tx_buflen - 1)
|
||||
{
|
||||
tx = CONFIG_STM32_I2C_SLAVE_DEFAULT_TX;
|
||||
if (priv->callback)
|
||||
{
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_USEWQ
|
||||
work_queue(I2CSWORK, &priv->irqwork,
|
||||
stm32_i2c_txdone_work, priv, 0);
|
||||
#else
|
||||
priv->callback(priv->callback_arg, I2CS_TX_COMPLETE,
|
||||
priv->tx_curptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No... Send the default value. */
|
||||
|
||||
tx = CONFIG_STM32_I2C_SLAVE_DEFAULT_TX;
|
||||
}
|
||||
|
||||
stm32_i2c_putreg(priv, STM32_I2C_TXDR_OFFSET, tx);
|
||||
}
|
||||
|
||||
/* Was a byte received? */
|
||||
|
||||
if ((isr & I2C_ISR_RXNE) != 0)
|
||||
{
|
||||
/* Write it if not overflowed. RXDR must be read to clear int. */
|
||||
|
||||
rx = stm32_i2c_getreg(priv, STM32_I2C_RXDR_OFFSET);
|
||||
if (priv->rx_curptr < priv->rx_buflen)
|
||||
{
|
||||
priv->rx_buffer[priv->rx_curptr++] = rx;
|
||||
}
|
||||
}
|
||||
|
||||
/* Was the stop condition detected? */
|
||||
|
||||
if ((isr & I2C_INT_STOP) != 0)
|
||||
{
|
||||
/* Clear the interrupt. */
|
||||
|
||||
stm32_i2c_putreg(priv, STM32_I2C_ICR_OFFSET, I2C_INT_STOP);
|
||||
|
||||
/* If RX was present, notify the upper driver. */
|
||||
|
||||
if (priv->read)
|
||||
{
|
||||
if (priv->callback)
|
||||
{
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_USEWQ
|
||||
work_queue(I2CSWORK, &priv->irqwork,
|
||||
stm32_i2c_rxdone_work, priv, 0);
|
||||
#else
|
||||
priv->callback(priv->callback_arg, I2CS_RX_COMPLETE,
|
||||
priv->rx_curptr);
|
||||
priv->rx_curptr = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_RETRANSFER
|
||||
else
|
||||
{
|
||||
priv->tx_curptr = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Was an address matched? */
|
||||
|
||||
if ((isr & I2C_INT_ADDR) != 0)
|
||||
{
|
||||
/* Clear the address match flag. */
|
||||
|
||||
stm32_i2c_putreg(priv, STM32_I2C_ICR_OFFSET, I2C_INT_ADDR);
|
||||
|
||||
/* Repeated Start */
|
||||
|
||||
if (priv->rx_curptr > 0 && priv->callback)
|
||||
{
|
||||
#ifdef CONFIG_STM32_I2C_SLAVE_USEWQ
|
||||
work_queue(I2CSWORK, &priv->irqwork,
|
||||
stm32_i2c_rxdone_work, priv, 0);
|
||||
#else
|
||||
priv->callback(priv->callback_arg, I2CS_RX_COMPLETE,
|
||||
priv->rx_curptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check whether RX or TX should be done. */
|
||||
|
||||
if ((isr & I2C_ISR_DIR) != 0)
|
||||
{
|
||||
/* Write transfer. Flush the TX buffer by writing ISR_TXE.
|
||||
* Then send any remaining data. Or send the default TX byte.
|
||||
*/
|
||||
|
||||
stm32_i2c_putreg32(priv, STM32_I2C_ISR_OFFSET, I2C_ISR_TXE);
|
||||
|
||||
if (priv->tx_curptr < priv->tx_buflen)
|
||||
{
|
||||
tx = priv->tx_buffer[priv->tx_curptr];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Nothing to be sent. */
|
||||
|
||||
tx = CONFIG_STM32_I2C_SLAVE_DEFAULT_TX;
|
||||
}
|
||||
|
||||
stm32_i2c_putreg(priv, STM32_I2C_TXDR_OFFSET, tx);
|
||||
priv->read = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Initialize the reading. */
|
||||
|
||||
priv->read = true;
|
||||
priv->rx_curptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_setownaddress
|
||||
*
|
||||
* Description:
|
||||
* Sets up the address of the I2C Slave
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_setownaddress(struct i2c_slave_s *dev, int addr,
|
||||
int nbits)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)dev;
|
||||
uint16_t oar1;
|
||||
uint32_t cr1;
|
||||
|
||||
i2cinfo("SETOWNADDR %d\n", addr);
|
||||
|
||||
/* STM32 supports up to 2 addresses the slave can respond to.
|
||||
* However, NuttX supports the setting of only one address.
|
||||
* Use only the first "OWN ADDRESS" (I2C_OAR1)
|
||||
* Before this, reset the peripheral by disabling it.
|
||||
* If the peripheral is launched for the first time, this does nothing.
|
||||
*/
|
||||
|
||||
cr1 = stm32_i2c_getreg32(priv, STM32_I2C_CR1_OFFSET);
|
||||
stm32_i2c_putreg32(priv, STM32_I2C_CR1_OFFSET,
|
||||
cr1 & ~(I2C_CR1_ALLINTS | I2C_CR1_PE));
|
||||
|
||||
/* Clear I2C_OAR1_OA1EN, then configure I2C_OAR1_OA1[9:0].
|
||||
* Afterwards, set I2C_OAR1_{OA1MODE, OA1EN}.
|
||||
* According to i2c_slave_open in i2c_slave_driver.c, SETOWNADDRESS comes
|
||||
* after SETUP. Therefore, enable the peripheral and RX and TX interrupts
|
||||
* here.
|
||||
*
|
||||
* Attention: If a 10 bit address is used, all 10 bits are used.
|
||||
* However, bits 7:1 are used (instead of 6:0) in the 7 bit mode.
|
||||
* Therefore, the address must be shifted.
|
||||
*/
|
||||
|
||||
if (nbits == 10)
|
||||
{
|
||||
oar1 = ((uint16_t) addr) & 0x03ff;
|
||||
oar1 |= I2C_OAR1_OA1MODE;
|
||||
}
|
||||
else if (nbits == 7)
|
||||
{
|
||||
oar1 = ((uint16_t) addr << 1) & 0x00fe;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wrong nbits. */
|
||||
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Clear OA1EN (whole register can be cleared) */
|
||||
|
||||
stm32_i2c_putreg32(priv, STM32_I2C_OAR1_OFFSET, 0);
|
||||
stm32_i2c_putreg32(priv, STM32_I2C_OAR1_OFFSET, oar1);
|
||||
|
||||
/* Enable the address here. */
|
||||
|
||||
oar1 |= I2C_OAR1_OA1EN;
|
||||
stm32_i2c_putreg32(priv, STM32_I2C_OAR1_OFFSET, oar1);
|
||||
|
||||
/* Enable the peripheral and interrupts */
|
||||
|
||||
priv->read = false;
|
||||
stm32_i2c_putreg32(priv, STM32_I2C_CR1_OFFSET,
|
||||
I2C_CR1_ALLINTS | I2C_CR1_PE);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_setup
|
||||
*
|
||||
* Description:
|
||||
* Sets up the STM32 I2C peripheral
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_setup(struct i2c_slave_s *dev)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)dev;
|
||||
DEBUGASSERT(dev);
|
||||
|
||||
/* Enable the interrupts here. This function is called when the device
|
||||
* is opened for the first time.
|
||||
*/
|
||||
|
||||
irq_attach(priv->ev_irq, stm32_i2c_isr, priv);
|
||||
up_enable_irq(priv->ev_irq);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_shutdown
|
||||
*
|
||||
* Description:
|
||||
* Shutdown the STM32 I2C peripheral
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_shutdown(struct i2c_slave_s *dev)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)dev;
|
||||
uint32_t cr1;
|
||||
|
||||
DEBUGASSERT(dev);
|
||||
|
||||
/* Disable I2C_CR1_PE. Disabling the I2C should have no effect
|
||||
* on configuration bits, and SCL and SDA lines are released.
|
||||
* Disable TX and TX interrupts.
|
||||
*/
|
||||
|
||||
cr1 = stm32_i2c_getreg32(priv, STM32_I2C_CR1_OFFSET);
|
||||
stm32_i2c_putreg32(priv, STM32_I2C_CR1_OFFSET,
|
||||
cr1 & ~(I2C_CR1_ALLINTS | I2C_CR1_PE));
|
||||
|
||||
/* Disable the interrupts here. This function is called when the device
|
||||
* is closed by the last task.
|
||||
*/
|
||||
|
||||
up_disable_irq(priv->ev_irq);
|
||||
irq_detach(priv->ev_irq);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_write
|
||||
*
|
||||
* Description:
|
||||
* Receive a pointer to a buffer where to write data to
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_write(struct i2c_slave_s *dev, const uint8_t *buffer,
|
||||
int buflen)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)dev;
|
||||
int flags;
|
||||
|
||||
DEBUGASSERT(dev);
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Initialize the TX buffer. */
|
||||
|
||||
priv->tx_buffer = buffer;
|
||||
priv->tx_buflen = buflen;
|
||||
priv->tx_curptr = 0;
|
||||
|
||||
leave_critical_section(flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_read
|
||||
*
|
||||
* Description:
|
||||
* Receive a pointer to a buffer where to read data to
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_read(struct i2c_slave_s *dev, uint8_t *buffer,
|
||||
int buflen)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)dev;
|
||||
int flags;
|
||||
|
||||
DEBUGASSERT(dev);
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Initialize the RX buffer. */
|
||||
|
||||
priv->rx_buffer = buffer;
|
||||
priv->rx_buflen = buflen;
|
||||
|
||||
leave_critical_section(flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_registercallback
|
||||
*
|
||||
* Description:
|
||||
* Register a function which notifies the upperhalf driver
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_registercallback(struct i2c_slave_s *dev,
|
||||
i2c_slave_callback_t *callback,
|
||||
void *arg)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)dev;
|
||||
int flags;
|
||||
|
||||
DEBUGASSERT(dev);
|
||||
flags = enter_critical_section();
|
||||
|
||||
/* Initialize the pointer to a callback. */
|
||||
|
||||
priv->callback = callback;
|
||||
priv->callback_arg = arg;
|
||||
|
||||
leave_critical_section(flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_init
|
||||
*
|
||||
* Description:
|
||||
* Initialize STM32 I2C peripheral - clocks and pins
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_init(struct stm32_i2cslave_s *priv)
|
||||
{
|
||||
DEBUGASSERT(priv);
|
||||
|
||||
modifyreg32(STM32_RCC_APB1ENR, 0, priv->clk_bit);
|
||||
modifyreg32(STM32_RCC_APB1RSTR, 0, priv->reset_bit);
|
||||
modifyreg32(STM32_RCC_APB1RSTR, priv->reset_bit, 0);
|
||||
|
||||
if (stm32_configgpio(priv->scl_pin) < 0)
|
||||
{
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if (stm32_configgpio(priv->sda_pin) < 0)
|
||||
{
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2c_deinit
|
||||
*
|
||||
* Description:
|
||||
* Deinitialize STM32 I2C peripheral - clocks and pins
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int stm32_i2c_deinit(struct stm32_i2cslave_s *priv)
|
||||
{
|
||||
DEBUGASSERT(priv);
|
||||
|
||||
modifyreg32(STM32_RCC_APB1ENR, priv->clk_bit, 0);
|
||||
modifyreg32(STM32_RCC_APB1RSTR, 0, priv->reset_bit);
|
||||
stm32_unconfiggpio(priv->scl_pin);
|
||||
stm32_configgpio(priv->sda_pin);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2cbus_slaveinitialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize on I2C bus as a slave and get pointer to i2c_slave_s struct
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
struct i2c_slave_s *stm32_i2cbus_slaveinitialize(int port)
|
||||
{
|
||||
int ret;
|
||||
struct stm32_i2cslave_s *priv = NULL;
|
||||
|
||||
i2cinfo("SLAVEINIT");
|
||||
|
||||
switch (port)
|
||||
{
|
||||
#ifdef CONFIG_STM32_I2C1_SLAVE
|
||||
case 1:
|
||||
priv = (struct stm32_i2cslave_s *)&stm32_i2c1_priv;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_STM32_I2C2_SLAVE
|
||||
case 2:
|
||||
priv = (struct stm32_i2cslave_s *)&stm32_i2c2_priv;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_STM32_I2C3_SLAVE
|
||||
case 3:
|
||||
priv = (struct stm32_i2cslave_s *)&stm32_i2c3_priv;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nxmutex_lock(&priv->lock);
|
||||
if (priv->refs++ == 0)
|
||||
{
|
||||
ret = stm32_i2c_init(priv);
|
||||
if (ret < 0)
|
||||
{
|
||||
stm32_i2c_deinit(priv);
|
||||
priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nxmutex_unlock(&priv->lock);
|
||||
return (struct i2c_slave_s *)priv;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: stm32_i2cbus_slaveunitialize
|
||||
*
|
||||
* Description:
|
||||
* Denitialize a given I2C device.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int stm32_i2cbus_slaveunitialize(struct i2c_slave_s *dev)
|
||||
{
|
||||
struct stm32_i2cslave_s *priv = (struct stm32_i2cslave_s *)dev;
|
||||
stm32_i2c_deinit(priv);
|
||||
return OK;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue