Compilación de programas en lenguaje C para el microcontrolador ...
Compilación de programas en lenguaje C para el microcontrolador ...
Compilación de programas en lenguaje C para el microcontrolador ...
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
Compilación <strong>de</strong> <strong>programas</strong> <strong>en</strong> l<strong>en</strong>guaje C <strong>para</strong> <strong>el</strong> <strong>microcontrolador</strong><br />
AVR<br />
1. Com<strong>en</strong>zando<br />
Las herrami<strong>en</strong>tas <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> aplicaciones provi<strong>en</strong><strong>en</strong> <strong>de</strong> GNU y funcionan <strong>en</strong> Linux. Para arrancar este<br />
sistema operativo <strong>en</strong> los or<strong>de</strong>nadores <strong>de</strong>l laboratorio hay que esperar que arranqu<strong>en</strong> <strong>de</strong> la red (pue<strong>de</strong>n tardar<br />
unos 20 seg.) y luego, <strong>en</strong> <strong>el</strong> m<strong>en</strong>ú que se muestra s<strong>el</strong>eccionar “3) Linux”.<br />
Tras <strong>el</strong> arranque <strong>de</strong> Linux hay que <strong>en</strong>trar <strong>en</strong> la cu<strong>en</strong>ta <strong>de</strong> prácticas:<br />
Login: **********<br />
Password: *******<br />
D<strong>en</strong>tro <strong>de</strong> la cu<strong>en</strong>ta <strong>de</strong> prácticas cada grupo t<strong>en</strong>drá su propio subdirectorio.<br />
Este es un listado <strong>de</strong> las herrami<strong>en</strong>tas <strong>de</strong> <strong>de</strong>sarrollo y otras aplicaciones útiles:<br />
avr-gcc: compilador <strong>de</strong> C <strong>para</strong> AVR.<br />
avr-as: <strong>en</strong>samblador <strong>para</strong> AVR.<br />
avr-ld: linker.<br />
avr-objcopy: conversor <strong>de</strong> formatos <strong>de</strong> ficheros ejecutables.<br />
avr-objdump: <strong>de</strong>s<strong>en</strong>samblador <strong>para</strong> AVR.<br />
make: gestor <strong>de</strong> proyectos.<br />
avrprg: programador <strong>para</strong> <strong>el</strong> prototipo <strong>de</strong>l <strong>microcontrolador</strong> (<strong>de</strong>fecto: COM1)<br />
terminal: terminal <strong>para</strong> <strong>el</strong> puerto serie (<strong>de</strong>fecto: COM2, 9600 baudios)<br />
nedit: editor <strong>de</strong> texto <strong>para</strong> <strong>programas</strong> <strong>en</strong> C.<br />
1
2. Programa <strong>de</strong> ejemplo<br />
En <strong>el</strong> directorio “ejemplo” hay un conjunto <strong>de</strong> ficheros que mustran <strong>de</strong> forma básica como g<strong>en</strong>erar imág<strong>en</strong>es<br />
<strong>de</strong> memoria <strong>para</strong> <strong>el</strong> <strong>microcontrolador</strong> AVR a partir <strong>de</strong> código fu<strong>en</strong>te <strong>en</strong> C y <strong>en</strong> <strong>en</strong>samblador. Para compilar <strong>el</strong><br />
programa <strong>de</strong> ejemplo <strong>en</strong> primer lugar copiaremos los ficheros a otro directorio y luego siemplem<strong>en</strong>te ejecutaremos<br />
<strong>el</strong> comando “make”. Los ficheros <strong>de</strong>l ejemplo son:<br />
main.c: programa <strong>en</strong> C que incluye la función “main”.<br />
Makefile: <strong>de</strong>p<strong>en</strong><strong>de</strong>ncias y control <strong>de</strong> la compilación.<br />
El proceso <strong>de</strong> la compilación queda repres<strong>en</strong>tado <strong>de</strong> forma gráfica <strong>en</strong> la sigui<strong>en</strong>te figura:<br />
inicialización<br />
linker−script<br />
código fu<strong>en</strong>te<br />
main.c<br />
avr−gcc<br />
crts2313.o<br />
objeto ELF<br />
main.o<br />
avr2.x<br />
ejecutable ELF<br />
avr−ld<br />
avr−objcopy<br />
co<strong>de</strong>.<strong>el</strong>f<br />
ejecutable<br />
hexa<strong>de</strong>cimal<br />
(int<strong>el</strong>−hex)<br />
co<strong>de</strong>.ihx<br />
cabeceras<br />
io.h<br />
interrupt.h<br />
librerías<br />
libgcc.a<br />
libc.a<br />
ld.map<br />
mapa <strong>de</strong> memoria<br />
En la compilación intervi<strong>en</strong><strong>en</strong> tanto nuestro código fu<strong>en</strong>te como otros ficheros que se <strong>en</strong>cu<strong>en</strong>tran conv<strong>en</strong>i<strong>en</strong>tem<strong>en</strong>te<br />
instalados <strong>en</strong> <strong>el</strong> servidor. Estos últimos son los ficheros <strong>de</strong> cabecera, las librerías <strong>de</strong>l gcc (libgcc.a) y<br />
estándar (libc.a), <strong>el</strong> código <strong>de</strong> inicialización <strong>de</strong>l programa (crts2313.o) y <strong>el</strong> “linker-script” (avr2.x) que controla<br />
<strong>el</strong> proceso <strong>de</strong> <strong>en</strong>lazado. Las <strong>de</strong>p<strong>en</strong><strong>de</strong>ncias <strong>en</strong>tre ficheros y los comandos que se han <strong>de</strong> ejecutar <strong>para</strong> g<strong>en</strong>erarlos<br />
están <strong>de</strong>tallados <strong>en</strong> <strong>el</strong> fichero “Makefile”, que conti<strong>en</strong>e líneas como las sigui<strong>en</strong>tes:<br />
ACFLAGS = -Os -mmcu=at90s2313<br />
OBJS = main.o<br />
En estas líneas se <strong>de</strong>fin<strong>en</strong> algunas variables que se utilizarán a continuación. En particular la variable ACFLAGS<br />
conti<strong>en</strong>e las opciones que se pasarán al compilador “avr-gcc”, tales como <strong>el</strong> niv<strong>el</strong> <strong>de</strong> optimización (-Os) y <strong>el</strong><br />
mo<strong>de</strong>lo <strong>de</strong>l <strong>microcontrolador</strong> <strong>para</strong> <strong>el</strong> que se va a g<strong>en</strong>erar <strong>el</strong> código (-mmcu=at90s2313). La variable OBJS<br />
conti<strong>en</strong>e una lista <strong>de</strong> los ficheros objeto que intervi<strong>en</strong><strong>en</strong> <strong>en</strong> la compilación. En este caso tan sólo hay un fichero<br />
objeto (main.o).<br />
2
co<strong>de</strong>.ihx:(tab) co<strong>de</strong>.<strong>el</strong>f<br />
(tab)<br />
avr-objcopy -O ihex $< $@<br />
En estas líneas se indica que “co<strong>de</strong>.ihx” se obti<strong>en</strong>e a partir <strong>de</strong> “co<strong>de</strong>.<strong>el</strong>f” y que <strong>el</strong> comando que hay que ejecutar<br />
<strong>para</strong> g<strong>en</strong>erar “co<strong>de</strong>.ihx” es “avr-objcopy ...”. Las variables “$
• -O0 : sin optimización.<br />
• -O1 : optimización <strong>de</strong> transfer<strong>en</strong>cias <strong>en</strong>tre registros.<br />
• -O2 : más optimizaciones.<br />
• -O3 : a<strong>de</strong>más se incluy<strong>en</strong> funciones como “inline”<br />
• -Os : optimización <strong>para</strong> reducir <strong>el</strong> espacio <strong>de</strong> memoria ocupado por <strong>el</strong> código.<br />
-mmcu=: S<strong>el</strong>ecciona <strong>el</strong> conjunto <strong>de</strong> instrucciones a<strong>de</strong>cuado <strong>para</strong> <strong>el</strong> tipo <strong>de</strong> <strong>microcontrolador</strong><br />
especificado.<br />
-Wl,,,... : Permite pasar opciones al linker.<br />
avr-ld<br />
-T : Indica <strong>el</strong> fichero que se usará <strong>para</strong> controlar <strong>el</strong> proceso <strong>de</strong> <strong>en</strong>lazado. En este<br />
fichero se <strong>de</strong>talla la organización <strong>de</strong> la memoria.<br />
-Map : g<strong>en</strong>era un listado <strong>de</strong>l mapa <strong>de</strong> memoria <strong>en</strong> <strong>el</strong> fichero indicado.<br />
avr-objcopy<br />
-O : indica <strong>el</strong> formato <strong>de</strong>l fichero <strong>de</strong> salida (<strong>el</strong> fichero <strong>de</strong> <strong>en</strong>trada está <strong>en</strong> formato ELF). Los<br />
formatos que nos pue<strong>de</strong>n interesar son:<br />
• srec : Formato hexa<strong>de</strong>cimal <strong>de</strong> Motorola.<br />
• ihex : Formato hexa<strong>de</strong>cimal <strong>de</strong> Int<strong>el</strong>.<br />
• binary : Imag<strong>en</strong> <strong>de</strong> la memoria <strong>en</strong> binario.<br />
avr-objdump<br />
-d : <strong>de</strong>s<strong>en</strong>samblar secciones <strong>de</strong> código <strong>de</strong>l fichero objeto o ejecutable ELF que se indique.<br />
-D: <strong>de</strong>s<strong>en</strong>samblar todas las secciones <strong>de</strong>l fichero.<br />
4. Particularida<strong>de</strong>s <strong>de</strong>l compilador GCC <strong>para</strong> AVR<br />
Tipos <strong>de</strong> datos:<br />
Las longitu<strong>de</strong>s <strong>de</strong> los tipos <strong>de</strong> datos <strong>para</strong> avr-gcc son:<br />
4
Tipo <strong>de</strong> dato<br />
char<br />
short<br />
int<br />
long<br />
float<br />
double<br />
Longitud<br />
8 bits<br />
16 bits<br />
16 bits<br />
32 bits<br />
32 bits<br />
32 bits<br />
Las variables <strong>de</strong> coma flotante aunque estén soportadas por <strong>el</strong> compilador no son nada recom<strong>en</strong>dables, ya<br />
que su uso requiere incluir <strong>en</strong> <strong>el</strong> programa las rutinas <strong>de</strong> emulación <strong>de</strong> coma flotante <strong>de</strong> “libgcc.a”, que ocupan<br />
mucha memoria.<br />
Modificadores<br />
volatile<br />
Este modificador indica que la variable a la que hace refer<strong>en</strong>cia pue<strong>de</strong> modificarse por causas aj<strong>en</strong>as al<br />
programa, y por lo tanto <strong>el</strong> compilador no <strong>de</strong>be optimizar sus accesos a dichas variables. Este modificador <strong>de</strong>be<br />
usarse, por ejemplo, cuando se acce<strong>de</strong> a los registros <strong>de</strong> los periféricos, o cuando una variable pue<strong>de</strong> modificarse<br />
<strong>de</strong>s<strong>de</strong> una rutina <strong>de</strong> interrupción.<br />
Ejemplo:<br />
dato=*(volatile unsigned char *)0x30; // lectura <strong>de</strong> PIND<br />
En la dirección 0x30 está <strong>el</strong> puerto <strong>de</strong> E/S PIND y su valor <strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong> las t<strong>en</strong>siones aplicadas a los pines <strong>de</strong>l<br />
<strong>microcontrolador</strong>. Por lo tanto <strong>el</strong> puntero a dicha dirección <strong>de</strong> memoria se <strong>de</strong>be <strong>de</strong>clarar como “volatile”.<br />
inline, extern inline<br />
Si una función se <strong>de</strong>clara como “inline” <strong>el</strong> compilador no g<strong>en</strong>era llamadas a la subrutina <strong>de</strong> dicha función.<br />
En su lugar <strong>el</strong> código <strong>de</strong> la función se inserta <strong>en</strong> cada ocasión <strong>en</strong> la que se llama a la función <strong>en</strong> <strong>el</strong> programa.<br />
Esto pue<strong>de</strong> hacer los <strong>programas</strong> más largos, pero más rápidos al evitar los tiempos <strong>de</strong> llamada y retorno <strong>de</strong><br />
subrutinas. Este modificador es particularm<strong>en</strong>te útil con funciones muy cortas. Cuando la función <strong>en</strong> cuestión<br />
se <strong>de</strong>clara como “inline” todavía se g<strong>en</strong>era una subrutina por si acaso es llamada <strong>de</strong>s<strong>de</strong> otros ficheros objeto<br />
que se puedan <strong>en</strong>lazar con <strong>el</strong> programa. Esto se evita <strong>de</strong>fini<strong>en</strong>do la función como “extern inline”. En este caso<br />
no se g<strong>en</strong>era dicha subrutina.<br />
5
Código <strong>en</strong> <strong>en</strong>samblador<br />
En los <strong>programas</strong> <strong>en</strong> C se pue<strong>de</strong> insertar directam<strong>en</strong>te código <strong>en</strong> <strong>en</strong>samblador mediante la palabra clave<br />
“asm” o “asm volatile”. Ejemplo:<br />
asm volatile (“sei”);<br />
// Habilitamos interrupciones<br />
El modificador “volatile“ fuerza al compilador a insertar nuestro código exactam<strong>en</strong>te <strong>en</strong> la parte <strong>de</strong>l programa<br />
<strong>en</strong> la que se <strong>en</strong>cu<strong>en</strong>tra. El comando “asm” es mucho más sofisticado y permite <strong>el</strong> paso <strong>de</strong> parámetros <strong>en</strong>tre <strong>el</strong><br />
código <strong>en</strong> C y <strong>en</strong> <strong>en</strong>samblador, como ocurre <strong>en</strong> <strong>el</strong> ejemplo sigui<strong>en</strong>te:<br />
a=100; b=20;<br />
asm volatile ("swap %0\n\tadd %0, %1":"+r"(b):"r"(a));<br />
que <strong>el</strong> compilador convierte <strong>en</strong>:<br />
ldi r24,lo8(100)<br />
ldi r25,lo8(20)<br />
swap r25<br />
add r25,r24<br />
don<strong>de</strong> vemos que las variables “a” y “b” han sido almac<strong>en</strong>adas <strong>en</strong> los registros “r24” y “r25”. La sintaxis <strong>de</strong> la<br />
línea “asm” es como sigue:<br />
asm volatile (“código <strong>en</strong>samblador” : parámetros <strong>de</strong> salida : parámetros <strong>de</strong> <strong>en</strong>trada);<br />
Don<strong>de</strong>, <strong>en</strong> <strong>el</strong> código <strong>en</strong> <strong>en</strong>samblador hacemos refer<strong>en</strong>cia a los parámetros como “ %0” <strong>para</strong> <strong>el</strong> primero <strong>de</strong><br />
las listas, “ %1” <strong>para</strong> <strong>el</strong> sigui<strong>en</strong>te y así sucesivam<strong>en</strong>te. Las listas <strong>de</strong> parámetros <strong>de</strong> <strong>en</strong>trada y/o salida se <strong>de</strong>fin<strong>en</strong><br />
como:<br />
“modificadores” (variable), “modificadores” (variable)....<br />
Los modificadores indican:<br />
“=” : se sobreescribe la variable.<br />
“+” : se lee y modifica.<br />
“r” : la variable resi<strong>de</strong> <strong>en</strong> un registro.<br />
“m” : la variable es una posición <strong>de</strong> memoria.<br />
“i” : la variable es un operando inmediato (constante).<br />
6
Funciones <strong>de</strong> interrupción<br />
Las funciones <strong>de</strong> interrupción son especiales ya que están concebidas como manejadores <strong>de</strong> interrupciones.<br />
Esto significa que difier<strong>en</strong> <strong>de</strong> las funciones normales <strong>en</strong> los sigui<strong>en</strong>tes aspectos:<br />
Guardan todos los registros que modifican <strong>en</strong> la pila, incluy<strong>en</strong>do SREG, al comi<strong>en</strong>zo <strong>de</strong> la función.<br />
Recuperan los valores originales <strong>de</strong> los registros <strong>de</strong> la pila al finalizar la función.<br />
Retornan con la instrucción “RETI”.<br />
Para que una función actúe como rutina <strong>de</strong> at<strong>en</strong>ción a una interrupción se <strong>de</strong>be <strong>de</strong>clarar mediante la macro<br />
ISR() tal y como se muestra <strong>en</strong> <strong>el</strong> ejemplo:<br />
ISR(TIMER0_OVF0_vect)<br />
{<br />
nirqs++;<br />
}<br />
La macro ISR <strong>de</strong>l ejemplo proporciona un manejador <strong>de</strong> interrupciones <strong>para</strong> <strong>el</strong> <strong>de</strong>sbordami<strong>en</strong>to <strong>de</strong>l temporizador<br />
“TIMER0”. Este manejador lo único que hace es increm<strong>en</strong>tar una variable global <strong>de</strong>l programa. Las<br />
funciones <strong>de</strong> interrupción no ti<strong>en</strong><strong>en</strong> parámetros ni <strong>de</strong>vu<strong>el</strong>v<strong>en</strong> resultados. Des<strong>de</strong> estas funciones no se <strong>de</strong>be<br />
llamar a ninguna otra función <strong>de</strong>l programa pues se podría alterar <strong>el</strong> valor <strong>de</strong> algún registro que no ha sido<br />
previam<strong>en</strong>te guardado <strong>en</strong> la pila.<br />
Los nombres <strong>de</strong> los manejadores <strong>de</strong> interrupción <strong>de</strong>p<strong>en</strong><strong>de</strong>n <strong>de</strong>l <strong>microcontrolador</strong>. Para <strong>el</strong> at90s2313 son los<br />
sigui<strong>en</strong>tes:<br />
Nombre <strong>de</strong>l Manejador<br />
N o <strong>de</strong>l vector<br />
INT0_vect 1<br />
INT1_vect 2<br />
TIMER1_CAPT1_vect 3<br />
TIMER1_COMP1_vect 4<br />
TIMER1_OVF1_vect 5<br />
TIMER0_OVF0_vect 6<br />
UART_RX_vect 7<br />
UART_UDRE_vect 8<br />
UART_TX_vect 9<br />
ANA_COMP_vect 10<br />
7
Como recom<strong>en</strong>daciones g<strong>en</strong>erales <strong>para</strong> la escritura <strong>de</strong>l código <strong>de</strong> rutinas <strong>de</strong> interrupción po<strong>de</strong>mos <strong>de</strong>stacar:<br />
No llamar a otras funciones <strong>de</strong>s<strong>de</strong> una rutina <strong>de</strong> interrupción.<br />
Recordar <strong>de</strong>clarar como “volatile” todas las variables globales que modifique la rutina <strong>de</strong> interrupción.<br />
No hacer retardos <strong>de</strong>ntro <strong>de</strong> una rutina <strong>de</strong> interrupción. Evitar los bucles.<br />
Cuando una rutina <strong>de</strong> interrupción modifique una variable <strong>de</strong> 16 o 32 bits hay que garantizar un acceso<br />
atómico a dicha variable <strong>en</strong> <strong>el</strong> programa principal, lo que implica:<br />
• Apagar las interrupciones: cli();<br />
• Leer la variable: valor=var_global;<br />
• Activar las interrupciones: sei();<br />
Si la rutina <strong>de</strong> interrupción acce<strong>de</strong> a algún registro <strong>de</strong> E/S <strong>de</strong> 16 bits <strong>de</strong>l temporizador también hay<br />
que garantizar un acceso atómico <strong>de</strong>s<strong>de</strong> <strong>el</strong> programa principal a TODOS los registros <strong>de</strong> 16 bits <strong>de</strong><br />
temporizador. Ello se <strong>de</strong>be a que la interrupción pue<strong>de</strong> alterar <strong>el</strong> valor <strong>de</strong> un registro temporal <strong>de</strong> 8 bits<br />
que se usa <strong>para</strong> sincronizar <strong>el</strong> acceso a la parte alta <strong>de</strong>l dato <strong>de</strong> 16 bits.<br />
Acceso a registros <strong>de</strong> E/S<br />
Los registros <strong>de</strong> <strong>en</strong>trada y salida <strong>de</strong>l AVR 90S2313 están <strong>de</strong>finidos <strong>en</strong> <strong>el</strong> fichero <strong>de</strong> cabecera “”. El<br />
cont<strong>en</strong>ido <strong>de</strong> este fichero varía <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> <strong>microcontrolador</strong> que se especifice <strong>en</strong> la compilación<br />
(avr-gcc -mmcu=....). Los registros están <strong>de</strong>findos como posiciones fijas <strong>de</strong> la memoria <strong>de</strong> datos, tal y como se<br />
muestra <strong>en</strong> <strong>el</strong> ejemplo sigui<strong>en</strong>te:<br />
#<strong>de</strong>fine UBRR (*(volatile unsigned char *)0x29)<br />
Por lo tanto, <strong>en</strong> nuestros <strong>programas</strong>, los nombres <strong>de</strong> los registros <strong>de</strong> E/S <strong>de</strong> <strong>microcontrolador</strong> repres<strong>en</strong>tan a<br />
variables a las que se pue<strong>de</strong>n asignar valores y que pue<strong>de</strong>n interv<strong>en</strong>ir <strong>en</strong> expresiones aritméticas y logicas. El<br />
compilador es sufici<strong>en</strong>tem<strong>en</strong>te int<strong>el</strong>ig<strong>en</strong>te <strong>para</strong> convertir las refer<strong>en</strong>cias a dichas variables <strong>en</strong> instrucciones “IN”<br />
y “OUT”, como <strong>en</strong> <strong>el</strong> sigui<strong>en</strong>te ejemplo:<br />
UBRR=64;<br />
que se compila como:<br />
ldi r24,0x40<br />
out 0x09,r24<br />
8
También <strong>el</strong> compilador pue<strong>de</strong> <strong>de</strong>tectar un cambio <strong>de</strong> un único bit <strong>en</strong> estas variables y utilizar las instrucciones<br />
<strong>de</strong> manejo <strong>de</strong> bit SBI/CBI, como <strong>en</strong> este ejemplo:<br />
PORTD|=(1<
prog_int16_t <strong>de</strong>f_val[3]={1234,4237,25315};<br />
prog_int32_t <strong>de</strong>f_val32[4]={1234567,10000000,99999999,87654321};<br />
...<br />
int val;<br />
long v32;<br />
val=__LPM_word(&<strong>de</strong>f_val[1]);<br />
val=pgm_read_word(&<strong>de</strong>f_al[1]);<br />
// val=4237<br />
// sinónimo<br />
v32=__LPM_dword(&<strong>de</strong>f_val32[2]); // v32=99999999<br />
v32=pgm_read_dword(&<strong>de</strong>f_val32[2]);<br />
//sinónimo<br />
Organización <strong>de</strong> la memoria<br />
La memoria <strong>en</strong> los <strong>programas</strong> compilados con gcc queda organizada <strong>en</strong> secciones, como se muestra <strong>en</strong> la<br />
sigui<strong>en</strong>te figura:<br />
0x7ff<br />
memoria <strong>de</strong><br />
programa<br />
Libre<br />
0xdf<br />
memoria <strong>de</strong><br />
datos<br />
Pila<br />
.data<br />
se copia<br />
al arrancar<br />
Libre<br />
.text<br />
_<strong>en</strong>d<br />
.bss<br />
0x0<br />
.progmem<br />
.init<br />
0x60<br />
.data<br />
regs. E/S, CPU<br />
Cada sección conti<strong>en</strong>e un tipo distinto <strong>de</strong> datos. Estos son:<br />
.init : tabla <strong>de</strong> saltos <strong>de</strong> interrupciones y código <strong>de</strong> inicialización.<br />
.progmem : Constantes <strong>en</strong> la memoria <strong>de</strong> programa.<br />
.text : Código <strong>de</strong>l programa.<br />
.data : Valores iniciales <strong>de</strong> variables estáticas inicializadas. Estos valores son copiados a la RAM antes<br />
<strong>de</strong> llamar a la función “main”.<br />
.bss : variables estáticas sin inicializar. Su valor inicial es 0.<br />
El símbolo “_<strong>en</strong>d” nos indica la primera posición libre <strong>de</strong> la memoria RAM.<br />
10
Uso <strong>de</strong> los registros<br />
Paso <strong>de</strong> parámetros <strong>en</strong> funciones <strong>de</strong>l l<strong>en</strong>guaje C.<br />
f() : función que se llama <strong>de</strong>s<strong>de</strong> <strong>el</strong> programa<br />
p8<br />
: parámetro <strong>de</strong> 8 bits (char), (short)<br />
p16 : parámetro <strong>de</strong> 16 bits (int), punteros<br />
p32 : parámetro <strong>de</strong> 32 bits (long)<br />
Ejemplos <strong>de</strong> asignación <strong>de</strong> registros a parámetros (or<strong>de</strong>n <strong>de</strong> los parámetros: f(pr1,pr2,pr3,...);):<br />
Param. 1 Param. 2 Param. 3 Param. 4<br />
f(p8)<br />
R24<br />
f(p8,p8,p8,p8) R24 R22 R20 R18<br />
f(p16,p16) R25:R24 ◦ R23:R22<br />
f(p16,p16,p16) R25:R24 R23:R22 R21:R20<br />
f(p32,p32) R25:R24:R23:R22 ◦ R21:R20:R19:R18<br />
f(p32,p32,p32) R25:R24:R23:R22 R21:R20:R19:R18 R17:R16:R15:R14<br />
f(p8,p16,p32) R24 R23:R22 R21:R20:R19:R18<br />
f(p32,p16,p8) R25:R24:R23:R22 R21:R20 R18<br />
◦ MSB izq, LSB <strong>de</strong>r.<br />
Como po<strong>de</strong>mos ver los parámetros se asocian a registros <strong>de</strong> modo que su byte m<strong>en</strong>os significativo queda<br />
siempre <strong>en</strong> un registro par.<br />
Valor <strong>de</strong> retorno (si lo hubiese):<br />
p8
Registro Uso (código C) Acción<br />
R0 temporal Se pue<strong>de</strong> cambiar<br />
R1 valor 0 Debe valer 0 al retornar<br />
R2-R17 variables Se <strong>de</strong>b<strong>en</strong> PRESERVAR<br />
R18-R25 variables/parámetros Se pue<strong>de</strong>n cambiar<br />
R26:R27 Puntero X Se pue<strong>de</strong>n cambiar<br />
R28:R29 Puntero Y Se <strong>de</strong>b<strong>en</strong> PRESERVAR<br />
R30:R31 Puntero Z Se pue<strong>de</strong>n cambiar<br />
Los registros marcados como "se pue<strong>de</strong> cambiar" pue<strong>de</strong>n usarse <strong>en</strong> subrutinas escritas <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>samblador<br />
como varables temporales y su valor inicial no necesita ser recuperado antes <strong>de</strong> retornar.<br />
Al compilador avr-gcc se le pue<strong>de</strong> instruir <strong>para</strong> que no utilice <strong>de</strong>terminados registros a la hora <strong>de</strong> g<strong>en</strong>erar <strong>el</strong><br />
código. Esto se hace mediante opciones <strong>de</strong>l tipo “gcc -avr -mmcu=at90s2313 -ffixed-r3 -ffixed-r4 ...”. En este<br />
ejemplo <strong>el</strong> compilador nos garantiza que no utilizará los registros R3 y R4. Eso no significa que no se utilic<strong>en</strong><br />
<strong>en</strong> algunas funciones <strong>de</strong> librería, así que esta opción <strong>de</strong>be usarse con cuidado.<br />
12