tweaked detection to use photoresistor through an opamp

Dependents:   rgb_sensor_buffer

Fork of rgb_sensor by Milosch Meriac

rgb_sensor.cpp

Committer:
meriac
Date:
2014-06-28
Revision:
6:fc64a14a2f4a
Parent:
5:1fed2b68e661
Child:
8:88acb970df76

File content as of revision 6:fc64a14a2f4a:

/* Discrete RGB color sensor
 *
 * - uses single-channel light-dependent resistor (via ADC)
 *   and a RGB LED.
 * -  compensates background light
 *
 * Copyright (c) 2014 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <mbed.h>
#include <pinmap.h>
#include "rgb_sensor.h"

static const PinMap RGB_Sensor__PinMap_ADC[] = {
    {P0_23, ADC0_0, 1},
    {P0_24, ADC0_1, 1},
    {P0_25, ADC0_2, 1},
    {P0_26, ADC0_3, 1},
    {P1_30, ADC0_4, 3},
    {P1_31, ADC0_5, 3},
    {P0_2,  ADC0_7, 2},
    {P0_3,  ADC0_6, 2},
    {NC,    NC,     0}
};

DigitalOut g_dbg(LED1);

/* initialize globally */
RGB_Sensor* RGB_Sensor::m_global = NULL;

RGB_Sensor::RGB_Sensor(PinName red, PinName green, PinName blue, PinName adc)
    :m_red(red), m_green(green), m_blue(blue)
{
    uint32_t clkdiv;

    m_done=true;

    /* allow only one instance */
    if(m_global)
    {
        m_adc_channel = (ADCName)NC;
        return;
    }

    /* disable LED's */
    m_red   = !RGB_LED_ON;
    m_green = !RGB_LED_ON;
    m_blue  = !RGB_LED_ON;

    // enable ADC power
    LPC_SC->PCONP |= (1<<12);
    // set ADC clock to PCLK
    LPC_SC->PCLKSEL0 = (LPC_SC->PCLKSEL0 & ~(0x3UL<<24)) | (0x1UL<<24);

    // determine mapping
    m_adc_channel = (ADCName)pinmap_peripheral(adc, RGB_Sensor__PinMap_ADC);
    if (m_adc_channel == (ADCName)NC)
        return;

    // initialize ADC
    clkdiv = ((SystemCoreClock+RGB_MAX_ADC_CLK-1)/RGB_MAX_ADC_CLK)-1;    
    LPC_ADC->ADCR = (1UL<<m_adc_channel) | (clkdiv<<8) | (1UL<<21);

    // set up ADC IRQ
    NVIC_SetVector(ADC_IRQn, (uint32_t)&__adc_irq);
    LPC_ADC->ADINTEN = (1UL<<m_adc_channel);
    NVIC_EnableIRQ(ADC_IRQn);

    // propagate pin setting
    pinmap_pinout(adc, RGB_Sensor__PinMap_ADC);

    // remember this instance
    m_global = this;
}

RGB_Sensor::~RGB_Sensor(void)
{
    /* only deal with fully initialized objects */
    if(m_adc_channel == (ADCName)NC)
        return;
    /* wait for completion */
    wait();
    /* reset global reference */
    m_global = NULL;
    /* turn off ADC */
    LPC_ADC->ADINTEN = 0;
    LPC_ADC->ADCR = 0;
    LPC_SC->PCONP &= ~(1UL<<12);
}

void RGB_Sensor::__adc_irq(void)
{
    if(m_global)
        m_global->adc_irq();
}

int RGB_Sensor::filter(int sample)
{
    int a,b,x,y,z;

    /* get the two previous samples */
    a = m_adc_filter[m_rgb_channel][0];
    b = m_adc_filter[m_rgb_channel][1];

    /* calculate node distances in triplet */
    x = abs(a-sample);
    y = abs(b-sample);
    z = abs(a-b);

    /* remember current sample */
    m_adc_filter[m_rgb_channel][m_adc_filter_pos] = sample;

    /* choose edge with shortest distance and
     * return average of the two edge nodes */
    if((x<=y) && (x<=z))
        return (a+sample)/2;
    if((y<=x) && (y<=z))
        return (b+sample)/2;
    return (a+b)/2;
}

void RGB_Sensor::adc_irq(void)
{
    int sample;
    uint32_t status;

    status = LPC_ADC->ADSTAT;
    if(status & (1UL<<m_adc_channel))
    {
        /* always read sample to acknowledge IRQ */
        sample = (((&LPC_ADC->ADDR0)[m_adc_channel])>>4) & 0xFFF;

        if(!m_done)
        {
            /* filter value to remove ADC noise/conversion errors */
            m_adc_aggregation[m_rgb_channel] += filter(sample);

            m_adc_count++;
            if(m_adc_count>=RGB_OVERSAMPLING)
            {
                m_adc_count=0;
                m_rgb_channel++;

                /* prepare LEDs for upcoming channel */
                switch(m_rgb_channel)
                {
                    case 1:
                        m_red = RGB_LED_ON;
                        break;

                    case 2:
                        m_red = !RGB_LED_ON;
                        m_green = RGB_LED_ON;
                        break;

                    case 3:
                        m_green = !RGB_LED_ON;
                        m_blue = RGB_LED_ON;
                        break;

                    default:
                        m_blue = !RGB_LED_ON;
                        m_rgb_channel = 0;

                        if(!m_callback)
                            m_done = true;
                        else
                        {
                            TRGB rgb;
                            convert(rgb);
                            m_done = !m_callback(rgb);
                        }
                        /* stop data aquisition */
                        if(m_done)
                            LPC_ADC->ADCR &= ~(1UL<<16);
                        else
                        {
                            m_adc_filter_pos ^= 1;
                            memset(&m_adc_aggregation, 0, sizeof(m_adc_aggregation));
                        }
                }                        
            }
        }
    }
    LPC_ADC->ADSTAT = status;
}

bool RGB_Sensor::capture(TRGB_Callback callback)
{
    /* ignore mis-configurations */
    if(m_adc_channel==(ADCName)NC)
        return false;

    m_callback = callback;
    m_done = false;
    m_adc_filter_pos = m_adc_count = m_rgb_channel = 0;
    memset((void*)&m_adc_aggregation, 0, sizeof(m_adc_aggregation));
    memset(&m_adc_filter, 0, sizeof(m_adc_filter));

    /* start ADC burst mode */
    LPC_ADC->ADCR |= (1UL<<16);

    return true;
}

bool RGB_Sensor::wait(void)
{
    /* ignore mis-configurations */
    if(m_adc_channel==(ADCName)NC)
        return false;

    while(!m_done)
        __WFE();

    return true;
}

void RGB_Sensor::convert(TRGB &rgb)
{
    /* correct "DC" offset by subdstracting
     * environment light
     */
    rgb.ch.red   = m_adc_aggregation[1] - m_adc_aggregation[0];   
    rgb.ch.green = m_adc_aggregation[2] - m_adc_aggregation[0];   
    rgb.ch.blue  = m_adc_aggregation[3] - m_adc_aggregation[0];
} 

bool RGB_Sensor::capture(TRGB &rgb)
{
    if(!(capture(NULL) && wait()))
        return false;

    convert(rgb);
    return true;  
}