Introducción al Lenguaje C para MCUs.

Anuncio
LENGUAJE C
PARA SISTEMAS DEDICADOS
FUNDAMENTOS
Computadora
z
Se dispone de un S.O.
z
El S.O. inicia y configura los periféricos.
z
El S.O. brinda al usuario subrutinas para utilizar los
periféricos ( system calls ).
Microcontrolador
z
No posee un S.O.
z
El firmware debe iniciar los periféricos.
z
El usuario debe crear sus propias subrutinas para
utilizar los periféricos.
En lenguaje C para sistemas
dedicados no podemos hacer:
printf(“Hola Mundo”);
Llamada a la función main
zA
pesar que main es el punto de entrada
al programa, el firmware realiza tareas
previas a main. Entre ellas.
Cargar registros de configuración.
z Limpiar zonas de RAM.
z Cargar el puntero de pila SP.
z
Código en ensamblador
ORG
$EE00
…configuración de los periféricos…
….
BRA
$
Código en C
ORG
$EE00
…código de inicialización…
CALL/JMP
void
{
main
main(void)
…código escrito por el usuario…
while(1)
{
}
}
Recursos
Arquitecturas de memoria
Modelo tiny ( Freescale )
MOV PORTA,PORTB
Modelo small ( Freescale )
LDA PORTA
STA var1
Pasaje de parámetros
Al igual que en un computadora, la pila se
utiliza para el pasaje de parámetros:
int
funcion(char a, int b, float c,….)
Ejemplo de pasaje de parámetros
usando la pila
void main(void)
{
int
dunga, donga;
dunga = 3;
donga = 5;
dunga = suma(dunga, donga);
donga = resta(dunga, donga);
}
int
{
sum(int s1, int s2)
return s1 + s2;
}
int
{
resta(int r1, int r2)
return r1 – r2;
}
Pasaje de parámetros usando la pila
Ventajas:
z Al retornar de la
función se libera el
espacio de memoria.
Desventajas:
z Llamados anidados
a funciones pueden
limitar la RAM libre.
Uso del heap
Se hace declarando variables estáticas
externas, mas conocidas como
VARIABLES GLOBALES.
Ejemplo de pasaje de parámetros
usando el heap
iInt dunga, donga;
void main(void)
{
dunga = 3;
donga = 5;
suma();
resta();
}
int
{
suma(void)
dunga = dunga + donga;
}
int
{
resta(void)
dunga = dunga - donga;
}
Pasaje de parámetros usando el
heap
Desventajas:
Ventajas:
z Pueden anidarse los
z La memoria usada
llamados a funciones
en el heap no puede
sin que crezca la pila
liberarse.
enormemente.
Uso de las funciones recursivas
void
factorial(int n)
{
if(n == 1)
return 1;
else
return factorial(n-1) *
n;
}
factorial(8);
Variables
Tipos de datos básicos
En PC:
z char: 1 byte
z int: 2 ó 4 bytes
z float: 4 bytes
En microcontroladores:
z char: 1 byte
z int: 2 bytes
z float: 4 bytes
Variantes de los tipos básicos
z Se
admiten los modificadores signed y
unsigned.
z También los modificadores short y long.
z El float puede no estar contemplado en
versiones gratuitas de los compiladores.
Operaciones básicas
Aritméticas
z Suma +
z Resta –
z Multiplicación *
z División /
z Resto %
Lógicas
z AND &
z OR |
z XOR ^
No necesariamente existe en
ensamblador una instrucción
para realizar cierta operación
aritmética. Ej: Los PIC 16F no
cuentan con instrucciones de
multiplicación.
En ese caso el compilador debe
generar un algoritmo en assembler
que ejecute esa operación.
Alternativas al tipo float
z En
general queremos representar cifras
del tipo: 20,3 mV
z Se trata de un número decimal de punto
fijo.
z Tampoco se dispone de representación
en punto fijo.
Solución:
Trabajar las mangintudes
multiplicadas x10, x100, …
La cifra 20,3 se almacena como
203. Así puede contenerse en un
tipo int.
Manejo de bits como variables
z Es
válida aplicar una asignación a un bit.
z Son válidas las operaciones lógicas
entre bits |, & y ^.
z Ejemplos:
Freescale:
PTAD_PTAD4 = 1;
z PIC:
PORTBbits.RB4 = 0;
z
Volatile
Código en C
void findecuenta(void)
{
…
if(time == 100)
{
time = 0;
…
}
}
z
z
Posible compilación en
ensamblador:
findecuenta:
LDA
time
…
CMP
#!100
BNE
…
CLR
time
…
Volatile
z Si
time no cambia antes del if, la
compilación anterior es válida.
z Si time proviene del módulo timer puede
modificarse entre que es cargada y el if.
Solución
z
Código en C:
volatile
int time;
z
Compilación en
ensamblador:
void findecuenta(void)
{
…
if(time == 100)
{
time = 0;
…
}
}
findecuenta:
…
LDA
CMP
BNE
CLR
time
#!100
…
time
Reseña de punteros
z Se
declaran y usan de la forma
convencional:
int *p;
char *p;
int hola;
p= &hola;
*p = 3;
( hola = 3 )
Punteros a RAM
int
int
hola;
*p;
p = &hola;
*p = 3;
p apunta a una posición en RAM.
Punteros a ROM
char
char
*p;
texto[20] = “Hola mundo”;
p = texto;
p apunta a una posición en ROM (FLASH).
Arquitectura Von Neumann
( Freescale )
La RAM y ROM están en un
mismo mapa. Pueden accederse
por una única clase de punteros.
Arquitectura Harvard ( PIC )
La RAM y ROM están en mapas
diferentes. Se deben indicar a que
mapa apunta el puntero.
Punteros en micros Hardvard
z
Punteros a RAM:
char
hola;
char
*p;
p = &hola;
z
Punteros a ROM:
const char rom
char rom *p;
p = texto;
texto[20] = “me duermo”;
Entrada y salida
En la computadora
z Acceso
al I/O de entrada:
a = getch();
z Acceso al I/O de salida:
printf(“Hola mundo”);
Sistema dedicado
z Crear
bibliotecas de funciones:
LCD_printf(char *texto);
LCD_Dato(char caracter);
LCD_gotoxy(char x, char y);
sprintf resuelve todo
z
Genera textos.
z
z
Convierte datos int a
BCD.
Convierte datos float
a BCD.
Permite generar
salidas en formatos
muy complejos.
z
z
z
z
z
sprintf(texto,
“Chau”);
sprintf(texto, “%d”,
a);
sprintf(texto, “%f”, d);
sprintf(texto, “V =
%02.1f mV”, voltios);
Uso de los puertos de E/S
z
Acceso a pines individuales:
struct
{
byte
byte
byte
byte
byte
byte
byte
byte
}Bits;
PTAD0:1
PTAD1:1
PTAD2:1
PTAD3:1
PTAD4:1
PTAD5:1
PTAD6:1
PTAD7:1
Bits.PTAD0 = 1;
Mas sobre puertos
z
Acceso a todo el puerto:
typedef
union
{
byte
Byte;
struct
{
byte
byte
byte
byte
byte
byte
byte
byte
}Bits;
}PTADSTR;
PTAD0:1
PTAD1:1
PTAD2:1
PTAD3:1
PTAD4:1
PTAD5:1
PTAD6:1
PTAD7:1
PTADSTR.Byte = 0xff;
PTADSTR.Bits.PTAD0 = 1;
PTAD_PTAD0 = 1;
Manejo de periféricos
z En
PC usando system calls.
z En microcontroladores:
z
Freescale:
Asistentes de configuración ( Processor Expert )
z Funciones de biblioteca.
z
z
En PIC:
z
Bibliotecas.
Processor Expert
z
Permite configurar
cualquier módulo del
microcontrolador.
Processor Expert
z
Pueden configurarse
todos los parámetros
del módulo.
Processor Expert
z
z
z
Genera el código de
inicialización
Genera funciones
para el manejo de
módulo.
Genera vectores de
interrupción.
Bibliotecas para PIC
z
z
z
z
z
z
A/D
Comparador
EEPROM
I2C
PWM
…
Temporización
Demoras cortas
z
Intercalando código en ensamblador:
unsigned char
contador_1;
asm
{
LDA #$32
STA contador_1
dem_100us_1:
NOP
NOP
NOP
DBNZ
contador_1,dem_100us_1
}
Ttotal = 50 x 2µs = 100µs
Tint = 10 ciclos x 200ns = 2µs
Demoras largas
z
Intercalando código en ensamblador:
unsigned char contador_1;
unsigned char contador_2;
unsigned char contador_3;
for(contador_3 = 0; contador_3 < 100;
contador_3++)
for(contador_2 = 0; contador_2 < 20;
contador_2++)
{
asm
{
LDA #$C8
STA contador_1
dem_1s_1:
NOP
NOP
NOP
DBNZ contador_1,dem_1s_1
}
}
Ttotal = 100 x 10ms = 1s
Text = 20 x 500µs = 10ms
Tint = 250 x 2µs = 500µs
Demoras largas
z
Módulo timer con interrupciones:
void
main(void)
{
TI1_SetPeriodSec(1);
}
ISR(TI1_Interrupt)
{
(void)TPM1C0SC;
TPM1C0SC = 0x80;
(código del usuario)
}
Código bloqueante
Durante el llamado a una función el programa
no continúa su ejecución hasta retornar de
ella.
Ej:
z
void main(void)
{
demora_seg(1);
}
Código no bloqueante
z
El programa puede continuar con otras tareas mientras una función no
entregue los resultados deseados.
Ej:
void
{
…
}
main(void)
ISR(TI1_Interrupt)
{
(void)TPM1C0SC;
TPM1C0SC = 0x80;
(código del usuario)
}
Timer tick de la PC
z
z
z
Produce
interrupciones cada
1ms.
El usuario puede
generar eventos por
timer tick.
El usuario no debe
atender la
interrupción.
void __fastcall TForm1::Timer1Timer(TObject
*Sender)
{
( código del usuario )
}
Eventos de timer con
microcontroladores
z
z
z
La velocidad del
timer tick es
determinada por el
firmware.
Pueden generarse
eventos por timer
tick.
El usuario no debe
atender la
interrupción.
void TI1_OnInterrupt(void)
{
( código del usuario )
}
Interrupciones
Arquitectura PIC
z
z
Existen 1 ó 2 vectores
de interrupción.
El usuario debe
determinar con un if que
periférico disparó la
interrupción.
#pragma code low_vector=0x18
void interrupt_at_low_vector(void)
{
_asm
GOTO low_isr
_endasm
}
#pragma code
#pragma interruptlow low_isr
void low_isr(void)
{
}
Arquitectura Freescale
z El
Processor Expert resuelve buena
parte del código.
z Existen vectores independientes para
cada evento y periférico.
z El usuario no debe atender una
interrupción. El Processor Expert genera
el código que lo hace.
Evento de conversión A/D
word AD1_OutV;
ISR(AD1_Interrupt)
{
((TWREG*)(&AD1_OutV))->b.high = ADC1RH;
((TWREG*)(&AD1_OutV))->b.low = ADC1RL;
OutFlg = TRUE;
AD1_OnEnd();
ModeFlg = STOP;
}
void AD1_OnEnd(void)
{
unsigned int medicion;
}
¡CHAU!
Descargar