I2C 传输到 Thunderboard Sense 微控制器永远不会完成

物联网 微控制器
2021-06-25 08:31:31

我正在使用 Thunderboard Sense 微控制器,通过 USB 插入我的桌面 (Windows),以及带有编码语言 C 的 Simplicity IDE。我正在尝试使用 I2C_Transfer 函数写入寄存器,以初始化板的 IMU 传感器。然而,传输总是“进行中”并且永远不会完成。

我首先唤醒板的中断控制器,然后启用 I2C 命令,然后初始化 I2C,然后尝试传输。为此,我首先调用 I2C_TransferInit 函数,然后轮询 I2C_Transfer 函数,等待它完成。
我查看了 I2C_Transfer 函数的代码,发现它卡住了transfer->state = i2cStateWFStopSent,状态永远不会继续到i2cStateDone这似乎是因为中断标志寄存器没有做它应该做的事情(pending变量的值,可以在 I2C_Transfer 函数的代码中看到,是由这个中断设置的(我认为))。

我的代码如下。要查找发生错误的部分,请搜索​​评论“ERROR IS HERE”。

/* Board headers */
#include "boards.h"
#include "ble-configuration.h"
#include "board_features.h"

/* Bluetooth stack headers */
#include "bg_types.h"
#include "native_gecko.h"
#include "gatt_db.h"
#include "aat.h"

/* Libraries containing default Gecko configuration values */
#include "em_emu.h"
#include "em_cmu.h"
#include "em_gpio.h"
#include "em_i2c.h"
#ifdef FEATURE_BOARD_DETECTED
#include "bspconfig.h"
#include "pti.h"
#endif

/* Device initialization header */
#include "InitDevice.h"

#ifdef FEATURE_SPI_FLASH
#include "em_usart.h"
#include "mx25flash_spi.h"
#endif /* FEATURE_SPI_FLASH */

#ifndef MAX_CONNECTIONS
#define MAX_CONNECTIONS 4
#endif
uint8_t bluetooth_stack_heap[DEFAULT_BLUETOOTH_HEAP(MAX_CONNECTIONS)];

#ifdef FEATURE_PTI_SUPPORT
static const RADIO_PTIInit_t ptiInit = RADIO_PTI_INIT;
#endif

/* Gecko configuration parameters (see gecko_configuration.h) */
static const gecko_configuration_t config = {
  .config_flags = 0,
  .sleep.flags = SLEEP_FLAGS_DEEP_SLEEP_ENABLE,
  .bluetooth.max_connections = MAX_CONNECTIONS,
  .bluetooth.heap = bluetooth_stack_heap,
  .bluetooth.heap_size = sizeof(bluetooth_stack_heap),
  .bluetooth.sleep_clock_accuracy = 100, // ppm
  .gattdb = &bg_gattdb_data,
  .ota.flags = 0,
  .ota.device_name_len = 3,
  .ota.device_name_ptr = "OTA",
  #ifdef FEATURE_PTI_SUPPORT
  .pti = &ptiInit,
  #endif
};

/* Flag for indicating DFU Reset must be performed */
uint8_t boot_to_dfu = 0;


// Print to putty
int _write_r(struct _reent *r, int fd, const void *data, unsigned int count)
{
    char *c = (char *)data;
    for (unsigned int i = 0; i < count; i++)
    {
        USART_Tx(USART0, c[i]);
    }
    return count;
}


void main(void)
{

#ifdef FEATURE_SPI_FLASH
  MX25_init();
  MX25_DP();
  /* We must disable SPI communication */
  USART_Reset(USART1);

#endif /* FEATURE_SPI_FLASH */

  /* Initialize peripherals */
  enter_DefaultMode_from_RESET();

  /* Initialize stack */
  gecko_init(&config);

  // Wake interrupt controller
  unsigned int pin = 0;
  GPIO_PinOutClear(gpioPortD, pin);

  // Enable I2C
  I2C_Enable(I2C0, true);

  // Initialise I2C commands
  I2C_Init_TypeDef i2cInit = I2C_INIT_DEFAULT;
  I2C_Init(I2C0, &i2cInit);

  // Enable IMU sensor
  printf("\n\r\nDefining sequence to be sent\n\r");
  I2C_TransferSeq_TypeDef seq;
  seq.addr = 0x90;
  seq.flags = 0x0001;
  seq.buf[0].data[0] = 0x00;
  seq.buf[0].len = 1;
  seq.buf[1].data[0] = 0x01;
  seq.buf[1].len = 1;
  printf("Attempting to start transfer\n\r");
  I2C_TransferReturn_TypeDef I2C_Status;
  I2C_Status = I2C_TransferInit(I2C0, &seq);
  printf("Transfer started\n\r");
  while (I2C_Status == i2cTransferInProgress)
  {
      // ERROR IS HERE: this loop never stops running (the transfer is never completed)
      I2C_Status = I2C_Transfer(I2C0);
  }
  printf("Transfer finished\n\r");

  // Initialise variables
  int IMU = 1;


  while (1)
  {

      // IMU = read IMU value here
      printf("IMU value: %d \n\r", IMU);



    /* Event pointer for handling events */
    struct gecko_cmd_packet* evt;

    /* Check for stack event. */
    evt = gecko_wait_event();

    /* Handle events */
    switch (BGLIB_MSG_ID(evt->header)) {
      /* This boot event is generated when the system boots up after reset.
       * Here the system is set to start advertising immediately after boot procedure. */
      case gecko_evt_system_boot_id:

        /* Set advertising parameters. 100ms advertisement interval. All channels used.
         * The first two parameters are minimum and maximum advertising interval, both in
         * units of (milliseconds * 1.6). The third parameter '7' sets advertising on all channels. */
        gecko_cmd_le_gap_set_adv_parameters(160, 160, 7);

        /* Start general advertising and enable connections. */
        gecko_cmd_le_gap_set_mode(le_gap_general_discoverable, le_gap_undirected_connectable);
        break;

      case gecko_evt_le_connection_closed_id:

        /* Check if need to boot to dfu mode */
        if (boot_to_dfu) {
          /* Enter to DFU OTA mode */
          gecko_cmd_system_reset(2);
        } else {
          /* Restart advertising after client has disconnected */
          gecko_cmd_le_gap_set_mode(le_gap_general_discoverable, le_gap_undirected_connectable);
        }
        break;

      /* Events related to OTA upgrading
         ----------------------------------------------------------------------------- */

      /* Check if the user-type OTA Control Characteristic was written.
       * If ota_control was written, boot the device into Device Firmware 
Upgrade (DFU) mode. */
      case gecko_evt_gatt_server_user_write_request_id:

        if (evt->data.evt_gatt_server_user_write_request.characteristic == 
gattdb_ota_control) {
          /* Set flag to enter to OTA mode */
          boot_to_dfu = 1;
          /* Send response to Write Request */
          gecko_cmd_gatt_server_send_user_write_response(
            evt->data.evt_gatt_server_user_write_request.connection,
            gattdb_ota_control,
            bg_err_success);

          /* Close connection to enter to DFU OTA mode */
          gecko_cmd_endpoint_close(evt-
>data.evt_gatt_server_user_write_request.connection);
        }
        break;

      default:
        break;
    }
  }
}

/** @} (end addtogroup app) */
/** @} (end addtogroup Application) */

这是 I2C_Transfer 函数的代码:

I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c)
{
  uint32_t                tmp;
  uint32_t                pending;
  I2C_Transfer_TypeDef    *transfer;
  I2C_TransferSeq_TypeDef *seq;

  EFM_ASSERT(I2C_REF_VALID(i2c));

  /* Support up to 2 I2C buses */
  if (i2c == I2C0) {
    transfer = i2cTransfer;
  }
#if (I2C_COUNT > 1)
  else if (i2c == I2C1) {
    transfer = i2cTransfer + 1;
  }
#endif
#if (I2C_COUNT > 2)
  else if (i2c == I2C2) {
    transfer = i2cTransfer + 2;
  }
#endif
  else {
    return i2cTransferUsageFault;
  }

  seq = transfer->seq;
  for (;; ) {
    pending = i2c->IF;

    /* If some sort of fault, abort transfer. */
    if (pending & I2C_IF_ERRORS) {
      if (pending & I2C_IF_ARBLOST) {
        /* If arbitration fault, it indicates either a slave device */
        /* not responding as expected, or other master which is not */
        /* supported by this SW. */
        transfer->result = i2cTransferArbLost;
      } else if (pending & I2C_IF_BUSERR) {
        /* A bus error indicates a misplaced start or stop, which should */
        /* not occur in master mode controlled by this SW. */
        transfer->result = i2cTransferBusErr;
      }

      /* If error situation occurred, it is difficult to know */
      /* exact cause and how to resolve. It will be up to a wrapper */
      /* to determine how to handle a fault/recovery if possible. */
      transfer->state = i2cStateDone;
      goto done;
    }

    switch (transfer->state) {
      /***************************************************/
      /* Send first start+address (first byte if 10 bit) */
      /***************************************************/
      case i2cStateStartAddrSend:
        if (seq->flags & I2C_FLAG_10BIT_ADDR) {
          tmp = (((uint32_t)(seq->addr) >> 8) & 0x06) | 0xf0;

          /* In 10 bit address mode, the address following the first */
          /* start always indicate write. */
        } else {
          tmp = (uint32_t)(seq->addr) & 0xfe;

          if (seq->flags & I2C_FLAG_READ) {
            /* Indicate read request */
            tmp |= 1;
          }
        }

        transfer->state = i2cStateAddrWFAckNack;
        i2c->TXDATA     = tmp;/* Data not transmitted until START sent */
        i2c->CMD        = I2C_CMD_START;
        goto done;

      /*******************************************************/
      /* Wait for ACK/NACK on address (first byte if 10 bit) */
      /*******************************************************/
      case i2cStateAddrWFAckNack:
        if (pending & I2C_IF_NACK) {
          i2c->IFC         = I2C_IFC_NACK;
          transfer->result = i2cTransferNack;
          transfer->state  = i2cStateWFStopSent;
          i2c->CMD         = I2C_CMD_STOP;
        } else if (pending & I2C_IF_ACK) {
          i2c->IFC = I2C_IFC_ACK;

          /* If 10 bit address, send 2nd byte of address. */
          if (seq->flags & I2C_FLAG_10BIT_ADDR) {
            transfer->state = i2cStateAddrWF2ndAckNack;
            i2c->TXDATA     = (uint32_t)(seq->addr) & 0xff;
          } else {
            /* Determine whether receiving or sending data */
            if (seq->flags & I2C_FLAG_READ) {
              transfer->state = i2cStateWFData;
              if (seq->buf[transfer->bufIndx].len == 1) {
                i2c->CMD  = I2C_CMD_NACK;
              }
            } else {
              transfer->state = i2cStateDataSend;
              continue;
            }
          }
        }
        goto done;

      /******************************************************/
      /* Wait for ACK/NACK on second byte of 10 bit address */
      /******************************************************/
      case i2cStateAddrWF2ndAckNack:
        if (pending & I2C_IF_NACK) {
          i2c->IFC         = I2C_IFC_NACK;
          transfer->result = i2cTransferNack;
          transfer->state  = i2cStateWFStopSent;
          i2c->CMD         = I2C_CMD_STOP;
        } else if (pending & I2C_IF_ACK) {
          i2c->IFC = I2C_IFC_ACK;

          /* If using plain read sequence with 10 bit address, switch to send */
          /* repeated start. */
          if (seq->flags & I2C_FLAG_READ) {
            transfer->state = i2cStateRStartAddrSend;
          }
          /* Otherwise expected to write 0 or more bytes */
          else {
            transfer->state = i2cStateDataSend;
          }
          continue;
        }
        goto done;

      /*******************************/
      /* Send repeated start+address */
      /*******************************/
      case i2cStateRStartAddrSend:
        if (seq->flags & I2C_FLAG_10BIT_ADDR) {
          tmp = ((seq->addr >> 8) & 0x06) | 0xf0;
        } else {
          tmp = seq->addr & 0xfe;
        }

        /* If this is a write+read combined sequence, then read is about to start */
        if (seq->flags & I2C_FLAG_WRITE_READ) {
          /* Indicate read request */
          tmp |= 1;
        }

        transfer->state = i2cStateRAddrWFAckNack;
        /* We have to write START cmd first since repeated start, otherwise */
        /* data would be sent first. */
        i2c->CMD    = I2C_CMD_START;
        i2c->TXDATA = tmp;
        goto done;

      /**********************************************************************/
      /* Wait for ACK/NACK on repeated start+address (first byte if 10 bit) */
      /**********************************************************************/
      case i2cStateRAddrWFAckNack:
        if (pending & I2C_IF_NACK) {
          i2c->IFC         = I2C_IFC_NACK;
          transfer->result = i2cTransferNack;
          transfer->state  = i2cStateWFStopSent;
          i2c->CMD         = I2C_CMD_STOP;
        } else if (pending & I2C_IF_ACK) {
          i2c->IFC = I2C_IFC_ACK;

          /* Determine whether receiving or sending data */
          if (seq->flags & I2C_FLAG_WRITE_READ) {
            transfer->state = i2cStateWFData;
          } else {
            transfer->state = i2cStateDataSend;
            continue;
          }
        }
        goto done;

      /*****************************/
      /* Send a data byte to slave */
      /*****************************/
      case i2cStateDataSend:
        /* Reached end of data buffer? */
        if (transfer->offset >= seq->buf[transfer->bufIndx].len) {
          /* Move to next message part */
          transfer->offset = 0;
          transfer->bufIndx++;

          /* Send repeated start when switching to read mode on 2nd buffer */
          if (seq->flags & I2C_FLAG_WRITE_READ) {
            transfer->state = i2cStateRStartAddrSend;
            continue;
          }

          /* Only writing from one buffer, or finished both buffers */
          if ((seq->flags & I2C_FLAG_WRITE) || (transfer->bufIndx > 1)) {
            transfer->state = i2cStateWFStopSent;
            i2c->CMD        = I2C_CMD_STOP;
            goto done;
          }

          /* Reprocess in case next buffer is empty */
          continue;
        }

        /* Send byte */
        i2c->TXDATA     = (uint32_t)(seq->buf[transfer->bufIndx].data[transfer->offset++]);
        transfer->state = i2cStateDataWFAckNack;
        goto done;

      /*********************************************************/
      /* Wait for ACK/NACK from slave after sending data to it */
      /*********************************************************/
      case i2cStateDataWFAckNack:
        if (pending & I2C_IF_NACK) {
          i2c->IFC         = I2C_IFC_NACK;
          transfer->result = i2cTransferNack;
          transfer->state  = i2cStateWFStopSent;
          i2c->CMD         = I2C_CMD_STOP;
        } else if (pending & I2C_IF_ACK) {
          i2c->IFC        = I2C_IFC_ACK;
          transfer->state = i2cStateDataSend;
          continue;
        }
        goto done;

      /****************************/
      /* Wait for data from slave */
      /****************************/
      case i2cStateWFData:
        if (pending & I2C_IF_RXDATAV) {
          uint8_t       data;
          unsigned int  rxLen = seq->buf[transfer->bufIndx].len;

          /* Must read out data in order to not block further progress */
          data = (uint8_t)(i2c->RXDATA);

          /* Make sure not storing beyond end of buffer just in case */
          if (transfer->offset < rxLen) {
            seq->buf[transfer->bufIndx].data[transfer->offset++] = data;
          }

          /* If we have read all requested data, then the sequence should end */
          if (transfer->offset >= rxLen) {
            /* If there is only one byte to receive we need to transmit the
               NACK now, before the stop. */
            if (1 == rxLen) {
              i2c->CMD  = I2C_CMD_NACK;
            }

            transfer->state = i2cStateWFStopSent;
            i2c->CMD        = I2C_CMD_STOP;
          } else {
            /* Send ACK and wait for next byte */
            i2c->CMD = I2C_CMD_ACK;

            if ( (1 < rxLen) && (transfer->offset == (rxLen - 1)) ) {
              /* If there is more than one byte to receive and this is the next
                 to last byte we need to transmit the NACK now, before receiving
                 the last byte. */
              i2c->CMD  = I2C_CMD_NACK;
            }
          }
        }
        goto done;

      /***********************************/
      /* Wait for STOP to have been sent */
      /***********************************/
      case i2cStateWFStopSent:
        if (pending & I2C_IF_MSTOP) {
          i2c->IFC        = I2C_IFC_MSTOP;
          transfer->state = i2cStateDone;
        }
        goto done;

      /******************************/
      /* Unexpected state, SW fault */
      /******************************/
      default:
        transfer->result = i2cTransferSwFault;
        transfer->state  = i2cStateDone;
        goto done;
    }
  }

  done:

  if (transfer->state == i2cStateDone) {
    /* Disable interrupt sources when done */
    i2c->IEN = 0;

    /* Update result unless some fault already occurred */
    if (transfer->result == i2cTransferInProgress) {
      transfer->result = i2cTransferDone;
    }
  }
  /* Until transfer is done keep returning i2cTransferInProgress */
  else {
    return i2cTransferInProgress;
  }

  return transfer->result;
}

下面是 I2C_TransferInit 函数的代码:

I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c,
                                            I2C_TransferSeq_TypeDef *seq)
{
  I2C_Transfer_TypeDef *transfer;

  EFM_ASSERT(I2C_REF_VALID(i2c));
  EFM_ASSERT(seq);

  /* Support up to 2 I2C buses */
  if (i2c == I2C0) {
    transfer = i2cTransfer;
  }
#if (I2C_COUNT > 1)
  else if (i2c == I2C1) {
    transfer = i2cTransfer + 1;
  }
#endif
#if (I2C_COUNT > 2)
  else if (i2c == I2C2) {
    transfer = i2cTransfer + 2;
  }
#endif
  else {
    return i2cTransferUsageFault;
  }

  /* Check if in busy state. Since this SW assumes single master, we can */
  /* just issue an abort. The BUSY state is normal after a reset. */
  if (i2c->STATE & I2C_STATE_BUSY) {
    i2c->CMD = I2C_CMD_ABORT;
  }

  /* Make sure user is not trying to read 0 bytes, it is not */
  /* possible according to I2C spec, since slave will always start */
  /* sending first byte ACK on address. The read operation can */
  /* only be stopped by NACKing a received byte, ie minimum 1 byte. */
  if (((seq->flags & I2C_FLAG_READ) && !(seq->buf[0].len))
      || ((seq->flags & I2C_FLAG_WRITE_READ) && !(seq->buf[1].len))
      ) {
    return i2cTransferUsageFault;
  }

  /* Prepare for a transfer */
  transfer->state   = i2cStateStartAddrSend;
  transfer->result  = i2cTransferInProgress;
  transfer->offset  = 0;
  transfer->bufIndx = 0;
  transfer->seq     = seq;

  /* Ensure buffers are empty */
  i2c->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
  if (i2c->IF & I2C_IF_RXDATAV) {
    (void)i2c->RXDATA;
  }

  /* Clear all pending interrupts prior to starting transfer. */
  i2c->IFC = _I2C_IFC_MASK;

  /* Enable those interrupts we are interested in throughout transfer. */
  /* Notice that the I2C interrupt must also be enabled in the NVIC, but */
  /* that is left for an additional driver wrapper. */
  i2c->IEN |= I2C_IF_NACK | I2C_IF_ACK | I2C_IF_MSTOP
              | I2C_IF_RXDATAV | I2C_IF_ERRORS;

  /* Start transfer */
  return I2C_Transfer(i2c);
}

/** @} (end addtogroup I2C) */
/** @} (end addtogroup emlib) */
#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */

以下是一些相关链接:
我正在使用的微控制器的数据表 - UG250:Thunderboard Sense 用户指南

I2C 文档 - EFR32 Blue Gecko 1 软件文档

PS我找不到很多相关的标签,如果其他人能想到一些请添加它们;我是 IoT 论坛的新手,所以不确定常用标签是什么。

0个回答
没有发现任何回复~