BAP as a logic analyzer

There are several different things I have found it found it very useful to have a logic analyzer to test the different IO levels. One of the best purchases I have made in awhile was the logic analyzer by www.saleae.com for $150. As I was getting set up to be able to test the DIY receiver again I decided to see how hard it would be to write a rudimentary one that ran on the BAP that I was going to test the DIY receiver to see if I was generating the proper timings on the transmitter side. Note: I have not changed the transmiter side code yet…

So I hacked a bit this afternoon. It is not pretty yet, but I do think it is sort-of starting to work…

The current code waits until you hit ButtonA on the BB2 and then calls a function that is mostly assembly code that captures the state of IO pins 0-7 and records when the change happened. It times out pretty quickly (probably pretty quickly < 15 seconds or when the capture buffer of the number of events is full. I currently can capture 256 changes.

I then have a function that simply dumps the changes and it tries to figure out what changed, like IO pin 0 went to a 1 (high) and it remembers the time that happened and also prints out delta times per each pin. It is not pretty, would be nicer to have it talk to a VB app that does graphics or the like, but…

Here is part of an output with the 7 IO lines of the DIY receiver plugged into IO pins 0-6

Count of items256 T:7926(1EF6) IO:08 Bit: 3->1 DT:7926 T:9426(24D2) IO:00 Bit: 3->0 DT:1500 T:9488(2510) IO:04 Bit: 2->1 DT:9488 T:10988(2AEC) IO:00 Bit: 2->0 DT:1500 T:11049(2B29) IO:02 Bit: 1->1 DT:11049 T:12549(3105) IO:00 Bit: 1->0 DT:1500 T:12609(3141) IO:20 Bit: 5->1 DT:12609 T:14338(3802) IO:00 Bit: 5->0 DT:1729 T:14400(3840) IO:01 Bit: 0->1 DT:14400 T:15399(3C27) IO:00 Bit: 0->0 DT:999 T:15461(3C65) IO:10 Bit: 4->1 DT:15461 T:17318(43A6) IO:00 Bit: 4->0 DT:1857 T:17377(43E1) IO:C0 Bit: 6->1 DT:17377 Bit: 7->1 DT:17377 T:18478(482E) IO:00 Bit: 6->0 DT:1101 Bit: 7->0 DT:1101 T:29924(74E4) IO:08 Bit: 3->1 DT:20498 T:31424(7AC0) IO:00 Bit: 3->0 DT:1500 T:31485(7AFD) IO:04 Bit: 2->1 DT:20497 T:32983(80D7) IO:00 Bit: 2->0 DT:1498 T:33045(8115) IO:02 Bit: 1->1 DT:20496 T:34545(86F1) IO:00 Bit: 1->0 DT:1500 T:34606(872E) IO:20 Bit: 5->1 DT:20268 T:36334(8DEE) IO:00 Bit: 5->0 DT:1728 T:36395(8E2B) IO:01 Bit: 0->1 DT:20996 T:37396(9214) IO:00 Bit: 0->0 DT:1001 T:37456(9250) IO:10 Bit: 4->1 DT:20138 T:39313(9991) IO:00 Bit: 4->0 DT:1857

You will notice the transition to 0 (going low) that there many values around 1500 which is what you would expect from the RC receiver without my hands on the joysticks…

Again if anyone would like to play with this, here is the code:

[code];==============================================================================
; This is the beginnings of a simple logic analyzer, that over time
; may increase in functionality. The first version will simply
; wait until the user press the button on the BB2 and then will enter an
; assembly level function which will, loop reading the IO state of IO pins 0-7
; repeatily and capture whenever the state changes. It will also captuer the WTIMER
; time when the change happened. It will stay looping until either our WTIMER value
; exceeds some threshold or until we max out our capture size.
;
;==============================================================================

BUFFSIZE con 256

; The buffer will contain up to BUFFSIZE transisition, where each transition
; will be 32 bits
; 8 - IO state of IO pins 0-7
; 24 - WTimer value extended to 24 bits. we will setup wtimer to take the clock
; divided by 8 so I think this will give us about 8 seconds.
CaptureBuffer var long(256) ; we will save away up to 256 transistions.
cItems var word ; number of items returned

;button variables…
buttonA var bit

prevA var bit

;==============================================================================
; Complete initialization
;==============================================================================
sound 9,[50\3800, 50\4200, 40\4100]
input p12
output p14

high p14		; will use the LED to show when we the scan completes.

;==============================================================================
; Main Loop
;==============================================================================

main:
prevA = buttonA

buttonA = in12

if (buttonA = 0) AND (prevA = 1) then
	sound 9,[50\3800]
	gosub DoCapture
	; OK now, lets setup to do a capture.
	; Lets transition to assembly language here.
	gosub DumpCapture
endif

goto main

;==============================================================================
; Main Capture function. - Mostly assembly language…
;
; Will capture state of BAP io pins 0-7 which on H8 is pins P50-P57
;==============================================================================
DoCapture:
low p14 ; Show LED to let know capture is active

TCRW = 0x30 			;clears TCNT and sets the timer to inc clock cycle / 8 
TMRW = 0x80 			;starts the timer counting 
TCNT = 0

; Transition to assembly
xor.w	r0,r0				; zero out value, to set all IO pins to read
mov.b	r0l, @(PDR5-0xd0+0x70)	; update the shadow location
mov.b	r0l, @PCR5				; update the actual direction word which is write only

; Now lets init the WTIMER to divide by 8

; mov.b r0l, @TMRW:8 ; I think it is stopped now.
; mov.w r0, @TCNT:16 ; make sure our counter starts at zero
; mov.b #0x30, r0l
; mov.b r0l, @TCRW:8 ; Clock divided by 8
; bclr.b #7, @TSRW:8 ; make sure the overflow is has cleared.

; Now lets setup everything for our loop
mov.l	#CAPTUREBUFFER, er5			; Setup pointer to our buffer
mov.w	#BUFFSIZE, r4				; Count of items left to get
xor.w	e3, e3					; use e3 for our timer overflow count. R3 to hold value for WTIMER

; Now lets start the ball rolling

; bset.b #7,@TMRW:8 ; start clock

_DCL_MAIN:
mov.b @PDR5:8, r0l ; Capture the current state of the 8 IO pins
mov.w @TCNT, r3 ; get current timer value - make sure consistent.
bld.b #7, @TSRW:8 ; get overflow count
bcc _DCL_NOOVERFLOW:8 ; timer has not overflowed yet
inc.w #1, e3 ; increment overflow count
bclr.b #7, @TSRW:8 ; clear the overflow
mov.w @TCNT, r3 ; get current timer value again to take care of boundry conditions
cmp.w #512, e3 ; hopefully will figure out right comparison here
bge _DCL_TIMEDOUT:8 ; We have gone long enough

_DCL_NOOVERFLOW:
cmp.b r0l, r0h ; see if the same as before.
beq _DCL_MAIN:8 ; yes, so go back to our hard main loop

; We have a new value	
mov.b	r0l, r0h				; update our value we are looking at
xor.b	r0l, r0l				; zero out low value to combine for buffer entry.
mov.l	er3, er2				; move timer value into ER2 to modify into buffer value
shlr.l	er2						; divide by 2 so now in microseconds...
or.w	r0, e2					; and save away the IO Status bits into the high byte of the long value

; now lets save it away
mov.l	er2, @er5				; save away the entry...
add.l	#4,er5					; point to the next entry
dec.w	#1, r4					; decrement our counter
bne		_DCL_MAIN:16				; not full
; fall through if we filled our buffer	

_DCL_TIMEDOUT:
mov.w #BUFFSIZE, r0
sub.w r4, r0 ; calculate how many we did actually store
mov.w r0, @CITEMS:16 ; save it away for basic to use.

; now fall through to return.

high p14
return		

;==============================================================================
; Dump Capture
;
; Later may try more fancy things…
;==============================================================================
; localish variable definitions
i var word
j var byte
ItemTime var long
ItemIOBits var byte

ItemTimePrev	var	long
ItemIOBitsPrev	var	byte
BitTimes		var	long(8)
BitMask			var	byte

DumpCapture:

ItemTimePrev = 0
ItemIOBitsPrev = 0
BitTimes = 0,0,0,0,0,0,0,0

serout s_out, i9600, "Count of items", dec cItems, 13]

; Walk through all of the items we captured
if cItems > 0 then
	for i = 0 to cItems-1
		ItemTime = (CaptureBuffer(i) & 0xffffff)	; 
		ItemIOBits = CaptureBuffer(i).highbyte

		; Start by dumping simply the time mask and the IO bits in hex
		serout s_out, i9600, "T:", dec ItemTime, "(", hex ItemTime, ") IO:", hex2 ItemIOBits\2]
		
		; Then try to tell what bit(s) changed and the delta time for those bits...
		bitMask = 0x01	; low bit
		for j = 0 to 7
			if ((ItemIOBits & BitMask) <> (ItemIOBitsPrev & BitMask)) then
				if (ItemIOBits & BitMask) then
					serout	s_out, i9600, " Bit: ", dec j, "->1 DT:", dec (ItemTime - BitTimes(j))]
				else
					serout	s_out, i9600, " Bit: ", dec j, "->0 DT:", dec (ItemTime - BitTimes(j))]
				endif
				
				BitTimes(j) = ItemTime
			endif
			bitMask = BitMask << 1
		next
		
		serout s_out, i9600, [13]	; output the end of line.
		
		; save away the current valut to compare against for the next item
		ItemTimePrev = ItemTime
		ItemIOBitsPrev = ItemIOBits
		
	next	
else
	serout s_out, i9600, "*** No Items returned ***", 13]
endif
; we are done
return

[/code]

Hi Kurt,

I’m always amazed with the new applications you will come up with. Especially the ASM parts! :wink:

Using the BAP as logic analyzer would be fun!

If I got it right the sample rate will be something like this right?
Sample rate: 15/256 = 0.05859 sec = 17 samples a second.

Xan

I’m also amazed by your programming skills when it comes to ASM and interrupts.

But since you already inspired me to buy a logic analyzer from saleae I’ll probably not test this code :wink: Hopefully this can be used to other stuff too.

Great work!

Thanks guys :smiley:

I actually have always found it reasonably easy to read assembly code, as I have done it on many different processors. I like the H8s which the BAP is based on as it has a nice instruction set and it has 32 bit registers which allow for good math functions…

Yep this was just for fun. The code is not what I would call industrial strength. It is more a proof of concept. As for Sample rate, it is not completely consistent, but it does check many many more times than 17 times per second. If we look at the main loop of the capture function:

[code]
_DCL_MAIN:
mov.b @PDR5:8, r0l ; (*4)Capture the current state of the 8 IO pins
mov.w @TCNT, r3 ; (*6)get current timer value - make sure consistent.
bld.b #7, @TSRW:8 ; (*6)get overflow count
bcc _DCL_NOOVERFLOW:8 ; (*4)timer has not overflowed yet
inc.w #1, e3 ; (#2)increment overflow count
bclr.b #7, @TSRW:8 ; (#8)clear the overflow
mov.w @TCNT, r3 ; (#6)get current timer value again to take care of boundry conditions
cmp.w #512, e3 ; (#4)hopefully will figure out right comparison here
bge _DCL_TIMEDOUT:8 ; (#4)We have gone long enough

_DCL_NOOVERFLOW:
cmp.b r0l, r0h ; (*2)see if the same as before.
beq _DCL_MAIN:8 ; (*4)yes, so go back to our hard main loop

; We have a new value	
mov.b	r0l, r0h				; ($2)update our value we are looking at
xor.b	r0l, r0l				; ($2)zero out low value to combine for buffer entry.
mov.l	er3, er2				; ($2)move timer value into ER2 to modify into buffer value
shlr.l	er2						; ($2)divide by 2 so now in microseconds...
or.w	r0, e2					; ($2)and save away the IO Status bits into the high byte of the long value

; now lets save it away
mov.l	er2, @er5				; ($8)save away the entry...
add.l	#4,er5					; ($6)point to the next entry
dec.w	#1, r4					; ($2)decrement our counter
bne		_DCL_MAIN:8				; ($4)not full
; fall through if we filled our buffer		

_[/code]
In the above code if we do not get a timer overflow and none of the IO values change (This is the code with an *n numbers in the comments), then I believe that it is checking every 26 clock cycles or 26/16000000 or 0.000001625 seconds.

Now in the cycles where the WTIMER overflows, which happen every: (8*65536) clocks or 0.032768 seconds, then we would add the # instructions to the loop: which is 24 clock cycles.

When we find a change this would add the $ instructions to this loop for an additional: 30 clock cycles.

So worst case is probably every 80 clocks.

The current version times out after 512 timerW overflows, which is probably to fast. I did this as to save each sample in 32 bits, with the upper 8 bits being the IO state. I also wanted the us resolution for the timings…, but the current code will time out in: 0.032768 * 512 = 16.777216 seconds. This could be expanded several different ways, including converting the timer save to 32 bits, or could divide down the value more and save for example in .5us increments…

I don’t know if any of this makes any sense.

Kurt

Looks like you’re having fun! That’s what counts. I’m lazy, I’ll stick to my 'scope and logic analyzer (Intronix LA1034).

Alan KM6VV

Actually me too, I will mostly use my Logic Analyzer, :smiley:
But I thought it would be fun to see if I could work…

Kurt

Looking for another interesting project?

Parallax has an line image sensor that I’m going to interface:

TSL1401 Linescan Imaging Sensor Daughterboard

parallax.com/Store/Sensors/ColorLight/tabid/175/CategoryID/50/List/0/Level/a/ProductID/566/Default.aspx?SortField=ProductName%2cProductName

Looks like a job for a logic analyzer!

Alan KM6VV