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:
Wed Jul 09 10:27:38 2014 +0000
Revision:
10:b95dfd2d6d4d
Parent:
9:7bd80f4a965e
Added RGB_MASK declararation

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 6:fc64a14a2f4a 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
meriac 5:1fed2b68e661 125 /* choose edge with shortest distance and
meriac 5:1fed2b68e661 126 * return average of the two edge nodes */
meriac 5:1fed2b68e661 127 if((x<=y) && (x<=z))
meriac 5:1fed2b68e661 128 return (a+sample)/2;
meriac 5:1fed2b68e661 129 if((y<=x) && (y<=z))
meriac 5:1fed2b68e661 130 return (b+sample)/2;
meriac 5:1fed2b68e661 131 return (a+b)/2;
meriac 5:1fed2b68e661 132 }
meriac 5:1fed2b68e661 133
meriac 0:576e43bd193d 134 void RGB_Sensor::adc_irq(void)
meriac 0:576e43bd193d 135 {
meriac 6:fc64a14a2f4a 136 int sample;
meriac 6:fc64a14a2f4a 137 uint32_t 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 10:b95dfd2d6d4d 143 sample = (((&LPC_ADC->ADDR0)[m_adc_channel])>>4) & RGB_MASK;
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 9:7bd80f4a965e 148 sample = filter(sample);
meriac 9:7bd80f4a965e 149 if(m_adc_count>=RGB_SENSOR_IGNORE)
meriac 9:7bd80f4a965e 150 m_adc_aggregation[m_rgb_channel] += sample;
meriac 0:576e43bd193d 151
meriac 0:576e43bd193d 152 m_adc_count++;
meriac 9:7bd80f4a965e 153 if(m_adc_count>=(RGB_OVERSAMPLING+RGB_SENSOR_IGNORE))
meriac 0:576e43bd193d 154 {
meriac 0:576e43bd193d 155 m_adc_count=0;
meriac 0:576e43bd193d 156 m_rgb_channel++;
meriac 0:576e43bd193d 157
meriac 0:576e43bd193d 158 /* prepare LEDs for upcoming channel */
meriac 0:576e43bd193d 159 switch(m_rgb_channel)
meriac 0:576e43bd193d 160 {
meriac 0:576e43bd193d 161 case 1:
meriac 2:4545984e62b6 162 m_red = RGB_LED_ON;
meriac 0:576e43bd193d 163 break;
meriac 0:576e43bd193d 164
meriac 0:576e43bd193d 165 case 2:
meriac 2:4545984e62b6 166 m_red = !RGB_LED_ON;
meriac 2:4545984e62b6 167 m_green = RGB_LED_ON;
meriac 0:576e43bd193d 168 break;
meriac 0:576e43bd193d 169
meriac 0:576e43bd193d 170 case 3:
meriac 2:4545984e62b6 171 m_green = !RGB_LED_ON;
meriac 6:fc64a14a2f4a 172 m_blue = RGB_LED_ON;
meriac 0:576e43bd193d 173 break;
meriac 0:576e43bd193d 174
meriac 0:576e43bd193d 175 default:
meriac 6:fc64a14a2f4a 176 m_blue = !RGB_LED_ON;
meriac 6:fc64a14a2f4a 177 m_rgb_channel = 0;
meriac 6:fc64a14a2f4a 178
meriac 3:50e1ac3c56db 179 if(!m_callback)
meriac 3:50e1ac3c56db 180 m_done = true;
meriac 3:50e1ac3c56db 181 else
meriac 3:50e1ac3c56db 182 {
meriac 3:50e1ac3c56db 183 TRGB rgb;
meriac 3:50e1ac3c56db 184 convert(rgb);
meriac 3:50e1ac3c56db 185 m_done = !m_callback(rgb);
meriac 3:50e1ac3c56db 186 }
meriac 0:576e43bd193d 187 /* stop data aquisition */
meriac 3:50e1ac3c56db 188 if(m_done)
meriac 3:50e1ac3c56db 189 LPC_ADC->ADCR &= ~(1UL<<16);
meriac 4:0ffadc2caaf6 190 else
meriac 6:fc64a14a2f4a 191 {
meriac 6:fc64a14a2f4a 192 m_adc_filter_pos ^= 1;
meriac 4:0ffadc2caaf6 193 memset(&m_adc_aggregation, 0, sizeof(m_adc_aggregation));
meriac 6:fc64a14a2f4a 194 }
meriac 0:576e43bd193d 195 }
meriac 0:576e43bd193d 196 }
meriac 0:576e43bd193d 197 }
meriac 0:576e43bd193d 198 }
meriac 0:576e43bd193d 199 LPC_ADC->ADSTAT = status;
meriac 0:576e43bd193d 200 }
meriac 0:576e43bd193d 201
meriac 3:50e1ac3c56db 202 bool RGB_Sensor::capture(TRGB_Callback callback)
meriac 0:576e43bd193d 203 {
meriac 0:576e43bd193d 204 /* ignore mis-configurations */
meriac 0:576e43bd193d 205 if(m_adc_channel==(ADCName)NC)
meriac 0:576e43bd193d 206 return false;
meriac 0:576e43bd193d 207
meriac 3:50e1ac3c56db 208 m_callback = callback;
meriac 0:576e43bd193d 209 m_done = false;
meriac 6:fc64a14a2f4a 210 m_adc_filter_pos = m_adc_count = m_rgb_channel = 0;
meriac 0:576e43bd193d 211 memset((void*)&m_adc_aggregation, 0, sizeof(m_adc_aggregation));
meriac 5:1fed2b68e661 212 memset(&m_adc_filter, 0, sizeof(m_adc_filter));
meriac 5:1fed2b68e661 213
meriac 0:576e43bd193d 214 /* start ADC burst mode */
meriac 0:576e43bd193d 215 LPC_ADC->ADCR |= (1UL<<16);
meriac 0:576e43bd193d 216
meriac 0:576e43bd193d 217 return true;
meriac 0:576e43bd193d 218 }
meriac 0:576e43bd193d 219
meriac 0:576e43bd193d 220 bool RGB_Sensor::wait(void)
meriac 0:576e43bd193d 221 {
meriac 0:576e43bd193d 222 /* ignore mis-configurations */
meriac 0:576e43bd193d 223 if(m_adc_channel==(ADCName)NC)
meriac 0:576e43bd193d 224 return false;
meriac 0:576e43bd193d 225
meriac 0:576e43bd193d 226 while(!m_done)
meriac 0:576e43bd193d 227 __WFE();
meriac 0:576e43bd193d 228
meriac 0:576e43bd193d 229 return true;
meriac 0:576e43bd193d 230 }
meriac 0:576e43bd193d 231
meriac 3:50e1ac3c56db 232 void RGB_Sensor::convert(TRGB &rgb)
meriac 0:576e43bd193d 233 {
meriac 8:88acb970df76 234 int i, sample;
meriac 8:88acb970df76 235
meriac 0:576e43bd193d 236 /* correct "DC" offset by subdstracting
meriac 0:576e43bd193d 237 * environment light
meriac 0:576e43bd193d 238 */
meriac 8:88acb970df76 239 for(i=0; i<3; i++)
meriac 8:88acb970df76 240 {
meriac 8:88acb970df76 241 sample = m_adc_aggregation[0] - m_adc_aggregation[i+1];
meriac 8:88acb970df76 242 rgb.data[i] = (sample<0) ? 0 : sample;
meriac 8:88acb970df76 243 }
meriac 3:50e1ac3c56db 244 }
meriac 0:576e43bd193d 245
meriac 3:50e1ac3c56db 246 bool RGB_Sensor::capture(TRGB &rgb)
meriac 3:50e1ac3c56db 247 {
meriac 3:50e1ac3c56db 248 if(!(capture(NULL) && wait()))
meriac 3:50e1ac3c56db 249 return false;
meriac 3:50e1ac3c56db 250
meriac 3:50e1ac3c56db 251 convert(rgb);
meriac 0:576e43bd193d 252 return true;
meriac 0:576e43bd193d 253 }