Conceptos Esenciales de Programación en C: Parámetros, Almacenamiento y Gestión de Interrupciones
Clasificado en Informática
Escrito el en español con un tamaño de 9,09 KB
Paso de Parámetros a Funciones en C
El paso de parámetros a funciones en C es fundamental para la comunicación entre diferentes partes de un programa. Existen dos mecanismos principales:
Paso por Valor
En el paso por valor, se crea una copia de los valores de los parámetros en la pila de la función. Esto significa que cualquier modificación realizada a los parámetros dentro de la función no afectará a las variables originales fuera de ella. Las funciones solo pueden devolver un resultado simple a través de la instrucción
return
.int doble(int dato) { int resultado; resultado = 2 * dato; return resultado; } void main() { int x, y; x = 3; y = doble(x); // 'y' será 6, 'x' sigue siendo 3 }
Paso por Dirección o Referencia
En el paso por dirección (o referencia), se utilizan punteros para acceder y modificar directamente las variables originales que se encuentran fuera del ámbito de la función. Esto permite que la función altere el valor de las variables pasadas como argumento.
void duplicar(int* pDato) { *pDato = *pDato * 2; } void main() { int x; x = 3; duplicar(&x); // Se pasa la dirección de 'x'. Tras esta sentencia, 'x' tendrá el valor 6. }
Clases de Almacenamiento en C
En C, las clases de almacenamiento determinan el ámbito, la duración y la ubicación de las variables en la memoria. Las principales clases son:
extern
Se utiliza para declarar una variable global que ha sido definida en otro módulo fuente. Permite que una variable sea accesible desde múltiples archivos de código fuente, promoviendo la compartición de datos entre módulos.
static
El modificador
static
tiene diferentes comportamientos según el contexto:- Si es una variable global, su ámbito se restringe al módulo fuente donde está definida. Solo puede accederse desde instrucciones dentro de ese mismo archivo.
- Si es una variable local a una función, solo es visible dentro de esa función. Sin embargo, a diferencia de las variables locales automáticas, tiene almacenamiento estático, lo que significa que no se destruye al finalizar la función y mantiene su valor entre llamadas sucesivas a la función. Por defecto, las variables estáticas se inicializan a 0.
register
Se sugiere al compilador que almacene la variable en un registro de la CPU en lugar de la memoria principal. Esto puede acelerar significativamente las operaciones de lectura y escritura, ya que el acceso a los registros es mucho más rápido. Es una sugerencia, y el compilador puede ignorarla si no hay registros disponibles o si considera que no es óptimo.
volatile
Indica al compilador que el valor de la variable puede ser modificado por causas ajenas al programa (por ejemplo, por hardware, interrupciones o hilos concurrentes). Esto evita que el compilador realice optimizaciones que podrían asumir que el valor de la variable no cambia entre accesos, asegurando que cada lectura de la variable se realice directamente desde la memoria.
Comunicaciones: Interrupción vs. Consulta (Polling)
Existen dos enfoques principales para gestionar la comunicación con periféricos o eventos externos en un microcontrolador:
Comunicación por Consulta (Polling)
En el modelo de consulta (o polling), el microcontrolador debe verificar repetidamente el estado de un periférico o un "flag" para determinar si un evento ha ocurrido (por ejemplo, si un byte ha sido enviado o recibido). Esto implica que, cada vez que se quiera enviar un byte, el microcontrolador debe esperar a que el envío anterior finalice. De manera similar, para recibir un byte, debe consultar constantemente un indicador de llegada, bloqueando el programa en esta comprobación y sin poder realizar otras tareas. Estas esperas activas conllevan una pérdida de tiempo significativa, durante la cual el microcontrolador podría estar ejecutando otras operaciones útiles.
Comunicación por Interrupción
La programación de comunicaciones mediante interrupciones ofrece una alternativa más eficiente. Permite iniciar el envío de un byte en cualquier momento y, acto seguido, el microcontrolador puede continuar realizando otras tareas. Cuando el periférico está listo para el siguiente envío o ha recibido un byte, genera una interrupción. Esta señal detiene temporalmente la ejecución del programa principal y transfiere el control a una rutina de atención a la interrupción (ISR) específica. Solo entonces se ejecuta el código necesario para atender el evento (enviar el siguiente byte o procesar el recibido), y una vez finalizada la ISR, el programa principal reanuda su ejecución desde donde se quedó. Esto optimiza el uso del tiempo del microcontrolador, permitiendo la ejecución concurrente de tareas.
Orden de Bytes en Memoria: Big-Endian y Little-Endian
Cuando un tipo de variable (como un entero de 16 o 32 bits) requiere más de un byte para su almacenamiento en memoria, es crucial definir cómo se organizarán estos bytes. Los diferentes bytes que componen la variable se almacenarán en posiciones consecutivas de la memoria, y a la variable se le asignará la dirección del primer byte.
Existen dos esquemas principales para esta organización:
Little-Endian
En la organización Little-Endian, el primer byte (la dirección de memoria más baja) almacena el byte menos significativo (LSB) de la variable completa, y los bytes subsiguientes almacenan los bytes de mayor significancia, hasta que el último byte (la dirección de memoria más alta) contiene el byte más significativo (MSB).
Big-Endian
Por el contrario, en la organización Big-Endian, el primer byte (la dirección de memoria más baja) almacena el byte más significativo (MSB) de la variable, y los bytes subsiguientes almacenan los bytes de menor significancia, hasta que el último byte (la dirección de memoria más alta) contiene el byte menos significativo (LSB).
Uso de overlay
en Compiladores (MPLAB C18 para PIC)
El modificador overlay
, específico de algunos compiladores como MPLAB C18 para microcontroladores PIC, se utiliza para indicar al compilador que comparta la misma región de memoria para almacenar variables declaradas con este modificador. Esta técnica es una forma de optimización del uso de memoria, lo cual es particularmente crítico y necesario en arquitecturas con recursos de memoria muy limitados, como los microcontroladores.
Las variables declaradas con el modificador overlay
tendrán un almacenamiento estático, lo que significa que su espacio en memoria se asigna una vez y persiste durante toda la ejecución del programa, pero ese espacio puede ser reutilizado por otras variables overlay
en diferentes momentos de la ejecución, siempre y cuando no se necesiten simultáneamente.
Prioridad de Rutinas de Interrupción en C (Ejemplo PIC18F452)
En microcontroladores como el PIC18F452, se implementan sistemas de interrupción con diferentes niveles de prioridad para gestionar eficientemente múltiples eventos asíncronos. Específicamente, existen dos tipos de interrupciones:
Interrupciones de Alta Prioridad
Cuando ocurre una interrupción de alta prioridad, el control del programa se traslada automáticamente a la dirección de memoria de programa
0x0008
.Interrupciones de Baja Prioridad
Si se produce una interrupción de baja prioridad, el control se transfiere a la dirección de memoria de programa
0x0018
.
Para gestionar estas interrupciones de manera organizada, se utiliza la directiva de compilación #pragma code
. Esta directiva permite crear una nueva sección de código y especificar la dirección física exacta en la memoria de programa donde se desea almacenar ese código. De esta forma, es posible insertar un pequeño fragmento de código (un "salto" o jump) en las direcciones a las que el microcontrolador salta automáticamente al producirse una interrupción. Este código de salto, a su vez, redirige la ejecución a la rutina de atención a la interrupción (ISR) específica y más extensa que manejará el evento particular.