Interfacing Clock Switches to ATmega168

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First, I'd like to thank the community here for your wonderful tutorials--they especially helped me as I learned about using interrupts on my ATmega. I'm nearing completion on a project I've been working on but I've hit a snag that I'm hoping someone here can help me with.

The project is converting an alarm clock with a standard 7 segment display to use a 16x2 character display interfaced to an ATmega168. The new clock shows the full date and time, has a backlight that adjusts itself based on the ambient light, and uses both the 9V battery backup and 120V->12V transformer from the original clock (regulated to 5V DC).

What I can't figure out is how to interface the original clock's buttons to my ATmega. There are four buttons and one switch on their own small PCB. Pressing the buttons shorts two of seven output pins like so:

  • Alarm button: pins 4 & 5
  • Time button: pins 1 & 5
  • Hour button: pins 1 & 2
  • Minute button: pins 1 & 3
  • Alarm switch, on position: pins 5 & 7
  • Alarm switch, off position: pins 5 & 6
I assumed at first that pin 5 was a common ground and connected the other pins to my ATmega's PORTD using the internal pull-up resistors. As you can guess from the above, this didn't work for the hour and minute buttons. I would like to connect the buttons to PORTD (I have pins 0 through 4 available) and use pin change interrupts but I'm open to any suggestions.

Last Edited: Sun. Oct 29, 2017 - 11:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I often find a picture is worth 1,000 words so here's 1,000 words on the subject...

One thing becomes obvious is 5-6 and 5-7 are mutually exclusive so you don't need to read both. So either 6 or 7 can be left unconnected.

I would have said that 1+5 should both just be Gnd but then you could never read the 1-5 Time button so one of those two (your choice) needs to be a driven output (to Gnd) when the other buttons are scanned but then switched to an input when the "time" button is scanned. The others need to be set to inputs with the pull-up enabled and will then read 0 when the appropriate button is pressed.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After a bit of thought maybe try something like...

D0 is set to input when reading "Time" but otherwise is an output set to 0. All other lines are input with pull-ups enabled.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're right, the picture is worth 1,000 words. I do have pin 6 unconnected in my circuit so that I don't use two inputs for the alarm switch. I had also thought of making pin 1 a ground but couldn't solve the Time button problem.

Outputting 0 to pin 1 but switching to an input to read time should work great. I'll have to move my code out of the pin change interrupt but I don't think that can be helped. Maybe what I'll do is keep the interrupt for the other buttons and put only the scan of the Time button in the main loop. At the moment, my main loop is empty because everything is handled by timers and interrupts.

What I like most about your solution is that it doesn't require any schematic changes. I already have pins 1, 2, 3, 4 and 7 connected to PORTD with pin 5 wired to ground.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is a button matrix of some sort. Should be tutorials for that too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I'll have to move my code out of the pin change interrupt
You didn't want it there in the first place. Switches bounce and interrupts aren't good at debouncing.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was debouncing with a global variable that decremented on each clock cycle. Point taken though, I'll move all of my button logic out of the interrupt.

My original intent was to allow the processor to sleep between clock ticks and button presses. However, since it plugs into a wall outlet and only uses the 9V battery as a backup source, it doesn't make a big difference. The real power drain is the LCD backlight which I will turn off when the 12V source is lost.

Jepael, I'll check out some button matrix tutorials but I'm guessing the solution won't differ from clawson's.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Most button scanning solutions (including debounce) are based on a timer interrupt - often something like 10ms. You consider a button to have changed state when it's seen to be in a steady state for N such interrupts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I misspoke above and my debounce counter is in fact decremented by a timer interrupt that fires every 8ms. If I added the scan of the Time button to that timer interrupt, then I could leave all of my button code in interrupts. Does that sound like a good approach? I think I like it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I use the pin change interrupt for a row of switches. And then I set up a timer for 0.030 seconds intervals. Since the interval between a Pin Change IRQ and the timer IRQ can be any length, I set the timer interval at least twice as long as a standard debouncing interval.

I have two banks of flags. Each flag is a single bit in the GPIOR registers. Each GPIOR register is a bank of flags. One bank is called ACTIVE and other is BUTTON_STATUS. The ACTIVE uses 1 for a push-button switch in operation and B_S uses 0 for press and 1 for release. These reflect the hardware as pressing a switch connects it to ground and releasing it pulls it high through the port's internal pull-up resistors.

When a switch is pressed, the Pin change IRQ triggers. The IRQ determines which switch was pressed. It sets the active flag for the switch and puts 0 in the Button-status flag for the switch. Then it turns the pin-change IRQ off. When the timer1 IRQ happens, this IRQ checks if any switch is active by testing its ACTIVE flag. If active, it checks whether it is pressed or released. If pressed it confirms that the switch is connected to ground. If so, then it is debounced and valid. The timer IRQ sets a flag for valid switch press to the main code. The button-status flag for the switch is set for release. Then the pin change IRQ is reactivated.

When the switch is released the pin change IRQ triggers and the ACTIVE flag is set. The Pin change IRQ is switched off. The Timer IRQ checks that the switch is high, which means that the release debounce is valid. Usually the main code doesn't need to know about a push-button release, so a flag is not sent to the main code. But it could be if needed.

This algorithm allows for debouncing without forcing the main program to wait in a loop. It can be extended to handle multi-switch press-and-hold conditions. I suggest doing Warnier-Orr diagrams before coding in order to keep all the conditions and flags in order given the complexity and multiple interrupts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I do like the approach of disabling the pin change interrupt after a button press, checking for a consistent state in the timer interrupt, then reactivating the interrupt. It avoids executing an extraneous interrupt for each "bounce" as is happening now.

My code is in C so in my case I think I would allocate byte sized variables for my flags instead of manipulating GPIOR registers explicitly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Takaitra wrote:
What I can't figure out is how to interface the original clock's buttons to my ATmega. There are four buttons and one switch on their own small PCB. Pressing the buttons shorts two of seven output pins like so:
  • Alarm button: pins 4 & 5
  • Time button: pins 1 & 5
  • Hour button: pins 1 & 2
  • Minute button: pins 1 & 3
  • Alarm switch, on position: pins 5 & 7
  • Alarm switch, off position: pins 5 & 6
I assumed at first that pin 5 was a common ground and connected the other pins to my ATmega's PORTD using the internal pull-up resistors. As you can guess from the above, this didn't work for the hour and minute buttons. I would like to connect the buttons to PORTD (I have pins 0 through 4 available) and use pin change interrupts but I'm open to any suggestions.
Use the internal pull-ups. Every so often:
drive pin 1 low, read PIND,
pull pin 1 back up,
drive pin 5 low, read PIND
and pull pin 5 back up.
How often is left as an exercise for the reader.

I think that pin change interrupts are not an option.
You can't keep both pin 1 and pin 5 driven low.
You wouldn't be able to tell when they were shorted.
If pin 5 is not driven low,
all of its potential partners would have to be driven low.
That would make it impossible to tell which pin shorted with 5.
Similarly with pin 1 not driven low.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Takaitra wrote:
What I can't figure out is how to interface the original clock's buttons to my ATmega. There are four buttons and one switch on their own small PCB. Pressing the buttons shorts two of seven output pins like so:
  • Alarm button: pins 4 & 5
  • Time button: pins 1 & 5
  • Hour button: pins 1 & 2
  • Minute button: pins 1 & 3
  • Alarm switch, on position: pins 5 & 7
  • Alarm switch, off position: pins 5 & 6
I assumed at first that pin 5 was a common ground and connected the other pins to my ATmega's PORTD using the internal pull-up resistors. As you can guess from the above, this didn't work for the hour and minute buttons. I would like to connect the buttons to PORTD (I have pins 0 through 4 available) and use pin change interrupts but I'm open to any suggestions.
Use the internal pull-ups. Every so often:
drive pin 1 low, read PIND,
pull pin 1 back up,
drive pin 5 low, read PIND
and pull pin 5 back up.
How often is left as an exercise for the reader.

I think that pin change interrupts are not an option.
You can't keep both pin 1 and pin 5 driven low.
You wouldn't be able to tell when they were shorted.
If pin 5 is not driven low,
all of its potential partners would have to be driven low.
That would make it impossible to tell which pin shorted with 5.
Similarly with pin 1 not driven low.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm new around here and I have a project that needs push buttons. In debouncing posts you're always speaking about "Lee Danni's code" but I can't find it, could you please provide me the link? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

diogofvc wrote:
but I can't find it,

http://www.avrfreaks.net/comment...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the username is just "danni" - nothing to do with Lee 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Err no. There are two solutions - one vertical, one horizontal. One comes from Peter Dannegger ("danni") and the other comes from Lee Theusch ("theusch"). I've given a link to the "danni" code. I'm sure one might find Lee's solution easily.

 

Somewhere there is a most excellent series of posts by Johan Ekdahl where he analysed and explained the operation of each. Might be a challenge to try and find that too?

 

EDIT: not sure if this is the "definitive" solution but one example of Lee's code: http://www.avrfreaks.net/comment...

Last Edited: Fri. Oct 27, 2017 - 04:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Right, yes - so, to summarise: there is Lee's code, and there is danni's code, but there is no "Lee Danni's code" 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
not sure if this is the "definitive" solution but one example of Lee's code

It is the one I was going to dig up, where I needed to defend the assertion that a bank of 8 or 16 input signals could be debounced in a few microseconds every 10 milliseconds.

 

The routine in the link you gave was my starting point circa 2000.  It was ported to AVR by a co-worker from Moto code.  It is only a very few clock cycles, but does steal a number of AVR registers for "register variables".

 

Since, I have made C versions, both with a fixed number of unrolled intermediate stages as with the linked ASM and also with array-/loop-driven  cleaner reusable code at the expense of some cycles for array/loop work.  My approach can return either the current  state of the input bank, or newly-made edges.  Often I will return the current state, and then construct rising and falling edges as shown in http://www.avrfreaks.net/comment...

 

A post with the ASM version converted to C:  http://www.avrfreaks.net/comment... (the whole thread might be useful for OP, with my code and danni's code and others' code and lots of arguing discussion)  A complete production program example using array/loop, 16-wide, is given as an attachment in http://www.avrfreaks.net/comment...

 

clawson wrote:
Somewhere there is a most excellent series of posts by Johan Ekdahl where he analysed and explained the operation of each. Might be a challenge to try and find that too?

In http://www.avrfreaks.net/comment... Johan gave a link to http://www.avrfreaks.net/comment...

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, I'm sorry  for my mistake but in some posts I saw "Lee Danni" so I though that was the same code frown Thanks again!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And the oft-referenced article by Jack Ganssle on switch contact bounce & debouncing:

 

http://www.ganssle.com/debouncing.htm

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I already created my own thread with my problem. Thanks and if you could take a look...yes

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It would be helpful if you gave a link to that thread

 

http://www.avrfreaks.net/forum/problems-my-alarm-clock-project-not-sure-it-debouncing

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Somewhere there is a most excellent series of posts by Johan Ekdahl

Oh, gosh.. [blushing]

 

clawson wrote:
where he analysed and explained the operation of each

If my memory serves me, I only did Dannis code - simply because that is the one I like, and happen to use (or is that the other way around ;-). I seem to recall a most pleasant argument with Lee where I failed to grasp the horizontal-ness of it (entirely my fault, stupid, lazy and comfy as I am...). It was still worth the argument. Wrestling with a pig in mud and all that... ;-)

 

[ Apologies for reference to an ancient gag, the substance of which may not be entirely clear to anyone in the 2010's (-: ]

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

diogofvc wrote:
Ok, I already created my own thread with my problem. Thanks and if you could take a look...yes

 

awneil wrote:

It would be helpful if you gave a link to that thread

 

http://www.avrfreaks.net/forum/problems-my-alarm-clock-project-not-sure-it-debouncing

 

 

 

With that said, I'll Lock this so as not to create confusion

 

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

Topic locked