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!