Commit 9a711c22 authored by Henrique Silva's avatar Henrique Silva

I2C Driver Documentation

Comments on each function, macro and typedefs were added, following
Doxygen structure
parent 6ccc47d8
This diff is collapsed.
/*
* i2c.c
*
* AFCIPMI --
*
* Copyright (C) 2015 Henrique Silva <henrique.silva@lnls.br>
......@@ -19,6 +17,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* @file i2c.c
* @author Henrique Silva <henrique.silva@lnls.br>, LNLS
* @date August 2015
*
* @brief Implementation of a generic I2C driver using FreeRTOS features
*/
/* FreeRTOS includes */
#include "FreeRTOS.h"
#include "task.h"
......@@ -34,7 +40,7 @@
#include "board_defs.h"
/* Project definitions */
/* TODO: Move these definitions to a LPC17x specific header, so we have a more generic macro */
/*! @todo Move these definitions to a LPC17x specific header, so we have a more generic macro */
#define I2CSTAT( id ) LPC_I2Cx(id)->STAT
#define I2CCONSET( id, val ) LPC_I2Cx(id)->CONSET = val
#define I2CCONCLR( id, val ) LPC_I2Cx(id)->CONCLR = val
......@@ -44,7 +50,7 @@
#define I2CADDR_READ( id ) LPC_I2Cx(id)->ADR0
#define I2CMASK( id, val ) LPC_I2Cx(id)->MASK[0] = val
/* Configuration struct for each I2C interface */
/*! @brief Configuration struct for each I2C interface */
xI2C_Config i2c_cfg[] = {
{
.reg = LPC_I2C0,
......@@ -96,11 +102,14 @@ xI2C_Config i2c_cfg[] = {
}
};
/*! @brief Array of mutexes to access #i2c_cfg global struct
*
* Each I2C interface has its own mutex and it must be taken
* before setting/reading any field from #i2c_cfg struct,
* since it's used by multiple tasks simultaneously
*/
static SemaphoreHandle_t I2C_mutex[3];
/* Function prototypes */
void vI2C_ISR( uint8_t i2c_id );
void I2C0_IRQHandler( void )
{
vI2C_ISR( I2C0 );
......@@ -116,7 +125,14 @@ void I2C2_IRQHandler( void )
vI2C_ISR( I2C2 );
}
/* I2C_ISR - I2C interrupt service routine */
/*! @brief I2C common interrupt service routine
*
* I2STAT register is handled inside this function, a state-machine-like implementation for I2C interface.
*
* When a full message is trasmitted or received, the task whose handle is written to #i2c_cfg is notified, unblocking it. It also happens when an error occurs.
* @warning Slave Transmitter mode states are not implemented in this driver and are just ignored.
*/
void vI2C_ISR( uint8_t i2c_id );
void vI2C_ISR( uint8_t i2c_id )
{
/* Declare local variables */
......@@ -132,8 +148,7 @@ void vI2C_ISR( uint8_t i2c_id )
i2c_cfg[i2c_id].rx_cnt = 0;
i2c_cfg[i2c_id].tx_cnt = 0;
/* Write Slave Address in the I2C bus, if there's nothing
* to transmit, the last bit (R/W) will be set to 1
*/
* to transmit, the last bit (R/W) will be set to 1 */
I2CDAT_WRITE( i2c_id, ( i2c_cfg[i2c_id].msg.addr << 1 ) | ( i2c_cfg[i2c_id].msg.tx_len == 0 ) );
break;
......@@ -266,28 +281,14 @@ void vI2C_ISR( uint8_t i2c_id )
}
}
/***************
* vI2C_Init() *
***************
* I2C initialization code called by main
*/
void vI2CInit( I2C_ID_T i2c_id, I2C_Mode mode )
{
char pcI2C_Tag[4];
uint8_t sla_addr;
sprintf( pcI2C_Tag, "I2C%u", i2c_id );
/* Initialize I2C corresponding pins with the following characteristics:
* -> Open Drain
* -> Function #3 (If I2C0, it's function #1)
* -> No pull-up nor pull-down
* Configure and init the I2C interruption, with its priority set to one
* level below the maximum FreeRTOS priority, so the interruption service
* can access the API and manage the semaphore
*/
/** @todo Maybe wrap these functions, or use some board-specific defines
* so this code is generic enough to be applied on other hardware
/*! @todo Maybe wrap these functions, or use some board-specific defines
* so this code is generic enough to be applied on other hardware.
* Example: (if using LPC17xx and LPCOpen library)
* @code
* #define PIN_FUNC_CFG( port, pin, func ) Chip_IOCON_PinMux(...)
......@@ -358,7 +359,7 @@ i2c_err xI2CWrite( I2C_ID_T i2c_id, uint8_t addr, uint8_t * tx_data, uint8_t tx_
xSemaphoreGive( I2C_Mutex[i2c_id] );
/* Trigger the i2c interruption */
/* Is it safe to set the flag right now? Won't it stop another ongoing message that is being received for example? */
/* @bug Is it safe to set the flag right now? Won't it stop another ongoing message that is being received for example? */
I2CCONSET( i2c_id, ( I2C_I2EN | I2C_STA ) );
if ( ulTaskNotifyTake( pdTRUE, portMAX_DELAY ) == pdTRUE ){
......@@ -372,10 +373,9 @@ i2c_err xI2CWrite( I2C_ID_T i2c_id, uint8_t addr, uint8_t * tx_data, uint8_t tx_
i2c_err xI2CRead( I2C_ID_T i2c_id, uint8_t addr, uint8_t * rx_data, uint8_t rx_len )
{
/* Take the semaphore to access shared memory */
/* Take the mutex to access shared memory */
xSemaphoreTake( I2C_Mutex[i2c_id], portMAX_DELAY );
/* Maybe use memcpy to pass the tx buffer to the global structure */
i2c_cfg[i2c_id].msg.i2c_id = i2c_id;
i2c_cfg[i2c_id].msg.addr = addr;
i2c_cfg[i2c_id].msg.tx_len = 0;
......@@ -395,7 +395,7 @@ i2c_err xI2CRead( I2C_ID_T i2c_id, uint8_t addr, uint8_t * rx_data, uint8_t rx_l
configASSERT(i2c_cfg[i2c_id].msg.rx_data);
xSemaphoreTake( I2C_Mutex[i2c_id], portMAX_DELAY );
/* Copy the received message to the given pointer */
/* Copy the received message to the given pointer */
memcpy (rx_data, i2c_cfg[i2c_id].msg.rx_data, i2c_cfg[i2c_id].msg.rx_len );
xSemaphoreGive( I2C_Mutex[i2c_id] );
}
......@@ -404,11 +404,13 @@ i2c_err xI2CRead( I2C_ID_T i2c_id, uint8_t addr, uint8_t * rx_data, uint8_t rx_l
uint8_t xI2CSlaveTransfer ( I2C_ID_T i2c_id, uint8_t * rx_data, uint32_t timeout )
{
/* Take the mutex to access shared memory */
xSemaphoreTake( I2C_Mutex[i2c_id], portMAX_DELAY );
/* Register this task as the one to be notified when a message comes */
i2c_cfg[i2c_id].slave_task_id = xTaskGetCurrentTaskHandle();
/* Relase mutex */
xSemaphoreGive( I2C_Mutex[i2c_id] );
/* Function blocks here until a message is received */
......@@ -429,58 +431,59 @@ uint8_t xI2CSlaveTransfer ( I2C_ID_T i2c_id, uint8_t * rx_data, uint32_t timeout
return i2c_cfg[i2c_id].msg.rx_len;
}
/* Based on coreipm/coreipm/mmc.c
* Author: Gokhan Sozmen
/*
*==============================================================
* MMC ADDRESSING
*==============================================================
*/
/*
3.2.1 Geographic Address [2..0] (GA[2..0]) the state of each GA signal is
represented by G (grounded), U (unconnected), or P (pulled up to Management Power).
The MMC drives P1 low and reads the GA lines. The MMC then drives P1 high and
reads the GA lines. Any line that changes state between the two reads indicate
an unconnected (U) pin.
The IPMB-L address of a Module can be calculated as (70h + Site Number x 2).
G = 0, P = 1, U = 2
GGG 000 0 0x70
GGP 001 1 0x8A
GGU 002 2 0x72
GPG 010 3 0x8E
GPP 011 4 0x92
GPU 012 5 0x90
GUG 020 6 0x74
GUP 021 7 0x8C
GUU 022 8 0x76
PGG 100 9 0x98
PGP 101 10 0x9C
PGU 102 11 0x9A
PPG 110 12 0xA0
PPP 111 13 0xA4
PPU 112 14 0x88
PUG 120 15 0x9E
PUP 121 16 0x86
PUU 122 17 0x84
UGG 200 18 0x78
UGP 201 19 0x94
UGU 202 20 0x7A
UPG 210 21 0x96
UPP 211 22 0x82
UPU 212 23 0x80
UUG 220 24 0x7C
UUP 221 25 0x7E
UUU 222 26 0xA2
/*! @brief Table holding all possible address values in IPMB specification
* @see get_ipmb_addr()
*/
#define IPMBL_TABLE_SIZE 27
unsigned char IPMBL_TABLE[IPMBL_TABLE_SIZE] = {
0x70, 0x8A, 0x72, 0x8E, 0x92, 0x90, 0x74, 0x8C, 0x76,
0x98, 0x9C, 0x9A, 0xA0, 0xA4, 0x88, 0x9E, 0x86, 0x84,
0x78, 0x94, 0x7A, 0x96, 0x82, 0x80, 0x7C, 0x7E, 0xA2 };
/*! The state of each GA signal is represented by G (grounded), U (unconnected),
* or P (pulled up to Management Power).
*
* The MMC drives P1 low and reads the GA lines. The MMC then drives P1 high and
* reads the GA lines. Any line that changes state between the two reads indicate
* an unconnected (U) pin.
*
* The IPMB-L address of a Module can be calculated as (70h + Site Number x 2). <br>
* G = 0, P = 1, U = 2 <br>
* | Pin | Ternary | Decimal | Address |
* |:---:|:-------:|:-------:|:-------:|
* | GGG | 000 | 0 | 0x70 |
* | GGP | 001 | 1 | 0x8A |
* | GGU | 002 | 2 | 0x72 |
* | GPG | 010 | 3 | 0x8E |
* | GPP | 011 | 4 | 0x92 |
* | GPU | 012 | 5 | 0x90 |
* | GUG | 020 | 6 | 0x74 |
* | GUP | 021 | 7 | 0x8C |
* | GUU | 022 | 8 | 0x76 |
* | PGG | 100 | 9 | 0x98 |
* | PGP | 101 | 10 | 0x9C |
* | PGU | 102 | 11 | 0x9A |
* | PPG | 110 | 12 | 0xA0 |
* | PPP | 111 | 13 | 0xA4 |
* | PPU | 112 | 14 | 0x88 |
* | PUG | 120 | 15 | 0x9E |
* | PUP | 121 | 16 | 0x86 |
* | PUU | 122 | 17 | 0x84 |
* | UGG | 200 | 18 | 0x78 |
* | UGP | 201 | 19 | 0x94 |
* | UGU | 202 | 20 | 0x7A |
* | UPG | 210 | 21 | 0x96 |
* | UPP | 211 | 22 | 0x82 |
* | UPU | 212 | 23 | 0x80 |
* | UUG | 220 | 24 | 0x7C |
* | UUP | 221 | 25 | 0x7E |
* | UUU | 222 | 26 | 0xA2 |
*/
uint8_t get_ipmb_addr( void )
{
uint8_t ga0, ga1, ga2;
......@@ -496,6 +499,11 @@ uint8_t get_ipmb_addr( void )
* meaning that it is unconnected */
Chip_GPIO_SetPinState(LPC_GPIO, GA_PORT, GA_TEST_PIN, 0);
/*! @todo Add a delay before reading the pin, as some other users have reported
* that there's a 500ns "lag" in the Cortex-M3 IO, which could cause mistaken address readings.
* @see <https://groups.google.com/forum/#!topic/comp.arch.embedded/3r53OGmdCRQ>
*/
if ( ga0 != Chip_GPIO_GetPinState(LPC_GPIO, GA_PORT, GA0_PIN) )
{
ga0 = UNCONNECTED;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment