420 lines
10 KiB
C
420 lines
10 KiB
C
/******************************************************************//**
|
|
* @file drv_acmp.c
|
|
* @brief ACMP (analog comparator) driver of RT-Thread RTOS for EFM32
|
|
* COPYRIGHT (C) 2011, RT-Thread Development Team
|
|
* @author onelife
|
|
* @version 0.4 beta
|
|
**********************************************************************
|
|
* @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-02-21 onelife Initial creation for EFM32
|
|
* 2011-06-17 onelife Modify init and control function for efm32lib v2 upgrading
|
|
*********************************************************************/
|
|
|
|
/******************************************************************//**
|
|
* @addtogroup efm32
|
|
* @{
|
|
*********************************************************************/
|
|
|
|
/* Includes -------------------------------------------------------------------*/
|
|
#include "board.h"
|
|
#include "drv_acmp.h"
|
|
|
|
#if (defined(RT_USING_ACMP0) || defined(RT_USING_ACMP1))
|
|
/* Private typedef -------------------------------------------------------------*/
|
|
/* Private define --------------------------------------------------------------*/
|
|
/* Private macro --------------------------------------------------------------*/
|
|
#ifdef RT_ACMP_DEBUG
|
|
#define acmp_debug(format,args...) rt_kprintf(format, ##args)
|
|
#else
|
|
#define acmp_debug(format,args...)
|
|
#endif
|
|
|
|
/* Private variables ------------------------------------------------------------*/
|
|
#ifdef RT_USING_ACMP0
|
|
static struct rt_device acmp0_device;
|
|
#endif
|
|
|
|
#ifdef RT_USING_ACMP1
|
|
static struct rt_device acmp1_device;
|
|
#endif
|
|
|
|
/* Private function prototypes ---------------------------------------------------*/
|
|
ACMP_WarmTime_TypeDef efm32_acmp_WarmTimeCalc(rt_uint32_t hfperFreq);
|
|
|
|
/* Private functions ------------------------------------------------------------*/
|
|
/******************************************************************//**
|
|
* @brief
|
|
* Initialize ACMP device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @return
|
|
* Error code
|
|
*********************************************************************/
|
|
static rt_err_t rt_acmp_init(rt_device_t dev)
|
|
{
|
|
RT_ASSERT(dev != RT_NULL);
|
|
|
|
struct efm32_acmp_device_t *acmp;
|
|
|
|
acmp = (struct efm32_acmp_device_t *)(dev->user_data);
|
|
|
|
acmp->hook.cbFunc = RT_NULL;
|
|
acmp->hook.userPtr = RT_NULL;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
/******************************************************************//**
|
|
* @brief
|
|
* Configure ACMP device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] cmd
|
|
* ACMP control command
|
|
*
|
|
* @param[in] args
|
|
* Arguments
|
|
*
|
|
* @return
|
|
* Error code
|
|
*********************************************************************/
|
|
static rt_err_t rt_acmp_control(
|
|
rt_device_t dev,
|
|
rt_uint8_t cmd,
|
|
void *args)
|
|
{
|
|
RT_ASSERT(dev != RT_NULL);
|
|
|
|
struct efm32_acmp_device_t *acmp;
|
|
|
|
acmp = (struct efm32_acmp_device_t *)(dev->user_data);
|
|
|
|
switch (cmd)
|
|
{
|
|
case RT_DEVICE_CTRL_SUSPEND:
|
|
/* Suspend device */
|
|
dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
|
|
ACMP_Disable(acmp->acmp_device);
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_RESUME:
|
|
/* Resume device */
|
|
dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
|
|
ACMP_Enable(acmp->acmp_device);
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_ACMP_INIT:
|
|
{
|
|
rt_bool_t int_en = false;
|
|
struct efm32_acmp_control_t *control;
|
|
|
|
control = (struct efm32_acmp_control_t *)args;
|
|
acmp_debug("ACMP: control -> init start\n");
|
|
|
|
/* Configure ACMPn */
|
|
if (control->init == RT_NULL)
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
ACMP_Init(acmp->acmp_device, control->init);
|
|
ACMP_ChannelSet(acmp->acmp_device, control->negInput, control->posInput);
|
|
if (control->output != RT_NULL)
|
|
{
|
|
ACMP_GPIOSetup(
|
|
acmp->acmp_device,
|
|
control->output->location,
|
|
control->output->enable,
|
|
control->output->invert);
|
|
int_en = true;
|
|
}
|
|
if (control->hook.cbFunc != RT_NULL)
|
|
{
|
|
acmp->hook.cbFunc = control->hook.cbFunc;
|
|
acmp->hook.userPtr = control->hook.userPtr;
|
|
int_en = true;
|
|
}
|
|
|
|
if (int_en)
|
|
{
|
|
/* Enable edge interrupt */
|
|
ACMP_IntEnable(acmp->acmp_device, ACMP_IEN_EDGE);
|
|
ACMP_IntClear(acmp->acmp_device, ACMP_IFC_EDGE);
|
|
|
|
/* Enable ACMP0/1 interrupt vector in NVIC */
|
|
NVIC_ClearPendingIRQ(ACMP0_IRQn);
|
|
NVIC_SetPriority(ACMP0_IRQn, EFM32_IRQ_PRI_DEFAULT);
|
|
NVIC_EnableIRQ(ACMP0_IRQn);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_ACMP_OUTPUT:
|
|
*((rt_bool_t *)args) = \
|
|
(acmp->acmp_device->STATUS & ACMP_STATUS_ACMPOUT) ? true : false;
|
|
break;
|
|
|
|
default:
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
/******************************************************************//**
|
|
* @brief
|
|
* Register ACMP device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] device
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] name
|
|
* Device name
|
|
*
|
|
* @param[in] flag
|
|
* Configuration flags
|
|
*
|
|
* @param[in] acmp
|
|
* Pointer to ACMP device descriptor
|
|
*
|
|
* @return
|
|
* Error code
|
|
*********************************************************************/
|
|
rt_err_t rt_hw_acmp_register(
|
|
rt_device_t device,
|
|
const char *name,
|
|
rt_uint32_t flag,
|
|
struct efm32_acmp_device_t *acmp)
|
|
{
|
|
RT_ASSERT(device != RT_NULL);
|
|
|
|
device->type = RT_Device_Class_Char; /* fixme: should be acmp type */
|
|
device->rx_indicate = RT_NULL;
|
|
device->tx_complete = RT_NULL;
|
|
device->init = rt_acmp_init;
|
|
device->open = RT_NULL;
|
|
device->close = RT_NULL;
|
|
device->read = RT_NULL;
|
|
device->write = RT_NULL;
|
|
device->control = rt_acmp_control;
|
|
device->user_data = acmp;
|
|
|
|
/* register a character device */
|
|
return rt_device_register(device, name, flag);
|
|
}
|
|
|
|
/******************************************************************//**
|
|
* @brief
|
|
* ACMP edge trigger interrupt handler
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*********************************************************************/
|
|
void rt_hw_acmp_isr(rt_device_t dev)
|
|
{
|
|
RT_ASSERT(dev != RT_NULL);
|
|
|
|
struct efm32_acmp_device_t *acmp;
|
|
|
|
acmp = (struct efm32_acmp_device_t *)(dev->user_data);
|
|
|
|
if (acmp->hook.cbFunc != RT_NULL)
|
|
{
|
|
(acmp->hook.cbFunc)(acmp->hook.userPtr);
|
|
}
|
|
}
|
|
|
|
/******************************************************************//**
|
|
* @brief
|
|
* Initialize the specified ACMP unit
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] device
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] unitNumber
|
|
* Unit number
|
|
*
|
|
* @return
|
|
* Pointer to ACMP device
|
|
*********************************************************************/
|
|
static struct efm32_acmp_device_t *rt_hw_acmp_unit_init(
|
|
rt_device_t device,
|
|
rt_uint8_t unitNumber)
|
|
{
|
|
struct efm32_acmp_device_t *acmp;
|
|
efm32_irq_hook_init_t hook;
|
|
CMU_Clock_TypeDef acmpClock;
|
|
|
|
do
|
|
{
|
|
/* Allocate device */
|
|
acmp = rt_malloc(sizeof(struct efm32_acmp_device_t));
|
|
if (acmp == RT_NULL)
|
|
{
|
|
acmp_debug("ACMP err: no mem for ACMP%d\n", unitNumber);
|
|
break;
|
|
}
|
|
|
|
/* Initialization */
|
|
if (unitNumber >= ACMP_COUNT)
|
|
{
|
|
break;
|
|
}
|
|
switch (unitNumber)
|
|
{
|
|
case 0:
|
|
acmp->acmp_device = ACMP0;
|
|
acmpClock = (CMU_Clock_TypeDef)cmuClock_ACMP0;
|
|
break;
|
|
|
|
case 1:
|
|
acmp->acmp_device = ACMP1;
|
|
acmpClock = (CMU_Clock_TypeDef)cmuClock_ACMP1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Enable ACMP clock */
|
|
CMU_ClockEnable(acmpClock, true);
|
|
|
|
/* Reset */
|
|
ACMP_Reset(acmp->acmp_device);
|
|
|
|
/* Config interrupt and NVIC */
|
|
hook.type = efm32_irq_type_acmp;
|
|
hook.unit = unitNumber;
|
|
hook.cbFunc = rt_hw_acmp_isr;
|
|
hook.userPtr = device;
|
|
efm32_irq_hook_register(&hook);
|
|
|
|
return acmp;
|
|
} while(0);
|
|
|
|
if (acmp)
|
|
{
|
|
rt_free(acmp);
|
|
}
|
|
rt_kprintf("ACMP: Init failed!\n");
|
|
return RT_NULL;
|
|
}
|
|
|
|
/******************************************************************//**
|
|
* @brief
|
|
* Initialize all ACMP module related hardware and register ACMP device to kernel
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
*********************************************************************/
|
|
void rt_hw_acmp_init(void)
|
|
{
|
|
struct efm32_acmp_device_t *acmp;
|
|
|
|
#ifdef RT_USING_ACMP0
|
|
if ((acmp = rt_hw_acmp_unit_init(&acmp0_device, 0)) != RT_NULL)
|
|
{
|
|
rt_hw_acmp_register(&acmp0_device, RT_ACMP0_NAME, EFM32_NO_DATA, acmp);
|
|
}
|
|
#endif
|
|
|
|
#ifdef RT_USING_ACMP1
|
|
if ((acmp = rt_hw_acmp_unit_init(&acmp1_device, 1)) != RT_NULL)
|
|
{
|
|
rt_hw_acmp_register(&acmp1_device, RT_ACMP1_NAME, EFM32_NO_DATA, acmp);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
/******************************************************************//**
|
|
* @brief
|
|
* Calculate the warm-up time value providing at least 10us
|
|
*
|
|
* @param[in] hfperFreq
|
|
* Frequency in Hz of reference HFPER clock. Set to 0 to use currently defined HFPER clock
|
|
* setting
|
|
*
|
|
* @return
|
|
* Warm-up time value to use for ACMP in order to achieve at least 10us
|
|
*********************************************************************/
|
|
ACMP_WarmTime_TypeDef efm32_acmp_WarmTimeCalc(rt_uint32_t hfperFreq)
|
|
{
|
|
if (!hfperFreq)
|
|
{
|
|
hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER);
|
|
|
|
/* Just in case, make sure we get non-zero freq for below calculation */
|
|
if (!hfperFreq)
|
|
{
|
|
hfperFreq = 1;
|
|
}
|
|
}
|
|
|
|
/* Determine number of HFPERCLK cycle >= 10us */
|
|
if (4 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime4;
|
|
}
|
|
else if (8 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime8;
|
|
}
|
|
else if (16 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime16;
|
|
}
|
|
else if (32 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime32;
|
|
}
|
|
else if (64 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime64;
|
|
}
|
|
else if (128 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime128;
|
|
}
|
|
else if (256 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime256;
|
|
}
|
|
else if (512 * 1000000 / hfperFreq > 10)
|
|
{
|
|
return acmpWarmTime512;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
/******************************************************************//**
|
|
* @}
|
|
*********************************************************************/
|