Lenguaje C

En código C las instrucciones que se escriben se llaman “sentencias” y siempre deben finalizar en ; (que equivale al punto de un texto escrito), en un texto literario la falta del punto final no impediría su correcta lectura, pero en programación es imprescindible que esté todo escrito correctamente, por lo que la falta del punto y coma hará aparecer un error en el código (usualmente ese error aparece en la siguiente línea, así que cuando veamos un error en una línea que parezca estar bien, posiblemente se deba a la falta de “;” en la línea anterior).

El código C nos ayuda a los humanos a programar de forma más cómoda: es importante destacar que pese a que nuestros programas estén escritos en lenguaje C el compilador “traducirá” nuestro código a algo que el microcontrolador entienda (ensamblador=código máquina), y eso son unas sencillas instrucciones en unos y ceros.

Comentarios

Para poder entender mejor el código fuente usaremos frecuentemente comentarios en nuestros programas. Los comentarios son texto que ve el usuario pero Arduino ignora por completo Para ello existen 2 tipos, una línea y varias.

Comentarios de una línea

Este tipo de comentarios empieza por // y se considera comentario todo lo que venga detrás hasta un salto de línea.

Comentarios de varias líneas

Todo el texto que se “encierre” entre /* y */ se considera comentario.

Consejo: Utiliza el comentario de 1 línea siempre que sea posible, de esta forma se puede usar el de varias líneas cuando se quiere “anular” un trozo de código (por ejemplo para hacer una prueba) sin tener que borrar ese código “anulado”.

Variables

La forma habitual de crear una variable es la siguiente:

Primero se indica el tipo del datoint” (en este ejemplo un entero), después el nombre de la variable “hola” siendo sensible a mayúsculas (es decir “hola” y “Hola” son diferentes) y opcionalmente se puede asignar un valor inicial a la variable.

Pese a ser opcional, es una buena práctica la de asignar un valor inicial para evitar comportamientos inesperados en nuestros programas. Del mismo modo es muy útil poner un nombre a la variable que nos sirva para identificar qué es lo que hace.

Los tipos de variable disponibles son:

  • 8bit:
    • boolean: sólo 2 valores posibles true/false
    • byte: números sin signo entre 0 y 255 (incluidos)
    • char: números con signo entre -128 y 127, el compilador en algunas situaciones interpretará como un carácter dando resultados inesperados
    • unsigned char: se recomienda utilizar “byte” en su lugar ya que son similares
  • 16bit:
    • word: números sin signo entre 0 y 65535 (incluidos)
    • int: números con signo entre -32768 y 32767, es el tipo más utilizado
    • unsigned int: se recomienda utilizar “word” en su lugar
  • 32bit:
    • long: números con signo entre -2 147 483 648 y 2 147 483 647 (dos millardos)
    • unsigned long: números sin signo entre 0 y 4 294 967 295 (cuatro millardos), el uso más habitual de este tipo es para almacenar el valor de la función millis(), que es el número de milisegundos que lleva el programa ejecutándose.
    • float: con signo entre -3.4028235×10³⁸ y 3.4028235×10³⁸. En Arduino la coma flotante no es nativa y el compilador tendrá que hacer mucho trabajo, evitar utilizar este tipo para no comprometer el rendimiento de nuestros programas.

Una vez declarada la variable “hola” se puede consultar su valor en cualquier momento:

O modificar su valor:

Nótese que no hay que poner “int” cuando se modifique su valor.

Nota: las variables sólo son accesibles dentro de su ámbito, eso significa que si creamos una variable dentro de una función, su valor no será accesible fuera de ella. Pese a ello, se pueden crear “variables globales”, es decir que se pueden usar a lo largo de todo el programa, para hacer una variable global bastará con crearla al principio del código, fuera de las funciones loop() y setup().

Arrays

Similar a una variable están los “array” los cuales se pueden considerar como una agrupación de variables pero todos dentro del mismo elemento. Se crea de forma similar a la variable:

En la creación la diferencia con una variable está en los corchetes, que en el array es imprescindible. Durante la creación se puede limitar el tamaño de los elementos que contiene el array escribiendo dicho tamaño dentro del corchete.

Una vez que el array está creado y se quiera consultar o escribir contenido será necesario indicar dentro del corchete la posición que se está consultando/actualizando, por ejemplo:

Importante recordar que las posiciones del array empiezan en 0, es decir, la segunda posición será el número “1” dentro del corchete.
Es muy habitual utilizar un ‘for’ para recorrer todo el contenido de un array, el siguiente ejemplo pone a cero todos los valores mediante un bucle ‘for’:

Funciones

Son agrupaciones de sentencias y se utilizan para ejecutar “bloques” de código. Su función habitual es para no repetir un trozo de código varias veces, o para simplificar la lectura del código. Las funciones pueden recibir valores y pueden devolver valores, aunque no es obligatorio.

Las funciones que no devuelven valores son declaradas con un “void” delante:

Por ejemplo una función “Suma()” necesitaría recibir los 2 números y devolvería el resultado de la operación. Así que cuando sí devuelven un valor se declaran precedidas del tipo de dato que tiene dicho valor, por ejemplo:

Para ejecutar una función se escribirá su nombre seguida de un paréntesis, si necesita valores se introducirán en dicho paréntesis, si devuelve un valor se trata como una variable, es decir se asume que Suma() tiene un valor como si se tratase de una variable:

Cuando se “pasan” valores a una función éstos han de ser del mismo tipo que el que la función espera recibir, en nuestro ejemplo “sensor1” y “sensor2” tienen que ser de tipo entero (int) porque “valorA” y “valorB” están declarados como enteros dentro de la función.

Otro ejemplo, con valores preestablecidos:

Los nombres de las funciones (al igual que ocurría con las variables) son sensibles a mayúsculas: “MiFuncion()” es diferente de “mifuncion()“, “MIFUNCION()” o de “MiFuNcIoN()“.

En la medida de lo posible, siempre se usará un nombre en una función que explique qué es lo que hace. En nuestro ejemplo “Suma()” resume perfectamente lo que hace la mencionada función.

Sentencia condicional

Es un grupo de instrucciones que se ejecutarán sólo en el caso de que se cumpla una serie de condiciones.

Por ejemplo si quisiéramos escribir en el puerto serie si un número es negativo o positivo sería (asumiendo que “número” es un valor que cambia a lo largo del programa):

Tanto el “else if” como el “else” son opcionales, pero muy utilizados. “else if” sirve para añadir condiciones que no cumplan el primer “if” y finalmente las situaciones que no se han satisfecho son contempladas en el “else”.

Operadores de comparación

Existe una serie de operadores para cuando se quieren comparar 2 valores, el resultado de la comparación sólo puede ser TRUE (en caso de cumplirse) o FALSE en caso de no ser válida.

  • == “Igual que”: ambos valores serán idénticos (ojo, es diferente al “=” ).
  • != “Distinto de”: los valores no son iguales (es decir, son diferentes).
  • < “Menor que”: el valor de la izquierda es menor que el de la derecha.
  • <= “Menor o igual”: similar al anterior, pero incluye el caso de que sean iguales.
  • > “Mayor que”: el valor de la izquierda es mayor del de la derecha.
  • >= “Mayor o igual”: similar al anterior incluyendo el caso de ser idénticos.

Es importante que los valores a comparar sean del mismo tipo, pues si se compara un “int” con un “byte” posiblemente sucedan comportamientos inesperados.

Operadores lógicos

Los operadores lógicos devolverán también el resultado TRUE/FALSE y son “&&” para el operador AND y “||”para el operador OR.

Son útiles para hacer cumplir 2 condiciones, por ejemplo:

Los paréntesis no son obligatorios, pero sí altamente recomendables para entender mejor el código.

Switch

Su funcionamiento es equivalente al anidamiento de diversos “else if” pero permite al humano programador crear programas más legibles. Se utiliza cuando se precisan muchos tipos de acción ante diversos valores iniciales. Pese a que Switch sólo permite comparar con valores exactos de la variable es bastante habitual dicho tipo de comparación.

Al igual que ocurre en el “if”, si al final de las comparaciones no se han satisfecho ninguna de las comparaciones se ejecutará un bloque de código denominado “switch”.

Su forma es (en el ejemplo XXXX YYYY ZZZZ serían equivalentes a números enteros):

Como se puede ver consiste en un “switch (VAR)” y diferentes “case X:” cerrado por “break;”, siendo imprescindible este último break; para que el siguiente “case X:” sea válido.

Al final un “default:” para las situaciones que no se han satisfecho con los “case X:” precedentes, pese a ser no ser obligatorio puede estar vacío y no tener instrucciones asociadas aunque no se recomienda, pues se supone que “switch” se usa cuando se necesita que “ocurra algo” e incluso aunque se contemplen todas las posibles combinaciones es bueno tenerlo preparado para errores inesperados.

Imaginemos que disponemos de un selector que nos da 4 valores posibles (0 a 3) y queremos actuar sobre un motor en función de qué opción hemos elegido, podríamos utilizar un “if” para ello, pero también el “switch” nos permite realizar las mismas operaciones:

Esta forma nos permitiría de forma fácil añadir nuevas funcionalidades en el código, pues con añadir “case 2:” no sería necesario modificar el hardware simplemente añadir unas pocas líneas nuevas de código.

Bucles

Los condicionales servían para tomar decisiones, pero cuando es necesario repetir varias veces las mismas operaciones existen los bucles que consisten en estar ejecutando el código que tengan dentro hasta que se cumpla una condición.

while

En este caso estará ejecutándose el código hasta que la condición se vuelva “FALSE” son muy útiles cuando queremos esperar a que “ocurra” algo antes de seguir ejecutando más programa, típicamente esperar a que un botón sea pulsado para empezar a funcionar, por ejemplo:

Un error común con los bucles “while” es no actualizar el estado de la variable usada en la condición dentro del propio “while” dando lugar a bucles infinitos (es decir un bucle que nunca termina y por lo tanto bloqueando el resto del código).

for

Pese a que el funcionamiento es similar al “while”, el “for” se usa habitualmente cuando queremos repetir ‘n’ veces una porción de código, ya que este tipo de bucle tiene 3 “parámetros” de inicialización: inicialización, comparación e incremento:

  • Variable/s: Aquí se inicializa la variable que cambia en cada iteración.
  • Comparación: Cada vez que sea “FALSE” volverá a ejecutarse el bucle, si la comparación se volviese “TRUE” se terminaría la ejecución del bucle.
  • Incremento: Cuando la comparación fue “FALSE” se cambiará el valor de la variable inicial (de esta forma, en algún momento la comparación se volverá verdadera).

En este ejemplo se mostrará por puerto serie 10 veces un texto:

Viéndose por consola:

Nótese que la comparación se ha hecho con el número 10, pero en realidad sólo se muestra hasta el número 9. Es un error muy común con el bucle “for”: cuando se cumpla la condición el código no se ejecuta. Para solucionar la duda de si hay que poner 11 para que muestre el número 10 es más sencillo utilizar “<=” en lugar del “<”, de esta forma está contemplado el valor escrito.

Add a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *