1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-12-01 09:24:10 +01:00

i2c: rewrite i2c layer

The STM32 I2C block has a number of errata associated with it.
These errata are primarily related to timing sensitivities between
the peripheral and the interrupt handler.  In particular, the
correct generation of the stop bit relies on the I2C IRQ running
immediately and not being held off for any reason.

NOTE: The I2C interrupts must be the highest priority IRQs in the
      system to ensure correct operation.

I2C protocol is now implemented as a formal state machine.
See: stm32_i2c_fsm.{dot,jpg} for FSM description.

I2C init is now expressed by const initializers in pios_board.c
for both OP and AHRS boards.

I2C device drivers (ie. bmp085/hmc5843) now pass in const arrays
of an unlimited number of bus transfers to be done atomically.
The I2C adapter driver now handles all bus-level locking across the
list of transactions.  Generation of start/restart/stop conditions
are handled automatically over the list of transactions.

Timeouts have been removed from the API for now.  May be added
back later.

This driver has run error free on both the OP and AHRS boards for
up to 48hrs but it still sometimes fails earlier than that on the OP
board.  There is another possible set of improvements to the driver
that could employ the DMA engine for transfers of >= 2bytes.  This
change would reduce the timing sensitivities between the peripheral
and the driver but unfortunately, both the SPI and I2C interfaces
share the DMA1 engine.  That means only one of these two peripherals
can use the DMA engine and right now, SPI between OP and AHRS is
already using it.

Failures are currently fatal and will lock up the CPU.  This allows
useful information to be obtained in the failure cases.

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1241 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
stac 2010-08-08 04:15:08 +00:00 committed by stac
parent b6c1f43145
commit f74c6ffdd5
13 changed files with 1235 additions and 709 deletions

View File

@ -99,21 +99,9 @@ TIM8 | | | |
//------------------------
// PIOS_I2C
// See also pios_board.c
//------------------------
#define PIOS_I2C_PORT I2C1
#define PIOS_I2C_CLK RCC_APB1Periph_I2C1
#define PIOS_I2C_GPIO_PORT GPIOB
#define PIOS_I2C_SCL_PIN GPIO_Pin_6
#define PIOS_I2C_SDA_PIN GPIO_Pin_7
#define PIOS_I2C_DUTY_CYCLE I2C_DutyCycle_2
#define PIOS_I2C_BUS_FREQ 400000 // bit/s
#define PIOS_I2C_TIMEOUT_VALUE 50 // ms
#define PIOS_I2C_IRQ_EV_HANDLER void I2C1_EV_IRQHandler(void)
#define PIOS_I2C_IRQ_ER_HANDLER void I2C1_ER_IRQHandler(void)
#define PIOS_I2C_IRQ_EV_CHANNEL I2C1_EV_IRQn
#define PIOS_I2C_IRQ_ER_CHANNEL I2C1_EV_IRQn
#define PIOS_I2C_IRQ_EV_PRIORITY 2
#define PIOS_I2C_IRQ_ER_PRIORITY 2
#define PIOS_I2C_MAIN_ADAPTER 0
//-------------------------
// SPI

View File

@ -233,3 +233,90 @@ const uint8_t pios_com_num_devices = NELEMENTS(pios_com_devs);
#endif /* PIOS_INCLUDE_COM */
#if defined(PIOS_INCLUDE_I2C)
#include <pios_i2c_priv.h>
/*
* I2C Adapters
*/
void PIOS_I2C_main_adapter_ev_irq_handler(void);
void PIOS_I2C_main_adapter_er_irq_handler(void);
void I2C1_EV_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_ev_irq_handler")));
void I2C1_ER_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_er_irq_handler")));
const struct pios_i2c_adapter_cfg pios_i2c_main_adapter_cfg = {
.regs = I2C1,
.init = {
.I2C_Mode = I2C_Mode_I2C,
.I2C_OwnAddress1 = 0,
.I2C_Ack = I2C_Ack_Enable,
.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit,
.I2C_DutyCycle = I2C_DutyCycle_2,
.I2C_ClockSpeed = 200000, /* bits/s */
},
.transfer_timeout_ms = 50,
.scl = {
.gpio = GPIOB,
.init = {
.GPIO_Pin = GPIO_Pin_6,
.GPIO_Speed = GPIO_Speed_10MHz,
.GPIO_Mode = GPIO_Mode_AF_OD,
},
},
.sda = {
.gpio = GPIOB,
.init = {
.GPIO_Pin = GPIO_Pin_7,
.GPIO_Speed = GPIO_Speed_10MHz,
.GPIO_Mode = GPIO_Mode_AF_OD,
},
},
.event = {
.handler = PIOS_I2C_main_adapter_ev_irq_handler,
.flags = 0, /* FIXME: check this */
.init = {
.NVIC_IRQChannel = I2C1_EV_IRQn,
//.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST,
.NVIC_IRQChannelPreemptionPriority = 2,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE,
},
},
.error = {
.handler = PIOS_I2C_main_adapter_er_irq_handler,
.flags = 0, /* FIXME: check this */
.init = {
.NVIC_IRQChannel = I2C1_ER_IRQn,
//.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST,
.NVIC_IRQChannelPreemptionPriority = 2,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE,
},
},
};
/*
* Board specific number of devices.
*/
struct pios_i2c_adapter pios_i2c_adapters[] = {
{
.cfg = &pios_i2c_main_adapter_cfg,
},
};
uint8_t pios_i2c_num_adapters = NELEMENTS(pios_i2c_adapters);
void PIOS_I2C_main_adapter_ev_irq_handler(void)
{
/* Call into the generic code to handle the IRQ for this specific device */
PIOS_I2C_EV_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER);
}
void PIOS_I2C_main_adapter_er_irq_handler(void)
{
/* Call into the generic code to handle the IRQ for this specific device */
PIOS_I2C_ER_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER);
}
#endif /* PIOS_INCLUDE_I2C */

View File

@ -0,0 +1,175 @@
// Regenerate using graphviz/dotty with this command
// dot -Tjpg ./flight/Doc/Architecture/stm32_i2c_fsm.dot > ./flight/Doc/Architecture/stm32_i2c_fsm.jpg
digraph stm32_i2c_fsm {
label="STM32 I2C FSM"
labelloc=t
labeljust = l
{
node [ style=filled,color=lightgray ]
stopped [ shape="doublecircle" ]
}
node [ style=filled,color=blue ]
{
rank=same
stopped
stopping
starting
}
// start addr/w[A] data[A] stop
// SB ADDR BTF
// ADDR DR
// STOP
//
// start addr/w[A] data[A] data[A] stop
// SB ADDR BTF BTF
// ADDR DR DR
// STOP
//
// start addr/w[A] data[A] data[A] data[A] stop
// SB ADDR BTF BTF BTF
// ADDR DR DR DR
// STOP
//
subgraph cluster_w_last_txn {
label="Final Write Txn"
w_last_txn_addr
w_last_txn_middle
w_last_txn_last
}
// start addr/w[A] data[A] start
// SB ADDR BTF
// ADDR DR
// START
//
// start addr/w[A] data[A] data[A] start
// SB ADDR BTF BTF
// ADDR DR DR
// START
//
// start addr/w[A] data[A] data[A] data[A] stop
// SB ADDR BTF BTF BTF
// ADDR DR DR DR
// START
//
subgraph cluster_w_more_txn {
label="Write Txn with Restart"
w_more_txn_addr
w_more_txn_middle
w_more_txn_last
}
// start addr/r[A] [data] N stop
// SB ADDR RXNE
// ADDR STOP DR
// NAK
//
// start addr/r[A] [data] A [data] N stop
// SB ADDR RXNE RXNE
// ADDR DR DR
// ACK NAK
// STOP
//
// start addr/r[A] [data] A [data] A [data] N stop
// SB ADDR RXNE RXNE RXNE
// ADDR DR DR DR
// ACK ACK NAK
// STOP
//
subgraph cluster_r_last_txn {
label="Final Read Txn"
r_last_txn_addr
r_last_txn_pre_one
r_last_txn_pre_first
r_last_txn_pre_middle
r_last_txn_pre_last
r_last_txn_post_last
}
// start addr/r[A] [data] N start
// SB ADDR RXNE
// ADDR START DR
// NAK
//
// start addr/r[A] [data] A [data] N start
// SB ADDR RXNE RXNE
// ADDR DR DR
// ACK NAK
// START
//
// start addr/r[A] [data] A [data] A [data] N start
// SB ADDR RXNE RXNE RXNE
// ADDR DR DR DR
// ACK ACK NAK
// START
subgraph cluster_r_more_txn {
label="Read Txn with Restart"
r_more_txn_addr
r_more_txn_pre_one
r_more_txn_pre_first
r_more_txn_pre_middle
r_more_txn_pre_last
r_more_txn_post_last
}
stopping -> stopped [ label="auto" ]
stopped -> starting [ label="start" ]
starting -> w_last_txn_addr [ label="SB\nop=write\nlast txn" ]
starting -> w_more_txn_addr [ label="SB\nop=write\nmore txn" ]
starting -> r_last_txn_addr [ label="SB\nop=read\nlast txn" ]
starting -> r_more_txn_addr [ label="SB\nop=read\nmore txn" ]
w_last_txn_addr -> w_last_txn_last [ label="ADDR\nlen==1" ]
w_more_txn_addr -> w_more_txn_last [ label="ADDR\nlen==1" ]
w_last_txn_addr -> w_last_txn_middle [ label="ADDR\nlen>1" ]
w_more_txn_addr -> w_more_txn_middle [ label="ADDR\nlen>1" ]
w_last_txn_middle -> w_last_txn_middle [ label="BTF\nlen>1" ]
w_more_txn_middle -> w_more_txn_middle [ label="BTF\nlen>1" ]
w_last_txn_middle -> w_last_txn_last [ label="BTF\nlen==1" ]
w_more_txn_middle -> w_more_txn_last [ label="BTF\nlen==1" ]
w_last_txn_last -> stopped [ label="BTF\nlen==0" ]
w_more_txn_last -> starting [ label="BTF\nlen==0" ]
r_last_txn_addr -> r_last_txn_pre_one [ label="ADDR\nlen==1" ]
r_more_txn_addr -> r_more_txn_pre_one [ label="ADDR\nlen==1" ]
r_last_txn_addr -> r_last_txn_pre_last [ label="ADDR\nlen==2" ]
r_more_txn_addr -> r_more_txn_pre_last [ label="ADDR\nlen==2" ]
r_last_txn_addr -> r_last_txn_pre_first [ label="ADDR\nlen>2" ]
r_more_txn_addr -> r_more_txn_pre_first [ label="ADDR\nlen>2" ]
r_last_txn_pre_one -> r_last_txn_post_last [ label="BTF\nlen==1" ]
r_more_txn_pre_one -> r_more_txn_post_last [ label="BTF\nlen==1" ]
r_last_txn_pre_first -> r_last_txn_pre_last [ label="BTF\nlen==2" ]
r_more_txn_pre_first -> r_more_txn_pre_last [ label="BTF\nlen==2" ]
r_last_txn_pre_first -> r_last_txn_pre_middle [ label="BTF\nlen>2" ]
r_more_txn_pre_first -> r_more_txn_pre_middle [ label="BTF\nlen>2" ]
r_last_txn_pre_middle -> r_last_txn_pre_middle [ label="BTF\nlen>2" ]
r_more_txn_pre_middle -> r_more_txn_pre_middle [ label="BTF\nlen>2" ]
r_last_txn_pre_middle -> r_last_txn_pre_last [ label="BTF\nlen==2" ]
r_more_txn_pre_middle -> r_more_txn_pre_last [ label="BTF\nlen==2" ]
r_last_txn_pre_last -> r_last_txn_post_last [ label="BTF\nlen==1" ]
r_more_txn_pre_last -> r_more_txn_post_last [ label="BTF\nlen==1" ]
r_last_txn_post_last -> stopping [ label="auto" ]
r_more_txn_post_last -> starting [ label="auto" ]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

View File

@ -81,21 +81,9 @@ TIM8 | Servo 5 | Servo 6 | Servo 7 | Servo 8
//------------------------
// PIOS_I2C
// See also pios_board.c
//------------------------
#define PIOS_I2C_PORT I2C2
#define PIOS_I2C_CLK RCC_APB1Periph_I2C2
#define PIOS_I2C_GPIO_PORT GPIOB
#define PIOS_I2C_SDA_PIN GPIO_Pin_11
#define PIOS_I2C_SCL_PIN GPIO_Pin_10
#define PIOS_I2C_DUTY_CYCLE I2C_DutyCycle_2
#define PIOS_I2C_BUS_FREQ 400000 // bit/s
#define PIOS_I2C_TIMEOUT_VALUE 50 // ms
#define PIOS_I2C_IRQ_EV_HANDLER void I2C2_EV_IRQHandler(void)
#define PIOS_I2C_IRQ_ER_HANDLER void I2C2_ER_IRQHandler(void)
#define PIOS_I2C_IRQ_EV_CHANNEL I2C2_EV_IRQn
#define PIOS_I2C_IRQ_ER_CHANNEL I2C2_ER_IRQn
#define PIOS_I2C_IRQ_EV_PRIORITY PIOS_IRQ_PRIO_HIGH
#define PIOS_I2C_IRQ_ER_PRIORITY PIOS_IRQ_PRIO_HIGH
#define PIOS_I2C_MAIN_ADAPTER 0
//------------------------
// PIOS_BMP085

View File

@ -31,6 +31,7 @@
#include <pios_spi_priv.h>
#include <pios_usart_priv.h>
#include <pios_com_priv.h>
#include <pios_i2c_priv.h>
#include <openpilot.h>
#include <uavobjectsinit.h>
@ -327,7 +328,7 @@ const struct pios_usart_cfg pios_usart_telem_cfg = {
.handler = PIOS_USART_telem_irq_handler,
.init = {
.NVIC_IRQChannel = USART2_IRQn,
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST,
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGH,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE,
},
@ -575,6 +576,87 @@ struct pios_com_dev pios_com_devs[] = {
const uint8_t pios_com_num_devices = NELEMENTS(pios_com_devs);
/*
* I2C Adapters
*/
void PIOS_I2C_main_adapter_ev_irq_handler(void);
void PIOS_I2C_main_adapter_er_irq_handler(void);
void I2C2_EV_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_ev_irq_handler")));
void I2C2_ER_IRQHandler() __attribute__ ((alias ("PIOS_I2C_main_adapter_er_irq_handler")));
const struct pios_i2c_adapter_cfg pios_i2c_main_adapter_cfg = {
.regs = I2C2,
.init = {
.I2C_Mode = I2C_Mode_I2C,
.I2C_OwnAddress1 = 0,
.I2C_Ack = I2C_Ack_Enable,
.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit,
.I2C_DutyCycle = I2C_DutyCycle_2,
.I2C_ClockSpeed = 400000, /* bits/s */
},
.transfer_timeout_ms = 50,
.scl = {
.gpio = GPIOB,
.init = {
.GPIO_Pin = GPIO_Pin_10,
.GPIO_Speed = GPIO_Speed_10MHz,
.GPIO_Mode = GPIO_Mode_AF_OD,
},
},
.sda = {
.gpio = GPIOB,
.init = {
.GPIO_Pin = GPIO_Pin_11,
.GPIO_Speed = GPIO_Speed_10MHz,
.GPIO_Mode = GPIO_Mode_AF_OD,
},
},
.event = {
.handler = PIOS_I2C_main_adapter_ev_irq_handler,
.flags = 0, /* FIXME: check this */
.init = {
.NVIC_IRQChannel = I2C2_EV_IRQn,
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE,
},
},
.error = {
.handler = PIOS_I2C_main_adapter_er_irq_handler,
.flags = 0, /* FIXME: check this */
.init = {
.NVIC_IRQChannel = I2C2_ER_IRQn,
.NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE,
},
},
};
/*
* Board specific number of devices.
*/
struct pios_i2c_adapter pios_i2c_adapters[] = {
{
.cfg = &pios_i2c_main_adapter_cfg,
},
};
uint8_t pios_i2c_num_adapters = NELEMENTS(pios_i2c_adapters);
void PIOS_I2C_main_adapter_ev_irq_handler(void)
{
/* Call into the generic code to handle the IRQ for this specific device */
PIOS_I2C_EV_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER);
}
void PIOS_I2C_main_adapter_er_irq_handler(void)
{
/* Call into the generic code to handle the IRQ for this specific device */
PIOS_I2C_ER_IRQ_Handler(PIOS_I2C_MAIN_ADAPTER);
}
/**
* @}
*/

View File

@ -145,7 +145,7 @@ void PIOS_BMP085_ReadADC(void)
/* Read and store the 16bit result */
if(CurrentRead == TemperatureConv) {
/* Read the temperature conversion */
PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 2);
PIOS_BMP085_Read(BMP085_ADC_MSB, Data, 3); /* Read 3 since it is more reliable on i2c than the proper read of 2 bytes. */
RawTemperature = ((Data[0] << 8) | Data[1]);
X1 = (RawTemperature - CalibData.AC6) * CalibData.AC5 >> 15;
@ -196,30 +196,28 @@ int32_t PIOS_BMP085_GetPressure(void)
* \return -2 if BMP085 blocked by another task (retry it!)
* \return -4 if invalid length
*/
int32_t PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len)
bool PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len)
{
/* Try to get the I2C peripheral */
if(PIOS_I2C_LockDevice(0) == FALSE)
{
/* Request a retry */
return -2;
}
uint8_t addr_buffer[] = {
address,
};
/* Send I2C address and EEPROM address */
/* To avoid issues copy address into temporary buffer */
uint8_t addr_buffer[1] = {(uint8_t)address};
int32_t error = PIOS_I2C_Transfer(I2C_Write_WithoutStop, BMP085_I2C_ADDR, addr_buffer, 1);
const struct pios_i2c_txn txn_list[] = {
{
.addr = BMP085_I2C_ADDR,
.rw = PIOS_I2C_TXN_WRITE,
.len = sizeof(addr_buffer),
.buf = addr_buffer,
},
{
.addr = BMP085_I2C_ADDR,
.rw = PIOS_I2C_TXN_READ,
.len = len,
.buf = buffer,
}
};
/* Now receive byte(s) */
if(!error) {
error = PIOS_I2C_Transfer(I2C_Read, BMP085_I2C_ADDR, buffer, len);
}
/* Release I2C peripheral */
PIOS_I2C_UnlockDevice();
/* Return error status */
return error < 0 ? -1 : 0;
return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list));
}
@ -231,27 +229,23 @@ int32_t PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len)
* \return -1 if error during I2C transfer
* \return -2 if BMP085 blocked by another task (retry it!)
*/
int32_t PIOS_BMP085_Write(uint8_t address, uint8_t buffer)
bool PIOS_BMP085_Write(uint8_t address, uint8_t buffer)
{
/* Try to get the IIC peripheral */
if(PIOS_I2C_LockDevice(0) == FALSE)
{
/* Request a retry */
return -2;
}
uint8_t data[] = {
address,
buffer,
};
/* Send I2C address and data */
uint8_t WriteBuffer[2];
WriteBuffer[0] = address;
WriteBuffer[1] = buffer;
int32_t error = PIOS_I2C_Transfer(I2C_Write, BMP085_I2C_ADDR, WriteBuffer, 2);
/* Release I2C peripheral */
PIOS_I2C_UnlockDevice();
/* Return error status */
return error < 0 ? -1 : 0;
const struct pios_i2c_txn txn_list[] = {
{
.addr = BMP085_I2C_ADDR,
.rw = PIOS_I2C_TXN_WRITE,
.len = sizeof(data),
.buf = data,
},
};
return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list));
}

View File

@ -206,20 +206,28 @@ void PIOS_HMC5843_ReadID(uint8_t out[4])
* \return -1 if error during I2C transfer
* \return -4 if invalid length
*/
int32_t PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len)
bool PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len)
{
/* Send I2C address and register address */
/* To avoid issues copy address into temporary buffer */
uint8_t addr_buffer[1] = {(uint8_t)address};
int32_t error = PIOS_I2C_Transfer(I2C_Write_WithoutStop, PIOS_HMC5843_I2C_ADDR, addr_buffer, 1);
/* Now receive byte(s) */
if(!error) {
error = PIOS_I2C_Transfer(I2C_Read, PIOS_HMC5843_I2C_ADDR, buffer, len);
}
/* Return error status */
return error < 0 ? -1 : 0;
uint8_t addr_buffer[] = {
address,
};
const struct pios_i2c_txn txn_list[] = {
{
.addr = PIOS_HMC5843_I2C_ADDR,
.rw = PIOS_I2C_TXN_WRITE,
.len = sizeof(addr_buffer),
.buf = addr_buffer,
},
{
.addr = PIOS_HMC5843_I2C_ADDR,
.rw = PIOS_I2C_TXN_READ,
.len = len,
.buf = buffer,
}
};
return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list));
}
@ -230,17 +238,23 @@ int32_t PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len)
* \return 0 if operation was successful
* \return -1 if error during I2C transfer
*/
int32_t PIOS_HMC5843_Write(uint8_t address, uint8_t buffer)
bool PIOS_HMC5843_Write(uint8_t address, uint8_t buffer)
{
/* Send I2C address and data */
uint8_t WriteBuffer[2];
WriteBuffer[0] = address;
WriteBuffer[1] = buffer;
int32_t error = PIOS_I2C_Transfer(I2C_Write, PIOS_HMC5843_I2C_ADDR, WriteBuffer, 2);
/* Return error status */
return error < 0 ? -1 : 0;
uint8_t data[] = {
address,
buffer,
};
const struct pios_i2c_txn txn_list[] = {
{
.addr = PIOS_HMC5843_I2C_ADDR,
.rw = PIOS_I2C_TXN_WRITE,
.len = sizeof(data),
.buf = data,
},
};
return PIOS_I2C_Transfer(PIOS_I2C_MAIN_ADAPTER, txn_list, NELEMENTS(txn_list));
}
#endif

View File

@ -37,81 +37,521 @@
#if defined(PIOS_INCLUDE_I2C)
/* Options */
//#define USE_DEBUG_PINS
#ifdef PIOS_INCLUDE_FREERTOS
#define USE_FREERTOS
#if defined(PIOS_INCLUDE_FREERTOS)
#define USE_FREERTOS
#endif
#include <pios_i2c_priv.h>
/* Global Variables */
volatile uint32_t PIOS_I2C_UnexpectedEvent;
enum i2c_adapter_event {
I2C_EVENT_START,
I2C_EVENT_STARTED_MORE_TXN_READ,
I2C_EVENT_STARTED_MORE_TXN_WRITE,
I2C_EVENT_STARTED_LAST_TXN_READ,
I2C_EVENT_STARTED_LAST_TXN_WRITE,
I2C_EVENT_ADDR_SENT_LEN_EQ_0,
I2C_EVENT_ADDR_SENT_LEN_EQ_1,
I2C_EVENT_ADDR_SENT_LEN_EQ_2,
I2C_EVENT_ADDR_SENT_LEN_GT_2,
I2C_EVENT_TRANSFER_DONE_LEN_EQ_0,
I2C_EVENT_TRANSFER_DONE_LEN_EQ_1,
I2C_EVENT_TRANSFER_DONE_LEN_EQ_2,
I2C_EVENT_TRANSFER_DONE_LEN_GT_2,
I2C_EVENT_AUTO, /* FIXME: remove this */
/* Local types */
typedef union {
struct {
unsigned ALL:8;
};
struct {
unsigned BUSY:1;
unsigned STOP_REQUESTED:1;
unsigned ABORT_IF_FIRST_BYTE_0:1;
unsigned WRITE_WITHOUT_STOP:1;
unsigned INIRQ:1;
};
} TransferStateTypeDef;
I2C_EVENT_NUM_EVENTS /* Must be last */
};
static void go_faulted (struct pios_i2c_adapter * i2c_adapter);
static void go_stopping (struct pios_i2c_adapter * i2c_adapter);
static void go_stopped (struct pios_i2c_adapter * i2c_adapter);
static void go_starting (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_addr (struct pios_i2c_adapter * i2c_adapter);
static void go_r_more_txn_pre_one (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_pre_first (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_pre_middle (struct pios_i2c_adapter * i2c_adapter);
static void go_r_more_txn_pre_last (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_post_last (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_addr (struct pios_i2c_adapter * i2c_adapter);
static void go_r_last_txn_pre_one (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_pre_first (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_pre_middle (struct pios_i2c_adapter * i2c_adapter);
static void go_r_last_txn_pre_last (struct pios_i2c_adapter * i2c_adapter);
static void go_r_any_txn_post_last (struct pios_i2c_adapter * i2c_adapter);
static void go_w_any_txn_addr (struct pios_i2c_adapter * i2c_adapter);
static void go_w_any_txn_middle (struct pios_i2c_adapter * i2c_adapter);
static void go_w_more_txn_last (struct pios_i2c_adapter * i2c_adapter);
static void go_w_any_txn_addr (struct pios_i2c_adapter * i2c_adapter);
static void go_w_any_txn_middle (struct pios_i2c_adapter * i2c_adapter);
static void go_w_last_txn_last (struct pios_i2c_adapter * i2c_adapter);
struct i2c_adapter_transition {
void (*entry_fn)(struct pios_i2c_adapter * i2c_adapter);
enum i2c_adapter_state next_state[I2C_EVENT_NUM_EVENTS];
};
static void i2c_adapter_inject_event(struct pios_i2c_adapter * i2c_adapter, enum i2c_adapter_event event);
static void i2c_adapter_fsm_init(struct pios_i2c_adapter * i2c_adapter);
const static struct i2c_adapter_transition i2c_adapter_transitions[I2C_STATE_NUM_STATES] = {
[I2C_STATE_FAULTED] = {
.entry_fn = go_faulted,
},
[I2C_STATE_STOPPED] = {
.entry_fn = go_stopped,
.next_state = {
[I2C_EVENT_START] = I2C_STATE_STARTING,
},
},
[I2C_STATE_STOPPING] = {
.entry_fn = go_stopping,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STOPPED,
},
},
[I2C_STATE_STARTING] = {
.entry_fn = go_starting,
.next_state = {
[I2C_EVENT_STARTED_MORE_TXN_READ] = I2C_STATE_R_MORE_TXN_ADDR,
[I2C_EVENT_STARTED_MORE_TXN_WRITE] = I2C_STATE_W_MORE_TXN_ADDR,
[I2C_EVENT_STARTED_LAST_TXN_READ] = I2C_STATE_R_LAST_TXN_ADDR,
[I2C_EVENT_STARTED_LAST_TXN_WRITE] = I2C_STATE_W_LAST_TXN_ADDR,
},
},
/*
* Read with restart
*/
[I2C_STATE_R_MORE_TXN_ADDR] = {
.entry_fn = go_r_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_PRE_ONE,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_FIRST,
},
},
[I2C_STATE_R_MORE_TXN_PRE_ONE] = {
.entry_fn = go_r_more_txn_pre_one,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_POST_LAST,
},
},
[I2C_STATE_R_MORE_TXN_PRE_FIRST] = {
.entry_fn = go_r_any_txn_pre_first,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_MIDDLE,
},
},
[I2C_STATE_R_MORE_TXN_PRE_MIDDLE] = {
.entry_fn = go_r_any_txn_pre_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_MIDDLE,
},
},
[I2C_STATE_R_MORE_TXN_PRE_LAST] = {
.entry_fn = go_r_more_txn_pre_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_POST_LAST,
},
},
[I2C_STATE_R_MORE_TXN_POST_LAST] = {
.entry_fn = go_r_any_txn_post_last,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STARTING,
},
},
/*
* Read
*/
[I2C_STATE_R_LAST_TXN_ADDR] = {
.entry_fn = go_r_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_PRE_ONE,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_FIRST,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_FIRST,
},
},
[I2C_STATE_R_LAST_TXN_PRE_ONE] = {
.entry_fn = go_r_last_txn_pre_one,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_POST_LAST,
},
},
[I2C_STATE_R_LAST_TXN_PRE_FIRST] = {
.entry_fn = go_r_any_txn_pre_first,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_MIDDLE,
},
},
[I2C_STATE_R_LAST_TXN_PRE_MIDDLE] = {
.entry_fn = go_r_any_txn_pre_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_MIDDLE,
},
},
[I2C_STATE_R_LAST_TXN_PRE_LAST] = {
.entry_fn = go_r_last_txn_pre_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_POST_LAST,
},
},
[I2C_STATE_R_LAST_TXN_POST_LAST] = {
.entry_fn = go_r_any_txn_post_last,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STOPPING,
},
},
typedef struct {
I2C_TypeDef *base;
/*
* Write with restart
*/
uint8_t i2c_address;
uint8_t *tx_buffer_ptr;
uint8_t *rx_buffer_ptr;
volatile uint16_t buffer_len;
volatile uint16_t buffer_ix;
[I2C_STATE_W_MORE_TXN_ADDR] = {
.entry_fn = go_w_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_W_MORE_TXN_LAST,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
},
},
volatile TransferStateTypeDef transfer_state;
volatile int32_t transfer_error;
[I2C_STATE_W_MORE_TXN_MIDDLE] = {
.entry_fn = go_w_any_txn_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_W_MORE_TXN_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
},
},
#ifdef USE_FREERTOS
xSemaphoreHandle sem_readySignal;
portBASE_TYPE xHigherPriorityTaskWoken;
xSemaphoreHandle xBusyMutex;
[I2C_STATE_W_MORE_TXN_LAST] = {
.entry_fn = go_w_more_txn_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_0] = I2C_STATE_STARTING,
},
},
/*
* Write
*/
[I2C_STATE_W_LAST_TXN_ADDR] = {
.entry_fn = go_w_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_W_LAST_TXN_LAST,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
},
},
[I2C_STATE_W_LAST_TXN_MIDDLE] = {
.entry_fn = go_w_any_txn_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_W_LAST_TXN_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
},
},
[I2C_STATE_W_LAST_TXN_LAST] = {
.entry_fn = go_w_last_txn_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_0] = I2C_STATE_STOPPING,
},
},
};
static void go_faulted (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(0);
}
static void go_stopping (struct pios_i2c_adapter * i2c_adapter)
{
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
/* Spin waiting for the stop bit to be cleared before continuing */
#define I2C_CR1_STOP_REQUESTED 0x0200
while (i2c_adapter->cfg->regs->CR1 & I2C_CR1_STOP_REQUESTED);
}
static void go_stopped (struct pios_i2c_adapter * i2c_adapter)
{
#ifdef USE_FREERTOS
signed portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE;
#endif
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, ENABLE);
} I2CRecTypeDef;
#ifdef USE_FREERTOS
if (xSemaphoreGiveFromISR(i2c_adapter->sem_ready, &pxHigherPriorityTaskWoken) != pdTRUE) {
PIOS_DEBUG_Assert(0);
}
portEND_SWITCHING_ISR(pxHigherPriorityTaskWoken); /* FIXME: is this the right place for this? */
#endif /* USE_FREERTOS */
}
/* Local Prototypes */
static void PIOS_I2C_InitPeripheral(void);
static void EV_IRQHandler(I2CRecTypeDef *i2cx);
static void ER_IRQHandler(I2CRecTypeDef *i2cx);
static void go_starting (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
/* Local Variables */
static I2CRecTypeDef I2CRec;
i2c_adapter->active_byte = &(i2c_adapter->active_txn->buf[0]);
i2c_adapter->last_byte = &(i2c_adapter->active_txn->buf[i2c_adapter->active_txn->len - 1]);
/* Local Functions */
static void TransferStart(I2CRecTypeDef *i2cx);
static void TransferEnd(I2CRecTypeDef *i2cx);
I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
}
/* Macros */
#ifdef USE_DEBUG_PINS
#define DEBUG_PIN_ISR 0
#define DEBUG_PIN_BUSY 1
#define DEBUG_PIN_WAIT 2
#define DEBUG_PIN_ASSERT 7
#define DebugPinHigh(x) PIOS_DEBUG_PinHigh(x)
#define DebugPinLow(x) PIOS_DEBUG_PinLow(x)
#else
#define DebugPinHigh(x)
#define DebugPinLow(x)
#endif
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_addr (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
#define Assert(exp) PIOS_DEBUG_Assert(exp)
PIOS_DEBUG_Assert(i2c_adapter->active_txn->rw == PIOS_I2C_TXN_READ);
I2C_Send7bitAddress(i2c_adapter->cfg->regs, i2c_adapter->active_txn->addr, I2C_Direction_Receiver);
}
static void go_r_more_txn_pre_one (struct pios_i2c_adapter * i2c_adapter)
{
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE);
}
static void go_r_last_txn_pre_one (struct pios_i2c_adapter * i2c_adapter)
{
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE);
}
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_pre_first (struct pios_i2c_adapter * i2c_adapter)
{
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, ENABLE);
}
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_pre_middle (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
static void go_r_more_txn_pre_last (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
PIOS_IRQ_Disable();
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
PIOS_IRQ_Enable();
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
static void go_r_last_txn_pre_last (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
PIOS_IRQ_Disable();
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
PIOS_IRQ_Enable();
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_post_last (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
/* Move to the next byte */
i2c_adapter->active_byte++;
/* Move to the next transaction */
i2c_adapter->active_txn++;
}
/* Common to 'more' and 'last' transaction */
static void go_w_any_txn_addr (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn->rw == PIOS_I2C_TXN_WRITE);
I2C_Send7bitAddress(i2c_adapter->cfg->regs, i2c_adapter->active_txn->addr, I2C_Direction_Transmitter);
}
static void go_w_any_txn_middle (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte < i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte));
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
static void go_w_more_txn_last (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte));
/* Move to the next byte */
i2c_adapter->active_byte++;
/* Move to the next transaction */
i2c_adapter->active_txn++;
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
}
static void go_w_last_txn_last (struct pios_i2c_adapter * i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_BUF, DISABLE);
I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte));
// SHOULD MOVE THIS INTO A STOPPING STATE AND SET IT ONLY AFTER THE BYTE WAS SENT
I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE);
/* Move to the next byte */
i2c_adapter->active_byte++;
}
static void i2c_adapter_inject_event(struct pios_i2c_adapter * i2c_adapter, enum i2c_adapter_event event)
{
PIOS_IRQ_Disable();
/*
* Move to the next state
*
* This is done prior to calling the new state's entry function to
* guarantee that the entry function never depends on the previous
* state. This way, it cannot ever know what the previous state was.
*/
enum i2c_adapter_state prev_state = i2c_adapter->curr_state;
if (prev_state);
i2c_adapter->curr_state = i2c_adapter_transitions[i2c_adapter->curr_state].next_state[event];
/* Call the entry function (if any) for the next state. */
if (i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn) {
i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn(i2c_adapter);
}
PIOS_IRQ_Enable();
}
static void i2c_adapter_process_auto(struct pios_i2c_adapter * i2c_adapter)
{
PIOS_IRQ_Disable();
enum i2c_adapter_state prev_state = i2c_adapter->curr_state;
if (prev_state);
while (i2c_adapter_transitions[i2c_adapter->curr_state].next_state[I2C_EVENT_AUTO]) {
i2c_adapter->curr_state = i2c_adapter_transitions[i2c_adapter->curr_state].next_state[I2C_EVENT_AUTO];
/* Call the entry function (if any) for the next state. */
if (i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn) {
i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn(i2c_adapter);
}
}
PIOS_IRQ_Enable();
}
static void i2c_adapter_fsm_init(struct pios_i2c_adapter * i2c_adapter)
{
i2c_adapter->curr_state = I2C_STATE_STOPPED;
//go_stopped(i2c_adapter);
}
#include <pios_i2c_priv.h>
static struct pios_i2c_adapter * find_i2c_adapter_by_id (uint8_t adapter)
{
if (adapter >= pios_i2c_num_adapters) {
/* Undefined I2C adapter for this board (see pios_board.c) */
return NULL;
}
/* Get a handle for the device configuration */
return &(pios_i2c_adapters[adapter]);
}
/**
* Initializes IIC driver
@ -120,556 +560,232 @@ static void TransferEnd(I2CRecTypeDef *i2cx);
*/
int32_t PIOS_I2C_Init(void)
{
/* Configure IIC pins in open drain mode */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SCL_PIN;
GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = PIOS_I2C_SDA_PIN;
GPIO_Init(PIOS_I2C_GPIO_PORT, &GPIO_InitStructure);
PIOS_I2C_InitPeripheral();
struct pios_i2c_adapter * i2c_adapter;
for (uint8_t i = 0; i < pios_i2c_num_adapters; i++) {
/* Get a handle for the device configuration */
i2c_adapter = find_i2c_adapter_by_id(i);
PIOS_DEBUG_Assert(i2c_adapter);
#ifdef USE_FREERTOS
vSemaphoreCreateBinary(I2CRec.sem_readySignal);
I2CRec.xBusyMutex = xSemaphoreCreateMutex();
/*
* Must be done prior to calling i2c_adapter_fsm_init()
* since the sem_ready mutex is used in the initial state.
*/
vSemaphoreCreateBinary(i2c_adapter->sem_ready);
i2c_adapter->sem_busy = xSemaphoreCreateMutex();
#endif // USE_FREERTOS
TransferEnd(&I2CRec);
/* Initialize the state machine */
i2c_adapter_fsm_init(i2c_adapter);
/* Configure and enable I2C interrupts */
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = PIOS_I2C_IRQ_EV_CHANNEL;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_EV_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Initialize the GPIO pins */
GPIO_Init(i2c_adapter->cfg->scl.gpio, &(i2c_adapter->cfg->scl.init));
GPIO_Init(i2c_adapter->cfg->sda.gpio, &(i2c_adapter->cfg->sda.init));
NVIC_InitStructure.NVIC_IRQChannel = PIOS_I2C_IRQ_ER_CHANNEL;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = PIOS_I2C_IRQ_ER_PRIORITY;
NVIC_Init(&NVIC_InitStructure);
/* Enable the associated peripheral clock */
switch ((uint32_t)i2c_adapter->cfg->regs) {
case (uint32_t)I2C1:
/* Enable I2C peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
break;
case (uint32_t)I2C2:
/* Enable I2C peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
break;
}
DebugPinLow(2);
/* No error */
return 0;
}
/**
* Internal function to (re-)initialize the I2C peripheral
*/
static void PIOS_I2C_InitPeripheral(void)
{
I2C_InitTypeDef I2C_InitStructure;
I2CRecTypeDef *i2cx = &I2CRec;
/* Prepare I2C init-struct */
I2C_StructInit(&I2C_InitStructure);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
/* Define base address */
i2cx->base = PIOS_I2C_PORT;
/* enable peripheral clock of I2C */
RCC_APB1PeriphClockCmd(PIOS_I2C_CLK, ENABLE);
/* Set I2C clock bus clock params */
/* Note that the STM32 driver handles value <= 100kHz differently! (duty cycle always 1:1) */
/* Important: bus frequencies > 400kHz don't work stable */
I2C_InitStructure.I2C_DutyCycle = PIOS_I2C_DUTY_CYCLE;
I2C_InitStructure.I2C_ClockSpeed = PIOS_I2C_BUS_FREQ;
/* Trigger software reset via I2C_DeInit */
I2C_DeInit(i2cx->base);
/* Clear transfer state and error value */
i2cx->transfer_state.ALL = 0;
i2cx->transfer_error = 0;
/* Configure I2C peripheral */
I2C_Init(i2cx->base, &I2C_InitStructure);
}
#ifdef USE_FREERTOS
/**
* Semaphore handling: requests the IIC interface
* \param[in] timeout Timeout in ticks, 0 for no delay
* \return TRUE when the lock to the device was obtained
*/
bool PIOS_I2C_LockDevice(portTickType timeout)
{
if (xSemaphoreTake(I2CRec.xBusyMutex, timeout) == pdTRUE)
{
// Ok, got device
return TRUE;
}
else
{
return FALSE;
}
}
/**
* Semaphore handling: releases the IIC interface for other tasks
* \return < 0 on errors
*/
void PIOS_I2C_UnlockDevice(void)
{
xSemaphoreGive(I2CRec.xBusyMutex);
}
#endif // USE_FREERTOS
/**
* Internal function called at the start of a transfer
*/
static void TransferStart(I2CRecTypeDef *i2cx)
{
Assert(i2cx->transfer_state.BUSY == 0);
DebugPinHigh(DEBUG_PIN_BUSY);
i2cx->transfer_state.BUSY = 1;
// Enable Interrupts: I2V2 event, buffer and error interrupt
I2C_ITConfig(i2cx->base, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
}
/**
* Internal function called at the end of a transfer
*/
static void TransferEnd(I2CRecTypeDef *i2cx)
{
// Disable all interrupts
I2C_ITConfig(i2cx->base, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
DebugPinLow(DEBUG_PIN_BUSY);
i2cx->transfer_state.BUSY = 0;
#ifdef USE_FREERTOS
if (i2cx->transfer_state.INIRQ)
{
xSemaphoreGiveFromISR(i2cx->sem_readySignal, &i2cx->xHigherPriorityTaskWoken);
}
else
{
xSemaphoreGive(i2cx->sem_readySignal);
}
#if 0 /* FIXME: is this reset necessary? Should it be done in the stopped state? */
/* Reset the I2C block */
I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, ENABLE);
I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, DISABLE);
#endif
/* Reset the I2C block */
I2C_DeInit(i2c_adapter->cfg->regs);
/* Initialize the I2C block */
I2C_Init(i2c_adapter->cfg->regs, &(i2c_adapter->cfg->init));
#if 1 /* FIXME: is this reset necessary? Should it be done in the stopped state? */
#define I2C_BUSY 0x20
if (i2c_adapter->cfg->regs->SR2 & I2C_BUSY) {
/* Reset the I2C block */
I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, ENABLE);
I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, DISABLE);
}
#endif
/* Configure and enable I2C interrupts */
NVIC_Init(&(i2c_adapter->cfg->event.init));
NVIC_Init(&(i2c_adapter->cfg->error.init));
}
/* No error */
return 0;
}
/**
* Checks if transfer is finished
* \return 1 if ongoing transfer
* \return <=0 no transfer is busy; return value indicates error value of last transfer
* (PIOS_I2C_TransferBegin() has to be called again)
*/
int32_t PIOS_I2C_TransferCheck(void)
bool PIOS_I2C_Transfer(uint8_t i2c, const struct pios_i2c_txn txn_list[], uint32_t num_txns)
{
I2CRecTypeDef *i2cx = &I2CRec;
PIOS_DEBUG_Assert(txn_list);
PIOS_DEBUG_Assert(num_txns);
if(i2cx->transfer_state.BUSY)
return 1;
struct pios_i2c_adapter * i2c_adapter;
return i2cx->transfer_error;
}
/**
* Stop the current transfer
* \param error the error that must be reported
*/
void PIOS_I2C_TerminateTransfer(uint32_t error)
{
I2CRecTypeDef *i2cx = &I2CRec;
/* Send stop condition */
I2C_GenerateSTOP(i2cx->base, ENABLE);
/* Re-initialize peripheral */
PIOS_I2C_InitPeripheral();
i2cx->transfer_error = error;
}
/**
* Waits until transfer is finished.
* \return error value of the transfer
*/
int32_t PIOS_I2C_TransferWait(void)
{
I2CRecTypeDef *i2cx = &I2CRec;
DebugPinHigh(DEBUG_PIN_WAIT);
i2c_adapter = find_i2c_adapter_by_id(i2c);
PIOS_DEBUG_Assert(i2c_adapter);
#ifdef USE_FREERTOS
/* Lock the bus */
xSemaphoreTake(i2c_adapter->sem_busy, portMAX_DELAY);
#endif /* USE_FREERTOS */
if (i2cx->transfer_state.BUSY)
{
// Wait until we see the ready signal
if (xSemaphoreTake(i2cx->sem_readySignal, PIOS_I2C_TIMEOUT_VALUE/portTICK_RATE_MS) == pdTRUE)
{
// OK, got the semaphore, release it again
Assert(i2cx->transfer_state.BUSY == 0);
}
else
{
PIOS_I2C_TerminateTransfer(I2C_ERROR_TIMEOUT);
}
}
PIOS_DEBUG_Assert(i2c_adapter->curr_state == I2C_STATE_STOPPED);
i2c_adapter->first_txn = &txn_list[0];
i2c_adapter->last_txn = &txn_list[num_txns - 1];
i2c_adapter->active_txn = i2c_adapter->first_txn;
#ifdef USE_FREERTOS
/* Make sure the done/ready semaphore is consumed before we start */
xSemaphoreTake(i2c_adapter->sem_ready, portMAX_DELAY);
#endif
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_START);
/* Wait for the transfer to complete */
#ifdef USE_FREERTOS
xSemaphoreTake(i2c_adapter->sem_ready, portMAX_DELAY);
xSemaphoreGive(i2c_adapter->sem_ready);
#else
uint32_t repeat_ctr = PIOS_I2C_TIMEOUT_VALUE*100; // FIXME
uint16_t last_buffer_ix = i2cx->buffer_ix;
if (i2cx->transfer_state.BUSY)
{
while(--repeat_ctr > 0)
{
/* Check if buffer index has changed - if so, reload repeat counter */
if(i2cx->buffer_ix != last_buffer_ix) {
repeat_ctr = PIOS_I2C_TIMEOUT_VALUE;
last_buffer_ix = i2cx->buffer_ix;
}
/* Get transfer state */
int32_t check_state = PIOS_I2C_TransferCheck();
/* Exit if transfer finished */
if(check_state <= 0)
{
DebugPinLow(DEBUG_PIN_WAIT);
return check_state;
}
}
/* Timeout error - something is stalling... */
PIOS_I2C_TerminateTransfer(I2C_ERROR_TIMEOUT);
}
#endif
DebugPinLow(DEBUG_PIN_WAIT);
return i2cx->transfer_error;
}
/**
* Perform a transfer. No previous transfer should be ongoing when this function is called.
* When the function returns, the transfer is finished.
* See PIOS_I2C_StartTransfer() for details on the parameters.
* \return 0 no error
* \return < 0 on errors
*
*/
int32_t PIOS_I2C_Transfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len)
{
PIOS_I2C_StartTransfer(transfer, address, buffer, len);
return PIOS_I2C_TransferWait();
}
/**
* Starts a new transfer. No previous transfer should be ongoing when this function is called.
* When this function returns, the new transfer is ongoing. PIOS_I2C_TransferWait() should be called
* to wait for the transfer to finish and to retrieve the result code.
* \param[in] transfer type:<BR>
* <UL>
* <LI>I2C_Read: a common Read transfer
* <LI>I2C_Write: a common Write transfer
* <LI>I2C_Write_WithoutStop: don't send stop condition after transfer to allow
* a restart condition (e.g. used to access EEPROMs)
* \param[in] address of I2C device (bit 0 always cleared)
* \param[in] *buffer pointer to transmit/receive buffer
* \param[in] len number of bytes which should be transmitted/received
*/
void PIOS_I2C_StartTransfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len)
{
I2CRecTypeDef *i2cx = &I2CRec;
// Should not be busy
if (i2cx->transfer_state.BUSY)
{
Assert(0);
return;
}
/* Spin waiting for the transfer to finish */
while (i2c_adapter->curr_state != I2C_STATE_STOPPED);
#endif /* USE_FREERTOS */
#ifdef USE_FREERTOS
// Consume Ready semaphore in case it would be there for some reason
xSemaphoreTake(i2cx->sem_readySignal, 0);
Assert(xSemaphoreTake(i2cx->sem_readySignal, 0) == pdFALSE);
#endif
/* Unlock the bus */
xSemaphoreGive(i2c_adapter->sem_busy);
#endif /* USE_FREERTOS */
// Clear state
i2cx->transfer_state.ALL = 0;
i2cx->transfer_error = 0;
// Set buffer length and start index
i2cx->buffer_len = len;
i2cx->buffer_ix = 0;
if(transfer == I2C_Read)
{
/* Take new address/buffer/len */
/* Set bit 0 for read operation */
i2cx->i2c_address = address | 1;
/* Ensure that previous TX buffer won't be accessed */
i2cx->tx_buffer_ptr = NULL;
i2cx->rx_buffer_ptr = buffer;
// Ack the bytes we will be getting
I2C_AcknowledgeConfig(i2cx->base, ENABLE);
}
else if(transfer == I2C_Write || transfer == I2C_Write_WithoutStop)
{
/* Take new address/buffer/len */
/* Clear bit 0 for write operation */
i2cx->i2c_address = address & 0xfe;
i2cx->tx_buffer_ptr = buffer;
/* Ensure that nothing will be received */
i2cx->rx_buffer_ptr = NULL;
/* Option to skip stop-condition generation after successful write */
i2cx->transfer_state.WRITE_WITHOUT_STOP = transfer == I2C_Write_WithoutStop ? 1 : 0;
}
else
{
i2cx->transfer_error = I2C_ERROR_UNSUPPORTED_TRANSFER_TYPE;
return;
}
// Start the transfer
I2C_GenerateSTART(i2cx->base, ENABLE);
TransferStart(i2cx);
}
/**
* Internal function for handling IIC event interrupts
*/
static void EV_IRQHandler(I2CRecTypeDef *i2cx)
{
uint8_t b;
uint32_t event;
DebugPinHigh(DEBUG_PIN_ISR);
// Update state
i2cx->transfer_state.INIRQ = 1;
#ifdef USE_FREERTOS
i2cx->xHigherPriorityTaskWoken = pdFALSE;
#endif
/* Read SR1 and SR2 at the beginning (if not done so, flags may get lost) */
event = I2C_GetLastEvent(i2cx->base);
/* The order of the handling blocks is chosen by test results @ 1MHZ */
/* Don't change this order */
/* RxNE set, will be cleared by reading/writing DR */
/* Note: also BTF will be reset after a read of SR1 (TxE flag) followed by either read/write DR */
/* Or a START or STOP condition generated */
/* Failsave: really requested a receive transfer? If not, continue to check TXE flag, if not set, */
/* We'll end up in the unexpected event handler. */
if(event & I2C_FLAG_RXNE && i2cx->rx_buffer_ptr != NULL) {
/* Get received data */
b = I2C_ReceiveData(i2cx->base);
/* Failsave: still place in buffer? */
if(i2cx->buffer_ix < i2cx->buffer_len) {
i2cx->rx_buffer_ptr[i2cx->buffer_ix++] = b;
}
/* Last byte received, disable interrupts and return. */
if(i2cx->transfer_state.STOP_REQUESTED) {
TransferEnd(i2cx);
goto isr_return;
}
/* Request NAK and stop condition before receiving last data */
if(i2cx->buffer_ix >= i2cx->buffer_len-1) {
/* Request NAK */
I2C_AcknowledgeConfig(i2cx->base, DISABLE);
/* Request stop condition */
I2C_GenerateSTOP(i2cx->base, ENABLE);
i2cx->transfer_state.STOP_REQUESTED = 1;
}
goto isr_return;
}
/* ADDR set, TRA flag not set (indicates transmitter/receiver mode). */
/* ADDR will be cleared by a read of SR1 followed by a read of SR2 (done by I2C_GetLastEvent) */
/* If transmitter mode is selected (TRA set), we go on, TXE will be catched to send the first byte */
if((event & I2C_FLAG_ADDR) && !(event & I2C_FLAG_TRA)) {
/* Address sent (receiver mode), receiving first byte - check if we already have to request NAK/Stop */
if(i2cx->buffer_len == 1) {
/* Request NAK */
I2C_AcknowledgeConfig(i2cx->base, DISABLE);
/* Request stop condition */
I2C_GenerateSTOP(i2cx->base, ENABLE);
i2cx->transfer_state.STOP_REQUESTED = 1;
}
goto isr_return;
}
/* TxE set, will be cleared by writing DR, or after START or STOP was generated */
/* This handling also applies for BTF, as TXE will alway be set if BTF is. */
/* Note: also BTF will be reset after a read of SR1 (TxE flag) followed by either read/write DR */
/* Or a START or STOP condition generated */
if(event & I2C_FLAG_TXE) {
/* Last byte already sent, disable interrupts and return. */
if(i2cx->transfer_state.STOP_REQUESTED) {
TransferEnd(i2cx);
goto isr_return;
}
if(i2cx->buffer_ix < i2cx->buffer_len) {
/* Checking tx_buffer_ptr for NULL is a failsafe measure. */
I2C_SendData(i2cx->base, (i2cx->tx_buffer_ptr == NULL) ? 0 : i2cx->tx_buffer_ptr[i2cx->buffer_ix++]);
goto isr_return;
}
/* Peripheral is transfering last byte, request stop condition / */
/* On write-without-stop transfer-type, request start condition instead */
i2cx->transfer_state.STOP_REQUESTED = 1;
if(!i2cx->transfer_state.WRITE_WITHOUT_STOP)
{
I2C_GenerateSTOP(i2cx->base, ENABLE);
}
else
{
DebugPinHigh(2);
}
if(i2cx->buffer_len == 0) {
TransferEnd(i2cx);
} else {
/* Disable the I2C_IT_BUF interrupt after sending the last buffer data */
/* (last EV8) to not allow a new interrupt just with TxE - only BTF will generate it */
/* If this is not done, BUSY will be cleared before the transfer is finished */
I2C_ITConfig(i2cx->base, I2C_IT_BUF, DISABLE);
}
goto isr_return;
}
// Send address
/* SB set, cleared by reading SR1 (done by I2C_GetLastEvent) followed by writing DR register */
if(event & I2C_FLAG_SB) {
/* Don't send address if stop was requested (WRITE_WITHOUT_STOP - mode, start condition was sent) */
/* We have to wait for the application to start the next transfer */
if(i2cx->transfer_state.STOP_REQUESTED) {
TransferEnd(i2cx);
DebugPinLow(DEBUG_PIN_ISR);
goto isr_return;
}
/* Send IIC address */
I2C_Send7bitAddress(i2cx->base, i2cx->i2c_address,
(i2cx->i2c_address & 1)
? I2C_Direction_Receiver
: I2C_Direction_Transmitter);
goto isr_return;
}
DebugPinHigh(DEBUG_PIN_ASSERT);DebugPinLow(DEBUG_PIN_ASSERT);
//
// FredericG: Despite the comments below, it seems to me that this situation can happen and can
// be ignored without ill effects...
// For now this condition does not stop the transfer, but further investigation in needed
//
// Assert(0);
//
// /* This code is only reached if something got wrong, e.g. interrupt handler is called too late, */
// /* The device reset itself (while testing, it was always event 0x00000000). we have to stop the transfer, */
// /* Else read/write of corrupt data may be the result. */
//
// /* Notify error */
// PIOS_I2C_UnexpectedEvent = event;
// i2cx->transfer_error = I2C_ERROR_UNEXPECTED_EVENT;
//
// TransferEnd(i2cx);
//
// /* Do dummy read to send NAK + STOP condition */
// I2C_AcknowledgeConfig(i2cx->base, DISABLE);
// b = I2C_ReceiveData(i2cx->base);
// I2C_GenerateSTOP(i2cx->base, ENABLE);
isr_return:
// Cause task-switch when needed
#ifdef USE_FREERTOS
portEND_SWITCHING_ISR(i2cx->xHigherPriorityTaskWoken);
#endif
// Update state
i2cx->transfer_state.INIRQ = 0;
DebugPinLow(DEBUG_PIN_ISR);
}
/**
* Internal function for handling IIC error interrupts
*/
static void ER_IRQHandler(I2CRecTypeDef *i2cx)
{
/* Read SR1 and SR2 at the beginning (if not done so, flags may get lost) */
uint32_t event = I2C_GetLastEvent(i2cx->base);
i2cx->transfer_state.INIRQ = 1;
/* Note that only one error number is available */
/* The order of these checks defines the priority */
/* Bus error (start/stop condition during read */
/* Unlikely, should only be relevant for slave mode?) */
if(event & I2C_FLAG_BERR) {
I2C_ClearITPendingBit(i2cx->base, I2C_IT_BERR);
i2cx->transfer_error = I2C_ERROR_BUS;
}
/* Arbitration lost */
if(event & I2C_FLAG_ARLO) {
I2C_ClearITPendingBit(i2cx->base, I2C_IT_ARLO);
i2cx->transfer_error = I2C_ERROR_ARBITRATION_LOST;
}
/* No acknowledge received from slave (e.g. slave not connected) */
if(event & I2C_FLAG_AF) {
I2C_ClearITPendingBit(i2cx->base, I2C_IT_AF);
i2cx->transfer_error = I2C_ERROR_SLAVE_NOT_CONNECTED;
/* Send stop condition to release bus */
I2C_GenerateSTOP(i2cx->base, ENABLE);
}
/* Notify that transfer has finished (due to the error) */
TransferEnd(i2cx);
i2cx->transfer_state.INIRQ = 0;
}
/* Interrupt vectors */
PIOS_I2C_IRQ_EV_HANDLER
{
EV_IRQHandler((I2CRecTypeDef *)&I2CRec);
}
PIOS_I2C_IRQ_ER_HANDLER
{
ER_IRQHandler((I2CRecTypeDef *)&I2CRec);
return TRUE;
}
#endif
void PIOS_I2C_EV_IRQ_Handler(uint8_t i2c)
{
struct pios_i2c_adapter * i2c_adapter;
i2c_adapter = find_i2c_adapter_by_id(i2c);
PIOS_DEBUG_Assert(i2c_adapter);
uint32_t event = I2C_GetLastEvent(i2c_adapter->cfg->regs);
switch (event) {
case (I2C_EVENT_MASTER_MODE_SELECT | 0x40):
/* Unexplained event: EV5 + RxNE : Extraneous Rx. Probably a late NACK from previous read. */
/* Clean up the extra Rx until the root cause is identified and just keep going */
(void) I2C_ReceiveData(i2c_adapter->cfg->regs);
/* Fall through */
case I2C_EVENT_MASTER_MODE_SELECT: /* EV5 */
switch (i2c_adapter->active_txn->rw) {
case PIOS_I2C_TXN_READ:
if (i2c_adapter->active_txn == i2c_adapter->last_txn) {
/* Final transaction */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_LAST_TXN_READ);
} else if (i2c_adapter->active_txn < i2c_adapter->last_txn) {
/* More transactions follow */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_MORE_TXN_READ);
} else {
PIOS_DEBUG_Assert(0);
}
break;
case PIOS_I2C_TXN_WRITE:
if (i2c_adapter->active_txn == i2c_adapter->last_txn) {
/* Final transaction */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_LAST_TXN_WRITE);
} else if (i2c_adapter->active_txn < i2c_adapter->last_txn) {
/* More transactions follow */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_MORE_TXN_WRITE);
} else {
PIOS_DEBUG_Assert(0);
}
break;
default:
PIOS_DEBUG_Assert(0);
break;
}
break;
case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: /* EV6 */
case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: /* EV6 */
switch (i2c_adapter->last_byte - i2c_adapter->active_byte + 1) {
case 0:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_0);
break;
case 1:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_1);
break;
case 2:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_2);
break;
default:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_GT_2);
break;
}
break;
case 0x80: /* TxE only. TRA + MSL + BUSY have been cleared before we got here. */
/* Ignore */
{
static volatile bool halt = FALSE;
while (halt);
}
break;
case 0: /* Unexplained spurious event. Not sure what to do here. */
case 0x40: /* RxNE only. MSL + BUSY have already been cleared by HW. */
case 0x44: /* RxNE + BTF. MSL + BUSY have already been cleared by HW. */
case I2C_EVENT_MASTER_BYTE_RECEIVED: /* EV7 */
case (I2C_EVENT_MASTER_BYTE_RECEIVED | 0x4): /* EV7 + BTF */
case I2C_EVENT_MASTER_BYTE_TRANSMITTED: /* EV8_2 */
case 0x84: /* TxE + BTF. EV8_2 but TRA + MSL + BUSY have already been cleared by HW. */
switch (i2c_adapter->last_byte - i2c_adapter->active_byte + 1) {
case 0:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_0);
break;
case 1:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_1);
break;
case 2:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_2);
break;
default:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_GT_2);
break;
}
break;
case I2C_EVENT_MASTER_BYTE_TRANSMITTING: /* EV8 */
/* Ignore this event and wait for TRANSMITTED in case we can't keep up */
break;
default:
PIOS_DEBUG_Assert(0);
break;
}
i2c_adapter_process_auto(i2c_adapter);
}
void PIOS_I2C_ER_IRQ_Handler(uint8_t i2c)
{
struct pios_i2c_adapter * i2c_adapter;
i2c_adapter = find_i2c_adapter_by_id(i2c);
PIOS_DEBUG_Assert(i2c_adapter);
/* Fail hard on any errors for now */
PIOS_DEBUG_Assert(0);
}
/**
* @}
* @}
*/
*/

View File

@ -75,12 +75,12 @@ extern void PIOS_BMP085_StartADC(ConversionTypeTypeDef Type);
extern void PIOS_BMP085_ReadADC(void);
extern int16_t PIOS_BMP085_GetTemperature(void);
extern int32_t PIOS_BMP085_GetPressure(void);
extern int32_t PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len);
extern int32_t PIOS_BMP085_Write(uint8_t address, uint8_t buffer);
extern bool PIOS_BMP085_Read(uint8_t address, uint8_t *buffer, uint8_t len);
extern bool PIOS_BMP085_Write(uint8_t address, uint8_t buffer);
#endif /* PIOS_BMP085_H */
/**
* @}
* @}
*/
*/

View File

@ -103,12 +103,12 @@ extern void PIOS_HMC5843_Init(void);
extern void PIOS_HMC5843_Config(PIOS_HMC5843_ConfigTypeDef *HMC5843_Config_Struct);
extern void PIOS_HMC5843_ReadMag(int16_t out[3]);
extern void PIOS_HMC5843_ReadID(uint8_t out[4]);
extern int32_t PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len);
extern int32_t PIOS_HMC5843_Write(uint8_t address, uint8_t buffer);
extern bool PIOS_HMC5843_Read(uint8_t address, uint8_t *buffer, uint8_t len);
extern bool PIOS_HMC5843_Write(uint8_t address, uint8_t buffer);
#endif /* PIOS_HMC5843_H */
/**
* @}
* @}
*/
*/

View File

@ -31,42 +31,28 @@
#ifndef PIOS_I2C_H
#define PIOS_I2C_H
/* Local defines */
#define I2C_ERROR_INVALID_PORT -1
#define I2C_ERROR_GENERAL -2
#define I2C_ERROR_UNSUPPORTED_TRANSFER_TYPE -3
#define I2C_ERROR_TIMEOUT -4
#define I2C_ERROR_ARBITRATION_LOST -5
#define I2C_ERROR_BUS -6
#define I2C_ERROR_SLAVE_NOT_CONNECTED -7
#define I2C_ERROR_UNEXPECTED_EVENT -8
#define I2C_ERROR_RX_BUFFER_OVERRUN -9
#define I2C_ERROR_BUSY -10
/* Global Types */
enum pios_i2c_txn_direction {
PIOS_I2C_TXN_READ,
PIOS_I2C_TXN_WRITE
};
typedef enum {
I2C_Read,
I2C_Write,
I2C_Write_WithoutStop
} I2CTransferTypeDef;
struct pios_i2c_txn {
uint16_t addr;
enum pios_i2c_txn_direction rw;
uint32_t len;
uint8_t * buf;
};
/* Public Functions */
extern int32_t PIOS_I2C_Init(void);
extern int32_t PIOS_I2C_TransferCheck(void);
extern int32_t PIOS_I2C_TransferWait(void);
extern int32_t PIOS_I2C_Transfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len);
extern void PIOS_I2C_StartTransfer(I2CTransferTypeDef transfer, uint8_t address, uint8_t *buffer, uint16_t len);
#ifdef PIOS_INCLUDE_FREERTOS
extern bool PIOS_I2C_LockDevice(portTickType timeout);
extern void PIOS_I2C_UnlockDevice(void);
#endif // PIOS_INCLUDE_FREERTOS
extern bool PIOS_I2C_Transfer(uint8_t i2c, const struct pios_i2c_txn txn_list[], uint32_t num_txns);
extern void PIOS_I2C_EV_IRQ_Handler(uint8_t i2c);
extern void PIOS_I2C_ER_IRQ_Handler(uint8_t i2c);
#endif /* PIOS_I2C_H */
/**
* @}
* @}
*/
*/

View File

@ -0,0 +1,96 @@
/**
******************************************************************************
*
* @file pios_i2c_priv.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* Parts by Thorsten Klose (tk@midibox.org)
* @brief I2C private definitions.
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef PIOS_I2C_PRIV_H
#define PIOS_I2C_PRIV_H
#include <pios.h>
#include <pios_stm32.h>
struct pios_i2c_adapter_cfg {
I2C_TypeDef * regs;
I2C_InitTypeDef init;
uint32_t transfer_timeout_ms;
struct stm32_gpio scl;
struct stm32_gpio sda;
struct stm32_irq event;
struct stm32_irq error;
};
enum i2c_adapter_state {
I2C_STATE_FAULTED = 0, /* Must be zero so undefined transitions land here */
I2C_STATE_STOPPED,
I2C_STATE_STOPPING,
I2C_STATE_STARTING,
I2C_STATE_R_MORE_TXN_ADDR,
I2C_STATE_R_MORE_TXN_PRE_ONE,
I2C_STATE_R_MORE_TXN_PRE_FIRST,
I2C_STATE_R_MORE_TXN_PRE_MIDDLE,
I2C_STATE_R_MORE_TXN_PRE_LAST,
I2C_STATE_R_MORE_TXN_POST_LAST,
I2C_STATE_R_LAST_TXN_ADDR,
I2C_STATE_R_LAST_TXN_PRE_ONE,
I2C_STATE_R_LAST_TXN_PRE_FIRST,
I2C_STATE_R_LAST_TXN_PRE_MIDDLE,
I2C_STATE_R_LAST_TXN_PRE_LAST,
I2C_STATE_R_LAST_TXN_POST_LAST,
I2C_STATE_W_MORE_TXN_ADDR,
I2C_STATE_W_MORE_TXN_MIDDLE,
I2C_STATE_W_MORE_TXN_LAST,
I2C_STATE_W_LAST_TXN_ADDR,
I2C_STATE_W_LAST_TXN_MIDDLE,
I2C_STATE_W_LAST_TXN_LAST,
I2C_STATE_NUM_STATES /* Must be last */
};
struct pios_i2c_adapter {
const struct pios_i2c_adapter_cfg * const cfg;
void (*callback)(uint8_t, uint8_t);
#ifdef PIOS_INCLUDE_FREERTOS
xSemaphoreHandle sem_busy;
xSemaphoreHandle sem_ready;
#endif
enum i2c_adapter_state curr_state;
const struct pios_i2c_txn * first_txn;
const struct pios_i2c_txn * active_txn;
const struct pios_i2c_txn * last_txn;
uint8_t * active_byte;
uint8_t * last_byte;
};
extern struct pios_i2c_adapter pios_i2c_adapters[];
extern uint8_t pios_i2c_num_adapters;
#endif /* PIOS_I2C_PRIV_H */