Diferencia entre revisiones de «Cronometro»

De Proyectos
Ir a la navegación Ir a la búsqueda
Sin resumen de edición
 
(No se muestran 42 ediciones intermedias del mismo usuario)
Línea 1: Línea 1:
Tareas:
*terminar punto fusible
*Apartado lcd
**Probar integrado i2c con pantalla
**Añadir info integrado en entrada pantalla
= Propósito =
= Propósito =
Este proyecto se trata de construir un cronometro que desconecte un aparato eléctrico (220V) en un tiempo determinado.<br>
Este proyecto se trata de construir un cronometro que desconecte un aparato eléctrico (220V) en un tiempo determinado.<br>
Línea 14: Línea 8:
<br>
<br>
<gallery widths=300px mode="traditional">
<gallery widths=300px mode="traditional">
Image:circuito_cronometro.png |PCB F.A./Shield/Rele
Image:Crono_CircuitoCronometro.jpg |PCB F.A./Shield/Rele
Image: circuito_cronometro_tapa.png |PCB Tapa
Image: circuito_cronometro_tapa.png |PCB Tapa
</gallery>
</gallery>
Línea 94: Línea 88:
Utilizando la teoria del apartado [[Circuito_Relé | Circuito Relé]] calcularé la resistencia de base.<br>
Utilizando la teoria del apartado [[Circuito_Relé | Circuito Relé]] calcularé la resistencia de base.<br>
<br>
<br>
<math>I_C = I_{Rele} + I_{Led} = \frac{V_{Rele}}{R_{Rele}} + \frac {V_{Led}}{R_{Led}} = \frac{5+}{}</math><br>
<math>I_C = I_{Rele} + I_{Led} = \frac{V_{Rele}}{R_{Rele}} + I_{Led} = \frac{5}{125} + 15 = 55 mA</math><br>
<br>
<br>
<math>R_B = \frac {(V_{Control}-V_{BE})\cdot \beta}{I_C} = \frac {(5-0.6) \cdot 50 \cdot 125}{5} = 5k5 \Omega</math><br>
<math>R_B = \frac {(V_{Control}-V_{BE})\cdot \beta}{I_C} = \frac {(5-0.6) \cdot 50}{55} = 4k \Omega</math><br>
<br>
<br>
El valor normalizado es <math>5k6 \Omega</math><br>
El valor normalizado es <math>3k9 \Omega</math><br>
<br>
<br>
Según la fórmula de arriba podemos sacar la corriente que pasará por el relé:<br>
Según la fórmula de arriba podemos sacar la corriente que pasará por el relé:<br>
<br>
<br>
<math>I_{Rele} = \frac {V_{Rele}}{R_{Rele}} = \frac {5}{125} = 40mA</math><br>
<math>I_{Rele} = \frac {V_{Rele}}{R_{Rele}} = \frac {5}{125} = 40mA</math><br>
<br>
Y sabiendo que queremos que pase 15mA por el led indicador tendremos que la <math>I_C</math>:<br>
<br>
<math>I_C = I_{Rele} + I_{Led} = 40mA + 15mA = 55mA</math><br>
<br>
(Todo esto teniendo en cuenta el circuito del apartado [[Circuito_Relé | Circuito Relé]])<br>
<br>
<br>
La <math>I_B</math> la calcularemos a través de la relación de corrientes del transistor:<br>
La <math>I_B</math> la calcularemos a través de la relación de corrientes del transistor:<br>
<br>
<br>
<math>I_C = \beta \cdot I_B -> I_B = \frac{I_C}{\beta} = \frac {55mA}{50} = </math>
<math>I_C = \beta \cdot I_B \rightarrow I_B = \frac{I_C}{\beta} = \frac {55mA}{50} = 1,1 mA</math>
<br>
<br>


=== Calculo de la resistencia de masa ===
=== Calculo de la resistencia de masa ===
<br>
<br>
La resistencia a masa la pondré como mínimo de <math>56k \Omega</math> o <math>100k \Omega</math>.<br>
La resistencia a masa la pondré como mínimo de <math>39k \Omega</math> o <math>100k \Omega</math>.<br>
<br>
<br>
=== Calculo resistencia led ===
=== Calculo resistencia led ===
<br>
<br>
Línea 128: Línea 117:
<br>
<br>


== PCB 3D ==
=== Comparativa Medidas ===
{|class="wikitable" style="text-align:center;"
!
!Calculada
!Medida
|-
|<math>R_{Rele}</math>
|<math>125 \Omega</math>
|<math>64 \Omega</math>
|-
|<math>V_{Rele}</math>
|<math>5 V</math>
|<math>4.4 V</math>
|-
|<math>I_{Rele}</math>
|<math>40 mA</math>
|<math>65 mA</math>
|-
|<math>V_{Led}</math>
|<math>2 V</math>
|<math>2 V</math>
|-
|<math>I_{Led}</math>
|<math>15 mA</math>
|<math>10.5 mA</math>
|-
|<math>R_{Led}</math>
|<math>200 \Omega</math>
|<math>215 \Omega</math>
|-
|<math>I_C</math>
|<math>55 mA</math>
|<math>75.5 mA</math>
|-
|<math>I_B</math>
|<math>1.1 mA</math>
|<math>1.06 mA</math>
|-
|<math>R_B</math>
|<math>4k4 \Omega</math>
|<math>3K6 \Omega</math>
|-
|<math>\beta</math>
|<math>50</math>
|<math>313</math>
|}
== LCD via I2C ==
<br>
Para no gastar tantos pines del Arduino he utilizado el integrado [http://www.nxp.com/documents/data_sheet/PCF8574.pdf PCF8574]. Este integrado es un expansor de 8 bits via i2c. Con esto puedo conectar un LCD via i2c al arduino, ahorrandome 7 puertos del arduino.<br>
En la sección [[Cronometro#Circuito | Circuito]] se ve el esquematico de conexión del LCD al PCF8574. Básicamente utilizo el LCD en el modo de 4 hilos y juntamente con las señales RS, EN y RW las envio por i2c al Arduino.<br>
Para que el Arduino se pueda comunicar con el módulo PCF8574 necesito la libreria Wire.h y utilizar una libreria para controlar el LCD que lo haga a través del i2c, no a través de los pines digitales del arduino.<br>
En la sección [[HD44780U#Comunicaci.C3.B3n_I2C|Comunicación I2C]] de la página HD44780U está explicado más detalladamente como funciona.<br>
<br>
<br>
== Archivos Kicad ==
<br>
<br>
== Gerber ==
<b>Shield Arduino</b><br>
[[Media:CronometroShield.pdf|Fotolito Shiel Cronometro]]<br>
[[Media:CronoShieldKicad.7z|Archivos Kicad del Shield Arduino]]<br>
<br>
<br>
<b>PCB LCD y botones</b><br>
[[Media:TapaCrono.v1.pdf | Fotolito PCB del LCD y los botones]]<br>
[[Media:CronoLCD.7z|Archivos del PCB del LCD y los botones]]<br>
<br>
<br>
<b>imagenes</b><br>
<gallery mode="traditional">
Image:CronometroShieldBack.jpg
Image:CronometroShieldFront.jpg
Image:CronometroShieldMask.jpg
Image:TapaCrono.v1.jpg
</gallery>
<br>
= Código =
= Código =
== Explicación código ==
<br>
<br>
<div class="toccolours mw-collapsible mw-collapsed">
Archivo código: [http://wiki.erikcrane.net/images/2/24/Crono07.ino Cronometro.ino]<br>
<pre>
<br>
#include "Wire.h"
<b>Bloque 01 - Defines, declaraciones variables</b><br>
#include "lcd_i2c.h"
<br>
#include "Bounce.h"
*Las primeras tres lineas son los includes. Voy a usar la comunicación I2C para conectar el lcd al arduino por lo tanto hago un include de la libreria Wire.h. Tambien hago un include de la libreria para controlar el lcd y por último utilizare la libreria Bounce.h para poder hacer [[Debounce]] en los botones
*El siguiente mini-bloque es el encargado de definir los pines que usaré del arduino para los botones
*Un define de la "varaible" INTERVAL_MILLIS que usaré más adelante
*Otros defines que sirven para "traducir" el numero del estado en palabras mas entendibles
*Configuración del parámetro REBOUNCE_TIME para los Debounce
*Declaración del objeto lcd con la dirección I2C y las lineas y caracteres que tiene el lcd
*Creación de los objetos debounce (uno por cada boton)
*Creación de la variable tiempo que es un struct dónde almacenaré el tiempo que he configurado como cuenta atrás
*Creación de una variable que almaceno los milisegundos que pasan desde que enciendo el cronómetro
<br>
<b>Bloque 02 - Setup</b><br>
<br>
En el setup voy a configurar los pines del arduino e iniciar el objeto lcd:
*Lo primero que hago es activar el pin RELE como OUTPUT y ponerlo como LOW porque lo quiero desactivado
*Con un bucle configuro los 6 botones como INPUT_PULLUP (este modo es para ahorrarme colocar las pullup en la placa, ya que el arduino ya lleva integrada resistencias pullup en cada entrada/salida digital
*Llamo a la función init() del obejto lcd que inicia el LCD
<br>
<b>Bloque 3 - Loop</b><br>
<br>
El bloque del Loop consiste en un switch en donde cada case corresponde con un estado y la variable estate almacena en que estado se encuentra el programa.<br>
Hay 3 estados:
*Estado 0 - Inicial
**Es el primer estado cuando encendemos el cronometro
**En este estado lo que hacemos es:
***Mostrar el tiempo por pantalla (la función imprimirTiempo()
***Pasar directamente al siguiente estado (state = E_CONFIG_TIME;)
*Estado 1 - Configurar tiempo
**Lo primero es llamar a la función configTiempo(), que sirve para configurar los minutos y segundos de cuenta atrás
**Luego tenemos dos if's
***El primero (ev_reset()) hace que si apretamos el botón reset reiniciamos el cronometro a 0 y volvemos al estado inicial
***El segundo (ev_start()) hace que si apretamos el botón start activamos el relé y vamos al estado cuenta atras (state = E_CUENTA_ATRAS;)
*Estado 2 - Cuenta atras
**En este estado hay un if que miras si se ha acabado la cuenta atrás o se ha pulsado el boton reset. Si no pasa ningun de estas dos acciones empezamos la cuenta atrás (llamando a la función cuentaAtras();) y la seguimos ejecutando hasta que uno de estos dos eventos ocurran
**Si se acaba la cuenta atrás o se aprieta el botón reset lo que haremos es:
***Desactivar relé (desactivarRele();)
***Reiniciar el contador a 0 (resetTiempo();)
***Dirigirnos al estado inicial (state = E_INICIAL;)
<br>
<b>Bloque 4 - Funciones</b><br>
<br>
En el bloque 4 nos encontramos todas la funciones que utilizamos durante el loop. Las voy a listar y explicar brevemente:
*boolean ev_start()
**Utilizando las fórmulas del objeto Bounce miramos si se ha apretado el botón start
**Si se ha apretado se devuelve true (ATENCIÓN si el pulsador está en LOW quiere decir que está pulsado. Esto es debido a que la lógica se invierte porque utilizo resistencias pullup en vez de pulldown)
**Sino se ha apretado se devuelve false
*boolean ev_reset()
**Lo mismo que en la función de ev_start pero con el botón reset
*void imprimirTiempo()
**Función que se encarga de escribir en el LCD el tiempo
*void configTiempo()
**Configura el tiempo de la cuenta atrás según los botones que apretamos
**Si se llega a 59 segundos se pasa a 1 minuto y los segundos pasan a 0
**Si se llega a los 59 minutos se pasa a 1 hora y se pone 0 en minutos
**Tengo en cuenta que no se puede bajar de 0 segundos ni de 0 minutos
**Cuando acabo de configurar el tiempo llamo a la función imprimirTiempo() para actualizar la pantalla
*void resetTiempo()
**Coloco a 0 los campos del struct cuenta (horas, minutos, segundos y decimas)
*boolean ev_fin_cuenta_atras()
**Si los minutos, segundos y decimas llegan a 0 devuelve true
*void cuentaAtras()


#define RELE 12
*void activarRele()
#define START 4
**Escribe HIGH en el pin RELE
#define RESET 5
*void desactivarRele()
#define SEG_UP 6
**Escribe LOW en el pin RELE
#define SEG_DOWN 7
<br>
#define MIN_UP 8
#define MIN_DOWN 9


#define INTERVAL_MILLIS 5
#define E_INICIAL 0
#define E_CONFIG_TIME 1
#define E_CUENTA_ATRAS 2
#define E_RELE_ACTIVADO 3
#define REBOUNCE_TIME 250
LCD_I2C lcd(0x38);
uint8_t state = E_CONFIG_TIME;
Bounce buttonStart = Bounce(START, INTERVAL_MILLIS);
Bounce buttonReset = Bounce(RESET, INTERVAL_MILLIS);
Bounce buttonSegUp = Bounce(SEG_UP, INTERVAL_MILLIS);
Bounce buttonSegDown = Bounce(SEG_DOWN, INTERVAL_MILLIS);
Bounce buttonMinUp = Bounce(MIN_UP, INTERVAL_MILLIS);
Bounce buttonMinDown = Bounce(MIN_DOWN, INTERVAL_MILLIS);
struct tiempo{
  uint8_t horas;
  uint8_t minutos;
  uint8_t segundos;
  uint8_t decimas;
}cuenta = {0,0,0,0};
long milisegundos;
////////////////////////////////////////////
//*************SETUP*********************//
void setup(){
  pinMode(RELE, OUTPUT);
  digitalWrite(RELE, LOW);
 
  for(int i=4; i<10; i++){
    pinMode(i, INPUT_PULLUP);
  }
  lcd.init(0x38);
}
//*************SETUP***********************//
////////////////////////////////////////////
//**************LOOP*********************//
void loop(){
  switch(state){
    case E_INICIAL:{
      imprimirTiempo();
      state = E_CONFIG_TIME;
    }break;
    case E_CONFIG_TIME:{
      configTiempo();
      if(ev_reset()){
        resetTiempo();
        state = E_INICIAL;
      }
      if(ev_start()){
        activarRele();
        state = E_CUENTA_ATRAS;
      }
    }break;
    case E_CUENTA_ATRAS:{
      if(!ev_fin_cuenta_atras() && !ev_reset()){
        cuentaAtras();
      }else{
        desactivarRele();
        resetTiempo();
        state = E_INICIAL;
      }
    }break;
  }
}
//**************LOOP***********************//
////////////////////////////////////////////
boolean ev_start(){
 
  if(buttonStart.update() && buttonStart.read() == LOW){
    return true;
  }
  return false;
}
boolean ev_reset(){
  if(buttonReset.update() && buttonReset.read() == LOW){
    return true;
  }
  return false;
}
void imprimirTiempo(){
  lcd.setCursor(3,0);
  if(cuenta.horas < 10){
    lcd.print("0");
  }
  lcd.print(cuenta.horas);
  lcd.print(":");
  if(cuenta.minutos < 10){
    lcd.print("0");
  }
  lcd.print(cuenta.minutos);
  lcd.print(":");
  if(cuenta.segundos < 10){
    lcd.print("0");
  }
  lcd.print(cuenta.segundos);
  lcd.print(":");
  lcd.print(cuenta.decimas); 
}
void configTiempo(){
  if(buttonMinUp.update() && buttonMinUp.read() == LOW){
    buttonMinUp.rebounce(REBOUNCE_TIME);
    if(cuenta.minutos < 59){
      cuenta.minutos++;
    }else{
      cuenta.minutos = 0;
    }
  }
  if(buttonMinDown.update() && buttonMinDown.read() == LOW){
    buttonMinDown.rebounce(REBOUNCE_TIME);
    if(cuenta.minutos > 0){
      cuenta.minutos--;
    }
  }
  if(buttonSegUp.update() && buttonSegUp.read() == LOW){
    buttonSegUp.rebounce(REBOUNCE_TIME);
    if(cuenta.segundos < 59){
      cuenta.segundos++;
    }else{
      cuenta.segundos = 0;
    }
  }
  if(buttonSegDown.update() && buttonSegDown.read() == LOW){
    buttonSegDown.rebounce(REBOUNCE_TIME);
    if(cuenta.segundos > 0){
      cuenta.segundos--;
    }
  }
  imprimirTiempo();
}
void resetTiempo(){
  cuenta.horas = 0;
  cuenta.minutos = 0;
  cuenta.segundos = 0;
  cuenta.decimas = 0;
}
boolean ev_fin_cuenta_atras(){
  if(cuenta.minutos == 0 && cuenta.segundos == 0 && cuenta.decimas == 0){
    return true;
  }
  return false;
}
void cuentaAtras(){
  int decimasTotales = (((cuenta.minutos*60)+cuenta.segundos)*10)+cuenta.decimas;
  milisegundos = millis();
  if(milisegundos % 100 == 0){
    decimasTotales--;
    cuenta.decimas = decimasTotales % 10;
    cuenta.segundos = ((decimasTotales - cuenta.decimas)/10) % 60;
    cuenta.minutos = ((decimasTotales - cuenta.decimas)/10)/60;
 
    imprimirTiempo();
  }
}
void activarRele(){
  digitalWrite(RELE, HIGH);
}
void desactivarRele(){
  digitalWrite(RELE, LOW);
}
</pre>
</div>
<br>
== Máquina de estados ==
== Máquina de estados ==
<br>
<br>
[[File: maquina_estados_cronometro.png | 420px]]<br>
[[File: maquina_estados_cronometro.png | 420px]]<br>
<br>
<br>
 
= Galeria =
= Ensamblaje =
<gallery mode="traditional">
<br>
Image: MontajeCrono01.jpg
<br>
Image: MontajeCrono02.jpg
Image: MontajeCrono03.jpg
Image: MontajeCrono04.jpg
Image: MontajeCrono05.jpg
Image: MontajeCrono06.jpg
Image: MontajeCrono07.jpg
Image: MontajeCrono08.jpg
Image: MontajeCrono09.jpg
</gallery>

Revisión actual - 14:59 10 jun 2015

Propósito

[editar]

Este proyecto se trata de construir un cronometro que desconecte un aparato eléctrico (220V) en un tiempo determinado.
Se podria hacer con un 555 pero para aprender sobre la programación de un microcontrolador y sobre las pantalals LCDs he optado por utilizar un Arduino Leonardo y fabricar un shield que conecte la pantalla, botones, leds, etc.
El objetivo es tener un cronometro al que enchufaremos directamente el aparato a desconectar/conectar, es decir el aparato se alimentará a través del cronometro y un rele conectado al microcontrolador desconectará el aparato.

Circuito

[editar]



General

[editar]

Fuente Alimentación

[editar]

Diseñar una fuente de alimentación para alimentar un arduino a 12V y 500mA. Colocar un fusible en el primario y un interruptor que corte la corriente del primario.
El transformador que he encontrado es de 400 y pico mA Crovisa 12V 5VA

Fusible

[editar]


El fusible se puede poner en el circuito primario o el secundario. En mi caso lo pondré en el primario y para ello necesito calcular la corriente máxima que circulará por el primario.

Sabemos la corriente que irá por el secundario y la fórmula que relaciona ambas corrientes es:
Vpri⋅Ipri=Vsec⋅Isec

Hay que tener en cuenta que hay una pérdida de poténcia de un 60% del primario al secundario, solo es efectivo un 40% de la energia que transforma:
Vpri⋅Ipri⋅40%=Vsec⋅Isec

Aqui despejamos:
Ipri=Vsec⋅Isec⋅1.4Vpri=12⋅0.416⋅1.4230=30mA

En las hojas de especificaciones nos dicen que la corriente del primario es de 36mA, así que utilizaremos un fusible de ese valor o superior.

Elección Transformador

[editar]


Voy a necesitar un transformador que el secundario tenga como mínimo el voltaje que alimentará al Arduino (9V) más 3V que es lo que va a caer en el regulador de tensión (7809).
Varduino+Vreg=9+3=12V

Los 12V son en valor eficaz, lo que significa que una vez pasados a valor de pico voy a tener:
Vp=Vef⋅(2)=16.97V

Como en el puente rectificador va a caer voltaje:
Vp−1.4V=15.57V

Este será el voltaje que tendré a la entrada del regulador, y como se ve va de sobras para que en el regulador caigan 3V.

Por lo tanto utilizaré un transformador de 230V en el primario y 12V en el secundario. He escogido un transformador de Crovisa 12V 5VA

Elección Puente Rectificador

[editar]


Al elegir el puente rectificador tenemos que tener en cuenta la corriente que puede aguantar en conducción y el voltaje que puede aguantar en corte. Como hemos visto en el apartado del transformador tendremos una tensión máxima de unos 17V y la corriente que nos suministrará el transformador es de unos 400mA.
También hay que tener en cuenta la corriente que circulará por el condesador que hace de filtro durante la carga. Este efecto está explicado en un apartado de la entrada Efecto del condensador en la conducción del diodo rectificador y en Corriente Inicial.



En esta imagen se ve en la linea verde la tensión que tiene el condensador con un rizado del 10% de la tensión máxima y la linea roja es la corriente que pasa por el condensador para cargarse. El primer pico que se ve es de 14A (el condensador es un corto circuito y se intenta cargar en el menor tiempo posible, así que pasa la máxima corriente). Los siguientes picos son de unos 4.3A.

El puente rectificador debe poder aguantar unos 14A (mejor más por seguridad) durante un periodo de 6ms como mínimo (es lo que dura el pulso).

El modelo de puente es el W08M. Aguanta una corriente directa de 1.5A, suficiente para nuestros 417mA y un voltaje en inversa 800V. También encontramos el dato de aguanta 50A en picos de 8.3ms, en principio suficiente para aguantar los picos que vemos en la imagen superior.

Cálculos Condensador Filtrado

[editar]


En la sección Uso práctico del Filtro de la entrada Fuente de Alimentación comento la regla práctica de 1000uF por cada amperio de corriente en la salida.
En este caso tendré unos 400mA como máximo a la salida, por lo tanto escogeré un condensador de 470uF (que es el valor anterior a 1000uF).
También colocaré los condensadores que comenta el usuario Fogonazo en el forosdeelectronica.com con los valores de C2 - 100nF, C3 - 100nF y C4 - uF.

Elección Regulador de Tensión

[editar]


Necesito un regulador de tensión que entre 9V en la salida y que aguante unos 400mA. El 7809 es un regulador de estas características.

Circuito Proteus

[editar]

Para poder simular un transformador de 230V/12V se tiene que utilizar la fórmula L2=L1⋅(V2V1)2
Fijamos L1=20mH y entonces calculamos:

L2=20mH⋅(12V230V)2=54μH

Relé

[editar]


El relé se encargará de encender la carga conectada (una bombilla, insoladora, ...). Utilizaré un relé Serie 32 - Mini-relé para circuito impreso de 6A para poder conectar como máximo 230V⋅6A=1380W.
Debido a que necesito 40mA para poder activar el relé y la salida de un pin de Arduino entrega justo 40mA me quedo corto de corriente (además quiero encender un led cuando se encienda el relé y consume unos 10mA). Utilizaré un transistor controlado por el arduino para commutar el relé. El circuito que resulta está explicado en la sección Circuito Relé.
Voy a usar la fuente de 5V que provee el Arduino Leonardo para activar el relé. En cada pin el Arduino Leonardo entrega 5V y 40mA como máximo, pero en el pin de alimentación de 5V tengo más corriente.

Calculo resistencia base

[editar]

Utilizando la teoria del apartado Circuito Relé calcularé la resistencia de base.

IC=IRele+ILed=VReleRRele+ILed=5125+15=55mA

RB=(VControl−VBE)⋅βIC=(5−0.6)⋅5055=4kΩ

El valor normalizado es 3k9Ω

Según la fórmula de arriba podemos sacar la corriente que pasará por el relé:

IRele=VReleRRele=5125=40mA

La IB la calcularemos a través de la relación de corrientes del transistor:

IC=β⋅IB→IB=ICβ=55mA50=1,1mA

Calculo de la resistencia de masa

[editar]


La resistencia a masa la pondré como mínimo de 39kΩ o 100kΩ.

Calculo resistencia led

[editar]


La tensión de trabajo del led rojo es de 1.5v - 2.2v y consume una corriente entre 10mA y 20mA. Para nuestro caso escogeremos una tensión de 2v y corriente de 15mA.

RLED=VRELE−VLEDILED=5−20.015=200Ω

Pondré una resistencia de 220Ω.

Comparativa Medidas

[editar]
Calculada Medida
RRele 125Ω 64Ω
VRele 5V 4.4V
IRele 40mA 65mA
VLed 2V 2V
ILed 15mA 10.5mA
RLed 200Ω 215Ω
IC 55mA 75.5mA
IB 1.1mA 1.06mA
RB 4k4Ω 3K6Ω
β 50 313

LCD via I2C

[editar]


Para no gastar tantos pines del Arduino he utilizado el integrado PCF8574. Este integrado es un expansor de 8 bits via i2c. Con esto puedo conectar un LCD via i2c al arduino, ahorrandome 7 puertos del arduino.
En la sección Circuito se ve el esquematico de conexión del LCD al PCF8574. Básicamente utilizo el LCD en el modo de 4 hilos y juntamente con las señales RS, EN y RW las envio por i2c al Arduino.
Para que el Arduino se pueda comunicar con el módulo PCF8574 necesito la libreria Wire.h y utilizar una libreria para controlar el LCD que lo haga a través del i2c, no a través de los pines digitales del arduino.
En la sección Comunicación I2C de la página HD44780U está explicado más detalladamente como funciona.

Archivos Kicad

[editar]


Shield Arduino
Fotolito Shiel Cronometro
Archivos Kicad del Shield Arduino

PCB LCD y botones
Fotolito PCB del LCD y los botones
Archivos del PCB del LCD y los botones

imagenes


Código

[editar]

Explicación código

[editar]


Archivo código: Cronometro.ino

Bloque 01 - Defines, declaraciones variables

  • Las primeras tres lineas son los includes. Voy a usar la comunicación I2C para conectar el lcd al arduino por lo tanto hago un include de la libreria Wire.h. Tambien hago un include de la libreria para controlar el lcd y por último utilizare la libreria Bounce.h para poder hacer Debounce en los botones
  • El siguiente mini-bloque es el encargado de definir los pines que usaré del arduino para los botones
  • Un define de la "varaible" INTERVAL_MILLIS que usaré más adelante
  • Otros defines que sirven para "traducir" el numero del estado en palabras mas entendibles
  • Configuración del parámetro REBOUNCE_TIME para los Debounce
  • Declaración del objeto lcd con la dirección I2C y las lineas y caracteres que tiene el lcd
  • Creación de los objetos debounce (uno por cada boton)
  • Creación de la variable tiempo que es un struct dónde almacenaré el tiempo que he configurado como cuenta atrás
  • Creación de una variable que almaceno los milisegundos que pasan desde que enciendo el cronómetro


Bloque 02 - Setup

En el setup voy a configurar los pines del arduino e iniciar el objeto lcd:

  • Lo primero que hago es activar el pin RELE como OUTPUT y ponerlo como LOW porque lo quiero desactivado
  • Con un bucle configuro los 6 botones como INPUT_PULLUP (este modo es para ahorrarme colocar las pullup en la placa, ya que el arduino ya lleva integrada resistencias pullup en cada entrada/salida digital
  • Llamo a la función init() del obejto lcd que inicia el LCD


Bloque 3 - Loop

El bloque del Loop consiste en un switch en donde cada case corresponde con un estado y la variable estate almacena en que estado se encuentra el programa.
Hay 3 estados:

  • Estado 0 - Inicial
    • Es el primer estado cuando encendemos el cronometro
    • En este estado lo que hacemos es:
      • Mostrar el tiempo por pantalla (la función imprimirTiempo()
      • Pasar directamente al siguiente estado (state = E_CONFIG_TIME;)
  • Estado 1 - Configurar tiempo
    • Lo primero es llamar a la función configTiempo(), que sirve para configurar los minutos y segundos de cuenta atrás
    • Luego tenemos dos if's
      • El primero (ev_reset()) hace que si apretamos el botón reset reiniciamos el cronometro a 0 y volvemos al estado inicial
      • El segundo (ev_start()) hace que si apretamos el botón start activamos el relé y vamos al estado cuenta atras (state = E_CUENTA_ATRAS;)
  • Estado 2 - Cuenta atras
    • En este estado hay un if que miras si se ha acabado la cuenta atrás o se ha pulsado el boton reset. Si no pasa ningun de estas dos acciones empezamos la cuenta atrás (llamando a la función cuentaAtras();) y la seguimos ejecutando hasta que uno de estos dos eventos ocurran
    • Si se acaba la cuenta atrás o se aprieta el botón reset lo que haremos es:
      • Desactivar relé (desactivarRele();)
      • Reiniciar el contador a 0 (resetTiempo();)
      • Dirigirnos al estado inicial (state = E_INICIAL;)


Bloque 4 - Funciones

En el bloque 4 nos encontramos todas la funciones que utilizamos durante el loop. Las voy a listar y explicar brevemente:

  • boolean ev_start()
    • Utilizando las fórmulas del objeto Bounce miramos si se ha apretado el botón start
    • Si se ha apretado se devuelve true (ATENCIÓN si el pulsador está en LOW quiere decir que está pulsado. Esto es debido a que la lógica se invierte porque utilizo resistencias pullup en vez de pulldown)
    • Sino se ha apretado se devuelve false
  • boolean ev_reset()
    • Lo mismo que en la función de ev_start pero con el botón reset
  • void imprimirTiempo()
    • Función que se encarga de escribir en el LCD el tiempo
  • void configTiempo()
    • Configura el tiempo de la cuenta atrás según los botones que apretamos
    • Si se llega a 59 segundos se pasa a 1 minuto y los segundos pasan a 0
    • Si se llega a los 59 minutos se pasa a 1 hora y se pone 0 en minutos
    • Tengo en cuenta que no se puede bajar de 0 segundos ni de 0 minutos
    • Cuando acabo de configurar el tiempo llamo a la función imprimirTiempo() para actualizar la pantalla
  • void resetTiempo()
    • Coloco a 0 los campos del struct cuenta (horas, minutos, segundos y decimas)
  • boolean ev_fin_cuenta_atras()
    • Si los minutos, segundos y decimas llegan a 0 devuelve true
  • void cuentaAtras()
  • void activarRele()
    • Escribe HIGH en el pin RELE
  • void desactivarRele()
    • Escribe LOW en el pin RELE


Máquina de estados

[editar]




Galeria

[editar]