Publicado el 21 de Febrero del 2020
1.967 visualizaciones desde el 21 de Febrero del 2020
164,5 KB
55 paginas
Creado hace 12a (16/08/2012)
SASE 2012
Programación en C
para Sistemas Embebidos
(con ejemplos basados en MSP430)
Mg. Guillermo Friedrich
UTN-FRBB
Tópicos destacados
- Generalidades sobre la arquitectura MSP430
- Paso de parámetros a funciones y variables locales
- Uso de estructuras y punteros a estructuras
- Estructuras y uniones “anónimas”
- Campos de bits
- Constantes
- Variables asociadas a SFR del micro
- Proteger la concurrencia sobre variables compartidas
- Atención de interrupciones
- Fijar direcciones de memoria usadas por código y datos
SASE 2012
2
MSP430
- Arquitectura RISC 16-Bit, Ciclo de Instrucción 62.5 o
125-ns
- MSP430F133: 8KB+256B Flash Memory, 256B RAM
- MSP430F135: 16KB+256B Flash Memory, 512B RAM
- MSP430F147: 32KB+256B Flash Memory, 1KB RAM
- MSP430F148: 48KB+256B Flash Memory, 2KB RAM
- MSP430F149: 60KB+256B Flash Memory, 2KB RAM
- MSP430G2553: 16KB+256B Flash Memory, 512 B
RAM
SASE 2012
3
MSP430: mapa de memoria
SASE 2012
4
MSP430
- La reducida cantidad de RAM obliga a tener en cuenta
algunos detalles al programar en C.
- Los parámetros y variables locales (auto) de las funciones
utilizan registros del procesador y a veces también la pila.
- Las variables con valores constantes (por ej. tablas de
parámetros, textos predefinidos, etc.) conviene declararlas
como const, para que sean ubicadas en memoria Flash.
SASE 2012
5
Codificación eficiente para aplicaciones embebidas
- Seleccionar los tipos de datos más adecuado.
- Controlar la ubicación de los objetos de código y
datos en memoria.
- Controlar las optimizaciones del compilador.
- Escribir código eficiente.
SASE 2012
6
Selección de los tipos de datos
- Usar los tipos más pequeños que sea posible.
- Tratar de evitar el uso de double y long long (64 bits)
- No usar campos de bit de tamaño mayor que 1.
- Tratar de evitar el uso de punto flotante.
- Si un puntero no se va a usar para modificar datos,
declararlo const (Por ej.: const char *p).
SASE 2012
7
Uso de punto flotante
- En un micro sin coprocesador matemático, las
operaciones de punto flotante son ineficientes en
tiempo de ejecución y uso de memoria.
- Si se puede, es preferible usar tipos float (32 bits)
en lugar de double (64 bits).
- Las constantes de punto flotante por defecto son
double, excepto que se las especifique como float.
a += 1.0f // float
a += 1.0 // double
SASE 2012
8
Campos de bits
- Por ejemplo, dada la siguiente estructura de campos de
bits:
union bits
{
struct
{
unsigned int b1:1;
unsigned int b2:2;
unsigned int b3:3;
unsigned int b4:4;
unsigned int b6:6;
}b;
unsigned int w;
}bf;
SASE 2012
9
Campos de bits
- Las operaciones sobre campos de un bit pueden
requerir una sola instrucción de máquina.
En C:
bf.b.b1 = 1;
En lenguaje ensamblador:
bis.w
#0x1,&bf
SASE 2012
10
Campos de bits
- Las operaciones sobre campos de más de un bit
requieren varias instrucciones de máquina:
En C:
bf.b.b3 = 5;
En lenguaje ensamblador:
mov.w
and.w
bis.w
mov.w
&bf,R15 ; copia
#0xFFC7,R15 ; borra
#0x28,R15 ; setea
R15,&bf ; copia
SASE 2012
11
Campos de bits
- La misma operación implementada sin campos de
bits es más eficiente:
En C:
w &= 0x00c7; // borra 3 bits
w |= 5 << 3; // escribe el 5
En lenguaje ensamblador:
and.w
bis.w
#0xC7,&w ; borra
#0x28,&w ; escribe
SASE 2012
12
Estructuras y Uniones anónimas
- Son propias del lenguaje C++
- Algunos compiladores de C tienen extensiones que
las admiten.
- Los nombres de los miembros tienen alcance fuera
de la estructura o unión.
SASE 2012
13
Estructuras y Uniones anónimas
union bits
{
struct
{
unsigned int b1:1;
unsigned int b2:2;
unsigned int b3:3;
unsigned int b4:4;
unsigned int b6:6;
}b;
unsigned int w;
}bf;
// unnamed
union
{
struct
{
unsigned int b1:1;
unsigned int b2:2;
unsigned int b3:3;
unsigned int b4:4;
unsigned int b6:6;
};
unsigned int w;
};
Estructuras y Uniones anónimas
// unnamed
union
{
struct
{
unsigned int b1:1;
unsigned int b2:2;
unsigned int b3:3;
unsigned int b4:4;
unsigned int b6:6;
};
unsigned int w;
};
}
// los miembros de las
// struct y union
// unnamed tienen
// alcance global,
// no pueden repetirse
main()
{ // Pone todo a 0
w = 0;
// asigna valores
// a algunos campos
b3 = 5;
b6 = 9;
Estructuras y Uniones anónimas
- Las estructuras y uniones anónimas son de utilidad
para manipular los registros del procesador que
están mapeados en direcciones de memoria.
- Por ejemplo: el registro U0CTL (Control de USART0)
está ubicado en la dirección 0x070, es de 8 bits y
tiene la siguiente estructura:
SASE 2012
16
Estructuras y Uniones “unnamed” (sin nombre)
__no_init volatile union
{
unsigned char U0CTL;
struct
{
unsigned char SWRST : 1;
unsigned char MM : 1;
unsigned char SYNC : 1;
unsigned char LISTEN: 1;
unsigned char CHAR : 1;
unsigned char SPB : 1;
unsigned char PEV : 1;
unsigned char PENA : 1;
};
} @ 0x0070; // mapea la variable a la dir 70h
Estructuras y Uniones “unnamed” (sin nombre)
// Configuración de USART0 como UART,
// 8 bits, paridad par, un bit de stop
USART0 = 0; // pone todo a cero
// setea los bits necesarios
CHAR = 1; // 8 bits
PENA = 1; // paridad habilitada
PEV = 1; // paridad par
SASE 2012
18
Estructuras y Uniones anónimas
- También son de utilidad para definir variables booleanas,
ahorrando memoria.
- Para ello se usan estructuras de campos de bits. Por ejemplo:
union {
struct {
unsigned int waitingSomeEvent: 1;
unsigned int ready4something : 1;
......
unsigned int someEvent
unsigned int anotherFlag
: 1;
: 1;
};
unsigned int flags;
};
SASE 2012
19
Estructuras y Uniones anónimas
- Poner a cero todos los flags:
flags = 0;
- Modificar o testear algún flag:
if( waitingSomeEvent && someEvent )
{
}
someEvent = 0;
SASE 2012
20
Manejo de registros mapeados en memoria usando máscaras
- Para el mismo ejemplo del registro U0CTL:
#define U0CTL (*(unsigned char *)0x0070)
#define SWRST 0x01
#define MM 0x02
......
#define CHAR 0x10
......
#define PENA 0x80
Manejo de registros mapeados en memoria usando máscaras
- Ej. poner a 1 CHAR y MM, el resto en cero:
U0CTL = CHAR | MM;
- Ej. poner a cero MM sin modificar el resto:
U0CTL &= ~MM;
Alineamiento de estructuras
- En el MSP430 (y otros micros de 16 bits), los datos de
tamaño mayor que un byte deben estar alineados en
direcciones pares.
- En los de 32 bits, se alínean en direcciones múltiplos de 4.
- El compilador agrega bytes de relleno para alinear variables
- También se hace alineamiento dentro de las estructuras.
- El acceso a variables desalineadas requiere de más
instrucciones de máquina.
SASE 2012
23
Alineamiento de estructuras
- Cuando no es admisible el relleno dentro de una
estructura (por ej. para respetar el formato de un
mensaje), se usa la directiva #pragma pack
- El acceso a una variable desalineada genera más
código de máquina y es más lento.
SASE 2012
24
Alineamiento de estructuras
- Por ejemplo, dada la siguiente estructura:
struct var{
char c; // entre c y n1
int n1; // un byte de relleno
int n2;
}v;
Veamos la operación v.n1 = w; con y sin
alineamiento
SASE 2012
25
Alineamiento de estructuras
- Con alineamiento de a 16 bits (por default):
mov.w
R10, &0x228
- Con alineamiento por byte ( #pragma pack(1) ):
mov.b R10,R14
swpb R10
and.w #0xFF,R10
mov.b R14,&0x24D ; LSB a dir impar
mov.b R10,&0x24E ; MSB a dir par
Paso de parámetros a funciones
El paso de parámetros a funciones consume tiempo
y puede consumir memoria de la pila.
Dependiendo de la arquitectura del procesador y del
compilador, los parámetros se pueden pasar en
registros y/o a través de la pila.
En los casos siguientes –sobre MSP430– se ven
ambas situaciones.
SASE 2012
27
Paso de parámetros a funciones
Una función con hasta 4 parámetros los recibe en registros
(R12, R13, R14 y R15).
Ejemplo:
void f2(unsigned char b, int n)
{
}
int a, i;
for(i=0, a=0; i<n; ++i)
a += b+i;
SASE 2012
28
Paso de parámetros a funciones
La llamada a ésta función queda así:
En C:
f2('a',32);
En lenguaje ensamblador:
mov.w #0x20,R13 ; 32
mov.b #0x61,R12 ; ‘a’
call #f2
SASE 2012
29
Paso de parámetros a funciones
Una función con más de 4 parámetros recibe 4 en registros
(R12, R13, R14 y R15) y el resto en la pila.
Ejemplo:
int f5(int a,int b,int c,int d,int e)
{
}
int suma = a + b + c + d + e;
return suma;
SASE 2012
30
Paso de parámetros a funciones
El llamado a ésta función se realiza así:
En C:
n = f5(10,20,30,40,50);
En lenguaje ensamblador:
push.w #0x32 ;el 5° a la pila
mov.w #0x28, R15 ;4° al 1° en reg.
mov.w #0x1E, R14
mov.w #0x14, R13
mov.w #0x0A, R12
call #f5
mov.w R12, R15 ; retorna en R12
Paso de parámetros a funciones
Un par de consejos prácticos serían:
• Tratar de usar la mínima de parámetros que sea necesario.
• Analizar si conviene usar variables globales:
Si bien no es una práctica recomendada en general, en
arquitecturas con poca cantidad de RAM puede ayudar a
optimizar el uso de memoria y la performance.
• Agrupar variables en estructuras y pasar como parámetro el
puntero a la estructura.
SASE 2012
32
Paso de estructuras a funciones
El paso de variables struct, ya sea como parámetro o como
valor retornado, conviene hacerlo mediante punteros.
Por ejemplo, dada la siguiente estructura y variables:
struct int5
{
};
int a, b, d, c, e;
struct int5 A, B;
33
Paso de estructuras a funciones
Si en lugar de la función f5 del ejemplo anterior se
implementara mediante otra función f5s, que espera un
puntero a struct int5, en C quedaría así:
n = f5s(&A);
Y en lenguaje ensamblador:
mov.w
#0x220,R12 ; R12: puntero a A
call
#f5s
mov.w
R12,R10 ; R12: valor retorno
SASE 2012
34
Paso de estructuras a funciones
Las estructuras son muy útiles porque permiten
agrupar variables que tienen relación entre si.
También s
Comentarios de: Programación en C para Sistemas Embebidos (con ejemplos basados en MSP430) (0)
No hay comentarios