1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-11-30 08:24:11 +01:00
LibrePilot/flight/PiOS/STM32F10x/pios_i2c.c
stac 47c36b3da0 i2c: fix error in FSM for restarted reads
The transition from the ADDR state to the read state
was broken for non-final reads.  The FSM diagram was
also wrong for this transition.

Since reads are always the last transaction in a sequence
in our current usage, this doesn't actually fix any known
bugs.

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1350 ebee16cc-31ac-478f-84a7-5cbb03baadba
2010-08-21 16:19:06 +00:00

834 lines
28 KiB
C

/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_I2C I2C Functions
* @brief STM32 Hardware dependent I2C functionality
* @{
*
* @file pios_i2c.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org)
* @brief I2C Enable/Disable routines
* @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
*/
/* Project Includes */
#include "pios.h"
#if defined(PIOS_INCLUDE_I2C)
#if defined(PIOS_INCLUDE_FREERTOS)
#define USE_FREERTOS
#endif
#include <pios_i2c_priv.h>
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 */
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_FIRST,
[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,
},
},
/*
* Write with restart
*/
[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,
},
},
[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,
},
},
[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);
#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 */
}
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);
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]);
I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
}
/* 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);
PIOS_DEBUG_Assert(i2c_adapter->active_txn->rw == PIOS_I2C_TXN_READ);
I2C_Send7bitAddress(i2c_adapter->cfg->regs, (i2c_adapter->active_txn->addr)<<1, 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)<<1, 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
* \param[in] mode currently only mode 0 supported
* \return < 0 if initialisation failed
*/
int32_t PIOS_I2C_Init(void)
{
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
/*
* 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
/* Initialize the state machine */
i2c_adapter_fsm_init(i2c_adapter);
/* 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->sda.gpio, &(i2c_adapter->cfg->sda.init));
/* 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;
}
#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;
}
bool PIOS_I2C_Transfer(uint8_t i2c, const struct pios_i2c_txn txn_list[], uint32_t num_txns)
{
PIOS_DEBUG_Assert(txn_list);
PIOS_DEBUG_Assert(num_txns);
struct pios_i2c_adapter * i2c_adapter;
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 */
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
/* Spin waiting for the transfer to finish */
while (i2c_adapter->curr_state != I2C_STATE_STOPPED);
#endif /* USE_FREERTOS */
#ifdef USE_FREERTOS
/* Unlock the bus */
xSemaphoreGive(i2c_adapter->sem_busy);
#endif /* USE_FREERTOS */
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 */
goto skip_event;
break;
default:
PIOS_DEBUG_Assert(0);
break;
}
i2c_adapter_process_auto(i2c_adapter);
skip_event:
;
}
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);
}
/**
* @}
* @}
*/