/******************************************************************************* | |
* (c) Copyright 2007-2008 Actel Corporation. All rights reserved. | |
* | |
* SmartFusion microcontroller subsystem I2C bare metal software driver | |
* implementation. | |
* | |
* SVN $Revision: 2152 $ | |
* SVN $Date: 2010-02-11 14:44:11 +0000 (Thu, 11 Feb 2010) $ | |
*/ | |
#include "i2c.h" | |
#include "../../CMSIS/mss_assert.h" | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/*------------------------------------------------------------------------------ | |
* I2C transaction direction. | |
*/ | |
#define WRITE_DIR 0 | |
#define READ_DIR 1 | |
/* -- TRANSACTIONS TYPES -- */ | |
#define NO_TRANSACTION 0 | |
#define MASTER_WRITE_TRANSACTION 1 | |
#define MASTER_READ_TRANSACTION 2 | |
#define MASTER_RANDOM_READ_TRANSACTION 3 | |
#define WRITE_SLAVE_TRANSACTION 4 | |
#define READ_SLAVE_TRANSACTION 5 | |
#define RANDOM_READ_SLAVE_TRANSACTION 6 | |
/* -- SMBUS H/W STATES -- */ | |
/* -- MASTER STATES -- */ | |
#define ST_START 0x08 /* start condition sent */ | |
#define ST_RESTART 0x10 /* repeated start */ | |
#define ST_SLAW_ACK 0x18 /* SLA+W sent, ack received */ | |
#define ST_SLAW_NACK 0x20 /* SLA+W sent, nack received */ | |
#define ST_TX_DATA_ACK 0x28 /* Data sent, ACK'ed */ | |
#define ST_TX_DATA_NACK 0x30 /* Data sent, NACK'ed */ | |
#define ST_LOST_ARB 0x38 /* Master lost arbitration */ | |
#define ST_SLAR_ACK 0x40 /* SLA+R sent, ACK'ed */ | |
#define ST_SLAR_NACK 0x48 /* SLA+R sent, NACK'ed */ | |
#define ST_RX_DATA_ACK 0x50 /* Data received, ACK sent */ | |
#define ST_RX_DATA_NACK 0x58 /* Data received, NACK sent */ | |
/* -- SLAVE STATES -- */ | |
#define ST_SLAVE_SLAW 0x60 /* SLA+W received */ | |
#define ST_SLAVE_SLAR_ACK 0xA8 /* SLA+R received, ACK returned */ | |
#define ST_SLV_LA 0x68 /* Slave lost arbitration */ | |
#define ST_GCA 0x70 /* GCA received */ | |
#define ST_GCA_LA 0x78 /* GCA lost arbitration */ | |
#define ST_RDATA 0x80 /* Data received */ | |
#define ST_SLA_NACK 0x88 /* Slave addressed, NACK returned */ | |
#define ST_GCA_ACK 0x90 /* Previously addresses with GCA, data ACKed */ | |
#define ST_GCA_NACK 0x98 /* GCA addressed, NACK returned */ | |
#define ST_RSTOP 0xA0 /* Stop received */ | |
#define ST_REPEAT 0xA0 /* Repeated start received */ | |
#define ST_SLAR_ACKS 0xA8 /* Slave read received, ACKed */ | |
#define ST_SLARW_LA 0xB0 /* Arbitration lost */ | |
#define ST_RACK 0xB8 /* Byte sent, ACK received */ | |
#define ST_SLAVE_RNACK 0xC0 /* Byte sent, NACK received */ | |
#define ST_FINAL 0xC8 /* Final byte sent, ACK received */ | |
#define ST_BERR 0x00 /* Error on the bus */ | |
#define ST_SLV_RST 0xD8 /* Slave reset state */ | |
/*------------------------------------------------------------------------------ | |
* | |
*/ | |
static uint32_t disable_interrupts( void ); | |
static void restore_interrupts( uint32_t primask ); | |
static void mss_i2c_isr( mss_i2c_instance_t * this_i2c ); | |
/*------------------------------------------------------------------------------ | |
* | |
*------------------------------------------------------------------------------ | |
* | |
*/ | |
mss_i2c_instance_t g_mss_i2c0; | |
mss_i2c_instance_t g_mss_i2c1; | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_init() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_init | |
( | |
mss_i2c_instance_t * this_i2c, | |
uint8_t ser_address, | |
mss_i2c_clock_divider_t ser_clock_speed | |
) | |
{ | |
uint_fast16_t clock_speed = ser_clock_speed; | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
if ( this_i2c == &g_mss_i2c0 ) | |
{ | |
this_i2c->irqn = I2C0_IRQn; | |
this_i2c->hw_reg = I2C0; | |
this_i2c->hw_reg_bit = I2C0_BITBAND; | |
/* reset I2C0 */ | |
SYSREG->SOFT_RST_CR |= SYSREG_I2C0_SOFTRESET_MASK; | |
/* Clear any previously pended I2C0 interrupt */ | |
NVIC_ClearPendingIRQ( I2C0_IRQn ); | |
/* Take I2C0 out of reset. */ | |
SYSREG->SOFT_RST_CR &= ~SYSREG_I2C0_SOFTRESET_MASK; | |
} | |
else | |
{ | |
this_i2c->irqn = I2C1_IRQn; | |
this_i2c->hw_reg = I2C1; | |
this_i2c->hw_reg_bit = I2C1_BITBAND; | |
/* reset I2C1 */ | |
SYSREG->SOFT_RST_CR |= SYSREG_I2C1_SOFTRESET_MASK; | |
/* Clear any previously pended I2C1 interrupt */ | |
NVIC_ClearPendingIRQ( I2C1_IRQn ); | |
/* Take I2C1 out of reset. */ | |
SYSREG->SOFT_RST_CR &= ~SYSREG_I2C1_SOFTRESET_MASK; | |
} | |
this_i2c->transaction = NO_TRANSACTION; | |
this_i2c->ser_address = ser_address; | |
this_i2c->tx_buffer = 0; | |
this_i2c->tx_size = 0; | |
this_i2c->tx_idx = 0; | |
this_i2c->rx_buffer = 0; | |
this_i2c->rx_size = 0; | |
this_i2c->rx_idx = 0; | |
this_i2c->status = MSS_I2C_SUCCESS; | |
this_i2c->random_read_addr = 0; | |
this_i2c->slave_write_handler = 0; | |
this_i2c->slave_mem_offset_length = 0; | |
this_i2c->hw_reg_bit->CTRL_ENS1 = 0x01; /* set enable bit */ | |
this_i2c->hw_reg_bit->CTRL_CR2 = (clock_speed >> 2) & 0x01; | |
this_i2c->hw_reg_bit->CTRL_CR1 = (clock_speed >> 1) & 0x01; | |
this_i2c->hw_reg_bit->CTRL_CR0 = clock_speed & 0x01; | |
this_i2c->hw_reg->ADDR = this_i2c->ser_address; | |
/* The interrupt can cause a context switch, so ensure its priority is | |
between configKERNEL_INTERRUPT_PRIORITY and configMAX_SYSCALL_INTERRUPT_PRIORITY. */ | |
NVIC_SetPriority( this_i2c->irqn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); | |
vSemaphoreCreateBinary( ( this_i2c->xI2CCompleteSemaphore ) ); | |
xSemaphoreTake( ( this_i2c->xI2CCompleteSemaphore ), 0 ); | |
configASSERT( ( this_i2c->xI2CCompleteSemaphore ) ); | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_set_slave_mem_offset_length() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_set_slave_mem_offset_length | |
( | |
mss_i2c_instance_t * this_i2c, | |
uint8_t offset_length | |
) | |
{ | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
this_i2c->slave_mem_offset_length = offset_length; | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_register_write_handler() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_register_write_handler | |
( | |
mss_i2c_instance_t * this_i2c, | |
mss_i2c_slave_wr_handler_t handler | |
) | |
{ | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
this_i2c->slave_write_handler = handler; | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_write() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_write | |
( | |
mss_i2c_instance_t * this_i2c, | |
uint8_t serial_addr, | |
const uint8_t * write_buffer, | |
uint16_t write_size, | |
uint8_t options | |
) | |
{ | |
volatile uint8_t stat_ctrl; | |
uint8_t serial_interrupt; | |
uint32_t primask; | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
configASSERT( ( this_i2c->xI2CCompleteSemaphore ) ); | |
primask = disable_interrupts(); | |
this_i2c->transaction = MASTER_WRITE_TRANSACTION; | |
this_i2c->target_addr = serial_addr; | |
this_i2c->dir = WRITE_DIR; | |
this_i2c->tx_buffer = write_buffer; | |
this_i2c->tx_size = write_size; | |
this_i2c->tx_idx = 0; | |
this_i2c->status = MSS_I2C_IN_PROGRESS; | |
this_i2c->options = options; | |
/* Clear interrupts if required (depends on repeated starts).*/ | |
serial_interrupt = this_i2c->hw_reg_bit->CTRL_SI; | |
this_i2c->hw_reg_bit->CTRL_STA = 0x01; | |
if ( serial_interrupt != 0 ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_SI = 0x00; | |
NVIC_ClearPendingIRQ( this_i2c->irqn ); | |
} | |
stat_ctrl = this_i2c->hw_reg->STATUS; | |
NVIC_EnableIRQ( this_i2c->irqn ); | |
restore_interrupts( primask ); | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_read() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_read | |
( | |
mss_i2c_instance_t * this_i2c, | |
uint8_t serial_addr, | |
uint8_t * read_buffer, | |
uint16_t read_size, | |
uint8_t options | |
) | |
{ | |
uint32_t primask; | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
if ( read_size > 0 ) | |
{ | |
volatile uint8_t stat_ctrl; | |
uint8_t serial_interrupt; | |
primask = disable_interrupts(); | |
this_i2c->transaction = MASTER_READ_TRANSACTION; | |
this_i2c->target_addr = serial_addr; | |
this_i2c->dir = READ_DIR; | |
this_i2c->rx_buffer = read_buffer; | |
this_i2c->rx_size = read_size; | |
this_i2c->rx_idx = 0; | |
this_i2c->status = MSS_I2C_IN_PROGRESS; | |
this_i2c->options = options; | |
/* Clear interrupts if required (depends on repeated starts).*/ | |
serial_interrupt = this_i2c->hw_reg_bit->CTRL_SI; | |
this_i2c->hw_reg_bit->CTRL_STA = 0x01; | |
if ( serial_interrupt != 0 ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_SI = 0x00; | |
NVIC_ClearPendingIRQ( this_i2c->irqn ); | |
} | |
stat_ctrl = this_i2c->hw_reg->STATUS; | |
NVIC_EnableIRQ( this_i2c->irqn ); | |
restore_interrupts( primask ); | |
} | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_write_read() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_write_read | |
( | |
mss_i2c_instance_t * this_i2c, | |
uint8_t serial_addr, | |
const uint8_t * addr_offset, | |
uint16_t offset_size, | |
uint8_t * read_buffer, | |
uint16_t read_size, | |
uint8_t options | |
) | |
{ | |
volatile uint8_t stat_ctrl; | |
uint8_t serial_interrupt; | |
uint32_t primask; | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
primask = disable_interrupts(); | |
this_i2c->transaction = MASTER_RANDOM_READ_TRANSACTION; | |
this_i2c->target_addr = serial_addr; | |
this_i2c->dir = WRITE_DIR; | |
this_i2c->tx_buffer = addr_offset; | |
this_i2c->tx_size = offset_size; | |
this_i2c->tx_idx = 0; | |
this_i2c->rx_buffer = read_buffer; | |
this_i2c->rx_size = read_size; | |
this_i2c->rx_idx = 0; | |
this_i2c->status = MSS_I2C_IN_PROGRESS; | |
this_i2c->options = options; | |
/* Clear interrupts if required (depends on repeated starts).*/ | |
serial_interrupt = this_i2c->hw_reg_bit->CTRL_SI; | |
this_i2c->hw_reg_bit->CTRL_STA = 0x01; | |
if ( serial_interrupt != 0 ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_SI = 0x00; | |
NVIC_ClearPendingIRQ( this_i2c->irqn ); | |
} | |
stat_ctrl = this_i2c->hw_reg->STATUS; | |
NVIC_EnableIRQ( this_i2c->irqn ); | |
restore_interrupts( primask ); | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_set_slave_rx_buffer() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_set_slave_rx_buffer | |
( | |
mss_i2c_instance_t * this_i2c, | |
uint8_t * rx_buffer, | |
uint16_t rx_size | |
) | |
{ | |
uint32_t primask; | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
primask = disable_interrupts(); | |
this_i2c->rx_buffer = rx_buffer; | |
this_i2c->rx_size = rx_size; | |
this_i2c->rx_idx = 0; | |
restore_interrupts( primask ); | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_get_status() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
mss_i2c_status_t MSS_I2C_get_status | |
( | |
mss_i2c_instance_t * this_i2c | |
) | |
{ | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
return this_i2c->status; | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_set_slave_tx_buffer() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_set_slave_tx_buffer | |
( | |
mss_i2c_instance_t * this_i2c, | |
uint8_t * tx_buffer, | |
uint16_t tx_size | |
) | |
{ | |
uint32_t primask; | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
primask = disable_interrupts(); | |
this_i2c->tx_buffer = tx_buffer; | |
this_i2c->tx_size = tx_size; | |
this_i2c->tx_idx = 0; | |
restore_interrupts( primask ); | |
/* Set the assert acknowledge bit. */ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x01; | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_enable_slave_rx() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
void MSS_I2C_enable_slave_rx | |
( | |
mss_i2c_instance_t * this_i2c | |
) | |
{ | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
/* Set the assert acknowledge bit. */ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x01; | |
/* accept GC addressing. */ | |
this_i2c->hw_reg_bit->ADDR_GC = 0x01; | |
NVIC_EnableIRQ( this_i2c->irqn ); | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS_I2C_wait_complete() | |
* See "mss_i2c.h" for details of how to use this function. | |
*/ | |
mss_i2c_status_t MSS_I2C_wait_complete | |
( | |
mss_i2c_instance_t * this_i2c | |
) | |
{ | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
#ifdef USE_OLD_I2C_POLLING_CODE | |
while ( this_i2c->status == MSS_I2C_IN_PROGRESS ) | |
{ | |
/* Wait for transaction to compltete.*/ | |
; | |
} | |
#else | |
configASSERT( ( this_i2c->xI2CCompleteSemaphore ) ); | |
if( xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED ) | |
{ | |
while ( this_i2c->status == MSS_I2C_IN_PROGRESS ) | |
{ | |
/* Wait for transaction to compltete.*/ | |
; | |
} | |
} | |
else | |
{ | |
xSemaphoreTake( this_i2c->xI2CCompleteSemaphore, portMAX_DELAY ); | |
} | |
#endif | |
return this_i2c->status; | |
} | |
/*------------------------------------------------------------------------------ | |
* MSS I2C interrupt service routine. | |
*------------------------------------------------------------------------------ | |
* Parameters: | |
* | |
* mss_i2c_instance_t * this_i2c: | |
* Pointer to the mss_i2c_instance_t data structure holding all data related to | |
* the MSS I2C instance that generated the interrupt. | |
*/ | |
static void mss_i2c_isr | |
( | |
mss_i2c_instance_t * this_i2c | |
) | |
{ | |
volatile uint8_t status; | |
uint8_t data; | |
uint8_t hold_bus; | |
uint8_t clear_irq = 1; | |
long lHigherPriorityTaskWoken = pdFALSE; | |
configASSERT( ( this_i2c->xI2CCompleteSemaphore ) ); | |
ASSERT( (this_i2c == &g_mss_i2c0) || (this_i2c == &g_mss_i2c1) ); | |
status = this_i2c->hw_reg->STATUS; | |
switch( status ) | |
{ | |
/************** MASTER TRANSMITTER / RECEIVER *******************/ | |
case ST_START: /* start has been xmt'd */ | |
case ST_RESTART: /* repeated start has been xmt'd */ | |
this_i2c->hw_reg_bit->CTRL_STA = 0x0; | |
this_i2c->hw_reg->DATA = this_i2c->target_addr; | |
this_i2c->hw_reg_bit->DATA_DIR = this_i2c->dir; | |
this_i2c->tx_idx = 0; | |
this_i2c->rx_idx = 0; | |
break; | |
case ST_LOST_ARB: | |
/* Set start bit. Let's keep trying! Don't give up! */ | |
this_i2c->hw_reg_bit->CTRL_STA = 0x01; | |
break; | |
/******************* MASTER TRANSMITTER *************************/ | |
case ST_SLAW_ACK: | |
/* call address has been xmt'd with ACK, time to send data byte and increment index. */ | |
if ( this_i2c->tx_idx < this_i2c->tx_size ) | |
{ | |
/* load data byte */ | |
this_i2c->hw_reg->DATA = this_i2c->tx_buffer[this_i2c->tx_idx++]; | |
} | |
else | |
{ | |
NVIC_DisableIRQ( this_i2c->irqn ); | |
} | |
break; | |
case ST_SLAW_NACK: | |
#if 0 | |
/* SLA+W has been transmitted; not ACK has been received - let's stop. */ | |
this_i2c->hw_reg_bit->CTRL_STO = 0x01; | |
this_i2c->status = MSS_I2C_FAILED; | |
#endif | |
/* call address has been xmt'd with ACK, time to send data byte and increment index. */ | |
if ( this_i2c->tx_idx < this_i2c->tx_size ) | |
{ | |
/* load data byte */ | |
this_i2c->hw_reg->DATA = this_i2c->tx_buffer[this_i2c->tx_idx++]; | |
} | |
else | |
{ | |
NVIC_DisableIRQ( this_i2c->irqn ); | |
} | |
break; | |
case ST_TX_DATA_ACK: | |
/* data byte has been xmt'd with ACK, time to send stop bit or repeated start. */ | |
if (this_i2c->tx_idx < this_i2c->tx_size) | |
{ | |
this_i2c->hw_reg->DATA = this_i2c->tx_buffer[this_i2c->tx_idx++]; | |
} | |
else if ( this_i2c->transaction == MASTER_RANDOM_READ_TRANSACTION ) | |
{ | |
/* We are finished sending the address offset part of a random read transaction. | |
* It is is time to send a restart in order to change direction. */ | |
this_i2c->dir = READ_DIR; | |
this_i2c->hw_reg_bit->CTRL_STA = 0x01; | |
} | |
else /* done sending. let's stop */ | |
{ | |
hold_bus = this_i2c->options & MSS_I2C_HOLD_BUS; | |
if ( hold_bus == 0 ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_STO = 0x01; /*xmt stop condition */ | |
} | |
else | |
{ | |
NVIC_DisableIRQ( this_i2c->irqn ); | |
clear_irq = 0; | |
} | |
this_i2c->status = MSS_I2C_SUCCESS; | |
xSemaphoreGiveFromISR( this_i2c->xI2CCompleteSemaphore, &lHigherPriorityTaskWoken ); | |
} | |
break; | |
case ST_TX_DATA_NACK: | |
#if 0 | |
/* data byte SENT, ACK to be received | |
* In fact, this means we've received a NACK (This may not be | |
* obvious, but if we've rec'd an ACK then we would be in state | |
* 0x28!) hence, let's send a stop bit | |
*/ | |
this_i2c->hw_reg_bit->CTRL_STO = 0x01; | |
this_i2c->status = MSS_I2C_FAILED; | |
#endif | |
/* data byte has been xmt'd with ACK, time to send stop bit or repeated start. */ | |
if (this_i2c->tx_idx < this_i2c->tx_size) | |
{ | |
this_i2c->hw_reg->DATA = this_i2c->tx_buffer[this_i2c->tx_idx++]; | |
} | |
else if ( this_i2c->transaction == MASTER_RANDOM_READ_TRANSACTION ) | |
{ | |
/* We are finished sending the address offset part of a random read transaction. | |
* It is is time to send a restart in order to change direction. */ | |
this_i2c->dir = READ_DIR; | |
this_i2c->hw_reg_bit->CTRL_STA = 0x01; | |
} | |
else /* done sending. let's stop */ | |
{ | |
hold_bus = this_i2c->options & MSS_I2C_HOLD_BUS; | |
if ( hold_bus == 0 ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_STO = 0x01; /*xmt stop condition */ | |
} | |
else | |
{ | |
NVIC_DisableIRQ( this_i2c->irqn ); | |
clear_irq = 0; | |
} | |
this_i2c->status = MSS_I2C_SUCCESS; | |
xSemaphoreGiveFromISR( this_i2c->xI2CCompleteSemaphore, &lHigherPriorityTaskWoken ); | |
} | |
break; | |
/********************* MASTER (or slave?) RECEIVER *************************/ | |
/* STATUS codes 08H, 10H, 38H are all covered in MTX mode */ | |
case ST_SLAR_ACK: /* SLA+R tx'ed. */ | |
/* Let's make sure we ACK the first data byte received (set AA bit in CTRL) unless | |
* the next byte is the last byte of the read transaction. | |
*/ | |
if( this_i2c->rx_size > 1 ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x01; | |
} | |
else | |
{ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x00; | |
} | |
break; | |
case ST_SLAR_NACK: /* SLA+R tx'ed; let's release the bus (send a stop condition) */ | |
this_i2c->hw_reg_bit->CTRL_STO = 0x01; | |
this_i2c->status = MSS_I2C_FAILED; | |
xSemaphoreGiveFromISR( this_i2c->xI2CCompleteSemaphore, &lHigherPriorityTaskWoken ); | |
break; | |
case ST_RX_DATA_ACK: /* Data byte received, ACK returned */ | |
/* First, get the data */ | |
this_i2c->rx_buffer[this_i2c->rx_idx++] = this_i2c->hw_reg->DATA; | |
if( this_i2c->rx_idx >= this_i2c->rx_size - 1) | |
{ | |
/* If we're at the second last byte, let's set AA to 0 so | |
* we return a NACK at the last byte. */ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x00; | |
} | |
break; | |
case ST_RX_DATA_NACK: /* Data byte received, NACK returned */ | |
/* Get the data, then send a stop condition */ | |
this_i2c->rx_buffer[this_i2c->rx_idx++] = this_i2c->hw_reg->DATA; | |
hold_bus = this_i2c->options & MSS_I2C_HOLD_BUS; | |
if ( hold_bus == 0 ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_STO = 0x01; /*xmt stop condition */ | |
} | |
else | |
{ | |
NVIC_DisableIRQ( this_i2c->irqn ); | |
clear_irq = 0; | |
} | |
this_i2c->status = MSS_I2C_SUCCESS; | |
// xSemaphoreGiveFromISR( this_i2c->xI2CCompleteSemaphore, &lHigherPriorityTaskWoken ); | |
break; | |
/******************** SLAVE RECEIVER **************************/ | |
case ST_GCA_NACK: /* NACK after, GCA addressing */ | |
case ST_SLA_NACK: /* Get Data, but also re-enable AA (assert ack) bit for future transmissions */ | |
if ( this_i2c->rx_buffer != 0 ) | |
{ | |
this_i2c->rx_buffer[this_i2c->rx_idx] = this_i2c->hw_reg->DATA; | |
} | |
this_i2c->hw_reg_bit->CTRL_AA = 0x01; | |
break; | |
case ST_SLAVE_SLAW: /* SLA+W received, ACK returned */ | |
this_i2c->transaction = WRITE_SLAVE_TRANSACTION; | |
this_i2c->rx_idx = 0; | |
this_i2c->random_read_addr = 0; | |
#ifndef INCLUDE_SLA_IN_RX_PAYLOAD | |
/* Only break from this case if the slave address must NOT be included at the | |
* beginning of the received write data. */ | |
break; | |
#endif | |
case ST_GCA_ACK: /* DATA received; ACK sent after GCA */ | |
case ST_RDATA: /* DATA received; must clear DATA register */ | |
if (this_i2c->rx_idx >= this_i2c->rx_size - 2) | |
{ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x00; /* send a NACK when done (next reception) */ | |
} | |
data = this_i2c->hw_reg->DATA; | |
this_i2c->rx_buffer[this_i2c->rx_idx++] = data; | |
this_i2c->random_read_addr = (this_i2c->random_read_addr << 8) + data; | |
break; | |
case ST_RSTOP: | |
/* STOP or repeated START occured. */ | |
/* We cannot be sure if the transaction has actually completed as | |
* this hardware state reports that either a STOP or repeated START | |
* condition has occured. We assume that this is a repeated START | |
* if the transaction was a write from the master to this point.*/ | |
if ( this_i2c->transaction == WRITE_SLAVE_TRANSACTION ) | |
{ | |
if ( this_i2c->rx_idx == this_i2c->slave_mem_offset_length ) | |
{ | |
this_i2c->transaction = RANDOM_READ_SLAVE_TRANSACTION; | |
this_i2c->tx_idx = this_i2c->random_read_addr; | |
} | |
else | |
{ | |
/* Call the slave's write transaction handler if it exists. */ | |
if ( this_i2c->slave_write_handler != 0 ) | |
{ | |
mss_i2c_slave_handler_ret_t h_ret; | |
h_ret = this_i2c->slave_write_handler( this_i2c->rx_buffer, (uint16_t)this_i2c->rx_idx ); | |
if ( MSS_I2C_REENABLE_SLAVE_RX == h_ret ) | |
{ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x01; | |
} | |
else | |
{ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x00; | |
} | |
} | |
} | |
} | |
/* Mark any previous master write transaction as complete. */ | |
this_i2c->status = MSS_I2C_SUCCESS; | |
// xSemaphoreGiveFromISR( this_i2c->xI2CCompleteSemaphore, &lHigherPriorityTaskWoken ); | |
break; | |
case ST_SLV_RST: /* SMBUS ONLY: timeout state. must clear interrupt */ | |
case ST_SLV_LA: /* Arbitr. lost (SLA rec'd) */ | |
case ST_GCA: /* General call address received, ACK returned */ | |
case ST_GCA_LA: /* Arbitr. lost (GCA rec'd) */ | |
/* do nothing */ | |
break; | |
/****************** SLAVE TRANSMITTER **************************/ | |
case ST_SLAVE_SLAR_ACK: /* SLA+R received, ACK returned */ | |
case ST_SLARW_LA: /* Arbitration lost, and: */ | |
case ST_RACK: /* Data tx'ed, ACK received */ | |
if ( status == ST_SLAVE_SLAR_ACK ) | |
{ | |
this_i2c->transaction = READ_SLAVE_TRANSACTION; | |
this_i2c->random_read_addr = 0; | |
} | |
/* Load the data, and determine if it is the last one */ | |
this_i2c->hw_reg->DATA = this_i2c->tx_buffer[this_i2c->tx_idx++]; | |
if (this_i2c->tx_idx >= this_i2c->tx_size - 1) /* last byte? */ | |
{ | |
this_i2c->hw_reg_bit->CTRL_AA = 0x00; | |
/* Next read transaction will result in slave's transmit buffer | |
* being sent from the first byte. */ | |
this_i2c->tx_idx = 0; | |
} | |
break; | |
case ST_SLAVE_RNACK: /* Data byte has been transmitted; not-ACK has been received. */ | |
/* We assume that the transaction will be stopped by the master. | |
* Reset tx_idx so that a subsequent read will result in the slave's | |
* transmit buffer being sent from the first byte. */ | |
this_i2c->tx_idx = 0; | |
break; | |
case ST_FINAL: /* Last Data byte tx'ed, ACK recieved */ | |
default: | |
/* do nothing */ | |
break; | |
} | |
if ( clear_irq ) | |
{ | |
/* clear interrupt. */ | |
this_i2c->hw_reg_bit->CTRL_SI = 0x00; | |
} | |
/* Read the status register to ensure the last I2C registers write took place | |
* in a system built around a bus making use of posted writes. */ | |
status = this_i2c->hw_reg->STATUS; | |
portEND_SWITCHING_ISR( lHigherPriorityTaskWoken ); | |
} | |
/*------------------------------------------------------------------------------ | |
* | |
*/ | |
uint32_t disable_interrupts( void ) | |
{ | |
uint32_t primask; | |
primask = __get_PRIMASK(); | |
return primask; | |
} | |
/*------------------------------------------------------------------------------ | |
* | |
*/ | |
void restore_interrupts( uint32_t primask ) | |
{ | |
__set_PRIMASK( primask ); | |
} | |
/*------------------------------------------------------------------------------ | |
* | |
*/ | |
#if defined(__GNUC__) | |
__attribute__((__interrupt__)) void I2C0_IRQHandler( void ) | |
#else | |
void I2C0_IRQHandler( void ) | |
#endif | |
{ | |
mss_i2c_isr( &g_mss_i2c0 ); | |
NVIC_ClearPendingIRQ( I2C0_IRQn ); | |
} | |
/*------------------------------------------------------------------------------ | |
* | |
*/ | |
#if defined(__GNUC__) | |
__attribute__((__interrupt__)) void I2C1_IRQHandler( void ) | |
#else | |
void I2C1_IRQHandler( void ) | |
#endif | |
{ | |
mss_i2c_isr( &g_mss_i2c1 ); | |
NVIC_ClearPendingIRQ( I2C1_IRQn ); | |
} | |
#ifdef __cplusplus | |
} | |
#endif |