How to blink a LED?

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

Hello,

Since I am quite experienced in ATmega, I am completely new to AVR32. I use AT32UC3B0256 microcontroller (at speed of 60MHz) -- more precisely, I have already ordered mkII programmer, evaluation board, a few microcontrollers and boards -- I will have those items in a few days.

I would like to write simple application in C using AVR32 Studio. This application should just blink a LED. I found (after much time of searching -- personally, I find available documentation not enough, not comprehensive; there is no simple examples, no tutorials) a piece of code shown below:

#include 
#include "gpio.h"
int main( void )
{ int i;
  while (1) {
    gpio_set_gpio_pin(AVR32_PIN_PA03);
    for (i = 0; i < 100; i++);
    gpio_clr_gpio_pin(AVR32_PIN_PA03);
    for (i = 0; i < 100; i++);
  }
  return 0;
}

When I run the application in Debug mode (it throws some exception -- but it is not important now), I saw
that assembler code of gpio_set_gpio_pin(AVR32_PIN_PA03) is like this:

0x800000fc :     pushm  r7,lr
0x80000100 :   mov    r7,sp
0x80000102 :   sub    sp,8
0x80000104 :   st.w   r7[-8],r12
  volatile avr32_gpio_port_t *gpio_port = &GPIO.port[pin >> 5];
0x80000108 :  ld.w   r8,r7[-8]
0x8000010c :  lsr    r8,0x5
0x8000010e :  lsl    r8,0x8
0x80000110 :  sub    r8,61440
0x80000114 :  st.w   r7[-4],r8
  gpio_port->ovrs  = 1 << (pin & 0x1F); // Value to be driven on the I/O line: 1.
0x80000118 :  ld.w   r8,r7[-8]
0x8000011c :  bfextu r9,r8,0x0,0x5
0x80000120 :  mov    r8,1
0x80000122 :  lsl    r8,r8,r9
0x80000126 :  mov    r9,r8
0x80000128 :  ld.w   r8,r7[-4]
0x8000012c :  st.w   r8[84],r9
  gpio_port->oders = 1 << (pin & 0x1F); // The GPIO output driver is enabled for that pin.
0x80000130 :  ld.w   r8,r7[-8]
0x80000134 :  bfextu r9,r8,0x0,0x5
0x80000138 :  mov    r8,1
0x8000013a :  lsl    r8,r8,r9
0x8000013e :  mov    r9,r8
0x80000140 :  ld.w   r8,r7[-4]
0x80000144 :  st.w   r8[68],r9
  gpio_port->gpers = 1 << (pin & 0x1F); // The GPIO module controls that pin.
0x80000148 :  ld.w   r8,r7[-8]
0x8000014c :  bfextu r9,r8,0x0,0x5
0x80000150 :  mov    r8,1
0x80000152 :  lsl    r8,r8,r9
0x80000156 :  mov    r9,r8
0x80000158 :  ld.w   r8,r7[-4]
0x8000015c :  st.w   r8[0x4],r9

It is a huge amount of code just to light a LED.

I would be glad, if somebody would show me faster (after compilation, as small amount of machine commands as possible) and elegant method in C to set or clear particular pin of AT32UC3B0256 microcontroller.
Probably I could do something myself (after hours or days of experimenting), but I wish some experienced AVR32 programmer to show me the best, the fastest and the most elegant (canonical) method in C to set or clear a pin state.

Thanks in advance for Your help.

I find your answer very useful not only to me, but to every unexperienced AVR32 programmer.

Regards,
CypressValley.

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

Hi,
I want to give you a short answer for today but will think about a longer one later.
I'm not happy the a project has to be done. It's to complicated and no real getting started is available.
Your problem is the gpio driver. It's not 'optimized'. Have a look on gpio_set_gpio_pin. Atmel sets not only the output value register. They do set the output driver and the pin function as well.
I think in most cases a pin will be initialized once and you have to use it the way you want. For a simple output pin you will toggle the pin later on for example. This could be done by a define that way

#define gpio_tgl_pin(pin) AVR32_GPIO.port[pin >> 5].ovrt = Bit(pin & 0x1F)

and can be used like

gpio_tgl_pin(AVR32_PIN_PA03);

and will give you the code

80002010:	fe 79 10 00 	mov	r9,-61440
80002014:	30 88       	mov	r8,8
80002016:	f3 48 00 5c 	st.w	r9[92],r8

which will not really get shorter or faster.
What do you think about a way like this?

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

Hi,

Thanks for you answer.
I agree that the method you have shown is probably the best possible. Maybe it is possible to create "register variables" which one of them will has value of AVR32_PIN_PA03, and the second will be the value which now is "r9". Then we should have not three assembler commands, but only one (st.w reg1,reg2).

Regards,
CypressValley

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

Hello again,

Some new facts about speeding up of the code...
Maybe it will be useful for somebody.

First of all, the code in my first post in this topic was compiled with -O0 gcc option. So, the function gpio_set_gpio_pin() had 30 assembler instructions.

After compiling the code with -O3 compiler option, the body of gpio_set_gpio_pin() looks like that:

  volatile avr32_gpio_port_t *gpio_port = &GPIO.port[pin >> 5];
0x80000112 :  lsr   r12,0x5
0x80000114 :  lsl   r12,0x8
0x80000116 : sub   r12,61440
  gpio_port->ovrs  = 1 << (pin & 0x1F); // Value to be driven on the I/O line: 1.
0x8000010c :    mov   r8,1
0x8000010e :  lsl   r8,r8,r12
0x8000011a : st.w  r12[84],r8
  gpio_port->oders = 1 << (pin & 0x1F); // The GPIO output driver is enabled for that pin.
0x8000011e : st.w  r12[68],r8
  gpio_port->gpers = 1 << (pin & 0x1F); // The GPIO module controls that pin.
0x80000122 : st.w  r12[0x4],r8
}

So, it is only 9 assembler commands -- pretty good optimalisation comparing to previous 30 instructions.

Do not forget that entering the function also costs (calling a function takes extra commands).
In my case, making original gpio_set_gpio_pin() function (in gpio.h and gpio.c) the "inline" function does not work. But when I copied this function from gpio.c and pasted it in my main source code, then the compiler pasted the function's code inline the "main code", even without "inline" keyword. Remember that I still use -O3 parameter.

So, at this stage we have only 9 assembler instructions of gpio_set_gpio_pin() and no extra cost for calling this function.
But it is not enough for me!
Let's see the code below:

#include 
#include "gpio.h"

int main(void)
{
	// initialization
	avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[AVR32_PIN_PA03 >> 5];
	gpio_port->oders = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO output driver is enabled for that pin.
	gpio_port->gpers = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO module controls that pin.

	while (1) {
		// turn on the LED at PA03
		gpio_port->ovrs  = 1 << (AVR32_PIN_PA03 & 0x1F);
	}

	return 0;
}

It turn on the LED (set "1" at PA03) all the time (yes, yes, I know that is aimless and stupid, but it is only an example).
Now, let's see the code above but in assembler:

	gpio_port->oders = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO output driver is enabled for that pin.
0x800000fc 
: mov r9,-61440 0x80000100 : mov r8,8 0x80000104 : st.w r9[68],r8 gpio_port->gpers = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO module controls that pin. 0x80000108 : st.w r9[0x4],r8 gpio_port->ovrs = 1 << (AVR32_PIN_PA03 & 0x1F); 0x80000102 : mov r10,r9 0x8000010a : st.w r10[84],r8 0x8000010e : rjmp 0x8000010a

Look at last two assembler commands. "st.w" set "1" at PA03, and "rjmp" jumps all the time at the address of "st.w".

So, after optimalisation and writing smart code, it is only one assembler command to set the bit to "1" -- pretty good comparing to 30 assembler commands (and extra cost of calling a function) at the beginning.

Now, it is time for blinking a LED:

#include 
#include "gpio.h"

int main(void)
{
	delay_init(60000000);

	// initialization
	avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[AVR32_PIN_PA03 >> 5];
	gpio_port->oders = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO output driver is enabled for that pin.
	gpio_port->gpers = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO module controls that pin.

	while (1) {
		// turn on the LED at PA03
		gpio_port->ovrs  = 1 << (AVR32_PIN_PA03 & 0x1F);

		delay_ms(200);

		gpio_port->ovrc  = 1 << (AVR32_PIN_PA03 & 0x1F);

		delay_ms(200);
	}

	return 0;
}

Now, the assembler code is:


{
0x800000fc 
: pushm r5-r6,lr delay_init(60000000); 0x80000100 : mov r12,34560 0x80000104 : orh r12,0x393 0x80000108 : mcall 0x80000138 gpio_port->oders = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO output driver is enabled for that pin. 0x8000010c : mov r9,-61440 0x80000110 : mov r8,8 0x80000114 : st.w r9[68],r8 gpio_port->gpers = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO module controls that pin. 0x80000112 : st.w r9[0x4],r8 gpio_port->ovrs = 1 << (AVR32_PIN_PA03 & 0x1F); 0x80000118 : mov r5,-61440 0x8000011c : mov r6,8 0x80000122 : st.w r5[84],r6 delay_ms(200); 0x8000011e : mov r12,200 0x80000126 : mcall 0x8000013c gpio_port->ovrc = 1 << (AVR32_PIN_PA03 & 0x1F); 0x8000012e : st.w r5[88],r6 delay_ms(200); 0x8000012a : mov r12,200 0x80000132 : mcall 0x8000013c 0x80000136 : rjmp 0x80000118 0x80000138 : ld.sh r0,r0[0x0] 0x8000013a : ld.w r0,--r0 0x8000013c : ld.sh r0,r0[0x0] 0x8000013e : ld.w r12,--r0

Well... to set the bit, there are three commands:

0x80000118 : mov   r5,-61440
0x8000011c : mov   r6,8
0x80000122 : st.w  r5[84],r6

and to clear the bit, there is one command (it uses r5 and r6 set at the beginning of infinitive loop):

0x8000012e : st.w  r5[88],r6

It is approximately two commands to set and two commands to clear I/O pin. It should be better (one command to set and one command to clear the I/O pin) when we declare two "register variables" (one with value of 1 << (AVR32_PIN_PA03 & 0x1F) and one with value of GPIO port). Unfortunately register variables seems not to be handled by a compiler.
But, there is the other solution -- use assembler directly in the code. I hope, that compiler is smart enough not to use the registers I used in my assembler code (if somebody knows, please let me know). The other thing we should think about is a pipeline mechanism in CPU -- maybe the code generated be compiler would be faster than mine, even when my code is shorter.

#include 
#include "gpio.h"

int main(void)
{
	delay_init(60000000);

	// initialization
	avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[AVR32_PIN_PA03 >> 5];
	gpio_port->oders = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO output driver is enabled for that pin.
	gpio_port->gpers = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO module controls that pin.

	asm("mov r5,-61440; mov r6,8");
	while (1) {
		// turn on the LED at PA03
		asm("st.w r5[84],r6");

		delay_ms(200);

		asm("st.w  r5[88],r6");

		delay_ms(200);

	}

	return 0;
}

Now, the assembler code is:

{
0x800000fc 
: pushm lr delay_init(60000000); 0x800000fe : mov r12,34560 0x80000102 : orh r12,0x393 0x80000106 : mcall 0x80000138 gpio_port->oders = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO output driver is enabled for that pin. 0x8000010a : mov r9,-61440 0x8000010e : mov r8,8 0x80000112 : st.w r9[68],r8 gpio_port->gpers = 1 << (AVR32_PIN_PA03 & 0x1F); // The GPIO module controls that pin. 0x80000110 : st.w r9[0x4],r8 asm("mov r5,-61440; mov r6,8"); 0x80000116 : mov r5,-61440 0x8000011a : mov r6,8 asm("st.w r5[84],r6"); 0x8000011c : st.w r5[84],r6 delay_ms(200); 0x80000120 : mov r12,200 0x80000124 : mcall 0x8000013c asm("st.w r5[88],r6"); 0x80000128 : st.w r5[88],r6 delay_ms(200); 0x8000012c : mov r12,200 0x80000130 : mcall 0x8000013c 0x80000134 : rjmp 0x8000011c 0x80000136 : add r0,r0 0x80000138 : ld.sh r0,r0[0x0] 0x8000013a : ld.w r0,--r0 0x8000013c : ld.sh r0,r0[0x0] 0x8000013e : ld.w r12,--r0

What we have now? One assembler command to set the pin and one assembler command to clear the bit! It is not elegant and clear method, but at least fast.

Regards,
CypressValley