How to use memory mapped IO in ATMega32A

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

The datasheet for the ATMega32A shows the IO ports as being memory mapped.  E.g., the address for PORTA is given as 0x1B (0x3B).

 

1.  What's the difference between those two addresses?

 

2.  To access ports via the memory map, do you just use normal pointer operations?  Can someone provide an example of, say, toggling bit 3 in PORTA this way?

 

Thanks.

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

You use the lower address with the I/O instructions (ie in, out etc.) or the higher address with memory instructions (ie lds, sts etc.)

 

If you are using C then it's all done for you and just refer to the registers as PORTA/PINA etc.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

And the 0x20 offset is due to the 32 register addresses, which occupy memory (data) addresses 0x00 to 0x1F.  The I/O instruction addressing begins above the registers.

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

To be clear, ALL special function registers are mapped into memory. But, there is a select group that are arranged to have the lowest possible addresses. THESE can use the in and out assembly operations. However, <avr/io.h> and avr-libc takes care of all of that. Unless you insist in using assembly language, you simply write SFRname = value (write) or value = SFRname (read) and all is taken care of, for you.

 

Jim

 

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

I’m using C. I’d like to access each bit in a port by incrementing a variable, or couple of variables. So, e.g., PORTA ^= (1 << x), would let me toggle each pin in PORTA just by incrementing x. But, how would I do this in both PORTA and PORTB via a variable? Would I start with the address of PORTA (0x1B) in a pointer and just subtract the offset (3) to PORTB (0x18)?

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

Whats wrong with 

 

PORTA ^= (1<<x);
PORTB ^= (1<<x);

If you use a newer MCU, such as Mega48/88/128/328, you can toggle a bit in a port by setting the corresponding bit in the associated PINx register. For example, 

PINA |= (1<<x);
PINB |= (1<<x);

This will toggle the xth bit of PORTA and PORTB. And it will do it faster, because the first set requires reading the port register, XORing the value that was read, then write it back. The simple OR operation is done in a single assembly instruction if only a single bit is involved.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

Last Edited: Mon. Aug 27, 2018 - 05:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

Whats wrong with 

 

PORTA ^= (1<<x);
PORTB ^= (1<<x);


That’ll work, and I’ll probably just go with it. I was just wondering if there was a way to do away with using “PORTA” and “PORTB.”

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

Jim's PINA example raises some serious questions about the GCC compiler.
.
From a semantic point of view it should read the value of the PINA register (i.e. input state of the pin)
Then XOR it before writing the result back to the PINA register (i.e. toggle the output latch of the port)
.
The PINA write is designed to be an = to perform the toggle and not a read modify write operation like |= or &= or ^=
.
The reason for the PINA design is to use a single cycle OUT instruction.
^= is always going to be a long sequence of ASM instructions which can be interrupted.
.
Life becomes very complicated when PINA has separate read and write registers. Likewise UDR0 or SPDR0.
GCC uses SBI and CBI which "works" with something like PORTA
.
David.

Last Edited: Mon. Aug 27, 2018 - 07:34 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
However, and avr-libc takes care of all of that.
lautman wrote:
I’m using C.

Why doesn everyone think that the only toolchain for C language for an AVR8 target is the infinite-value brand?

 

lautman wrote:
1. What's the difference between those two addresses?

0x20.

Or, more completely, have you looked at the information on this in the datasheet?

2. I/O Registers within the address range 0x00 - 0x1F are directly bit-accessible using the SBI and CBI instructions. In these registers, the value of single bits
can be checked by using the SBIS and SBIC instructions.
...

4. When using the I/O specific commands IN and OUT, the I/O addresses 0x00 - 0x3F must be used. When addressing I/O Registers as data space using LD
and ST instructions, 0x20 must be added to these addresses. The ATmega48A/PA/88A/PA/168A/PA/328/P is a complex microcontroller with more
peripheral units than can be supported within the 64 location reserved in Opcode for the IN and OUT instructions. For the Extended I/O space from 0x60 -
0xFF in SRAM, only the ST/STS/STD and LD/LDS/LDD instructions can be used.
 

lautman wrote:
2. To access ports via the memory map, do you just use normal pointer operations? Can someone provide an example of, say, toggling bit 3 in PORTA this way?

Surely, the first part also depends on your toolchain.

For the second part, it is a trick question as you have specified '32A.  Do you really use that model instead of a '324 which is in all probability less expensive along with having other features including a port-bit-toggle feature?

 

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
PINA |= (1<<x);
PINB |= (1<<x);

 

Writing to PINx is a special case which should never be done RMW.  Should:

PINA = (1<<x);
PINB = (1<<x);

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

lautman wrote:
I’m using C. I’d like to access each bit in a port by incrementing a variable, or couple of variables. So, e.g., PORTA ^= (1 << x), would let me toggle each pin in PORTA just by incrementing x.

Now, without the PINx toggle feature, toggling a bit on AVR8 is a relatively painful procedure -- cycles; words; RMW effects.  Thus the addition of the PINx feature.

 

IMO/IME toggling rarely comes up, except in trivial/don't care situations such as a sanity LED.

 

If you really want to control with bits on an I/O port are on/off as a bank, then do the application in a status variable, and then apply that to I/O.  With that approach you can handle various ports; n outputs; no RMW effects or races.

 

 

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

Are you looking for something like:

 

volatile uint8_t *myport;

myport = &PORTB;  // or PORTD, etc...

*myport ^= 1<<mybit;   // complement a bit.

Note that this will prevent any of the optimizations that people have been talking about, since the compiler won't know at compile time whether myport will be within range of OUT or SBI or anything.

You can look at the Arduino code for digitalWrite() and etc.  It works this way, and that's one of the main reasons that it's so much slower than the "fastest possible code"...

https://github.com/arduino/Ardui...

 

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

westfw wrote:
Are you looking for something like:

...

So, how did that work out for you?  (I see no use of the stored pointer...)

	uint8_t mybit;
myport = &PORTB;  // or PORTD, etc...
  8a:	85 e2       	ldi	r24, 0x25	; 37
  8c:	90 e0       	ldi	r25, 0x00	; 0
  8e:	90 93 01 01 	sts	0x0101, r25	; 0x800101 <_edata+0x1>
  92:	80 93 00 01 	sts	0x0100, r24	; 0x800100 <_edata>
mybit = PINB;
  96:	23 b1       	in	r18, 0x03	; 3
*myport ^= 1<<mybit;   // complement a bit.
  98:	45 b1       	in	r20, 0x05	; 5
  9a:	81 e0       	ldi	r24, 0x01	; 1
  9c:	90 e0       	ldi	r25, 0x00	; 0
  9e:	bc 01       	movw	r22, r24
  a0:	02 c0       	rjmp	.+4      	; 0xa6 <main+0x1c>
  a2:	66 0f       	add	r22, r22
  a4:	77 1f       	adc	r23, r23
  a6:	2a 95       	dec	r18
  a8:	e2 f7       	brpl	.-8      	; 0xa2 <main+0x18>
  aa:	9b 01       	movw	r18, r22
  ac:	24 27       	eor	r18, r20
  ae:	25 b9       	out	0x05, r18	; 5

 

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

“Something like” - if we just assign a constant to myport, the compiler will optimize...

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

westfw wrote:
“Something like” - if we just assign a constant to myport, the compiler will optimize...

Interesting that myport is volatile, so the assignment is done, then fuss with mybit, then another access using volatile myport.  Now, how does the compiler know that the volatile myport still points to PORTB (or whatever), and hasn't changed from external forces?

 

But more pertinent to the topic, the code (other than the direct assignment) doesn't use the SRAM-mapped address 0x25 but rather the I/O address 0x05.  For some reason OP wants the SRAM address to be used?

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

theusch wrote:
Now, how does the compiler know that the volatile myport still points to PORTB (or whatever), and hasn't changed from external forces?
myport is not volatile, only the target it points to is.

Stefan Ernst

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

sternst wrote:
myport is not volatile, only the target it points to is.
westfw wrote:
volatile uint8_t *myport;

...which I used in my source.  How would you do the declaration differently to make it volatile?  If I want to use "volatile uint8_t myflag;" in an ISR and mainline, that is the way I would declare it.  It is different for a pointer?  If so, how do you say the pointer is volatile?  In the code given, couldn't myport change in an ISR at any time?

#include<avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t *myport;

int main () {
	uint8_t mybit;
myport = &PORTB;  // or PORTD, etc...
mybit = PINB;
*myport ^= 1<<mybit;   // complement a bit.
myport = &PORTD;  // or PORTD, etc...
mybit = PIND;
*myport ^= 1<<mybit;   // complement a bit.
while (1);
}
ISR (INT0_vect)
{
	myport = &PORTC;
}
0000008a <main>:
#include <avr/interrupt.h>
volatile uint8_t *myport;

int main () {
	uint8_t mybit;
myport = &PORTB;  // or PORTD, etc...
  8a:	85 e2       	ldi	r24, 0x25	; 37
  8c:	90 e0       	ldi	r25, 0x00	; 0
  8e:	90 93 01 01 	sts	0x0101, r25	; 0x800101 <_edata+0x1>
  92:	80 93 00 01 	sts	0x0100, r24	; 0x800100 <_edata>
mybit = PINB;
  96:	23 b1       	in	r18, 0x03	; 3
*myport ^= 1<<mybit;   // complement a bit.
  98:	45 b1       	in	r20, 0x05	; 5
  9a:	81 e0       	ldi	r24, 0x01	; 1
  9c:	90 e0       	ldi	r25, 0x00	; 0
  9e:	bc 01       	movw	r22, r24
  a0:	02 c0       	rjmp	.+4      	; 0xa6 <main+0x1c>
  a2:	66 0f       	add	r22, r22
  a4:	77 1f       	adc	r23, r23
  a6:	2a 95       	dec	r18
  a8:	e2 f7       	brpl	.-8      	; 0xa2 <main+0x18>
  aa:	9b 01       	movw	r18, r22
  ac:	24 27       	eor	r18, r20
  ae:	25 b9       	out	0x05, r18	; 5
myport = &PORTD;  // or PORTD, etc...
  b0:	2b e2       	ldi	r18, 0x2B	; 43
  b2:	30 e0       	ldi	r19, 0x00	; 0
  b4:	30 93 01 01 	sts	0x0101, r19	; 0x800101 <_edata+0x1>
  b8:	20 93 00 01 	sts	0x0100, r18	; 0x800100 <_edata>
mybit = PIND;
  bc:	39 b1       	in	r19, 0x09	; 9
*myport ^= 1<<mybit;   // complement a bit.
  be:	2b b1       	in	r18, 0x0b	; 11
  c0:	01 c0       	rjmp	.+2      	; 0xc4 <main+0x3a>
  c2:	88 0f       	add	r24, r24
  c4:	3a 95       	dec	r19
  c6:	ea f7       	brpl	.-6      	; 0xc2 <main+0x38>
  c8:	82 27       	eor	r24, r18
  ca:	8b b9       	out	0x0b, r24	; 11
  cc:	ff cf       	rjmp	.-2      	; 0xcc <main+0x42>

000000ce <__vector_1>:
while (1);
}
ISR (INT0_vect)
{
  ce:	1f 92       	push	r1
  d0:	0f 92       	push	r0
  d2:	0f b6       	in	r0, 0x3f	; 63
  d4:	0f 92       	push	r0
  d6:	11 24       	eor	r1, r1
  d8:	8f 93       	push	r24
  da:	9f 93       	push	r25
	myport = &PORTC;
  dc:	88 e2       	ldi	r24, 0x28	; 40
  de:	90 e0       	ldi	r25, 0x00	; 0
  e0:	90 93 01 01 	sts	0x0101, r25	; 0x800101 <_edata+0x1>
  e4:	80 93 00 01 	sts	0x0100, r24	; 0x800100 <_edata>
  e8:	9f 91       	pop	r25
  ea:	8f 91       	pop	r24
  ec:	0f 90       	pop	r0
  ee:	0f be       	out	0x3f, r0	; 63
  f0:	0f 90       	pop	r0
  f2:	1f 90       	pop	r1
  f4:	18 95       	reti

000000f6 <_exit>:
  f6:	f8 94       	cli

000000f8 <__stop_program>:
  f8:	ff cf       	rjmp	.-2      	; 0xf8 <__stop_program>

 

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.

Last Edited: Tue. Aug 28, 2018 - 12:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

theusch wrote:
How would you do the declaration differently to make it volatile?

volatile uint8_t *myport;

Non-volatile pointer to volatile data.

uint8_t * volatile myport;

Volatile pointer to non-volatile data.

volatile uint8_t * volatile myport;

Volatile pointer to volatile data.

Stefan Ernst

Last Edited: Tue. Aug 28, 2018 - 01:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

my port is volatile

So is mine, if I leave the cap off the bottle it evaporates...hic...

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly