error with 50 Hz as a clock time base

Following through with my enquiry of a few days ago about using RTC's I thought it would be interesting to try and use 50Hz as a time source. As a test I have tried to achieve getting 30 second pulses to drive a vintage slave clock and now also a also a mechanical counter to confirm that the errors I am finding are originating from the circuit or program not the clock mechanism.

The circuit (illustrated below) is driven from an 8vac bell transformer with a very ordinary bridge rectifier, capacitor and regulator to get a 5vdc supply for the picaxe and a further adjustable regulator to get 1.4volts to drive the clock. (My counter is driven from an external power supply through an opto-coupler not included in the diagram). Half of an LM358 opamp is used to convert the incoming sine to a square wave form, this going to C.2 on an 08M2 to do some counting and supply a pulse output on C.4. The square wave is inverted from the sine wave and hence I am using the negative side of the square to equal the positive side of the sine (though I have tried un-inverted also just out of curiosity). I have included caps to eliminate any transients and looking closely with a scope, the wave forms and supplies are very clean and stable. The interrupt included in the code is to be able to fast forward the clock but this should not affect the present function.

The program originally counted 1500 inputs (30 seconds x 50Hz) and sent an output for the last two cycles - 40 milliseconds which perfectly suits the clock mechanism. The code in this form however consistently runs too slowly and an inconsistency of seconds is visible after only a few minutes using computer time as a reliable comparison.

Calculating the delay and resetting the values to count 1492 cycles for each 30 seconds results in an error in accuracy of a couple of seconds after seven hours (this being a consistent increase over that time so not likely to be the result of an occasional glitch) which error is anyway within what may occur due to mains frequency variations which I am aware of.
Code:
;read 50hz mains to get pulse out each 30 secs using 08M2

init:
setfreq M8
let W0 = 0
 
goto main:


main:
setint %00001000,%00001000     ; activate interrupt
if pinC.2 = 1 then goto main: 
if pinC.2 = 0 then do
endif
if pinC.2 = 0 then pauseus 10
endif
if pinC.2 = 1 then goto pulse:
if pinC.2 = 0 then loop   
endif

pulse:
inc W0
let W0 = W0 max 1492

if W0 = 1490 then high C.4
endif
if W0 = 1492 then low C.4
endif
if W0 = 1492 then let W0 = 0
endif
goto main

; Interrupt service routine
interrupt:   ;interrupt: loop here until the interrupt cleared
if pinC.3 = 1 then
high C.4 ; switch output 4 on
pause 80
low C.4
pause 500
endif

setint %00001000,%00001000 ; re-activate interrupt
return ; return from sub
Confirming that the error is as shown by the validity of the alternative value of 1492, it is apparent that somewhere the program is losing 266us per second. I had expected that the way I had written the code and the speed of the processor would easily accommodate the time needed to achieve my intention within the much larger 20000us period of each cycle but I am very obviously wrong.
In the longer term, the inaccuracy of this is anyway going to accrue and I feel sure that this should work using the full 1500 cycles as the necessary measure.
Can you offer some pointers please as to what terrible error of judgement I have made...
Many thanks...
 

Attachments

rq3

Senior Member
Following through with my enquiry of a few days ago about using RTC's I thought it would be interesting to try and use 50Hz as a time source. As a test I have tried to achieve getting 30 second pulses to drive a vintage slave clock and now also a also a mechanical counter to confirm that the errors I am finding are originating from the circuit or program not the clock mechanism.

The circuit (illustrated below) is driven from an 8vac bell transformer with a very ordinary bridge rectifier, capacitor and regulator to get a 5vdc supply for the picaxe and a further adjustable regulator to get 1.4volts to drive the clock. (My counter is driven from an external power supply through an opto-coupler not included in the diagram). Half of an LM358 opamp is used to convert the incoming sine to a square wave form, this going to C.2 on an 08M2 to do some counting and supply a pulse output on C.4. The square wave is inverted from the sine wave and hence I am using the negative side of the square to equal the positive side of the sine (though I have tried un-inverted also just out of curiosity). I have included caps to eliminate any transients and looking closely with a scope, the wave forms and supplies are very clean and stable. The interrupt included in the code is to be able to fast forward the clock but this should not affect the present function.

The program originally counted 1500 inputs (30 seconds x 50Hz) and sent an output for the last two cycles - 40 milliseconds which perfectly suits the clock mechanism. The code in this form however consistently runs too slowly and an inconsistency of seconds is visible after only a few minutes using computer time as a reliable comparison.

Calculating the delay and resetting the values to count 1492 cycles for each 30 seconds results in an error in accuracy of a couple of seconds after seven hours (this being a consistent increase over that time so not likely to be the result of an occasional glitch) which error is anyway within what may occur due to mains frequency variations which I am aware of.
Code:
;read 50hz mains to get pulse out each 30 secs using 08M2

init:
setfreq M8
let W0 = 0

goto main:


main:
setint %00001000,%00001000     ; activate interrupt
if pinC.2 = 1 then goto main:
if pinC.2 = 0 then do
endif
if pinC.2 = 0 then pauseus 10
endif
if pinC.2 = 1 then goto pulse:
if pinC.2 = 0 then loop 
endif

pulse:
inc W0
let W0 = W0 max 1492

if W0 = 1490 then high C.4
endif
if W0 = 1492 then low C.4
endif
if W0 = 1492 then let W0 = 0
endif
goto main

; Interrupt service routine
interrupt:   ;interrupt: loop here until the interrupt cleared
if pinC.3 = 1 then
high C.4 ; switch output 4 on
pause 80
low C.4
pause 500
endif

setint %00001000,%00001000 ; re-activate interrupt
return ; return from sub
Confirming that the error is as shown by the validity of the alternative value of 1492, it is apparent that somewhere the program is losing 266us per second. I had expected that the way I had written the code and the speed of the processor would easily accommodate the time needed to achieve my intention within the much larger 20000us period of each cycle but I am very obviously wrong.
In the longer term, the inaccuracy of this is anyway going to accrue and I feel sure that this should work using the full 1500 cycles as the necessary measure.
Can you offer some pointers please as to what terrible error of judgement I have made...
Many thanks...
My first thought is that the poor op-amp is being driven into wicked saturation with an 8vac input while it is running on a 5vdc supply. It's a very old device, and may not have the latch-up protection that newer op-amps have. I think what you really want is a comparator with reasonable input protection.

I know that the basic idea will work. I built a stepper motor driven clock for a science museum in Brazil about forty years ago which got it's timing from the mains in a similar fashion. The last I heard it was still keeping good time.
 
Last edited:
Thanks for that rq.
The output on the scope is as perfect as I would ever expect from any opamp and the error I think too consistent to be caused by that. I cannot see it likely that the op amp would only occasionally - but regularly! - send an erroneous output.
Having looked at it very closely for a couple of days, the problem seems to be much closer to the 08m2 - or my code...
 

Buzby

Senior Member
Try this ..

Code:
;read 50hz mains to get pulse out each 30 secs using 08M2

' C.2 is 50Hz input

symbol Count50 = b0
symbol Count30 = b1
symbol Flag30 = bit16

' Code starts here
setfreq M8
let W0 = 0
setint %00000100,%00000100     ; Interrupt when C.2 is high 
 
do
    if flag30 = 1 then
        high C.4    ' Energise coil
        pause 2000
        low C.4    ' De-energise coil
        flag30 = 0
    endif
loop

; Interrupt service routine
interrupt:  
do : loop until pinC.2 = 0 ' Wait for interrupt pin to go low
    
inc Count50 ' Count 50 interrupts
if Count50 = 50 then
    Count50 = 0    ' Reset interrupt counter
    inc Count30 ' Count 30 seconds
    if Count30 = 30 then
        Count30 = 0 ' Reset seconds counter    
        flag30 = 1 ' Set flag every 30 seconds
    end if
end if
setint %00000100,%00000100 ; re-activate interrupt
return
Also, check your interrupt mask settings are correct.
I think they are for C.3, not C.2.

Cheers,

Buzby
 

AllyCat

Senior Member
Hi,
The code in this form however consistently runs too slowly and an inconsistency of seconds is visible after only a few minutes using computer time as a reliable comparison.
The mains frequency is not guaranteed to have an "instantaneous" frequency of 50 Hz, only that it has an accurate average frequency over a "long" time. I don't know if there's a formal specification, but the average might be over a period of hours, or even a day, or more. Probably the best (or only) time to check would be in the middle of the night !

Buzby looks correct about the interrupt mask, but IMHO the design (hardware and software) is totally OTT. No need for an Op-Amp or interrupts - just an R-C Low-pass filter and/or a few PAUSES. ;)

You need a software and hardware design that is totally resistant to spikes and glitches, and then divide the input cycles by exactly 1500. No "fiddle factors", if it doesn't keep "perfect" time over a period of a few days, then look for a bug in your software or hardware design. But don't overlook that "time signals" via DAB radio or TV can now have delays of several seconds (almost 10 seconds in the case of HDTV ! ).

Cheers, Alan.
 

papaof2

Senior Member
Re: time signals

When WWV (US time station) had 300 baud dialup ports, they could compute the phone line delay and send the signal "early" enough that you saw it at the correct time. I used it for setting the clocks on the multiple Unix machines I was responsible for back then.

Not all "ancient" technology has been improved on ;-)
 

tmfkam

Senior Member
The frequency on the grid does drift, but in reasonably equal amounts of too fast, too slow and just right. Over a period of seven hours it should be very close to "just right".

Premelec pointed you to my Grid Frequency Meter design. Depending on something or other (never found what!) it was fussy about where it sat on the TV cabinet. It seemed to pick up random noise which would give short bursts of either very high or very low readings. I always assumed this was due to harmonics on the incoming supply but never found a definitive reason. I tried to filter this electrically and in code which improved it no end, but in certain positions on the cabinet it would still sometimes jump for a fraction of a second.

I've recently rewritten the code for a "base" PIC device (16F1825) where I have been able to average 20 (I think) readings for both frequency and Voltage. That has eliminated the jumping, using the same PCB to test it. There is a new PCB but the electronics are nearly identical to the original.

Perhaps some harmonics are causing your problems?

I originally started the PicAxe GFM as a clock time base, with mains frequency as the reference but failed to get anywhere close to the accuracy of a DS1307.
 
Thank you all for the further responses.
My curiosity is primarily to understand why the expected code is not working as it should and as of now, I am still far from sure but I'm working on it...

@Buzby The interrupt procedure in my code is just to allow fast forwarding the clock to a desired setting, as being a pawl and ratchet mechanism it is impossible to manually rotate the hands as with most clocks. Hence this part of the code should not play any part in the counting

@AllyCat As mentioned in my original post, the interrupt is only there for reasons explained above and the of the circuit components, the only excess to me is the parts necessary to supply the clock with a voltage that ensures function without excessive mechanical noise.
I agree totally that the need is to get the required functionality using 1500 cycles as the measure.

Just for your collective interest:

The Electricity Safety, Quality and Continuity Regulations (ESQCR) 2002 (Amended 2006)1 The ESQCR’s are decreed under the Electricity Act (section 29).
The ESQCR specifies that
1.) The declared frequency for the system is 50 hertz.
2.)A variation not exceeding 1 percent above or below the declared frequency is permitted.

There seems to be no determination of over what period of time the frequency is corrected but for many decades, numerous clocks driven by synchronous motors have been operating with a reliance on the accuracy of the mains and in this case that's quite accurate enough. For more critical purposes we can use RTCs.

It is elsewhere stated that the usual operational limits are 49.8 -50.2 Hz

This is a site devoted to watching the grid in real time: gridwatch
 
Last edited:

Buzby

Senior Member
@Buzby The interrupt procedure in my code is just to allow fast forwarding the clock to a desired setting, as being a pawl and ratchet mechanism it is impossible to manually rotate the hands as with most clocks. Hence this part of the code should not play any part in the counting
Sorry, I thought the interrupt was meant to count the mains cycles. No problem, we can change the code to this ...

Code:
;read 50hz mains to get pulse out each 30 secs using 08M2

' C.2 is 50Hz interrupt input
' C.3 is push button

symbol Count50 = b0
symbol Count30 = b1
symbol Flag30  = bit16
symbol PBcopy  = bit17
symbol PBmem   = bit18
symbol PB1shot = bit19


' Code starts here
setfreq M8
let W0 = 0
setint %00000100,%00000100     ; Interrupt when C.2 is high

do
   ' Make 1-shot from pushbuttom
    PBcopy = pinC.3
    PB1shot = PBcopy ANDNOT PBmem
    PBmem = PBcopy

   '  Drive coil if 30s counted or button pressed
    if Flag30 = 1 or PB1shot = 1 then
        high C.4    ' Energise coil
        pause 80
        low C.4    ' De-energise coil
        Flag30 = 0
    endif
loop

; Interrupt service routine
interrupt:
do : loop until pinC.2 = 0 ' Wait for interrupt pin to go low

inc Count50 ' Count 50 interrupts
if Count50 >= 50 then
    Count50 = 0    ' Reset interrupt counter
    inc Count30 ' Count 30 seconds
    if Count30 >= 30 then
        Count30 = 0 ' Reset seconds counter
        Flag30 = 1 ' Set flag every 30 seconds
    end if
end if
setint %00000100,%00000100 ; re-activate interrupt
return
Now the 50Hz interrupt still does the timing, and the button triggers a pulse when pressed.
 
Last edited:

AllyCat

Senior Member
Code:
if pinC.2 = 1 then goto main: 
if pinC.2 = 0 then do
endif
That does appear to be a (potentially) "dangerous" structure. Firstly, it is nearly always better to code an "alternative" situation using an ELSE, or a direct branch, (e.g. .... THEN GOTO main : GOTO <somewhere else>), to ensure that only one path is executed on each pass. Secondly, splitting a DO .... LOOP across two different conditional expressions seems very "strange".

Since you require/know that a "1" should occur for about 10 ms and then a "0" for for a similar time, I would be inclined to "hard code" those requirements into the code. For example ensure that a significant number of "1"s is followed by a similar number of "0"s (perhaps separated by Pauses), or even implement a (software emulation of a) Phase-Locked-Loop. Personally, I would retain the Sine Wave as an input (i.e. a resistive voltage divider rather than a comparator) and maybe read it with the ADC. Then it would be possible to reconstruct/verify the sine-wave shape and perhaps resolve the phase to (much) better than a single cycle.

The gridwatch website is interesting; there do appear to be periods of around 6 hours where the "error" is continuously of the same sign, and the "daily" averages have errors up to approximately 0.02 Hz. I calculate that that accumulates to around half a minute, or 1500 cycles ! The Long-Term error should be (virtually) zero, but it's a moot point whether a "Power Cut" may occur before the (say) 10 year life of a RTC battery.

Cheers, Alan.
 

tmfkam

Senior Member
Watching the frequency of the Grid is fascinating. Which is why I made my GFMeter.

I have two 1950s Valve (Tube) clock radios which have AC motors for the clock mechanisms. These keep reasonable time but not close to a DS3231. Possibly better than a DS1307 though.
 

Buzby

Senior Member
(a) if pinC.2 = 1 then goto main:
(b) if pinC.2 = 0 then do
(c) end if

That does appear to be a (potentially) "dangerous" structure.
Yes, this an actual, not potential, error.

Imagine a situation where C.2 is low. Line (a) does not perform the goto.
If C.2 now changes to high, in the time between (a) and (b), then line (b) does not execute.

The same effect is possible throughout the original code, everywhere that an 'if pinC.2' is used.

Much better to read the pin once into a 'copy bit', e.g

C2copy = pinC.2
if C2copy = 1 then ....
if C2copy = 0 then ....

Now, even if the actual pin changes state between instructions, the code will run as expected. ( For an example, see in the code I wrote to handle the pushbutton. )

It's difficult to analyse exactly what the effect of this error is, but it could be the source of the OP's problem.

Cheers,

Buzby
 

AllyCat

Senior Member
Hi,
inc W0
let W0 = W0 max 1492
.....
if W0 = 1492 then let W0 = 0
endif
Another example of "dangerous" programming. The max 1492 has obviously been introduced to "patch" a problem, rather than fix it. The danger is that it will be "forgotten" and cause the program to totally fail at a later date (when the count has been fixed to be the correct 1499 or 1500). That line should have been omitted and the equivalent "temporary patch" could have been if W0 => 1492... (note the ">") .

The GRIDWATCH website is interesting and does give some "worthwhile" data, but is also an example of "Don't believe all you see on the Internet". At the foot of the "Last Year (Day Averages)" graph is the statement "minimum: 49.695 HZ maximum: 50.329 HZ average: 50.003 HZ". I had to repeat my calculations several times, but an "average" of 50.003 is +60 parts per million which amounts to 31.5 minutes per year (not seconds). And obviously the "minimum" and "maximum" values are NOT "day" values (which would represent almost 10 minutes per day), but presumably 10-minute averages (as in the earlier graphs).

It looks as if the website (hardware) is basically measuring the number of mains frequency "zero crossings" in each 10 minute period. The "resolution" is thus 1 in 50 * 60 * 10 = 1 in 30,000 or 33 parts per million. Which gives us a clue to that 50.003 "error"; it is possible that the reference oscillator is 60 ppm in error, but much more likely that the 10 minute counting interval has an "edge error" of the count by 2. Obviously we'd need to see the program code to see what's gone wrong. But a simple PICaxe should be able to do much better with the correct program and some form of frequency reference such as a RTC. :)

Finally, to expand on my comment about a (software) PLL. PICaxe Basic has the CALIBFREQ command which permits its master oscillator to be trimmed up or down in (approximately) 0.1% steps (up to around +/-3%). If, each 20ms, the software were to check the polarity of the mains (i.e. whether a zero-crossing had just occurred or not), it could increase or decrease its own oscillator slightly to either "catch up" or "slow down" to align with the zero-crossing. It's not even necessary to get the (edge) "polarity" correct, the loop should automatically lock to the "correct" edge (and run away from the other). In practice, this arrangement would probably have too much loop gain (and be unstable), so one might update the value, perhaps only once per second and/or scale down the control range (since at most +/-1% is required).

Another issue is where to obtain a reference "20 ms" event. Possibly a "Servo" pulse could be used (which is related to Timer 1 and the "time" variable), but these are all (system) software-driven and are known to be "upset" by certain PICaxe instructions. A PWM output might be more suitable, but sadly cannot be set as low as 50 (or 60) Hz, and 100 Hz needs a Division by 64 which prevents an exact integer division down to a 10 ms cycle being set (e.g. pwmout pwmdiv64, B.1, 155, 312 gives a frequency of 100.16... Hz). So a few refinements might be needed, but a PLL gives the opportunity to maintain the drive pulses at (almost) the correct frequency in the event of a Power Cut. Provided that the software is able to recognise that the reference pulse/edge has "vanished" and not just "moved away" (and chase after it).

Cheers, Alan.
 
Last edited:

Buzby

Senior Member
24519

Prompted by the errors the OP is seeing, I lashed up a simple 50Hz driven clock.

It's about 2 sec ahead of actual time at the moment, and has been since I started it at 19:56.
( I've not got any setting buttons, so it's a bit of guesswork to set the time.)

.24520

This is what my 50Hz looks like at pin C.2. ( The circuit is not optimised, I just used what bits were nearest to hand !. )

I'll leave it running overnight, and check again in the morning
 

Attachments

Buzby

Senior Member
It's morning now, about 09:05.

My clock is now about 8 seconds slow.

I think it might be because I'm using 'serout', which disables interrupts while its busy.

The picture below shows the serial data on the blue trace.

As you can see, the serial data overlaps the rising edge of the 50Hz, which is what the code is using.

I'll try using hserout instead.

.24522
 

tmfkam

Senior Member
There are a few online monitors for the Grid frequency. Trouble is they almost never agree. I had a discussion with a lecturer at College when I made the original for classroom demonstrations. Dr. P (A doctor of Electrical Engineering) said "I don't need that" when I introduced it in a presentation, "I use the online one, much easier". "Which one?" I asked. I then called up three online, all showed different frequencies by some measure, then, true to form, one froze and one crashed. Every other lecturer used it, not Dr. P. Never did!

I tried to use the online ones to calibrate my later design once. They still couldn't agree. Much better to wait for 3am and set it to 50Hz then.
 

Buzby

Senior Member
There are a few online monitors for the Grid frequency. Trouble is they almost never agree.
I noticed that too, and my scope shows frequency as well, so that was another one in the mix.

However, the differences shouldn't amount to 10 seconds discrepancy in 12 hours, so my code or hardware is wrong !.

Cheers,

Buzby
 

AllyCat

Senior Member
Hi,
I'll try using hserout instead.
Yes, that "should" help, but only because you are not sending sequences of more than two characters. I believe that although HSEROUT does not "block" the internal (system) interrupts (e.g. for the Servos/time variable) it does still prevent the execution of other PICaxe Basic instructions, until transmission of the last character of any HSEROUT instruction has been started. So it should only delay the interrupt by up to about 5 ms.

I don't think it matters if the SEROUT is transmitting at the leading edge of the input signal, but if it is still transmitting (the second character) when the trailing edge occurs, then the (polled) interrupt will never be seen. Another solution might be to poll the "Interrupt On Change" FLAG, which is available on all 08M2 pins (and most pins of other M2s). Or of course a (software) PLL system will easily ride over any missing events. ;)

Cheers, Alan
 
Last edited:

Buzby

Senior Member
Hi Alan,

I'll bear that in mind. I've never used 'hserstuff' on a M2 PICAXE before, usually I use X2, and it's a doddle to use it there.

As it is, I'm struggling to get 'hserout' working at this moment.

My code is below. It should clear the OLED and then print 'Ready', but all I get on my AXE133 is squiggles.

Code:
setfreq m8

' Setup hserial
hsersetup B2400_8 , %00010010 ' Disable hserin, invert hserout

pause 4000 ' Let OLED start
hserout 0,(254,1) : pause 4000    ' Clear screen
hserout 0,(254,130)            ' line 1 pos 2
hserout 0,("Ready ..")
 

AllyCat

Senior Member
Hi,

First try sending individual characters, because the AXE13x (software) cannot handle concatonated (adjoining) characters reliably. I don't know what gap the HSEROUT may include between characters, but I did notice recently that the "Hello I am your PICAXE.." from a new 08M2 had a gap of around two bit-periods. But that's at 4800 baud (or mine's actually 4650 baud ! ), which the AXE13x can't receive anyway. Interestingly, the PE6 (or PE5) Terminal Emulator does transmit concatonated bytes if the default "Insert 5ms Delay" option (which is often around 20ms) is cancelled.

Cheers, Alan.
 

Buzby

Senior Member
Still struggling !

Single character TX, and a decoupling cap on power pins, but still no joy.

Code:
#picaxe 08M2
#no_data

setfreq m8

hsersetup B2400_8 , %00010010 ' Disable hserin, invert hserout

' Let OLED start
pause 4000 

 ' Clear screen
hserout 0,(254)
hserout 0,(1)

pause 4000    

' line 1 pos 2
hserout 0,(254)
hserout 0,(130)    
        
hserout 0,("R") 
hserout 0,("e")
hserout 0,("a")
hserout 0,("d")
hserout 0,("y")
hserout 0,(".")

pause 4000

do  
    pause 1000
    inc b0
    hserout 0,(#b0)
loop
 

Buzby

Senior Member
I've given up faffing with hserout on 08M2 !.

Now the clock has been started again, but without the OLED updating every second.

Only if I set pinC.3 low will the update happen, so I'll only do this every hour or so.

Regarding the short and long-term accuracy of the 50Hz supply, somewhere I've got a TCXO module. Using this to clock a PICAXE would make the basis of a decent frequency meter. ( Not on my list of lockdown projects though. )

Cheers,

Buzby
 

AllyCat

Senior Member
Hi,

Strange. The HSERSETUP looks correct. Does your 'scope show no output on c.0 or "wrong" data? You could also try Poking each byte directly to the SFR (TXREG = $7A) , but my recent trial seemed to show that the HSEROUT command is more efficient. Ah, you might need to initialise the port.pin with a LOW C.0 before using it.

However, I would expect single characters via SEROUT to work as well, since there should still be at least a 5ms window available to catch each interrupt. But for reference, the SFR addresses of the Interrupt-On-Change Registers are Positive-Going Enable (IOCAP = $F1) , Negative-Going Enable (IOCAN = $F2) and Flags IOCAF = $F3 (POKESFR a zero to clear the flag(s) before each time ). In each case the register bit corresponds to the port.pin number (i.e. the weight of C.2 is 4).

Cheers, Alan.
 

Buzby

Senior Member
My 50Hz controlled clock is spot on !.

Running without using 'serout' has not lost any interrupts, and the clock is keeping perfect time.

If I was to take this any further I would put some code in to minimise the effect of any induced noise, and maybe improve the hardware circuit.

But I'm not taking it any further, as I'm now certain the code in post #10 will drive the OPs clock.

Regard the trouble I'm having with hserout on the 08M2, if I decide investigate the problem I'll start a new thread for it.

Cheers,

Buzby
 

tmfkam

Senior Member
My 50Hz controlled clock is spot on !.

Running without using 'serout' has not lost any interrupts, and the clock is keeping perfect time.

If I was to take this any further I would put some code in to minimise the effect of any induced noise, and maybe improve the hardware circuit.

But I'm not taking it any further, as I'm now certain the code in post #10 will drive the OPs clock.

Regard the trouble I'm having with hserout on the 08M2, if I decide investigate the problem I'll start a new thread for it.

Cheers,

Buzby

Thinking back... It was seven years or so ago! I have a vague recollection that I was scrolling results in the terminal when I experimented with this. Possibly only once every five minutes or so, but perhaps that is why it didn't work well for me too?
 
Again my thanks for all your input.

From AllyCats earlier suggestion of not using the opamp or comparator, I rebuilt the circuit using a voltage divider and resistor before seeing Buzby's similar suggestion.

This now works as I had intended and for all the argument or discussion about the long term accuracy of using mains cycles as the time base, I think this will be perfectly adequate for the wall mounted workshop clock. It's hardly sensible to incorporate automatic daylight saving changes (because of the mechanics of the machine) and there are bound to be power supply interruptions in its place of use so all in all, the precision will be adequate between the times when it will be necessary to occasionally reset or correct the time - as of course we have generally had to do frequently over most of the period during which humans have been using clocks.

My looking into this was anyway more an out-of-interest/learning exercise than a professional job but I am anyway pleased to see that the original query has piqued some further interest among some of you.


@AllyCat If I may indulge in your effort, can you say a bit more about why my suggested code...
... is 'dangerous' and how this might not be correctly processed.
(a) if pinC.2 = 1 then goto main:
(b) if pinC.2 = 0 then do
(c) end if

That does appear to be a (potentially) "dangerous" structure.
As one mains cycle is 20 mS but the processing time is very shorter, the reasoning (that's my questionable logic!) is that in this way I can be quite certain that the count to increase the variable can only be achieved on the arrival the high input but will not be affected if the sequence returns back to the start while the input remains high.
Using fixed time pauses generated by the processor might achieve the same thing but is it really more reliable?

I ask this (with no denial of my near novice status) as I have used similar structures for previous projects and they have worked well, whereas using a processor generated time in parallel to an external time seems to be fraught with the possibility of an accrued out of sequence error.

In a similar vein and just to offer a slight defence for my use of the "let W0 =W0 max 1492" this is not so much to patch a problem but to eliminate the program hanging due to some error that I have not considered and can't see . I have often used this in the development of code having learnt from experience that this can be useful.
That the use of debug slows down programs so much is a frequent regret - though not a complaint.


@ Buzby If you don't mind indulging me with a little more of your time...

The revised code that you suggested does not seem to work for me! The time between 30 sec pulses are correct but the 80mS pause is not functional so that whatever value is entered, the pause remains the same, this being too short to drive the clock mechanism. I have managed to deduce by eliminating three references to the function that this is something to do with the interrupt but for the life of me I cannot follow your thinking to understand what is amiss.

I have also been trying to understand what this piece of the code is intended to achieve.

Code:
PBcopy = pinC.3
PB1shot = PBcopy ANDNOT PBmem
PBmem = PBcopy
Putting this in byte values this reads as

bit 17 = pinC.3
bit 19 = bit 17 andnot bit 18
bit 18 = bit 17

I haven't even managed to work out what the 'bits' are referring to let alone why they are needed.

I have mangled your code a bit - mmm, actually quite a lot - and now have this running in parallel to my own updated version each with entirely independent power supplies and counters; both are so far keeping identical time which is slightly wandering from computer time but this being presumably because of the irregularity of the mains frequency.

Code:
;read 50hz mains to get pulse out each 30 secs using 08M2

' C.2 is 50Hz input
' C.3 is push button interrupt

symbol Count50 = b0
symbol Count30 = b1
symbol Flag30  = b3

' Code starts here

Init:
setfreq M4
let W0 = 0

main:
setint %00001000,%00001000     ; Interrupt when C.3 is high
do : loop until pinC.2 = 1     ' Wait for mains cycle to go high

inc Count50 ' Count 50 interrupts
if Count50 >= 50 then
    Count50 = 0    ' Reset interrupt counter
    inc Count30 ' Count 30 seconds
    if Count30 >= 30 then
        Count30 = 0 ' Reset seconds counter
       Flag30 = 1 ' Set flag every 30 seconds
         
         if Flag30 = 1 then ; Drive coil if 30s counted
           high C.4    ' Energise coil
           pause 40
           low C.4 ' De-energise coil
         Flag30 = 0
       
endif
   endif
       endif
do : loop until pinC.2 = 0 ' Wait for mains cycle to go low

goto main:


interrupt:    ; Interrupt service routine - pushbuttom

      if pinC.3 = 1 then ; when button depressed
       do
    high C.4    ' Energise coil
      pause 40
      low C.4' De-energise coil
    pause 500
    if pinC.3 = 0 then exit ; when button released
loop
    endif

  let Flag30 = 0

setint %00000100,%00000100 ; re-activate interrupt
return
There is a need to add a second pause to limit the rate of the push button operation to a sensible speed for adjusting the time.


In the code you suggest you are using 'copy' but I can see no reference to this in my picaxe manuals. Is this a recent addition? Is using it documented somewhere?


I rather regret that I have not had the opportunity to work in conjunction with 'professionals' and see much more development of code from a rather more first-hand view. My knowledge is limited to what I have found within the picaxe manuals, what I have gleaned from this forum and what I have learnt from the application of thought to trial and error.

It's very often not so easy to grasp the potency of the reasoning presented by the more learned at this distance!

Again, my thanks for all your suggestions. I have learnt quite a few useful things from your interest in this exercise....

(I'm not going anywhere near the discussion about using hserout!!)
 

Buzby

Senior Member
Hi John,

A 1-Shot is the name given to a type of variable which is active for just one cycle of the main loop, triggered by a pushbutton or other signal.
Another name is 'Edge triggered', as it detects the 'edge' of a rising signal. ( It can also be written to detect the falling edge. )

It was used in the code I wrote to allow the PB signal to be 'ORed' with the 30sec pulse, without 'swamping' the effect of the 30sec if the button is held down for more than 30 seconds.

To understand the action you need to think about what happens to the two lines 'PB1shot = PBcopy ANDNOT PBmem' and 'PBmem = PBcopy' on consecutive loops of the code.

Code:
PBcopy = pinC.3
PB1shot = PBcopy ANDNOT PBmem
PBmem = PBcopy
This code is in a continuous loop, either from a 'main .. goto main' or a 'do .. loop' structure.

(( this table is shown in a 'code' window just to preserve the text formatting. ))
Code:
                | pinC.3  | PBcopy  | PBmem | PB1shot
 T0           |    0       |      0       |       0      |      0
 T1           |    1       |      1       |       0      |      1
 T2           |    1       |      1       |       1      |      0  
 T3           |    1       |      1       |       1      |      0
Most times round the loop the PB is not pressed, so pinC.3 is low. This is row T0 above.
When pinC.3 goes high row T1 detects this, and as PBmem has not yet been updated ( that happens in the next line of code ) the PB1shot is set.
In further loops with the PB still pressed ( T2, T3, etc ) the 1-shot is reset, as no 'edge' has been detected.

The easiest way to see this step-by-step is run this litte bit of code in the simulator. All will become clear !

Regarding bit variables, they are variables that can only have values of 0 or 1. See Manual 2 Page 13.

I think I now know why my code in post #10 did not work for you, exhibiting the non-alterable 'pause' effect.
There is a 'bug/feature' in PICAXE BASIC where an interrupt truncates a 'pause', not resuming the delay after the interrupt has executed.
This is not easily visible in the simulator, but the code below does demonstrate it. Sorry I didn't spot it before I posted the code in #10.

Code:
setint %00000100,%00000100     ; Interrupt when C.2 is high 

do    
    pause 10000
    high c.4
    pause 10000
    low C.4
loop

interrupt:
    do : loop until pinC.2 = 0 ' Wait for interrupt pin to go low
    high c.1
    pause 500
    low c.1
    setint %00000100,%00000100 
return
Anyway, we've both learnt something from this thread, so thanks for starting it !

Cheers,

Buzby
 

AllyCat

Senior Member
Hi,
...... can you say a bit more about why my suggested code...... is 'dangerous' and how this might not be correctly processed
I "disliked" that code as "potentially dangerous" at three levels: (1) It might not work, (2) It might not work the way YOU believe it works, and (3) WE (or at least I) cannot (easily) see IF or HOW it works.

It was particularly the two consecutive lines: IF pinC.2 = 1 ... and IF pinC.2 = 0 ... which made me "suspicious" (normally one would use the IF .. THEN .. ELSE .. ENDIF structure. The assumption is that either one or the other of the IFs is true, but pinC.2 changes every 10 ms and each line takes about 1 ms to execute, so there's about a 5% chance that both will be either true or false. It might be a "legitimate" way to detect an "edge" (i.e. a change in the logic level) but what if it occurs just before or just after that pair of instructions?

To assist particularly with (3), most computer languages have a "formal" structure, for example IF ... ENDIF and DO ... LOOP pairs of keywords, which are often shown on separate lines, indented by the same number of columns (for example to indicate that they are a "matching pair"). However, PICaxe's IF ... THEN GOTO ... is NOT one of these and some programmers "hate" the GOTO. I'm not one of those and often do use that format, but the "GOTO" is optional, so I might write, for example : IF pinC.2 = 1 THEN {GOTO} flash_a_LED.

However, GOTO main is usually taken to mean "The task is done, now go back and start again" (for example after the "flash_a_LED" section above). Looking again at your program, I (now) see that yours is a "waiting" loop (for pinC.2 to become 0) but (unnecessarily) re-activating the interrupts repeatedly. So IMHO that would be better coded as DO : LOOP UNTIL pinC.2 = 0. The DO and LOOP might be equally indented on consecutive lines, but as it's an "empty" loop, we might code it with the "newline" separator " : " (although the PICaxe Basic Compiler "guesses" that, if you "forget" to put in the " : " ).
Code:
main:
setint %00001000,%00001000     ; activate interrupt
if pinC.2 = 1 then goto main: 
if pinC.2 = 0 then do
endif
if pinC.2 = 0 then pauseus 10
endif
if pinC.2 = 1 then goto pulse:
if pinC.2 = 0 then loop   
endif
pulse:
The next "confusing" aspect of the above block of code is that it has Five IF ... THENs,, Three ENDIFs, Two GOTOs and One DO .. LOOP, "buried" inside the IF .. THENs. To say the least, I would consider the IF .. THEN .. DO .. ENDIF construction is "unusual". The IF pinC.2 = 0 THEN .. PAUSEUS 10 .. ENDIF is also "questionable" because it will execute in just over 1ms, whether the condition is true or false ! It would be better written as follows, but executing the PAUSEUS takes almost the same amount of time as "jumping over" the PAUSEUS:
Code:
   IF pinC.2 = 0 THEN
       PAUSEUS 10      ; Nominally 100 us actually takes about 400 us to execute (including interpreter overhead)
   ENDIF
;    Is actually the same as:
   IF pinC.2 = 1 THEN GOTO skip   ; The "Jump" takes around 400 us longer than a "fall through" into the next line.
       PAUSEUS 10        ; About 400 us in practice
skip:
Again, it looks as if the last pair of IF pinC.2 THENs in your original program could be replaced by another "waiting" loop DO : LOOP UNTIL pinC.2 = 1 and then fall into "pulse". So I think your code above might be basically the same as:
Code:
    DO : LOOP UNTIL pinC.2 = 0
    PAUSE 1
    DO : LOOP UNTIL pinC.2 = 1
pulse:
As for the if W0 = 1492 then low C.4 type of structure, that is also "dangerous" because there is the possibility that (somehow) the program might jump from (say) 1491 to 1493 and then the conditional test would fail almost "forever". So yes, a MAX is one way of avoiding that (but might then cause a problem if you later mistakenly changed only the conditional limit to 1500), but a better method is to change the conditional test itself to if W0 => 1492 then low C.4 (i.e. Equal To OR Greater Than 1492).

Cheers, Alan.
 
Top