mirror of
https://github.com/oopuuu/zTC1.git
synced 2025-12-17 15:38:14 +08:00
517 lines
14 KiB
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);
|
|
}
|
|
}
|
|
|