timing pulse edge with CTRA register (is it possible?)
I based all my code on the main clock (cnt) but realized that what I really need is a time counter from a pulse edge.
I would like to code a function that provides me with the time (in clock counts) starting from the time a trigger last went on.
In other words, each time the signal goes on - the counter gets reset to zero. Calling the function returns the clock counts that have gone by since the trigger last went on.
Can this be done using one of the CTRA registers? I did read AN001 (about Propeller Counters) but somehow it is not clicking. More examples would be helpful.
For me the speed and accuracy is very important here, because I use this number as a base for all further time sensitive calculations.
Thanks in advance for your suggestions on how to best do this.
I would like to code a function that provides me with the time (in clock counts) starting from the time a trigger last went on.
In other words, each time the signal goes on - the counter gets reset to zero. Calling the function returns the clock counts that have gone by since the trigger last went on.
Can this be done using one of the CTRA registers? I did read AN001 (about Propeller Counters) but somehow it is not clicking. More examples would be helpful.
For me the speed and accuracy is very important here, because I use this number as a base for all further time sensitive calculations.
Thanks in advance for your suggestions on how to best do this.

Comments
There is no HW Clear, but there is a GATED Clock mode, that Adds FRQx (Usually =1) to PHSx on every CLK, while the Gate is HI.
( POS mode mentioned in #2)
You can then sense the GATE going low, [Count now Freezes] Read and Clear PHSx in SW, then the HW waits until GATE next goes high, when it resumes counting from Zero.
This does need a (small) minimum LOW time.
Usually you need two WAIT phases, one for =\_ when you capture/clear inSW, and another for _/= which is used to ready the WAIT =\_
If you can spare another pin, I think you can use 'POS detector with feedback' mode, and now wait for TWO pins, A and B(A@T-1), and those two bits will will be 01 for one single sample time, on a falling edge - so now a single wait can be used.
This is my code (the signal I am monitoring is a video field signal)
'Set up the counters ctra := %11111 << 26 + video_field_trigger_pin 'counter A in POSdetect mode ctrb := %11111 << 26 + laser_trigger_pin 'counter B in POSdetect mode frqa := frqb := 1 'increment 1 per pulse phsa := phsb := 0 'reset count registers repeat ' sets wait for pin to go low=0, or high=1 ' ┌──────────────────────────── ' │ ' │ sets pin number, pin P6 equals six zeros ' │┌────┐ ' ││ │ waitpeq(%1000000, |< video_field_trigger_pin, 0) A_total := phsa 'save highest count phsa := 0 'reset count registers waitcnt(2_000_000 + cnt)or
More 'accurate' at what ? - waitcnt has the best (lowest) granularity, but it is not checking a pin.
The WAIT family of opcodes work to 1 CLK cycle, so they have (much) less granularity than any read-and-Check polling.
CON video_field_trigger_pin = 1 ' for example, I/O pin 1 VAR long countVar pub initialize countVar := 0 cognew(@cogIt,@countVar) pub returnCount return countVar DAT org 0 cogIt mov ctra,mask1 mov frqa,#1 myloop waitpeq bit1,bit1 ' wait for video_field_trigger_pin to be a one mov phsa,#0 ' clear the counter waitpne bit1,bit1 ' wait for video_field_trigger_pin to be a zero mov temp,phsa ' get count value wrlong temp,par ' copy count to location passed in PAR jmp #myLoop mask1 long %11111 << 26 + video_field_trigger_pin bit1 long |< video_field_trigger_pin temp res 1org 0 cogIt waitpeq bit1,bit1 ' wait for video_field_trigger_pin to be a one mov baseTime,cnt ' initialize the counter waitpne bit1,bit1 ' wait for video_field_trigger_pin to be a zero mov tempTime,cnt ' end of the interval sub tempTime,baseTime wrlong tempTime,par ' copy interval to location passed in PAR jmp #cogIt bit1 long |< video_field_trigger_pin baseTime res 1 tempTime res 1Here there's no error since the first WAITPNE/MOV time cancels the second WAITPEQ/MOV and the WRLONG isn't involved in the timing.It is kind of scary to think that reading the assembly language code snippet is starting to look like something that can be read (by me that is...) and somewhat understood. I tried to get more familiar with the assembly language, but did not find easy example codes and how-to's, so I gave up on that idea some time ago.
I was trying to stay away of having to start a new cog just for that task, that is why I really liked the register counter idea,
Sofar I wrote all the code using timestamps and cnt as reference, but the code was getting messy and hard to trouble shoot. And I was getting tired of following this kind of workaround to avoid the logic pitfall when the counter goes from 2,147,483,647 to -2,147,483,648
... waitpeq(000, |< video_field_trigger_pin, 0) 'wait for Pin to go LOW -- going low has a cleaner edge video_start_time_local := cnt if video_start_time_local > last_start_time_local elapsed_time := video_start_time_local - last_start_time_local ' normal situation else elapsed_time := (POSX-last_start_time_local)+(POSX+video_start_time_local) video_start_time_master := video_start_time_local video_start_time_master_last := last_start_time_local ...I love the idea of having these two counters available (it would be nice if there would be a third one ... but two will have to do.)
I just realized that my requirements on the resolution or "granularity" as jmg so nicely states is 824 counts, which is 1/3 of an HD frame line (80_000_000 / 29.97 / 1080 / 3). So technically speaking if checking on the count or doing something with it, should stay under 824 count. So 746 ticks is OK.
PS. It is not only the actual interval time that is of interest to me. I am actually much more interested in the timing of three different occurring events that need to happen within two time-windows; and during that phase of one frame. I need to know the tick count from the edge to these different events.
time_stamp_A := cnt time_stamp_B := 0 ... 'check during time window 0 to 10_000 for event B repeat while get_passed_time(time_stamp_A) < 10_000 and time_stamp_B == 0 if ina[monitor_event_pin_B] > 0 time_stamp_B := cnt ... PRI get_passed_time(time_stamp) | now now := cnt if now > time_stamp return (now - time_stamp) ' normal situation else return ((POSX - time_stamp) + (now - NEGX))and now learned that I can also code it like this. It uses less code, but takes longer to process: which makes my time_stamp_B less accurate.
phsa := 0 time_stamp_B := 0 ... 'check during time window 0 to 10_000 for event B repeat while phsa < 10_000 and time_stamp_B == 0 if ina[monitor_event_pin_B] > 0 time_stamp_B := cnt ...but is there a better why to do this sort of thing using assembly language to make it faster? Not sure.
[SIZE=1][FONT=courier new]PUB snippet [/FONT][/SIZE][SIZE=1][FONT=courier new] timeStamp_B := 0 [/FONT][/SIZE][SIZE=1][FONT=courier new] ctra := %00100 << 26 + flagPin ' nco mode, output to flagPin phsa := 10000 frqa := -1 ' flagPin will go 0->1 at end of interval waitpne(0, (|< flagPin) | (|< monitor_event_pin_B), 0) ' high on either pin exits timeStamp_B := phsa ' quick capture phsa (or cnt if you want) if timeStamp_B > 0 ' positive value is within interval. timeStamp_B := 10000 - timeStamp_B [/FONT][/SIZE]The latency is deterministic between exiting the wait and capturing the value of phsa, so it could be adjusted for better absolute accuracy. How accurate are you expecting?Very very nice!!! This looks like a much better approach. I did not know you could to that. I think this is it
Do I need to set the flagPin as input and output?
The only disadvantage of using this code is that one has to be carfull when to "set" the flagPin
Tracy Allen, somehow this code is not working for me. My flagPin is always high. I even tried to set it low, but even that is not working. How would I set the FlagPin to go high only after say 3_380_000 counts?
This is what I have
[FONT=courier new] [SIZE=1] phsb:=0 phsb:=0 Pin 6 (no signal) v v ───────────────────────────────┐┌───────────────┐┌───────────────┐┌─── ││ Signal ││ ││ └┘ └┘ └┘ [/SIZE][/FONT][SIZE=1] [FONT=courier new][SIZE=1] ctrb := 111 << 26 ' count everything mode (set CTRB CTRMODE ' and << 26 is to set all other bits to z[/SIZE]ero) frqb := 1 ' +1 per tick phsb := 0 ' reset count register '-------- ctra := 100 << 26 + video_flagPin ' nco mode (numerically controlled oscillator) frqa := -1 ' -1 per tick, flagPin will go 0->1 at end of interval phsa := 10_400_000 ' set [SIZE=1]interval[/SIZE][/FONT][/SIZE] [SIZE=1][FONT=courier new] 'this is what I am suing in the loop to ho[SIZE=1]ld '[SIZE=1]i[SIZE=1]f[/SIZE] the [SIZE=1]hold is [SIZE=1]too long I wha[SIZE=1]nt the hold to[SIZE=1] break 'for that I would like to [SIZE=1]be using[/SIZE] counter [SIZE=1]A[/SIZE][/SIZE][/SIZE][/SIZE][/SIZE][/SIZE][/SIZE] waitpne(000000, 000000, 0) ' hold if flagPin 7 is low and 6 is HIGH[/FONT][/SIZE]I think I just found a solution. This is working -- but the counter is only counting every other count (very strange)
now I am witnessing a strange counting behavior. I am using counter B as a "logical" counter (one count per clock tick); but for some reason it is only counting 1/2 of the counts it should get.
Is this normal for counter B?
Thank you Tracy! YES - I did find something in the code that was pulling up that flagPin. I fixed it, and now your code is working! Very nice. However, I am only getting 1 count for 2 cycles. This is very strange. Is there something that could have changed my clock in that cog?
frqa := 300 '???
phsa := 0
I dumped the frqa := 300 and phsa := 0 approach all together. It worked to kill the loop, but is less elegant then your code.
This is all the code in that cog total (171 bytes.) I reserved 200 for it (50 lonsg)
[SIZE=1][FONT=courier new]PUB cog_video | VS_cnt_local, last_VS_cnt_local, video_VS_cnt_local, video_timeStamp_B_local, elapsed_time_local, video_flagPin set_pin_to_input(video_field_trigger_pin) video_flagPin := 7 ' P7 set_pin_to_output(video_flagPin) cog_video_COGID := COGID ctra := %00100 << 26 + video_flagPin ' nco mode (numerically controlled oscillator) frqa := -1 phsa := 4_000_000 ' set in[SIZE=1]t[/SIZE]erval ctrb := %11111 << 26 ' count everything mode frqb := 1 ' +1 per tick phsb := 0 ' reset count register repeat if video_timeStamp_B_local < 4_000_000 if VS_cnt_local > last_VS_cnt_local elapsed_time_local := VS_cnt_local - last_VS_cnt_local ' normal situation else elapsed_time_local := (POSX-last_VS_cnt_local)+(POSX+VS_cnt_local) else elapsed_time_local := 0 ' write to global vars elapsed_time := elapsed_time_local video_timeStamp_B := video_timeStamp_B_local turn_Off(7) waitcnt(100_000+cnt) '[SIZE=1] make [SIZE=1]sure P7 it is back HIGH[/SIZE][/SIZE] '' ┌──────────────────────────────────────────────┐ '' │ vertical sync is on P6 (6) %01000000 │ '' │ reserved flag pin is on P7 (7) %10000000 │ '' └──────────────────────────────────────────────┘ last_VS_cnt_local := VS_cnt_local waitpne(%01000000, %11000000, 0) ' hold if 7 is LOW and 6 is HIGH video_timeStamp_B_local := phsb ' save highest count on B counter[/FONT][/SIZE] [SIZE=1][FONT=courier new] phsb := 0 ' reset B counter [/FONT][/SIZE][SIZE=1][FONT=courier new] VS_cnt_local := cnt[/FONT][/SIZE][SIZE=1][FONT=courier new] phsa := 4_000_000 waitcnt(1_000+cnt) ' to prevent bounce if video_timeStamp_B_local < 3_400_000 VS_cnt := VS_cnt_local else VS_cnt := 0 [/FONT][/SIZE]The phase duration for PAL video is 3_200_000 and I am getting ony 1_599_952 for elapsed_time; only half the expected value. So is video_timeStamp_B: 1_599_382
I could just multiply by 2 -- but something is off if it is suppose to count every count ?!?
CON _clkmode = XTAL1|PLL16X _xinfreq = 5_000_000 CON video_flagPin = 7 OBJ serial: "FullDuplexSerial" VAR long elapsed_time long video_timeStamp_B long VS_cnt long stack[32] PUB null serial.start(31, 30, %0000, 115200) cognew(@pulse, 0) cognew(cog_video, @stack{0}) waitcnt(clkfreq*3 + cnt) repeat serial.dec(elapsed_time) serial.tx(13) waitcnt(clkfreq/2 + cnt) PUB cog_video | VS_cnt_local, last_VS_cnt_local, video_VS_cnt_local, video_timeStamp_B_local, elapsed_time_local dira[video_flagPin]~~ ctra := %00100 << 26 + video_flagPin ' nco mode (numerically controlled oscillator) frqa := -1 phsa := 4_000_000 ' set interval ctrb := %11111 << 26 ' count everything mode frqb := 1 ' +1 per tick phsb := 0 ' reset count register repeat if video_timeStamp_B_local < 4_000_000 if VS_cnt_local > last_VS_cnt_local elapsed_time_local := VS_cnt_local - last_VS_cnt_local ' normal situation else elapsed_time_local := (POSX-last_VS_cnt_local)+(POSX+VS_cnt_local) else elapsed_time_local := 0 ' write to global vars elapsed_time := elapsed_time_local video_timeStamp_B := video_timeStamp_B_local ' turn_Off(7) waitcnt(100_000+cnt) ' make sure P7 it is back HIGH '' ┌──────────────────────────────────────────────┐ '' │ vertical sync is on P6 (6) %01000000 │ '' │ reserved flag pin is on P7 (7) %10000000 │ '' └──────────────────────────────────────────────┘ last_VS_cnt_local := VS_cnt_local waitpne(%01000000, %11000000, 0) ' hold if 7 is LOW and 6 is HIGH video_timeStamp_B_local := phsb ' save highest count on B counter phsb := 0 ' reset B counter VS_cnt_local := cnt phsa := 4_000_000 waitcnt(1_000+cnt) ' to prevent bounce if video_timeStamp_B_local < 3_400_000 VS_cnt := VS_cnt_local else VS_cnt := 0 DAT org 0 pulse mov dira, mask mov cnt, cnt add cnt, #9 loop waitcnt cnt, delay andn outa, mask nop or outa, mask '{timeout} waitpeq $, #0 jmp #loop delay long 3_200_000 mask long |< 6 fit DATMy sync pulse is what I expect it to be ... yes. And the timeout behavior (flagPin) is also working well.
Are you getting a value of 3M2 for the elapsed_time?
I am trying to find an explanation for the counters behavior - but some how can't. What is strange is that somehow counter A & B are only counting one clock cycle for every 2 clock cycles. This is not what I intend it to do. It is like if this one cog is running at 40MHz for counter A & B but 80MHz for the cnt clock. Very strange.
If I use this code in place of the above posted code it gives me the correct clock count for the two counters. But this is not the way I expected spin to work. This would be a "quick fix" but with no logical explanation of why the counters are not counting properly.
[SIZE=1][FONT=courier new] frqa := [COLOR=#ff0000][SIZE=2][B]-2[/B][/SIZE][/COLOR] [COLOR=#ff0000]'was -1[/COLOR] ' further down in the code ... frqb := [/FONT][/SIZE][SIZE=1][FONT=courier new][COLOR=#ff0000][SIZE=2][B]2[/B][/SIZE][/COLOR][/FONT][/SIZE][SIZE=1][FONT=courier new] [COLOR=#ff0000] 'was 1[/COLOR][/FONT][/SIZE] [SIZE=1][FONT=courier new] ' further down in the code ... [/FONT][/SIZE][SIZE=1][FONT=courier new] elapsed_time := elapsed_time_local * [/FONT][/SIZE][SIZE=1][FONT=courier new][COLOR=#ff0000][SIZE=2][B]2[/B][/SIZE][/COLOR][/FONT][/SIZE] [COLOR=#ff0000] [SIZE=1][FONT=courier new]'was [SIZE=1]not [SIZE=1]multiplied[/SIZE] x2[/SIZE][/FONT][/SIZE][/COLOR]So with this quick fix in place I am getting the expected 3M2 counts per period (phase), but without doubling it I am only getting half the counts. Something is not right.
Would this kind of behavior point to a memory collision somewhere?
PAL is 25 frames per second ... so 25Hz is right ... or 20uS per frame or 3_200_000 counts per frame.
Try this :http://en.wikipedia.org/wiki/PAL
The vertical timings are:
Vertical frequency 50 Hz
Frame rate in the PALs I know, are all 50Hz, however they do interlace, which may be where your 25Hz figure has come from.
PAL frame rate is 25 fps, NTSC is 29.97!
Frame rate is not the same as AC rate (Hz)
My VS (vertical sync) pulse is one pulse per frame (NOT to be confused by fields!!!)
kuroneko, yes. I am sorry ... I meant mS and not uS ... and not 20 but 40 per frame. So it is 40mS per frame and 20mS per field, and there are 2 files in one video frame. So one PAL frame has 3m2 cycles
And the vertical sync that the code is monitoring is a short pulse that goes low, one time per frame.
[FONT=courier new][SIZE=1] [SIZE=1]setting[/SIZE] [/SIZE][/FONT][FONT=courier new] [SIZE=1] phsa:=[/SIZE][/FONT][SIZE=1][FONT=courier new]4_000_000[/FONT][/SIZE][FONT=courier new][SIZE=1] phsa:=[/SIZE][/FONT][SIZE=1][FONT=courier new]4_000_000[/FONT][/SIZE] [FONT=courier new] [SIZE=1] phsb:=0 phsb:=0 [/SIZE][/FONT][FONT=courier new][SIZE=1] Pin 6 (no signal) v v ───────────────────────────────┐┌───────────────┐┌───────────────┐┌─── ││ Signal ││ ││ └┘ └┘ └┘ [/SIZE][/FONT]But back to my spin misbehavior question in post 20. Somehow my register CTRA and CTRB are not counting one tick per cycle, but only every other tick per cycle. It is as if they would be taking turns in the counting department. And so I am ending up with only half the counts that would be expected for the duration of one frame. I expect 3M2 but get only 1M6 per frame. Is this normal behavoir, or is this some hardware bug? I thought that the two counters would count every clock count.
This is how I set up the two counters:
I plan on using these counters in other parts of my code, and would like to know how they work -- or am I witnessing some memory collision that is messing up the counting? It just is all very strange. Thanks for the help!
I don't think the Prop is lying to you. Do you have a 'scope or logic analyzer where you can visualize the signal?
Get another instrument, and measure that vertical sync rate. Many multimeters have frequency counters.