how to use general purpose register in c

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

i am using atemga165p and i want to use general purpose register in c language
i am using avr gcc compiler
plz guide me
thanks

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

It's not common to do so in a high level language. There are compiler-specific ways to pass parameters and get results from assembly language functions, if that's what you're trying to do.

Any scheme you come up for going from C to asm will work now and not in some future version, nor will it port to other brands of C.

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

Quote:
i want to use general purpose register in c language
i am using avr gcc compiler

I use:

typedef struct 
{ 
  unsigned char bit0:1; 
  unsigned char bit1:1; 
  unsigned char bit2:1; 
  unsigned char bit3:1; 
  unsigned char bit4:1; 
  unsigned char bit5:1; 
  unsigned char bit6:1; 
  unsigned char bit7:1; 
}io_reg; 

#define BIT_buffer_status		((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit0 
#define BIT_recv_error		((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit1 
#define BIT_active			((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit2 
#define BIT_use_sample1		((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit3 

#define BIT_byte_count		((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit4 
#define BIT_receive_132		((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit5 
#define BIT_wait_uart_low	((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit6 
#define BIT_RS232_calibrated	((volatile io_reg*)_SFR_MEM_ADDR(TWAR))->bit7 

but that's because I'm using a mega16 that does not have GPIORn registers. In your case simply replace TWAR in this with GPIOR0. That gives you 8 "bit variables" which will generate SBI/CBI access code:

BIT_recv_error=1;
BIT_active=0;
if (BIT_wait_uart_low = 1) {...

For GPIOR1 and GPIOR2, though they are in IN/OUT range they are sadly out of SBI/CBI range so there's no real value in trying to split them into bit variables and you might as well just use:

GPIOR1 = 0xNN;
if (GPIOR2 == 0xMM) {
and so on

and just treat them as an 8 bit var (but it'll use IN/OUT rather than LDS/STS or X/Y/Z indexing so will be a little faster than SRAM)

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

but datasheet says that it has 32*8 general purpose register so can i implement that scheme on R0,R1 and so on

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

We know that adding a variable from a register is faster than adding a variable from ram, because we dont need the instruction to load the variable from ram in the first place. So if an expert assembler programmer was implementing an algorithm that had to run as fast as possible. he would keep as many of the variables as possible in the avr registers. When all registers are used, he must use variable in ram. If the programmer uses c, the compiler keeps track of which variable is in which register. which allows the programmer to concentrate on algorithm rather tham implementation.

Imagecraft compiler user

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

Quote:

but datasheet says that it has 32*8 general purpose register so can i implement that scheme on R0,R1 and so on

But those registers simply get used by the C compiler "behind your back" - you don't have to do anything special to make it do this. If you write:

int main(void) {
 uint8_t a, b, c;
 a = 37;
 b = 51;
 c = 97;
 DDRA = 0xFF;
 DDRB = 0xFF;
 DDRC = 0xFF;
 while(1) {
  PORTA = a;
  PORTB = b;
  PORTC = c;
  a = b + c;
  b = c - a;
  c = b + a;
}

the C compiler genrates:

00000082 
: int main(void) { uint8_t a, b, c; a = 37; b = 51; c = 97; DDRA = 0xFF; 82: 8f ef ldi r24, 0xFF ; 255 84: 8a bb out 0x1a, r24 ; 26 DDRB = 0xFF; 86: 87 bb out 0x17, r24 ; 23 DDRC = 0xFF; 88: 84 bb out 0x14, r24 ; 20 8a: 85 e2 ldi r24, 0x25 ; 37 8c: 93 e3 ldi r25, 0x33 ; 51 while(1) { PORTA = a; PORTB = b; PORTC = c; 8e: 21 e6 ldi r18, 0x61 ; 97 c = 97; DDRA = 0xFF; DDRB = 0xFF; DDRC = 0xFF; while(1) { PORTA = a; 90: 8b bb out 0x1b, r24 ; 27 PORTB = b; 92: 98 bb out 0x18, r25 ; 24 PORTC = c; 94: 25 bb out 0x15, r18 ; 21 a = b + c; 96: 89 2f mov r24, r25 98: 8f 59 subi r24, 0x9F ; 159 b = c - a; 9a: 92 2f mov r25, r18 9c: 98 1b sub r25, r24 9e: f8 cf rjmp .-16 ; 0x90

So the C compiler has chosen to hold 'a' in R24, 'b' in R25 and 'c' in R18.

Now some C compilers will let you try and persuade them a specific register to be used to hold a specific variable but that can compromise the compiler's choices about where it would prefer to put variables and there can be problems if you call precompiled library routines that aren't aware of the register reservation.

Bottom line: in C think "variables" not registers.

(except for those reserved AVR registes GPIORn I mentioned above which ARE there for your own use and the compiler makes no assumptions about them)

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

y2k_eng wrote:
i am using atemga165p and i want to use general purpose register in c language
i am using avr gcc compiler
plz guide me
thanks

#if defined( __ASSEMBLER__ )
    #define REGcounter	r2
    #define ALIcounter	r3
#else
    register unsigned char REGcounter asm("r2");
    register unsigned char ALIcounter asm("r3");
#endif

and in Makefile:

REGS  = -ffixed-r2  -ffixed-r3

C_FLAGS         := $(CFLAGS) $(REGS) $(OPTIMIZE)

$(C_OBJS) : $(OBJDIR)/%.o : %.c Makefile
	$(CC) -c $(C_FLAGS) $< -o $@

In that case you must use -ffixed-reg option for compile program.

AVR-GCC should not use regs from r2 through r7 if they mention in -ffixed-reg option. Other regs are under question (see .lss file carefully).

Ilya

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

Quote:

AVR-GCC should not use regs from r2 through r7 if they mention in -ffixed-reg option. Other regs are under question (see .lss file carefully).

But the avr-libc code was compiled without this knowledge so there's a strong chance any lib call will trash the reserved registers. (FP is likely to be particuarly bad for register usage)

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

clawson wrote:
Quote:
AVR-GCC should not use regs from r2 through r7 if they mention in -ffixed-reg option. Other regs are under question (see .lss file carefully).
But the avr-libc code was compiled without this knowledge so there's a strong chance any lib call will trash the reserved registers.

What do you mean by this? avr-libc will ignore and override these flags? I'm also looking to reserve 4 registers for a short bit so that I can use them during a time-critical point of my algorithm (measuring 4 ADC channels pseudo-simultaneously), and would like to avoid implementing it entirely in Assembler...

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

Quote:
measuring 4 ADC channels pseudo-simultaneously

If you mean the internal ADC channels, then using registers for this will do nothing for you.
Quote:
What do you mean by this?

When avr-libc was compiled, these flags were not set. It will have no knowledge of you program having them set.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:
What do you mean by this? avr-libc will ignore and override these flags?
The avr-libc has no knowledge of the flags since it was already compiled/assembled. So whatever registers it uses are hard coded already. So when selecting registers to reserve you need to make sure that avr-libc won't use and corrupt there values.

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

Koshchi wrote:
tlucas wrote:
measuring 4 ADC channels pseudo-simultaneously
If you mean the internal ADC channels, then using registers for this will do nothing for you.
I mean the external channels, selected by ADMUX, though we may be referencing the same thing. I'll have 1.5 ADC clock cycles, running at 1 MHz, to move ADCH (ADCL will be garbage) in between Free Running conversions, which translates to 24 XTAL clock cycles (16 MHz). I'm not sure whether or not this is enough time, so I thought I would implement this part of the program in Assembler, which has well-documented timing, just to be sure.

Rereading the ADC documentation, however, doesn't support (or disprove) my understanding that I must read ADCH before the next conversion takes place. Do I only need to read it before the next conversion is finished? This would give me 13/1MHz -- 208 system tics -- plenty of time. :P

atomicdog wrote:
The avr-libc has no knowledge of the flags since it was already compiled/assembled. So whatever registers it uses are hard coded already. So when selecting registers to reserve you need to make sure that avr-libc won't use and corrupt there values.
Thanks for the golden link. :D

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

Koshchi wrote:
When avr-libc was compiled, these flags were not set.

And there is worse that some functions in avr-libc written at assembler with hard coded registers. And you need rewrite that function if you want use register for global variables.

Or do not use that functions. As I do :-)

Ilya

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

And there is much more worse that avr-gcc may SILENTLY reuse registers that mention in -ffixed-reg option. (I think that it is bug, but I do not know where write about this. Sorry my english)

Ilya

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

The imagecraft compiler has a checkbox in the IDE that says 'dont use r20-r23' AND the IDE links with separate libraries that are compiled with this option on. You specify which vars go in which of those regs. LT says CV has a similar optimizing capability.

Imagecraft compiler user

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

Quote:

LT says CV has a similar optimizing capability.

Yup you can reserve a number of registers and then any library call is rebuilt at compile time to avoid reserved register usage.

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

Quote:

I'll have 1.5 ADC clock cycles, running at 1 MHz, to move ADCH (ADCL will be garbage) in between Free Running conversions, which translates to 24 XTAL clock cycles (16 MHz). I'm not sure whether or not this is enough time, so I thought I would implement this part of the program in Assembler, which has well-documented timing, just to be sure.

My outfit has done many, many AVR production apps, and I've never resorted to these kind of shenanigans. It depends on the app, of course, but for me "pseudo-simultaneous" is like "in the same period of a few milliseconds". So standard round-robin conversions with ISRs and single-conversions triggered by the completion of the previous and change of ADMUX. Not free-running.

This allows me plenty of time for the "real" app. Let's look at your "solution". Even if you don't miss a cycle at a 200kHz ADC clock it takes 250us to do four conversions, and you have to have interrupts turned off during that time. Perhaps some apps would tolerate that; it would be disruptive in many with missed events.

Given that, I don't see how C would be any less deterministic in a tight loop polling ADSC (I guess ADIF for free-running) bit.

Now, you are sure about the new ADMUX setting taking effect for that very next conversion? Bzzzt--

Quote:
The MUXn and REFS1:0 bits in the ADMUX Register are single buffered through a temporary register to which the CPU has random access. This ensures that the channels and reference selection only takes place at a safe point during the conversion. The channel and reference selection is continuously updated until a conversion is started. Once the conversion starts, the channel and reference selection is locked to ensure a sufficient sampling time for the ADC. ...

If really a requirement for an AVR app, I'd put a sample/hold mux in front of the AVR.

To get closer, you could consider an Xmega with DMA sampling and interleaved conversions. Especially with the bigger models with twin-ADCs you are coming damn close--closer than any method with a classic AVR8.

Lee

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

Quote:
I mean the external channels, selected by ADMUX

If you are selecting the channel through ADMUX, then it is obviously the internal ADC, not an external ADC.
Quote:
I'll have 1.5 ADC clock cycles, running at 1 MHz, to move ADCH (ADCL will be garbage) in between Free Running conversions

Why do you say that? The results of the conversion will be valid until the next conversion completes, so you have a full 13 ADC clocks.

Regards,
Steve A.

The Board helps those that help themselves.

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

theusch wrote:
My outfit has done many, many AVR production apps, and I've never resorted to these kind of shenanigans. It depends on the app, of course, but for me "pseudo-simultaneous" is like "in the same period of a few milliseconds". So standard round-robin conversions with ISRs and single-conversions triggered by the completion of the previous and change of ADMUX. Not free-running.

This allows me plenty of time for the "real" app. Let's look at your "solution". Even if you don't miss a cycle at a 200kHz ADC clock it takes 250us to do four conversions, and you have to have interrupts turned off during that time. Perhaps some apps would tolerate that; it would be disruptive in many with missed events.

You're right about my 'solution' being hay-wire, for several reasons, but I do need some quick conversions.
Quote:
Given that, I don't see how C would be any less deterministic in a tight loop polling ADSC (I guess ADIF for free-running) bit.
I'm moreso looking for reproduceable timing so that I can account for it in the measurements. The 'sample and hold' timing in free running mode is well documented, and very quick.
Quote:
Now, you are sure about the new ADMUX setting taking effect for that very next conversion? Bzzzt--
Quote:
The MUXn and REFS1:0 bits in the ADMUX Register are single buffered through a temporary register to which the CPU has random access. This ensures that the channels and reference selection only takes place at a safe point during the conversion. The channel and reference selection is continuously updated until a conversion is started. Once the conversion starts, the channel and reference selection is locked to ensure a sufficient sampling time for the ADC. ...
Yep, the "safe point during the conversion" is immediately after the 13'th ADC clock cycle ("MUX and REFS Update" on Fig. 104 in ATmega32(L) manual).
Quote:

To get closer, you could consider an Xmega with DMA sampling and interleaved conversions. Especially with the bigger models with twin-ADCs you are coming damn close--closer than any method with a classic AVR8.
That's a really good idea, one I'll surely give some thought to while writing the report for this project. I think I may actually recommend 4 separate external ADC's with their own buffers and timers to get truely simultaneous readings.

Koshchi wrote:
Quote:
I'll have 1.5 ADC clock cycles, running at 1 MHz, to move ADCH (ADCL will be garbage) in between Free Running conversions
Why do you say that? The results of the conversion will be valid until the next conversion completes, so you have a full 13 ADC clocks.
I'm not sure why I assumed I would be shut out of ADCH/L during a conversion. This reveals much more time, and eliminates my apprehension. :P

Hope thread hijacking isn't looked down upon in these parts. :wink:

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

Quote:
Yep, the "safe point during the conversion" is immediately after the 13'th ADC clock cycle ("MUX and REFS Update" on Fig. 104 in ATmega32(L) manual).

No, that is when the MUX and REFS take effect, not when it is "safe" for you to update them. You must set those bits before that time for it to affect the next conversion.

From the datasheet:

Quote:
ADMUX can be safely updated in the following ways:
1. When ADATE or ADEN is cleared.
2. During conversion, minimum one ADC clock cycle after the trigger event.
3. After a conversion, before the Interrupt Flag used as trigger source is cleared.

The first one is when auto-trigger or the entire ADC is off, and the third one of these does not apply to free-running since the interrupt is also the trigger for the next conversion.

Quote:
Hope thread hijacking isn't looked down upon in these parts.

Hope has failed you there.

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
No, that is when the MUX and REFS take effect, not when it is "safe" for you to update them. You must set those bits before that time for it to affect the next conversion.
Yep, I understand that.