Files
tinyUSB/hw/mcu/nxp/lpc_chip_13xx/src/i2cm_13xx.c

260 lines
7.4 KiB
C
Raw Normal View History

/*
* @brief LPC13xx I2C master driver
*
* @note
* Copyright(C) NXP Semiconductors, 2013
* All rights reserved.
*
* @par
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* LPC products. This software is supplied "AS IS" without any warranties of
* any kind, and NXP Semiconductors and its licensor disclaim any and
* all warranties, express or implied, including all implied warranties of
* merchantability, fitness for a particular purpose and non-infringement of
* intellectual property rights. NXP Semiconductors assumes no responsibility
* or liability for the use of the software, conveys no license or rights under any
* patent, copyright, mask work right, or any other intellectual property rights in
* or to any products. NXP Semiconductors reserves the right to make changes
* in the software without notification. NXP Semiconductors also makes no
* representation or warranty that such application will be suitable for the
* specified use without further testing or modification.
*
* @par
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors' and its
* licensor's relevant copyrights in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers. This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
*/
#include "chip.h"
/*****************************************************************************
* Private types/enumerations/variables
****************************************************************************/
/* Control flags */
#define I2C_CON_FLAGS (I2C_CON_AA | I2C_CON_SI | I2C_CON_STO | I2C_CON_STA)
/*****************************************************************************
* Public types/enumerations/variables
****************************************************************************/
/*****************************************************************************
* Private functions
****************************************************************************/
/*****************************************************************************
* Public functions
****************************************************************************/
/* Initializes the LPC_I2C peripheral with specified parameter */
void Chip_I2CM_Init(LPC_I2C_T *pI2C)
{
/* Enable I2C clock */
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_I2C);
/* Peripheral reset control to I2C */
Chip_SYSCTL_PeriphReset(RESET_I2C0);
}
/* De-initializes the I2C peripheral registers to their default reset values */
void Chip_I2CM_DeInit(LPC_I2C_T *pI2C)
{
/* Disable I2C clock */
Chip_Clock_DisablePeriphClock(SYSCTL_CLOCK_I2C);
}
/* Set up bus speed for LPC_I2C interface */
void Chip_I2CM_SetBusSpeed(LPC_I2C_T *pI2C, uint32_t busSpeed)
{
uint32_t clockDiv = (Chip_Clock_GetMainClockRate() / busSpeed);
Chip_I2CM_SetDutyCycle(pI2C, (clockDiv >> 1), (clockDiv - (clockDiv >> 1)));
}
/* Master transfer state change handler handler */
uint32_t Chip_I2CM_XferHandler(LPC_I2C_T *pI2C, I2CM_XFER_T *xfer)
{
uint32_t cclr = I2C_CON_FLAGS;
switch (Chip_I2CM_GetCurState(pI2C)) {
case 0x08: /* Start condition on bus */
case 0x10: /* Repeated start condition */
pI2C->DAT = (xfer->slaveAddr << 1) | (xfer->txSz == 0);
break;
/* Tx handling */
case 0x20: /* SLA+W sent NAK received */
case 0x30: /* DATA sent NAK received */
if ((xfer->options & I2CM_XFER_OPTION_IGNORE_NACK) == 0) {
xfer->status = I2CM_STATUS_NAK;
cclr &= ~I2C_CON_STO;
break;
}
case 0x18: /* SLA+W sent and ACK received */
case 0x28: /* DATA sent and ACK received */
if (!xfer->txSz) {
if (xfer->rxSz) {
cclr &= ~I2C_CON_STA;
}
else {
xfer->status = I2CM_STATUS_OK;
cclr &= ~I2C_CON_STO;
}
}
else {
pI2C->DAT = *xfer->txBuff++;
xfer->txSz--;
}
break;
/* Rx handling */
case 0x58: /* Data Received and NACK sent */
case 0x50: /* Data Received and ACK sent */
*xfer->rxBuff++ = pI2C->DAT;
xfer->rxSz--;
case 0x40: /* SLA+R sent and ACK received */
if ((xfer->rxSz > 1) || (xfer->options & I2CM_XFER_OPTION_LAST_RX_ACK)) {
cclr &= ~I2C_CON_AA;
}
if (xfer->rxSz == 0) {
xfer->status = I2CM_STATUS_OK;
cclr &= ~I2C_CON_STO;
}
break;
/* NAK Handling */
case 0x48: /* SLA+R sent NAK received */
xfer->status = I2CM_STATUS_SLAVE_NAK;
cclr &= ~I2C_CON_STO;
break;
case 0x38: /* Arbitration lost */
xfer->status = I2CM_STATUS_ARBLOST;
break;
case 0x00: /* Bus Error */
xfer->status = I2CM_STATUS_BUS_ERROR;
cclr &= ~I2C_CON_STO;
break;
default:
xfer->status = I2CM_STATUS_ERROR;
cclr &= ~I2C_CON_STO;
break;
}
/* Set clear control flags */
pI2C->CONSET = cclr ^ I2C_CON_FLAGS;
pI2C->CONCLR = cclr;
return xfer->status != I2CM_STATUS_BUSY;
}
/* Transmit and Receive data in master mode */
void Chip_I2CM_Xfer(LPC_I2C_T *pI2C, I2CM_XFER_T *xfer)
{
/* set the transfer status as busy */
xfer->status = I2CM_STATUS_BUSY;
/* Clear controller state. */
Chip_I2CM_ResetControl(pI2C);
/* Enter to Master Transmitter mode */
Chip_I2CM_SendStart(pI2C);
}
/* Transmit and Receive data in master mode */
uint32_t Chip_I2CM_XferBlocking(LPC_I2C_T *pI2C, I2CM_XFER_T *xfer)
{
uint32_t ret = 0;
/* start transfer */
Chip_I2CM_Xfer(pI2C, xfer);
while (ret == 0) {
/* wait for status change interrupt */
while ( Chip_I2CM_StateChanged(pI2C) == 0) {}
/* call state change handler */
ret = Chip_I2CM_XferHandler(pI2C, xfer);
}
return ret;
}
/* Master tx only */
uint32_t Chip_I2CM_Write(LPC_I2C_T *pI2C, const uint8_t *buff, uint32_t len)
{
uint32_t txLen = 0, err = 0;
/* clear state change interrupt status */
Chip_I2CM_ClearSI(pI2C);
/* generate START condition */
Chip_I2CM_SendStart(pI2C);
while ((txLen < len) && (err == 0)) {
/* wait for status change interrupt */
while ( Chip_I2CM_StateChanged(pI2C) == 0) {}
/* check status and send data */
switch (Chip_I2CM_GetCurState(pI2C)) {
case 0x08: /* Start condition on bus */
case 0x10: /* Repeated start condition */
case 0x18: /* SLA+W sent and ACK received */
case 0x28: /* DATA sent and ACK received */
Chip_I2CM_WriteByte(pI2C, buff[txLen++]);
break;
case 0x38: /* Arbitration lost */
break;
default: /* we shouldn't be in any other state */
err = 1;
break;
}
/* clear state change interrupt status */
Chip_I2CM_ClearSI(pI2C);
}
return txLen;
}
/* Sequential master read */
uint32_t Chip_I2CM_Read(LPC_I2C_T *pI2C, uint8_t *buff, uint32_t len)
{
uint32_t rxLen = 0, err = 0;
/* clear state change interrupt status */
Chip_I2CM_ClearSI(pI2C);
/* generate START condition and auto-ack data received */
pI2C->CONSET = I2C_CON_AA | I2C_CON_STA;
while ((rxLen < len) && (err == 0)) {
/* wait for status change interrupt */
while ( Chip_I2CM_StateChanged(pI2C) == 0) {}
/* check status and send data */
switch (Chip_I2CM_GetCurState(pI2C)) {
case 0x08: /* Start condition on bus */
case 0x10: /* Repeated start condition */
case 0x40: /* SLA+R sent and ACK received */
case 0x50: /* Data Received and ACK sent */
buff[rxLen++] = Chip_I2CM_ReadByte(pI2C);
break;
case 0x38: /* Arbitration lost */
break;
default: /* we shouldn't be in any other state */
err = 1;
break;
}
/* clear state change interrupt status */
Chip_I2CM_ClearSI(pI2C);
}
return rxLen;
}