Writing a logical 1 in assembler

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

I'm having some trouble cleaning the GIFR. So, I was wondering what was the proper way to write a logical one to the flag.

Using VMLab and coding for an ATtiny22, I tried loading the proper bit in to a register and then using OUT to the flag register.

	ldi tmp,$40
	out $3a,tmp

Flag stays set.

I tried using OR on the flag register and the temp register. and that gave me a compile err about improper operands, I tried both addressed for flag register, $3A and ($5A).

Thus I find myself at a loss to write a logical one to clear the flag.

Also I wondering why this bit is still set after the interrupt routine is done. I though it was suppose to self clear.

I have a rjmp in the interrupt routine, could that be causing the flag to remain set?

interrupt:
	sbrc flg,0		; Was interrupt external?
	rjmp soft_int			
	cbi $18,0      ; Yes, turn on LED 1
	rjmp restart
soft_int:
	cbi $18,2      ; No, turn on LED 2
restart:
	ldi tmp,$40
	or $3a,tmp
	sbi $18,1		; Reset pb1 high
	ldi flg,0  		; Reset interrupt flag
	sei    			; Re-enable interrupts
	reti           ; Onward...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
ldi tmp,$40 
out $3a,tmp

This should work, assuming that that is the correct bit in the correct register. I would use:

ldi tmp, 1<<INTF0 
out GIFR, tmp

But if you actually have the interrupt enabled, then there is no need to clear the bit manually, the interrupt mechanism clears it automatically.

How have you got the interrupt set up?

Regards,
Steve A.

The Board helps those that help themselves.

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

The whole thing is listed at the bottom of this thread.

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=91350&start=0&postdays=0&postorder=asc&highlight=

It might be a simulator bug. I can see a STK500 on the horizon...

Last Edited: Tue. Mar 23, 2010 - 02:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
   sei             ; Re-enable interrupts 
   reti           ; Onward... 

RETI does SEI automatically so you don't need to do it. One thing you need to do in the interrupts is to save on entry and restore on exit SREG.

edit just looked at the other thread and I find it a bit of a mess. I have no idea if you are using the correct bits or the correct registers as you are using NUMBERS rather the NAMES. So it would take a LONG time for anyone to check your work as one would need to search the data sheet for the address of the registers or the bit position.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

OK. Here's a screen shot. The GIFR starts out clear, than after pressing any of the three buttons, it stays set ever after. The global interrupt enable self resets though.

I just noticed looking at the screen shot, that it's the wrong bit. Bit 5 is set in the simulators GIFR register. That's suppose to be a reserved bit always set to zero.

Me thinks it'sa simulator glich.

forever:
	sbic $16,3		; Key pressed
	rjmp forever
   ldi flg,1  		; Set flag for soft interrupt
	cbi $18,1		; Set bp1 low--triggering int0
	rjmp forever
interrupt:
   in tmp,SREG
	sbrc flg,0		; Was interrupt external?
	rjmp soft_int			
	cbi $18,0      ; Yes, turn on LED 1
	rjmp restart
soft_int:
	cbi $18,2      ; No, turn on LED 2
restart:
;	ldi tmp,1<<INTF0
;	OUT GIFR,tmp
	sbi $18,1		; Reset pb1 high
	ldi flg,0  		; Reset interrupt flag
	out SREG,tmp
	reti           ; Onward...

Attachment(s): 

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

But do you realise that by pressing any button connected to an interrupt pin that is not debounced you could get tens of interrupts that will be serviced one after the other as soon as you exit the ISR in real life?

And can you trust that simulator? Maybe it's so good that it know the above.

Also

Quote:
sbic $16,3 ; Key pressed
..and similar, which port is being used? What's wrong with using the port name?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

There's was example of debouncing code I wanted to read. I did read in the the data sheet that it would keep track of interrupts during interrupts... I'll look for it tomorrow it's getting late tonight.

Quote:
..and similar, which port is being used? What's wrong with using the port name?

As in:

sbic PINB,3

Well, with your help, I'll be learning good coding style right from the get go. I'm actually learning quite a bit from this one little exercise already. :D

Thanks,

Fred

forever:
	sbic PINB,3		; Key 2 pressed
	rjmp forever
   ldi flg,1  		; Set flag for soft interrupt
	cbi PORTB,1		; Set PORTB,1 low--triggering int0
	rjmp forever
interrupt:
   in tmp,SREG		; Store SREG
	sbrc flg,0		; Was interrupt external?
	rjmp soft_int			
	cbi PORTB,0    ; Yes, turn on LED 1
	rjmp restart
soft_int:
	cbi PORTB,2    ; No, turn on LED 2
restart:
	sbi PORTB,1		; Reset PORTB,1 high
	ldi flg,0  		; Reset interrupt flag
	out SREG,tmp	; Restore SREG
	reti           ; Onward...

That is a lot easier to read. 8)

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

Ok now that it it a little better to read I ran the code in Studio's simulator. It is not very good and GMISK does not appear in the simulator windows and INT0 is not defined in the ASM2 include file but it is defined in the older ASM1 file.

The chip is so old that does not even appear as a product in the Atmel website, so get yourself some newer chip to work with.

It all seems to work well provided that you bring PB3 high as soon as you enter the ISR otherwise it will trigger again and again. The simulator doesn't even see the pull up resistor.

.include "tn22def.inc"

#define INT0 6		;Not defined in include file??

.def	tmp=r24
.def	flg=r16

.org	0x0000
	rjmp	start

.org	INT0addr
	rjmp	interrupt	

start:

	ldi		tmp,low(ramend)
	out		spl,tmp

	ldi		tmp, (1<<PB3 | 1<<PB4 | 1<<PB1)	;Enable pullup resistor, start PB1 high
	out		portb,tmp

	ldi		tmp, (1<<PB2 | 1<<PB1 | 1<<PB0)	;Setup output bits
	out		ddrb, tmp
	
	ldi		tmp, (1<<INT0)	
	out		GIMSK, tmp

	sei

forever: 
   sbic 	PINB,3		; Key 2 pressed 
   rjmp 	forever 
   ldi 		flg,1		; Set flag for soft interrupt 
   cbi 		PORTB,1		; Set PORTB,1 low--triggering int0 
   rjmp 	forever 

interrupt: 
   in 		tmp,SREG	; Store SREG 
   sbrc 	flg,0		; Was interrupt external? 
   rjmp 	soft_int          
   cbi 		PORTB,0		; Yes, turn on LED 1 
   rjmp 	restart 
soft_int: 
   cbi 		PORTB,2		; No, turn on LED 2 
restart: 
   sbi 		PORTB,1		; Reset PORTB,1 high 
   ldi 		flg,0		; Reset interrupt flag 
   out 		SREG,tmp	; Restore SREG 
   reti           		; Onward... 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I'll add a couple of things that might make life easier when code grows bigger.

1. Take advantage of your ability to define things. It's normally easier to remember a short descriptive name on a pin or register than the number of it.
Reading your code, my guess is that you already made a typo with numbers for LED 2.

2. Good thing you save SREG entering your ISR but, unless you are really sure that "tmp" register only will be used during that ISR you might be bitten when using that register somewhere else in your code.
Also if you don't, you have "wasted" one of the high registers (r16-r31). Those registers can use a larger set of instructions than r0-r15. I'll advise you to PUSH "tmp" when entering ISR and POP "tmp" exiting ISR.

PUSH saves a registers value to STACK and POP restore that value by fetching it from STACK. This also goes for any other registers used both in ISR's and main code.
I believe C-compilers automatically use PUSH and POP on all?? registers whenever running an ISR. In assembler you need to do this yourself. No need to PUSH/POP all registers but every register also used in your MAIN need to use PUSH/POP to not lose the value it had in MAIN.
So taking some of your code as example I would add this:

.equ	led_1  		= 0		;LED 1 = PB0
.equ	led_2		= 1		;LED 2 = PB1

interrupt:
   push   tmp      ;save tmp to stack
   in       tmp,SREG   ; Store SREG
   sbrc    flg,0      ; Was interrupt external?
   rjmp    soft_int         
   cbi       PORTB,led_1      ; Yes, turn on LED 1
   rjmp    restart
soft_int:
   cbi       PORTB,led_2      ; No, turn on LED 2
restart:
   sbi       PORTB,led_2      ; Reset PORTB,1 high LED 2 OFF???
   ldi       flg,0      ; Reset interrupt flag
   out       SREG,tmp   ; Restore SREG
   pop   tmp      ;restore tmp 
   reti                 ; Onward... 

Btw, there's no problem to use RJMP/JMP/RCALL/CALL inside an ISR. You can also do a jump to some other code placed anywhere in your program as long as it ends popping pushed registers(in reverse order), restoring SREG and (VERY IMPORTANT) end with RETI.

General advice is to keep ISR's as short as ever possible, just set a flag and do the other stuff in MAIN.
I think you're making great progress...

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

Lennart wrote:
No need to PUSH/POP all registers but every register also used in your MAIN need to use PUSH/POP to not lose the value it had in MAIN.
I think you mean
Quote:
No need to PUSH/POP all registers but every register also used in your ISR (or anything called from it) need to use PUSH/POP to not lose the value it had in MAIN.

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

Quote:
Lennart wrote:
No need to PUSH/POP all registers but every register also used in your MAIN need to use PUSH/POP to not lose the value it had in MAIN.
I think you mean
Quote:
No need to PUSH/POP all registers but every register also used in your ISR (or anything called from it) need to use PUSH/POP to not lose the value it had in MAIN.

Yes, that's what I meant described in a less confusing way.

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

Hey John,

I'm just using the ATtiny22 to learn from because it's so simple and the the model is in VMLab.

Studio4, which I would rather use, has a problem running it's simulator on my linux machine. So, I'm left to get by as I can. I'll be ordering a STK500 soon. :)

Hey Lennart,

LED 2 is on PB2 because the external interrupt key has to be attached to PB1, right? That way int0 can be triggered in two ways, from hardware and from software.

VMLab gave me an err when I tried using a lower register for my interrupt flag, as in:

LDI r15, $00

I guess the load immediate need a high register. Maybe if I bounced it off tmp then into r15...

LDI tmp, $00
MOV r15, tmp

Seems to work. I see now all the immediate instructions need a high register.

VMLab also gave me compile errs when I tried to PUSH SREG, it seems it wants a register value for an operand.

OUT TMP, SREG
PUSH TMP
...
POP TMP
IN SREG, TMP

Seemed to work. Hmm, that's actually what you said, push and pop tmp. OK, I got it now.

Thanks,

Fred

Last Edited: Tue. Mar 23, 2010 - 02:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Fred

Quote:
That seems awkward.

That's the limitations you have to live with.
For low registers you can use
CLR which clear all bits(0x00) and
SER which set all bits(0xFF).
In larger projects you might benefit from using one of the low registers dedicated to load zero into stuff.(Avoid r0 and r1 since they are used to get results from multiplication).
Like this

.def	zero		=r2  ;zero reg

Then you can use it eg like this

	sts	tick_timer,zero	;reset RTC TIMER

Regarding SREG it follow the same rules as I earlier described. You must use a register to get something IN or OUT a I/O register.

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

Quote:

I guess the load immediate need a high register.

As the opcode manual tells you, LDI can only be used on R16 to R31

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

Lennart wrote:
Hi Fred

For low registers you can use
CLR which clear all bits(0x00) and
SER which set all bits(0xFF).

For my my purposes with this that would work well, just use the whole register as the flag.
Quote:

In larger projects you might benefit from using one of the low registers dedicated to load zero into stuff.(Avoid r0 and r1 since they are used to get results from multiplication).
Like this

.def	zero		=r2  ;zero reg

Then you can use it eg like this

	sts	tick_timer,zero	;reset RTC TIMER

Cool.
Quote:

Regarding SREG it follow the same rules as I earlier described. You must use a register to get something IN or OUT a I/O register.

Got it. :)

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

Hey John,

In this example, if the int0 was set to trigger on the falling edge of a PB1 state change rather than triggering on a PB1 low state, would that effectively debounce the software interrupt employed here?

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

If you want to know about debouncing then read the bible. Hopefully it'll be obvious that most solutions involving polling signal states on a timer interrupt - only trust the state when it's remained in the same state for a number of time periods. Using a direct external interrupt for a source that's subject to bounce (anything with mechanical contacts) is a very bad idea. The one use of the ext-int might be simply to wake a sleeping CPU but once it's awake then disable the ext-int and let the timer polling debouncer take over.

Good examples of AVR debounce code have been posted here by both users Lee Theusch (id=theusch) and Peter Danneger (id=danni)

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

Yes, I'm reading Peter's example at the moment, and trying to figure out how to translate into assembler. I'm getting there...

Good point about the mechanical switch. That is still connected at all times in this example, even if the falling edge triggering would work for the soft interrupt.

Come to think of it, the edge triggering might make the device even more sensitive to noise from the switch.

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

Please read the Ganssle bible on button bounce - I'd be surprised if you have any questions left after reading it.

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

Quote:
That way int0 can be triggered in two ways, from hardware and from software.
Not quite, unless you want to cook PB1 by shorting it to ground with a switch.

As far as debouncing I have never used software debounce but just resistor and cap.

Of course in this situation you can just look at the switch in your loop rather than using an INT.

Try to use another chip if available in the simulator. You cannot even get the data sheet from Atmel for that chip.
A Tiny13 is just as simple but current.

I always use r15 exclusively for the SREG save, I also use r24 for temp and r25 for temp1.
If you use a chip that has hardware multiply and you want to use it then try not to use r0 and r1 in your code as it gets used by that bit of the chip.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Oh, now you tell me you never used software debounce.

I got Peter's example running in assembler.

I'll read the bible reference next. The logic of the the ISR in this example still eludes me. I suppose it's easier to understand in C.

The data sheet for the T22 is available on the net. I just googled for it.

VMLab lacks a model for the ATtiny13, although it has them for the 11, 12 and 15.

Anyway here's what I did with Peter's example.

Attachment(s): 

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

Here's one example of assembler debouncing.
The first loop is to make sure that a real push and not a spike from mains have triggered input pin.

Second part is sort of opposite. Code loops until 8 succesive pin reads are high. You'll probably manage with only this part.
If you want to, of course hardware timers can be involved in the delay loop so that you can do other stuff meanwhile.

Personally I like to wait in a tight loop whenever possible, in this case since the outcome of the first loop return to main if triggered by a spike from mains.

;******************

toggle_hatch:
	ldi	ctr,40		;set up ctr	
	ldi	temp,0		;set up delay
more_toggle: 
	ldi	data,0x01		;reset debounce
toggle_del:	
	wdr				;reset WATCHDOG
	dec	temp			;dec ctr
	brne	toggle_del		;until zero
	sec				;set carry
	sbic	pinb,hatch		;if pin hi
	clc				;clear carry
	rol	data			;shift in carry
	brcc	toggle_del		;while carry clear
	dec	ctr			;dec ctr
	brne	more_toggle		;until zero
	cpi	data,0xff	;if 8 passes lo
	breq	no_spike		;do debounce
	ret				;else return MAIN

no_spike:
	ldi	data,0x01		;reset debounce
real_toggle:	
	wdr				;reset WATCHDOG
	dec	temp			;dec ctr
	brne	real_toggle		;until zero
	sec				;set carry
	sbic	pinb,hatch		;if pin hi
	clc				;clear carry
	rol	data			;shift in carry
	brcc	real_toggle		;while carry clear
	cpi	data,0x00		else ;while not all zero
	brne	no_spike		;do another round
	lds	data,air_flag	;else load AIR FLAG
	ldi	temp,0x81		;load AIR ON
	sbrc	data,0		;if AIR ON
	ldi	temp,0xc0		;load AIR OFF
	sts	air_flag,temp	;save to AIR FLAG
	ret				;return MAIN
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That first loop is interesting. It cycles through 40 carry overflows of the data byte, then checks to see if there are eight consecutive low reads.

I presume the watchdog resets the program flow if pinb.hatch stays high.

What triggers a branch to toggle.hatch to begin with?

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

Quote:
What triggers a branch to toggle.hatch to begin with?

A push on a momentary button short it to GND.
This signal is connected to PINB,HATCH
The button is connected through a cable that can be 10-15 meters.That's why part one of the code came about.
My prototype kept getting false triggered by a fridge.
A human push WILL be longer than a spike on mains, no matter how fast you are. So, it's a digital spike filter.
Quote:
I presume the watchdog resets the program flow if pinb.hatch stays high.
When I looked at the code I don't really like how I use watchdog.
In this application it's set to fire after 1 sec.
My worry was that a user may push the button for more than one sec, and I wanted to avoid firing watchdog.

I think the proper way to do this would be to change watchdog prescaler at top of the code to expire, say after 8 sec.
Then avoid doing WDR all the time.
It's actually also used to make the delay loop longer but a NOP is good enough.
Then reset prescaler to 1 sec when exiting routine.
Will give an escape route should input fail permanently shorted to GND.

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

So the start up load of the fridge was causing pinb.hatch to go low aye?

Hmm, I been studying and building vacuum tube guitar amps lately, and the EM noise that gets picked up by guitar cables is incredible... I just had a thought about that, maybe I should put an RF choke on the input.

Also I worked out the problem I was having with branching instructions in translating Peter's example. It was human err of course. I was trying to do something like:

sbic key_mask,(1<<KEY1)

; Instead of

sbic key_mask,KEY1

Now it works like a champ.

I guess the code is structured that way in the ISR to keep track of the state of eight keys efficiently in parallel. It's rather ingenious, although it seems overly elaborate for just one key. Nifty snippets. :D

Thanks,

Fred

Giving that watchdog thing a little more thought. I would imagine that after filtering, debouncing, and validating a key press, you change states, then call a delay routine for 8 secs or whatever that prevents another change of state during that interval, prevents the whole filter/debounce/validation process from even starting until the 'change of state' delay is over.

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

Quote:
Oh, now you tell me you never used software debounce.
Why should I have told you earlier? :) I did mention debounce just not software debounce because I don't personally like it. I try to keep the rubbish from entering the chip in the first place.

All the software debouncing in the world will not stop a static discharge from blowing up the chip. A resistor and cap will make things a lot better healthwise.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Well, it's always good to know more than one way to do something.

So, software vs hardware debounce. I find these little buggers to be a very interesting mix of electronics and code. 8)

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

clawson wrote:
Please read the Ganssle bible on button bounce - I'd be surprised if you have any questions left after reading it.

Good stuff.

How commonly do people buffer the key inputs with RC networks and Schmitt triggers?

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

Quote:
How commonly do people buffer the key inputs with RC networks
100% of the time here. :wink:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly