ANSI C

Contenido

LENGUAJE C ANSI

Ventajas

Desventajas

Los tipos de datos

MODIFICADORES ESTABLECIDOS POR LA NORMA ANSI

CUALIFICADORES ANSI

OPERADORES

DIVISIONES ENTERAS VS REALES

OPERADORES DE MANEJO DE BITS

OPERADORES LÓGICOS Y RELACIONALES

FUNCIONES

ESTRUCTURAS DE CONTROL

PUNTEROS A MEMORIA

DIRECTIVAS

ENSAMBLADOR EMPOTRADO

ESTRUCTURAS

UNIONES

MACROS

OPTIMIZACIÓN DEL CÓDIGO C

Programación orientada a objetos

¿Cómo definimos un objeto?

Recomendaciones generales de optimización en C

 

 LENGUAJE C ANSI

Ventajas:

  • Desarrollo de aplicaciones más rápido
  • Programación más cómoda, disponibilidad de funciones de manejo de módulos internos
  • Mantenimiento menos costoso

Desventajas:

  • Código menos eficiente
  • Mayor ocupación de memoria

Cosas que se requieren en un lenguaje de programación para microcontroladores:

  • Acceso directo a la memoria y al hardware, programación de periféricos
  • Llamar a rutinas en ensamblador o insertar código máquina
  • Conexión directa con las interrupciones
  • Generación de código eficiente en ocupación de memoria y velocidad de ejecución

Entonces, el objetivo de éste tema es aprender a hacer un código más eficiente y algunos trucos que servirán para tener acceso directo a funciones, memoria, periféricos y nuevos tipos de variable.

Los tipos de datos

Sus tamaños y valores, ya conocidos por típicos, son los siguientes:

Tipo Tamaño (bits) Valor mínimo decimal Valor máximo decimal
Char 8 -128 128
Unsigned char 8 0 255
Int 16 -32768 32767
Unsigned int 16 0 65535
Short long 24 -8388608 8388607
Unsigned short long 24 0 16777215
Long 32 -2147483648 2147483647
Unsigned long 32 0 4292967295

 

Y con flotantes:

Tipo Tamaño (bits) Exponente mínimo Exponente máximo
Float 32 -126 128
Double 32 -126 128

 

Una variable contiene un dato que puede modificarse, la declaración incluye:

  • Nombre: máximo 8 caracteres, iniciando con letra y que no sea palabra reservada
  • Tipo
  • Ambito: global, local o externa

 

 

MODIFICADORES ESTABLECIDOS POR LA NORMA ANSI

Se escriben delante del tipo

  1. Auto:
    - Las variables declaradas fuera de las funciones son globales
    - Las declaradas en funciones son locales. El ámbito local tiene prioridad.
    - Si no se inicializa el valor es indefinido
    - Las globales se comportan como estáticas
  2. Extern:
    - Indica que la variable declarada pertenece a otro módulo, por lo que no es necesario reservarle memoria
    - Dentro de un mismo fichero fuente sirve para acceder a una variable aunque todavía no se haya llegado a su declaración
    - Se autoinicializan a cero
  3. Register:
    - Se debe guardar en uno de los registros del procesador
    - Si no es posible, se comporta como Auto
    - Se usa para optimizar el tiempo de ejecución de algunas funciones
  4. Static:
    - Variables locales a una función que retienen su valor en llamadas sucesivas a dicha función
    - Hay que poner static delante de la declaración
    - Se inicializan a cero
    - Ahorra el número de instrucciones para acceder a las variables
  5. Typedef:
    - Asigna un nuevo nombre a un tipo de datos definido por el programador

 

CUALIFICADORES ANSI

  • Const: el contenido de la variable es fijo
  • Volatile: el contenido de la variable puede cambiar
  • RAM: la variable se situa en la memoria de datos
  • ROM: la variable está en la memoria de programa, y se puede asignar cerca o lejos

Ejemplos:

  • Variable en memoria de datos: char data;
  • Variable en código cercano: rom near char data;
  • Variable en código lejano: rom far char data;

CASTING: mecanismo usado para cambiar el tipo de expresiones y variables a = (int)b;

 

 

OPERADORES

Operador Operación aritmética
+ Suma
- Resta
* Multiplicación
/ División
% Módulo
++ Incremento
-- Decremento
= Asignación
Formas reducidas Equivalencia
a+= b a = a+b
A*=b A = a*b

 

DIVISIONES ENTERAS VS REALES

A veces puede ocurrir que una división de números enteros con resultado real se guarda en entero, esto se puede evitar añadiendo .0 a uno de los números, por ejemplo:

  • 4 / 3 = 1 porque el resultado se da en entero
  • 0 / 3 = 1.333 ya está el resultado en real
  • 4 / 3.0 = 1.333
  • 0 / 3.0 = 1.333

OPERADORES DE MANEJO DE BITS

Operador Operación con bits
& AND
| OR
^ XOR
~ NOT
>> Desplazamiento a derecha
<< Desplazamiento a izquierda

 

OPERADORES LÓGICOS Y RELACIONALES

Operador Operación lógica o relacional
== Igual
!= Distinto
> Mayor
>= Mayor o igual
< Menor
<= Menor o igual
&& And
|| Or
! Not

 

FUNCIONES

  • Asociadas a eventos: se incluye el código que se ejecuta cuando se produzca un evento, por ejemplo una interrupción
  • De propósito general: se usan cuando una parte del código se repite varias veces en el programa, haciendo una llamada a la subrutina o función

 

Se componen de tipo, nombre, paso de parámetros (opcional) y un conjunto de instrucciones

Para la devolución de una variable al programa principal, en lenguaje C no hay paso por referencia, se pasa por valor la dirección de la variable a modificar:

Int reiniciar(int *a, int b){ … }

Así, la variable que pasemos como a se modificará tras ejecutar la función pero la variable que pasemos por b se quedará igual. La llamada a la función se tendría que hacer:

Reiniciar(&x, y);

Cuando un array se pasa como argumento a una función, la última de las dimensiones no se define:

Void calcula(int v[], int m[4][]){…}

 

ESTRUCTURAS DE CONTROL

  • Repetitivas: while, do-while, for
  • Selectivas: if, if-else, switch
  • Bifurcación de control: break, continue, goto, return, exit

Una forma simple de hacer un if en una sola línea es:

Y = (a>9 ? 100:200);

Equivale a if(a>9) Y = 100; else Y = 200;

Sentencia break: interrumpe la ejecución de un bucle while, do-while o for

Sentencia continue: se utiliza en los bucles para pasar a la siguiente repetición saltándose lo que falte para el final

Sentencia goto: transfiere el control a la sentencia etiquetada por el identificador. NUNCA debe usarse en C.

PUNTEROS A MEMORIA

Es una variable que contiene la dirección a una zona de memoria donde reside un tipo de dato. Los punteros a memoria de datos ocupan 16 bits.

Char car; //Variable tipo char

Char pcar; //Puntero a una variable tipo char

Car = 0xAA;
pcar = &car; //& me da  la dirección de ‘car’

Hay una forma optimizada de acceso a los datos que es:

#define CAR(*(char *)0x701)
CAR = 0xAA;

Pero entonces el compilador no chequea posibles conflictos

Como habíamos visto antes, algunas constantes se pueden almacenar en la ROM cerca o lejos, y entonces el puntero tiene un tamaño de 16 bits para cerca y 24 bits para lejos

Se pueden hacer punteros a funciones para ejecutar una función distinta según conveniencia, declarando en primer lugar un puntero, después asignando el nombre de la función al puntero y ejecutando el puntero:

Void (*fp)(); //Declara el puntero a una función void

fp = función_A //Asigna el nombre de la función que se va a ejecutar

(*fp)(); //Ejecuta la función asignada

…

Void función_A(void){…} //La función debe estar declarada y desarrollada

Con esto, también se puede pasar una función como argumento de otra función

Char (*f)(int, int); //f es un puntero a una función que devuelve un char y recibe dos enteros como argumento

A un puntero a función como el que acabamos de declarar se le puede asignar como valor cualquier identificador de una función que tenga los mismos argumentos y resultado

DIRECTIVAS

Se escribe antes de la declaración de una variable

  • #pragma varlocate ‘banco’ ‘nombre-variable’
    Esta directiva le dice al compilador, en el momento del linkado, dónde puede encontrar la variable especificada, optimizando el cambio de banco.
    Nosotros debemos especificar en los ficheros fuentes qué variables están afectadas
  • Linker script: es un fichero que proporciona diversa información al linker
    • Define posiciones en memoria de las secciones
    • Define el tamaño y situación de la pila (stack) software
    • Indica la situación de las palabras de configuración

ENSAMBLADOR EMPOTRADO

A veces es necesario incluir algo de ensamblador en medio de un código en C o llamar a una librería en ensamblador

Cómo se llama a variables de C desde ensamblador:

  • Las variables se declaran globales en C
  • Las variables se declaran extern en ensamblador
  • Se utilizan directamente

Cómo se llama a variables de ASM desde C

  • Las variables se declaran globales en ASM
  • Las variables se declaran extern en C
  • Se utilizan directamente

Para llamar a funciones se lleva a cabo el mismo procedimiento, pero la devolución de los valores tiene que ser siguiendo la norma del C18. La función se llama como si se tratase de una en C.

ESTRUCTURAS

Es una estructura de datos donde podemos clasificar las variables como si se tratase de un vector pero pudiendo hacer llamada a una variable concreta de una estructura concreta. Pongamos por ejemplo la lista de propiedades de un objeto, vamos a ver, en el caso de una persona, qué se le puede asignar:

Struct datos

{

Int  peso, altura;

Char nombre[];

} persona;

Entonces podemos llamar a las variables como:

Persona.peso;

Persona.altura;

O asignar sus valores

Persona.nombre = “Pedro”;

Las estructuras de datos son tipos complejos y no deben ser pasados como argumentos ni devueltos por funciones, en su lugar se usan punteros a dichas estructuras.

UNIONES

Un union es igual que una estructura pero todos los campos comparten la misma memoria. Es decir, si modificas una de las variables estarás modificando un trozo del resto.

struct

MACROS

Es un identificador equivalente a una expresión, sentencia o grupo de sentencias

Por ejemplo podemos declarar en macro una función que contiene un if y devuelve el valor del parámetro con valor máximo:

#define máximo (a,b) ((a>b) ? a:b)

En palabras, si a es mayor que b, devuelve a, y si no, devuelve b

Luego se puede llamar como cualquier función: MAX = máximo(x,y);

OPTIMIZACIÓN DEL CÓDIGO C

El objetivo es usar estructuras de datos para aumentar la eficiencia en la programación, es decir, usar objetos. Un objeto es la abstracción de una entidad del mundo real, por lo que se acerca la solución a la semántica de los seres humanos, modelando como objetos las entidades que aparecen en el programa.

Programación orientada a objetos:

  • Busca sintetizar las partes bien definidas de una aplicación mediante la utilización de objetos
  • Es fácil dividir el trabajo
  • Facilidad de prueba de módulos independientes
  • Los objetos pueden ser reutilizables en diferentes sistemas y proyectos
  • Un mismo problema puede requerir definir unos objetos u otros en función del nivel de detalle que se le quiera dar.

¿Cómo definimos un objeto?

Antes hemos visto las clases:

  • Una clase es una abstracción que define propiedades (atributos) y comportamientos (métodos) para un grupo de objetos, y tiene sentido en el diseño.
  • La clase es la plantilla
  • Ejemplo: idea de coche, sabemos que tiene ruedas, un color, una potencia de motor…

Ahora el objeto:

  • Es una realización concreta de una clase
  • Es una instancia (copia) en tiempo de ejecución de una clase
  • Ejemplo: definimos que es un coche de carreras, el número ruedas, el color, motor… etc

¿Qué es un método de una clase? Son las funciones que acceden a los datos atributo de la clase, y llevan a cabo operaciones que cambian su estado. Es el conjunto de mensajes a los que un objeto puede responder. Si quiero saber o modificar el número de ruedas del coche x de la clase “coche”, debo llamar a una función método que devuelve o modifica éste número.

Recomendaciones generales de optimización en C

  • Usar lazos simples
  • Evitar llamadas a subrutinas en lazos
  • Usar subrutinas inline: el compilador inserta la función directamente donde se encuentra la llamada
  • Evitar divisiones o modularizaciones de las operaciones
  • Usar & y shift cuando sea posible
  • Usar la regla 90/10: el 10% de las líneas de código deben ocupar el 90% del tiempo de la carga de la CPU
  • Como objetivo, obtener un código que se ejecuta de forma más eficiente:
    • En tiempo de ejecución
    • En espacio de memoria utilizado
  • Algoritmo adecuado: robusto y eficiente, pensar qué está haciendo realmente el código, familiarizarse con el cuerpo del programa para usar los algoritmos más apropiados.
  • Estructura de datos apropiada: optimizar el uso del espacio interno del código de los programas y la legibilidad de los mismos
  • Reutilización de código: librerías ya optimizadas
  • Elección del tipo variable a utilizar
  • Claridad en el código: programas fáciles de leer y entender para cualquier programador
  • Eliminación de código no utilizado, que nunca se ejecuta
  • Evitar en lo posible las variables globales
  • Mantener las variables lo más locales que se pueda, y mejorar su propagación
  • Cuando se necesite acceder al valor de una variable de una función, se pasará como argumento
  • Utilizar los comentarios para dejar clara la codificación
  • Utilizar MACROs para aclarar las construcciones confusas
  • Insertar líneas en blanco en la codificación para agrupar ideas del código
  • Importante declarar variables volatile cuando:
    • La localización de memoria pueda ser modificada por algo que no sea el compilador
    • El orden de las operaciones no debe ser cambiado por el compilador

Filtrado digital

Contenido

Introducción

Ventajas

Aplicaciones

Clasificación de los filtros digitales

Filtro de valor medio

Diseño de un filtro recursivo

Implementación en código C

Introducción

Es un sistema diseñado para la atenuación o eliminación de determinadas componentes de frecuencia del espectro de la señal de entrada. Y se puede hacer digitalmente en vez de con componentes activos o pasivos externos.

Ventajas:

  • Características imposibles de conseguir en analógicos, como la fase lineal y la no variación con la temperatura
  • Bajo coste, al implementarse en un procesador que ya está
  • Fácil de modificar
  • Inmunidad alta al ruido
  • Alta precisión, limitada por los errores de la aritmética que se emplee

Aplicaciones:

  • Mejora de las señales de tensiones/corrientes obtenidas por sensores
  • Control digital de motores
  • Procesado de señales digitales
  • Sistemas de comunicaciones
  • Procesamiento del habla
  • Ingeniería biomédica

El filtro digital se basa en la implementación de las ecuaciones matemáticas correspondientes a la función de transferencia del filtro, mediante la ecuación en diferencias

Es muy importante tener en cuenta el teorema de Nyquist y limitar la entrada al ADC a señales con frecuencias menores a la frecuencia de muestreo dividida por 2, añadiendo un filtro analógico paso bajo llamado filtro anti-aliasing.

Clasificación de los filtros digitales:

  • Recursivos: se obtiene a partir de la discretización de la FDT de los filtros en continuo, reutiliza las salidas del propio filtro como entradas, lo que suele dar lugar a respuestas de duración ilimitada a una señal impulso. De ahí sale el nombre de filtro IIR (infinite impulse response).
  • No recursivos: se obtiene a partir de la aproximación de filtros recursivos discretos, por división polinómica, de los filtros en continuo. Dicho de forma simple, únicamente usa la entrada (o un histórico de entradas) para el cálculo de la salida. En este caso la repuesta ante un impulso es limitada, por lo que se llaman filtros FIR (finite impulse response).

Filtro de valor medio

Opera con un número determinado de muestras de la señal de entrada que producen la señal de salida. Se basa en hacer la media, por lo que es útil para señales de continua con ruido pero no logra separar frecuencias.

La media se hace sobre un número fijo de muestras (ventana), de la siguiente  forma:

  • Inicialmente hay un vector donde se almacena un número de muestras, pongamos 30.
  • Cada vez que se toma una muestra, se elimina la primera del vector, se desplaza hacia la izquierda y la muestra nueva se almacena en el último espacio, por eso se le llama filtro de desplazamiento (de la ventana).
  • Se realiza la media cada vez que entra una muestra nueva

Esto se puede optimizar por ejemplo haciendo la media cada 10 muestras nuevas en un vector de 1000, de forma que hay más tiempo para otros procesos.

Diseño de un filtro recursivo

Hay unos pasos a seguir:

  1. Elección de frecuencia de corte y muestreo
  2. Elección del tipo y orden del filtro digital
  3. Obtener la FDT discreta y pasarla a ecuación en diferencias
  4. Simular para asegurar que funciona e implementar

Las FDT continuas ya las conocemos, vamos a ver cómo se discretiza.

Hay que realizar una transformación de la variable s, puede ser bilineal, trapezoidal u otras, y depende de la frecuencia de muestreo (Fs) que origina un desplazamiento de la frecuencia de corte en el plano continuo:

fd1

A este cambio se le conoce como ajuste “PreWarping”

La discretización cambia según el método, por ejemplo en el bilineal bilin

Se sustituye la frecuencia de corte del filtro en la FDT continua por la frecuencia con el ajuste PreWarping que hemos calculado antes, y la s por la expresión que le corresponda. Por ejemplo: un filtro paso bajo con frecuencia de corte 100 rad/s, Fs 1000 Hz, tiene una expresión típica en continuo así:

fd2

Y al discretizar con los datos dados queda así:

fd3

Por último se debe pasar esta FDT a una ecuación en diferencias que tenga en cuenta la salida que vamos a obtener, la entrada y/o las entradas y salidas anteriores que debemos guardar en memoria, de la siguiente forma:

fd4

Pasando la entrada a la derecha y despejando la salida:

fd5

Esto se traduce a lo siguiente:

fd6

En palabras: la salida actual va a ser igual a 1/21 por la entrada actual, más la entrada anterior, más 19 veces la salida anterior.

Implementación en código C

Hay que tener en cuenta la resolución requerida para la implementación, y por lo tanto el tipo de variables que se van a usar:

  • En coma flotante, sólo útil en procesadores con FPU, ya que un procesador de coma fija tardaría demasiado en procesar la operación mediante librerías.
  • Variables enteras, se usaría el redondeo
  • Formato IQ, que se puede utilizar en procesadores de coma fija con la librería matemática IQMath en un tiempo pequeño.

Para simplificar, suponiendo que disponemos de un procesador con FPU, la implementación se llevaría a cabo de la siguiente forma:

  • Declaración de las variables

Float Xk = 0, Xk_1 = 0, Yk = 0, Yk_1 = 0;

  • Configuración e inicialización de dispositivos: ADC, timer que hace saltar una interrupción cada Ts segundos, otros GPIO…
  • Rutina de interrupción del timer: lee la entrada del ADC y se la asigna a Xk
  • Bucle del main: tras la interrupción y por lo tanto la obtención del valor Xk, se realiza la operación con la ecuación en diferencias literalmente, después se reasignan los valores para tener el valor anterior cuando realicemos un nuevo muestreo:
while(1){

     if(interrupt==1){

          Yk = (1/21)*Xk+Xk_1+19*Yk_1;

          Yk_1 = Yk;

          Xk_1 = Xk;

          interrupt = 0;

       }

     … //Se hace con la información lo que sea necesario

    }

} //Fin del main

LCD en Arduino

Igual que se hizo con el PIC, Arduino tiene funciones específicas para manejar una LCD, y es más simple y amigable.

Las conexiones base son las mismas, el resto se configuran durante el programa.

/*
 LiquidCrystal Library - Hello World
 
 Demonstrates the use a 16x2 LCD display. The LiquidCrystal
 library works with all LCD displays that are compatible with the 
 Hitachi HD44780 driver. There are many of them out there, and you
 can usually tell them by the 16-pin interface.
 
 This sketch prints "Hello World!" to the LCD
 and shows the time.
 
 The circuit:
 * LCD RS pin to digital pin 12
 * LCD Enable pin to digital pin 11
 * LCD D4 pin to digital pin 5
 * LCD D5 pin to digital pin 4
 * LCD D6 pin to digital pin 3
 * LCD D7 pin to digital pin 2
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)
 
 Library originally added 18 Apr 2008
 by David A. Mellis
 library modified 5 Jul 2009
 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009
 by Tom Igoe
 modified 22 Nov 2010
 by Tom Igoe
 
 This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
 */
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //pines RS,E,D4,D5,D6,D7
void setup() {
 // set up the LCD's number of columns and rows: 
 lcd.begin(16, 2); //número de filas y columnas de la LCD
 // Print a message to the LCD.
 lcd.print("hello, world!");
}
void loop() {
 // set the cursor to column 0, line 1
 // (note: line 1 is the second row, since counting begins with 0):
 lcd.setCursor(0, 1);
 // print the number of seconds since reset:
 lcd.print(millis()/1000);
}

Para poder imprimir una variable entre texto hay que hacer por ejemplo

lcd.print("Hay: ");
lcd.print(temperatura); //ésta es la variable
lcd.print(" Grados");

Conversor AD y puerto USB

Como ya sabemos desde el pin A0 al A5 podemos meter una señal analógica y que nuestro Arduino la interprete. Con ciertas limitaciones.

El máximo son 5 voltios y su resolución es de 10 bits. Es decir, cuando tengamos 5 voltios a la entrada tendremos como resultado 1024, y si tenemos 0 voltios tendremos como resultado 0. Ésto nos da un paso de 0,005 voltios.

A su vez sabemos que la placa es capaz de enviar y recibir datos por USB. En éste programa usaremos ésa capacidad para ver el resultado.

A la  entrada analógica pondremos un potenciómetro conectado a +5 y GND, conformando un divisor de tensión para que la salida intermedia de un rango de 5 a 0 voltios. Éste será el funcionamiento generalizado de cualquier sensor, pero lo podemos manipular.

Vamos con el programa:

void setup(){
 pinMode(2,INPUT); //el pin 2 es input
 pinMode(13,OUTPUT); //el pin 13 output
 Serial.begin(9600); //mandar cosas por USB, a cierta velocidad (bits/s)
}
void loop(){
 double lecturas[4]; //declaramos un vector de 5 posiciones
 double mandar; //y una variable flotante
 if(digitalRead(2)==LOW){ //si apretamos el boton y dejamos que el pin 2 se conecte a tierra
 Serial.println("leyendo"); //mandas esto por USB
 for(int x=0;x<5;x++){ //y lees 5 veces
 lecturas[x]=analogRead(0); //el puerto analógico AN0 y lo vas guardando en el vector
 delay(100); //con un pequeño delay para que le dé tiempo
 }
 mandar=5*(lecturas[0]+lecturas[1]+lecturas[2]+lecturas[3]+lecturas[4])/(5*1024.0); //hacemos un promedio de las lecturas
 Serial.print(mandar); //y mandamos el resultado por USB
 }
}

Las instrucciones nuevas son:

Serial.begin(bauds) -> Inicializa el puerto serie (USB) para el envío de datos a una cierta cantidad de bits por segundo (bauds).

Serial.println(var) o Serial.println("text") -> Envía por el puerto USB una variable o un texto. También se puede utilizar Serial.print(var) para que no haga un salto de línea.

analogRead(pin) -> lee el pin analógico seleccionado, en éste caso 0, A0, y devuelve un número entre 0 y 1023.

He procesado el resultado de la lectura analógica para que muestre, en vez de un número entre 0 y 1024, un determinado valor de la tensión que está entrando por el pin analógico.

El resultado lo podéis ver en vuestro monitor serie, que se encuentra en la pestaña Herramientas -> Monitor Serial del software de arduino.

Puerto digital Arduino

Ahora ya sabemos que podemos utilizar los pines 2 a 13 como puerto digital. El inconveniente es que *de momento*, tendremos que declarar los pines uno a uno. ¿Os acordáis del LED que parpadea para indicar que la placa está bien nada más llegar a casa? Pues es un programa genial para explicar la estructura general y varias instrucciones:

Cabe destacar que éste programa es de dominio público y se encuentra, junto a muchos más programas de ejemplo, en la web de Arduino y en nuestro software.

La estructura es la siguiente: declaración de variables, setup y loop. En la declaración de variables indicamos al programa que usaremos un tipo de variable con un nombre concreto durante todo el programa. En setup ponemos las cosas que sólo se ejecutarán una vez, por ejemplo la configuración de pines.

La configuración de pines es muy sencilla, sólo tienes 2 opciones:

pinMode(númerodepin,OUTPUT), para salida

pinMode(númerodepin,INPUT), para entrada

Observad que en éste programa se ha declarado "led" asignandole el numero 13 y luego hemos usado "led" como "número de pin", así se hace más sencillo recordar qué tienes en cada pin. Recordemos que en el pin 13 no puede haber INPUT porque hay un LED que actúa como diodo unidireccional, sólo salida.

Una vez hemos configurado nuestro dispositivo pasamos a la parte del programa que se va a repetir infinitas veces si lo dejamos conectado: el loop. Sencillamente programamos aquí lo que queremos que haga. Es simple como programar en C pero con varias instrucciones. No sirve de nada ponerle a la variable 1 o 0 porque no lo entiende.

Para escribir un 1 o un 0 en un pin, tenemos que usar las instrucciones:

digitalWrite(númerodepin,HIGH) si queremos ponerlo en estado alto (1)

digitalWrite(númerodepin,LOW) si queremos ponerlo en estado bajo (0)

Para indicar al programa que se tiene que esperar un tiempo x, en milisegundos, escribimos la función delay(x).

Recordemos que al pasar a la práctica, cualquier dispositivo LED que pongamos a una salida deberá llevar una resistencia acorde a la tensión de salida y a la corriente máxima. Lo mismo haremos en una entrada, para limitar la corriente que pasa por ella y dirigirla a HIGH o LOW como convenga, sin cortocircuitar la placa.

Para comprobar el estado de una entrada utilizaremos la función:

var = digitalRead(númerodepin)

La función digitalRead devolverá un 1 o un 0 dependiendo de si el pin está en estado alto o bajo, respectivamente. Para que un pin esté en estado bajo deberá estar conectado a GND, no servirá si dejamos el cable suelto. Después haremos la acción que queramos con un if(var==1) o if(var==0) según convenga.

Ésto es lo básico y os animo a que entrenéis un poco el tema. Una buena práctica es hacer las luces del coche fantásitico. Ésto se basa en poner 8 LED que se encienden y apagan individualmente. En vez de escribir 2000 líneas de código, podéis utilizar un for o un while. Más tarde podéis implementar un botón para que se ejecute ésa acción u otra cualquiera al estar cerrado o abiero. Mucha atención de poner resistencias mínimo 220 ohms a las salidas.

Cualquier duda es bien recibida en comentarios y será resuelta o por lo menos se intentará.