/** * @file * * @ingroup lpc176x * * @brief ADC library for the lpc176x bsp. */ /* * Copyright (c) 2014 Taller Technologies. * * @author Diaz Marcos (marcos.diaz@tallertechnologies.com) * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #include #include #include static lpc176x_adc_device *const adc_device = (lpc176x_adc_device *) AD0_BASE_ADDR; static const lpc176x_adc_pin_map adc_pinmap[ ADC_DEVICES_COUNT ] = { { .pin_number = 23u, .pin_function = LPC176X_PIN_FUNCTION_01 }, { .pin_number = 24u, .pin_function = LPC176X_PIN_FUNCTION_01 }, { .pin_number = 25u, .pin_function = LPC176X_PIN_FUNCTION_01 }, { .pin_number = 26u, .pin_function = LPC176X_PIN_FUNCTION_01 }, { .pin_number = 62u, .pin_function = LPC176X_PIN_FUNCTION_11 }, { .pin_number = 63u, .pin_function = LPC176X_PIN_FUNCTION_11 }, { .pin_number = 3u, .pin_function = LPC176X_PIN_FUNCTION_10 }, { .pin_number = 2u, .pin_function = LPC176X_PIN_FUNCTION_10 } }; /** * @brief Checks for a valid pin number for an ADC * * @param pin_number The pin to check * @param adc_number The returned ADC device corresponding to that pin. * * @return true if valid, false otherwise. */ static bool valid_pin_number ( const lpc176x_pin_number pin_number, lpc176x_adc_number *const adc_number ) { bool found = false; lpc176x_adc_number adc_device = ADC_0; while (!found && (adc_device < ADC_DEVICES_COUNT)) { if (adc_pinmap[adc_device].pin_number == pin_number) { *adc_number = adc_device; found = true; } ++adc_device; } return found; } /** * @brief Turns on the device and sets its clock divisor. * */ static void turn_on_and_set_clkdiv( void ) { const uint32_t clkdiv = LPC176X_CCLK / ( LPC176X_PCLKDIV * MAX_ADC_CLK ); adc_device->ADCR = ADC_CR_PDN | ADC_CR_CLKDIV( clkdiv ); } rtems_status_code adc_open( const lpc176x_pin_number pin_number ) { rtems_status_code sc = RTEMS_INVALID_NUMBER; lpc176x_adc_number adc_number = 0; if ( valid_pin_number( pin_number, &adc_number ) ) { sc = lpc176x_module_enable( LPC176X_MODULE_ADC, LPC176X_MODULE_PCLK_DEFAULT ); RTEMS_CHECK_SC( sc, "enable adc module" ); turn_on_and_set_clkdiv(); lpc176x_pin_select( adc_pinmap[ adc_number ].pin_number, adc_pinmap[ adc_number ].pin_function ); lpc176x_pin_set_mode( adc_pinmap[ adc_number ].pin_number, LPC176X_PIN_MODE_NONE ); } return sc; } rtems_status_code adc_close( void ) { adc_device->ADCR &= ~ADC_CR_PDN; return lpc176x_module_disable( LPC176X_MODULE_ADC ); } /** * @brief Starts the conversion for the given channel. * * @param number The channel to start the conversion. */ static inline void start_conversion( const lpc176x_adc_number number ) { adc_device->ADCR = ADC_CR_SEL_SET( adc_device->ADCR, ( 1 << number ) ) | ADC_CR_START_NOW; } /** * @brief Stops the conversion. * */ static inline void stop_conversion( void ) { adc_device->ADCR &= ~ADC_CR_START_NOW; } /** * @brief Gets float percentage of the result of a conversion. * * @param data The result of a conversion. * @return A float percentage (between 0.0f and 1.0f). */ static inline float get_float( const uint32_t data ) { return ( (float) data / (float) ADC_RANGE ); } /** * @brief Reads the ADC value for the given ADC number. * * @param adc_number Which ADC device read. * @return The read value. */ static uint32_t read( const lpc176x_adc_number adc_number ) { uint32_t data; start_conversion( adc_number ); do { data = adc_device->ADGDR; } while ( !ADC_DATA_CONVERSION_DONE( data ) ); stop_conversion(); return ADC_DR_VALUE( data ); } /** * @brief Checks if the passed parameters are ordered from lowest * to highest. * * @param a The suggested lowest value. * @param b The suggested middle value. * @param c The suggested highest value. * @return True if it is in the right order, false otherwise. */ static inline bool lowest_to_highest_ordered( const uint32_t a, const uint32_t b, const uint32_t c ) { return ( ( a <= b ) && ( b <= c ) ); } /** * @brief Gets median value from the three passed parameters. * * @param a The first parameter. * @param b The second parameter. * @param c The third parameter. * * @return The median of the three parameters. */ static uint32_t get_median( const uint32_t a, const uint32_t b, const uint32_t c ) { uint32_t median; if ( lowest_to_highest_ordered( a, b, c) || lowest_to_highest_ordered( c, b, a ) ) { median = b; } else if ( lowest_to_highest_ordered( b, a, c ) || lowest_to_highest_ordered( c, a, b ) ) { median = a; } else { median = c; } return median; } rtems_status_code adc_read( const lpc176x_pin_number pin_number , float *const result ) { rtems_status_code sc = RTEMS_INVALID_NUMBER; lpc176x_adc_number adc_number = 0; if ( valid_pin_number( pin_number, &adc_number ) ) { const uint32_t first = read( adc_number ); const uint32_t second = read( adc_number ); const uint32_t third = read( adc_number ); const uint32_t median = get_median( first, second, third ); *result = get_float( median ); sc = RTEMS_SUCCESSFUL; } return sc; }