Este artículo le ayudará a comprender qué son las funciones, por qué se utilizan y cómo se implementan en el hardware integrado.
Cada programa C tiene una función main (). Ciertamente, es posible escribir un programa exitoso en el que la única función sea main (). Mi conjetura es que esto se ha hecho muchas veces, y es cierto que en ciertas aplicaciones simples no se necesita ninguna otra función.
Sin embargo, el uso extenso de funciones es una indicación de que la persona que escribe el código es un desarrollador de firmware con experiencia. ¿Por qué? Porque las funciones nos permiten escribir mejor código más rápido, con menos trabajo y menos errores. Para aquellos que pasan una parte significativa de su firmware de escritura de la vida profesional, estas son las ventajas que no se pueden ignorar. Incluso si inicialmente nos resistimos al uso de funciones porque parecen requerir más trabajo, la experiencia nos enseña gradualmente que los beneficios superan con creces los costos.
Una función C es un grupo de instrucciones que trabajan juntas para implementar un tipo específico de actividad de procesador. En muchos casos, una función realizará una tarea específica, como recuperar datos de un búfer SPI, configurar un temporizador para que genere un retraso específico, o leer un valor de la memoria y cargarlo en un registro DAC.
Sin embargo, no hay ninguna ley que establezca que una función puede realizar una sola tarea. Puede resultarle conveniente tener una función que actualice tres máquinas de estado no relacionadas, o podría escribir una función que transmita un byte a través de UART, luego verifica el bit de estado hasta que se recibe un byte, y luego incorpora el valor del byte recibido en algunos cálculos matemáticos.
Una función consta de un nombre, una lista de parámetros de entrada, las declaraciones de código que implementan la funcionalidad requerida y un tipo de retorno.
El siguiente fragmento de código le da un ejemplo.
char Convert_to_Lowercase (char UppercaseLetter)
{
if (mayúscula 90)
devuelve 0x00;
más
return (UppercaseLetter + 32);
}
Me gusta hacer que los nombres de mis funciones sean muy descriptivos. Esto hace que el código sea más legible y le ayuda a mantener sus pensamientos organizados.
Las instrucciones están entre corchetes; esta parte de la definición de la función se llama el cuerpo de la función. La palabra clave "retorno" se usa para salir de la función e identificar qué datos deben enviarse a la parte del código que se ejecuta anteriormente.
El tipo de retorno, colocado antes del nombre de la función, identifica el tipo de datos de la información que se devolverá. Es perfectamente aceptable tener una función que simplemente realice una tarea, sin necesidad de devolver datos. En este caso, usaría la palabra clave "vacío" en lugar de un tipo de datos.
Los parámetros de entrada, también llamados argumentos, se incluyen entre paréntesis y se colocan después del nombre de la función. Una función C puede tener múltiples argumentos, en cuyo caso están separados por comas. Cada argumento debe ir acompañado de un tipo de datos.
En aplicaciones integradas, a menudo no es necesario utilizar argumentos. Puedo pensar en dos razones para esto.
Primero, el firmware incorporado interactúa con frecuencia directamente con el hardware del dispositivo y, por lo tanto, una función puede obtener la información que necesita de los registros de configuración, los registros de comunicación o los pines del puerto.
En segundo lugar, los programas de C simples escritos para microcontroladores pueden usar variables globales, es decir, variables que están presentes en todo el programa y pueden ser accedidas por cualquier función. Como lo entiendo, se desaconseja el uso de variables globales en la programación de aplicaciones, o tal vez incluso "condenado" sería la palabra adecuada. Pero en mi opinión, muchos proyectos de firmware, especialmente aquellos escritos completamente por un programador, pueden beneficiarse de la simplicidad de las variables globales.
Al definir una función que no tiene argumentos, puede dejar los paréntesis vacíos o insertar la palabra clave "vacío". En teoría, el enfoque "vacío" es mejor que los paréntesis vacíos, pero en el contexto del desarrollo integrado, especialmente teniendo en cuenta lo ingeniosos que son los compiladores modernos son, no sé cuánto realmente importa.
Examinemos brevemente la definición de función que se muestra arriba.
Al igual que la memoria de datos de un procesador no admite directamente los detalles adjuntos a una variable C, la memoria de código de un procesador es mucho más simple que una función C. La memoria de código es una secuencia larga de ubicaciones de almacenamiento que no están categorizadas u organizadas de ninguna manera útil. Lo único que identifica una ubicación particular en la memoria de código es la dirección.
Una función C, entonces, es una forma elaborada y fácil de programar para colocar bloques de código en la memoria y dirigir el procesador al bloque que debe ejecutarse. Si ha trabajado con lenguaje ensamblador, está familiarizado con la realidad de bajo nivel de la ejecución del código: cada instrucción tiene una dirección. Usamos una etiqueta de texto para representar una dirección dada, y si queremos que el procesador ejecute las instrucciones en esta dirección, le decimos que salte a la etiqueta.
Las funciones C son una mejora importante con respecto a las subrutinas básicas utilizadas en el lenguaje ensamblador, pero no son fundamentalmente diferentes. Cuando llama, o "invoca" a una función, el contador del programa del procesador recibe la dirección de la primera instrucción en lenguaje de máquina asociada con esa función.
Una sección de la memoria conocida como pila de llamadas se usa para almacenar la dirección de la memoria de código a la que el procesador debería regresar después de que se haya ejecutado la función. La pila también proporciona ubicaciones de memoria para las variables locales, es decir, las variables que se crean cuando se llama a la función y se usan solo dentro de la función.
La pila de llamadas puede servir como un lugar para almacenar datos que se pasan a la función como un parámetro de entrada, pero, según tengo entendido, los compiladores utilizarán registros para esto en lugar de memoria siempre que sea posible (porque los registros son más rápidos).
Espero que este artículo le haya dado una buena visión general de la estructura y el comportamiento de una función, tanto dentro de un programa C como en el hardware real del procesador. Continuaremos nuestra discusión de las funciones de C en el próximo artículo.
ga('create', 'UA-1454132-1', 'auto'); ga('require', 'GTM-MMWSMVL'); ga('require', 'displayfeatures'); ga('set',{'dimension1':'embedded,computing,memory,embedded-software,arduino'}); ga('set',{'contentGroup1':'embedded,computing,memory,embedded-software,arduino'}); ga('send', 'pageview');
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, document,'script','https://connect.facebook.net/en_US/fbevents.js'); fbq('init', '1808435332737507'); // Insert your pixel ID here. fbq('track', 'PageView'); fbq('track', 'ViewContent', { content_ids: ['embedded','computing','memory','embedded-software','arduino'], content_type: 'category'});
_linkedin_data_partner_id = "353081"; (function(){var s = document.getElementsByTagName("script")[0]; var b = document.createElement("script"); b.type = "text/javascript";b.async = true; b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js"; s.parentNode.insertBefore(b, s);})(); } if(jstz.determine().name().indexOf("Europe") === -1) { showSocialCode(); // NOT EU } else { showSocialCode(); window.addEventListener("load", function () { window.cookieconsent.initialise({ "palette": { "popup": { "background": "#252e39" }, "button": { "background": "#14a7d0" } }, "type": "opt-out", "content": { "message": "This website uses tracking cookies to ensure you get the best experience on our website.", "href": "https://www.allaboutcircuits.com/privacy-policy/", "dismiss": "OK, GOT IT" }, onInitialise: function (status) { var type = this.options.type; var didConsent = this.hasConsented(); if (type == 'opt-out' && didConsent) { console.log("eu"); //showSocialCode(); } },
onStatusChange: function (status, chosenBefore) { var type = this.options.type; var didConsent = this.hasConsented(); if (type == 'opt-out' && didConsent) { console.log("eu"); //showSocialCode(); } },
onRevokeChoice: function () { var type = this.options.type; if (type == 'opt-out') { console.log("eu"); //showSocialCode(); } },
}) }); }
Los días felices de la PDA y Blackberry han quedado definitivamente atrás, pero el factor…
Tutorial sobre cómo pronosticar usando un modelo autorregresivo en PythonFoto de Aron Visuals en UnsplashForecasting…
Si tienes un iPhone, los AirPods Pro son la opción obvia para escuchar música, ¡aunque…
Ilustración de Alex Castro / The Verge Plus nuevos rumores sobre el quinto Galaxy Fold.…
Se rumorea que los auriculares premium de próxima generación de Apple, los AirPods Max 2,…
El desarrollador Motive Studio y el editor EA han lanzado un nuevo tráiler de la…