Discrete RGB colour sensor using a colour LED flashing at high speed and a monochrome LDR (light dependent resistor) for detecting the colour via ADC conversion. The library implements interrupt driven ADC conversion at high speed (370 RGB readings per second, 128 times oversampling per channelfor noise reduction). The detection can optionally run in background.

Dependents:   rgb_sensor_buffer discrete_rgb_color_sensor_example

Committer:
meriac
Date:
Sat Jun 28 06:59:51 2014 +0000
Revision:
5:1fed2b68e661
Parent:
4:0ffadc2caaf6
Child:
6:fc64a14a2f4a
Added ADC value filtering to ignore ADC conversion errors

Who changed what in which revision?

UserRevisionLine numberNew contents of line
meriac 0:576e43bd193d 1 /* Discrete RGB color sensor
meriac 0:576e43bd193d 2 *
meriac 0:576e43bd193d 3 * - uses single-channel light-dependent resistor (via ADC)
meriac 0:576e43bd193d 4 * and a RGB LED.
meriac 0:576e43bd193d 5 * - compensates background light
meriac 0:576e43bd193d 6 *
meriac 0:576e43bd193d 7 * Copyright (c) 2014 ARM Limited
meriac 0:576e43bd193d 8 *
meriac 0:576e43bd193d 9 * Licensed under the Apache License, Version 2.0 (the "License");
meriac 0:576e43bd193d 10 * you may not use this file except in compliance with the License.
meriac 0:576e43bd193d 11 * You may obtain a copy of the License at
meriac 0:576e43bd193d 12 *
meriac 0:576e43bd193d 13 * http://www.apache.org/licenses/LICENSE-2.0
meriac 0:576e43bd193d 14 *
meriac 0:576e43bd193d 15 * Unless required by applicable law or agreed to in writing, software
meriac 0:576e43bd193d 16 * distributed under the License is distributed on an "AS IS" BASIS,
meriac 0:576e43bd193d 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
meriac 0:576e43bd193d 18 * See the License for the specific language governing permissions and
meriac 0:576e43bd193d 19 * limitations under the License.
meriac 0:576e43bd193d 20 */
meriac 0:576e43bd193d 21
meriac 0:576e43bd193d 22 #include <mbed.h>
meriac 0:576e43bd193d 23 #include <pinmap.h>
meriac 0:576e43bd193d 24 #include "rgb_sensor.h"
meriac 0:576e43bd193d 25
meriac 0:576e43bd193d 26 static const PinMap RGB_Sensor__PinMap_ADC[] = {
meriac 0:576e43bd193d 27 {P0_23, ADC0_0, 1},
meriac 0:576e43bd193d 28 {P0_24, ADC0_1, 1},
meriac 0:576e43bd193d 29 {P0_25, ADC0_2, 1},
meriac 0:576e43bd193d 30 {P0_26, ADC0_3, 1},
meriac 0:576e43bd193d 31 {P1_30, ADC0_4, 3},
meriac 0:576e43bd193d 32 {P1_31, ADC0_5, 3},
meriac 0:576e43bd193d 33 {P0_2, ADC0_7, 2},
meriac 0:576e43bd193d 34 {P0_3, ADC0_6, 2},
meriac 0:576e43bd193d 35 {NC, NC, 0}
meriac 0:576e43bd193d 36 };
meriac 0:576e43bd193d 37
meriac 0:576e43bd193d 38 DigitalOut g_dbg(LED1);
meriac 0:576e43bd193d 39
meriac 0:576e43bd193d 40 /* initialize globally */
meriac 0:576e43bd193d 41 RGB_Sensor* RGB_Sensor::m_global = NULL;
meriac 0:576e43bd193d 42
meriac 0:576e43bd193d 43 RGB_Sensor::RGB_Sensor(PinName red, PinName green, PinName blue, PinName adc)
meriac 0:576e43bd193d 44 :m_red(red), m_green(green), m_blue(blue)
meriac 0:576e43bd193d 45 {
meriac 0:576e43bd193d 46 uint32_t clkdiv;
meriac 0:576e43bd193d 47
meriac 0:576e43bd193d 48 m_done=true;
meriac 0:576e43bd193d 49
meriac 0:576e43bd193d 50 /* allow only one instance */
meriac 0:576e43bd193d 51 if(m_global)
meriac 0:576e43bd193d 52 {
meriac 0:576e43bd193d 53 m_adc_channel = (ADCName)NC;
meriac 0:576e43bd193d 54 return;
meriac 0:576e43bd193d 55 }
meriac 0:576e43bd193d 56
meriac 0:576e43bd193d 57 /* disable LED's */
meriac 2:4545984e62b6 58 m_red = !RGB_LED_ON;
meriac 2:4545984e62b6 59 m_green = !RGB_LED_ON;
meriac 2:4545984e62b6 60 m_blue = !RGB_LED_ON;
meriac 0:576e43bd193d 61
meriac 0:576e43bd193d 62 // enable ADC power
meriac 0:576e43bd193d 63 LPC_SC->PCONP |= (1<<12);
meriac 0:576e43bd193d 64 // set ADC clock to PCLK
meriac 0:576e43bd193d 65 LPC_SC->PCLKSEL0 = (LPC_SC->PCLKSEL0 & ~(0x3UL<<24)) | (0x1UL<<24);
meriac 0:576e43bd193d 66
meriac 0:576e43bd193d 67 // determine mapping
meriac 0:576e43bd193d 68 m_adc_channel = (ADCName)pinmap_peripheral(adc, RGB_Sensor__PinMap_ADC);
meriac 0:576e43bd193d 69 if (m_adc_channel == (ADCName)NC)
meriac 0:576e43bd193d 70 return;
meriac 0:576e43bd193d 71
meriac 0:576e43bd193d 72 // initialize ADC
meriac 0:576e43bd193d 73 clkdiv = ((SystemCoreClock+RGB_MAX_ADC_CLK-1)/RGB_MAX_ADC_CLK)-1;
meriac 0:576e43bd193d 74 LPC_ADC->ADCR = (1UL<<m_adc_channel) | (clkdiv<<8) | (1UL<<21);
meriac 0:576e43bd193d 75
meriac 0:576e43bd193d 76 // set up ADC IRQ
meriac 0:576e43bd193d 77 NVIC_SetVector(ADC_IRQn, (uint32_t)&__adc_irq);
meriac 0:576e43bd193d 78 LPC_ADC->ADINTEN = (1UL<<m_adc_channel);
meriac 0:576e43bd193d 79 NVIC_EnableIRQ(ADC_IRQn);
meriac 0:576e43bd193d 80
meriac 0:576e43bd193d 81 // propagate pin setting
meriac 0:576e43bd193d 82 pinmap_pinout(adc, RGB_Sensor__PinMap_ADC);
meriac 0:576e43bd193d 83
meriac 0:576e43bd193d 84 // remember this instance
meriac 0:576e43bd193d 85 m_global = this;
meriac 0:576e43bd193d 86 }
meriac 0:576e43bd193d 87
meriac 0:576e43bd193d 88 RGB_Sensor::~RGB_Sensor(void)
meriac 0:576e43bd193d 89 {
meriac 0:576e43bd193d 90 /* only deal with fully initialized objects */
meriac 0:576e43bd193d 91 if(m_adc_channel == (ADCName)NC)
meriac 0:576e43bd193d 92 return;
meriac 0:576e43bd193d 93 /* wait for completion */
meriac 0:576e43bd193d 94 wait();
meriac 0:576e43bd193d 95 /* reset global reference */
meriac 0:576e43bd193d 96 m_global = NULL;
meriac 0:576e43bd193d 97 /* turn off ADC */
meriac 0:576e43bd193d 98 LPC_ADC->ADINTEN = 0;
meriac 0:576e43bd193d 99 LPC_ADC->ADCR = 0;
meriac 0:576e43bd193d 100 LPC_SC->PCONP &= ~(1UL<<12);
meriac 0:576e43bd193d 101 }
meriac 0:576e43bd193d 102
meriac 0:576e43bd193d 103 void RGB_Sensor::__adc_irq(void)
meriac 0:576e43bd193d 104 {
meriac 0:576e43bd193d 105 if(m_global)
meriac 0:576e43bd193d 106 m_global->adc_irq();
meriac 0:576e43bd193d 107 }
meriac 0:576e43bd193d 108
meriac 5:1fed2b68e661 109 int RGB_Sensor::filter(int sample)
meriac 5:1fed2b68e661 110 {
meriac 5:1fed2b68e661 111 int a,b,x,y,z;
meriac 5:1fed2b68e661 112
meriac 5:1fed2b68e661 113 /* get the two previous samples */
meriac 5:1fed2b68e661 114 a = m_adc_filter[m_rgb_channel][0];
meriac 5:1fed2b68e661 115 b = m_adc_filter[m_rgb_channel][1];
meriac 5:1fed2b68e661 116
meriac 5:1fed2b68e661 117 /* calculate node distances in triplet */
meriac 5:1fed2b68e661 118 x = abs(a-sample);
meriac 5:1fed2b68e661 119 y = abs(b-sample);
meriac 5:1fed2b68e661 120 z = abs(a-b);
meriac 5:1fed2b68e661 121
meriac 5:1fed2b68e661 122 /* remember current sample */
meriac 5:1fed2b68e661 123 m_adc_filter[m_rgb_channel][m_adc_filter_pos] = sample;
meriac 5:1fed2b68e661 124 m_adc_filter_pos ^= 1;
meriac 5:1fed2b68e661 125
meriac 5:1fed2b68e661 126 /* choose edge with shortest distance and
meriac 5:1fed2b68e661 127 * return average of the two edge nodes */
meriac 5:1fed2b68e661 128 if((x<=y) && (x<=z))
meriac 5:1fed2b68e661 129 return (a+sample)/2;
meriac 5:1fed2b68e661 130 if((y<=x) && (y<=z))
meriac 5:1fed2b68e661 131 return (b+sample)/2;
meriac 5:1fed2b68e661 132 return (a+b)/2;
meriac 5:1fed2b68e661 133 }
meriac 5:1fed2b68e661 134
meriac 0:576e43bd193d 135 void RGB_Sensor::adc_irq(void)
meriac 0:576e43bd193d 136 {
meriac 0:576e43bd193d 137 uint32_t sample, status;
meriac 0:576e43bd193d 138
meriac 0:576e43bd193d 139 status = LPC_ADC->ADSTAT;
meriac 0:576e43bd193d 140 if(status & (1UL<<m_adc_channel))
meriac 0:576e43bd193d 141 {
meriac 0:576e43bd193d 142 /* always read sample to acknowledge IRQ */
meriac 0:576e43bd193d 143 sample = (((&LPC_ADC->ADDR0)[m_adc_channel])>>4) & 0xFFF;
meriac 0:576e43bd193d 144
meriac 0:576e43bd193d 145 if(!m_done)
meriac 0:576e43bd193d 146 {
meriac 5:1fed2b68e661 147 /* filter value to remove ADC noise/conversion errors */
meriac 5:1fed2b68e661 148 m_adc_aggregation[m_rgb_channel] += filter(sample);
meriac 0:576e43bd193d 149
meriac 0:576e43bd193d 150 m_adc_count++;
meriac 0:576e43bd193d 151 if(m_adc_count>=RGB_OVERSAMPLING)
meriac 0:576e43bd193d 152 {
meriac 0:576e43bd193d 153 m_adc_count=0;
meriac 0:576e43bd193d 154 m_rgb_channel++;
meriac 0:576e43bd193d 155
meriac 0:576e43bd193d 156 /* prepare LEDs for upcoming channel */
meriac 0:576e43bd193d 157 switch(m_rgb_channel)
meriac 0:576e43bd193d 158 {
meriac 0:576e43bd193d 159 case 1:
meriac 2:4545984e62b6 160 m_red = RGB_LED_ON;
meriac 0:576e43bd193d 161 break;
meriac 0:576e43bd193d 162
meriac 0:576e43bd193d 163 case 2:
meriac 2:4545984e62b6 164 m_red = !RGB_LED_ON;
meriac 2:4545984e62b6 165 m_green = RGB_LED_ON;
meriac 0:576e43bd193d 166 break;
meriac 0:576e43bd193d 167
meriac 0:576e43bd193d 168 case 3:
meriac 2:4545984e62b6 169 m_green = !RGB_LED_ON;
meriac 2:4545984e62b6 170 m_blue= RGB_LED_ON;
meriac 0:576e43bd193d 171 break;
meriac 0:576e43bd193d 172
meriac 0:576e43bd193d 173 default:
meriac 2:4545984e62b6 174 m_blue= !RGB_LED_ON;
meriac 3:50e1ac3c56db 175 if(!m_callback)
meriac 3:50e1ac3c56db 176 m_done = true;
meriac 3:50e1ac3c56db 177 else
meriac 3:50e1ac3c56db 178 {
meriac 3:50e1ac3c56db 179 TRGB rgb;
meriac 3:50e1ac3c56db 180 convert(rgb);
meriac 3:50e1ac3c56db 181 m_done = !m_callback(rgb);
meriac 3:50e1ac3c56db 182 m_rgb_channel = 0;
meriac 3:50e1ac3c56db 183 }
meriac 0:576e43bd193d 184 /* stop data aquisition */
meriac 3:50e1ac3c56db 185 if(m_done)
meriac 3:50e1ac3c56db 186 LPC_ADC->ADCR &= ~(1UL<<16);
meriac 4:0ffadc2caaf6 187 else
meriac 4:0ffadc2caaf6 188 memset(&m_adc_aggregation, 0, sizeof(m_adc_aggregation));
meriac 0:576e43bd193d 189 }
meriac 0:576e43bd193d 190 }
meriac 0:576e43bd193d 191 }
meriac 0:576e43bd193d 192 }
meriac 0:576e43bd193d 193 LPC_ADC->ADSTAT = status;
meriac 0:576e43bd193d 194 }
meriac 0:576e43bd193d 195
meriac 3:50e1ac3c56db 196 bool RGB_Sensor::capture(TRGB_Callback callback)
meriac 0:576e43bd193d 197 {
meriac 0:576e43bd193d 198 /* ignore mis-configurations */
meriac 0:576e43bd193d 199 if(m_adc_channel==(ADCName)NC)
meriac 0:576e43bd193d 200 return false;
meriac 0:576e43bd193d 201
meriac 3:50e1ac3c56db 202 m_callback = callback;
meriac 0:576e43bd193d 203 m_done = false;
meriac 0:576e43bd193d 204 m_adc_count = m_rgb_channel = 0;
meriac 0:576e43bd193d 205 memset((void*)&m_adc_aggregation, 0, sizeof(m_adc_aggregation));
meriac 5:1fed2b68e661 206 m_adc_filter_pos = 0;
meriac 5:1fed2b68e661 207 memset(&m_adc_filter, 0, sizeof(m_adc_filter));
meriac 5:1fed2b68e661 208
meriac 0:576e43bd193d 209 /* start ADC burst mode */
meriac 0:576e43bd193d 210 LPC_ADC->ADCR |= (1UL<<16);
meriac 0:576e43bd193d 211
meriac 0:576e43bd193d 212 return true;
meriac 0:576e43bd193d 213 }
meriac 0:576e43bd193d 214
meriac 0:576e43bd193d 215 bool RGB_Sensor::wait(void)
meriac 0:576e43bd193d 216 {
meriac 0:576e43bd193d 217 /* ignore mis-configurations */
meriac 0:576e43bd193d 218 if(m_adc_channel==(ADCName)NC)
meriac 0:576e43bd193d 219 return false;
meriac 0:576e43bd193d 220
meriac 0:576e43bd193d 221 while(!m_done)
meriac 0:576e43bd193d 222 __WFE();
meriac 0:576e43bd193d 223
meriac 0:576e43bd193d 224 return true;
meriac 0:576e43bd193d 225 }
meriac 0:576e43bd193d 226
meriac 3:50e1ac3c56db 227 void RGB_Sensor::convert(TRGB &rgb)
meriac 0:576e43bd193d 228 {
meriac 0:576e43bd193d 229 /* correct "DC" offset by subdstracting
meriac 0:576e43bd193d 230 * environment light
meriac 0:576e43bd193d 231 */
meriac 0:576e43bd193d 232 rgb.ch.red = m_adc_aggregation[1] - m_adc_aggregation[0];
meriac 0:576e43bd193d 233 rgb.ch.green = m_adc_aggregation[2] - m_adc_aggregation[0];
meriac 0:576e43bd193d 234 rgb.ch.blue = m_adc_aggregation[3] - m_adc_aggregation[0];
meriac 3:50e1ac3c56db 235 }
meriac 0:576e43bd193d 236
meriac 3:50e1ac3c56db 237 bool RGB_Sensor::capture(TRGB &rgb)
meriac 3:50e1ac3c56db 238 {
meriac 3:50e1ac3c56db 239 if(!(capture(NULL) && wait()))
meriac 3:50e1ac3c56db 240 return false;
meriac 3:50e1ac3c56db 241
meriac 3:50e1ac3c56db 242 convert(rgb);
meriac 0:576e43bd193d 243 return true;
meriac 0:576e43bd193d 244 }