summaryrefslogblamecommitdiffstats
path: root/bsps/arm/lpc176x/start/adc.c
blob: 49288c07c6d0ab1c985a7c4bf28dc9bb4d7338ca (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   
        












                                                            
                                        




                                

                                             





























































































































































































































                                                                               
/**
 * @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 <rtems/status-checks.h>
#include <bsp/adc.h>
#include <bsp/adc-defs.h>

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;
}