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

i2c: force bus free on warm starts

Since the i2c bus is bidirectional, there are certain
states (eg. part way through a read) where the slave
device is in control of driving the SDA line.

On a cold start (power on), the slave devices are all
quiescent and will not drive the bus.  However, on a warm
start (eg. watchdog or jtag restart), it is possible that as
the CPU boots, the slave device may be holding the SDA line
low.  This is a bus busy condition and will prevent the I2C
bus master in the CPU from being able to seize the bus during
init.

The fix for this is to clock the i2c bus sufficiently to ensure
that the the slave device finishes its transaction and releases
the bus.

Once the slave has released the bus, the bus master can properly
initialize and assert a STOP condition on the bus.

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1349 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
stac 2010-08-21 16:19:02 +00:00 committed by stac
parent 3ad47be673
commit 98822bff98
3 changed files with 48 additions and 1 deletions

View File

@ -579,7 +579,45 @@ int32_t PIOS_I2C_Init(void)
/* Initialize the state machine */ /* Initialize the state machine */
i2c_adapter_fsm_init(i2c_adapter); i2c_adapter_fsm_init(i2c_adapter);
/* Initialize the GPIO pins */ /* Make sure the bus is free by clocking it until any slaves release the bus. */
GPIO_InitTypeDef scl_gpio_init;
scl_gpio_init = i2c_adapter->cfg->scl.init;
scl_gpio_init.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
GPIO_Init(i2c_adapter->cfg->scl.gpio, &scl_gpio_init);
GPIO_InitTypeDef sda_gpio_init;
sda_gpio_init = i2c_adapter->cfg->sda.init;
sda_gpio_init.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_SetBits(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin);
GPIO_Init(i2c_adapter->cfg->sda.gpio, &sda_gpio_init);
i2c_adapter->bus_needed_reset = false;
for (uint8_t i = 0; i < 9; i++) {
/* Set clock high and wait for any clock stretching to finish. */
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
while (GPIO_ReadInputDataBit(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin) == Bit_RESET);
PIOS_DELAY_WaituS(2);
if (GPIO_ReadInputDataBit(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin) != Bit_SET) {
/* Note that the bus needed reset so we can tell this was useful */
i2c_adapter->bus_needed_reset = true;
}
/* Set clock low */
GPIO_ResetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
PIOS_DELAY_WaituS(2);
}
/* Set data and clock high and wait for any clock stretching to finish. */
GPIO_SetBits(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin);
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
while (GPIO_ReadInputDataBit(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin) == Bit_RESET);
/* Wait for data to be high */
while (GPIO_ReadInputDataBit(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin) != Bit_SET);
/* Bus signals are guaranteed to be high (ie. free) after this point */
/* Initialize the GPIO pins to the peripheral function */
GPIO_Init(i2c_adapter->cfg->scl.gpio, &(i2c_adapter->cfg->scl.init)); GPIO_Init(i2c_adapter->cfg->scl.gpio, &(i2c_adapter->cfg->scl.init));
GPIO_Init(i2c_adapter->cfg->sda.gpio, &(i2c_adapter->cfg->sda.init)); GPIO_Init(i2c_adapter->cfg->sda.gpio, &(i2c_adapter->cfg->sda.init));
@ -765,6 +803,7 @@ void PIOS_I2C_EV_IRQ_Handler(uint8_t i2c)
break; break;
case I2C_EVENT_MASTER_BYTE_TRANSMITTING: /* EV8 */ case I2C_EVENT_MASTER_BYTE_TRANSMITTING: /* EV8 */
/* Ignore this event and wait for TRANSMITTED in case we can't keep up */ /* Ignore this event and wait for TRANSMITTED in case we can't keep up */
goto skip_event;
break; break;
default: default:
PIOS_DEBUG_Assert(0); PIOS_DEBUG_Assert(0);
@ -772,6 +811,9 @@ void PIOS_I2C_EV_IRQ_Handler(uint8_t i2c)
} }
i2c_adapter_process_auto(i2c_adapter); i2c_adapter_process_auto(i2c_adapter);
skip_event:
;
} }
void PIOS_I2C_ER_IRQ_Handler(uint8_t i2c) void PIOS_I2C_ER_IRQ_Handler(uint8_t i2c)

View File

@ -31,6 +31,8 @@
#ifndef PIOS_I2C_H #ifndef PIOS_I2C_H
#define PIOS_I2C_H #define PIOS_I2C_H
#include <stdbool.h>
/* Global Types */ /* Global Types */
enum pios_i2c_txn_direction { enum pios_i2c_txn_direction {
PIOS_I2C_TXN_READ, PIOS_I2C_TXN_READ,

View File

@ -29,6 +29,7 @@
#include <pios.h> #include <pios.h>
#include <pios_stm32.h> #include <pios_stm32.h>
#include <stdbool.h>
struct pios_i2c_adapter_cfg { struct pios_i2c_adapter_cfg {
I2C_TypeDef * regs; I2C_TypeDef * regs;
@ -81,6 +82,8 @@ struct pios_i2c_adapter {
xSemaphoreHandle sem_ready; xSemaphoreHandle sem_ready;
#endif #endif
bool bus_needed_reset;
enum i2c_adapter_state curr_state; enum i2c_adapter_state curr_state;
const struct pios_i2c_txn * first_txn; const struct pios_i2c_txn * first_txn;
const struct pios_i2c_txn * active_txn; const struct pios_i2c_txn * active_txn;