The TCS230 programmable color light-to-frequency converter combines configurable silicon photodiodes and a current-to-frequency converter on single monolithic CMOS integrated circuit. The output is a square wave (50% duty cycle) with frequency directly proportional to light intensity (irradiance). The full-scale output frequency can be scaled by one of three preset values via two control input pins. Digital inputs and digital output allow direct interface to a microcontroller or other logic circuitry. Output enable (OE) places the output in the high-impedance state for multiple-unit sharing of a microcontroller input line.
The light-to-frequency converter reads an 8 x 8 array of photodiodes. Sixteen photodiodes have blue filters, 16 photodiodes have green filters, 16 photodiodes have red filters, and 16 photodiodes are clear with no filters. The four types (colors) of photodiodes are interdigitated to minimize the effect of non-uniformity of incident irradiance. All 16 photodiodes of the same color are connected in parallel and which type of photodiode the device uses during operation is pin-selectable. Photodiodes are 120 mm x 120 mm in size and are on 144-mm centers.
Functional Block Diagram

Terminal Function
Terminal | No | I/O | Description |
| GND | 4 | - | Power supply ground. All voltages are referenced to GND. |
| OE | 3 | In | Enable for fo (active low). |
| OUT | 6 | Out | Output frequency (fo). |
| S0, S1 | 1,2 | In | Output frequency scaling selection inputs. |
| S2,S3 | 7,8 | In | Photodiode type selection inputs. |
| VDD | 5 | - | Supply voltage |
S0 | S1 | Output Frequency Scaling ( fc ) | S2 | S3 | Photodiode Type | |
L | L | Power Down | L | L | Red | |
L | H | 2% | L | H | Blue | |
H | L | 20% | H | L | Clear ( No Filter ) | |
H | H | 100% | H | H | Green |
APPLICATION INFORMATION
Power supply considerations
Power-supply lines must be decoupled by a 0.01-mF to 0.1-mF capacitor with short leads mounted close to the device package.
Input interface
A low-impedance electrical connection between the device OE pin and the device GND pin is required for improved noise immunity.
Output interface
The output of the device is designed to drive a standard TTL or CMOS logic input over short distances. If lines greater than 12 inches are used on the output, a buffer or line driver is recommended.
Photodiode type (color) selection
The type of photodiode (blue, green, red, or clear) used by the device is controlled by two logic inputs, S2 and S3 (see Table 1).
Output frequency scaling
Output-frequency scaling is controlled by two logic inputs, S0 and S1. The internal light-to-frequency converter generates a fixed-pulsewidth pulse train. Scaling is accomplished by internally connecting the pulse-train output of the converter to a series of frequency dividers. Divided outputs are 50%-duty cycle square waves with relative frequency values of 100%, 20%, and 2%. Because division of the output frequency is accomplished by counting pulses of the principal internal frequency, the final-output period represents an average of the multiple periods of the principle frequency. The output-scaling counter registers are cleared upon the next pulse of the principal frequency after any transition of the S0, S1, S2, S3, and OE lines. The output goes high upon the next subsequent pulse of the principal frequency, beginning a new valid period. This minimizes the time delay between a change on the input lines and the resulting new output period. The response time to an input programming change or to an irradiance step change is one period of new frequency plus 1 mS. The scaled output changes both the full-scale frequency and the dark frequency by the selected scale factor. The frequency-scaling function allows the output range to be optimized for a variety of measurement techniques. The scaled-down outputs may be used where only a slower frequency counter is available, such as low-cost microcontroller, or where period measurement techniques are used.
Measuring the frequency
The choice of interface and measurement technique depends on the desired resolution and data acquisition rate. For maximum data-acquisition rate, period-measurement techniques are used. Output data can be collected at a rate of twice the output frequency or one data point every microsecond for full-scale output. Period measurement requires the use of a fast reference clock with available resolution directly related to reference clock rate. Output scaling can be used to increase the resolution for a given clock rate or to maximize resolution as the light input changes. Period measurement is used to measure rapidly varying light levels or to make a very fast measurement of a constant light source. Maximum resolution and accuracy may be obtained using frequency-measurement, pulse-accumulation, or integration techniques. Frequency measurements provide the added benefit of averaging out random- or high-frequency variations (jitter) resulting from noise in the light signal. Resolution is limited mainly by available counter registers and allowable measurement time. Frequency measurement is well suited for slowly varying or constant light levels and for reading average light levels over short periods of time. Integration (the accumulation of pulses over a very long period of time) can be used to measure exposure, the amount of light present in an area over a given time period.
Schematic Diagram System

;====================================================
;FREQUENCY COUNTER BASE ON MICROCONTROLLER 89S51 ;
;1.This Program is to count frequency from T0, by using mode 1 Counter 16 bit, then counter will overflow after 65.535d or FFFFh pulse
;2.Counter resets every 1 second that generated by timer 1, configured as timer 16 bit trough ; TF1 interrupt ;3.Every 1 second data will show on LCD Character and data saved on RAM address 30h trough 6fh for 30 data
;4.Subrutine SearchMax, used to find the biggest data betwen 30 data that already saved
;
;by: Triwiyanto, S.Si.,MT ; www.mytutorialcafe.com
;======================================================
; dispclr equ 00000001b funcset equ 00111000b entrmod equ 00000110b
dispon equ 00001100b ones equ 71h tens equ 72h hundreds equ 73h thousands
equ 74h tenthousands equ 75h Count10 equ 76h DataCounter_MaxL equ 77h
DataCounter_MaxH equ 78h DataCounter_nextL equ 79h DataCounter_nextH
equ 7ah DataRAMCounterL equ 7bh DataRAMCounterH equ 7ch DataFlowRateL
equ 7dh DataFlowRateH equ 7eh NewDataL equ 7fh NewDataH equ 70h ; RS
bit P3.0 EN bit P3.1 PortLCD equ P0 button bit P2.3 ;Ram 30h s/d 4fh
;Ram 50h s/d 6fh ; ; org 0h sjmp start ; org 0bh ; address interrupt
for timer0 ljmp Timer_Interupsi0 ; long jump to timer interrupt 0 TF0
;
start:
call ResetRAM
call Init_LCD AgainD:
call lcd_demo jb button,AgainD
jnb button,$ ; setb P2.0; mode no filter TCS230
clr P2.1 ; mode no filter TCS230
clr P2.2 ; output enable TCS230
call init_lcd call init_interupsi_Timer0 ; call LCD_Flow Forever: call
DisplayFlowRate jb button, Forever jnb button,$ call lcd_Search call
ldelay call init_lcd call LCD_maxFLow Finish: call DisplayFlowRateMax
call stopcounter jb button,Finish jnb button,$ call resetcounter sjmp
start ; DisplayFlowRate: mov DataFlowRateL,NewDataL mov DataFlowRateH,NewDataH
; call Hex16toBCD ; mov R3,#086h call write_inst mov R3,tenthousands
call write_data ; mov R3,#087h call write_inst mov R3,thousands call
write_data ; mov R3,#088h call write_inst mov R3,hundreds call write_data
; mov R3,#089h call write_inst mov R3,tens call write_data ; mov R3,#08ah
call write_inst mov R3,ones call write_data ret ; DisplayFlowRateMax:
call SearchingMax ; mov DataFlowRateL,DataCounter_MaxL mov DataFlowRateH,DataCounter_MaxH
; call Hex16toBCD ; mov R3,#0c6h call write_inst mov R3,tenthousands
call write_data ; mov R3,#0c7h call write_inst mov R3,thousands call
write_data ; mov R3,#0c8h call write_inst mov R3,hundreds call write_data
; mov R3,#0c9h call write_inst mov R3,tens call write_data ; mov R3,#0cah
call write_inst mov R3,ones call write_data ret ; ;==========================================================
;Subroutine Hex16toBCD
;===========================================================
;To convert hex 16 bit to 5 digit decimal
;input Data 8 High byte = R1
;input Data 8 Low byte = R2
;
;Output tenthousands = R7
;Output thousands = R6
;Output hundreds = R5
;Output tens = R4
;Output ones = R3
;
;Contoh:
1A2C h —-> 06799 d
;=======================================================
Hex16toBCD:
ANL PSW,#11101111b ; Aktivate bank 1 MOV R1,DataFlowRateH;
MSByte
MOV R2,DataFlowRateL; LSByte
MOV R3,#00D
MOV R4,#00D
MOV R5,#00D
MOV R6,#00D
MOV R7,#00D MOV B,#10D
MOV A,R2
DIV AB
MOV R3,B ;
MOV B,#10 ; R7,R6,R5,R4,R3
DIV AB
MOV R4,B
MOV R5,A
CJNE R1,#0H,HIGH_BYTE ; CHECK FOR HIGH BYTE
SJMP ENDD
HIGH_BYTE:
MOV A,#6
ADD A,R3
MOV B,#10
DIV AB
MOV R3,B
ADD A,#5
ADD A,R4
MOV B,#10
DIV AB
MOV R4,B
ADD A,#2
ADD A,R5
MOV B,#10
DIV AB
MOV R5,B
CJNE R6,#00D,ADD_IT
SJMP CONTINUE
ADD_IT:
ADD A,R6
CONTINUE:
MOV R6,A
DJNZ R1,HIGH_BYTE
MOV B, #10D
MOV A,R6
DIV AB
MOV R6,B
MOV R7,A
ENDD:
Mov A,R3
Add A,#30h
MOV ones,A
;
Mov A,R4
Add A,#30h
Mov tens,A
;
Mov A,R5
Add A,#30h
Mov hundreds,A
;
Mov A,R6
Add A,#30h
Mov thousands,A
;
Mov A,R7
Add A,#30h
Mov tenthousands,A
ANL PSW,#11100111b ;Aktivasi Bank 0
ret
;============================================================
;Subrutine Timer_Interupsi0
;===================================================
;This subruoutine is to give an interruption and will overflow
;every 0.05 second or 50000 u second.
;and data that loadee is 65.536-50.000=15536 d = ( 3CB0h )
;B0 h loaded to TL1 and data 3C loaded to TH1
;
;Each this subroutine read, pencacah20 will decrement till Pencacah20=0
;this will happen every 20 X 50000 uS = 20 X 0,05s = 1 s,and then
;subrutine updatedata-stopcounter-savedatacounter
;dan resetcounter will call.
;==========================================================
Timer_Interupsi0:
mov tl0,#0B0h
mov th0,#03ch
djnz Count10,EndInterupsi
mov Count10,#20
call UpdateData
call StopCounter
call SaveDataCounter
call ResetCounter
EndInterupsi:
reti
;
;=====================================================
;Subrutine Init_interupsi_timer0
;=====================================================
;This subroutine is used to initiate RAM low byte and RAM high byte
;Initiate mode timer: timer 0 is functioned as timer 16 bit mode 1
;timer 1 is functioned as counter 16 bit mode 1
;Initiate register counter TL0 dan TH0: with data 3CB0h so counter will over
;flow every 0,05 second
;Initiate register counter TL1 dan TH1: with data 0000h so counter
;start rock and roll in start condition 0000
;Initate Timer0 Interrupt
;=====================================================
Init_interupsi_Timer0:
mov DataRamCounterL,#30h
mov DataRAMCounterH,#50h
mov Count10,#20
;Initiate for counter down 10 x 0.05 = 0.5 second
mov tl0,#0B0h
mov th0,#03ch
mov tl1,#00h
mov th1,#00h
Mov TMOD,#01010001b
;timer0 = as timer 16 bit, timer1 = as counter16 bit
setb ET0 ; Enable timer 0 interruption
Setb EA ; Master Enable All Interuppt
setb TR0 ; start rock and roll timer 0
setb TR1 ; start rock and roll timer 1
ret
;
UpdateData:
mov NewDataL,TL1
mov NewDataH,TH1
ret
;
StopCounter:
clr TR0
clr TR1
ret
;
;=======================================================
;Subrutine Savedatacounter
;=======================================================
;This subruoutine is to save data to register counter 16 bit TL1 and TH1
;Data register TL1 is saved in RAM with address 30h s/d 4fh
;Data register TH1 is saved in RAM with address 50h s/d 6fh
;this subroutine will be called every timer 0 over flow each 20x 0.05 second = 1second
;after data is saved than address RAM will increment
;
;Index Transfer Data : mov @R0,Data1
; mov Data2,@R0
; Example:
; org 0h
; mov R0,#30h
;start:mov DataADC,P2 ;gets data from ADC
; mov @R0,DataADC ; saves data ADC in addresss 30h
; inc R0 ; increment, R0:= R0 + 1, to save the next data
; call delay ; next address is 31h
; sjmp start
;======================================================
SaveDataCounter:
mov R0,DataRamCounterL
mov @R0,TL1
mov R1,DataRAMCounterH
mov @R1,TH1
inc DataRAMCounterL
inc DataRAMCounterH
mov A,DataRAMCounterL
cjne A,#50h,QuitSDC
mov DataRamCounterL,#30h; Reset to base Address 30h
mov DataRamCounterH,#50h; Reset to base Address 50h
QuitSDC:
ret
;
ResetCounter:
mov TL1,#0
mov TH1,#0
Setb TR0
Setb TR1
ret
;
ResetRAM:
mov R1,#64
mov R0,#30h
NextRam:mov @R0,#0
inc R0
djnz R1,NextRam
ret
;
delay: mov R6,#25
del1: mov R5,#255
djnz R5,$
djnz R6,del1
ret
;
ldelay: mov R7,#30
ldel1: call delay
djnz R7,ldel1
ret
;==========================================================
; Subrutine SearchingMax ( Oleh: Triwiyanto )
;==========================================================
;This subroutine is to search data 16 bit betwen 30 data
;with metode, to compare data betwen now and next data
;RAM have capacity ony 1 byte so low byte will be saved to 30h .. 40h
;and high byte will be saved on 50h..61h
;by using the algorith from high programmming language then :
;===========================================================
;DataMax:=DataNext[0]
;for i:=1 to 30 do
; begin
; if DataNext[i] > DataMax then DataMax := DataNext[i]
; end
;=======================================================
;How to detect if a data is bigger then others, is
; by using instruction SUBB A,Data and JNC Label
; for example A=19, Data=20 then C = 1
; A=21, Data=20 then C = 0
;=======================================================
SearchingMax:
mov R7,#30
;number address ram is 2 x 30 address
mov R0,#30h
mov R1,#50h
mov DataCounter_MaxL,@R0
mov DataCounter_MaxH,@R1
NextData:
inc R0
inc R1
mov DataCounter_NextL,@R0
mov DataCounter_NextH,@R1
;
mov A,DataCounter_NextL
clr C
subb A,DataCounter_MaxL
mov A,DataCounter_NextH
subb A,DataCounter_MaxH
;
jnc SaveDataCounterMax
djnz R7,NextData
Sjmp QuitS
SaveDataCounterMax:
mov DataCounter_MaxL,DataCounter_NextL
mov DataCounter_MaxH,DataCounter_NextH
djnz R7,NextData
QuitS: ret
;
init_lcd:
mov R3,#dispclr
acall write_inst
mov R3,#funcset
acall write_inst
mov R3,#dispon
acall write_inst
mov R3,#entrmod
acall write_inst
ret
;
Write_inst:
Clr RS ; mode write instruction
Mov PortLCD,R3 ; D7 s/d D0 = P0 = R1
Setb EN ; EN = 1 = Enable data
Acall delay ; calldelay time
Clr EN ; EN = 0
ret
;
Write_data:
Setb RS ; mode write instruction
Mov PortLCD,R3 ; D7 s/d D0 = P0 = R1
Setb EN ; EN = 1
Acall delay ; call delay time
Clr EN ; EN = 0 ret
;
LCD_Demo:
mov dptr,#writeDemo; DPTR = [ writean1 ]
mov r7,#16 ; R3=16,Number character to display
mov r3,#080h ; R1=80h,character position row=1,col=1
acall write_inst
;
Demo: clr a ; A = 0
movc a,@a+dptr ; A = [A+ DPTR]
mov r3,A ; R1 = A
inc dptr ; DPTR = DPTR +1
acall write_data
djnz r7,Demo ;R3=R3-1,jump to write1 if R3 = 0
ret
;
LCD_Flow:
mov dptr,#writeFlow; DPTR = [ writean1 ]
mov r7,#16 ; R3= 16, Number character to display
mov r3,#080h ;R1= 80h,character position row=1, col=1
acall write_inst
Flow: clr a ; A = 0
movc a,@a+dptr ; A = [A+ DPTR]
mov r3,A ; R1 = A
inc dptr ; DPTR = DPTR +1
acall write_data
djnz r7,Flow ; R3 = R3-1,jump to write1 if R3=0
ret
;
LCD_Search:
mov dptr,#writeSearch; DPTR = [ writean1 ]
mov r7,#16 ; R3=16,Number character to display
mov r3,#080h ;R1=80h,character position row=1,col=1
acall write_inst
;
Search:clr a ; A = 0
movc a,@a+dptr ; A = [A+ DPTR]
mov r3,A ; R1 = A
inc dptr ; DPTR = DPTR +1
acall write_data
djnz r7,Search ; R3 = R3-1,jump to write1 if R3=0
ret
;
LCD_MaxFlow:
mov dptr,#writeFlow; DPTR = [ writean1 ]
mov r7,#16 ;R3=16,Number character to display
mov r3,#0c0h;R1=80h,character position row=1, col=1
acall write_inst
MaxFlow:
clr a ; A = 0
movc a,@a+dptr ; A = [A+ DPTR]
mov r3,A ; R1 = A
inc dptr ; DPTR = DPTR +1
acall write_data
djnz r7,MaxFlow ;R3=R3-1,jump to write1 if R3=0
ret
;
writeDemo:
DB 'Spirometer 89s51'
writeFlow:
DB 'FLowR: ml/s'
writeSearch:
DB 'Searching...Max '
;
endsource: mytutorialcafe.com
