Subversion Repositories svnkaklik

Rev

Go to most recent revision | Details | Last modification | View Log

Rev Author Line No. Line
151 kaklik 1
;
2
;    Copyright (C) 2004    John Orlando
3
;    
4
;   AVRcam: a small real-time image processing engine.
5
 
6
;    This program is free software; you can redistribute it and/or
7
;    modify it under the terms of the GNU General Public
8
;    License as published by the Free Software Foundation; either
9
;    version 2 of the License, or (at your option) any later version.
10
 
11
;    This program is distributed in the hope that it will be useful,
12
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
;    General Public License for more details.
15
 
16
;    You should have received a copy of the GNU General Public
17
;    License along with this program; if not, write to the Free Software
18
;    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 
20
;   For more information on the AVRcam, please contact:
21
 
22
;   john@jrobot.net
23
 
24
;   or go to www.jrobot.net for more details regarding the system.
25
;**********************************************************************
26
;       Module Name: CanInterfaceAsm.S
27
;       Module Date: 04/14/2004
28
;       Module Auth: John Orlando
29
;
30
;       Description: This module provides the low-level interface
31
;       to the OV6620 camera hardware.  It is responsible for
32
;   	acquiring each pixel block (R,G,B), performing the mapping
33
;       into an actual color (orange, purple, etc), run-length
34
;       encoding the data, and storing the info off to the appropriate
35
;       line buffer.  This routine is synchronized with the pixel data
36
;       so that no polling of the camera data needs to be done (the
37
;       OV6620 is clocked off of the same crystal source as the mega8,
38
;       thus providing inherent synchronization between the two).
39
;
40
;       Revision History:
41
;       Date        Rel Ver.    Notes
42
;       4/10/2004      0.1     Module created
43
;       6/30/2004      1.0     Initial release for Circuit Cellar
44
;                              contest.
45
;       1/16/2005      1.4     Fixed issue with the TCCR1B register
46
;                              where PCLK was getting routed to the
47
;                              timer1 even when it wasn't needed.
48
;                              This caused excessive counter overflow
49
;                              interrupts, and caused problems.  Now,
50
;                              the "PCLK" pipe feeds timer1 when needed,
51
;                              and is turned off when it isn't needed.
52
 
53
#include <avr/io.h>
54
#include "Events.h"
55
 
56
		.extern fastEventBitmask    ; This is the flag used to indicate to the rest
57
									; of the system that the line is complete
58
 
59
#define HREF_INTERRUPT_ENABLE_MASK   0x80
60
#define HREF_INTERRUPT_DISABLE_MASK  0x7F
61
#define ENABLE_PCLK_TIMER1_OVERFLOW_BITMASK  0x04
62
#define DISABLE_PCLK_TIMER1_OVERFLOW_BITMASK 0xFB
63
#define G_PORT						_SFR_IO_ADDR(PINC)  
64
#define RB_PORT						_SFR_IO_ADDR(PINB)  
65
#define PIXEL_RUN_START_INITIAL     0x50     	; This value causes our pixel counter (TCNT1)
66
												; to overflow after 176 (horizontal) pixels
67
 
68
#define RED_MEM_OFFSET				0x00
69
#define GREEN_MEM_OFFSET			0x10
70
#define BLUE_MEM_OFFSET				0x20
71
 
72
; A pixelBlock is defined as a contiguous group of 4 pixels that are combined 
73
; together to form a specific color.  Typically, this is formed by sampling a
74
; a green value, followed by a red and blue value (since we are dealing
75
; with Bayer color data).  We could optionally sample a second green with
76
; the red and average the greens, because the eye is more sensitive to
77
; green, but for speed we don't do this.  These three values (RGB) are then
78
; used as indices into the color membership lookup table (memLookup) to
79
; determine which color the pixelBlock maps into.  The memLookup table is
80
; manually generated for now (though it will hopefully be modified over
81
; the serial interface eventually).
82
;
83
; Here is a pixel block:
84
; ...G  G  G  G...  (row x)
85
; ...B  R  B  R...  (row x+1)
86
;    |  |  |  |--this is skipped 
87
;    |  |  |--this is skipped
88
;    |  |--this is sampled
89
;    |--this is sampled
90
 
91
; As pixel blocks are sampled, the red, green, and blue values are
92
; used to index into their respective color maps.  The color maps
93
; return values that can be logically ANDed together so that a 
94
; particular RGB triplet will result in a single bit being set
95
; after the AND operation.  This single bit indicates which color
96
; the RGB triplet represents.  It is also possible for no bits to
97
; be set after the AND process, indicating that the RGB triplet
98
; does not map to any of the colors configured in the color map.
99
; This isn't quite as fast as a pure RGB lookup table, but
100
; it then again it doesn't require 2^12 (4-bits for each color
101
; channel) bytes to store the lookup table.  It takes just a few
102
; more cycles, and only requires 48 bytes of precious RAM (16
103
; per color channel, since our resolution on each color channel
104
; is only 4-bits).  Not bad....for more information, see:
105
; http://www.cs.cmu.edu/~trb/papers/wirevision00.pdf for more
106
; information on this color segmentation technique.
107
 
108
; One other note: this code does depend on the colorMap residing
109
; at a well-defined position in memory; specifically, it mus
110
; start at a 256-byte boundary so that the lowest byte in the
111
; map is set to 0x00.  Currently, the colorMap is forced to
112
; start at RAM location 0x300.  This could potentially be changed
113
; by the developer if needed, but offsets would have to be added
114
; in to the colorMap look-up code below to make it work.
115
 
116
 
117
; These are the registers that will be used throughout this
118
; module for acquiring each line of pixel data
119
pixelCount			= 16
120
pixelRunStart		= 17
121
lastColor     		= 18
122
tmp1				= 19	; be sure to not use tmp1 and color simultaneously
123
tmp2				= 20
124
color           	= 19
125
greenData       	= 20
126
blueData        	= 21
127
colorMapLow	  		= 22
128
colorMapHigh		= 23
129
prevLineBuffLow  	= 22  	; overlaps with memLookupLow (but orthogonal)
130
prevLineBuffHigh	= 23	; overlaps with memLookupHigh (but orthogonal)
131
currLineBuffLow     = 24
132
currLineBuffHigh  	= 25
133
 
134
        .section .text
135
 
136
; These are the global assembly function names that are accessed via other
137
; C functions
138
        .global CamIntAsm_waitForNewTrackingFrame
139
		.global CamIntAsm_waitForNewDumpFrame
140
		.global CamIntAsm_acquireDumpLine
141
		.global CamIntAsm_acquireTrackingLine
142
		.global SIG_INTERRUPT0
143
		.global SIG_INTERRUPT1
144
		.global SIG_OVERFLOW0
145
		.global SIG_OVERFLOW1
146
 
147
;*****************************************************************		
148
;   	Function Name: CamIntAsm_waitForNewTrackingFrame
149
;       Function Description: This function is responsible for
150
;       going to sleep until a new frame begins (indicated by
151
;    	VSYNC transitioning from low to high.  This will wake
152
;       the "VSYNC sleep" up and allow it to continue with 
153
;       the acquireLine function, where the system waits for
154
;       an "HREF sleep" that we use to synchronize with the
155
;       data.  
156
;       Inputs:  r25 - MSB of currentLineBuffer
157
;                r24 - LSB of currentLineBuffer
158
;				 r23 - MSB of colorMap
159
; 				 r22 - LSB of colorMap
160
;       Outputs: none
161
;       NOTES: This function doesn't really return...it sorta just
162
;       floats into the acquireLine function after the "VSYNC sleep"
163
;       is awoken, then begins processing the line data.  Once
164
;		176 pixels are sampled (and the counter overflows), then
165
;		an interrupt will occur, the 'T' bit in the SREG will be
166
;		set, and the function will return.
167
;*****************************************************************
168
 
169
CamIntAsm_waitForNewTrackingFrame:
170
		sbi		_SFR_IO_ADDR(PORTD),PD6  ; For testing...
171
		cbi		_SFR_IO_ADDR(PORTD),PD6		
172
		sleep
173
 
174
;*****************************************************************
175
; REMEMBER...everything from here on out is critically timed to be
176
; synchronized with the flow of pixel data from the camera...
177
;*****************************************************************
178
 
179
CamIntAsm_acquireTrackingLine:
180
		brts	_cleanUp
181
		;sbi		_SFR_IO_ADDR(PORTD),PD6 ; For testing...
182
		;cbi		_SFR_IO_ADDR(PORTD),PD6
183
 
184
        in      tmp1,_SFR_IO_ADDR(TCCR1B) ; Enable the PCLK line to actually
185
        ori     tmp1, 0x07                 ; feed Timer1
186
        out     _SFR_IO_ADDR(TCCR1B),tmp1 
187
										; The line is about to start...		
188
		ldi     pixelCount,0			; Initialize the RLE stats...
189
		ldi		pixelRunStart,PIXEL_RUN_START_INITIAL  	; Remember, we always calculate
190
														; the pixel run length as
191
														; TCNT1L - pixelRunStart
192
 
193
		ldi		lastColor,0x00				; clear out the last color before we start
194
 
195
		mov   	XH,currLineBuffHigh    	; Load the pointer to the current line
196
		mov		XL,currLineBuffLow		; buffer into the X pointer regs		 
197
 
198
		mov   	ZH,colorMapHigh      	; Load the pointers to the membership
199
		mov		ZL,colorMapLow			; lookup tables (ZL and YL will be overwritten
200
		mov 	YH,colorMapHigh	 		; as soon as we start reading data) to Z and Y
201
 
202
		in		tmp1, _SFR_IO_ADDR(TIMSK)			; enable TIMER1 to start counting
203
		ori		tmp1, ENABLE_PCLK_TIMER1_OVERFLOW_BITMASK 	; external PCLK pulses and interrupt on 
204
		out		_SFR_IO_ADDR(TIMSK),tmp1			; overflow
205
 
206
		ldi 	tmp1,PIXEL_RUN_START_INITIAL	; set up the TCNT1 to overflow (and
207
		ldi 	tmp2,0xFF 						; interrupts) after 176 pixels		
208
		out 	_SFR_IO_ADDR(TCNT1H),tmp2		
209
		out 	_SFR_IO_ADDR(TCNT1L),tmp1				
210
 
211
		mov		YL,colorMapLow		
212
 
213
		in 		tmp1, _SFR_IO_ADDR(GICR)	; enable the HREF interrupt...remember, we
214
											; only use this interrupt to synchronize
215
											; the beginning of the line
216
		ori 	tmp1, HREF_INTERRUPT_ENABLE_MASK
217
		out		_SFR_IO_ADDR(GICR), tmp1
218
 
219
;*******************************************************************************************
220
;   Track Frame handler 
221
;*******************************************************************************************		
222
 
223
_trackFrame:		
224
		sbi		_SFR_IO_ADDR(PORTD),PD6
225
		sleep   ; ...And we wait...
226
 
227
	; Returning from the interrupt/sleep wakeup will consume
228
	; 14 clock cycles (7 to wakeup from idle sleep, 3 to vector, and 4 to return)	
229
 
230
	; Disable the HREF interrupt
231
		cbi		_SFR_IO_ADDR(PORTD),PD6
232
		in 		tmp1, _SFR_IO_ADDR(GICR)
233
		andi 	tmp1, HREF_INTERRUPT_DISABLE_MASK
234
		out		_SFR_IO_ADDR(GICR), tmp1
235
 
236
	; A couple of NOPs are needed here to sync up the pixel data...the number (2)
237
	; of NOPs was determined emperically by trial and error.
238
		nop
239
		nop
240
_acquirePixelBlock:							;							Clock Cycle Count
241
		in		ZL,RB_PORT         			; sample the red value (PINB)		(1)
242
		in		YL,G_PORT         			; sample the green value (PINC)		(1)
243
		andi	YL,0x0F            			; clear the high nibble				(1)
244
		ldd		color,Z+RED_MEM_OFFSET  	; lookup the red membership			(2)
245
		in		ZL,RB_PORT         			; sample the blue value (PINB)		(1)
246
		ldd		greenData,Y+GREEN_MEM_OFFSET; lookup the green membership		(2)
247
		ldd		blueData,Z+BLUE_MEM_OFFSET	; lookup the blue membership		(2)
248
		and		color,greenData 			; mask memberships together			(1)
249
		and		color,blueData  			; to produce the final color		(1)
250
		brts    _cleanUpTrackingLine		; if some interrupt routine has		(1...not set)
251
											; come in and set our T flag in 
252
											; SREG, then we need to hop out
253
											; and blow away this frames data (common cleanup)									
254
		cp		color,lastColor     		; check to see if the run continues	(1)
255
		breq    _acquirePixelBlock  		;									(2...equal)
256
											;									___________
257
											;								16 clock cycles 		
258
											; (16 clock cycles = 1 uS = 1 pixelBlock time)
259
 
260
		; Toggle the debug line to indicate a color change
261
		sbi     _SFR_IO_ADDR(PORTD),PD6
262
		nop
263
		cbi		_SFR_IO_ADDR(PORTD),PD6
264
 
265
		mov		tmp2,pixelRunStart				; get the count value of the
266
												; current pixel run
267
		in		pixelCount,_SFR_IO_ADDR(TCNT1L)	; get the current TCNT1 value 
268
		mov   	pixelRunStart,pixelCount		; reload pixelRunStart for the
269
												; next run
270
		sub		pixelCount,tmp2     			; pixelCount = TCNT1L - pixelRunStart
271
 
272
		st		X+,lastColor			; record the color run in the current line buffer
273
		st		X+,pixelCount			; with its length
274
		mov		lastColor,color			; set lastColor so we can figure out when it changes
275
 
276
		nop								; waste one more cycle for a total of 16
277
		rjmp	_acquirePixelBlock	
278
 
279
; _cleanUpTrackingLine is used to write the last run length block off to the currentLineBuffer so
280
; that all 176 pixels in the line are accounted for.
281
_cleanUpTrackingLine:		
282
		ldi		pixelCount,0xFF		; the length of the last run is ALWAYS 0xFF minus the last
283
		sub		pixelCount,pixelRunStart  	; pixelRunStart
284
 
285
		inc		pixelCount				; increment pixelCount since we actually need to account
286
										; for the overflow of TCNT1
287
 
288
		st		X+,color				; record the color run in the current line buffer
289
		st		X,pixelCount		
290
		rjmp	_cleanUp
291
 
292
_cleanUpDumpLine:		
293
		; NOTE: If serial data is received, to interrupt the tracking of a line, we'll
294
		; get a EV_SERIAL_DATA_RECEIVED event, and the T bit set so we will end the
295
		; line's processing...however, the PCLK will keep on ticking for the rest of
296
		; the frame/line, which will cause the TCNT to eventually overflow and
297
		; interrupt us, generating a EV_ACQUIRE_LINE_COMPLETE event.  We don't want
298
		; this, so we need to actually turn off the PCLK counting each time we exit
299
		; this loop, and only turn it on when we begin acquiring lines....
300
        ; NOT NEEDED FOR NOW...
301
		;in		tmp1, _SFR_IO_ADDR(TIMSK)			; disable TIMER1 to stop counting
302
		;andi	tmp1, DISABLE_PCLK_TIMER1_OVERFLOW_BITMASK 	; external PCLK pulses
303
		;out		_SFR_IO_ADDR(TIMSK),tmp1
304
 
305
_cleanUp:
306
        ; Disable the external clocking of the Timer1 counter 
307
        in      tmp1, _SFR_IO_ADDR(TCCR1B)
308
        andi    tmp1, 0xF8
309
        out     _SFR_IO_ADDR(TCCR1B),tmp1
310
 
311
		; Toggle the debug line to indicate the line is complete
312
		sbi     _SFR_IO_ADDR(PORTD),PD6
313
		cbi		_SFR_IO_ADDR(PORTD),PD6
314
		clt				; clear out the T bit since we have detected
315
						; the interruption and are exiting to handle it
316
_exit:
317
		ret
318
 
319
;*****************************************************************		
320
;   	Function Name: CamIntAsm_waitForNewDumpFrame
321
;       Function Description: This function is responsible for
322
;       going to sleep until a new frame begins (indicated by
323
;    	VSYNC transitioning from low to high.  This will wake
324
;       the "VSYNC sleep" up and allow it to continue with 
325
;       acquiring a line of pixel data to dump out to the UI.
326
;       Inputs:  r25 - MSB of currentLineBuffer
327
;                r24 - LSB of currentLineBuffer
328
;				 r23 - MSB of prevLineBuffer
329
;				 r22 - LSB of prevLineBuffer
330
;       Outputs: none
331
;       NOTES: This function doesn't really return...it sorta just
332
;       floats into the acquireDumpLine function after the "VSYNC sleep"
333
;       is awoken.
334
;*****************************************************************		
335
CamIntAsm_waitForNewDumpFrame:
336
		sbi		_SFR_IO_ADDR(PORTD),PD6  ; For testing...
337
		cbi		_SFR_IO_ADDR(PORTD),PD6
338
		sleep
339
 
340
;*****************************************************************
341
; REMEMBER...everything from here on out is critically timed to be
342
; synchronized with the flow of pixel data from the camera...
343
;*****************************************************************
344
 
345
CamIntAsm_acquireDumpLine:
346
		brts	_cleanUp
347
		;sbi		_SFR_IO_ADDR(PORTD),PD6 ; For testing...
348
		;cbi		_SFR_IO_ADDR(PORTD),PD6
349
 
350
		mov   	XH,currLineBuffHigh    	; Load the pointer to the current line
351
		mov		XL,currLineBuffLow		; buffer into the X pointer regs
352
 
353
		mov		YH,prevLineBuffHigh		; Load the pointer to the previous line
354
		mov		YL,prevLineBuffLow  	; buffer into the Y pointer regs
355
 
356
		ldi 	tmp1,PIXEL_RUN_START_INITIAL	; set up the TCNT1 to overflow (and
357
		ldi 	tmp2,0xFF 						; interrupts) after 176 pixels		
358
		out 	_SFR_IO_ADDR(TCNT1H),tmp2		
359
		out 	_SFR_IO_ADDR(TCNT1L),tmp1		
360
 
361
        in      tmp1, _SFR_IO_ADDR(TCCR1B) ; Enable the PCLK line to actually
362
        ori     tmp1, 0x07                 ; feed Timer1
363
        out     _SFR_IO_ADDR(TCCR1B),tmp1
364
        nop
365
 
366
		in		tmp1, _SFR_IO_ADDR(TIMSK)			; enable TIMER1 to start counting
367
		ori		tmp1, ENABLE_PCLK_TIMER1_OVERFLOW_BITMASK 	; external PCLK pulses and interrupt on 
368
		out		_SFR_IO_ADDR(TIMSK),tmp1			; overflow			
369
 
370
		in 		tmp1, _SFR_IO_ADDR(GICR)	; enable the HREF interrupt...remember, we
371
											; only use this interrupt to synchronize
372
											; the beginning of the line
373
		ori 	tmp1, HREF_INTERRUPT_ENABLE_MASK
374
		out		_SFR_IO_ADDR(GICR), tmp1
375
 
376
;*******************************************************************************************
377
;   Dump Frame handler 
378
;*******************************************************************************************		
379
 
380
_dumpFrame:		
381
		sbi		_SFR_IO_ADDR(PORTD),PD6
382
		sleep   ; ...And we wait...
383
 
384
		cbi		_SFR_IO_ADDR(PORTD),PD6
385
		in 		tmp1, _SFR_IO_ADDR(GICR)			; disable the HREF interrupt
386
		andi 	tmp1, HREF_INTERRUPT_DISABLE_MASK  	; so we don't get interrupted
387
		out		_SFR_IO_ADDR(GICR), tmp1			; while dumping the line
388
 
389
		nop		; Remember...if we ever remove the "cbi" instruction above,
390
				; we need to add two more NOPs to cover this
391
 
392
; Ok...the following loop needs to run in 8 clock cycles, so we can get every
393
; pixel in the line...this shouldn't be a problem, since the PCLK timing was
394
; reduced by a factor of 2 whenever we go to dump a line (this is to give us
395
; enough time to do the sampling and storing of the pixel data).  In addition,
396
; it is assumed that we will have to do some minor processing on the data right
397
; before we send it out, like mask off the top 4-bits of each, and then pack both
398
; low nibbles into a single byte for transmission...we just don't have time to
399
; do that here (only 8 instruction cycles :-)  )
400
_sampleDumpPixel:
401
		in		tmp1,G_PORT				; sample the G value					(1)
402
		in		tmp2,RB_PORT			; sample the R/B value					(1)
403
		st		X+,tmp1					; store to the currLineBuff and inc ptrs(2)
404
		st		Y+,tmp2					; store to the prevLineBuff and inc ptrs(2)
405
		brtc	_sampleDumpPixel		; loop back unless flag is set			(2...if not set)
406
										;									___________
407
										;									8 cycles normally
408
 
409
		; if we make it here, it means the T flag is set, and we must have been interrupted
410
		; so we need to exit (what if we were interrupted for serial? should we disable it?)
411
		rjmp	_cleanUpDumpLine
412
 
413
;***********************************************************
414
;	Function Name: <interrupt handler for External Interrupt0> 
415
;	Function Description: This function is responsible
416
;	for handling a rising edge on the Ext Interrupt 0.  This
417
;	routine simply returns, since we just want to wake up
418
;	whenever the VSYNC transitions (meaning the start of a new
419
;	frame).
420
;	Inputs:  none
421
;	Outputs: none
422
;***********************************************************
423
SIG_INTERRUPT0:
424
; This will wake us up when VSYNC transitions high...we just want to return
425
		reti
426
 
427
;***********************************************************
428
;	Function Name: <interrupt handler for External Interrupt1> 
429
;	Function Description: This function is responsible
430
;	for handling a falling edge on the Ext Interrupt 1.  This
431
;	routine simply returns, since we just want to wake up
432
;	whenever the HREF transitions (meaning the pixels 
433
;	are starting after VSYNC transitioned, and we need to
434
; 	start acquiring the pixel blocks
435
;	Inputs:  none
436
;	Outputs: none
437
;***********************************************************	
438
SIG_INTERRUPT1:
439
; This will wake us up when HREF transitions high...we just want to return
440
		reti
441
 
442
;***********************************************************
443
;	Function Name: <interrupt handler for Timer0 overflow>
444
;	Function Description: This function is responsible
445
;	for handling the Timer0 overflow (hooked up to indicate
446
;	when we have reached the number of HREFs required in a
447
;	single frame).  We set the T flag in the SREG to
448
;	indicate to the _acquirePixelBlock routine that it needs
449
;	to exit, and then set the appropriate action to take in
450
;	the eventList of the Executive module.
451
;	Inputs:  none
452
;	Outputs: none
453
;   Note: Originally, the HREF pulses were also going to
454
;   be counted by a hardware counter, but it didn't end up
455
;   being necessary
456
;***********************************************************
457
;SIG_OVERFLOW0:
458
;		set				; set the T bit in SREG
459
;		lds		tmp1,eventBitmask
460
;		ori		tmp1,EV_ACQUIRE_FRAME_COMPLETE
461
;		sts		eventBitmask,tmp1
462
;		reti
463
 
464
;***********************************************************
465
;	Function Name: <interrupt handler for Timer1 overflow>
466
;	Function Description: This function is responsible
467
;	for handling the Timer1 overflow (hooked up to indicate
468
;	when we have reached the end of a line of pixel data,
469
;	since PCLK is hooked up to overflow TCNT1 after 176 
470
;	pixels).  This routine generates an acquire line complete
471
;	event in the fastEventBitmask, which is streamlined for
472
;	efficiency reasons.
473
;***********************************************************
474
SIG_OVERFLOW1:				
475
		lds		tmp1,fastEventBitmask   		; set a flag indicating
476
		ori		tmp1,FEV_ACQUIRE_LINE_COMPLETE	; a line is complete
477
		sts		fastEventBitmask,tmp1
478
		set		; set the T bit in SREG 
479
		;sbi		_SFR_IO_ADDR(PORTD),PD6 ; For testing...
480
		;cbi		_SFR_IO_ADDR(PORTD),PD6 ; For testing...
481
 
482
		reti
483
 
484
; This is the default handler for all interrupts that don't
485
; have handler routines specified for them.
486
        .global __vector_default              
487
__vector_default:
488
        reti
489
 
490
        .end