Use of PORTB_OUTSET and PORTB_OUTTGL in assembler

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

Hello,
I'm older (70+) and interested in trying assembler.
I work with Atmel Studio 7 and an ATtiny414.
I have been looking for information about the use (code example) of PORTB_OUTSET and PORTB_OUTTGL in assembler for days.
I would be grateful for hints and a small example.
Regards Klaus

Last Edited: Wed. Apr 8, 2020 - 02:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

What exactly is that you want? 

 

They are registers and they work by writing a value in them. The simplest way to see examples for them would be to write some C code for them in Atmel Studio, compile and check the .lss file.

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

I tried this for Tiny414 but got errors about "unrecognized core version" so I think my AS7 needs the assembler updating. So I picked Atxmega32E5 which is an Xmega a bit like tiny414 (AVR-0/AVR-1 basically are Xmega) and this builds OK...

.def temp = r16

.cseg
.org 0

	rjmp RESET
RESET:
	ldi temp, low(RAMEND)
	out CPU_SPL, temp
	ldi temp, high(RAMEND)
	out CPU_SPH, temp

	ldi temp, 0xFF
	sts PORTD_DIR, temp // all D pins to output
	ldi temp, 0xAA
	sts PORTD_OUTSET, temp // set alternate pins high
	ldi temp, 0xFF
loop:
	sts PORTD_OUTTGL, temp // toggle all 8 pins (AA/55/AA/55 ...)
	rjmp loop

 

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

Thanks a lot,

ldi temp, 0xAA
sts PORTD_OUTSET, temp // set up alternative pins

 

What is the difference or the advantage over

ldi temp, 0xAA
sts PORT_OUT, temp

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

what does the datasheet say?

 

That's a question about the hardware operation - nothing to do with the assembler.

 

Please see Tip #1 (in my signature, below) for how to properly post source code - including assembler code.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


maximat wrote:
What is the difference or the advantage over

As I understand it having limited experience with these new chips, in your case since your writing all 8 bits it does not matter writing to port or to portout.

However, using portout lets you write to one or more pins without touching the others in the same port.

 

Jim

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

If you want something you've never had...

...you must be willing to do something you've never done!

Lets go Brandon!

Last Edited: Wed. Apr 8, 2020 - 01:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you very much, I'll give it a try and make my experience

Klaus

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

Consider a (really) traditional AVR where the ports only have 3 registers: DDRB, PORTB, PINB. Say you want to set bits 3 and 5 in PORTB then you would write something like

PORTB |= (1 << 3) | (1 << 5);

which in Asm terms would translate to something like:

IN r16, PORTB
LDI r17, 0x28
OR r16, r17
OUT PORTB, r16

To set bit 3/5 it was necessary to read the current register, OR in the 3/5 bits (0x28 mask) then OUT this update. In doing so perhaps an interrupt (that also makes use of PORTB) could happen during this sequence. So having a specific "SET" register would allow for:

LDI r16, 0x28
OUT PORTB_SET, r16

it's more compact, faster and bit changing sequence cannot be interrupted. So Xmega fmaily AVRs add these extra OUTSET, OUTCLR registers. A similar argument applies for toggling too where the OR becomes an EOR. However the traditional AVRs did make a step in that direction when some of the later ones add a feature where a write to PIN (not PORT) would act as a PORT_TGL. In fact that SET/CLR/TGL you see in Xmega family (Xmega/AVR-0/AVR-1) may have come out of that first addition to traditional AVR. 

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

I suspected something like that. I think you explained that well. Then it is probably good to get used to PORTx_SET and PORTx_CLEAR

Thank you

Klaus

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


Well yes and no. It's nice to have "bells and whistles" I guess. But the modern (Xmega) AVRs risk hiding the trees in the wood.. A traditional AVR has:

 

 

Just three registers to achieve everything the port is capable of. An Xmega has:

 

 

At the end of the day:

DDRD  = PORTD_DIR
PORTD = PORTD_OUT
PIND  = PORTD_IN

Everything else is just gravy !

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

You also have the 'traditional' registers available via the VPORT registers. There really is no downside to using VPORT until you want to set/clr more than one pin and you want them all to change at the same time. Probably not needed very often.

 

Here is c++ code and its generated asm code. The c++ code is just to show what is going on at the higher level, and the resulting asm is what you are after- and simply gets back to sbi/cbi like the 'old' avr's. You have a pinctrl register for each pin which has to be accessed via lds/sts because of its address (like most other  peripheral registers), but the rest can use VPORT which is in the cbi/sbi address range.

 

 

int main(){
    Port<PB1> led( OUTPUT, LOWISON ); //active low
    led.toggle();

 

//Port<PB1> led( OUTPUT, LOWISON );
  //clearPinctrl();
  a6:    10 92 31 04     sts    0x0431, r1      //pin1ctrl = 0 (clears any previous config)
  //output();
  aa:    21 9a           sbi    0x04, 1         //VPORTB.DIR bit 1 = 1 (1 = output)
  //invertOn();
  ac:    80 91 31 04     lds    r24, 0x0431     //load pin1ctrl
  b0:    80 68           ori    r24, 0x80       //invert = 1 (we can now use sbi/cbi normally- on() is low, off() is high)
  b2:    80 93 31 04     sts    0x0431, r24     //store
  //off();
  b6:    29 98           cbi    0x05, 1         //VPORTB.OUT bit 1 = 0 (off, which is high in this case)

 

//led.toggle();
  b8:    31 9a           sbi    0x06, 1         //VPORTB.IN bit 1 = 1 (write to IN = toggle)

 

 

//and using OUTTGL-

ldi r24, 0x02 ;bit 1, or 1<<1

sts 0x427, r24 ;PORTB.OUTTGL = 0x02

 

 

You can also let the C compiler teach you how to use asm by looking at the code it generates-

https://godbolt.org/z/Rr-3WA

There is limited support for avr0/1 in this online compiler, but it has the mega4809 include which is close enough for testing (register numbers may be different, but the port addresses will be the same).  I'm assuming you are familiar with C. The asm output shows decimal numbers in this case for the lds/sts address, so watch for that. You can also do this with your own compiler, but the online compiler you just type and watch the output as you go.

 

Interesting extra- I was looking at the asm output of my c++ pin init contraption, and noticed an improvement could be made. I can now 'accumulate' all init options provided in any order and can then take care of the actual register writing when there are no more arguments. In this case, the pinctrl register is written once with its 'final' value. The advantage is the smaller size, but the bigger advantage is the same- pin init arguments have no specific order but the init code will be written in a specific order.

 

000000a6 <main>:
  a6:    80 e8           ldi    r24, 0x80  //invert on
  a8:    80 93 31 04     sts    0x0431, r24
  ac:    21 9a           sbi    0x04, 1 //PB1 dir = output
  ae:    29 98           cbi    0x05, 1 //PB1 off

  b0:    31 9a           sbi    0x06, 1 //PB1 toggle
 

Last Edited: Thu. Apr 9, 2020 - 03:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for the many supports. I get on well with VPORT and SBI and CBI.
The BLD command does not work with VPORTB_OUT. Or is there another trick?

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

It would be nice if this were possible:

 

ldi r16, 0x01
bst r16, 0
bld VPORTB_OUT, 0

 

 

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

Use ORI instead of bst. In your example above it doesnt's make sense as you are still working on bit 0 that is already 1.

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

maximat wrote:

It would be nice if this were possible:

 

ldi r16, 0x01
bst r16, 0
bld VPORTB_OUT, 0

If it worked it would be equivalent to (which does work):

sbi VPORTB_OUT, 0

 

Letting the smoke out since 1978

 

 

 

 

Last Edited: Fri. Apr 24, 2020 - 10:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wanted to output 8 bits in a loop

 

ldi temp, 0xaa
ldi cnt, 0x08
loop:
  bst temp, 0    ; temp 0 in T-flag
  bld VPORTB_0   ; T-Flag in out
  lsr temp
  dec cnt
brne loop

I'm doing it this way now

 

ldi temp, 0xaa
ldi cnt, 0x08
loop:
cbi VPORTB_OUT, 0
lsr temp           ; bit 0 in Carry
brcc next          ; skip if Carry clear
sbi VPORTB_OUT, 0
next:
dec cnt
brne loop 
...

 

it is one line more

Thanks a lot