I want to control the chiming of the hour in a refurbished mantel clock. The primary source of time information is a 30ms once-per-second pulse from a quartz clock module.
I’m thinking pseudo code something like that below. My question is where to place the interrupt enable and the interrupt flag reset so that the program will recognize the once-per-second interrupt and increment the counter even during the chiming, which takes several seconds. Grateful for any comments and suggestions.
PSEUDO CODE
DO
Enable interrupt on change for pin X
On interrupt go sub increment counter
LOOP
Sub increment counter
Seconds = Seconds + 1
If Seconds = 60 Then
Minutes + Minutes + 1
Seconds = 0
End If
If Minutes = 60 Then
Hours = Hours +1
Minutes = 0
gosub Strike Hour
End If
End sub
Sub Strike Hour
For numcount to Hours
output a pulse to strike chime
Next
If Hours = 12 Then
Hours = 1
End If
reset interrupt flag
End sub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Interrupts are quite easy in GCB. But there still is a couple of basic things to remember.
1) you want to spend a minimum amount of time in the interrupt routine or you may miss the next interrupt
2) Use "flag" variable to initiate events in the main routine. This would be like your strike hour "hour" routine
3) You may want to use ExtInt0 instead of "Enable interrupt on change for pin X". Because you can choose the rising or falling edge and a 30ms pulse will cause more than 1 interrupt.
So the "On Interrupt" statement needs only to be placed in the initialization before the "Main" loop. Look up the On Interrupt in the help.
Your "increment counter" is fine except for the "gosub Strike Hour" which is going to take time and you don't want to miss the next second pulse. So here you would use a variable like "hourStrike" and set it to True or 1. Then in the Main you would check with an IF if hourstrike was True then you would call your "StrikeHour" sub. You do not need to reset the interrupt flag but you do need to reset your "hourStrike" variable to False or 0. The interrupt resets the interrupt flag in the background.
One final comment on the if statements in the "incrementcounter " . "If Seconds = 60 Then" It is usually good practice to use "If Seconds > 59 Then" because it would catch it if something strange happens.
The Interrupt is automatically enabled when you run the On Interrupt statement and you are not going to shut it off because you want to catch every pulse.
GL
Mike
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks Mike. You clearly understand that my problem is making sure not to miss any of the once-per-second pulses during a several second chiming sequence. Somehow I am not completely wrapping my head around the logic of how the interrupt works.
You're right about using >= instead of +, I did run into that problem.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I would use the quartz clock modude and count the 30ms pulses.
You could use the millis function.
#includemillis().hdimtempmillisaslongtempmillis=millis()doifmillis()-tempmillis=3600000;milliseconds in an hourthenchime : tempmillis=millis()endifloop
Last edit: stan cartwright 2021-04-26
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Attached is example code that should do want it to do. It is well commented so you can understand what is going on. Remove the hserial sections after testing and debugging.
William
Note: There was a bug in the original file where it would chime 13 times when the clock hit 1. This has been corrected in the newly attached file.
I'd go for timer 0 overflow as there's a calculator to get the interrupt time.
Set it up for mS and use a counter to get the hour.
I got 328 code if you're interested.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks William, this is very helpful. I'm studying it and trying to make sure I understand it. I need to use the 16F1829 with a 32kHZ clock because this is a battery-operated circuit. So I changed that and also the appropriate chime output pin. For the ON Interrupt line, I need a falling edge on portb.6 . Does that go on the same line replacing portb.0?
I made those changes and got errors related to the USART. Errors 464 and 466 "Cannot store -1 in the byte variable SPBRG" and same for SPBRGH. When I commented out all the USART related lines it compiles OK.
I really appreciate your input. The serial output should be very helpful for debugging if I can get it to work.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I mentioned the 30ms pulse every second first William. Thought Reginald wanted an alternative.
The timer 0 overflow interrupt is a nice way to start with interrupts. They are surrounded with they're complicated but just not explained properly and are useful.
Gcb can do a short bit of code in a 1ms interrupt, in fact a counter to make it every 20ms so to use rc servos and stepper motors. I got timer 0 overflow to work with avr and pic.
someone should do a tutorial. mr Roper has used timer 0 overflow to write the millis function, which is useful but never finished the do every which he gave me a demo to test.
I mainly use 328 and the interrupts that are in gcb demos/help but they are useful.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
It's surprising what code can be done in a 1ms interrupt .. ie before the next interrupt.
It can be a counter so stuff happens every 20 ms. and still carry on with the main program.
The millis() funtion is an example of timer 0 overflow and is sorted so it works across pic and avr...no simple feat.
Try setting up a timer 0 overflow interrupt every 1ms for an experiment .. port b1=not port b1
scope the output
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Reginald,
I assume the 1 second pulse also runs the clock, so you would not want the program to provide the timer. You already have the precision time from that pulse.
One additional thing you need is a way to set the chime clock hours and minutes to the right time. You could use 7segment leds but they would kill your battery, so you could turn them on only when a a button is pressed.
portb.6 can be used for the interrupt using IOC
The neg edge can be used by setting the IOCBN6 bit in this register:
REGISTER 13-5: IOCBN: INTERRUPT-ON-CHANGE PORTB NEGATIVE EDGE REGISTER
(PIC16(L)F1829 ONLY)
IOCBN7 IOCBN6 IOCBN5 IOCBN4 — — — —
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes, the external 1/s pulse is the time reference. My plan for setting the time is to provide a SYNC button that sets time to zero and a INCR button that advances the hour counter and confirms the new hour by initiating the chime sequence.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I just found an 16F1829 that I can throw on a dev board. I would think that using INTO on falling edge on would be more straight forward than IOC. I will try a few things and posts some code to test.
What stage are you in now? Do you have a chip on a board connected all up and ready to test?
Have you successfully operated the 16F1829 at 32Khz?
What is the pulse requirement for the chime output ?
How much time between consecutive chimes do you want ?
FYI: This can be done just as easily without using an interrupt. But either will work
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Glad to have any ideas. I'm not a sophisticated user.
I have a breadboard that I use to test clock programs. I have a PicKit 3 that I keep handy. Yes, have used the 16F1829 with the 32kHZ internal oscillator. My previous version of the program is for a circuit that uses a separate dedicated IC to do the counting, so the uchip device gets a square wave input with a one hour period. Plenty of time for even the longest chime sequence. I'm sure it's pretty clunky but it has worked for years. So my immediate goal is to eliminate the separate IC. With a 16LF1829 I was able to reduce the supply current to about 1uA. Battery lasts a long time!
Another part of the circuit is a transistor interface that takes the quartz module pulse of 1.5 VDC and outputs a 5 VDC output . I see that the 16F1829 has a comparator function, so I'm hoping that it may be possible to use that instead of a discrete transistor, simplifying the circuit even further.
The solenoid driver pulse length needs to be about 400 ms. GCB doesn't accurately scale the timing with the 32kHZ oscillator, so the the actual time value in the program needs to be larger by a factor of about eleven.
Again, thanks very much for your help and suggestions. This forum is a great resource!
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Here is a test program that works nicely. It demonstrates how to use the 16f1829 chip at 32Khz and how to generate relatively accurate delays/ pulse times given that GCB timings and delays are not supported at 31Khz.
I do not recommend using IOC for this application , but instead use the interrupt on the falling edge of the dedicated interrupt pin on PortA.2.
Do you want to eliminate the external timer and use pic interrupts which if using a crystal for the clock should be accurate? I did a clock, albeit analogue, that ran off a timer 0 overflow interrupt every ms. It was mainly to learn trig. There's 1 ms interrupt demos.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Adding a chime function to an existing clock (Crystal Clock Module) so that it chimes 1 time for every hour of the current time when hours change.
So for example , when the time changes from 11:59:59 to 12:00;00. the PIC will send 12 chiming pulses of 400 ms each to a solenoid to ring the chime . Chime pulses are spaced apart by 2 seconds (adjustable). And while chiming, the 400 ms pulses cannot be interrupted and the chiming pulse cannot interfere with the seconds counter.
There is absolutely no need for 1 ms pulses, 1 ms delays, or 1 ms interrupts or for a timer in this application.
Besides, you cannot get 1 ms accuracy with a PIC operating at 31.25Khz with its internal clock. But you can get precise 1 second accuracy when the Pic is receiving a pulse every second from an external crystal clock module that has an accuracy of 6=10 ppm. The PIC simply needs to sync with the external clock and chime on the hour.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
William, I changed "IF seconds >59" to "IF seconds > 5" so that it increments 10x faster. It's been running for several hours with no errors.
I tried to change Clock_Pulse pin from PORTA.2 to PORTB.6, which was how my breadboard was wired, but that made the program hang.
I'm trying to think how to use the serial commands. Ideally, there would be a way to do this within the IDE, while the chip is still on the LPC or Curiosity board, just plug in the external 1pps signal.
Thanks again for your carefully commented program. I'm still absorbing its subtleties.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
interrupt guidance
I want to control the chiming of the hour in a refurbished mantel clock. The primary source of time information is a 30ms once-per-second pulse from a quartz clock module.
I’m thinking pseudo code something like that below. My question is where to place the interrupt enable and the interrupt flag reset so that the program will recognize the once-per-second interrupt and increment the counter even during the chiming, which takes several seconds. Grateful for any comments and suggestions.
PSEUDO CODE
DO
Enable interrupt on change for pin X
On interrupt go sub increment counter
LOOP
Sub increment counter
Seconds = Seconds + 1
If Seconds = 60 Then
Minutes + Minutes + 1
Seconds = 0
End If
If Minutes = 60 Then
Hours = Hours +1
Minutes = 0
gosub Strike Hour
End If
End sub
Sub Strike Hour
For numcount to Hours
output a pulse to strike chime
Next
If Hours = 12 Then
Hours = 1
End If
reset interrupt flag
End sub
I would say to pull the On Interrupt stuff out of the loop. Just initialize and forget.
Interrupts are quite easy in GCB. But there still is a couple of basic things to remember.
1) you want to spend a minimum amount of time in the interrupt routine or you may miss the next interrupt
2) Use "flag" variable to initiate events in the main routine. This would be like your strike hour "hour" routine
3) You may want to use ExtInt0 instead of "Enable interrupt on change for pin X". Because you can choose the rising or falling edge and a 30ms pulse will cause more than 1 interrupt.
So the "On Interrupt" statement needs only to be placed in the initialization before the "Main" loop. Look up the On Interrupt in the help.
Your "increment counter" is fine except for the "gosub Strike Hour" which is going to take time and you don't want to miss the next second pulse. So here you would use a variable like "hourStrike" and set it to True or 1. Then in the Main you would check with an IF if hourstrike was True then you would call your "StrikeHour" sub. You do not need to reset the interrupt flag but you do need to reset your "hourStrike" variable to False or 0. The interrupt resets the interrupt flag in the background.
One final comment on the if statements in the "incrementcounter " . "If Seconds = 60 Then" It is usually good practice to use "If Seconds > 59 Then" because it would catch it if something strange happens.
The Interrupt is automatically enabled when you run the On Interrupt statement and you are not going to shut it off because you want to catch every pulse.
GL
Mike
Thanks Mike. You clearly understand that my problem is making sure not to miss any of the once-per-second pulses during a several second chiming sequence. Somehow I am not completely wrapping my head around the logic of how the interrupt works.
You're right about using >= instead of +, I did run into that problem.
I would use the quartz clock modude and count the 30ms pulses.
You could use the millis function.
Last edit: stan cartwright 2021-04-26
here's a clock i wrote using every 1ms interrupt https://www.youtube.com/watch?v=kEcN4Zo3Syg
Hi Reginald.
Attached is example code that should do want it to do. It is well commented so you can understand what is going on. Remove the hserial sections after testing and debugging.
William
Note: There was a bug in the original file where it would chime 13 times when the clock hit 1. This has been corrected in the newly attached file.
Last edit: William Roth 2021-04-28
I'd go for timer 0 overflow as there's a calculator to get the interrupt time.
Set it up for mS and use a counter to get the hour.
I got 328 code if you're interested.
Why?
The project already receives a 30 ms pulse every second from a crystal clock. There is no need to implement a timer or to count ms.
As I understand it Reginald already has a clock. He is adding a chiming function to an existing clock that outputs a short pulse every second.
Last edit: William Roth 2021-04-28
Thanks William, this is very helpful. I'm studying it and trying to make sure I understand it. I need to use the 16F1829 with a 32kHZ clock because this is a battery-operated circuit. So I changed that and also the appropriate chime output pin. For the ON Interrupt line, I need a falling edge on portb.6 . Does that go on the same line replacing portb.0?
I made those changes and got errors related to the USART. Errors 464 and 466 "Cannot store -1 in the byte variable SPBRG" and same for SPBRGH. When I commented out all the USART related lines it compiles OK.
I really appreciate your input. The serial output should be very helpful for debugging if I can get it to work.
I mentioned the 30ms pulse every second first William. Thought Reginald wanted an alternative.
The timer 0 overflow interrupt is a nice way to start with interrupts. They are surrounded with they're complicated but just not explained properly and are useful.
Gcb can do a short bit of code in a 1ms interrupt, in fact a counter to make it every 20ms so to use rc servos and stepper motors. I got timer 0 overflow to work with avr and pic.
someone should do a tutorial. mr Roper has used timer 0 overflow to write the millis function, which is useful but never finished the do every which he gave me a demo to test.
I mainly use 328 and the interrupts that are in gcb demos/help but they are useful.
Thanks Stan. I think the real problem is I haven't quite wrapped my had around the way the interrupt works. Still studying.
This is 328 but same for pic
It's surprising what code can be done in a 1ms interrupt .. ie before the next interrupt.
It can be a counter so stuff happens every 20 ms. and still carry on with the main program.
The millis() funtion is an example of timer 0 overflow and is sorted so it works across pic and avr...no simple feat.
Try setting up a timer 0 overflow interrupt every 1ms for an experiment .. port b1=not port b1
scope the output
Reginald,
I assume the 1 second pulse also runs the clock, so you would not want the program to provide the timer. You already have the precision time from that pulse.
One additional thing you need is a way to set the chime clock hours and minutes to the right time. You could use 7segment leds but they would kill your battery, so you could turn them on only when a a button is pressed.
portb.6 can be used for the interrupt using IOC
The neg edge can be used by setting the IOCBN6 bit in this register:
REGISTER 13-5: IOCBN: INTERRUPT-ON-CHANGE PORTB NEGATIVE EDGE REGISTER
(PIC16(L)F1829 ONLY)
IOCBN7 IOCBN6 IOCBN5 IOCBN4 — — — —
Yes, the external 1/s pulse is the time reference. My plan for setting the time is to provide a SYNC button that sets time to zero and a INCR button that advances the hour counter and confirms the new hour by initiating the chime sequence.
Hi Reginald.
I just found an 16F1829 that I can throw on a dev board. I would think that using INTO on falling edge on would be more straight forward than IOC. I will try a few things and posts some code to test.
What stage are you in now? Do you have a chip on a board connected all up and ready to test?
Have you successfully operated the 16F1829 at 32Khz?
What is the pulse requirement for the chime output ?
How much time between consecutive chimes do you want ?
FYI: This can be done just as easily without using an interrupt. But either will work
Glad to have any ideas. I'm not a sophisticated user.
I have a breadboard that I use to test clock programs. I have a PicKit 3 that I keep handy. Yes, have used the 16F1829 with the 32kHZ internal oscillator. My previous version of the program is for a circuit that uses a separate dedicated IC to do the counting, so the uchip device gets a square wave input with a one hour period. Plenty of time for even the longest chime sequence. I'm sure it's pretty clunky but it has worked for years. So my immediate goal is to eliminate the separate IC. With a 16LF1829 I was able to reduce the supply current to about 1uA. Battery lasts a long time!
Another part of the circuit is a transistor interface that takes the quartz module pulse of 1.5 VDC and outputs a 5 VDC output . I see that the 16F1829 has a comparator function, so I'm hoping that it may be possible to use that instead of a discrete transistor, simplifying the circuit even further.
The solenoid driver pulse length needs to be about 400 ms. GCB doesn't accurately scale the timing with the 32kHZ oscillator, so the the actual time value in the program needs to be larger by a factor of about eleven.
Again, thanks very much for your help and suggestions. This forum is a great resource!
Ok.
I think I now have the information I needed to write a good example for you to test.
Reginald.
Here is a test program that works nicely. It demonstrates how to use the 16f1829 chip at 32Khz and how to generate relatively accurate delays/ pulse times given that GCB timings and delays are not supported at 31Khz.
I do not recommend using IOC for this application , but instead use the interrupt on the falling edge of the dedicated interrupt pin on PortA.2.
Enjoy
Last edit: William Roth 2021-04-30
Preliminary tests look good. Attached is the schematic of my current version with the separate counter chip that I want to eliminate. More in a bit.
The PDF will not open. File format error
Do you want to eliminate the external timer and use pic interrupts which if using a crystal for the clock should be accurate? I did a clock, albeit analogue, that ran off a timer 0 overflow interrupt every ms. It was mainly to learn trig. There's 1 ms interrupt demos.
Not making a clock here Stan.
Adding a chime function to an existing clock (Crystal Clock Module) so that it chimes 1 time for every hour of the current time when hours change.
So for example , when the time changes from 11:59:59 to 12:00;00. the PIC will send 12 chiming pulses of 400 ms each to a solenoid to ring the chime . Chime pulses are spaced apart by 2 seconds (adjustable). And while chiming, the 400 ms pulses cannot be interrupted and the chiming pulse cannot interfere with the seconds counter.
There is absolutely no need for 1 ms pulses, 1 ms delays, or 1 ms interrupts or for a timer in this application.
Besides, you cannot get 1 ms accuracy with a PIC operating at 31.25Khz with its internal clock. But you can get precise 1 second accuracy when the Pic is receiving a pulse every second from an external crystal clock module that has an accuracy of 6=10 ppm. The PIC simply needs to sync with the external clock and chime on the hour.
William, I changed "IF seconds >59" to "IF seconds > 5" so that it increments 10x faster. It's been running for several hours with no errors.
I tried to change Clock_Pulse pin from PORTA.2 to PORTB.6, which was how my breadboard was wired, but that made the program hang.
I'm trying to think how to use the serial commands. Ideally, there would be a way to do this within the IDE, while the chip is still on the LPC or Curiosity board, just plug in the external 1pps signal.
Thanks again for your carefully commented program. I'm still absorbing its subtleties.