tweaked detection to use photoresistor through an opamp
Fork of rgb_sensor by
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; }