Atmega 32 button input not working

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

Hello everyone, I am skipping ahead of myself a little from just making LEDs blink various patterns to attempting to get button input. I have a small program that is suppose to light up a LED for however long the button is pressed down and turn off the LED as soon as the button is released. The program does not work. Once I turn on my dev board the LED just turns on and stays on. The button doesn't have any effect on the LED. What is wrong with the code? The LEDs on my dev board turn when they receive a 0. My LEDs are on PORTA and my buttons on PORTB.

Here is my code:

CBI DDRB, 0
SBI DDRA, 0

LOOP:	
SBIC PINB, 0 
RJMP LOOP
SBI PORTA, 0
CBI PORTA, 0
RJMP LOOP	
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As for the led staying on - your code would flash the led at 100,000 to over a million times a second - do you think your eyes are fast enough to see this? Most likely not. You need to have some means of slowing things down so you ,as a human, can see it flash. Something like 10 times a second.
Secondly, we don't know how you've connected the button to pinb.0. If you don't have a pullup or pulldown resistor, the input may float. Normally you would connect the button between 0v and the port pin and enable the pullup by putting a 1 in the PORT register for that pin.

If you run the code in a simulator you can see step by step what your code is doing.this may help.

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

The LED is always on and I can see that clearly. It is suppose to be off until I press the button. There aren't any resistors on my dev board for the buttons. The only pullup resistors are in the microcontroller itself and I don't have any pulldown resistors. The program works fine when I debug it in the simulator.

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

My point is the led is flashing so fast you only see it as being on. Yes, the AVR has pullup resistors, but your code has to turn them on. Also, is the led on when the port pin is high or low?

Ahah! I can see you answered my last question. Your led is on when the port bit is 0. What is the last port operation your code did before testing the button input? Answer: you did a cbi to make it low. Swap your sbi and cbi and the led will go off. You wont see it flash though.

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

I haven't implemented any hardware or software debouncing. I don't think I need it in this simple program. Maybe the LED would flicker a little once I press the button but after a second or two shouldn't it stabilize and turn on? The LEDs on my dev board turn on when the port pin is LOW. The problem is that the program runs as if the button is pressed down the whole time, even when it's not pressed down.

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

I tried swapping CBI and SPI, there is no difference. The LED isn't suppose to flash. Just turn on and turn off.

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

You seem to be missing the critical information i'm giving you. Go back to your simulator and check your code.

What cbi and sbi did you swap? The first pair or the second? Show your code with the fixes i suggested.

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

I switched the second pair.

CBI DDRB, 0
SBI DDRA, 0

LOOP:   
SBIC PINB, 0
RJMP LOOP
CBI PORTA, 0
SBI PORTA, 0
RJMP LOOP    
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You still havent turned on the pullup resistor.

Take things step by step.
Write code to turn on the led
Write code to turn off the led
Write code to turn the led on/off by the button

Google for some code that has an example of a delay.

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

I don't understand why you think I need a delay. The LED is suppose to be turned as long as you are physically pressing the button down. So you hold down 1/2 seconds the LED is on for 1/2 seconds and it is synchronized. You hold the switch down for 5 seconds and the LED is on for those 5 seconds. You release the button and the LED turns off.
I wrote a program that does what I want it to but I feel that the code is "ugly" and there must be a "cleaner" way to do this. Please point out where I could improve the code using better logic and better instructions.


.def tmp=r22
 
.cseg

rjmp reset

reset:
	ldi tmp, low(RAMEND)
	out SPL, tmp
	ldi tmp, high(RAMEND)
	out SPH, tmp

	ldi tmp, 0b00000000
	out DDRB, tmp 
	ldi tmp, 0b11111111
	out PORTB, tmp 
	out DDRA, tmp
	out PORTA, tmp

main:	
	IsButtonPressedDown:
	in R25, PINB
	cpse tmp, R25
	rjmp TURNLEDON 
	rjmp IsButtonPressedDown

	TURNLEDON:
	out PORTA, R25
		
rjmp reset



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

If you don't want it to flash then why do you do a cbi porta,0 followed by a sbi porta,0 ? From what i can see, the code either loops or clears/sets the port bit. From what you tell me you want the led on when you press the button and off when the button is not pressed. The sbic will either skip the jump or execute the jump.

Trace the code in the simulator carefully to see what it really does.

As for the second piece of code, it uses byte operations instead of bit operations. In this instance cbi/sbi/sbic make better sense. The first rule is get the code working then optimise.

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

That was a mistake then, I should have only used cbi to turn the led on. The second program works perfectly I just want someone to critique it as far as logic, syntax, use of alternative instructions goes.

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

When you find yourself using LDI with 0 ask yourself if CLR (which is just EOR with itself) might not be a better choice. Similarly when you load a register with 0xFF could you use SER?

Also why does the code at TRUNLEDON: end with an RJMP to reset? What chance do you actually have to see the "LED on" if you immediately reset?

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

Yes, CLR and SER are better choices. About the immediate reset...I see your point but the program works perfectly on the hardware. Maybe RJMP to IsButtonPressedDown would make more sense.

Edit: I tried RJMP to IsButtonPressedDown and now the LED stays on until key press another button. So you press button 1 LED 1 lights up and stays on until you press some other button. Once you press the other button LED1 turns off and the LED that corresponds to that other button turns off. I want the LED to be on only while the corresponding button is being pressed down.

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

I tried

CLR DDRB
SER PORTB
SER DDRA
SER PORTA

and I can't compile. It says invalid register for all 4 instructions. You can't do it like that can you. You still have to load to a GPR before.

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

Cliff wasn't saying that you should replace the LDI, OUT sequence with a single CLR or SER. He was saying that you could replace the LDI with a CLR or a SER. E.g.

   clr tmp
   out DDRB, tmp

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

Just to say that there's no real benefit one way or the other. An LDI or a CLR both cost 1 word and 1 cycle. It's just that the intention of the programmer is possibly clearer simply by using:

   clr tmp
   out DDRB, tmp
   ser tmp
   out PORTB, tmp
   out DDRA, tmp
   out PORTA, tmp

than the previous code.

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

I know that there are rising edge triggered interrupts and falling edge triggered interrupts. Is there a way to this with ordinary buttons on PORTB? I want one button to do something as soon as i press it down and before I release it. I want another button to not do anything until I press down and release the the button. Is this only possible with interrupts?

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

No, it does not need interrupts to do that, and what is even more important, it especially should not be done with interrupts. At least not with interrupts triggered by buttons, because buttons tend to bounce and so the interrupts can be triggered many times.

You have a microcontroller that can read the button state approximately million times per second. Just read the pin state, and count if it has been pushed or released for long enough time before determining you should consider it as push event or release event. You might want to do that in a timer interrupt happening every 1ms for example.

Search up on debouncing on this forum.

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

I know that you can set interrupts to be either rising edge or falling edge. Can you have rising edge and falling edge reactant buttons on PORTB? I want one button to do something as soon as I press it down and before I release it. I want the other button to something only after I release it. So, I can hold it down for 1 second 1 minute or 1 hour and it won't do anything until I release it. Can this be done without the use of timers and interrupts?

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

Quote:

I know that you can set interrupts to be either rising edge or falling edge. Can you have rising edge and falling edge reactant buttons on PORTB?

Rising/Falling is only for the INTn pins. Other port pins (on modern AVRs) can act as PCINT pins. You cannot configure these for falling/rising. You simply get an interrupt each time an enabled pin changes state. It's then up to you to determine (by reading the PIN register) which of the pins caused the interrupt and which state it has changed into (XOR is useful for this).

but as you've been told but seem to be ignoring it is the worst possible design imaginable to use buttons to generate INTn or PCINT interrupts because of contact bounce.

Read this:

http://www.ganssle.com/debouncin...

The one time you might use INTn/PCINT for buttons is if the device is going to SLEEP in a power down mode. You can use the external interrupt to wake the chip from sleep when a button even occurs. However as soon as the chip awakens disable the interrupt (until you are ready to sleep next time) and once awake do what the many many thread here on Freaks tell you about handling buttons: You run a ticker interrupt that samples the PINs you are interested in on a regular basis (maybe 1/5/10ms?) you log a button as having achieved a new state when it's been seen in the same state for a number of such "ticks".

When you implement such a polling timer interrupt you can then add further functionality like detecting when a button has been held down for a while and so on.

There are many examples of such code (admittedly in C which is much easier to use) on this board. Look in particular for code by "danni" and "theusch". You should be able to take the algorithms they implement and convert to Asm code (actually C compilers are pretty good at doing that for you!).

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

I see, thank you for the reply. I'm a beginner so I'm intentionally not immediately going for the most elegant solution. I prefer to do things "the long way" with the intention of gaining a better understanding of fundamental building blocks. Interrupts are too advanced of a topic for me at the moment. I will certainly refer back to your post once I get to interrupts. Just to make sure one last time, the idea of holding down a button(for a second, minute, hour) that is connected to PORTB and having it turn on a LED only once it is released is not possible? So, if I'm playing around with LEDs and buttons on PORTB and I write a program that flashes a single LED, all I can really do as far as buttons go is:
1) While button 1 is pressed down, flash LED.
2) Press down button 2 and release it.Flash LED for 5 seconds.
3) Press down button 2, don't release it LED will start flashing and flash for 5 seconds even though we haven't released the button.
4)Press down button 2, once it is released flash LED for 5 seconds - not possible, only possible with interrupts.

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

binaryavr wrote:
I'm a beginner so I'm intentionally not immediately going for the most elegant solution.

I would suggest the opposite way.
Use an easy to use and bulletproof solution.
And then try to understand, how it works.

E.g.:
https://www.avrfreaks.net/index.p...

Starting from scratch would be the hardest way without real gain.

Peter

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

Quote:

that is connected to PORTB and having it turn on a LED only once it is released is not possible?

Yes that's possible but if you are avoiding interrupts your only way to do it is "polling". That is regularly reading the input state and watching for changes but this is still not immune to bounce. As you press or release the button then as the contacts get close or begin to separate the switch will appear to make lots of presses or releases. So you really need a method to "retain state". That is remember how it has been "for the last while" and only consider it to now be in a fixed state when it has been seen in a new state "for a while".

So if you are sitting in a flasher delay loop for 100ms between now and the next time you switch LED states (for example) then actually break that up into ten lots of 10ms (say). After each 10ms have a look at the input states. Note any that have changed from last time. Maybe even keep a count of the number of times each input has been seen to be "on" or "off". Only when the counts reach 5 (say) should you consider that it is fully on or fully off.

Sure this complicates your delaying routine and the normal way to handle this is to separate out that button watching code into a separate process using a timer interrupt handler. But if you want to avoid interrupts for now it's all you can do even if it actually makes for a MORE complex solution.

(so now might be the time to start investigating interrupts and timers - in fact you may be approaching your learning in the wrong order. Lurn about driven IO (LEDs on and off) and then delays and THEN flashing of LEDs using timer interrupt BEFORE you go onto what sounds simple but can actually turn out to be quite complex: reading button inputs).

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

I have managed to get leds on porta to turn on whenever a button is pressed on portb. I am trying to do it with individual pins and it is not working.

ser temp
 out ddra, temp
 out portb,temp

 clr temp
 out ddrb,temp


main:	

in temp, pinb0
out pa0, temp

rjmp main

The program works fine when I use pinb instead of pinb0 and porta instead of pa0, it works perfectly without any debouncing, so ignore debouncing. But for some reason in this version all the leds on porta light up and stay lit, button 1 doesn't do anything.

The idea is led0 is off. As long as button1 is depressed led0 is on. As soon as you release button1, led0 turns off.

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

no one?

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

binaryavr wrote:
I have managed to get leds on porta to turn on whenever a button is pressed on portb. I am trying to do it with individual pins and it is not working.

ser temp
 out ddra, temp
 out portb,temp

 clr temp
 out ddrb,temp


main:	

in temp, pinb0
out pa0, temp

rjmp main

The program works fine when I use pinb instead of pinb0 and porta instead of pa0, it works perfectly without any debouncing, so ignore debouncing. But for some reason in this version all the leds on porta light up and stay lit, button 1 doesn't do anything.

The idea is led0 is off. As long as button1 is depressed led0 is on. As soon as you release button1, led0 turns off.


What do you think "in temp, pinb0" does? Or "out pa0, temp"?

Also, what is the input value when the button is not pushed, given the way you initialize the hardware?

Hint: you're making errors in both matters.

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

kk6gm wrote:

What do you think "in temp, pinb0" does? Or "out pa0, temp"?

Also, what is the input value when the button is not pushed, given the way you initialize the hardware?

Hint: you're making errors in both matters.

I think "in temp, pinb0" reads the value of pin 0 on PORTB.
I think "out pa0, temp" outputs the value that was just read from pin 0 on port b to pin 0 on porta.

I activated the pullup resistors, so that is why all the leds are on?

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

Quote:
I think "in temp, pinb0" reads the value of pin 0 on PORTB.
You think wrong. PINB0 is simply defined as 0. IN is only capable of reading the whole register. If you are only interested in one bit of the register, you have to look at the value of the bit in temp (usually done with masking) after you have read it in from PINB. If this two step process bothers you, you can look at the SBIS and SBIC opcodes.

Regards,
Steve A.

The Board helps those that help themselves.

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

binaryavr wrote:

I think "in temp, pinb0" reads the value of pin 0 on PORTB.
I think "out pa0, temp" outputs the value that was just read from pin 0 on port b to pin 0 on port a.

As noted, this is not how the "in" and "out" instructions work. If what you wrote compiled, I can somewhat understand your confusion, but "pinb0" and "pa0" are just defined as numbers, and the "in" and "out" instructions will be using those numbers as addresses, not bit numbers within an address.

Quote:
I activated the pullup resistors, so that is why all the leds are on?

You did indeed, that was my oversight (but in a code review you'd be dinged for lack of comments). The LEDs are on because you're never writing to the port in your loop.

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

Quote:
I think "in temp, pinb0" reads the value of pin 0 on PORTB.

As others have noted this is not correct.

You need to download the document "AVR Instruction Set" from Atmels web site, and have it handy as a reference while coding.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

Quote:

You need to download the document "AVR Instruction Set" from Atmels web site

I'd also recommend reading the "Bit Manipulation 101" thread in Tutorial Forum. It talks about C not Asm but the concepts are exactly the same. In fact the (1<<PINB0) syntax is the same whether you use C or Asm. What differs in Asm is that you may be able to check a single bit using one opcode such as:

  SBIC PINB, PINB0
  RJMP bit_0_in_B_is_set
no_it_isnt:

In C you might do the same with:

if (PINB & (1<<PINB0)) {
  bit_0_in_B_is_set();

(not 100% equivalent!). You can also use (1<<) syntax in Asm to do something almost identical to that C:

 IN   r16, PINB
 ANDI r16, (1 << PINB0)
 BREQ no_it_isnt
 RJMP bit_0_in_B_is_set

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

You can use instructions SBIC and SBIS for testing bits in IO registers like PORTx, PINx.

main:
   sbis  pinb, 0    // skip next line if pinb.0 set
   rjmp  pb0_clear    
   sbi   porta,0    // pb0 set, so set pa0
   rjmp  done   

pb0_clear:   
   cbi   porta,0    // pb0 cleared, so clear pa0  
   
done:

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

I'm not sure if I'm reading the datasheet correctly but is it possible to put 2 or even 3 instructions to be skipped after SBIS?
For example:
sbis pinb, 0
instruction 1 ;skip
instruction 2 ;skip
instruction 3 ;skip

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

No just one in all skip instructions. So you normally skip a jump or call that then achieves more than one opdcode can.

C compilers are good at showing you how to do all this stuff. Write conditional code and you'll find the compiler generating skips and jumps all over the place.

(of course if you are going to write in C you might as well stick there - it'll generally do as good a job as an average Asm programmer).