mirror of
https://github.com/oopuuu/zTC1.git
synced 2025-12-17 15:38:14 +08:00
740 lines
25 KiB
C
740 lines
25 KiB
C
/**
|
|
******************************************************************************
|
|
* @file paltform_i2c.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 "platform.h"
|
|
#include "platform_peripheral.h"
|
|
#include "stm32f2xx.h"
|
|
#include "platform_logging.h"
|
|
|
|
/******************************************************
|
|
* Constants
|
|
******************************************************/
|
|
|
|
#define I2C_FLAG_CHECK_TIMEOUT ( 1000 )
|
|
#define I2C_FLAG_CHECK_LONG_TIMEOUT ( 1000 )
|
|
|
|
//#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
|
|
******************************************************/
|
|
|
|
/******************************************************
|
|
* Function Declarations
|
|
******************************************************/
|
|
|
|
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( I2C_TypeDef* i2c, uint32_t event_id, uint32_t number_of_waits );
|
|
static OSStatus i2c_transfer_message_no_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message );
|
|
#ifdef I2C_USE_DMA
|
|
static OSStatus i2c_rx_with_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message );
|
|
static OSStatus i2c_tx_with_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message );
|
|
#else
|
|
static OSStatus i2c_tx_no_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message );
|
|
static OSStatus i2c_rx_no_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message );
|
|
#endif
|
|
|
|
/******************************************************
|
|
* Function Definitions
|
|
******************************************************/
|
|
|
|
OSStatus platform_i2c_init( const platform_i2c_t* i2c, const platform_i2c_config_t* config )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
I2C_InitTypeDef I2C_InitStructure;
|
|
|
|
platform_mcu_powersave_disable( );
|
|
require_action_quiet( i2c != NULL, exit, err = kParamErr);
|
|
|
|
// Init I2C GPIO clocks
|
|
RCC_APB1PeriphClockCmd( i2c->peripheral_clock_reg, ENABLE );
|
|
err = platform_gpio_enable_clock( i2c->pin_scl );
|
|
require_noerr(err, exit);
|
|
err = platform_gpio_enable_clock( i2c->pin_sda );
|
|
require_noerr(err, exit);
|
|
RCC_APB2PeriphClockCmd( RCC_APB2Periph_SYSCFG, ENABLE );
|
|
|
|
// Reset the I2C clock
|
|
RCC_APB1PeriphResetCmd( i2c->peripheral_clock_reg, ENABLE );
|
|
RCC_APB1PeriphResetCmd( i2c->peripheral_clock_reg, DISABLE );
|
|
|
|
// GPIO Configuration
|
|
platform_gpio_set_alternate_function( i2c->pin_scl->port, i2c->pin_scl->pin_number, GPIO_OType_OD, GPIO_PuPd_NOPULL, i2c->gpio_af );
|
|
platform_gpio_set_alternate_function( i2c->pin_sda->port, i2c->pin_sda->pin_number, GPIO_OType_OD, GPIO_PuPd_NOPULL, i2c->gpio_af );
|
|
|
|
#ifdef I2C_USE_DMA
|
|
// Enable the DMA clock
|
|
RCC_AHB1PeriphClockCmd( i2c_mapping[device->port].tx_dma_peripheral_clock, ENABLE );
|
|
|
|
// Configure the DMA streams for operation with the CP
|
|
i2c_dma_init.DMA_Channel = i2c_mapping[device->port].tx_dma_channel;
|
|
i2c_dma_init.DMA_PeripheralBaseAddr = (uint32_t)&i2c_mapping[device->port].i2c->DR;
|
|
i2c_dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
|
i2c_dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
|
i2c_dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
|
i2c_dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
|
i2c_dma_init.DMA_Mode = DMA_Mode_Normal;
|
|
i2c_dma_init.DMA_Priority = DMA_Priority_VeryHigh;
|
|
//dma_init.DMA_FIFOMode = DMA_FIFOMode_Enable;
|
|
//dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
|
|
i2c_dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single;
|
|
i2c_dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
|
|
i2c_dma_init.DMA_Memory0BaseAddr = (uint32_t) 0; // This parameter will be configured during communication
|
|
i2c_dma_init.DMA_DIR = DMA_DIR_MemoryToPeripheral; // This parameter will be configured during communication
|
|
i2c_dma_init.DMA_BufferSize = 0xFFFF; // This parameter will be configured during communication
|
|
|
|
DMA_DeInit( i2c_mapping[device->port].rx_dma_stream );
|
|
DMA_DeInit( i2c_mapping[device->port].tx_dma_stream );
|
|
|
|
// Clear any pending flags, disable, and clear the Tx DMA channel
|
|
//DMA_ClearFlag( i2c_mapping[device->port].tx_dma_stream, CP_TX_DMA_FLAG_FEIF | CP_TX_DMA_FLAG_DMEIF | CP_TX_DMA_FLAG_TEIF | CP_TX_DMA_FLAG_HTIF | CP_TX_DMA_FLAG_TCIF );
|
|
DMA_Cmd( i2c_mapping[device->port].tx_dma_stream, DISABLE );
|
|
DMA_Cmd( i2c_mapping[device->port].rx_dma_stream, DISABLE );
|
|
|
|
// Clear any pending flags, disable, and clear the Rx DMA channel
|
|
//DMA_ClearFlag( i2c_mapping[device->port].rx_dma_stream, CP_RX_DMA_FLAG_FEIF | CP_RX_DMA_FLAG_DMEIF | CP_RX_DMA_FLAG_TEIF | CP_RX_DMA_FLAG_HTIF | CP_RX_DMA_FLAG_TCIF );
|
|
#endif
|
|
|
|
// Initialize the InitStruct for the CP
|
|
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
|
|
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
|
|
I2C_InitStructure.I2C_OwnAddress1 = 0xA0;
|
|
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
|
|
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
|
|
|
|
if ( config->speed_mode == I2C_LOW_SPEED_MODE )
|
|
{
|
|
I2C_InitStructure.I2C_ClockSpeed = 10000;
|
|
}
|
|
else if ( config->speed_mode == I2C_STANDARD_SPEED_MODE )
|
|
{
|
|
I2C_InitStructure.I2C_ClockSpeed = 100000;
|
|
}
|
|
else if ( config->speed_mode == I2C_HIGH_SPEED_MODE )
|
|
{
|
|
I2C_InitStructure.I2C_ClockSpeed = 400000;
|
|
}
|
|
|
|
// Enable and initialize the I2C bus
|
|
I2C_Cmd( i2c->port, ENABLE );
|
|
I2C_Init( i2c->port, &I2C_InitStructure );
|
|
|
|
#ifdef I2C_USE_DMA
|
|
// Enable DMA on the I2C bus
|
|
I2C_DMACmd( i2c->port, ENABLE );
|
|
#endif
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable( );
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
#ifdef I2C_USE_DMA
|
|
static uint32_t dma_flag_tc( int stream_id )
|
|
{
|
|
const uint32_t transfer_complete_flags[]=
|
|
{
|
|
/* for every stream get a transfer complete flag */
|
|
[0] = DMA_FLAG_TCIF0,
|
|
[1] = DMA_FLAG_TCIF1,
|
|
[2] = DMA_FLAG_TCIF2,
|
|
[3] = DMA_FLAG_TCIF3,
|
|
[4] = DMA_FLAG_TCIF4,
|
|
[5] = DMA_FLAG_TCIF5,
|
|
[6] = DMA_FLAG_TCIF6,
|
|
[7] = DMA_FLAG_TCIF7,
|
|
};
|
|
|
|
return transfer_complete_flags[stream_id];
|
|
}
|
|
#endif
|
|
|
|
bool platform_i2c_probe_device( const platform_i2c_t* i2c, const platform_i2c_config_t* config, int retries )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( i2c != NULL, exit, err = kParamErr);
|
|
|
|
err = i2c_address_device( i2c, config, retries, I2C_Direction_Transmitter );
|
|
|
|
I2C_GenerateSTOP( i2c->port, ENABLE );
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return ( err == kNoErr) ? true : false;
|
|
}
|
|
|
|
#ifdef I2C_USE_DMA
|
|
static OSStatus i2c_dma_config_and_execute( mico_i2c_device_t* device, mico_i2c_message_t* message, bool tx_dma )
|
|
{
|
|
uint32_t counter;
|
|
|
|
/* Initialize the DMA with the new parameters */
|
|
if ( tx_dma == true )
|
|
{
|
|
/* Enable DMA channel for I2C */
|
|
I2C_DMACmd( i2c_mapping[device->port].i2c, ENABLE );
|
|
|
|
/* TX DMA configuration */
|
|
DMA_DeInit( i2c_mapping[device->port].tx_dma_stream );
|
|
|
|
/* Configure the DMA TX Stream with the buffer address and the buffer size */
|
|
i2c_dma_init.DMA_Memory0BaseAddr = (uint32_t)message->tx_buffer;
|
|
i2c_dma_init.DMA_DIR = DMA_DIR_MemoryToPeripheral;
|
|
i2c_dma_init.DMA_BufferSize = (uint32_t)message->tx_length;
|
|
DMA_Init(i2c_mapping[device->port].tx_dma_stream, &i2c_dma_init);
|
|
|
|
/* Enable DMA channel */
|
|
DMA_Cmd( i2c_mapping[device->port].tx_dma_stream, ENABLE );
|
|
|
|
/* wait until transfer is completed */
|
|
/* TODO: change flag!!!!,wait on a semaphore */
|
|
counter = DMA_TIMEOUT_LOOPS;
|
|
while ( DMA_GetFlagStatus( i2c_mapping[device->port].tx_dma_stream, DMA_FLAG_TC(i2c_mapping[device->port].tx_dma_stream_id) ) == RESET )
|
|
{
|
|
--counter;
|
|
if ( counter == 0 )
|
|
{
|
|
return kGeneralErr;
|
|
}
|
|
}
|
|
|
|
/* Disable DMA and channel */
|
|
I2C_DMACmd( i2c_mapping[device->port].i2c, DISABLE );
|
|
DMA_Cmd( i2c_mapping[device->port].tx_dma_stream, DISABLE );
|
|
}
|
|
else
|
|
{
|
|
/* Enable dma channel for I2C */
|
|
I2C_DMACmd( i2c_mapping[device->port].i2c, ENABLE );
|
|
|
|
/* RX DMA configuration */
|
|
DMA_DeInit( i2c_mapping[device->port].rx_dma_stream );
|
|
|
|
/* Configure the DMA Rx Stream with the buffer address and the buffer size */
|
|
i2c_dma_init.DMA_Memory0BaseAddr = (uint32_t)message->rx_buffer;
|
|
i2c_dma_init.DMA_DIR = DMA_DIR_PeripheralToMemory;
|
|
i2c_dma_init.DMA_BufferSize = (uint32_t)message->rx_length;
|
|
DMA_Init(i2c_mapping[device->port].rx_dma_stream, &i2c_dma_init);
|
|
|
|
/* Enable DMA channel */
|
|
DMA_Cmd( i2c_mapping[device->port].rx_dma_stream, ENABLE );
|
|
|
|
/* wait until transfer is completed */
|
|
counter = DMA_TIMEOUT_LOOPS;
|
|
while ( DMA_GetFlagStatus( i2c_mapping[device->port].rx_dma_stream, DMA_FLAG_TC(i2c_mapping[device->port].rx_dma_stream_id) ) == RESET )
|
|
{
|
|
--counter;
|
|
if ( counter == 0 )
|
|
{
|
|
return kGeneralErr;
|
|
}
|
|
}
|
|
|
|
/* disable DMA and channel */
|
|
I2C_DMACmd( i2c_mapping[device->port].i2c, DISABLE );
|
|
DMA_Cmd( i2c_mapping[device->port].rx_dma_stream, DISABLE );
|
|
}
|
|
|
|
return kNoErr;
|
|
}
|
|
|
|
static OSStatus i2c_dma_transfer( mico_i2c_device_t* device, mico_i2c_message_t* message )
|
|
{
|
|
OSStatus result;
|
|
uint32_t counter;
|
|
int i = 0;
|
|
|
|
if ( message->combined == true )
|
|
{
|
|
/* combined transaction case, please refer to Philips I2C document to have an understanding of a combined fragment */
|
|
|
|
/* some chips( authentication and security related chips ) has to be addressed several times before they acknowledge their address */
|
|
for ( i = 0; i < message->retries; i++ )
|
|
{
|
|
/* generate a start condition and address a device in write mode */
|
|
I2C_GenerateSTART( i2c_mapping[device->port].i2c, ENABLE );
|
|
|
|
/* wait till start condition is generated and the bus becomes free */
|
|
result = i2c_wait_for_event( i2c_mapping[device->port].i2c, I2C_EVENT_MASTER_MODE_SELECT, I2C_FLAG_CHECK_TIMEOUT );
|
|
if ( result != kNoErr )
|
|
{
|
|
return kTimeoutErr;
|
|
}
|
|
|
|
if ( device->address_width == I2C_ADDRESS_WIDTH_7BIT )
|
|
{
|
|
/* send the address and R/W bit set to write of the requested device, wait for an acknowledge */
|
|
I2C_Send7bitAddress( i2c_mapping[device->port].i2c, (uint8_t) ( device->address << 1 ), I2C_Direction_Transmitter );
|
|
|
|
/* wait till address gets sent and the direction bit is sent and */
|
|
result = i2c_wait_for_event( i2c_mapping[device->port].i2c, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, I2C_FLAG_CHECK_LONG_TIMEOUT );
|
|
if ( result == kNoErr )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//TODO
|
|
/* send 10 bits of the address and wait for an acknowledge */
|
|
}
|
|
}
|
|
|
|
if ( i == message->retries )
|
|
{
|
|
return kTimeoutErr;
|
|
}
|
|
|
|
/* configure dma tx channel for i2c */
|
|
i2c_dma_config_and_execute( device, message, true );
|
|
|
|
/* wait till the byte is actually sent from the i2c peripheral */
|
|
counter = 1000;
|
|
while ( I2C_GetFlagStatus( i2c_mapping[device->port].i2c, I2C_FLAG_BTF ) == RESET )
|
|
{
|
|
--counter;
|
|
if ( counter == 0 )
|
|
{
|
|
return kGeneralErr;
|
|
}
|
|
}
|
|
|
|
/* generate start condition again and address a device in read mode */
|
|
/* some chips( authentication and security related chips ) has to be addressed several times before they acknowledge their address */
|
|
for ( i = 0; i < message->retries; i++ )
|
|
{
|
|
/* generate a start condition */
|
|
I2C_GenerateSTART( i2c_mapping[device->port].i2c, ENABLE );
|
|
|
|
/* wait till start condition is generated and the bus becomes free */
|
|
result = i2c_wait_for_event( i2c_mapping[device->port].i2c, I2C_EVENT_MASTER_MODE_SELECT, I2C_FLAG_CHECK_TIMEOUT );
|
|
if ( result != kNoErr )
|
|
{
|
|
return kTimeoutErr;
|
|
}
|
|
|
|
if ( device->address_width == I2C_ADDRESS_WIDTH_7BIT )
|
|
{
|
|
/* send the address and R/W bit set to write of the requested device, wait for an acknowledge */
|
|
I2C_Send7bitAddress( i2c_mapping[device->port].i2c, (uint8_t) ( device->address << 1 ), I2C_Direction_Receiver );
|
|
|
|
/* wait till address gets sent and the direction bit is sent and */
|
|
result = i2c_wait_for_event( i2c_mapping[device->port].i2c, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, I2C_FLAG_CHECK_LONG_TIMEOUT );
|
|
if ( result == kNoErr )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//TODO
|
|
/* send 10 bits of the address and wait for an acknowledge */
|
|
}
|
|
}
|
|
|
|
if ( i == message->retries )
|
|
{
|
|
return kTimeoutErr;
|
|
}
|
|
|
|
/* receive data from the slave device */
|
|
if ( message->rx_length == 1 )
|
|
{
|
|
/* disable acknowledgement before we start receiving bytes, this is a single byte transmission */
|
|
I2C_AcknowledgeConfig( i2c_mapping[device->port].i2c, DISABLE );
|
|
}
|
|
else
|
|
{
|
|
/* enable acknowledgement before we start receiving bytes, this is a single byte transmission */
|
|
I2C_AcknowledgeConfig( i2c_mapping[device->port].i2c, ENABLE );
|
|
}
|
|
|
|
/* start dma which will read bytes */
|
|
i2c_dma_config_and_execute( device, message, false );
|
|
/* maybe we will have to wait on the BTF flag!!! */
|
|
}
|
|
else
|
|
{
|
|
|
|
/* read or write transaction */
|
|
|
|
/* some chips( authentication and security related chips ) has to be addressed several times before they acknowledge their address */
|
|
for ( i = 0; i < message->retries; i++ )
|
|
{
|
|
/* generate a start condition */
|
|
I2C_GenerateSTART( i2c_mapping[device->port].i2c, ENABLE );
|
|
|
|
/* wait till start condition is generated and the bus becomes free */
|
|
result = i2c_wait_for_event( i2c_mapping[device->port].i2c, I2C_EVENT_MASTER_MODE_SELECT, I2C_FLAG_CHECK_TIMEOUT );
|
|
if ( result != kNoErr )
|
|
{
|
|
return kTimeoutErr;
|
|
}
|
|
|
|
if ( device->address_width == I2C_ADDRESS_WIDTH_7BIT )
|
|
{
|
|
/* send the address of the requested device, wait for an acknowledge */
|
|
I2C_Send7bitAddress( i2c_mapping[device->port].i2c, (uint8_t) ( device->address << 1 ), ( ( message->tx_buffer ) ? I2C_Direction_Transmitter : I2C_Direction_Receiver ) );
|
|
|
|
/* wait till address gets sent and the direction bit is sent */
|
|
if ( message->tx_buffer )
|
|
{
|
|
result = i2c_wait_for_event( i2c_mapping[device->port].i2c, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED, I2C_FLAG_CHECK_LONG_TIMEOUT );
|
|
}
|
|
else
|
|
{
|
|
result = i2c_wait_for_event( i2c_mapping[device->port].i2c, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, I2C_FLAG_CHECK_LONG_TIMEOUT );
|
|
}
|
|
|
|
if ( result == kNoErr )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//TODO
|
|
/* send 10 bits of the address and wait for an acknowledge */
|
|
}
|
|
}
|
|
if ( i == message->retries )
|
|
{
|
|
return kTimeoutErr;
|
|
}
|
|
|
|
if ( message->tx_buffer )
|
|
{
|
|
/* write transaction */
|
|
/* configure dma tx channel for i2c */
|
|
i2c_dma_config_and_execute( device, message, true );
|
|
|
|
/* wait till the byte is actually sent from the i2c peripheral */
|
|
counter = 1000;
|
|
while ( I2C_GetFlagStatus( i2c_mapping[device->port].i2c, I2C_FLAG_BTF ) == RESET )
|
|
{
|
|
--counter;
|
|
if ( counter == 0 )
|
|
{
|
|
return kGeneralErr;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* read transaction */
|
|
if ( message->rx_length == 1 )
|
|
{
|
|
/* disable acknowledgement before we are going to receive a single byte */
|
|
I2C_AcknowledgeConfig( i2c_mapping[device->port].i2c, DISABLE );
|
|
}
|
|
else
|
|
{
|
|
/* enable acknowledgement before we start receiving multiple bytes */
|
|
I2C_AcknowledgeConfig( i2c_mapping[device->port].i2c, ENABLE );
|
|
}
|
|
|
|
/* start dma which will read bytes */
|
|
i2c_dma_config_and_execute( device, message, false );
|
|
|
|
/* wait til the last byte is received */
|
|
counter = 1000;
|
|
while ( I2C_GetFlagStatus( i2c_mapping[device->port].i2c, I2C_FLAG_BTF ) == RESET )
|
|
{
|
|
--counter;
|
|
if ( counter == 0 )
|
|
{
|
|
return kGeneralErr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* generate a stop condition */
|
|
I2C_GenerateSTOP( i2c_mapping[device->port].i2c, ENABLE );
|
|
return kNoErr;
|
|
}
|
|
#else
|
|
|
|
static OSStatus i2c_transfer_message_no_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message )
|
|
{
|
|
OSStatus err;
|
|
|
|
if ( message->tx_buffer != NULL )
|
|
{
|
|
err = i2c_tx_no_dma( i2c, config, message );
|
|
if ( err != kNoErr )
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if ( message->rx_buffer != NULL )
|
|
{
|
|
err = i2c_rx_no_dma( i2c, config, message );
|
|
}
|
|
|
|
exit:
|
|
/* generate a stop condition */
|
|
I2C_GenerateSTOP( i2c->port, ENABLE );
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
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(platform_i2c_message_t));
|
|
message->tx_buffer = tx_buffer;
|
|
message->retries = retries;
|
|
message->tx_length = tx_buffer_length;
|
|
|
|
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(platform_i2c_message_t));
|
|
message->rx_buffer = rx_buffer;
|
|
message->retries = retries;
|
|
message->rx_length = rx_buffer_length;
|
|
|
|
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(platform_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;
|
|
|
|
exit:
|
|
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;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( i2c != NULL, exit, err = kParamErr);
|
|
|
|
for( i=0; i < number_of_messages; i++ )
|
|
{
|
|
#ifdef I2C_USE_DMA
|
|
err = i2c_dma_transfer(device, &messages[i]);
|
|
require_noerr(err, exit);
|
|
#else
|
|
err = i2c_transfer_message_no_dma( i2c, config, &messages[ i ] );
|
|
require_noerr(err, exit);
|
|
#endif
|
|
}
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
OSStatus platform_i2c_deinit( const platform_i2c_t* i2c, const platform_i2c_config_t* config )
|
|
{
|
|
UNUSED_PARAMETER( config );
|
|
OSStatus err = kNoErr;
|
|
|
|
platform_mcu_powersave_disable();
|
|
|
|
require_action_quiet( i2c != NULL, exit, err = kParamErr);
|
|
|
|
/* Disable I2C peripheral clocks */
|
|
RCC_APB1PeriphClockCmd( i2c->peripheral_clock_reg, DISABLE );
|
|
|
|
/* Disable DMA */
|
|
#ifdef I2C_USE_DMA
|
|
DMA_DeInit( i2c->rx_dma_stream );
|
|
DMA_DeInit( i2c->tx_dma_stream );
|
|
RCC_AHB1PeriphClockCmd( i2c->tx_dma_peripheral_clock, DISABLE );
|
|
#endif
|
|
|
|
exit:
|
|
platform_mcu_powersave_enable();
|
|
return err;
|
|
}
|
|
|
|
|
|
static OSStatus i2c_wait_for_event( I2C_TypeDef* i2c, uint32_t event_id, uint32_t number_of_waits )
|
|
{
|
|
mico_rtos_delay_milliseconds(2);
|
|
|
|
while ( I2C_CheckEvent( i2c, event_id ) != SUCCESS )
|
|
{
|
|
number_of_waits--;
|
|
if ( number_of_waits == 0 )
|
|
{
|
|
return kTimeoutErr;
|
|
}
|
|
}
|
|
return kNoErr;
|
|
}
|
|
|
|
static OSStatus i2c_address_device( const platform_i2c_t* i2c, const platform_i2c_config_t* config, int retries, uint8_t direction )
|
|
{
|
|
OSStatus err = kTimeoutErr;
|
|
|
|
/* Some chips( authentication and security related chips ) has to be addressed several times before they acknowledge their address */
|
|
for ( ; retries != 0 ; --retries )
|
|
{
|
|
/* Generate a start condition and address a i2c in write mode */
|
|
I2C_GenerateSTART( i2c->port, ENABLE );
|
|
|
|
/* wait till start condition is generated and the bus becomes free */
|
|
err = i2c_wait_for_event( i2c->port, I2C_EVENT_MASTER_MODE_SELECT, I2C_FLAG_CHECK_TIMEOUT );
|
|
if(err != kNoErr)
|
|
continue;
|
|
|
|
if ( config->address_width == I2C_ADDRESS_WIDTH_7BIT )
|
|
{
|
|
/* send the address and R/W bit set to write of the requested i2c, wait for an acknowledge */
|
|
I2C_Send7bitAddress( i2c->port, (uint8_t) ( config->address << 1 ), direction );
|
|
|
|
/* wait till address gets sent and the direction bit is sent and */
|
|
err = i2c_wait_for_event( i2c->port, (direction == I2C_Direction_Transmitter) ? I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED, I2C_FLAG_CHECK_LONG_TIMEOUT );
|
|
if ( err == kNoErr )
|
|
goto exit;
|
|
}
|
|
/* TODO: Support other address widths */
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static OSStatus i2c_tx_no_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
int i;
|
|
|
|
/* Send data */
|
|
err = i2c_address_device( i2c, config, message->retries, I2C_Direction_Transmitter );
|
|
require_noerr(err, exit);
|
|
|
|
for ( i = 0; i < message->tx_length; i++ )
|
|
{
|
|
I2C_SendData( i2c->port, ((uint8_t*)message->tx_buffer)[ i ] );
|
|
|
|
/* wait till it actually gets transferred and acknowledged */
|
|
err = i2c_wait_for_event( i2c->port, I2C_EVENT_MASTER_BYTE_TRANSMITTED, I2C_FLAG_CHECK_TIMEOUT );
|
|
require_noerr(err, exit);
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static OSStatus i2c_rx_no_dma( const platform_i2c_t* i2c, const platform_i2c_config_t* config, platform_i2c_message_t* message )
|
|
{
|
|
OSStatus err = kNoErr;
|
|
int i;
|
|
|
|
err = i2c_address_device( i2c, config, message->retries, I2C_Direction_Receiver );
|
|
require_noerr(err, exit);
|
|
|
|
/* Disable acknowledgement if this is a single byte transmission */
|
|
if ( message->rx_length == 1 )
|
|
{
|
|
I2C_AcknowledgeConfig( i2c->port, DISABLE );
|
|
}
|
|
else
|
|
{
|
|
I2C_AcknowledgeConfig( i2c->port, ENABLE );
|
|
}
|
|
|
|
/* Start reading bytes */
|
|
for ( i = 0; i < message->rx_length; i++ )
|
|
{
|
|
/* wait till something is in the i2c data register */
|
|
err = i2c_wait_for_event( i2c->port, I2C_EVENT_MASTER_BYTE_RECEIVED, I2C_FLAG_CHECK_TIMEOUT );
|
|
require_noerr(err, exit);
|
|
|
|
/* get data */
|
|
((uint8_t*)message->rx_buffer)[ i ] = I2C_ReceiveData( i2c->port );
|
|
|
|
/* Check if last byte has been received */
|
|
if ( i == ( message->rx_length - 1 ) )
|
|
{
|
|
}
|
|
else /* Check if the second last byte has been received */
|
|
if ( i == ( message->rx_length - 2 ) )
|
|
{
|
|
/* setup NACK for the last byte to be received */
|
|
I2C_AcknowledgeConfig( i2c->port, DISABLE );
|
|
}
|
|
else
|
|
{
|
|
/* setup an acknowledgement beforehand for every byte that is to be received */
|
|
I2C_AcknowledgeConfig( i2c->port, ENABLE );
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
|