rt-thread/bsp/efm32/drv_iic.c
onelife.real ea6d73f140 *** EFM32 branch ***
1. Upgrade Cortex driver library (CMSIS -> CMSIS & Device): version 2.3.2 -> 3.0.1 & 3.0.0
 - Remove "bsp/efm32/Libraries/CMSIS/Lib/ARM", "bsp/efm32/Libraries/CMSIS/Lib/G++" and "bsp/efm32/Libraries/CMSIS/SVD" to save space
2. Upgrade EFM32 driver libraries (efm32lib -> emlib): version 2.3.2 -> 3.0.0
 - Remove "bsp/efm32/Libraries/Device/EnergyMicro/EFM32LG" and "bsp/efm32/Libraries/Device/EnergyMicro/EFM32TG" to save space
3. Upgrade EFM32GG_DK3750 development kit driver library: version 1.2.2 -> 2.0.1
4. Upgrade EFM32_Gxxx_DK development kit driver library: version 1.7.3 -> 2.0.1
5. Add energy management unit driver and test code
6. Modify linker script and related code to compatible with new version of libraries
7. Change EFM32 branch version number to 1.0
8. Add photo frame demo application

git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2122 bbd45198-f89e-11dd-88c7-29a3b14d5316
2012-05-18 04:40:40 +00:00

884 lines
24 KiB
C

/***************************************************************************//**
* @file drv_iic.c
* @brief Serial API of RT-Thread RTOS for EFM32
* COPYRIGHT (C) 2012, RT-Thread Development Team
* @author onelife
* @version 1.0
*******************************************************************************
* @section License
* The license and distribution terms for this file may be found in the file
* LICENSE in this distribution or at http://www.rt-thread.org/license/LICENSE
*******************************************************************************
* @section Change Logs
* Date Author Notes
* 2011-01-06 onelife Initial creation for EFM32
* 2011-06-17 onelife Modify init function for EFM32 library v2.0.0
* upgrading
* 2011-07-11 onelife Add lock (semaphore) to prevent simultaneously
* access
* 2011-08-04 onelife Change the usage of the second parameter of Read
* and Write functions from (seldom used) "Offset" to "Slave address"
* 2011-08-04 onelife Add a timer to prevent from forever waiting
* 2011-11-29 onelife Modify init function for EFM32 library v2.2.2
* upgrading
* 2011-12-27 onelife Utilize "I2C_PRESENT" and "I2C_COUNT"
* 2011-12-27 onelife Change IIC read format
******************************************************************************/
/***************************************************************************//**
* @addtogroup efm32
* @{
******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "board.h"
#include "hdl_interrupt.h"
#include "drv_iic.h"
#if (defined(RT_USING_IIC0) || defined(RT_USING_IIC1))
#if !defined(I2C_PRESENT)
#error "IIC module is not available"
#endif
/* Private typedef -----------------------------------------------------------*/
struct efm32_iic_block
{
struct rt_device device;
struct rt_semaphore lock;
struct rt_timer timer;
};
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
#ifdef RT_IIC_DEBUG
#define iic_debug(format,args...) rt_kprintf(format, ##args)
#else
#define iic_debug(format,args...)
#endif
/* Private variables ---------------------------------------------------------*/
#ifdef RT_USING_IIC0
#if (RT_USING_IIC0 > EFM32_IIC_LOCATION_COUNT)
#error "Wrong location number"
#endif
static struct efm32_iic_block iic0;
#endif
#ifdef RT_USING_IIC1
#if (I2C_COUNT <= 1)
#error "Wrong unit number"
#endif
#if (RT_USING_IIC1 > EFM32_IIC_LOCATION_COUNT)
#error "Wrong location number"
#endif
static struct efm32_iic_block iic1;
#endif
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/***************************************************************************//**
* @brief
* Initialize IIC device
*
* @details
*
* @note
*
* @param[in] dev
* Pointer to device descriptor
*
* @return
* Error code
******************************************************************************/
static rt_err_t rt_iic_init (rt_device_t dev)
{
struct efm32_iic_device_t* iic;
iic = (struct efm32_iic_device_t*)dev->user_data;
if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
{
/* Enable IIC */
I2C_Enable(iic->iic_device, true);
iic->rx_buffer = RT_NULL;
iic->state = 0;
dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
return RT_EOK;
}
/***************************************************************************//**
* @brief
* Open IIC device
*
* @details
*
* @note
*
* @param[in] dev
* Pointer to device descriptor
*
* @param[in] oflag
* Device open flag
*
* @return
* Error code
******************************************************************************/
static rt_err_t rt_iic_open(rt_device_t dev, rt_uint16_t oflag)
{
RT_ASSERT(dev != RT_NULL);
struct efm32_iic_device_t *iic;
iic = (struct efm32_iic_device_t *)(dev->user_data);
iic->counter++;
iic_debug("IIC: Open with flag %x\n", oflag);
return RT_EOK;
}
/***************************************************************************//**
* @brief
* Close IIC device
*
* @details
*
* @note
*
* @param[in] dev
* Pointer to device descriptor
*
* @return
* Error code
******************************************************************************/
static rt_err_t rt_iic_close(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
struct efm32_iic_device_t *iic;
iic = (struct efm32_iic_device_t *)(dev->user_data);
if (--iic->counter == 0)
{
rt_free(iic->rx_buffer->data_ptr);
rt_free(iic->rx_buffer);
iic->rx_buffer = RT_NULL;
}
return RT_EOK;
}
/***************************************************************************//**
* @brief
* Read from IIC device
*
* @details
*
* @note
*
* @param[in] dev
* Pointer to device descriptor
*
* @param[in] pos
* Slave address
*
* @param[in] buffer
* Poniter to the buffer
*
* @param[in] size
* Buffer size in byte
*
* @return
* Error code
******************************************************************************/
static rt_size_t rt_iic_read (
rt_device_t dev,
rt_off_t pos,
void* buffer,
rt_size_t size)
{
rt_err_t err_code;
rt_size_t read_size;
struct efm32_iic_device_t* iic;
I2C_TransferSeq_TypeDef seq;
I2C_TransferReturn_TypeDef ret;
if (!size)
{
return 0;
}
err_code = RT_EOK;
read_size = 0;
iic = (struct efm32_iic_device_t*)dev->user_data;
/* Lock device */
if (rt_hw_interrupt_check())
{
ret = rt_sem_take(iic->lock, RT_WAITING_NO);
}
else
{
ret = rt_sem_take(iic->lock, RT_WAITING_FOREVER);
}
if (ret != RT_EOK)
{
return ret;
}
if (iic->state & IIC_STATE_MASTER)
{
seq.addr = (rt_uint16_t)pos << 1;
if (*(rt_uint8_t *)buffer == IIC_OP_READ_ONLY)
{
seq.flags = I2C_FLAG_READ;
/* Set read buffer pointer and size */
seq.buf[0].data = (rt_uint8_t *)buffer;
seq.buf[0].len = size;
}
else
{
seq.flags = I2C_FLAG_WRITE_READ;
/* Set register to be read */
seq.buf[0].data = (rt_uint8_t *)buffer;
seq.buf[0].len = 1;
/* Set read buffer pointer and size */
seq.buf[1].data = (rt_uint8_t *)buffer;
seq.buf[1].len = size;
}
/* Do a polled transfer */
iic->timeout = false;
rt_timer_stop(iic->timer);
rt_timer_start(iic->timer);
ret = I2C_TransferInit(iic->iic_device, &seq);
while ((ret == i2cTransferInProgress) && !iic->timeout)
{
ret = I2C_Transfer(iic->iic_device);
}
if (ret != i2cTransferDone)
{
iic_debug("IIC: read error %x\n", ret);
iic_debug("IIC: read address %x\n", seq.addr);
iic_debug("IIC: read data0 %x -> %x\n", seq.buf[0].data, *seq.buf[0].data);
iic_debug("IIC: read len0 %x\n", seq.buf[0].len);
iic_debug("IIC: read data1 %x -> %x\n", seq.buf[1].data, *seq.buf[1].data);
iic_debug("IIC: read len1 %x\n", seq.buf[1].len);
err_code = (rt_err_t)ret;
}
else
{
read_size = size;
iic_debug("IIC: read size %d\n", read_size);
}
}
else
{
rt_uint8_t* ptr;
ptr = buffer;
/* interrupt mode Rx */
while (size)
{
rt_base_t level;
struct efm32_iic_int_mode_t *int_rx;
int_rx = iic->rx_buffer;
/* disable interrupt */
level = rt_hw_interrupt_disable();
if (int_rx->read_index != int_rx->save_index)
{
/* read a character */
*ptr++ = int_rx->data_ptr[int_rx->read_index];
size--;
/* move to next position */
int_rx->read_index ++;
if (int_rx->read_index >= IIC_RX_BUFFER_SIZE)
{
int_rx->read_index = 0;
}
}
else
{
/* set error code */
err_code = -RT_EEMPTY;
/* enable interrupt */
rt_hw_interrupt_enable(level);
break;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
read_size = (rt_uint32_t)ptr - (rt_uint32_t)buffer;
iic_debug("IIC: slave read size %d\n", read_size);
}
/* Unlock device */
rt_sem_release(iic->lock);
/* set error code */
rt_set_errno(err_code);
return read_size;
}
/***************************************************************************//**
* @brief
* Write to IIC device
*
* @details
*
* @note
*
* @param[in] dev
* Pointer to device descriptor
*
* @param[in] pos
* Slave address
*
* @param[in] buffer
* Poniter to the buffer
*
* @param[in] size
* Buffer size in byte
*
* @return
* Error code
******************************************************************************/
static rt_size_t rt_iic_write (
rt_device_t dev,
rt_off_t pos,
const void* buffer,
rt_size_t size)
{
rt_err_t err_code;
rt_size_t write_size;
struct efm32_iic_device_t* iic;
I2C_TransferSeq_TypeDef seq;
I2C_TransferReturn_TypeDef ret;
if (!size)
{
return 0;
}
err_code = RT_EOK;
write_size = 0;
iic = (struct efm32_iic_device_t*)dev->user_data;
/* Lock device */
if (rt_hw_interrupt_check())
{
ret = rt_sem_take(iic->lock, RT_WAITING_NO);
}
else
{
ret = rt_sem_take(iic->lock, RT_WAITING_FOREVER);
}
if (ret != RT_EOK)
{
return ret;
}
if (iic->state & IIC_STATE_MASTER)
{
seq.addr = (rt_uint16_t)pos << 1;
seq.flags = I2C_FLAG_WRITE;
/* Set write buffer pointer and size */
seq.buf[0].data = (rt_uint8_t *)buffer;
seq.buf[0].len = size;
}
else
{
// TODO: Slave mode TX
}
/* Do a polled transfer */
iic->timeout = false;
rt_timer_stop(iic->timer);
rt_timer_start(iic->timer);
ret = I2C_TransferInit(iic->iic_device, &seq);
while ((ret == i2cTransferInProgress) && !iic->timeout)
{
ret = I2C_Transfer(iic->iic_device);
}
if (ret != i2cTransferDone)
{
err_code = (rt_err_t)ret;
}
else
{
write_size = size;
}
/* Unlock device */
rt_sem_release(iic->lock);
/* set error code */
rt_set_errno(err_code);
return write_size;
}
/***************************************************************************//**
* @brief
* Configure IIC device
*
* @details
*
* @note
*
* @param[in] dev
* Pointer to device descriptor
*
* @param[in] cmd
* IIC control command
*
* @param[in] args
* Arguments
*
* @return
* Error code
******************************************************************************/
static rt_err_t rt_iic_control (
rt_device_t dev,
rt_uint8_t cmd,
void *args)
{
RT_ASSERT(dev != RT_NULL);
rt_err_t ret;
struct efm32_iic_device_t *iic;
iic = (struct efm32_iic_device_t*)dev->user_data;
/* Lock device */
if (rt_hw_interrupt_check())
{
ret = rt_sem_take(iic->lock, RT_WAITING_NO);
}
else
{
ret = rt_sem_take(iic->lock, RT_WAITING_FOREVER);
}
if (ret != RT_EOK)
{
return ret;
}
switch (cmd)
{
case RT_DEVICE_CTRL_SUSPEND:
/* suspend device */
dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
I2C_Enable(iic->iic_device, false);
break;
case RT_DEVICE_CTRL_RESUME:
/* resume device */
dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
I2C_Enable(iic->iic_device, true);
break;
case RT_DEVICE_CTRL_IIC_SETTING:
{
/* change device setting */
struct efm32_iic_control_t *control;
control = (struct efm32_iic_control_t *)args;
iic->state = control->config & (IIC_STATE_MASTER | IIC_STATE_BROADCAST);
iic->address = control->address << 1;
if (!(iic->state & IIC_STATE_MASTER))
{
if (iic->rx_buffer == RT_NULL)
{
iic->rx_buffer = rt_malloc(sizeof(struct efm32_iic_int_mode_t));
if (iic->rx_buffer == RT_NULL)
{
iic_debug("IIC err: no MEM for IIC RX structure\n");
return -RT_ENOMEM;
}
/* Allocate RX buffer */
if ((iic->rx_buffer->data_ptr = \
rt_malloc(IIC_RX_BUFFER_SIZE)) == RT_NULL)
{
iic_debug("IIC err: no MEM for IIC RX buffer\n");
rt_free(iic->rx_buffer);
return -RT_ENOMEM;
}
rt_memset(iic->rx_buffer->data_ptr, 0, IIC_RX_BUFFER_SIZE);
iic->rx_buffer->data_size = IIC_RX_BUFFER_SIZE;
iic->rx_buffer->read_index = 0;
iic->rx_buffer->save_index = 0;
}
/* Enable slave mode */
I2C_SlaveAddressSet(iic->iic_device, iic->address);
I2C_SlaveAddressMaskSet(iic->iic_device, 0xFF);
iic->iic_device->CTRL |= I2C_CTRL_SLAVE | I2C_CTRL_AUTOACK | I2C_CTRL_AUTOSN;
/* Enable interrupts */
I2C_IntEnable(iic->iic_device, I2C_IEN_ADDR | I2C_IEN_RXDATAV | I2C_IEN_SSTOP);
I2C_IntClear(iic->iic_device, _I2C_IFC_MASK);
/* Enable I2Cn interrupt vector in NVIC */
if (dev == &iic0.device)
{
NVIC_ClearPendingIRQ(I2C0_IRQn);
NVIC_SetPriority(I2C0_IRQn, EFM32_IRQ_PRI_DEFAULT);
NVIC_EnableIRQ(I2C0_IRQn);
}
#if (I2C_COUNT > 1)
if (dev == &iic1.device)
{
NVIC_ClearPendingIRQ(I2C1_IRQn);
NVIC_SetPriority(I2C1_IRQn, EFM32_IRQ_PRI_DEFAULT);
NVIC_EnableIRQ(I2C1_IRQn);
}
#endif
}
}
break;
}
/* Unlock device */
rt_sem_release(iic->lock);
return RT_EOK;
}
/***************************************************************************//**
* @brief
* IIC timeout interrupt handler
*
* @details
*
* @note
*
* @param[in] parameter
* Parameter
******************************************************************************/
static void rt_iic_timer(void *timeout)
{
*(rt_bool_t *)timeout = true;
}
/***************************************************************************//**
* @brief
* Register IIC device
*
* @details
*
* @note
*
* @param[in] device
* Pointer to device descriptor
*
* @param[in] name
* Device name
*
* @param[in] flag
* Configuration flags
*
* @param[in] iic
* Pointer to IIC device descriptor
*
* @return
* Error code
******************************************************************************/
rt_err_t rt_hw_iic_register(
rt_device_t device,
const char *name,
rt_uint32_t flag,
struct efm32_iic_device_t *iic)
{
RT_ASSERT(device != RT_NULL);
if ((flag & RT_DEVICE_FLAG_DMA_TX) || (flag & RT_DEVICE_FLAG_DMA_RX) ||
(flag & RT_DEVICE_FLAG_INT_TX))
{
RT_ASSERT(0);
}
device->type = RT_Device_Class_I2C;
device->rx_indicate = RT_NULL;
device->tx_complete = RT_NULL;
device->init = rt_iic_init;
device->open = rt_iic_open;
device->close = rt_iic_close;
device->read = rt_iic_read;
device->write = rt_iic_write;
device->control = rt_iic_control;
device->user_data = iic;
/* register a character device */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | flag);
}
/***************************************************************************//**
* @brief
* IIC slave mode RX data valid interrupt handler
*
* @details
*
* @note
*
* @param[in] dev
* Pointer to device descriptor
******************************************************************************/
static void rt_hw_iic_slave_isr(rt_device_t dev)
{
struct efm32_iic_device_t *iic;
struct efm32_iic_int_mode_t *int_rx;
rt_uint32_t status;
volatile rt_uint32_t temp;
/* interrupt mode receive */
RT_ASSERT(dev->flag & RT_DEVICE_FLAG_INT_RX);
iic = (struct efm32_iic_device_t*)dev->user_data;
int_rx = iic->rx_buffer;
status = iic->iic_device->IF;
if (status & I2C_IF_ADDR)
{
/* Address Match */
/* Indicating that reception is started */
temp = iic->iic_device->RXDATA & 0xFFUL;
if ((temp != 0x00) || (iic->state & IIC_STATE_BROADCAST))
{
iic->state |= IIC_STATE_RX_BUSY;
}
}
else if (status & I2C_IF_RXDATAV)
{
if (iic->state & IIC_STATE_RX_BUSY)
{
rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* save character */
int_rx->data_ptr[int_rx->save_index] = \
(rt_uint8_t)(iic->iic_device->RXDATA & 0xFFUL);
int_rx->save_index ++;
if (int_rx->save_index >= IIC_RX_BUFFER_SIZE)
int_rx->save_index = 0;
/* if the next position is read index, discard this 'read char' */
if (int_rx->save_index == int_rx->read_index)
{
int_rx->read_index ++;
if (int_rx->read_index >= IIC_RX_BUFFER_SIZE)
{
int_rx->read_index = 0;
}
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
else
{
temp = iic->iic_device->RXDATA;
}
}
if(status & I2C_IF_SSTOP)
{
/* Stop received, reception is ended */
iic->state &= ~(rt_uint8_t)IIC_STATE_RX_BUSY;
}
}
/***************************************************************************//**
* @brief
* Initialize the specified IIC unit
*
* @details
*
* @note
*
* @param[in] unitNumber
* Unit number
*
* @param[in] location
* Pin location number
******************************************************************************/
static struct efm32_iic_device_t *rt_hw_iic_unit_init(
struct efm32_iic_block *block,
rt_uint8_t unitNumber,
rt_uint8_t location)
{
struct efm32_iic_device_t *iic;
CMU_Clock_TypeDef iicClock;
GPIO_Port_TypeDef port_scl, port_sda;
rt_uint32_t pin_scl, pin_sda;
I2C_Init_TypeDef init = I2C_INIT_DEFAULT;
efm32_irq_hook_init_t hook;
rt_uint8_t name[RT_NAME_MAX];
do
{
/* Allocate device */
iic = rt_malloc(sizeof(struct efm32_iic_device_t));
if (iic == RT_NULL)
{
iic_debug("IIC err: no MEM for IIC%d driver\n", unitNumber);
break;
}
iic->counter = 0;
iic->timer = &block->timer;
iic->timeout = false;
iic->state |= IIC_STATE_MASTER;
iic->address = 0x0000;
iic->rx_buffer = RT_NULL;
/* Initialization */
if (unitNumber >= I2C_COUNT)
{
break;
}
switch (unitNumber)
{
case 0:
iic->iic_device = I2C0;
iicClock = (CMU_Clock_TypeDef)cmuClock_I2C0;
port_scl = AF_I2C0_SCL_PORT(location);
pin_scl = AF_I2C0_SCL_PIN(location);
port_sda = AF_I2C0_SDA_PORT(location);
pin_sda = AF_I2C0_SDA_PIN(location);
break;
#if (I2C_COUNT > 1)
case 1:
iic->iic_device = I2C1;
iicClock = (CMU_Clock_TypeDef)cmuClock_I2C1;
port_scl = AF_I2C1_SCL_PORT(location);
pin_scl = AF_I2C1_SCL_PIN(location);
port_sda = AF_I2C1_SDA_PORT(location);
pin_sda = AF_I2C1_SDA_PIN(location);
break;
#endif
default:
break;
}
rt_sprintf(name, "iic%d", unitNumber);
/* Enabling clock */
CMU_ClockEnable(iicClock, true);
/* Reset */
I2C_Reset(iic->iic_device);
/* Config GPIO */
GPIO_PinModeSet(
port_scl,
pin_scl,
gpioModeWiredAndPullUpFilter,
1);
GPIO_PinModeSet(
port_sda,
pin_sda,
gpioModeWiredAndPullUpFilter,
1);
hook.type = efm32_irq_type_iic;
hook.unit = unitNumber;
hook.cbFunc = rt_hw_iic_slave_isr;
hook.userPtr = (void *)&block->device;
efm32_irq_hook_register(&hook);
/* Enable SDZ and SCL pins and set location */
iic->iic_device->ROUTE = I2C_ROUTE_SDAPEN | I2C_ROUTE_SCLPEN | \
(location << _I2C_ROUTE_LOCATION_SHIFT);
/* Initializing IIC */
init.enable = false;
I2C_Init(iic->iic_device, &init);
/* Abort current TX data and clear TX buffers */
iic->iic_device->CMD = I2C_CMD_ABORT | I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
/* Initialize lock */
iic->lock = &block->lock;
if (rt_sem_init(iic->lock, name, 1, RT_IPC_FLAG_FIFO) != RT_EOK)
{
break;
}
/* Initialize timer */
rt_timer_init(iic->timer, name, rt_iic_timer, &iic->timeout,
IIC_TIMEOUT_PERIOD, RT_TIMER_FLAG_ONE_SHOT);
return iic;
} while(0);
if (iic)
{
rt_free(iic);
}
iic_debug("IIC err: Unit %d init failed!\n", unitNumber);
return RT_NULL;
}
/***************************************************************************//**
* @brief
* Initialize all IIC module related hardware and register IIC device to kernel
*
* @details
*
* @note
******************************************************************************/
void rt_hw_iic_init(void)
{
struct efm32_iic_device_t *iic;
rt_uint32_t flag;
do
{
flag = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX;
/* Initialize and register iic0 */
if ((iic = rt_hw_iic_unit_init(&iic0, 0, RT_USING_IIC0)) != RT_NULL)
{
rt_hw_iic_register(&iic0.device, RT_IIC0_NAME, flag, iic);
}
else
{
break;
}
#if (I2C_COUNT > 1)
/* Initialize and register iic1 */
if ((iic = rt_hw_iic_unit_init(&iic1, 1, RT_USING_IIC1)) != RT_NULL)
{
rt_hw_iic_register(&iic1.device, RT_IIC1_NAME, flag, iic);
}
else
{
break;
}
#endif
iic_debug("IIC: H/W init OK!\n");
return;
} while (0);
rt_kprintf("IIC: H/W init failed!\n");
}
#endif /* (defined(RT_USING_IIC0) || defined(RT_USING_IIC1)) */
/***************************************************************************//**
* @}
******************************************************************************/