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
rgb_sensor.cpp@10:b95dfd2d6d4d, 2014-07-09 (annotated)
- 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?
User | Revision | Line number | New 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 | } |