123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- /****************************************************************************
- * drivers/i2c/i2c_bitbang.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.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <assert.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/spinlock.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/i2c/i2c_master.h>
- #include <nuttx/i2c/i2c_bitbang.h>
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- struct i2c_bitbang_dev_s
- {
- struct i2c_master_s i2c;
- struct i2c_bitbang_lower_dev_s *lower;
- #ifndef CONFIG_I2C_BITBANG_NO_DELAY
- int32_t delay;
- #endif
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- static int i2c_bitbang_transfer(FAR struct i2c_master_s *dev,
- FAR struct i2c_msg_s *msgs, int count);
- static int i2c_bitbang_set_scl(FAR struct i2c_bitbang_dev_s *dev,
- bool high, bool nodelay);
- static void i2c_bitbang_set_sda(FAR struct i2c_bitbang_dev_s *dev,
- bool high);
- static int i2c_bitbang_wait_ack(FAR struct i2c_bitbang_dev_s *dev);
- static void i2c_bitbang_send(FAR struct i2c_bitbang_dev_s *dev,
- uint8_t data);
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- static const struct i2c_ops_s g_i2c_ops =
- {
- .transfer = i2c_bitbang_transfer
- };
- /****************************************************************************
- * Inline Functions
- ****************************************************************************/
- inline static bool i2c_bitbang_get_sda(FAR struct i2c_bitbang_dev_s *dev)
- {
- return dev->lower->ops->get_sda(dev->lower);
- }
- #ifdef CONFIG_I2C_BITBANG_CLOCK_STRETCHING
- inline static bool i2c_bitbang_get_scl(FAR struct i2c_bitbang_dev_s *dev)
- {
- return dev->lower->ops->get_scl(dev->lower);
- }
- #endif
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: i2c_bitbang_transfer
- ****************************************************************************/
- static int i2c_bitbang_transfer(FAR struct i2c_master_s *dev,
- FAR struct i2c_msg_s *msgs, int count)
- {
- FAR struct i2c_bitbang_dev_s *priv = (FAR struct i2c_bitbang_dev_s *)dev;
- int ret = OK;
- int i;
- irqstate_t flags;
- /* Lock to enforce timings */
- flags = spin_lock_irqsave(NULL);
- for (i = 0; i < count; i++)
- {
- uint8_t addr;
- FAR struct i2c_msg_s *msg = &msgs[i];
- #ifndef CONFIG_I2C_BITBANG_NO_DELAY
- /* Compute delay from frequency */
- priv->delay = (USEC_PER_SEC / (2 * msg->frequency)) -
- CONFIG_I2C_BITBANG_GPIO_OVERHEAD;
- if (priv->delay < 0)
- {
- priv->delay = 0;
- }
- #endif
- /* If this is the start of transfer or we're changing sending direction
- * from last transfer, send START
- */
- if (i == 0 ||
- (msgs[i - 1].flags & I2C_M_READ) != (msgs[i].flags & I2C_M_READ))
- {
- /* Send start bit */
- i2c_bitbang_set_scl(priv, true, false);
- i2c_bitbang_set_sda(priv, false);
- i2c_bitbang_set_scl(priv, false, false);
- /* Send the address */
- addr = (msg->flags & I2C_M_READ ? I2C_READADDR8(msg->addr) :
- I2C_WRITEADDR8(msg->addr));
- i2c_bitbang_send(priv, addr);
- /* Wait for ACK */
- ret = i2c_bitbang_wait_ack(priv);
- if (ret < 0)
- {
- goto out;
- }
- }
- i2c_bitbang_set_scl(priv, false, false);
- if (msg->flags & I2C_M_READ)
- {
- int j;
- int k;
- for (j = 0; j < msg->length; j++)
- {
- uint8_t data = 0;
- i2c_bitbang_set_sda(priv, true);
- msg->buffer[j] = 0;
- for (k = 0; k < 8; k++)
- {
- i2c_bitbang_set_scl(priv, true, false);
- data |= (i2c_bitbang_get_sda(priv) & 1) << (7 - k);
- i2c_bitbang_set_scl(priv, false, false);
- }
- msg->buffer[j] = data;
- if (j < msg->length - 1)
- {
- /* Send ACK */
- i2c_bitbang_set_sda(priv, false);
- i2c_bitbang_set_scl(priv, true, false);
- i2c_bitbang_set_scl(priv, false, false);
- }
- else
- {
- /* On the last byte send NAK */
- i2c_bitbang_set_sda(priv, true);
- i2c_bitbang_set_scl(priv, true, false);
- i2c_bitbang_set_scl(priv, false, false);
- }
- }
- }
- else
- {
- int j;
- for (j = 0; j < msg->length; j++)
- {
- /* Send the data */
- i2c_bitbang_send(priv, msg->buffer[j]);
- ret = i2c_bitbang_wait_ack(priv);
- if (ret < 0)
- {
- goto out;
- }
- i2c_bitbang_set_scl(priv, false, false);
- }
- }
- if (!(msg->flags & I2C_M_NOSTOP))
- {
- /* Send stop */
- i2c_bitbang_set_sda(priv, false);
- i2c_bitbang_set_scl(priv, true, true);
- i2c_bitbang_set_sda(priv, true);
- }
- }
- out:
- /* Ensure lines are released */
- i2c_bitbang_set_scl(priv, true, false);
- i2c_bitbang_set_sda(priv, true);
- spin_unlock_irqrestore(NULL, flags);
- return ret;
- }
- /****************************************************************************
- * Name: i2c_bitbang_wait_ack
- ****************************************************************************/
- static int i2c_bitbang_wait_ack(FAR struct i2c_bitbang_dev_s *priv)
- {
- int ret = OK;
- int i;
- /* Wait for ACK */
- i2c_bitbang_set_sda(priv, true);
- i2c_bitbang_set_scl(priv, true, true);
- for (i = 0; i2c_bitbang_get_sda(priv) &&
- i < CONFIG_I2C_BITBANG_TIMEOUT; i++)
- {
- up_udelay(1);
- }
- if (i == CONFIG_I2C_BITBANG_TIMEOUT)
- {
- ret = -EIO;
- }
- else
- {
- int remaining = priv->delay - i;
- if (remaining > 0)
- {
- up_udelay(remaining);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: i2c_bitbang_send
- ****************************************************************************/
- static void i2c_bitbang_send(FAR struct i2c_bitbang_dev_s *priv,
- uint8_t data)
- {
- uint8_t bit = 0b10000000;
- while (bit)
- {
- i2c_bitbang_set_sda(priv, !!(data & bit));
- i2c_bitbang_set_scl(priv, true, false);
- i2c_bitbang_set_scl(priv, false, false);
- bit >>= 1;
- }
- }
- /****************************************************************************
- * Name: i2c_bitbang_set_sda
- ****************************************************************************/
- static void i2c_bitbang_set_sda(FAR struct i2c_bitbang_dev_s *dev, bool high)
- {
- dev->lower->ops->set_sda(dev->lower, high);
- }
- /****************************************************************************
- * Name: i2c_bitbang_set_scl
- ****************************************************************************/
- static int i2c_bitbang_set_scl(FAR struct i2c_bitbang_dev_s *dev, bool high,
- bool nodelay)
- {
- dev->lower->ops->set_scl(dev->lower, high);
- #ifndef CONFIG_I2C_BITBANG_NO_DELAY
- if (!nodelay && dev->delay)
- {
- up_udelay(dev->delay);
- }
- #endif
- #ifdef CONFIG_I2C_BITBANG_CLOCK_STRETCHING
- /* Allow for clock stretching */
- if (high)
- {
- int i;
- for (i = 0; !i2c_bitbang_get_scl(dev) &&
- i < CONFIG_I2C_BITBANG_TIMEOUT; i++)
- {
- up_udelay(1);
- if (i == CONFIG_I2C_BITBANG_TIMEOUT)
- {
- return -ETIMEDOUT;
- }
- }
- }
- #endif
- return OK;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: i2c_bitbang_initialize
- *
- * Description:
- * Initialize a bitbang I2C device instance
- *
- * Input Parameters:
- * lower - Lower half of driver
- *
- * Returned Value:
- * Pointer to a the I2C instance
- *
- ****************************************************************************/
- FAR struct i2c_master_s *i2c_bitbang_initialize(
- FAR struct i2c_bitbang_lower_dev_s *lower)
- {
- FAR struct i2c_bitbang_dev_s *dev;
- DEBUGASSERT(lower && lower->ops);
- dev = (FAR struct i2c_bitbang_dev_s *)kmm_zalloc(sizeof(*dev));
- if (!dev)
- {
- return NULL;
- }
- dev->i2c.ops = &g_i2c_ops;
- dev->lower = lower;
- dev->lower->ops->initialize(dev->lower);
- return &dev->i2c;
- }
|