diff --git a/efm32/.cproject b/efm32/.cproject
index 95d19eb..3c083fe 100644
--- a/efm32/.cproject
+++ b/efm32/.cproject
@@ -23,7 +23,7 @@
-
+
@@ -67,9 +67,9 @@
-
+
@@ -93,9 +93,9 @@
-
+
@@ -128,7 +128,7 @@
-
+
@@ -158,7 +158,7 @@
-
+
@@ -177,9 +177,9 @@
-
+
-
+
diff --git a/efm32/EFM32.hwconf b/efm32/EFM32.hwconf
index d359390..054e13b 100644
--- a/efm32/EFM32.hwconf
+++ b/efm32/EFM32.hwconf
@@ -3,7 +3,7 @@
-
+
@@ -12,7 +12,7 @@
-
+
@@ -20,6 +20,7 @@
+
@@ -60,6 +61,7 @@
+
@@ -67,6 +69,10 @@
+
+
+
+
diff --git a/efm32/emlib/em_i2c.c b/efm32/emlib/em_i2c.c
new file mode 100644
index 0000000..106f0ea
--- /dev/null
+++ b/efm32/emlib/em_i2c.c
@@ -0,0 +1,811 @@
+/***************************************************************************//**
+ * @file em_i2c.c
+ * @brief Inter-integrated Circuit (I2C) Peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_i2c.h"
+#if defined(I2C_COUNT) && (I2C_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_bus.h"
+#include "em_assert.h"
+
+ #include
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup I2C
+ * @brief Inter-integrated Circuit (I2C) Peripheral API
+ * @details
+ * This module contains functions to control the I2C peripheral of Silicon
+ * Labs 32-bit MCUs and SoCs. The I2C interface allows communication on I2C
+ * buses with the lowest energy consumption possible.
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Validation of I2C register block pointer reference for assert statements. */
+#if (I2C_COUNT == 1)
+#define I2C_REF_VALID(ref) ((ref) == I2C0)
+#elif (I2C_COUNT == 2)
+#define I2C_REF_VALID(ref) ((ref == I2C0) || (ref == I2C1))
+#elif (I2C_COUNT == 3)
+#define I2C_REF_VALID(ref) ((ref == I2C0) || (ref == I2C1) || (ref == I2C2))
+#endif
+
+/** Error flags indicating I2C transfer has failed somehow. */
+/* Notice that I2C_IF_TXOF (transmit overflow) is not really possible with */
+/* this SW supporting master mode. Likewise for I2C_IF_RXUF (receive underflow) */
+/* RXUF is only likely to occur with this SW if using a debugger peeking into */
+/* RXDATA register. Thus, we ignore those types of fault. */
+#define I2C_IF_ERRORS (I2C_IF_BUSERR | I2C_IF_ARBLOST)
+
+/* Max I2C transmission rate constant */
+#if defined(_SILICON_LABS_32B_SERIES_0)
+#define I2C_CR_MAX 4
+#elif defined(_SILICON_LABS_32B_SERIES_1)
+#define I2C_CR_MAX 8
+#else
+#warning "Max I2C transmission rate constant is not defined"
+#endif
+
+/** @endcond */
+
+/*******************************************************************************
+ ******************************** ENUMS ************************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Master mode transfer states. */
+typedef enum {
+ i2cStateStartAddrSend, /**< Send start + (first part of) address. */
+ i2cStateAddrWFAckNack, /**< Wait for ACK/NACK on (first part of) address. */
+ i2cStateAddrWF2ndAckNack, /**< Wait for ACK/NACK on second part of 10 bit address. */
+ i2cStateRStartAddrSend, /**< Send repeated start + (first part of) address. */
+ i2cStateRAddrWFAckNack, /**< Wait for ACK/NACK on address sent after repeated start. */
+ i2cStateDataSend, /**< Send data. */
+ i2cStateDataWFAckNack, /**< Wait for ACK/NACK on data sent. */
+ i2cStateWFData, /**< Wait for data. */
+ i2cStateWFStopSent, /**< Wait for STOP to have been transmitted. */
+ i2cStateDone /**< Transfer completed successfully. */
+} I2C_TransferState_TypeDef;
+
+/** @endcond */
+
+/*******************************************************************************
+ ******************************* STRUCTS ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Structure used to store state information on an ongoing master mode transfer. */
+typedef struct {
+ /** Current state. */
+ I2C_TransferState_TypeDef state;
+
+ /** Result return code. */
+ I2C_TransferReturn_TypeDef result;
+
+ /** Offset in current sequence buffer. */
+ uint16_t offset;
+
+ /* Index to current sequence buffer in use. */
+ uint8_t bufIndx;
+
+ /** Reference to I2C transfer sequence definition provided by user. */
+ I2C_TransferSeq_TypeDef *seq;
+} I2C_Transfer_TypeDef;
+
+/** @endcond */
+
+/*******************************************************************************
+ ***************************** LOCAL DATA *******^**************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/**
+ * Lookup table for Nlow + Nhigh setting defined by CLHR. Set undefined
+ * index (0x3) to reflect default setting just in case.
+ */
+static const uint8_t i2cNSum[] = { 4 + 4, 6 + 3, 11 + 6, 4 + 4 };
+
+/** Transfer state info for ongoing master mode transfer */
+static I2C_Transfer_TypeDef i2cTransfer[I2C_COUNT];
+
+/** @endcond */
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Get current configured I2C bus frequency.
+ *
+ * @details
+ * This frequency is only of relevance when acting as master.
+ *
+ * @param[in] i2c
+ * Pointer to I2C peripheral register block.
+ *
+ * @return
+ * Current I2C frequency in Hz.
+ ******************************************************************************/
+uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c)
+{
+ uint32_t freqHfper;
+ uint32_t n;
+
+ /* Max frequency is given by freqScl = freqHfper/((Nlow + Nhigh)(DIV + 1) + I2C_CR_MAX)
+ * More details can be found in the reference manual,
+ * I2C Clock Generation chapter. */
+ freqHfper = CMU_ClockFreqGet(cmuClock_HFPER);
+ /* n = Nlow + Nhigh */
+ n = (uint32_t)(i2cNSum[(i2c->CTRL & _I2C_CTRL_CLHR_MASK) >> _I2C_CTRL_CLHR_SHIFT]);
+
+ return (freqHfper / ((n * (i2c->CLKDIV + 1)) + I2C_CR_MAX));
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set I2C bus frequency.
+ *
+ * @details
+ * The bus frequency is only of relevance when acting as a master. The bus
+ * frequency should not be set higher than the max frequency accepted by the
+ * slowest device on the bus.
+ *
+ * Notice that due to asymmetric requirements on low and high I2C clock
+ * cycles by the I2C specification, the actual max frequency allowed in order
+ * to comply with the specification may be somewhat lower than expected.
+ *
+ * Please refer to the reference manual, details on I2C clock generation,
+ * for max allowed theoretical frequencies for different modes.
+ *
+ * @param[in] i2c
+ * Pointer to I2C peripheral register block.
+ *
+ * @param[in] freqRef
+ * I2C reference clock frequency in Hz that will be used. If set to 0,
+ * then HFPER clock is used. Setting it to a higher than actual configured
+ * value only has the consequence of reducing the real I2C frequency.
+ *
+ * @param[in] freqScl
+ * Bus frequency to set (actual bus speed may be lower due to integer
+ * prescaling). Safe (according to I2C specification) max frequencies for
+ * standard, fast and fast+ modes are available using I2C_FREQ_ defines.
+ * (Using I2C_FREQ_ defines requires corresponding setting of @p type.)
+ * Slowest slave device on bus must always be considered.
+ *
+ * @param[in] i2cMode
+ * Clock low to high ratio type to use. If not using i2cClockHLRStandard,
+ * make sure all devices on the bus support the specified mode. Using a
+ * non-standard ratio is useful to achieve higher bus clock in fast and
+ * fast+ modes.
+ ******************************************************************************/
+void I2C_BusFreqSet(I2C_TypeDef *i2c,
+ uint32_t freqRef,
+ uint32_t freqScl,
+ I2C_ClockHLR_TypeDef i2cMode)
+{
+ uint32_t n, minFreq;
+ int32_t div;
+
+ /* Avoid divide by 0 */
+ EFM_ASSERT(freqScl);
+ if (!freqScl) {
+ return;
+ }
+
+ /* Set the CLHR (clock low to high ratio). */
+ i2c->CTRL &= ~_I2C_CTRL_CLHR_MASK;
+ BUS_RegMaskedWrite(&i2c->CTRL,
+ _I2C_CTRL_CLHR_MASK,
+ i2cMode << _I2C_CTRL_CLHR_SHIFT);
+
+ if (!freqRef) {
+ freqRef = CMU_ClockFreqGet(cmuClock_HFPER);
+ }
+
+ /* Check minumum HF peripheral clock */
+ minFreq = UINT_MAX;
+ if (i2c->CTRL & I2C_CTRL_SLAVE) {
+ switch (i2cMode) {
+ case i2cClockHLRStandard:
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ minFreq = 4200000; break;
+#elif defined(_SILICON_LABS_32B_SERIES_1)
+ minFreq = 2000000; break;
+#endif
+ case i2cClockHLRAsymetric:
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ minFreq = 11000000; break;
+#elif defined(_SILICON_LABS_32B_SERIES_1)
+ minFreq = 5000000; break;
+#endif
+ case i2cClockHLRFast:
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ minFreq = 24400000; break;
+#elif defined(_SILICON_LABS_32B_SERIES_1)
+ minFreq = 14000000; break;
+#endif
+ }
+ } else {
+ /* For master mode, platform 1 and 2 share the same
+ min frequencies */
+ switch (i2cMode) {
+ case i2cClockHLRStandard:
+ minFreq = 2000000; break;
+ case i2cClockHLRAsymetric:
+ minFreq = 9000000; break;
+ case i2cClockHLRFast:
+ minFreq = 20000000; break;
+ }
+ }
+
+ /* Frequency most be larger-than */
+ EFM_ASSERT(freqRef > minFreq);
+
+ /* SCL frequency is given by
+ * freqScl = freqRef/((Nlow + Nhigh) * (DIV + 1) + I2C_CR_MAX)
+ *
+ * Thus
+ * DIV = ((freqRef - (I2C_CR_MAX * freqScl))/((Nlow + Nhigh) * freqScl)) - 1
+ *
+ * More details can be found in the reference manual,
+ * I2C Clock Generation chapter. */
+
+ /* n = Nlow + Nhigh */
+ n = (uint32_t)(i2cNSum[i2cMode]);
+ div = ((freqRef - (I2C_CR_MAX * freqScl)) / (n * freqScl)) - 1;
+ EFM_ASSERT(div >= 0);
+ EFM_ASSERT((uint32_t)div <= _I2C_CLKDIV_DIV_MASK);
+
+ /* Clock divisor must be at least 1 in slave mode according to reference */
+ /* manual (in which case there is normally no need to set bus frequency). */
+ if ((i2c->CTRL & I2C_CTRL_SLAVE) && !div) {
+ div = 1;
+ }
+ i2c->CLKDIV = (uint32_t)div;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enable/disable I2C.
+ *
+ * @note
+ * After enabling the I2C (from being disabled), the I2C is in BUSY state.
+ *
+ * @param[in] i2c
+ * Pointer to I2C peripheral register block.
+ *
+ * @param[in] enable
+ * true to enable counting, false to disable.
+ ******************************************************************************/
+void I2C_Enable(I2C_TypeDef *i2c, bool enable)
+{
+ EFM_ASSERT(I2C_REF_VALID(i2c));
+
+ BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Initialize I2C.
+ *
+ * @param[in] i2c
+ * Pointer to I2C peripheral register block.
+ *
+ * @param[in] init
+ * Pointer to I2C initialization structure.
+ ******************************************************************************/
+void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init)
+{
+ EFM_ASSERT(I2C_REF_VALID(i2c));
+
+ i2c->IEN = 0;
+ i2c->IFC = _I2C_IFC_MASK;
+
+ /* Set SLAVE select mode */
+ BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_SLAVE_SHIFT, init->master ? 0 : 1);
+
+ I2C_BusFreqSet(i2c, init->refFreq, init->freq, init->clhr);
+
+ BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, init->enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Reset I2C to same state as after a HW reset.
+ *
+ * @note
+ * The ROUTE register is NOT reset by this function, in order to allow for
+ * centralized setup of this feature.
+ *
+ * @param[in] i2c
+ * Pointer to I2C peripheral register block.
+ ******************************************************************************/
+void I2C_Reset(I2C_TypeDef *i2c)
+{
+ i2c->CTRL = _I2C_CTRL_RESETVALUE;
+ i2c->CLKDIV = _I2C_CLKDIV_RESETVALUE;
+ i2c->SADDR = _I2C_SADDR_RESETVALUE;
+ i2c->SADDRMASK = _I2C_SADDRMASK_RESETVALUE;
+ i2c->IEN = _I2C_IEN_RESETVALUE;
+ i2c->IFC = _I2C_IFC_MASK;
+ /* Do not reset route register, setting should be done independently */
+}
+
+/***************************************************************************//**
+ * @brief
+ * Continue an initiated I2C transfer (single master mode only).
+ *
+ * @details
+ * This function is used repeatedly after a I2C_TransferInit() in order to
+ * complete a transfer. It may be used in polled mode as the below example
+ * shows:
+ * @verbatim
+ * I2C_TransferReturn_TypeDef ret;
+ *
+ * // Do a polled transfer
+ * ret = I2C_TransferInit(I2C0, seq);
+ * while (ret == i2cTransferInProgress)
+ * {
+ * ret = I2C_Transfer(I2C0);
+ * }
+ * @endverbatim
+ * It may also be used in interrupt driven mode, where this function is invoked
+ * from the interrupt handler. Notice that if used in interrupt mode, NVIC
+ * interrupts must be configured and enabled for the I2C bus used. I2C
+ * peripheral specific interrupts are managed by this SW.
+ *
+ * @note
+ * Only single master mode is supported.
+ *
+ * @param[in] i2c
+ * Pointer to I2C peripheral register block.
+ *
+ * @return
+ * Returns status for ongoing transfer.
+ * @li #i2cTransferInProgress - indicates that transfer not finished.
+ * @li #i2cTransferDone - transfer completed successfully.
+ * @li otherwise some sort of error has occurred.
+ *
+ ******************************************************************************/
+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;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Prepare and start an I2C transfer (single master mode only).
+ *
+ * @details
+ * This function must be invoked in order to start an I2C transfer
+ * sequence. In order to actually complete the transfer, I2C_Transfer() must
+ * be used either in polled mode or by adding a small driver wrapper utilizing
+ * interrupts.
+ *
+ * @note
+ * Only single master mode is supported.
+ *
+ * @param[in] i2c
+ * Pointer to I2C peripheral register block.
+ *
+ * @param[in] seq
+ * Pointer to sequence structure defining the I2C transfer to take place. The
+ * referenced structure must exist until the transfer has fully completed.
+ *
+ * @return
+ * Returns status for ongoing transfer:
+ * @li #i2cTransferInProgress - indicates that transfer not finished.
+ * @li otherwise some sort of error has occurred.
+ ******************************************************************************/
+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) */
diff --git a/efm32/inc/nfc.h b/efm32/inc/nfc.h
new file mode 100644
index 0000000..6935ebc
--- /dev/null
+++ b/efm32/inc/nfc.h
@@ -0,0 +1,13 @@
+/*
+ * nfc.h
+ *
+ * Created on: Jul 22, 2018
+ * Author: conor
+ */
+
+#ifndef INC_NFC_H_
+#define INC_NFC_H_
+
+void nfc_test();
+
+#endif /* INC_NFC_H_ */
diff --git a/efm32/src/InitDevice.c b/efm32/src/InitDevice.c
index d4153fc..5dbfacf 100644
--- a/efm32/src/InitDevice.c
+++ b/efm32/src/InitDevice.c
@@ -23,7 +23,7 @@
#include "em_cryotimer.h"
#include "em_crypto.h"
#include "em_gpio.h"
-#include "em_ldma.h"
+#include "em_i2c.h"
#include "em_usart.h"
// [Library includes]$
@@ -39,7 +39,7 @@ extern void enter_DefaultMode_from_RESET(void) {
ADC0_enter_DefaultMode_from_RESET();
USART0_enter_DefaultMode_from_RESET();
USART1_enter_DefaultMode_from_RESET();
- LDMA_enter_DefaultMode_from_RESET();
+ I2C0_enter_DefaultMode_from_RESET();
CRYOTIMER_enter_DefaultMode_from_RESET();
PORTIO_enter_DefaultMode_from_RESET();
// [Config Calls]$
@@ -104,7 +104,7 @@ extern void CMU_enter_DefaultMode_from_RESET(void) {
CMU_HFXOInit(&hfxoInit);
/* Setting system HFRCO frequency */
- CMU_HFRCOFreqSet (cmuHFRCOFreq_38M0Hz);
+ CMU_HFRCOFreqSet (cmuHFRCOFreq_13M0Hz);
/* Using HFRCO as high frequency clock, HFCLK */
CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO);
@@ -135,8 +135,8 @@ extern void CMU_enter_DefaultMode_from_RESET(void) {
/* Enable clock for CRYOTIMER */
CMU_ClockEnable(cmuClock_CRYOTIMER, true);
- /* Enable clock for LDMA */
- CMU_ClockEnable(cmuClock_LDMA, true);
+ /* Enable clock for I2C0 */
+ CMU_ClockEnable(cmuClock_I2C0, true);
/* Enable clock for USART0 */
CMU_ClockEnable(cmuClock_USART0, true);
@@ -455,9 +455,24 @@ extern void WDOG0_enter_DefaultMode_from_RESET(void) {
extern void I2C0_enter_DefaultMode_from_RESET(void) {
// $[I2C0 I/O setup]
+ /* Set up SCL */
+ I2C0->ROUTEPEN = I2C0->ROUTEPEN | I2C_ROUTEPEN_SCLPEN;
+ I2C0->ROUTELOC0 = (I2C0->ROUTELOC0 & (~_I2C_ROUTELOC0_SCLLOC_MASK))
+ | I2C_ROUTELOC0_SCLLOC_LOC3;
+ /* Set up SDA */
+ I2C0->ROUTEPEN = I2C0->ROUTEPEN | I2C_ROUTEPEN_SDAPEN;
+ I2C0->ROUTELOC0 = (I2C0->ROUTELOC0 & (~_I2C_ROUTELOC0_SDALOC_MASK))
+ | I2C_ROUTELOC0_SDALOC_LOC27;
// [I2C0 I/O setup]$
// $[I2C0 initialization]
+ I2C_Init_TypeDef init = I2C_INIT_DEFAULT;
+
+ init.enable = 1;
+ init.master = 1;
+ init.freq = I2C_FREQ_STANDARD_MAX;
+ init.clhr = i2cClockHLRStandard;
+ I2C_Init(I2C0, &init);
// [I2C0 initialization]$
}
@@ -610,6 +625,9 @@ extern void PORTIO_enter_DefaultMode_from_RESET(void) {
/* Pin PA3 is configured to Push-pull */
GPIO_PinModeSet(gpioPortA, 3, gpioModePushPull, 0);
+ /* Pin PA4 is configured to Open-drain with pull-up and filter */
+ GPIO_PinModeSet(gpioPortA, 4, gpioModeWiredAndPullUpFilter, 0);
+
/* Pin PA5 is configured to Push-pull */
GPIO_PinModeSet(gpioPortA, 5, gpioModePushPull, 1);
// [Port A Configuration]$
@@ -649,6 +667,9 @@ extern void PORTIO_enter_DefaultMode_from_RESET(void) {
// $[Port F Configuration]
+ /* Pin PF3 is configured to Open-drain with pull-up and filter */
+ GPIO_PinModeSet(gpioPortF, 3, gpioModeWiredAndPullUpFilter, 0);
+
/* Pin PF4 is configured to Push-pull */
GPIO_PinModeSet(gpioPortF, 4, gpioModePushPull, 0);
diff --git a/efm32/src/device.c b/efm32/src/device.c
index 37b7936..ed8b551 100644
--- a/efm32/src/device.c
+++ b/efm32/src/device.c
@@ -14,6 +14,7 @@
#include "em_adc.h"
#include "em_cmu.h"
#include "em_msc.h"
+#include "em_i2c.h"
#include "InitDevice.h"
#include "cbor.h"
@@ -23,6 +24,7 @@
#include "app.h"
#include "uECC.h"
#include "crypto.h"
+#include "nfc.h"
#define MSG_AVAIL_PIN gpioPortC,9
#define RDY_PIN gpioPortC,10
@@ -117,17 +119,19 @@ int ctap_user_presence_test()
// Must be implemented by application
// data is HID_MESSAGE_SIZE long in bytes
+#ifndef TEST_POWER
void ctaphid_write_block(uint8_t * data)
{
printf1(TAG_DUMP,"<< "); dump_hex1(TAG_DUMP, data, HID_MESSAGE_SIZE);
usbhid_send(data);
}
+#endif
void heartbeat()
{
GPIO_PinOutToggle(gpioPortF,4);
GPIO_PinOutToggle(gpioPortF,5);
-
+ nfc_test();
// printf("heartbeat %d %d\r\n", beat++,CRYOTIMER->CNT);
}
@@ -158,6 +162,7 @@ static void wait_for_efm8_busy()
;
}
+#ifndef TEST_POWER
int usbhid_recv(uint8_t * msg)
{
int i;
@@ -188,6 +193,8 @@ int usbhid_recv(uint8_t * msg)
return 0;
}
+#endif
+
void usbhid_send(uint8_t * msg)
{
int i;
diff --git a/efm32/src/nfc.c b/efm32/src/nfc.c
new file mode 100644
index 0000000..9564bb0
--- /dev/null
+++ b/efm32/src/nfc.c
@@ -0,0 +1,178 @@
+/*
+ * nfc.c
+ *
+ * Created on: Jul 22, 2018
+ * Author: conor
+ */
+
+#include
+#include
+#include
+
+#include "em_chip.h"
+#include "em_gpio.h"
+#include "em_i2c.h"
+
+#include "log.h"
+#include "util.h"
+#include "nfc.h"
+#include "app.h"
+
+
+I2C_TransferReturn_TypeDef I2CSPM_Transfer(I2C_TypeDef *i2c, I2C_TransferSeq_TypeDef *seq)
+{
+ I2C_TransferReturn_TypeDef ret;
+ uint32_t timeout = 10000;
+ /* Do a polled transfer */
+ ret = I2C_TransferInit(i2c, seq);
+ while (ret == i2cTransferInProgress && timeout--)
+ {
+ ret = I2C_Transfer(i2c);
+ }
+ return ret;
+}
+
+// data must be 16 bytes
+void read_block(uint8_t block, uint8_t * data)
+{
+ uint8_t addr = 0xAA;
+ I2C_TransferSeq_TypeDef seq;
+ I2C_TransferReturn_TypeDef ret;
+ uint8_t i2c_read_data[16];
+ uint8_t i2c_write_data[1];
+
+ seq.addr = addr;
+ seq.flags = I2C_FLAG_WRITE_READ;
+ /* Select command to issue */
+ i2c_write_data[0] = block;
+ seq.buf[0].data = i2c_write_data;
+ seq.buf[0].len = 1;
+ /* Select location/length of data to be read */
+ seq.buf[1].data = i2c_read_data;
+ seq.buf[1].len = 16;
+
+ ret = I2CSPM_Transfer(I2C0, &seq);
+
+ if (ret != i2cTransferDone) {
+ printf("I2C fail %04x\r\n",ret);
+ exit(1);
+ }
+ memmove(data, i2c_read_data, 16);
+}
+void write_reg_flash(uint8_t reg_addr, uint8_t mask,uint8_t data)
+{
+ uint8_t addr = 0xAA;
+ I2C_TransferSeq_TypeDef seq;
+ I2C_TransferReturn_TypeDef ret;
+ uint8_t i2c_write_data[4];
+
+ seq.addr = addr;
+ seq.flags = I2C_FLAG_WRITE;
+ i2c_write_data[0] = 0x3a;
+ i2c_write_data[1] = reg_addr;
+ i2c_write_data[2] = mask;
+ i2c_write_data[3] = data;
+
+ seq.buf[0].data = i2c_write_data;
+ seq.buf[0].len = 4;
+ seq.buf[1].data = NULL;
+ seq.buf[1].len = 0;
+
+ ret = I2CSPM_Transfer(I2C0, &seq);
+
+ if (ret != i2cTransferDone) {
+ printf("I2C fail %04x\r\n",ret);
+ exit(1);
+ }
+}
+void write_reg(uint8_t reg_addr, uint8_t mask,uint8_t data)
+{
+ uint8_t addr = 0xAA;
+ I2C_TransferSeq_TypeDef seq;
+ I2C_TransferReturn_TypeDef ret;
+ uint8_t i2c_write_data[4];
+
+ seq.addr = addr;
+ seq.flags = I2C_FLAG_WRITE;
+ i2c_write_data[0] = 0xFE;
+ i2c_write_data[1] = reg_addr;
+ i2c_write_data[2] = mask;
+ i2c_write_data[3] = data;
+
+ seq.buf[0].data = i2c_write_data;
+ seq.buf[0].len = 4;
+ seq.buf[1].data = NULL;
+ seq.buf[1].len = 0;
+
+ ret = I2CSPM_Transfer(I2C0, &seq);
+
+ if (ret != i2cTransferDone) {
+ printf("I2C fail %04x\r\n",ret);
+ exit(1);
+ }
+}
+
+uint8_t read_reg(uint8_t reg_addr)
+{
+ I2C_TransferSeq_TypeDef seq;
+ I2C_TransferReturn_TypeDef ret;
+ uint8_t write_data[2];
+ uint8_t read_data[1];
+
+ seq.addr = 0xAA;
+ seq.flags = I2C_FLAG_WRITE_READ;
+ write_data[0] = 0xFE;
+ write_data[1] = reg_addr;
+
+ seq.buf[0].data = write_data;
+ seq.buf[0].len = 2;
+ seq.buf[1].data = read_data;
+ seq.buf[1].len = 1;
+
+ ret = I2CSPM_Transfer(I2C0, &seq);
+
+ if (ret != i2cTransferDone) {
+ printf("I2C fail %04x\r\n",ret);
+ exit(1);
+ }
+
+ return read_data[0];
+}
+
+void read_reg_block(uint8_t * data)
+{
+ int i;
+ for (i = 0; i < 7; i++)
+ {
+ *data = read_reg(i);
+// printf("data %d: %x\n" ,i,(int)(*data));
+ data++;
+ }
+}
+
+
+void nfc_test()
+{
+ uint8_t data[16];
+ printf("-NFC test-\n");
+
+ read_block(0x00, data);
+ printf("block 00: "); dump_hex(data,16);
+
+ read_reg_block(data);
+ printf("block 3A [init]:"); dump_hex(data,8);
+
+ write_reg(0, 0xff, 0x43);
+ write_reg_flash(0, 0xff, 0x43);
+ write_reg(2, 0xff, 0x01);
+
+ read_reg_block(data);
+ printf("block 3A [done]:"); dump_hex(data,8);
+//
+// read_block(0x3A, data);
+// printf("block 3A [done]:"); dump_hex(data,16);
+
+ while(1)
+ ;
+
+}
diff --git a/fido2/main.c b/fido2/main.c
index b0fb0a3..bcaf701 100644
--- a/fido2/main.c
+++ b/fido2/main.c
@@ -9,7 +9,7 @@
#include "util.h"
#include "log.h"
#include "ctap.h"
-
+#include "app.h"
#if !defined(TEST)
diff --git a/fido2/test_power.c b/fido2/test_power.c
index e6a88ec..43f8907 100644
--- a/fido2/test_power.c
+++ b/fido2/test_power.c
@@ -1,3 +1,4 @@
+#include "app.h"
#ifdef TEST_POWER
/*
@@ -13,15 +14,29 @@
#include "cbor.h"
#include "device.h"
#include "ctaphid.h"
-#include "bsp.h"
#include "util.h"
#include "log.h"
#include "ctap.h"
+//#define BUTT NRF_GPIO_PIN_MAP(0,11)
+//#define TRIG NRF_GPIO_PIN_MAP(0,30)
+//
+//#define GPIO_CLEAR(PIN) nrf_gpio_pin_clear(PIN)
+//#define GPIO_SET(PIN) nrf_gpio_pin_set(PIN)
+//#define GPIO_OUTPUT(PIN) nrf_gpio_cfg_output(PIN)
+//#define GPIO_INPUT(PIN) nrf_gpio_cfg_input(PIN,NRF_GPIO_PIN_PULLUP)
+//#define GPIO_READ(PIN) nrf_gpio_pin_read(PIN)
+#include "em_gpio.h"
+#define BUTT gpioPortF,6
+#define TRIG gpioPortD,14
+
+#define GPIO_CLEAR(PIN) GPIO_PinOutClear(PIN)
+#define GPIO_SET(PIN) GPIO_PinOutSet(PIN)
+#define GPIO_OUTPUT(PIN) GPIO_PinModeSet(PIN,gpioModePushPull,1)
+#define GPIO_INPUT(PIN) GPIO_PinModeSet(PIN,gpioModeInputPull,1)
+#define GPIO_READ(PIN) GPIO_PinInGet(PIN)
-#define BUTT NRF_GPIO_PIN_MAP(0,11)
-#define TRIG NRF_GPIO_PIN_MAP(0,30)
int main(int argc, char * argv[])
{
@@ -45,16 +60,16 @@ int main(int argc, char * argv[])
/*TAG_DUMP|*/
/*TAG_GREEN|*/
/*TAG_RED|*/
- /*TAG_ERR*/
+ |TAG_ERR
);
device_init();
ctaphid_init();
ctap_init();
- nrf_gpio_cfg_input(BUTT, NRF_GPIO_PIN_PULLUP);
- nrf_gpio_cfg_output(TRIG);
- nrf_gpio_pin_clear(TRIG);
+ GPIO_INPUT(BUTT);
+ GPIO_OUTPUT(TRIG);
+ GPIO_CLEAR(TRIG);
memset(hidmsg,0,sizeof(hidmsg));
@@ -92,13 +107,7 @@ int main(int argc, char * argv[])
return 0;
}
-void delay(int ms)
-{
- uint64_t t1;
- t1 = millis();
- while(millis()-t1 < ms)
- ;
-}
+
void ctaphid_write_block(uint8_t * data)
{
@@ -130,8 +139,8 @@ int usbhid_recv(uint8_t * msg)
if (!reading)
{
delay(1);
- nrf_gpio_pin_clear(TRIG);
- val = nrf_gpio_pin_read(BUTT);
+ GPIO_CLEAR(TRIG);
+ val = GPIO_READ(BUTT);
if (val == 0)
{
if (lastval != 0)
@@ -145,7 +154,7 @@ int usbhid_recv(uint8_t * msg)
}
else
{
- nrf_gpio_pin_set(TRIG);
+ GPIO_SET(TRIG);
memmove(msg, hidcmds[reading-1], 64);
reading++;
if (reading-1 == sizeof(hidcmds)/64)
diff --git a/tools/nfcmon.py b/tools/nfcmon.py
new file mode 100644
index 0000000..c071045
--- /dev/null
+++ b/tools/nfcmon.py
@@ -0,0 +1,32 @@
+
+import datetime,sys
+from binascii import hexlify
+
+import Chameleon
+
+def verboseLog(text):
+ formatString = "[{}] {}"
+ timeString = datetime.datetime.utcnow()
+ print(formatString.format(timeString, text), )
+
+chameleon = Chameleon.Device(verboseLog)
+
+p = None
+for p in Chameleon.Device.listDevices():
+ break
+
+if p:
+ chameleon.connect(p)
+else:
+ raise RuntimeError('No chameleon mini connected')
+
+chameleon.execCmd('LOGMODE=LIVE')
+
+while 1:
+ b = chameleon.read(1,20)
+ h = hexlify(b)
+ h = h.decode()
+ sys.stdout.write(h)
+ sys.stdout.flush()
+
+chameleon.execCmd('LOGMODE=NONE')