PropCAM 4-bit Parallel Driver┌───────────────────────────────────────┐ │ PropCAM 4-bit Parallel Driver │ │ Author: Phil Pilgrim │ │(c) Copyright 2006 Bueno Systems, Inc. │ │ See end of file for terms of use. │ └───────────────────────────────────────┘ This is a 4-bit parallel driver for the Parallax PropCAM. It interfaces the Kodak KAC9630 to the P8X32A Propeller chip via the sensor's four most significant data outputs. Revision History 2006.07.17: v0.1 Alpha release. 2013.09.23: v1.0 Initial release Contact Information Phil Pilgrim: propeller@phipi.com IntroductionThis driver interfaces the PropCAM's KAC9630 image sensor to the Parallax P8X32A Propeller chip via the sensor's D7..D4 data pins.Data from the sensor is stored in a designated grayscale buffer, packed two pixels per byte. From here it can be dispayed on a video monitor and/or processed for things like motion detection, sizing, sorting, pattern recognition, etc. This object launches two additional cogs, which are used for image acquisition. In continuous acquisition modes it launches a third cog for scheduling and performing the acquisitions. It also provides the timing and user interface required for the camera's various operating modes. External ConnectionsThe PropCAM-DB, when used with this object, is plug-compatible with the Parallax Propeller Backpack (#28327). When used with other Propeller boards, connect as follows:Top View ┌─────────────────────────────┐ │ │ │ │ n/c ┼──• •─── n/c +3.3V │ P0 ┼──• •─── P1 ┬ │ P2 ┼──• •─── P3 4.7K │ SCL ┼──• •─────────────┻──── SDA │ VSYNC ┼──• •─── MCLK (10 MHz) │ Gnd ┼──• •─── +3.3V │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────────┘If the Propeller module does not provide the required pull-up on SDA, you can solder a jumper across the PropCAM's J2 through-holes to deploy its onboard 4.7K pull-up. NOTE: Only P3..P0 can be used for parallel data from the KAC9630 sensor. If those pins are not available, use one of the serial drivers instead. ConstantsFixed ValuesFixed characteristics of the sensor and of this driver oject.Parameter ArrayIndex names for parameter array passed to the start method.Start Routine ArgsConstants for setting argument to start methodExposure Type NamesConstants for setting EXP_TYPEAcquisition Mask NamesConstants for calling acquireInstance VariablesSOURCE CODE... long Evens[6], Odds[6], EvenSum, OddSum, Exp100 long Stack[64] word GrayBufAddr, ParamsAddr word Begin, Skip, Width, TakeItEven, TakeItOdd byte OddCog, EvenCog, LoopCog, TakeIt, AcqMode, Started, Lock byte Scl, Sda, Vsync, Mclk Public Spin Methodsstart(mode, pins, params)Start the image capture object. Starts two additional cogs, plus a third if CONTINUOUS mode is selected.Parameters:
mode: one of SNAPSHOT, TRIGGERED, CONTINUOUS, TIMED, or SYNCED.
pins: pointer to a byte array containing pin numbers for Scl, Sda, Vsync, Mclk, Data, and HSync, in that order.
params: the address of a 10-word parameter block. (See Parameter Array in CONstants section.)
Return: true on success; false on failure..
Example: success := img.start(img#CONTINUOUS, @PinList, @ParamList)
Start the image capture in CONTINUOUS mode (starts 3 cogs), using pins in byte array PinList,
and parameters in word array ParamList.
SOURCE CODE... PUB start(mode, pins, params) : retval | exp stop AcqMode := mode bytemove(@Scl, pins, 4) if mode <> SNAPSHOT if LoopCog := cognew(do_loop(params), @Stack) + 1 retval := true else retval := false else retval := _start(params) if retval waitcnt(cnt + clkfreq >> 3) stopStop the image capture object.Return: true if object had been Started; false if there was nothing to stop.
Example: success := img.stop
Stop the image capture object, and assign result to variable success.
SOURCE CODE... PUB stop if Started if LoopCog cogstop(LoopCog - 1) LoopCog~ if EvenCog cogstop(EvenCog - 1) EvenCog~ if OddCog cogstop(OddCog - 1) OddCog~ clear_buf ctra~ dira~ Started~ if Lock lockret(Lock - 1) return true else return false set_exp(new_exp_type, new_exp_time, new_gain, new_target)Set exposure parameters.Parameters:
new_exp_type: the type of exposure control (FIXED, AUTOEXP, AUTOGAIN, or AUTOBOTH)
new_exp_time: exposure time in tens of microseconds (20 - 2047)
new_gain: sensor gain (0 -31)
new_target: target average pixel value (normalized to 0-15), times 100.
Return: none
Example: set_exp(cam#AUTOGAIN, 1667, 15, 800)
Set to operate with automatic gain control, a fixed exposure time of 1/60th second (16670 usec),
a starting gain of 15, and a target average pixel value of 8.00 out of 15.00.
SOURCE CODE... PUB set_exp(new_exp_type, new_exp_time, new_gain, new_target) repeat while lockset(Lock - 1) word[ParamsAddr][EXP_TYPE] := new_exp_type #> FIXED <# AUTOBOTH word[ParamsAddr][EXP_TIME] := new_exp_time #> MIN_EXP <# MAX_EXP word[ParamsAddr][GAIN] := new_gain #> MIN_GAIN <# MAX_GAIN word[ParamsAddr][TARGET] := new_target #> 0 <# 1500 lockclr(Lock - 1) acquire(take)Acquire an image from the PropCAM and write it to memory.Parameters:
take: a bit-mapped parameter that determines how the image is acquired.
(See bit definitions in Acquisition Mask Names in CONstants section.)
Return: true when acquisition is complete; false if object was never Started.
Example: success := img.acquire(img#GRAB)
Grab a single frame (both even and odd lines) and store into the grayscale buffer.
Save true or false in variable success.
SOURCE CODE... PUB acquire(take) | dummy if not Started return false if AcqMode == SNAPSHOT _acquire(0, take) return true else TakeIt := take repeat while TakeIt & 3 return true Private Spin Methodsdo_loop(params)Operate PropCAM in CONTINUOUS mode as a separate cog.Parameters:
params: same as passed to start
Return: none. Starts as new cog.
SOURCE CODE... PRI do_loop(params) | timer, time0, sync_loc, pll, take case AcqMode SYNCED: sync_loc := word[params][SYNC_ADDR] wait_sync(sync_loc, 1) wait_sync(sync_loc, 0) time0 := cnt wait_sync(sync_loc, 1) time0 := (cnt - time0) << 1 CONTINUOUS: time0~ TIMED: time0 := word[params][TIME_INTERVAL] << 16 ~> 16 if time0 < 0 time0 := clkfreq / (||time0 <# 30) else time0 *= clkfreq if _start(params) timer := cnt repeat if time0 waitcnt(timer += time0) take := TakeIt | TakeIt >> 4 pll := _acquire(sync_loc, take) if (take) TakeIt &= !GRAB if AcqMode == SYNCED timer += pll * time0 >> 6 time0 += pll * 256 _start(params)Low-level start method.Parameters:
params: same as passed to start.
Return: true on success; false on failure.
SOURCE CODE... PRI _start(params) | height, i, buf_addr, left, top ParamsAddr := params left := word[params][LEFT_PIX] & $FFF8 top := word[params][TOP_PIX] GrayBufAddr := word[params][GRAY_BUF_ADDR] Width := word[GrayBufAddr][1] & $FFF8 height := word[GrayBufAddr][0] if left + 128 > Width or top + 96 > height return false GrayBufAddr += 4 Skip := Width - 64 Begin := GrayBufAddr + (top * Width + left) >> 1 dira := 1 << Mclk | 1 << Vsync | 1 << Scl frqa := 1 << 29 ctra := %00100 << 26 | Mclk clear_buf Evens[0] := @Exp100 Evens[1] := constant(1344 + 378) Evens[2] := Skip Evens[3] := @TakeItEven Evens[4] := 1 << Vsync Evens[5] := @EvenSum Odds[0] := @Exp100 Odds[1] := constant(2688 + 378) Odds[2] := Skip Odds[3] := @TakeItOdd Odds[4] := 1 << Vsync Odds[5] := @OddSum Started~~ if EvenCog := cognew(@entry, @Evens) + 1 and OddCog := cognew(@entry, @Odds) + 1 and Lock := locknew + 1 _config(1, %0000_0101) '_config(1, %0100_0001) _config(8, $80) else stop return Started clear_bufClear the gray-level acquisition buffer to all black.Return: none.
SOURCE CODE... PRI clear_buf | i return repeat i from 0 to 95 longfill(Begin + (Width >> 1) * i, 0, 16) wait_sync(sync_loc, state)Wait for sync in overlay driver to equal state. (Sync alternates on every vertical sync.)Parameters:
sync_loc: address of the overlay driver's sync byte.
state: sync state.
Return: returns after sync state == state.
SOURCE CODE... PRI wait_sync(sync_loc, state) repeat while (byte[sync_loc] <> 0) ^ (state <> 0) _acquire(sync_loc, now)Internal acquisition method, called by acquire and by do_loop.Parameters:
sync_loc: address of image overlay sync byte.
now: lower four bits defined by Acquisition Mask in CONstants section.
SOURCE CODE... PRI _acquire(sync_loc, now) : sync | avgpix, exp, sens, targ TakeItEven := (Begin + ((WIDTH / PIXELS_PER_LONG) << 2) & (now & EVEN_TO_ODD <> 0)) & (now & GRAB_EVEN <> 0) TakeItOdd := (Begin + ((WIDTH / PIXELS_PER_LONG) << 2) & (now & ODD_TO_EVEN == 0)) & (now & GRAB_ODD <> 0) exp := word[ParamsAddr][EXP_TIME] #> MIN_EXP <# MAX_EXP Exp100 := exp * 800 sens := word[ParamsAddr][GAIN] #> MIN_GAIN <# MAX_GAIN _config(2, sens) _config(3, exp & $FF) _config(5, exp >> 8) sync := 0 outa[Vsync]~~ waitcnt(cnt + 10000) outa[Vsync]~ repeat while (TakeItEven or TakeItOdd) if byte[sync_loc] sync := 1 avgpix := (EvenSum + OddSum) * 100 / constant(SAMPLES_PER_SUM * PIXEL_DEPTH / 16) word[ParamsAddr][AVG_PIX] := avgpix <# 1500 repeat while lockset(Lock - 1) targ := word[ParamsAddr][TARGET] case word[ParamsAddr][EXP_TYPE] #> FIXED <# AUTOBOTH AUTOEXP: exp := (exp * (90 + 10 * targ / (avgpix + 1)) / 100) #> MIN_EXP <# MAX_EXP word[ParamsAddr][EXP_TIME] := exp AUTOGAIN: if avgpix < targ * 94 / 100 sens := sens + 1 <# $1f elseif avgpix > targ * 106 / 100 sens := sens - 1 #> 0 word[ParamsAddr][GAIN] := sens AUTOBOTH: if avgpix < targ * 94 / 100 and (sens < $0f or exp == MAX_EXP) sens := sens + 1 <# 31 elseif avgpix > targ * 108 / 100 and (sens > $0f or exp == MIN_EXP) sens := sens - 1 #> 0 else exp := (exp * (90 + 10 * targ / (avgpix + 1)) / 100) #> MIN_EXP <# MAX_EXP word[ParamsAddr][EXP_TIME] := exp word[ParamsAddr][GAIN] := sens lockclr(Lock - 1) if byte[sync_loc] sync |= 2 case sync 0, 1: sync := -1 2: sync := 0 3: sync := 1 return sync _config(register, data)Write configuration information to sensor chip register.Parameters:
register: sensor chip's register address.
data: byte to write in register.
Return: none
SOURCE CODE... PRI _config(register, data) outa[Scl]~~ dira[Sda]~ outa[Sda]~ dira[Sda]~~ outa[Scl]~ _write($88) _write(register) _write(data) dira[Sda]~~ outa[Scl]~~ dira[Sda]~ _write(data)Low-level I2C write method.Parameters:
data: byte to write to I2C
SOURCE CODE... PRI _write(data) repeat 8 dira[Sda] := data & $80 == 0 outa[Scl]~~ data <<= 1 outa[Scl]~ dira[Sda]~ outa[Scl]~~ repeat while ina[Sda] outa[Scl]~ PASM CodeSOURCE CODE... entry mov par_ptr,par rdlong exp_100,par_ptr add par_ptr,#4 rdlong init_wait,par_ptr add par_ptr,#4 rdlong vskip,par_ptr add par_ptr,#4 rdlong snap,par_ptr add par_ptr,#4 rdlong _vsync,par_ptr add par_ptr,#4 rdlong sumaddr,par_ptr :frame waitpne _vsync,_vsync waitpeq _vsync,_vsync mov time,cnt rdlong _time,exp_100 add time,_time add time,init_wait rdword snapping,snap wrword $,snap mov ptr,snapping mov line_ctr,#ROWS_PER_FRAME / 2 mov pixsum,#0 :line waitcnt time,interline movi line_buf+0,ina shr line_buf+0,#4 movi line_buf+0,ina shr line_buf+0,#4 movi line_buf+0,ina shr line_buf+0,#4 movi line_buf+0,ina shr line_buf+0,#11 movi line_buf+1,ina shr line_buf+1,#4 movi line_buf+1,ina shr line_buf+1,#4 movi line_buf+1,ina shr line_buf+1,#4 movi line_buf+1,ina shl line_buf+1,#5 movi line_buf+2,ina shr line_buf+2,#4 movi line_buf+2,ina shr line_buf+2,#4 movi line_buf+2,ina shr line_buf+2,#4 movi line_buf+2,ina shr line_buf+2,#11 movi line_buf+3,ina shr line_buf+3,#4 movi line_buf+3,ina shr line_buf+3,#4 movi line_buf+3,ina shr line_buf+3,#4 movi line_buf+3,ina shl line_buf+3,#5 movi line_buf+4,ina shr line_buf+4,#4 movi line_buf+4,ina shr line_buf+4,#4 movi line_buf+4,ina shr line_buf+4,#4 movi line_buf+4,ina shr line_buf+4,#11 movi line_buf+5,ina shr line_buf+5,#4 movi line_buf+5,ina shr line_buf+5,#4 movi line_buf+5,ina shr line_buf+5,#4 movi line_buf+5,ina shl line_buf+5,#5 movi line_buf+6,ina shr line_buf+6,#4 movi line_buf+6,ina shr line_buf+6,#4 movi line_buf+6,ina shr line_buf+6,#4 movi line_buf+6,ina shr line_buf+6,#11 movi line_buf+7,ina shr line_buf+7,#4 movi line_buf+7,ina shr line_buf+7,#4 movi line_buf+7,ina shr line_buf+7,#4 movi line_buf+7,ina shl line_buf+7,#5 movi line_buf+8,ina shr line_buf+8,#4 movi line_buf+8,ina shr line_buf+8,#4 movi line_buf+8,ina shr line_buf+8,#4 movi line_buf+8,ina shr line_buf+8,#11 movi line_buf+9,ina shr line_buf+9,#4 movi line_buf+9,ina shr line_buf+9,#4 movi line_buf+9,ina shr line_buf+9,#4 movi line_buf+9,ina shl line_buf+9,#5 movi line_buf+10,ina shr line_buf+10,#4 movi line_buf+10,ina shr line_buf+10,#4 movi line_buf+10,ina shr line_buf+10,#4 movi line_buf+10,ina shr line_buf+10,#11 movi line_buf+11,ina shr line_buf+11,#4 movi line_buf+11,ina shr line_buf+11,#4 movi line_buf+11,ina shr line_buf+11,#4 movi line_buf+11,ina shl line_buf+11,#5 movi line_buf+12,ina shr line_buf+12,#4 movi line_buf+12,ina shr line_buf+12,#4 movi line_buf+12,ina shr line_buf+12,#4 movi line_buf+12,ina shr line_buf+12,#11 movi line_buf+13,ina shr line_buf+13,#4 movi line_buf+13,ina shr line_buf+13,#4 movi line_buf+13,ina shr line_buf+13,#4 movi line_buf+13,ina shl line_buf+13,#5 movi line_buf+14,ina shr line_buf+14,#4 movi line_buf+14,ina shr line_buf+14,#4 movi line_buf+14,ina shr line_buf+14,#4 movi line_buf+14,ina shr line_buf+14,#11 movi line_buf+15,ina shr line_buf+15,#4 movi line_buf+15,ina shr line_buf+15,#4 movi line_buf+15,ina shr line_buf+15,#4 movi line_buf+15,ina shl line_buf+15,#5 movi line_buf+16,ina shr line_buf+16,#4 movi line_buf+16,ina shr line_buf+16,#4 movi line_buf+16,ina shr line_buf+16,#4 movi line_buf+16,ina shr line_buf+16,#11 movi line_buf+17,ina shr line_buf+17,#4 movi line_buf+17,ina shr line_buf+17,#4 movi line_buf+17,ina shr line_buf+17,#4 movi line_buf+17,ina shl line_buf+17,#5 movi line_buf+18,ina shr line_buf+18,#4 movi line_buf+18,ina shr line_buf+18,#4 movi line_buf+18,ina shr line_buf+18,#4 movi line_buf+18,ina shr line_buf+18,#11 movi line_buf+19,ina shr line_buf+19,#4 movi line_buf+19,ina shr line_buf+19,#4 movi line_buf+19,ina shr line_buf+19,#4 movi line_buf+19,ina shl line_buf+19,#5 movi line_buf+20,ina shr line_buf+20,#4 movi line_buf+20,ina shr line_buf+20,#4 movi line_buf+20,ina shr line_buf+20,#4 movi line_buf+20,ina shr line_buf+20,#11 movi line_buf+21,ina shr line_buf+21,#4 movi line_buf+21,ina shr line_buf+21,#4 movi line_buf+21,ina shr line_buf+21,#4 movi line_buf+21,ina shl line_buf+21,#5 movi line_buf+22,ina shr line_buf+22,#4 movi line_buf+22,ina shr line_buf+22,#4 movi line_buf+22,ina shr line_buf+22,#4 movi line_buf+22,ina shr line_buf+22,#11 movi line_buf+23,ina shr line_buf+23,#4 movi line_buf+23,ina shr line_buf+23,#4 movi line_buf+23,ina shr line_buf+23,#4 movi line_buf+23,ina shl line_buf+23,#5 movi line_buf+24,ina shr line_buf+24,#4 movi line_buf+24,ina shr line_buf+24,#4 movi line_buf+24,ina shr line_buf+24,#4 movi line_buf+24,ina shr line_buf+24,#11 movi line_buf+25,ina shr line_buf+25,#4 movi line_buf+25,ina shr line_buf+25,#4 movi line_buf+25,ina shr line_buf+25,#4 movi line_buf+25,ina shl line_buf+25,#5 movi line_buf+26,ina shr line_buf+26,#4 movi line_buf+26,ina shr line_buf+26,#4 movi line_buf+26,ina shr line_buf+26,#4 movi line_buf+26,ina shr line_buf+26,#11 movi line_buf+27,ina shr line_buf+27,#4 movi line_buf+27,ina shr line_buf+27,#4 movi line_buf+27,ina shr line_buf+27,#4 movi line_buf+27,ina shl line_buf+27,#5 movi line_buf+28,ina shr line_buf+28,#4 movi line_buf+28,ina shr line_buf+28,#4 movi line_buf+28,ina shr line_buf+28,#4 movi line_buf+28,ina shr line_buf+28,#11 movi line_buf+29,ina shr line_buf+29,#4 movi line_buf+29,ina shr line_buf+29,#4 movi line_buf+29,ina shr line_buf+29,#4 movi line_buf+29,ina shl line_buf+29,#5 movi line_buf+30,ina shr line_buf+30,#4 movi line_buf+30,ina shr line_buf+30,#4 movi line_buf+30,ina shr line_buf+30,#4 movi line_buf+30,ina shr line_buf+30,#11 movi line_buf+31,ina shr line_buf+31,#4 movi line_buf+31,ina shr line_buf+31,#4 movi line_buf+31,ina shr line_buf+31,#4 movi line_buf+31,ina shl line_buf+31,#5 movs :slice,#line_buf movs :second,#line_buf+1 mov n,#LONGS_PER_ROW :slice mov x,0-0 and x,_0x0000ffff :second mov y,0-0 and y,_0xffff0000 or x,y test snapping,snapping wz if_nz wrlong x,ptr and x,#$0f add pixsum,x add ptr,#4 add :slice,#2 add :second,#2 djnz n,#:slice :nxtline add ptr,vskip djnz line_ctr,#:line wrlong pixsum,sumaddr wrword zero,snap jmp #:frame _0x0000ffff long $0000ffff _0xffff0000 long $ffff0000 zero long 0 interline long 2688 line_buf res 32 par_ptr res 1 exp_100 res 1 sumaddr res 1 pixsum res 1 init_wait res 1 vskip res 1 _vsync res 1 snap res 1 snapping res 1 line_ctr res 1 x res 1 y res 1 n res 1 ptr res 1 time res 1 _time res 1 License┌──────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this │ │software and associated documentation files (the "Software"), to deal in the Software │ │without restriction, including without limitation the rights to use, copy, modify, │ │merge, publish, distribute, sublicense, and/or sell copies of the Software, and to │ │permit persons to whom the Software is furnished to do so, subject to the following │ │conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies │ │or substantial portions of the Software. │ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, │ │INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A │ │PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT │ │HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF │ │CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE │ │OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────┘ |