# Using switches installed across pins on AVR

I have the following layout, the switches are part of an existing hardware piece I'd like to reuse.

I'd like to be able to detect button presses on the vol+ and vol- buttons. The plan was to actively monitor their states by using internal pull ups and changing individual pin states. For example, to poll vol-, I'd set PB0 as an output, low state, and PB4 as input with active pull up, I expected to read a 0 when the switch is pressed.

I'd like to know if this is the correct approach. So far I've been struggling with this and it doesn't work.

This is the polling function I wrote:

uint8_t  read_button_volm(){
//VOLM
DDRB |=_BV(PB0);
PORTB &=~_BV(PB0);
DDRB &=~_BV(PB4);
PORTB |=_BV(PB4);
return ((PINB & (1<<PB4)) == 0);}


For comparison, I get a perfect behavior with the mute button, using the following method:

uint8_t read_button_mute(){
DDRB &=~_BV(PB2);
PORTB |=_BV(PB2);
return ( (PINB & _BV(PB2)) == 0 );}


As an added consideration, the switches have a really high resistance when pushed but it doesn't seem to be a problem with the mute button.

• @brhans writing to a PORTxn bit when the corresponding DDRxn bit is an input will enable the pull-up resistor. The is the standard approach on any ATMega/ATTiny device. – Tom Carpenter Mar 7 '17 at 21:53
• @Polyphil try adding some nop instructions. You can use the following: #define nop() __asm__ __volatile__ ("nop \n\t"), and then in your code do nop();nop();nop(); just before you do the return statement. If it works, I'll write up an answer explaining why. At the moment it is a hunch. – Tom Carpenter Mar 7 '17 at 21:59
• @Tom Carpenter I don't notice a difference when adding nops. I understand your fix proposal may be unrelated to my problem but if you happened to have a link or a short explanation as to what you're suggesting, I'd be interested to learn about it. – Polyphil Mar 7 '17 at 22:17
• @Polyphil the inputs on the ATTiny have a two clock cycle latency due to a synchroniser chain, so it takes at least 2-3 clock cycles after changing the pull-up value before it is reflected in the PIN register. Adding a nop causes the processor to wait a clock cycle. – Tom Carpenter Mar 7 '17 at 22:20
• @Tom Carpenter Your nop() solution eventually allowed my project to function so thanx for the suggestion. – Polyphil Mar 11 '17 at 8:10

Looks like you have something else going on here.

If the switches are wired as shown and the resistance of a closed switch is less than 10K ohm, then your read_button_volm() should work.

I'd start by narrowing down possibilities.

• Does the function return 1 if you manually short PB4 to ground with a jumper?
• Does the function return 1 if you manually short PB0 to ground and push the button?
• What voltage do you see on pin PB4 with a voltmeter when the button is pushed? Up?

Keep in mind that the internal pullup on the ATTINY12 can be as high as 122K ohm...

..and the voltage must be lower than 0.1 * Vcc on on of the digital IO pins that also has the XTAL function on them (PB4 & PB3)...

This means that to reliably detect a 0 on PB4 when it has the pull-up connected, you need the resistance between PB4 and ground to be less than...

122K / x < 0.1
x < 12K


So your switch must be less than 12Kohm to work reliably on this pin of this chip with the pull-up enabled.

Solutions

If you absolutely must get this layout with these parts to Work, there are some tricks you can try.

The first one that comes to mind is using time and capacitance to detect the closed switch.

What you would do in your code is basically...

1. Enable the pull-up on PB0
2. Set PB4 to output, level LOW.
3. Set PB4 to INPUT, no pullup.
4. Time how long it takes for PB4 input to switch to HIGH with the button pressed.

You have basically made an RC circuit out of PB0, the switch, and the capacitance of pin PB4 and you are trying to measure the R of the switch by measuring the time constant of this RC.

Get a bunch of readings for this time count. This is how long the capacitance on the PB4 takes to drain to below the 0 bit threshold voltage.

Now you might be able to check if the button is pressed by repeating the above steps, but using the times you collected for a closed switch to set a timeout. If the pin goes high before the time, then the button is pressed, if it does not then it is not pressed.

Keep in mind that this approach has some limitations....

1. It is very sensitive to other paths that could discharge pin PB4 even when the switch is not pressed. For example, a moist finger or even some dirt across the traces could easily discharge the capacitance as quickly as your closed switch so you need to keep the board clean.

2. This takes much longer than just checking a bit value in a PIN register so is not time efficient.

3. You can't really set a pin interrupt on the button press because of the sequence involved. There are ways around this using the watchdog, but they are more complicated and less power efficient that a straight pin change interrupt.

Let us know if this approach works for you!

• Thanks for your concise answer. Going point by point: - "Does the function return 1 if you manually short PB4 to ground with a jumper?" : yes - Does the function return 1 if you manually short PB0 to ground and push the button? :yes - What voltage do you see on pin PB4 with a voltmeter when the button is pushed? Up? : Ig drops from Vcc to around 1.5v I'd add that the resistance of a closed switch varies considerably and can be as high as 500k – Polyphil Mar 7 '17 at 22:05
• If you're wanting to use the internal pullups, then your switches much have a resistance when closed that is at least an order of magnitude lower than the pullups. You're just making an unpredictable voltage divider otherwise. – brhans Mar 7 '17 at 22:57
• I should have also asked what your Vcc is! – bigjosh Mar 8 '17 at 14:47
• My VCC is around 4.5v. I've cleaned the contacts with sandpaper and WD40. The resistance of a closed switch has gone from 50-500k to below 1k, and I get a consistent operation. Now i need to make sure my polling method for each button correctly establishes the condition for an accurate reading. I consider the topic solved for now. Thanks again bigjosh for your assistance. – Polyphil Mar 8 '17 at 14:53

So far I've been struggling with this and it doesn't work.

the approach relies on enabling the weak pull-up on the pins to work.

Once set, you can simply read PORTB to sense the pins. One macro would do.

  BTN_DDR &=~(BTN_MUTE | BTN_VOLP | BTN_VOLM); //set the pins as input
BTN_PORT |= (BTN_MUTE | BTN_VOLP | BTN_VOLM); //enable pull-up.

...

tmp = (~BTN_PORTIN) & (BTN_MUTE | BTN_VOLP | BTN_VOLM); //read the pins, active low