我正在使用 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 论坛的新手,所以不确定常用标签是什么。