/******************************************************************//** * @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 /******************************************************************//** * @} *********************************************************************/