Files
zTC1/mico-os/platform/MCU/LPC5410x/peripherals/platform_i2c.c

517 lines
14 KiB
C

/**
******************************************************************************
* @file MicoDriverI2C.c
* @author William Xu
* @version V1.0.0
* @date 05-May-2014
* @brief This file provide I2C driver functions.
******************************************************************************
* UNPUBLISHED PROPRIETARY SOURCE CODE
* Copyright (c) 2016 MXCHIP Inc.
*
* The contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of MXCHIP Corporation.
******************************************************************************
*/
#include "mico_platform.h"
#include "mico_rtos.h"
#include "platform.h"
#include "platform_peripheral.h"
#include "chip.h"
#include "platform_logging.h"
/******************************************************
* Constants
******************************************************/
#define I2C_FLAG_CHECK_TIMEOUT ( 1000 )
#define I2C_FLAG_CHECK_LONG_TIMEOUT ( 1000 )
#define I2C_Direction_Transmitter ((uint8_t)0x00) // Wecan Modify 2015.06.05
#define I2C_Direction_Receiver ((uint8_t)0x01) // Wecan Modify 2015.06.05
//#define I2C_USE_DMA
#define DMA_FLAG_TC(stream_id) dma_flag_tc(stream_id)
#define DMA_TIMEOUT_LOOPS (10000000)
/******************************************************
* Enumerations
******************************************************/
/******************************************************
* Type Definitions
******************************************************/
/******************************************************
* Structures
******************************************************/
/******************************************************
* Variables Definitions
******************************************************/
platform_i2c_cb_t s_i2cCtxs[MICO_I2C_MAX];
/******************************************************
* Function Declarations
******************************************************/
//// Wecan ToDo 2015.06.05
//static OSStatus i2c_address_device( const platform_i2c_t* i2c, const platform_i2c_config_t* config, int retries, uint8_t direction );
//static OSStatus i2c_wait_for_event( LPC_I2C_T *pI2C, uint32_t number_of_waits );
//////////////////////////////////////////////////////////////////////////////////////////////
void _prvI2CHwCfg(const platform_i2c_t *i2c, const platform_i2c_config_t *config)
{
uint32_t pinSCL = 0, pinSDA = 0, pinCfg = 0, busHz = 0;
LPC_I2C_T *pI2C = i2c->port;
if (config->speed_mode == I2C_LOW_SPEED_MODE)
busHz = 10 * 1000;
else if (config->speed_mode == I2C_STANDARD_SPEED_MODE)
busHz = 100 * 1000;
else if (config->speed_mode == I2C_HIGH_SPEED_MODE)
busHz = 400 * 1000;
//else if (config->speed_mode == I2C_HISPD_PLUS_MODE)
// busHz = 1000 * 1000;
pinCfg = IOCON_FUNC1 | 1UL<<7 | 1UL<<8;
if (busHz > 400 * 1000)
pinCfg |= 1UL<<9 | 1UL<<10; // High driver, no filter
if (pI2C == LPC_I2C0)
pinSCL = 23 , pinSDA = 24;
else if (pI2C == LPC_I2C1)
pinSCL = 25 , pinSDA = 26;
else if (pI2C == LPC_I2C2)
pinSCL = 27 , pinSDA = 28;
g_pIO->PIO[0][pinSCL] = pinCfg;
g_pIO->PIO[0][pinSDA] = pinCfg;
//////////////////////////////////////////////////////////////////////////////////////////////////
/* Setup clock rate for I2C */
{
uint32_t t1;
// 12 times of wanted I2C bus clock
t1 = Chip_Clock_GetAsyncSyscon_ClockRate();
t1 /= 12 * busHz;
if (0 == t1)
t1 = 1;
if (t1 > 65536)
t1 = 65536;
pI2C->CLKDIV = t1 - 1;
pI2C->MSTTIME = (6-2) | ((6-2) << 4);
}
}
/******************************************************
* Function Definitions
******************************************************/
OSStatus platform_i2c_init(const platform_i2c_t* i2c, const platform_i2c_config_t* config )
{
OSStatus err = kNoErr;
LPC_I2C_T *pI2C;
#ifndef BOOTLOADER
platform_i2c_cb_t *pCtx = i2c->pCtx;
#endif
// platform_mcu_powersave_disable( );
require_action_quiet( (i2c != NULL && i2c->hwNdx < MICO_I2C_MAX && config != NULL && config->address_width == I2C_ADDRESS_WIDTH_7BIT), exit, err = kParamErr);
pI2C = i2c->port;
#ifndef BOOTLOADER
if (NULL == pCtx->semXfer)
mico_rtos_init_semaphore(&pCtx->semXfer, 1);
if (NULL == pCtx->mtxXfer)
mico_rtos_init_mutex(&pCtx->mtxXfer);
#endif
// Init & Reset I2C clocks Wecan modify 2015.06.03
Chip_I2C_Init(i2c->port);//(LPC_I2C_PORT);
_prvI2CHwCfg(i2c, config);
Chip_I2CM_Enable(pI2C);
NVIC_EnableIRQ((IRQn_Type)i2c->irqNdx);
exit:
// platform_mcu_powersave_enable( );
return err;
}
OSStatus platform_i2c_init_tx_message( platform_i2c_message_t* message, const void* tx_buffer, uint16_t tx_buffer_length, uint16_t retries )
{
OSStatus err = kNoErr;
require_action_quiet( ( message != NULL ) && ( tx_buffer != NULL ) && ( tx_buffer_length != 0 ), exit, err = kParamErr);
memset(message, 0x00, sizeof(mico_i2c_message_t));
message->rx_buffer = 0;
message->tx_buffer = tx_buffer;
message->retries = retries;
message->tx_length= tx_buffer_length;
message->rx_length= 0;
message->combined = 0;
exit:
return err;
}
OSStatus platform_i2c_init_rx_message( platform_i2c_message_t* message, void* rx_buffer, uint16_t rx_buffer_length, uint16_t retries )
{
OSStatus err = kNoErr;
require_action_quiet( ( message != NULL ) && ( rx_buffer != NULL ) && ( rx_buffer_length != 0 ), exit, err = kParamErr);
memset(message, 0x00, sizeof(mico_i2c_message_t));
message->rx_buffer = rx_buffer;
message->tx_buffer = 0;
message->retries = retries;
message->tx_length= 0;
message->rx_length= rx_buffer_length;
message->combined = 0;
exit:
return err;
}
OSStatus platform_i2c_init_combined_message( platform_i2c_message_t* message, const void* tx_buffer, void* rx_buffer, uint16_t tx_buffer_length, uint16_t rx_buffer_length, uint16_t retries )
{
OSStatus err = kNoErr;
require_action_quiet( ( message != NULL ) && ( tx_buffer != NULL ) && ( tx_buffer_length != 0 ) && ( rx_buffer != NULL ) && ( rx_buffer_length != 0 ), exit, err = kParamErr);
memset(message, 0x00, sizeof(mico_i2c_message_t));
message->rx_buffer = rx_buffer;
message->tx_buffer = tx_buffer;
message->retries = retries;
message->tx_length= tx_buffer_length;
message->rx_length= rx_buffer_length;
message->combined = 1;
exit:
return err;
}
/* Master transfer state change handler handler */
uint32_t _prvI2CMXferFSM(LPC_I2C_T *pI2C, platform_i2c_cb_t *xfer)
{
uint32_t status = pI2C->STAT;
if (status & I2C_STAT_MSTRARBLOSS) {
/* Master Lost Arbitration */
/* Set transfer status as Arbitration Lost */
xfer->status = I2CM_STATUS_ARBLOST;
/* Clear Status Flags */
pI2C->STAT = I2C_STAT_MSTRARBLOSS;
/* Master continue */
if (status & I2C_STAT_MSTPENDING) {
pI2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE;
}
}
else if (status & I2C_STAT_MSTSTSTPERR) {
/* Master Start Stop Error */
/* Set transfer status as Bus Error */
xfer->status = I2CM_STATUS_BUS_ERROR;
/* Clear Status Flags */
pI2C->STAT = I2C_STAT_MSTSTSTPERR;
/* Master continue */
if (status & I2C_STAT_MSTPENDING) {
pI2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE;
}
}
else if (status & I2C_STAT_MSTPENDING) {
/* Master is Pending */
/* Branch based on Master State Code */
switch ((pI2C->STAT & I2C_STAT_MSTSTATE) >> 1) {
case I2C_STAT_MSTCODE_IDLE: /* Master idle */
/* Can transition to idle between transmit and receive states */
if (xfer->txSz || xfer->isProbe) {
/* Start transmit state */
pI2C->MSTDAT = xfer->slaveAddr << 1;
pI2C->MSTCTL = I2C_MSTCTL_MSTSTART;
}
else if (xfer->rxSz) {
/* Start receive state with start ot repeat start */
pI2C->MSTDAT = (xfer->slaveAddr << 1) | 0x1;
pI2C->MSTCTL = I2C_MSTCTL_MSTSTART;
}
else {
/* No data to send, done */
xfer->status = I2CM_STATUS_OK;
}
break;
case I2C_STAT_MSTCODE_RXREADY: /* Receive data is available */
/* Read Data up until the buffer size */
if (xfer->rxSz) {
*xfer->rxBuff = pI2C->MSTDAT;
xfer->rxBuff++;
xfer->rxSz--;
}
if (xfer->rxSz) {
pI2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE;
}
else {
/* Last byte to receive, send stop after byte received */
pI2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE | I2C_MSTCTL_MSTSTOP;
}
break;
case I2C_STAT_MSTCODE_TXREADY: /* Master Transmit available */
if (xfer->txSz) {
/* If Tx data available transmit data and continue */
pI2C->MSTDAT = (uint32_t) *xfer->txBuff;
pI2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE;
xfer->txBuff++;
xfer->txSz--;
}
else if (xfer->rxSz == 0) {
pI2C->MSTCTL = I2C_MSTCTL_MSTSTOP;
}
else {
/* Start receive state with start ot repeat start */
pI2C->MSTDAT = (xfer->slaveAddr << 1) | 0x1;
#if 0 //#ifdef I2CM_DMA_SUPPORT
if (xfer->rxSz >= I2C_SENSOR_BUS_DMABYTELIM)
{
#ifdef I2CM_DMA_RXALL
setupI2CDMAXfer((void *) xfer->rxBuff, xfer->rxSz, 0 /*isTx*/);
#else
setupI2CDMAXfer((void *) xfer->rxBuff, xfer->rxSz - 1, 0 /*isTx*/);
#endif
LPC_I2C_PORT->INTENCLR = I2C_INTENSET_MSTPENDING;
// g_pDMA->DMACOMMON[0].SETVALID = (1 << I2C_SENSOR_BUS_DMAID);
pI2C->MSTCTL = I2C_MSTCTL_MSTSTART | I2C_MSTCTL_MSTDMA;
}
else
{
pI2C->MSTCTL = I2C_MSTCTL_MSTSTART;
}
#else
pI2C->MSTCTL = I2C_MSTCTL_MSTSTART;
#endif
}
break;
case I2C_STAT_MSTCODE_NACKADR: /* Slave address was NACK'ed */
/* Set transfer status as NACK on address */
xfer->status = I2CM_STATUS_NAK_ADR;
pI2C->MSTCTL = I2C_MSTCTL_MSTSTOP;
break;
case I2C_STAT_MSTCODE_NACKDAT: /* Slave data was NACK'ed */
/* Set transfer status as NACK on data */
xfer->status = I2CM_STATUS_NAK_DAT;
pI2C->MSTCTL = I2C_MSTCTL_MSTSTOP;
break;
default:
/* Illegal I2C master state machine case. This should never happen.
Try to advance state machine by continuing. */
xfer->status = I2CM_STATUS_ERROR;
pI2C->MSTCTL = I2C_MSTCTL_MSTCONTINUE;
break;
}
}
else {
/* Unsupported operation. This may be a call to the master handler
for a wrong interrupt type. This handler should only be called when a
master arbitration loss, master start/stop error, or master pending status
occurs. */
xfer->status = I2CM_STATUS_ERROR;
}
#ifndef BOOTLOADER
if (xfer->status != I2CM_STATUS_BUSY)
{
platform_i2c_t *pDev = (platform_i2c_t*) xfer->pDev;
pDev->pCtx->isProbe = 0;
mico_rtos_set_semaphore(&pDev->pCtx->semXfer);
}
#endif
return xfer->status != I2CM_STATUS_BUSY;
}
void MyI2CM_Xfer(const platform_i2c_t *pDev, platform_i2c_message_t *pMsg)
{
LPC_I2C_T *pI2C = pDev->port;
platform_i2c_cb_t *pCtx = pDev->pCtx;
/* set the transfer status as busy */
pCtx->rxBuff = pMsg->rx_buffer;
pCtx->txBuff = pMsg->tx_buffer;
pCtx->rxSz = pMsg->rx_length;
pCtx->txSz = pMsg->tx_length;
pCtx->retries = pMsg->retries;
pCtx->status = I2CM_STATUS_BUSY;
/* Reset master state machine */
Chip_I2CM_Disable(pI2C);
Chip_I2CM_Enable(pI2C);
/* Clear controller state. */
Chip_I2CM_ClearStatus(pI2C, I2C_STAT_MSTRARBLOSS | I2C_STAT_MSTSTSTPERR);
/* Handle transfer via initial call to handler */
_prvI2CMXferFSM(pI2C, pCtx);
}
OSStatus WaitForI2cXferComplete(const platform_i2c_t* i2c, platform_i2c_message_t *xferRecPtr)
{
OSStatus err = kNoErr;
#ifndef BOOTLOADER
//do {
err = mico_rtos_get_semaphore(&i2c->pCtx->semXfer, 300);
//} while (err != kNoErr);
#else
int i2c_timeout = 3000;
while (i2c->pCtx->status == I2CM_STATUS_BUSY && i2c_timeout-- > 0) {
/* Sleep until next interrupt */
__WFI();
}
if (0 == i2c_timeout)
err = kTimeoutErr;
#endif
if (err != kNoErr)
err = kTimeoutErr;
else
{
if (i2c->pCtx->status != I2CM_STATUS_OK)
err = kResponseErr;
}
return err;
}
OSStatus platform_i2c_transfer(const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* messages, uint16_t number_of_messages )
{
OSStatus err = kNoErr;
int i = 0, retryCnt;
platform_i2c_cb_t *pCtx = i2c->pCtx;
// platform_mcu_powersave_disable();
require_action_quiet( i2c != NULL, exit, err = kParamErr);
#ifndef BOOTLOADER
mico_rtos_lock_mutex(&pCtx->mtxXfer);
#endif
pCtx->slaveAddr = config->address;
pCtx->pDev = i2c;
for( i=0; i < number_of_messages; i++ )
{
_prvI2CHwCfg(i2c, config);
for (retryCnt = 0; retryCnt < messages[i].retries; retryCnt++)
{
MyI2CM_Xfer(i2c, messages + i);
/* Enable Master Interrupts */
Chip_I2C_EnableInt(i2c->port, I2C_INTENSET_MSTPENDING | I2C_INTENSET_MSTRARBLOSS | I2C_INTENSET_MSTSTSTPERR);
/* Wait for transfer completion */
err = WaitForI2cXferComplete(i2c, messages + i);
if (err == kNoErr)
break;
}
if (err != kNoErr)
break;
}
exit:
#ifndef BOOTLOADER
mico_rtos_unlock_mutex(&pCtx->mtxXfer);
#endif
// platform_mcu_powersave_enable();
return err;
}
bool platform_i2c_probe_device(const platform_i2c_t* i2c, const platform_i2c_config_t* config, int retries )
{
OSStatus err = kNoErr;
platform_i2c_message_t msg;
memset(&msg, 0, sizeof(msg));
msg.retries = retries;
i2c->pCtx->isProbe = 1;
err = platform_i2c_transfer(i2c, config, &msg, 1);
return err;
}
OSStatus platform_i2c_deinit(const platform_i2c_t* i2c, const platform_i2c_config_t* config )
{
UNUSED_PARAMETER( config );
OSStatus err = kNoErr;
#ifndef BOOTLOADER
platform_i2c_cb_t *pCtx = i2c->pCtx;
#endif
require_action_quiet( i2c != NULL, exit, err = kParamErr);
// Wecan Todo 2015.06.04
Chip_I2C_DeInit( i2c->port ); //Disable I2C peripheral clockss
#ifndef BOOTLOADER
if (NULL != pCtx->semXfer)
mico_rtos_deinit_semaphore(&pCtx->semXfer);
if (NULL != pCtx->mtxXfer)
mico_rtos_deinit_mutex(&pCtx->mtxXfer);
pCtx->semXfer = NULL;
pCtx->mtxXfer = NULL;
#endif
exit:
return err;
}
void OnNonBlockI2CMDone(LPC_I2C_T *pI2C)
{
Chip_I2C_DisableInt(pI2C, I2C_INTENSET_MSTPENDING | I2C_INTENSET_MSTRARBLOSS | I2C_INTENSET_MSTSTSTPERR);
}
void I2CMIrqHandler(platform_i2c_t *pDev)
{
LPC_I2C_T *pI2C = pDev->port;
uint32_t state = Chip_I2C_GetPendingInt(pI2C);
/* Error handling */
if (state & (I2C_INTENSET_MSTRARBLOSS | I2C_INTENSET_MSTSTSTPERR)) {
Chip_I2CM_ClearStatus(pI2C, I2C_STAT_MSTRARBLOSS | I2C_STAT_MSTSTSTPERR);
}
/* Call I2CM ISR function with the I2C device and transfer rec */
if (state & I2C_INTENSET_MSTPENDING) {
_prvI2CMXferFSM(pI2C, pDev->pCtx);
if (pDev->pCtx->status != I2CM_STATUS_BUSY)
OnNonBlockI2CMDone(pI2C);
}
}