Académique Documents
Professionnel Documents
Culture Documents
CARÁTULA
TESIS DE GRADO
EN INGENIERÍA ELECTRÓNICA
Autores:
Profesores:
2012
1
II. AGRADECIMIENTOS
Agradecemos las colaboraciones de los docentes Daniel Jacoby y Miguel Aguirre, quienes han
aportado valiosa información y apoyo en materia técnica, lo que posibilitó el desarrollo del presente
trabajo.
También expresamos nuestro agradecimiento a nuestros padres, quienes han apoyado nuestros
estudios desde sus inicios. Además, a todos nuestros compañeros, amigos, docentes y personal no
docente que ha convivido con nosotros, dentro del instituto, durante estos cinco años.
2
III. ÍNDICE
I. CARÁTULA 1
II. AGRADECIMIENTOS 2
III. ÍNDICE 3
IV. RESUMEN 6
V. INTRODUCCIÓN 7
1. HISTORIA, ANTECEDENTES 7
2. DEFINICIONES Y GLOSARIO DE TÉRMINOS 7
3. JUSTIFICACIÓN DEL PROYECTO 8
4. OBJETIVOS 8
VII. DEFINICIÓN DEL PRODUCTO 9
1. REQUERIMIENTOS DEL SISTEMA 9
1.1. Requerimientos vs. Especificaciones: Casa de Calidad 10
2. ESPECIFICACIONES 11
2.1. Especificaciones del Hardware 11
2.2. Especificaciones del Software 14
VIII. ANÁLISIS DE FACTIBILIDAD 16
1. FACTIBILIDAD TECNOLÓGICA 16
1.1. Propuesta de Alternativas de Diseño 16
1.2. Elección de una Solución 16
1.3. DFMEA 17
2. FACTIBILIDAD DE TIEMPOS 20
2.1. Planificación (PERT) 20
2.2. Simulación de Montecarlo 22
2.3. Diagrama de Gantt 23
3. FACTIBILIDAD ECONÓMICA 26
4. FACTIBILIDAD LEGAL Y RESPONSABILIDAD CIVIL 30
IX. INGENIERÍA DE DETALLE 31
1. HARDWARE 31
1.1. Diagrama de Bloques 31
1.2. Descripción Detallada de Cada Bloque 31
1.3. Detalles de Selección y Cálculo de los Elementos Circuitales de Cada Bloque 33
1.4. Plan de Pruebas de Cada Módulo 37
2. SOFTWARE 37
2.1. Diagrama de Estados y Flujogramas 38
2.2. Análisis de Complejidad 38
3
2.3. Descripción de Subrutinas 39
2.4. Listados Comentados del Código 42
2.5. Plan de Prueba de Módulos y de Depuración de Soft 68
X. CONTRUCCIÓN DEL PROTOTIPO 69
1. DEFINICIÓN DE LOS MÓDULOS 69
1.1. Gabinete 69
1.2. Placas impresas con sus componentes 69
1.3. Circuito de alimentación 70
1.4. Módulo de censado de temperatura 70
2. DISEÑO DE LOS CIRCUITOS IMPRESOS 70
3. DISEÑO MECÁNICO 73
4. DETALLES DE CONSTRUCCIÓN Y PRECAUCIONES DE MONTAJE 74
XI. VALIDACIÓN DEL PROTOTIPO 76
1. VALIDACIÓN DEL HARD 76
1.1. Plan y protocolos especiales de medición 76
1.2. Medidas 77
1.3. Evaluación 86
1.4. Resultados 87
2. VALIDACIÓN DEL SOFT 87
XII. ESTUDIOS DE CONFIABILIDAD DE HARD Y SOFT 88
1. CONFIABILIDAD DE HARDWARE 88
2. CONFIABILIDAD DE SOFTWARE 89
XIII. CONCLUSIONES 91
1. EXCELENCIAS Y OBJETIVOS ALCANZADOS 91
2. FALLOS Y RECOMENDACIONES PARA FUTUROS DISEÑOS 91
XIV. ANEXOS 92
1. ANÁLISIS COMPLETO DE FIABILIDAD 92
2. LISTADO DE COMPONENTES Y SUS COSTOS 93
2.1. Componentes del equipo: 93
2.2. Componentes de la placa lógica 94
2.3. Componentes de la placa de potencia: 95
2.4. Componentes utilizados para los termómetros: 95
2.5. Accesorios para automatizar la instalación: 96
2.6. Valor de las instalaciones: 96
2.7. Insumos utilizados para la fabricación de cerveza para validar el prototipo: 97
3. SOFTWARE COMPLETO 97
3.1. Archivo main.c 97
4
3.2. Archivo usr_entry.c 98
3.3. Archivo SystemInit.c 99
3.4. Archivo display.c 106
3.5. Archivo Temperatura.c 108
3.6. Archivo botones.c 112
3.7. Archivo Salida.c 112
3.8. Archivo Ejemplo_sistema.c 115
3.9. Archivo JM128_Bootloader.h 115
3.10. Archivo display.h 115
3.11. Archivo Temperatura.h 116
3.12. Archivo maindefs.h 116
3.13. Archivo botones.h 116
3.14. Archivo Salida.h 117
3.15. Archivo Proceso.h 118
4. ESQUEMÁTICOS 121
XV. BIBLIOGRAFÍA 123
1. Hojas de datos 123
2. Información acerca de la elaboración de cerveza 123
3. Libros 123
5
IV. RESUMEN
6
V. INTRODUCCIÓN
1. HISTORIA, ANTECEDENTES
La producción de cerveza artesanal, tanto a nivel industrial como hogareño, implica una
participación activa y constante por parte del productor en todo el lapso de producción de un batch (ver
glosario de términos), el cual puede durar desde unas 5 horas, hasta más de 10 horas, dependiendo de la
receta y del estilo elegidos. El maestro cervecero debe realizar mediciones de densidad, tiempos y
temperatura constantemente, de modo de llevar un control de su producción, ya que estos parámetros son
los indicativos del estado del proceso.
7
3. JUSTIFICACIÓN DEL PROYECTO
Este proyecto busca automatizar el proceso de producción de cerveza artesanal, con el mínimo
costo posible. Con este producto, el maestro cervecero podrá optimizar su tiempo, ya que el sistema,
programado e instalado adecuadamente, realizará la mayoría de las actividades que antes debían
realizarse manualmente. El módulo tendrá un sistema de alarmas que le avisará al productor cuándo debe
prestar atención, tomar una medición de densidad, incorporar aditivos, etc.
4. OBJETIVOS
8
VII. DEFINICIÓN DEL PRODUCTO
Con el fin de determinar las especificaciones que tendrá el proyecto, se buscaron cuáles son los
requerimientos que le interesan a un potencial cliente. Para este fin, se realizó un estudio de mercado muy
breve, pero efectivo. Se optó por interrogar a unos pocos distribuidores de productos y equipamientos
para cervecerías artesanales. Este estudio ignora la opinión del cliente final, pero se basa en un punto de
vista muy valioso, ya que los distribuidores consultados son proveedores de nuestros potenciales clientes,
por lo que conocen muy bien sus gustos y preferencias, y también tienen un dominio consistente del
mercado que manejan. Se les envió un mail comentando brevemente la idea del proyecto, y pidiendo
sugerencias, basadas en su experiencia y conocimiento del mercado. La respuesta fue muy positiva, ya
que la mayoría de los distribuidores que respondieron se mostraron interesados en la idea. Las
conclusiones más relevantes que se obtuvieron fueron las siguientes:
A nivel artesanal, para pequeñas productoras de cerveza artesanal, no existe en el país un producto
similar. (Obviamente, las grandes cervecerías sí están automatizadas, pero no forman parte del
target considerado en este proyecto).
El requerimiento más importante se encuentra en el precio. El producto más parecido que se puede
conseguir es un controlador que maneja únicamente curvas de maceración programables. Este
atributo sería solamente una parte de nuestro proyecto. En este sentido, se propone como meta, a
nivel económico, igualar el precio de venta de este producto ofreciendo, como valor agregado,
muchas más prestaciones. Este controlador se vende por 520 dólares. Si se logra un costo de
producción que no supere los 300 dólares, se podría vender el producto al mismo precio, con un
margen de ganancia de poco más del 40%.
Resultaría atractivo incorporar (fue una de las sugerencias más interesantes) un caudalímetro o
algún sensor que sea capaz de medir, directa o indirectamente, los volúmenes de líquidos dentro
de los recipientes. En función de los costos y alternativas, se evaluará esta posibilidad como un
agregado, ya que un sensor de caudal resultaría muy caro, contradiciendo el requerimiento
fundamental de un precio bajo.
Se deberá implementar una alarma sonora y visual que se active en caso de alguna falla, o lectura
anormal de algún sensor, o cuando se requiera la atención del usuario.
En todo momento, se deberá poder habilitar el control manual para corregir cualquier desperfecto o
anomalía, en caso de emergencia, por ejemplo, por un corte de luz.
El sistema deberá poder manejar una cantidad considerable de periféricos (válvulas, bombas,
sensores de temperatura, etc.).
La interconexión con los periféricos deberá ser sencilla, dando un margen mínimo para errores
humanos de conexión. Los cables deberán tener la menor cantidad de líneas posible, para que los
conectores sean simples.
El módulo principal tendrá una corriente de salida necesaria para manejar circuitos de relé de
estado sólido (triacs), que a su vez controlarán periféricos que se alimentan de la red eléctrica
(bombas, electroválvulas, etc.).
9
Los periféricos deben poder ubicarse razonablemente lejos del módulo principal, conectados por
cables sencillos, según las dimensiones de la planta productora, sin que las señales se vean
distorsionadas como para dar lugar a fallas en el control.
El módulo principal deberá adaptarse para poder controlar varios periféricos al mismo tiempo, en
el caso de que se tenga una cervecería con varios fermentadores, por ejemplo.
El módulo principal contará con una pequeña pantalla que indique temperaturas censadas, estado
del proceso, alarmas o fallas, etc. Tendrá la cantidad mínima e indispensable de botones. Todas
las configuraciones se harán previamente al proceso.
El sistema tendrá un programa de producción por defecto, el cual podrá ser modificado mediante
una interfaz simple que permita programar el proceso desde una PC.
El módulo principal solamente hará los cálculos y manejará las señales de control necesarias,
mientras muestra el estado del proceso en la pantalla LCD.
Todo el sistema electrónico tendrá que consumir la menor cantidad de potencia posible.
Una herramienta fundamental para poder organizar y cumplir los requerimientos del cliente es la
casa de calidad. Esta nos permite saber cómo estamos parados frente a los mismos y como está la
competencia respecto a éstos también. A continuación se desarrolla la misma.
10
Fig.1. Casa de calidad
2. ESPECIFICACIONES
Sus dimensiones serán acordes a la necesidad de inclusión de enchufes para controlar los distintos
periféricos y dimensión de las placas, pero deberá ser portable. Por lo tanto, se acotarán sus
dimensiones de la siguiente forma: alto máximo: 10cm +/- 5cm, largo máximo: 40cm +/- 10cm,
profundidad máxima: 30cm +/- 10cm.
11
Por ser un elemento preparado para permanecer fijo sobre, por ejemplo, un escritorio, no es
necesario que sea extremadamente liviano. Se acotará su peso máximo en 10Kg +/- 5Kg , de
modo que pueda moverse con comodidad.
Los enchufes de interconexión con los periféricos serán hembras, y se ubicarán de la siguiente
forma: la mitad en la parte posterior y la otra mitad en la parte anterior del módulo. El conector
será una bornera doble por cada salida (vivo y neutro).
En la parte superior, se ubicará una pequeña pantalla capaz de mostrar caracteres ASCII, indicando
el estado del proceso, temperaturas, etc. La pantalla estará orientada en la parte superior
izquierda del gabinete. Además, sobre el gabinete habrá botones de control, para, por ejemplo,
aceptar alguna orden del sistema. También habrá leds de distintos colores para indicar: equipo
encendido, salidas encendidas, aparición de alarmas, etc.
La fuente de alimentación será interna, del tipo lineal, para minimizar la complejidad técnica. La
tensión se convertirá a 5V +/- 0,1V de continua, es decir, con un ripple máximo de 100mV, de
modo tal que garantice el correcto funcionamiento del microprocesador, y deberá entregar una
corriente de al menos 300 mA para alimentar al control electrónico, al microprocesador, a los
sensores de temperatura, a la pantalla, led de encendido, etc.
Habrá una segunda fuente de alimentación de 5V +/- 0,2V, que se encargará de alimentar a los
optoacopladores (optotriacs utilizados para drivear los triacs de potencia) y los leds indicativos
de control de periféricos. El ripple de salida de ésta no es relevante (se acepta hasta 0,2V de
ripple máximo). La corriente de salida deberá ser de al menos 600mA, asumiendo que se active
la totalidad de las salidas al mismo tiempo.
El sistema contará con, al menos, un enchufe hembra USB, que permita conectar el módulo a una
PC.
En cuanto a las salidas para conectar periféricos, se colocarán 24 salidas controladas por triacs
(para manejar bombas de agua de 220V, electroválvulas, etc., con la posibilidad de entregar
hasta 2A por salida, es decir, hasta 440W de potencia). Además, habrá 14 salidas (todas
conectadas internamente, con conectores especiales polarizados para facilitar el conexionado)
con protocolo “1-Wire” para conectar los termómetros digitales (por limitaciones del protocolo
elegido, se pueden manipular hasta 255 termómetros simultáneamente, lo cual excede
ampliamente las necesidades mínimas). Esta cantidad de salidas excede el uso normal necesario
en un proceso estándar. No obstante, se elige la mencionada cantidad de salidas para posibles
extensiones futuras. Todas las salidas serán programables, y accesibles al usuario.
En conclusión, a partir de las especificaciones previas, se pueden resumir las características del
equipo en la siguiente lista:
Dimensiones del equipo: alto: 4,5cm. +/-0,5cm.; largo: 35cm. +/- 0,5cm.; profundidad: 17cm. +/-
0,5cm.
Fuente: lineal, con transformador de 9VAC de salida, rectificada y filtrada, con capacidad para
entregar 1,5A efectivos. Esto alimentará reguladores de tensión para obtener 5VDC +/-
0,05VDC.
Interfaz con el usuario: se utilizará un display LCD armado de 16x2 caracteres. Habrá un led de
encendido del equipo (rojo). Habrá 7 botones: 4 para direcciones (arriba, abajo, derecha,
izquierda), 1 de MENU, uno de ENTRAR, y uno para controlar la luz de la pantalla LCD.
Además, habrá 24 borneras numeradas, cada una con su led de encendido, y 14 conectores para
termómetros.
12
Del microprocesador:
El cable de alimentación del equipo para la red eléctrica será de 3 patas, haciendo una conexión
apropiada a masa de todo el gabinete, como se establece en las normativas vigentes de seguridad
eléctrica.
Los cables de interconexión con los periféricos serán de un solo recubrimiento, albergando en su
interior la cantidad de líneas de cobre necesarias según la potencia consumida por el periférico.
Los cables de censado de temperatura tendrán que soportar temperaturas de hasta 100ºC, al igual
que el material utilizado para su cobertura de sellado contra líquidos.
El cable USB se comprará armado.
13
Otras consideraciones y aclaraciones
El tipo de bombas y válvulas a utilizar será acorde al nivel productivo de la planta. Su elección y
cálculo queda fuera de los límites de este diseño. El sistema controlará switches de encendido y
apagado (relés de estado sólido, con triacs) de las mismas, únicamente.
El largo de los cables será variable según la distancia entre el periférico y el módulo principal.
Dicha distancia depende de la disposición de los elementos en la planta. En el prototipo se
utilizará el largo adecuado al caso concreto.
Se asume que el cliente ya posee una instalación capaz de producir cerveza. Este diseño tiene la
intención de automatizarlo, pero no de diseñar el proceso. Por esta razón, la elección de ollas,
fermentadores, maduradores, quemadores, etc., queda fuera de los límites de este diseño. Para el
prototipo se utilizarán los elementos necesarios para demostrar el funcionamiento del sistema.
Según el tamaño de los contenedores (ollas, fermentadores, etc.), se podrá colocar más de un
sensor de temperatura por contenedor, para mejorar la precisión de la medición de la temperatura
media, considerando distintos puntos.
Resultaría deseable poder fijar una cota máxima para el costo total del sistema en US$ 300.
14
usuario. Esta condición de secuencialidad resulta más que suficiente para efectuar un
proceso completo de producción, así como también de lavado del equipo, o de fermentación,
permitiendo buenas prestaciones con una dificultad mínima a la hora de diseñar el
programa.
Las acciones correspondientes al proceso podrán depender de parámetros propios de cada
receta. Así, por ejemplo, una receta tendrá una curva de maceración con 3 mesetas de 30
minutos, y otra receta tendrá una sola meseta a una sola temperatura. Las acciones serán las
mismas, pero cambiarán las condiciones de finalización, valores de temperatura, etc. De este
modo, con un mismo equipo, programado con la misma serie de acciones, se podrán realizar
distintas recetas de cerveza.
15
VIII. ANÁLISIS DE FACTIBILIDAD
1. FACTIBILIDAD TECNOLÓGICA
Para el caso de los sensores de temperatura, se optará por el uso de sensores DS18S20, que tienen
salida digital de una sola línea, y una precisión de 0,5ºC. Las electroválvulas compatibles con este diseño
utilizarán 220V de alimentación. El sistema está pensado para utilizar válvulas económicas del estilo de
un lavarropas. En cuanto a las bombas, serán con motores de inducción monofásicos, de hasta 40W, como
las utilizadas en lavarropas u otros electrodomésticos similares (de todos modos, las salidas serán
diseñadas para soportar cargas de hasta 440W, si la instalación requiriera de bombas u otros motores de
mayor potencia).
En suma, todos los componentes del proyecto se consiguen actualmente en el mercado nacional,
por lo que su adquisición es factible. Los periféricos, se recuerda, no se incluyen como parte del proyecto,
aunque también son fácilmente adquiribles, existiendo versiones de bajo costo, lo cual cumple con uno de
los objetivos primordiales del presente diseño.
Como algunos agregados opcionales, se puede mencionar un sistema de enfriamiento con celdas
Peltier, para favorecer y acelerar el proceso de enfriado, y para controlar adecuadamente la temperatura
de fermentación. También resultaría factible diseñar un sistema de agregado automático de lúpulo, a
partir de un contenedor dividido (en al menos 4 compartimientos) con una compuerta independiente para
cada división, que se abra en el momento indicado, independizando así al usuario de maniobras manuales
durante el hervor. Otro agregado interesante sería un sistema de llenado automático, con corte electrónico,
tanto para la cerveza fermentada, como para el adicionado de azúcar (en forma de almíbar, para permitir
gasificación natural en botella).
Como alternativa al diseño de salidas con triacs, se consideró la posibilidad de utilizar relés
(contactores) con bobinas. Esto otorgaría mayor versatilidad al equipo (por ejemplo, para manejar
tensiones distintas a las de la red eléctrica).
16
percibían reseteos ocasionales del microcontrolador, debido a picos de tensión en la alimentación durante
la conmutación de los relés. Se midió la amplitud de estos picos (que superaban los 10V pico, en la
alimentación de la placa lógica de 5V), y su frecuencia de transitorio (que oscilaba entre los 20MHz y
40MHz). Esta frecuencia puso en evidencia la naturaleza de este ruido de conmutación, de carácter
electromagnético. Además, como el problema se presentaba ocasionalmente, se determinó que su
aparición dependía de la tensión instantánea de la red eléctrica. Así, el pico de ruido aumentaba cuando la
tensión de 220VAC (50Hz) se encontraba cerca de un pico. Por este motivo, se determinó que la solución
sería un rediseño completo de ambas placas, si se seguían utilizando los relés con bobina. En cambio, se
optó por el uso de triacs con detector de cruce por cero, garantizando, de este modo, que la conmutación
se diera con tensión nula (o muy baja), eliminando el problema. Además, se cuenta con la ventaja de que
no aparece el típico ruido de conmutación de los contactos del relé. Este cambio no implica variaciones
considerables en el costo del equipo. En contraparte, se reduce la capacidad de entregar altos valores de
corriente por cada salida.
En cuanto a las fallas que se experimentaron utilizando relés de conmutación por bobina, se
pueden mencionar los siguientes hechos. La falla (evidenciada por un reseteo del microcontrolador) se da
cuando el relé se activa con sus bornes conectados a la línea (220VAC), y una carga (aunque sea mínima)
que haga circular una corriente. Entre las cargas que presentaron inconvenientes, se pueden mencionar:
motores, válvulas, un capacitor de 10nF, un varistor de 400V (incluso la mínima corriente de pérdida de
un varistor generaba reseteos). Cuando el relé no tenía ninguna carga en sus bornes, la conmutación no
traía inconvenientes. Tampoco se generaban reseteos cuando se desonectaba la tensión de línea del relé.
Esto puso en evidencia el carácter electromagnético del problema, descartando la posibilidad de
considerar a la bobina del relé como origen de la falla (la cual se conectó a un diodo de free-wheeling
disponible en el integrado ULN2003 que se utilizaba como driver).
Como recomendaciones para futuros diseños con relés controlados por microcontrolador, se
sugiere tratar de evitar este tipo de circuitos cuando no posean algún detector de cruce por cero de la
tensión o corriente de línea. Es posible que un mejoramiento en el diseño del circuito integrado y una
redistribución inteligente de las pistas pueda reducir el problema. No obstante, por la característica
electromagnética del ruido, resulta muy difícil generar un diseño que carezca de riesgos de fallas
utilizando este método. Algunos microcontroladores (el utilizado en este proyecto incluído) tienen un
circuito que monitorea la tensión de entrada y genera un reset si la misma cae por debajo de cierto valor.
Se probó deshabilitando (mediante la escritura de un registro) este circuito, pero la falla persistió, lo cual
sugiere que el pico de tensión era lo suficientemente importante como para apagar todos los circuitos del
micro. De hecho, se midió la tensión de alimentación (5VDC) durante la conmutación, y se observaron
picos que superaban los 10Vpp (incluyendo picos negativos, es decir, que invertían la polaridad de
alimentación, de hasta -5V), con un transitorio de estabilización que presentaba una frecuencia que
variaba entre 20MHz y 40MHz. Se puede afirmar que, al menos el microprocesador MCF51JM128 no
tolera este nivel de variaciones en su alimentación. También se sugiere, para estos circuitos, incorporar
algún tipo de snubber (por ejemplo, un RC serie en paralelo a la carga) para disminuir la amplitud de este
ruido.
1.3. DFMEA
17
Ítem Modo de S1 O1 Causas Control de Control de D1 R Acciones
falla prevención detección P recomendadas
N
Módulo No 8 2 Corte de Controlar Led de 1 16 Prever esta
principal enciende luz que siempre encendido situación en
esté todo momento
habilitado el y dejar
control habilitado el
manual del control manual
proceso
No 8 1 Daño de Diseñar para Led de 1 8 Diseñar la
enciende la fuente el peor caso encendido fuente de
de de consumo manera robusta,
alimenta- de corriente y que sea capaz
ción de entregar la
corriente
máxima que
pueda consumir
el sistema
No 6 1 Mala Revisar Vista de 1 6 Revisar las
enciende conexión conexión de caracteres en conexiones
la o la pantalla pantalla internas y
pantalla configura configuraciones
ción
Falla en 6 3 Daño de Controlar el No respuesta 3 54 Realizar
un la ficha correcto de un pruebas de
puerto hembra o funciona- periférico todos los
de salida de miento de puertos una vez
circuitos todos los armada la placa
internos puertos en el
prototipo
Microproce- Señales 8 5 Errores Realizar Mediante 6 24 Debuguear
sador distintas de debugueos mediciones de 0 todos los
a las programa de cada señales módulos, y
progra- ción módulo del generar
madas programa programas de
prueba cuando
hagan falta
Señales 8 1 Errores Revisar el Mediante 6 48 Medir
distintas de ruteo ruteo y las mediciones de continuidad de
a las y soldaduras señales los pines más
progra- soldadura críticos,
madas de la buscando
placa cortocircuitos y
conexiones
Sensores de No hay 6 3 Falla en Conectar Medición de la 5 90 Medir la
temperatura señal del el cable correctament señal de salida funcionalidad
sensor de e las líneas, del sensor de todos los
18
alimenta- y sellar muy sensores antes
ción bien para de utilizarlos
termómetros
sumergibles
Valor 6 2 Desper- Revisar los Medición de 5 60 Realizar un
medido fecto integrados un cambio programa
sin interno antes de muy brusco de optativo de
sentido del utilizarlos temperatura, o inicio que
sensor incoherente controle el
con la estado de los
sensación del termómetros
tacto digitales
Valor 2 3 Calibra- Calibrar Observación 4 24 Calibrar los
medido ción todos los de los valores sensores
con incorrec- sensores medidos previamente a
offset ta su conexión con
el sistema
Periféricos El 7 2 Mal Verificar No respuesta 2 28 Permitir el
(bombas, periféri- funciona- funciona- de un control manual
válvulas etc.) co no miento miento de periférico de cada salida
responde del cada para permitir el
dispositi- dispositivo chequeo de
vo que se conectado al todos los
desea equipo periféricos.
controlar
El 4 1 Falla en Verificar el El dispositivo 3 12 Verificar el
periféri- el estado de no se apaga, y correcto
co no se circuito todos los se enciende funcionamiento
apaga de triacs de solo al de cada salida,
cuando potencia potencia conectar el con carga
corres- de la equipo conectada. No
ponde salida en sobrecargar las
cuestión, salidas
con el exigiendo
triac mayor corriente
siempre que la permitida
cerrado
Cables de No hay 4 4 Rotura de Verificar el Medición de 2 32 Verificar el
interconexión señal una línea estado de los continuidad y estado de todos
del cable cables respuesta de los cables antes
los sistemas de iniciar el
proceso. Habrá
un programa
opcional de
inicio que
verifique
señales
Tabla 1. DFMEA
19
Se observa que las fallas a las que se le deberá prestar más atención son a los errores de
programación, y a la interconexión con los sensores. Para solucionar estos problemas, resultará crucial
realizar pruebas periódicas durante el diseño y el armado del prototipo, midiendo todas las señales
posibles, y debugueando el programa con pruebas y ejemplos simples.
Además, se producen fallas relativamente graves si hay fallas que perturben las mediciones (fallas
de un sensor, o de un puerto de salida). Esto tiene sentido, ya que el funcionamiento de todo el sistema se
basa en mediciones, y en el procesamiento de dichas mediciones para definir tareas de los actuadores.
Entonces, resultará imprescindible testear el correcto funcionamiento de todos los sensores utilizados en
el diseño, así como de todos los puertos de entrada y salida del sistema. Además, se implementará un
sistema de repetición de una misma medición, antes de tomarla como válida.
2. FACTIBILIDAD DE TIEMPOS
Como el proyecto presenta la dificultad de incluir tareas nunca antes realizadas, se deberán estimar
los tiempos de cada tarea estadísticamente. A continuación, se determinarán los tiempos esperados,
optimistas y pesimistas para cada una de las tareas mencionadas.
20
Tarea Precedentes Tiempo Tiempo Tiempo Promedio Desvío
optimista esperado pesimista
1 1 2 3 2 0,33
2 1 1 1 2 1,16 0,16
3 1 1 1 2 1,16 0,16
4 2 3 5 3,16 0,5
5 2,3,4 1 2 5 2,33 0,66
6 4 3 4 7 4,33 0,66
7 4,5,6 8 9 13 10 0,83
8 6 2 3 5 3,16 0,5
9 7,8 1 2 3 2 0,33
10 9 2 4 8 4,33 1
11 8 1 2 4 2,16 0,5
12 10,11 2 3 6 3,33 0,66
13 12 3 4 6 4,16 0,5
Con los datos anteriores, se puede construir un esquema basado en el modelo PERT:
21
A partir del mismo, podemos identificar el camino crítico:
El camino crítico está comprendido por las tareas 4,6,7,9,10,12,13. La suma de los tiempos medios
de las actividades críticas da una duración de 215,67 días (casi 31 semanas).
22
Fig. 5. Simulación de Montecarlo (tiempo en función de la probabilidad)
Se toma como fecha de inicio del proyecto el día 1 de abril de 2011. En este sentido, y habiendo
definido las actividades descriptas anteriormente, se realiza la siguiente tabla, donde se muestran las
fechas esperadas de inicio y finalización de cada tarea.
23
A partir de estos datos, se realizó el diagrama de Gantt, que permite estimar la fecha de
finalización del proyecto, respetando los tiempos asignados para cada tarea (diagrama realizado el 15 de
septiembre de 2011).
En base a este análisis, resultaba esperable completar el proyecto a fines de octubre de 2011, con lo
cual la totalidad del trabajo hubiese tenido una duración de poco más de 7 meses.
No obstante, la realidad fue muy distinta. Cuando se realizaron las pruebas en planta, con los
actuadores conectados, y las placas dentro del gabinete, surgió un problema grave: el microcontrolador se
reseteaba esporádicamente cuando el sistema cambiaba el estado de una salida. Esto llevó a una
evaluación de la causa del problema, y se resolvió rediseñar la placa de potencia. A partir de este
momento, todo el proyecto se vio seriamente retrasado, ya que tuvieron que redefinirse varias cuestiones
a partir de este inconveniente. Además, comenzó el receso de verano durante el cual no se trabajó. A
continuación, se muestra una lista completa de las actividades que se habían completado, junto con las
que debieron sumarse hasta la fecha de redacción del presente trabajo.
24
Fig. 8. Listado de tareas implementadas realmente
A partir de esta nueva y completa lista de actividades, se realizó un nuevo diagrama de Gantt, que
refleja las duraciones aproximadas reales de las actividades.
25
Fig. 9. Diagrama de Gantt correspondiente a las actividades realizadas realmente
Se observa que el tiempo total del proyecto aumentó considerablemente respecto de lo que se había
planificado. Esto surgió a partir de un imprevisto grave que llevó al rediseño de varias cuestiones.
A modo de autocrítica, se puede afirmar que la actividad 10 (pruebas de funcionamiento) no se
desarrolló de forma correcta, ya que el mencionado error no surgió sino hasta realizar las pruebas en
planta. Además, se subestimó el tiempo que llevaría preparar todo el equipo para probar el prototipo.
Todo esto dio como resultado una duración de casi 16 meses para completar el proyecto que, si se
descuentan los 70 días tomados de receso de verano, se puede afirmar que el tiempo real se duplicó
respecto de lo planificado.
3. FACTIBILIDAD ECONÓMICA
Se comenzará el análisis de factibilidad económica con una breve descripción del mercado de
productores de cerveza artesanal. El mismo incluye tanto a pequeñas cervecerías artesanales, como a
productores independientes, que fabrican para consumo personal. El producto intenta optimizar el proceso
de elaboración de cerveza, ya sea a nivel casero o de cervecerías pequeñas, mediante la automatización
del mismo. En el país no existe un producto similar disponible, lo cual resultará una ventaja a la hora de
capturar el mercado.
El mayor costo del sistema por unidad estará dado por las placas, el gabinete y sus componentes,
junto con el tiempo de diseño. Como se especificó anteriormente, en principio se pretende acotar los
costos a 300 dólares por unidad ($1380). Esto permitiría obtener un precio de venta de poco más de 500
dólares ($2300), compitiendo con el único producto que se ofrece en el mercado: un controlador de
maceración, que controla directamente electroválvulas y puede programar hasta 9 escalones de
temperatura. El presente diseño tendrá más atributos, ya que controlará no solamente la maceración, sino
también el enfriado, hervor, fermentación, etc. El valor agregado se centra en estos atributos extra,
pudiendo así ofrecer un producto mucho más versátil, al mismo precio que la competencia (o algo
parecido a ella).
26
Los costos involucrados en el desarrollo del prototipo de resumen en la siguiente tabla. Se pueden
verificar las tablas completas, con todos los componentes del equipo en el anexo correspondiente.
Las filas verdes representan el costo de materiales por unidad, es decir, $836,74, que es un número
de suma importancia en el análisis económico del proyecto. El valor fijo de las instalaciones no se tendrá
en cuenta como costo del proyecto, por ser un costo hundido (existe independientemente de la puesta en
marcha del proyecto), dado por la disponibilidad de elementos que poseía uno de los autores. Las filas
amarillas y celeste son los costos que implicaron el montaje de un sistema capaz de validar el prototipo,
que suma un total de $1035,28. Además, se realizó una placa con relés mecánicos que no dio resultado, se
elaboraron unos 20 litros de cerveza negra para verificar el funcionamiento de las instalaciones, y se
realizaron placas de prueba (para el micro, para relés, etc.). Todo esto supone un costo que oscila entre los
$500, y que será considerado como un costo adicional para la validación del prototipo, sumando entonces
unos $1535,28.
Entonces:
Se considerará un costo inicial dado por los componentes del prototipo, y el tiempo insumido en el
proceso de diseño. Para esto, se consideran 320 horas de trabajo, (160 horas por cada integrante) que
recibe un sueldo de $5000 en 4 semanas, trabajando 8 horas, 5 días a la semana. Esto da lugar a un tiempo
de diseño de 1 mes, si se trabajara en tiempo completo (los tiempos reales expresados en el diagrama de
Gantt no son para trabajo de tiempo completo, ya que el trabajo se realizó en forma cortada, utilizando el
tiempo disponible de cada integrante). Entonces, el costo de mano de obra corresponde a un mes de
trabajo de dos personas, es decir, $10000. Se definió un presupuesto de $837 para construir el prototipo,
unos $1536 para validarlo, lo cual da un costo total de diseño de $12373.
Se había definido el costo máximo de materiales de cada unidad en $837, a lo que debe sumarse la
mano de obra. Para esto, se considera un tiempo de trabajo de 16 horas por cada equipo construido,
probado e instalado. Asumiendo el mismo nivel salarial que el expresado anteriormente, la mano de obra
27
cuesta $31,25 por hora, dando lugar a un costo de $500 por equipo en concepto de mano de obra.
Estableciendo un precio de venta final de $2300 (IVA incluido), se tiene una rentabilidad por unidad e
$963, dada por:
Para evaluar la factibilidad económica del producto, se supondrá una venta mensual de 3 unidades,
durante el primer año, aumentando a 4 el segundo año, y 3 el tercero, suponiendo que el producto se irá
consolidando dentro del mercado con el paso del tiempo, y perderá su crecimiento a los dos años porque
el mercado comenzará a saturarse. En este sentido, se define el ciclo de vida de este producto en 3 años a
partir de su lanzamiento (si el producto perdura más, el proyecto tendrá mejores resultados que los
previstos, pero se toma éste como peor caso). Además, estos valores suponen que en el transcurso de unos
siete meses se habrá recuperado la inversión hecha en el diseño. Como se planifica la realización de pocas
placas, las mismas se desarrollarán en baja escala, y en forma manual, al igual que el soldado de los
componentes y puesta a punto.
El valor actual neto del producto está dado por (se considera una tasa de interés (libre de riesgo) de
10% anual, con los valores expresados en pesos):
n
FC
E o
VAN i
Otro costo a tener en cuenta es el de Ingresos Brutos. Esto corresponde al 3,5% de las ventas
anuales, lo que daría: $2898 el primer año, $3864 el segundo y $2898 el tercero, dando un total de $9660.
Si se consideran las ventas de cada año y se prorratean estos valores a cada equipo, se obtiene una
ganancia neta de $882 en lugar de $963.
Además de ingresos brutos, se calculó la diferencia de IVA que se generó en cada ciclo con los
flujos de caja de cada año obtenidos en las proyecciones de ventas y que debe abonarse al fisco. Esto
quiere decir que en cada flujo de cada año hay un 21% que se debe devolver al estado y que no debe
influir en el análisis del proyecto ya que no es dinero redituable para el empresario. Al resultado de estas
operaciones hay que reducirle un 35% de impuesto a las ganancias. El siguiente cuadro de resultados
muestra lo expresado anteriormente.
28
Entonces la VAN queda se expresa de la siguiente manera.
Se observa que el valor actual neto resulta superior a cero, considerando una tasa de interés del
10% (que es mucho más elevada de lo esperable, suponiendo condiciones estables; se tomó este valor
para situarnos en un caso desfavorable).
n
FCi
VAN´ Eo 0
i1 1ti i (5)
El valor (calculado por iteración) para ti es 1,047, es decir, que se obtiene una tasa de interés
equivalente al 104,7% anual.
A continuación se observa el flujo de caja correspondiente a los 3 años proyectados. Se espera una
meseta en el flujo luego del año tres que concluye con una estabilización en el mercado. De esta manera,
en el siguiente gráfico aparecen los flujos finales de caja de cada año.
Flujos de caja
20000
15000
10000
5000
0
0 0,5 1 1,5 2 2,5 3 3,5
-5000
-10000
-15000
Fig. 10. Gráficoque representa los flujos de caja a través de los años
29
4. FACTIBILIDAD LEGAL Y RESPONSABILIDAD CIVIL
Por ser un sistema eléctrico, el producto necesitará un certificado de seguridad eléctrica, ya que se
alimentará con los 220Vrms de la línea, ya que todos los aparatos eléctricos y electrónicos que funcionen
con más de 24V requieren esta certificación.
En cuanto a la fuente de alimentación, se implementará con circuitos del tipo lineal, con el fin de
simplificar el desarrollo, y abaratar los costos de componentes.
30
IX. INGENIERÍA DE DETALLE
1. HARDWARE
Fuente de alimentación
La fuente de alimentación será una fuente lineal con una entrada de 220VAC. La misma se llevará
a un valor de 9VAC por medio de un transformador, que trabajará a la frecuencia de línea (50Hz), y
podrá entregar una corriente de hasta 2A. Esta salida será rectificada mediante un puente completo
realizado con diodos del tipo 1N4007. Para filtrar el ripple de línea, se colocará un capacitor de 470uF.
De esto modo, se obtiene una tensión de unos 12 VDC, que alimentará 3 reguladores con salida de 5V de
31
continua, uno utilizado para el bloque de control, y otros dos alimentando cada uno la mitad de los
circuitos de salida de potencia (entrada de led de los optotriacs, y led de encendido de cada salida).
Placa de procesamiento
Incluye al microprocesador, que será un Coldfire V1, de la línea Freescale, con encapsulado tipo
LQFP de montaje superficial. El mismo tiene 64 pines de salida, que pueden multiplexarse para llegar a
trabajar hasta con 51 puertos de I/O. Según los programas de debugueo empleados, se definirán los
puertos de I/O utilizados por el programa para favorecer al máximo el layout de la placa. Los pines de I/O
se utilizarán del siguiente modo:
24 pines para manejar las salidas de potencia.
11 pines para controlar el display (8 de datos, y 3 de control).
7 pines de entrada para los 7 botones del equipo.
1 pin para controlar la luz del display.
1 pin para activar un buzzer para una alarma sonora.
3 pines de entrada auxiliares para actualizaciones futuras.
1 pin para protocolo “one-wire” que controla los termómetros.
1 pin para cambiar de modo: bootloader (grabación de firmware) o programa de
usuario.
Esto nos da un total de 49 pines utilizados como I/O. Además, se tienen:
2 pines para conectar un oscilador externo
2 pines de grabación por hardware (protocolo BDM): pin de /RESET y BKDBG.
2 pines para comunicación USB.
1 pin de regulación de 3,3V para USB (no utilizado).
4 pines de referencia para los A/D (VrefL, VrefH, Vssa, Vdda) (no utilizados).
2 pines de masa (Vss).
1 pin de alimentación (Vdd).
1 pin de interrupción externa por hardware (IRQ) (no utilizado).
De este modo se completa el uso de los 64 pines del encapsulado del microcontrolador utilizado.
Solamente quedan sin un uso específico 5 pines (es decir, se aprovecha más del 92%). Las 24 salidas
físicas del microcontrolador, irán directamente a un conector IDC (de 26 vías, 24 de datos, una de
referencia de masa, y otra sin uso), y tendrán una entrada con buffer en la placa de salida, para minimizar
la corriente que debe entregar el microcontrolador.
Sobre esta misma placa se colocará el led de encendido, la placa armada con el display, los 7
botones con sus filtros pasivos, el buzzer, 14 conectores para conectar circuitos de medición de
temperatura digitales, y un conector USB hembra para carga de firmware.
Esta placa contiene una bornera para la entrada del transformador (9VAC), con el puente de diodos
y capacitor de filtro correspondiente. También incluye los 3 reguladores mencionados (del tipo 7805, con
encapsulado TO220) para entregar 5VDC regulados y filtrados con capacitores. Se tienen además 4
borneras para alimentar con la tensión de línea (220 VAC) a 4 grupos de 6 salidas de alterna (esto se hace
para permitir alimentación independiente por grupos de 6 salidas, para el caso de que se requiera mucho
consumo; se contempla hasta unos 2,64KW por grupo, es decir, hasta 440W por cada salida, como
máximo).
Por otro lado, se tiene un conector central IDC, de 26 vías, para conectar fácilmente las líneas de
control. Estas líneas entran a 4 integrados de 6 buffers cada uno. A la salida de cada buffer, se conecta el
32
optotriac y el led de encendido de cada salida. A su vez, este optotriac dispara el triac de potencia, que
cerrará el circuito de los 220VAC que pasan por una bornera. Este circuito tiene a su salida un snubber
del tipo RC serie, conectado en paralelo con el triac, para proteger al mismo de posibles picos dados por
cargas inductivas. Esto se repite 24 veces, como se dijo anteriormente, con las salidas numeradas y
agrupadas de a 6.
Constará del sensor de temperatura (cuya descripción se detalla en el siguiente ítem: 9.1.3), con un
acondicionamiento físico para soportar el ambiente de trabajo. Cada sensor se protegerá con un tubo de
acero inoxidable, con un bulón roscado en un extremo, y sellado con pasta de teflón, que será sumergido
en el líquido cuya temperatura se desea medir. Las conexiones se protegerán con termocontraíble para
evitar cortocircuitos con el tubo de acero. Se utilizarán cables del tipo telefónico, utilizando 3 de sus 4
líneas. Se aprovecha este tipo de cables por tener buena resistencia a la temperatura (se testeó su
comportamiento sometido a 100ºC, con excelentes resultados) y bajo costo. En el otro extremo se soldará
el conector hembra, polarizado, compatible con los utilizados en la placa de control.
Microprocesador:
Se utilizará un transistor PNP tipo BC547, por ser estándar y versátil, con
buena disponibilidad y bajo costo.
Triacs de potencia:
Se utilizarán triacs del tipo BTA08600, con capacidad de hasta 8A. El mismo
tiene un encapsulado TO220AB. Como la potencia máxima de salida estará dada por la
capacidad de disipación térmica, se establece la corriente máxima de paso por este dispositivo
según los siguientes criterios. Esta limitación de corriente se definió teniendo en cuenta que no
se utilizarán disipadores, ya que éstos ocuparían mucho espacio, y aumentarían mucho el costo
del equipo.
33
Según los datos proporcionados por el fabricante, se tiene:
Rja 60º C / W
Tj(max) 125º C (7)
Fig. 12. Gráfico de potencia disipada en función de la corriente (para triac de potencia)
A partir de este análisis, se determina que la corriente máxima rms admisible es de 2A, lo cual
permite cargas con una potencia de hasta 440W. Esto establece un límite importante para la capacidad de
carga del circuito. No obstante, estos números permiten trabajar, por ejemplo, con bombas que trabajen
con un motor de 0,5HP (unos 373W), que resultan más que suficientes para las aplicaciones previstas
para el equipo.
Display LCD:
34
mercado local. Permite iluminarse con un led de backlight, driveado por un transistor, como se explicó
anteriormente.
Conector USB:
Se eligió un conector USB hembra, tipo A, de montaje para placa. Este conector permite
conectar el equipo a una PC para actualizar el firmware. Además, se contempla la posibilidad de
actualizar futuras versiones del programa que puedan leer y escribir un pen drive, que puede conectarse
en este tipo de conectores. Se eligió el protocolo USB por estar soportado por todas las computadoras
modernas, por tener muy buena velocidad, y porque no aumenta considerablemente el costo del equipo.
Todos los microcontroladores modernos soportan esta interfaz.
Sensor de temperatura:
Placa de potencia:
Este circuito se repite 24 veces en la placa de potencia. Como se explicó anteriormente, cada buffer
(integrado CD4050, con 6 buffers por integrado), se alimenta con 5VDC suministrados por un regulador
del tipo 7805. El led que muestra el estado de la salida, se eligió de color verde, y tiene una caída de
tensión aproximada de unos 2,4V. En este sentido, para entregarle alrededor de 8mA (con lo que se
obtiene una luminosidad baja, pero visible, con bajo consumo y larga vida útil para el led), se requiere un
valor de resistencia de:
35
V 5V 2,4V
R 325
I 0,008 A (9)
Ubicando el próximo valor comercial de resistencia disponible, se optó por utilizar un valor de
330Ω para R2. Análogamente, y sabiendo que el led del optotriac requiere una corriente de 15mA para
encender el triac, y tiene una caída de tensión típica de 1,3V (valores obtenidos de la hoja de datos),
operando análogamente se obtiene:
V 5V 1,3V
R 246,67
I 0,015 A (10)
En este contexto, se utiliza el valor comercial próximo más bajo, para garantizar la corriente
mínima, es decir, un valor para R1 de 220 Ω. Los valores de R3 y R4 se eligieron siguiendo las
recomendaciones del fabricante, ya que constituyen resistencias de bias que actúan en el circuito interno
del MOC3041. Este optotriac se eligió por ser el integrado de la serie MOC más económico del mercado
(a costa de un mayor consumo en su led de entrada) y tener un circuito de detección de cruce por cero, lo
cual garantizará que el encendido o apagado de un periférico se de con tensión nula (o muy baja), de
modo de no dar lugar a picos elevados de corriente durante el switcheo de los mismos (de hecho, este
problema surgió cuando se realizaron pruebas utilizando relés con bobina, lo cual generaba reseteos
esporádicos del microcontrolador, lo cual no es admisible para el funcionamiento del equipo). Para los
componentes seleccionados para el snubber, se considera lo siguiente: en primer lugar, se toma el caso de
una carga típica, de un motor que consume 1A, y tiene una inductancia de 3mHy. Este es un caso
representativo de motores asincrónicos monofásicos típicos utilizados para bombas centrífugas. En este
caso, se puede calcular la energía almacenada en la bobina del motor según la siguiente fórmula:
1
E * L * I 2 0,5 * 0,003Hy *12 A2 1, mJ
2 (11)
El capacitor de snubber se diseña con una capacidad de aislamiento que soporte hasta 630V, ya que
un pico de un 50% del valor máximo de tensión (380V) nos da un valor de 570V. Tomando este criterio
como el peor caso (pico de 50% del valor máximo), el valor comercial siguiente resulta el indicado. Con
estas suposiciones, el valor de capacidad capaz de absorber la energía calculada está dado por:
1 2 * E 2 * 0,0015J
E * C *V 2 C 2 7,56nF
2 V 630 2 (12)
Se toma un valor estándar de 10nF, para otorgar, además, un margen de seguridad. La reactancia
capacitiva, para la frecuencia de línea (50Hz), está dada por:
1 1
Xc 318309,9
2 * * f * C 2 * * 50 *1 *108 F (13)
36
respuesta ante un pico de corriente. Sabiendo que un ciclo completo dura 20ms (frecuencia de línea de
50Hz), y tomando un margen de tiempo muy cómodo de un tiempo característico que sea unas 500 veces
más rápido que el período de corriente en la bobina, se tiene un tiempo característico de 40us. El tiempo
dado por la red RC, es igual a la inversa del producto de la resistencia y la capacidad, dando un valor de
resistencia de 40Ω. Entonces, se toma como valor elegido el disponible comercialmente más cercano, es
decir, R=39 Ω.
Fuente de alimentación:
Se medirán las tensiones de salidas, cargando a la fuente con una resistencia equivalente
a la carga real, antes de conectarla al equipo.
Placa de procesamiento:
Se desarrolló una placa de desarrollo con las conexiones básicas necesarias para el
funcionamiento de este microprocesador. Se desarrollarán en mayor profundidad los distintos
drivers y su testeo. En cuanto al hardware, se comprobó el correcto funcionamiento del
microprocesador utilizando esta placa, por lo que se desarrollará el programa final sobre esta
plataforma. El diseño y armado de la placa final respetará los componentes incluidos en esta
placa de desarrollo.
2. SOFTWARE
El programa consiste en una máquina de estados, donde cada uno corresponde a cada etapa del
proceso de fabricación (Maceración, Cocción, Fermentación, etc.). Dentro de cada una se accionarán los
dispositivos correspondientes y se controlarán las variables específicas de cada proceso.
37
2.1. Diagrama de Estados y Flujogramas
Fig. 14. Estructura general de los estados del proceso de producción previsto
Los algoritmos y cálculos que deberán implementarse no tendrán gran complejidad. La capacidad
de procesamiento del microprocesador elegido supera ampliamente los requerimientos de velocidad que
requerirá el software. El sistema que requiere mayor frecuencia de clock es el protocolo USB, que
requiere un cristal de 12MHz para sincronización. Utilizando entonces un reloj de esta frecuencia, se
permite utilizar el módulo USB, y la capacidad de procesamiento del microcontrolador resulta mayor que
la necesaria para la aplicación concreta, dando lugar a un amplio margen que garantizará un
funcionamiento fluido. Cabe destacar, además, que se utiliza un microcontrolador de buenas prestaciones,
pero de bajo costo, con un valor de US$ 6, si se lo adquiere en bajas cantidades.
En cuanto a la complejidad algorítmica propiamente dicha, se puede afirmar que el tiempo de
procesamiento dentro del programa depende, fundamentalmente, de dos factores; por un lado, los eventos
que pueda producir el usuario cuando oprime un botón, y por el otro, algún evento interno al programa
que genera una serie de acciones correspondientes.
El software funciona en base a un modelo de máquina de estado, en la que se realiza “polling”.
Esto se puede observar en la función principal (main) del programa, que luego de un protocolo de
inicialización ejecuta el siguiente código:
38
Se puede observar, entonces, que el programa se mantiene infinitamente corroborando si se ha
producido un evento del proceso, o un evento producido mediante un botón oprimido por el usuario.
Como en ambos casos se ejecuta una cantidad fija de instrucciones en el caso de ausencia de
eventos (como sucede en la gran mayoría de los casos), la complejidad algorítmica resulta de orden
unitario (O(1)).
La frecuencia con la que puede interrumpir el proceso es del orden de los segundos, que es
muchísimo tiempo, por lo cual su ni siquiera vale la pena considerarlo. En cuanto al tiempo que puede
transcurrir para que el usuario oprima dos botones, se considera como mínimo unos 100ms. Este es un
tiempo considerable, que podría dar lugar a errores si el programa pasa varias veces por su analizador de
eventos. Por este motivo, se implementó un sistema que verifica que el usuario haya soltado un botón
hasta que se vuelva a actuar frente a un evento.
En conclusión, la complejidad del software resulta varios órdenes de magnitud menor que la
capacidad de procesamiento del microcontrolador, permitiendo garantizar que los tiempos de ejecución de
instrucciones son lo suficientemente pequeños como para asegurar un buen nivel de fluidez en el
programa y su interfaz con el usuario.
A continuación, se detallan las funciones correspondientes a los distintos drivers, y una breve
descripción de cada una. Las funciones se escribirán en lenguaje C.
#ifndef __DISPLAY_H_
#define __DISPLAY_H_
// Definicion de constantes
#define DELAYCONST 32000
#define DELAYCOM 1000
#define BORRAR_DISPLAY 0x01
//#define RS 0x02
//#define RW 0x10
//#define E 0x20
// Declaracion de funciones
//********************************************************************
*********************
// Funcion : void Delay(int iDelay)
// Descripcion: Realiza un delay a través de un for.
// Para Hacer : Determinar el tiempo aproximado del delay
//********************************************************************
*********************
void Delay(int iDelay);
//********************************************************************
*********************
// Funcion : void InicializarDisplay(void)
// Descripcion: Inicializa el display.
//********************************************************************
*********************
void InicializarDisplay(void);
//********************************************************************
*********************
// Funcion : void EscribeComandoDisplay(unsigned char chComando)
// Descripcion: Escribe un comando en el display.
39
//********************************************************************
*********************
void EscribeComandoDisplay(char chComando);
//********************************************************************
*********************
// Funcion : void EscribeCadenaDisplay(char chLinea,
// char *pszCadena)
// Descripcion: Escribe una cadena, en la linea indicada en el
display.
//********************************************************************
*********************
void EscribeCadenaDisplay(char chLinea, char *pszCadena);
//********************************************************************
*********************
// Funcion : void EscribeDatoDisplay(char chDato)
// Descripcion: Escribe un dato en el display.
//********************************************************************
*********************
void EscribeDatoDisplay(char chDato);
//********************************************************************
*********************
// Funcion : void BuildData (unsigned char ChByte)
// Descripcion: Arma un byte con el comando o dato de salida
//********************************************************************
*********************
void BuildData (char ChByte);
#endif // __DISPLAY_H_
#include "MCF51JM128.h"
#define TEMP_ERR -1
//Prototipos
//********************************************************************
***************
// Funcion : void onewire_reset(void)
// Descripcion: Escribe un comando de reset para todos los
dispositivos conectados al bus
//********************************************************************
***************
void onewire_reset(void);
//********************************************************************
***************
// Funcion : void onewire_write(unsigned char data)
// Descripcion: Escribe un comando de escritura y transmite un byte no
signado al dispositivo activo
//********************************************************************
***************
void onewire_write(unsigned char data);
//********************************************************************
***************
// Funcion : unsigned char onewire_read(void)
40
// Descripcion: lee un byte no signado del dispositivo activo
//********************************************************************
***************
unsigned char onewire_read(void);
//********************************************************************
***************
// Funcion : int ds1820_read(void)
// Descripcion: lee una secuencia completa (7 bytes) del dispositivo
activo
//********************************************************************
***************
int ds1820_read(void);
//********************************************************************
***************
// Funcion : void ds1820_configure(unsigned char TH, unsigned char TL,
unsigned char config)
// Descripcion: Escribe los 3 bytes de configuración al dispositivo
activo
//********************************************************************
***************
void ds1820_configure(unsigned char TH, unsigned char TL, unsigned
char config);
//********************************************************************
***************
// Funcion : void delay_us(int us)
// Descripcion: Retraso de una cantidad fija de microsegundos
//********************************************************************
***************
void delay_us(int us);
//********************************************************************
***************
// Funcion : void delay_ms(int ms)
// Descripcion: Retrasa una cantidad fija de milisegundos
//********************************************************************
***************
void delay_ms(int ms);
//********************************************************************
***************
// Funcion : char recorrer_dato (unsigned char dato, int i)
// Descripcion: Función auxiliar para recorrer los distintos bytes de
un dato completo
//********************************************************************
***************
41
byte DATA6 :1; /* Data
Direction for Port C Bit 6 */
byte DATA7 :1; /* Data
Direction for Port C Bit 7 */
} Bits;
} DATA;
Estos dos drivers corresponden al conjunto de subrutinas que requieren un desarrollo y debugueo
independientes, probándolos en distintas circunstancias y bajo condiciones diversas, de modo que se
pueda garantizar su correcto funcionamiento y no den lugar a errores de bajo nivel mientras se desarrollan
las capas más superficiales del programa.
Por supuesto, se han desarrollado distintas funciones de servicio y subrutinas, que se adjuntan en
los anexos correspondientes.
Este archivo alberga la función principal (de inicio para el programa de usuario) que realiza los
protocolos de inicialización, y luego ejecuta un loop infinito para que funcione la máquina de estados.
#include "MCF51JM128.h"
#include "display.h"
#include "Temperatura.h"
#include "botones.h"
#include "Salida.h"
#include "Proceso.h"
//Variables globales
int temp1,temp2,temp3;
//Programa principal
void main(void)
42
{
//Inicializo sistema
system_init();
clear_all_outputs();
InicializarDisplay();
Archivo botones.c
Este archivo contiene las funciones que verifican eventos de los botones y ejecutan las acciones
correspondientes. Se puede observar la estructura básica del menú del equipo, dado por la secuencia:
#include "botones.h"
#include "maindefs.h"
#include "MCF51JM128.h"
#include "display.h"
#include "Salida.h"
#include "Proceso.h"
#define MAX_TEMP 35
//Variables de control
int estado=ENCENDIDO;
int pre_estado=NOTHING;
int released = 1;
int boton_apretado = NOTHING;
int actuator_count = 0;
int boton_flag = 0;
int cont_actuador = 1;
int cont_accion = 0;
int cont_temp=1;
int leyendo_temp = 0;
43
}
//Función que verifica eventos de botones por polling
//Esta función se llama cuando se oprime un botón, para definir las
acciones a realizar
void check_botones (int boton)
{
//Entradas (Botones)
if(boton==LCD)
{
if (LCD_LIGHT==1)
LCD_LIGHT = 0;
else
LCD_LIGHT = 1;
}
else if(boton==MENU) //Menu
{
EscribeComandoDisplay(BORRAR_DISPLAY);
//EscribeCadenaDisplay(1, nothing);
EscribeCadenaDisplay(1, boton_disp_menu);
//EscribeCadenaDisplay(2, nothing);
estado=MENU;
pre_estado=NOTHING;
leyendo_temp=0;
}
//Estados
if (estado == ENCENDIDO)
{
EscribeCadenaDisplay(1, encendido_1);
EscribeCadenaDisplay(2, encendido_2);
if (boton == ENTER)
{
estado=NOTHING;
//Inicializo el programa principal
Init_System_configuration ();
Init_Proceso();
}
}
else if (estado==MENU)
{
if(boton==RIGHT) //Derecha
{
if (pre_estado==NOTHING)
{
if (Programa_activo()==0)
{
pre_estado=MENU_INICIAR;
EscribeCadenaDisplay(1, menu_iniciar);
}
else
{
pre_estado=MENU_PAUSAR;
EscribeCadenaDisplay(1, menu_pausa);
}
}
else if (pre_estado==MENU_INICIAR)
{
pre_estado=MENU_MANUAL;
EscribeCadenaDisplay(1, menu_control_manual);
}
else if (pre_estado==MENU_PAUSAR)
{
44
pre_estado=MENU_MANUAL;
EscribeCadenaDisplay(1, menu_control_manual);
}
else if (pre_estado==MENU_MANUAL)
{
pre_estado=MENU_IR_A;
EscribeCadenaDisplay(1, menu_ir_a);
}
else if (pre_estado==MENU_IR_A)
{
pre_estado=MENU_LEER_TEMP;
EscribeCadenaDisplay(1, leer_temp);
}
else if (pre_estado==MENU_LEER_TEMP)
{
pre_estado=MENU_SALIR;
EscribeCadenaDisplay(1, menu_salir);
}
else if (pre_estado==MENU_SALIR)
{
if (Programa_activo()==0)
{
pre_estado=MENU_INICIAR;
EscribeCadenaDisplay(1, menu_iniciar);
}
else
{
pre_estado=MENU_PAUSAR;
EscribeCadenaDisplay(1, menu_pausa);
}
}
}
else if(boton==LEFT) //Izquierda
{
if (pre_estado==NOTHING)
{
pre_estado=MENU_SALIR;
EscribeCadenaDisplay(1, menu_salir);
}
else if (pre_estado==MENU_INICIAR)
{
pre_estado=MENU_SALIR;
EscribeCadenaDisplay(1, menu_salir);
}
else if (pre_estado==MENU_SALIR)
{
pre_estado=MENU_LEER_TEMP;
EscribeCadenaDisplay(1, leer_temp);
}
else if (pre_estado==MENU_LEER_TEMP)
{
pre_estado=MENU_IR_A;
EscribeCadenaDisplay(1, menu_ir_a);
}
else if (pre_estado==MENU_IR_A)
{
pre_estado=MENU_MANUAL;
EscribeCadenaDisplay(1, menu_control_manual);
}
45
else if (pre_estado==MENU_MANUAL)
{
if (Programa_activo()==0)
{
pre_estado=MENU_INICIAR;
EscribeCadenaDisplay(1, menu_iniciar);
}
else
{
pre_estado=MENU_PAUSAR;
EscribeCadenaDisplay(1, menu_pausa);
}
}
else if (pre_estado==MENU_PAUSAR)
{
pre_estado=MENU_SALIR;
EscribeCadenaDisplay(1, menu_salir);
}
}
else if(boton==ENTER) //Enter
{
if (pre_estado==MENU_INICIAR)
{
estado=NOTHING;
//Inicializo el programa principal
Init_System_configuration ();
Init_Proceso();
}
else if (pre_estado==MENU_PAUSAR)
{
estado=pre_estado;
EscribeCadenaDisplay(1, pausa_continuar);
//Detengo el programa principal (las salidas no
cambian de estado)
Detener_programa ();
}
else if (pre_estado==MENU_MANUAL)
{
estado=pre_estado;
salida_numero[0]=(char) cont_actuador/10+48;
salida_numero[1]=(char) cont_actuador-
10*(cont_actuador/10)+48;
EscribeCadenaDisplay(1, manual_activar);
EscribeCadenaDisplay(2, salida_numero);
}
else if (pre_estado==MENU_IR_A)
{
estado=pre_estado;
accion_numero[0]=(char) cont_accion/10+48;
accion_numero[1]=(char) cont_accion-
10*(cont_accion/10)+48;
EscribeCadenaDisplay(1, accion_activar);
EscribeCadenaDisplay(2, accion_numero);
}
else if (pre_estado==MENU_LEER_TEMP)
{
estado=pre_estado;
leer_temp_num[13]=(char) cont_temp/10+48;
leer_temp_num[14]=(char) cont_temp-
10*(cont_temp/10)+48;
EscribeCadenaDisplay(1, leer_temp_num);
46
leyendo_temp=1;
}
else if (pre_estado==MENU_SALIR)
{
estado=NOTHING;
EscribeCadenaDisplay(1, nothing);
if (Programa_activo()==0)
{
estado = ENCENDIDO;
check_botones (UP);
}
}
}
}
else if (estado==MENU_PAUSAR)
{
if(boton==RIGHT) //Derecha
{
if (pre_estado==MENU_PAUSAR)
{
pre_estado=PAUSA_DETENER;
EscribeCadenaDisplay(1, pausa_detener);
}
else if (pre_estado==PAUSA_CONTINUAR)
{
pre_estado=PAUSA_DETENER;
EscribeCadenaDisplay(1, pausa_detener);
}
else if (pre_estado==PAUSA_DETENER)
{
pre_estado=PAUSA_RESETEAR;
EscribeCadenaDisplay(1, pausa_resetear);
}
else if (pre_estado==PAUSA_RESETEAR)
{
pre_estado=PAUSA_CONTINUAR;
EscribeCadenaDisplay(1, pausa_continuar);
}
}
else if(boton==LEFT)
{
if (pre_estado==MENU_PAUSAR)
{
pre_estado=PAUSA_RESETEAR;
EscribeCadenaDisplay(1, pausa_resetear);
}
else if (pre_estado==PAUSA_CONTINUAR)
{
pre_estado=PAUSA_RESETEAR;
EscribeCadenaDisplay(1, pausa_resetear);
}
else if (pre_estado==PAUSA_RESETEAR)
{
pre_estado=PAUSA_DETENER;
EscribeCadenaDisplay(1, pausa_detener);
}
else if (pre_estado==PAUSA_DETENER)
{
pre_estado=PAUSA_CONTINUAR;
EscribeCadenaDisplay(1, pausa_continuar);
}
47
}
else if (boton==ENTER)
{
if
((pre_estado==PAUSA_CONTINUAR)||(pre_estado==MENU_PAUSAR))
{
Reanudar_programa();
estado=NOTHING;
}
else if (pre_estado==PAUSA_DETENER)
{
//Seteo con condiciones iniciales las variables
Init_Proceso();
clear_all_outputs();
//Detengo el programa
Detener_programa();
EscribeCadenaDisplay(2, programa_detenido);
estado=PAUSA_DETENIDO;
}
else if (pre_estado==PAUSA_RESETEAR)
{
//Reseteo el programa desde el inicio
Init_Proceso();
estado=NOTHING;
}
}
}
else if (estado==MENU_MANUAL)
{
if (boton==ENTER)
{
if (get_out_state(cont_actuador)==0)
salida_on(cont_actuador);
else if (get_out_state(cont_actuador)==1)
salida_off(cont_actuador);
EscribeCadenaDisplay(1, manual_activar);
EscribeCadenaDisplay(2, salida_numero);
}
else if (boton==UP)
{
cont_actuador++;
if (cont_actuador>24)
cont_actuador=1;
salida_numero[0]=(char) cont_actuador/10+48;
salida_numero[1]=(char) cont_actuador-
10*(cont_actuador/10)+48;
EscribeCadenaDisplay(1, manual_activar);
EscribeCadenaDisplay(2, salida_numero);
}
else if (boton==DOWN)
{
cont_actuador--;
if (cont_actuador<1)
cont_actuador=24;
salida_numero[0]=(char) cont_actuador/10+48;
salida_numero[1]=(char) cont_actuador-
10*(cont_actuador/10)+48;
EscribeCadenaDisplay(1, manual_activar);
EscribeCadenaDisplay(2, salida_numero);
}
else if (boton==RIGHT)
48
{
pre_estado=MENU_IR_A;
estado=MENU;
EscribeCadenaDisplay(1, menu_ir_a);
EscribeCadenaDisplay(2, nothing);
}
else if (boton==LEFT)
{
if (Programa_activo()==0)
{
pre_estado=MENU_INICIAR;
EscribeCadenaDisplay(1, menu_iniciar);
}
else
{
pre_estado=MENU_PAUSAR;
EscribeCadenaDisplay(1, menu_pausa);
}
estado=MENU;
}
}
else if (estado==MENU_IR_A)
{
if (boton==ENTER)
{
Set_contador_de_acciones (cont_accion - 1);
Actualizar_a_proxima_accion ();
estado=NOTHING;
}
else if (boton==UP)
{
cont_accion++;
if (cont_accion>MAX_ACCIONES)
cont_accion=0;
accion_numero[0]=(char) cont_accion/10+48;
accion_numero[1]=(char) cont_accion-
10*(cont_accion/10)+48;
EscribeCadenaDisplay(1, accion_activar);
EscribeCadenaDisplay(2, accion_numero);
}
else if (boton==DOWN)
{
cont_accion--;
if (cont_accion<0)
cont_accion=MAX_ACCIONES-1;
accion_numero[0]=(char) cont_accion/10+48;
accion_numero[1]=(char) cont_accion-
10*(cont_accion/10)+48;
EscribeCadenaDisplay(1, accion_activar);
EscribeCadenaDisplay(2, accion_numero);
}
else if (boton==RIGHT)
{
pre_estado=MENU_LEER_TEMP;
estado=MENU;
EscribeCadenaDisplay(1, leer_temp);
EscribeCadenaDisplay(2, nothing);
}
else if (boton==LEFT)
{
pre_estado=MENU_MANUAL;
49
estado=MENU;
EscribeCadenaDisplay(1, menu_control_manual);
EscribeCadenaDisplay(2, nothing);
}
}
else if (estado==MENU_LEER_TEMP)
{
if (boton==UP)
{
cont_temp++;
if (cont_temp>MAX_TERMOMETROS)
cont_temp=1;
leer_temp_num[13]=(char) cont_temp/10+48;
leer_temp_num[14]=(char) cont_temp-10*(cont_temp/10)+48;
EscribeCadenaDisplay(1, leer_temp_num);
leyendo_temp=1;
}
else if (boton==DOWN)
{
cont_temp--;
if (cont_temp<1)
cont_temp=MAX_TERMOMETROS;
leer_temp_num[13]=(char) cont_temp/10+48;
leer_temp_num[14]=(char) cont_temp-10*(cont_temp/10)+48;
EscribeCadenaDisplay(1, leer_temp_num);
leyendo_temp=1;
}
else if (boton==RIGHT)
{
pre_estado=MENU_SALIR;
estado=MENU;
EscribeCadenaDisplay(1, menu_salir);
EscribeCadenaDisplay(2, nothing);
leyendo_temp=0;
}
else if (boton==LEFT)
{
pre_estado=MENU_IR_A;
estado=MENU;
EscribeCadenaDisplay(1, menu_ir_a);
EscribeCadenaDisplay(2, nothing);
leyendo_temp=0;
}
}
if (estado==NOTHING)
{
//Libero el flag de control
boton_flag=0;
//Borro el display
EscribeCadenaDisplay(1, nothing);
EscribeCadenaDisplay(2, nothing);
}
else
boton_flag = 1;
}
//Esta función se llama periódicamente para verificar si alguien
orpimió algún botón
//Utiliza Polling
void evento_botones (void)
{
int temperatura;
50
//Entradas (Botones)
if ((BOTON_LCD==0)) //LCD
boton_apretado = LCD;
else if((BOTON_MENU==0)) //Menu
boton_apretado = MENU;
else if((BOTON_ENTER==0)) //Enter
boton_apretado = ENTER;
else if((BOTON_ARRIBA==0)) //Arriba
boton_apretado = UP;
else if((BOTON_ABAJO==0)) //Abajo
boton_apretado = DOWN;
else if((BOTON_IZQUIERDA==0)) //Izquierda
boton_apretado = LEFT;
else if((BOTON_DERECHA==0)) //Derecha
boton_apretado = RIGHT;
else
{
boton_apretado = NOTHING;
released = 1;
}
if (leyendo_temp==1)
{
//Leo temperatura
temperatura=Get_Temp(cont_temp);
temp_disp[0]= ((char) (temperatura/100)) + 48;
temp_disp[1]= ((char) ((char) (temperatura -
100*(temperatura/100))/10)) +48;
temp_disp[2]= ((char) (temperatura - 100*(temperatura/100)-
10*(((temperatura - 100*(temperatura/100)))/10))) +48;
//Chequeo de error de temperatura
if ((temp_disp[0]<='9')&&(temp_disp[0]>='0')&&
(temp_disp[1]<='9')&&(temp_disp[1]>='0')&&
(temp_disp[2]<='9')&&(temp_disp[2]>='0'))
EscribeCadenaDisplay(2, temp_disp);
else
EscribeCadenaDisplay(2, temp_err);
}
if ((boton_apretado!=NOTHING) && released)
{
check_botones (boton_apretado);
released = 0;
}
}
Archivo Ejemplo_sistema.c
Este archivo corresponde al programa de ejemplo ejecutado para la validación del prototipo.
#include "Proceso.h"
#include "display.h"
#include "Temperatura.h"
#include "botones.h"
/*Variables de control*/
int proceso_en_marcha = OFF;
int contador_de_acciones;
acc_t accion_actual;
alarm_t alarma_actual;
tiempo_t tiempo_inicio_proceso;
tiempo_t tiempo_actual;
tiempo_t tiempo_inicio_accion;
51
tiempo_t tiempo_fin_accion;
tiempo_t tiempo_fin_proceso;
int temperatura_fin_accion;
int termometro_actual;
char process_disp [16] = "Temperatura: ";
int lcd_flag;
int buzzer_flag;
BOOL Mantener_temperatura;
int Temperatura_a_mantener;
int Aciertos_Temperatura;
int Receta_activa = 1;
int mesetas_temperatura [10];
int contador_temperatura = 0;
/************************/
/* UNIDADES */
/* Temperaturas en ºC */
/* Tiempos en minutos */
/* Volumen en litros */
/************************/
52
//Aqui inicializo las acciones de este ejemplo
//1-Prendo fuego
acciones[i].proceso = MACERACION;
acciones[i].salida = 17;
acciones[i].on_off = ON;
acciones[i].fin = POR_TEMPERATURA;
acciones[i].valor_fin =
recetas[n_receta].Temperatura_agua_maceracion_inicial;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = " Inicio ";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago fuego
acciones[i].proceso = MACERACION;
acciones[i].salida = 17;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 1;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 10
acciones[i].proceso = MACERACION;
acciones[i].salida = 10;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 15*60; //15 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 10
acciones[i].proceso = MACERACION;
acciones[i].salida = 10;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 1;
acciones[i].n_termometro = 2;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Inicio de curva de Maceración
//5-Primera meseta
acciones[i].proceso = CURVA_MACERACION;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_maceracion_1;
//30 minutos
acciones[i].n_termometro = 2;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
53
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Segunda meseta
acciones[i].proceso = CURVA_MACERACION;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_maceracion_2;
//30 minutos
acciones[i].n_termometro = 2;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Tercera meseta
acciones[i].proceso = CURVA_MACERACION;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_maceracion_3;
//15 minutos
acciones[i].n_termometro = 2;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Fin de curva de temperatura
//Preparo agua para lavado
//Abro válvula 16
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 16;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 30; //30 segundos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo fuego
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 17;
acciones[i].on_off = ON;
acciones[i].fin = POR_TEMPERATURA;
acciones[i].valor_fin = recetas[n_receta].Temperatura_agua_lavado;
//80ºC
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//10-Apago fuego
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 17;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
54
acciones[i].valor_fin = 1;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago válvula 16
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 16;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 30; //Espero 30 segundos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 11
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 11;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 12
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 12;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 11
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 11;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//15-Apago bomba 12
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 12;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
55
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 11
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 11;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 12
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 12;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 11
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 11;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 12
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 12;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//20-Prendo bomba 11
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 11;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
56
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 12
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 12;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 15*60; //15 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 11
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 11;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 1;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 12
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 12;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 1;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 9
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 9;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//25-Apago bomba 9
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 9;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
57
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 9
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 9;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5*60; //5 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo fuego
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 17;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 1;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Abro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Cierro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//30-Abro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
58
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Cierro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Abro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Cierro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Abro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//35-Cierro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
59
alarmas[i].titilar_lcd = FALSE;
i++;
//Abro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 3*60; //3 minutos
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Cierro válvula 18
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 18;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TEMPERATURA;
acciones[i].valor_fin = 100; //Espero a que
comience a hervir el mosto
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Aviso para inicio de Hervor
acciones[i].proceso = HERVOR;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_aditivo_1*60;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = "Inicio Hervor";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Aviso para incorporar Lúpulo Amargor
acciones[i].proceso = HERVOR;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_aditivo_2*60 -
recetas[n_receta].Tiempo_aditivo_1*60;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = "Aditivo 1";
alarmas[i].sonido = TRUE;
alarmas[i].titilar_lcd = TRUE;
i++;
//40-Aviso para incorporar Lúpulo Sabor
acciones[i].proceso = HERVOR;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_aditivo_3*60 -
recetas[n_receta].Tiempo_aditivo_2*60;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
60
alarmas[i].mensaje = "Aditivo 2";
alarmas[i].sonido = TRUE;
alarmas[i].titilar_lcd = TRUE;
i++;
//Aviso para incorporar Clarificante
acciones[i].proceso = HERVOR;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_aditivo_4*60 -
recetas[n_receta].Tiempo_aditivo_3*60;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = "Aditivo 3";
alarmas[i].sonido = TRUE;
alarmas[i].titilar_lcd = TRUE;
i++;
//Aviso para incorporar Lúpulo Aroma
acciones[i].proceso = HERVOR;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = recetas[n_receta].Tiempo_aditivo_5*60 -
recetas[n_receta].Tiempo_aditivo_4*60;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = "Aditivo 4";
alarmas[i].sonido = TRUE;
alarmas[i].titilar_lcd = TRUE;
i++;
//Aviso de fin de hervor
acciones[i].proceso = HERVOR;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = "Fin de Hervor";
alarmas[i].sonido = TRUE;
alarmas[i].titilar_lcd = TRUE;
i++;
//Apago fuego
acciones[i].proceso = HERVOR;
acciones[i].salida = 17;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 1;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//45-Aviso para hacer Whirlpool
acciones[i].proceso = HERVOR;
acciones[i].salida = 1;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 1*60; //1 minuto para hacer
whirlpool
61
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = "Whirlpool";
alarmas[i].sonido = TRUE;
alarmas[i].titilar_lcd = TRUE;
i++;
//Prendo bomba 10
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 10;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 10
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 10;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Prendo bomba 10
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 10;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 10;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 10
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 10;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 5;
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//50-Prendo bomba 10
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 10;
acciones[i].on_off = ON;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 20*60; //Enfrío el mosto
circulando por 20 minutos
62
acciones[i].n_termometro = 1;
acciones[i].alarm = FALSE;
alarmas[i].mensaje = "";
alarmas[i].sonido = FALSE;
alarmas[i].titilar_lcd = FALSE;
i++;
//Apago bomba 10
acciones[i].proceso = RECIRCULACION;
acciones[i].salida = 10;
acciones[i].on_off = OFF;
acciones[i].fin = POR_TIEMPO;
acciones[i].valor_fin = 10;
acciones[i].n_termometro = 1;
acciones[i].alarm = TRUE;
alarmas[i].mensaje = "Fin";
alarmas[i].sonido = TRUE;
alarmas[i].titilar_lcd = TRUE;
i++;
}
63
mesetas_temperatura [9] = 0;
//Recirculación y lavado
recetas[n_receta].Tiempo_recirculado = 10*60;
recetas[n_receta].Volumen_agua_lavado = 40;
recetas[n_receta].Temperatura_agua_lavado = 80;
//Aditivos durante el hervor
recetas[n_receta].Tiempo_aditivo_1 = 1;
recetas[n_receta].Tiempo_aditivo_2 = 45;
recetas[n_receta].Tiempo_aditivo_3 = 50;
recetas[n_receta].Tiempo_aditivo_4 = 59;
recetas[n_receta].Tiempo_aditivo_5 = 60;
recetas[n_receta].Tiempo_aditivo_6 = NOT_USED;
recetas[n_receta].Tiempo_aditivo_7 = NOT_USED;
recetas[n_receta].Tiempo_aditivo_8 = NOT_USED;
recetas[n_receta].Tiempo_aditivo_9 = NOT_USED;
recetas[n_receta].Tiempo_aditivo_10 = NOT_USED;
recetas[n_receta].Tiempo_de_hervor = 60;
recetas[n_receta].Tiempo_whirlpool = 1;
//Enfriamiento
recetas[n_receta].Tiempo_de_enfriamiento = 20;
}
//Función de inicialización
void Init_Proceso (void)
{
Reset_Timer();
proceso_en_marcha = ON;
contador_de_acciones = -1;
tiempo_inicio_proceso = Get_tiempo_actual();
Actualizar_a_proxima_accion();
set_flag(0);
}
//Función motor de la máquina de estados que avanza en las acciones
void Check_Proceso (void)
{
tiempo_t tiempo_aux;
int temperatura_actual;
int aux_temp;
if (Programa_activo() == ON)
{
//Chequeo condición de fin de acción
if (accion_actual.fin == POR_TIEMPO)
{
//Chequeo tiempo actual vs. tiempo fin
tiempo_aux = Get_tiempo_actual();
//Si se cumplió el tiempo de la acción, paso a la siguiente
if ((tiempo_aux.segundo >= tiempo_fin_accion.segundo)
&& (tiempo_aux.minuto >= tiempo_fin_accion.minuto)
&& (tiempo_aux.hora >= tiempo_fin_accion.hora)
&& (tiempo_aux.dia >= tiempo_fin_accion.dia))
{
Actualizar_a_proxima_accion ();
}
}
else if (accion_actual.fin == POR_TEMPERATURA)
{
aux_temp = Get_Temp(accion_actual.n_termometro);
if (aux_temp != TEMP_ERR)
{
temperatura_actual = aux_temp;
process_disp[13]= ((char) (temperatura_actual/100)) + 48;
64
process_disp[14]= ((char) ((char) (temperatura_actual -
100*(temperatura_actual/100))/10)) +48;
process_disp[15]= ((char) (temperatura_actual -
100*(temperatura_actual/100)-10*(((temperatura_actual -
100*(temperatura_actual/100)))/10))) +48;
}
else
{
temperatura_actual = -1;
process_disp[13]= 'E';
process_disp[14]= 'r';
process_disp[15]= 'r';
}
//Escribo el valor de temperatura, si no hay opciones de menu
if (get_flag()==0)
EscribeCadenaDisplay(1, process_disp);
//Chequeo temperatura actual vs. temp. fin
if (temperatura_actual>=accion_actual.valor_fin)
Aciertos_Temperatura++;
else
Aciertos_Temperatura = 0;
if (Aciertos_Temperatura >= ACIERTOS_TEMP)
{
Actualizar_a_proxima_accion ();
Aciertos_Temperatura = 0;
}
}
else if (accion_actual.fin == POR_BOTON)
{
//Chequeo que se oprima un botón...
if((BOTON_ENTER==0)) //Enter
{
Actualizar_a_proxima_accion ();
}
}
if (Mantener_temperatura == TRUE)
{
//Mido la temperatura
aux_temp = Get_Temp(accion_actual.n_termometro);
if (aux_temp != TEMP_ERR)
temperatura_actual = aux_temp;
//Mantengo la temperatura de maceración dentro del rango
establecido
if (temperatura_actual >= (Temperatura_a_mantener +
Margen_temperatura_superior))
{
//Chequeo que no haya errores de medición, por repetición
del valor
Aciertos_Temperatura++;
if (Aciertos_Temperatura >= ACIERTOS_TEMP)
{
//salida_off (accion_actual.salida);
salida_off(9);
salida_off(17);
salida_off(10);
Aciertos_Temperatura = 0;
}
}
else if (temperatura_actual <= (Temperatura_a_mantener -
Margen_temperatura_inferior))
{
65
//Chequeo que no haya errores de medición, por repetición
del valor
Aciertos_Temperatura++;
if (Aciertos_Temperatura >= ACIERTOS_TEMP)
{
//salida_on (accion_actual.salida);
salida_on(9);
salida_on(17);
salida_on(10);
Aciertos_Temperatura = 0;
}
}
}
}
}
//Funciones de servicio para el control de las acciones
tiempo_t Get_tiempo_actual (void)
{
tiempo_t este_tiempo;
este_tiempo.dia = get_dias();
este_tiempo.hora = get_horas();
este_tiempo.minuto = get_minutos();
este_tiempo.segundo = get_segundos();
return este_tiempo;
}
void Ejecutar_accion (int n_accion)
{
if (acciones[n_accion].on_off == ON)
salida_on (acciones[n_accion].salida);
else
salida_off (acciones[n_accion].salida);
}
void Ejecutar_alarma (int n_accion)
{
if (alarmas[n_accion].sonido == TRUE)
Set_buzzer_flag(TRUE);
//Prender buzzer
if (alarmas[n_accion].titilar_lcd == TRUE)
{
Set_lcd_flag(TRUE);
//Titilar LCD
}
//Actualizo mensaje
EscribeCadenaDisplay(2, alarmas[n_accion].mensaje);
}
void Actualizar_fin_tiempo_accion (int n_accion)
{
tiempo_t fin;
tiempo_t actual;
unsigned int sumar_dias;
unsigned int sumar_horas;
unsigned int sumar_minutos;
unsigned int sumar_segundos;
actual = Get_tiempo_actual();
sumar_dias = acciones[n_accion].valor_fin/(60*60*24);
sumar_horas = acciones[n_accion].valor_fin/(60*60) -
24*sumar_dias;
sumar_minutos = acciones[n_accion].valor_fin/(60) - 24*sumar_dias
- 60*sumar_horas;
66
sumar_segundos = acciones[n_accion].valor_fin - 24*sumar_dias -
60*sumar_horas - 60*sumar_minutos;
//Corrección sexagesimal
if (fin.segundo >= 60)
{
fin.minuto++;
fin.segundo -= 60;
}
if (fin.minuto >= 60)
{
fin.hora++;
fin.minuto -= 60;
}
if (fin.hora >= 24)
{
fin.dia++;
fin.hora -= 24;
}
tiempo_fin_accion = fin;
}
void Actualizar_fin_temperatura_accion (int n_accion)
{
temperatura_fin_accion = acciones[n_accion].valor_fin;
termometro_actual = acciones[n_accion].n_termometro;
}
void Actualizar_a_proxima_accion (void)
{
contador_de_acciones++;
accion_actual = acciones[contador_de_acciones];
alarma_actual = alarmas[contador_de_acciones];
Ejecutar_accion (contador_de_acciones);
if (accion_actual.alarm == TRUE)
{
set_flag(ON);
Ejecutar_alarma (contador_de_acciones);
}
else
set_flag(OFF);
if (accion_actual.fin == POR_TIEMPO)
Actualizar_fin_tiempo_accion (contador_de_acciones);
if (accion_actual.proceso == CURVA_MACERACION)
{
Temperatura_a_mantener =
mesetas_temperatura[contador_temperatura++];
Mantener_temperatura = TRUE;
}
else
Mantener_temperatura = FALSE;
}
67
BOOL Get_lcd_flag (void)
{
return lcd_flag;
}
void Set_buzzer_flag (BOOL valor)
{
buzzer_flag = valor;
}
BOOL Get_buzzer_flag (void)
{
return buzzer_flag;
}
int Programa_activo (void)
{
return proceso_en_marcha;
}
void Reanudar_programa (void)
{
proceso_en_marcha = ON;
}
void Detener_programa (void)
{
proceso_en_marcha = OFF;
}
void Set_contador_de_acciones (int valor)
{
contador_de_acciones = valor;
}
int Get_receta_activa (void)
{
return Receta_activa;
}
void Set_receta_activa (int n_receta)
{
Receta_activa = n_receta;
}
No se avanzará en el desarrollo del software sin haber completado una etapa previa, la cual incluye
la prueba de los módulos programados. El software se desarrollará en dos etapas:
Primera etapa: fase del programa de bajo nivel, que incluye el desarrollo y prueba de los distintos
drivers. Las aplicaciones que requieren drivers son: Display LCD, sensores de temperatura,
protocolo para registros de desplazamiento. Cada driver se realizará en forma independiente del
resto, utilizando pines físicos del microprocesador, para que puedan correr simultáneamente.
Una vez que se validen todos los drivers por separado, se procederá a iniciar la segunda etapa de
programación.
Segunda etapa: fase del programa de mayor nivel, representado por una máquina de estados que
controle el proceso de producción de cerveza. Esta etapa hará uso de los servicios provistos por
los drivers de la primera etapa. Esta etapa actuará directamente sobre el hardware, por lo que se
requerirá tener disponible un prototipo inicial de la placa de potencia, con al menos un circuito
completo para activar una salida. Para depurar la máquina de estados, se utilizará la placa de
salida con sus leds activados directamente desde los buffers, sin alimentar las entradas y alta
tensión (vivo y neutro) lo cual permitirá evaluar el comportamiento del software, son correr
riesgos para la seguridad del programador.
68
X. CONTRUCCIÓN DEL PROTOTIPO
Se construye un prototipo del proyecto, utilizando un gabinete fabricado manualmente, al igual que
las placas impresas. Estos dos elementos serán tercerizados en la etapa de producción. Además, se efectúa
el soldado de componentes, ensamblado del equipo y verificación funcional del sistema, actividades que
sí formarán parte de la producción de cada equipo.
Los módulos que constituyen el equipo se pueden distinguir en los siguientes grupos:
1.1. Gabinete
El gabinete está previsto que se construya en dos partes: una base, y una tapa. Para el caso del
prototipo, por baja disponibilidad de chapa de acero inoxidable, se construyó la tapa dividida en dos
partes. Los planos y el diseño mecánico se detalla en la sección correspondiente (10.3). Se eligió como
material acero inoxidable AISI 304, con un espesor de 0,75mm. Entre las virtudes de este material,
destacamos las siguientes, que llevaron a su selección:
El equipo tendrá dos placas impresas, una con los circuitos de la fuente, y salida de potencia, y la
otra con los circuitos lógicos, botones y display. La placa de potencia irá atornillada con 6 tornillos a la
base, mientras que la placa lógica se atornillará con 4 tornillos a la tapa el gabinete. Ambas placas se
comunicarán mediante un cable plano de 26 vías, con conectores IDC. La alimentación de la placa lógica
69
será proporcionada por el circuito de fuente ubicado en la placa de potencia, por lo cual otros dos cables
(positivo y negativo) conectarán estas dos placas.
Para resolver el censado de temperatura, se optó por un integrado digital, con datos seriales, que
funciona con 3 líneas (Vcc, Gnd y Data). Se utilizará cable del tipo telefónico por disponer de 4 líneas
(una queda sin uso), tener un bajo costo, excelente disponibilidad, y una resistencia térmica suficiente
para la aplicación. En el otro extremo del cable se utiliza un conector polarizado, para evitar errores de
conexión por parte del usuario. Tanto el integrado como el conector irán soldados al cable, y recubiertos
por aislante termocontraíble. Se prevé el uso de vainas de acero inoxidable para sumergir estos
termómetros en el mosto, agua, etc. El usuario podrá disponer de sus propias vainas, que requerirán un
diámetro interno de más de 5,5mm. Para el prototipo se utilizaron varillas cortadas de acero inoxidable
AISI 304, con un roscado en un extremo, en donde se colocó un bulón (también de acero inoxidable). La
unión se selló con pasta de teflón, para garantizar la hermeticidad de la vaina y el correcto
funcionamiento del termómetro.
Los circuitos impresos se diseñaron teniendo en cuenta la estructura con la que el equipo fue
ideado. La placa lógica albergaría al microprocesador, junto con los botones, display, y conectores para
los termómetros. Tendría el tamaño justo para que pueda situarse justo por encima de la placa de
potencia, dejando las borneras y los leds indicativos de esta última hacia fuera. La placa lógica va
atornillada a la tapa del gabinete, por lo cual define la posición de todos sus componentes con los que
interactuará el usuario. Por otra parte, la placa de salida tendrá su circuito en la parte central, alrededor del
conector IDC que se ubicará justo por debajo de la placa lógica. Las borneras y leds indicativos deben
quedar fuera del área que cubre la placa lógica.
A continuación, se muestran las figuras correspondientes al diseño de los dos PCBs:
70
Fig. 15. Diseño de PCB de la placa lógica
Las pistas rojas corresponden a la capa superior, mientras que las azules a la inferior, en ambos
casos. Las líneas amarillas corresponden a la forma de los componentes. No se prevé imprimir en las
71
placas finales esta última capa (líneas amarillas) en forma de serigrafía, de modo de minimizar el costo de
impresión de las placas. Por este motivo, la numeración e indicaciones relevantes se realiza utilizando el
mismo cobre de las pistas. Se prevé llevar a cabo el soldado de los componentes teniendo una placa ya
soldada y probada como referencia, de modo tal que el dibujo de los componentes no resulte necesario.
A continuación, se muestran dos imágenes que corresponden a las placas fabricadas manualmente
para el prototipo.
72
Consideraciones de ruteo:
Para el caso de la placa de salida de potencia, se procuró conservar una buena distribución de los
componentes, como se dijo antes, de modo de que las borneras y los leds quedaran visibles. Además,
había quedado prefijada la posición del conector IDC a partir del diseño de la placa lógica. En cuanto al
ruteo propiamente dicho, se ubicaron las líneas que conducen corrientes en alta tensión (220VAC) lo más
cerca posible del borde, tratando de que queden lejos de aquellos componentes que requerían la referencia
de tierra lógica (como el caso de los optotriacs). Por este motivo se observa, a grandes rasgos, que los
componentes lógicos (optotriacs y buffers), junto con el circuito de alimentación, se encuentran en la
parte central de la placa; mientras tanto, las borneras de salida, los triacs, y circuitos de snubber se
encuentran en los laterales. Como se mencionó anteriormente, se incorporaron 4 borneras dobles para
recibir la alta tensión, de modo de que cada una de estas borneras esté conectada a 6 triacs, y las salidas se
encuentren independizadas unas de otras, en grupos de a 6. Esto permitirá alimentarlas con distintas
fuentes de 220VAC cuando la potencia requerida sea muy grande. Para mayor información, se disponen
los esquemáticos de los circuitos en los anexos correspondientes.
Para la placa lógica, resultó fundamental la ubicación de algunos componentes, como botones,
display, led de encendido, y conectores para los termómetros, ya que estos saldrían por fuera del gabinete
y determinarían su apariencia. Se optó por ubicar la placa armada del display en el extremo superior
izquierdo de la placa, para evitar tener que realizar un calado interno, lo cual elevaría los costos. Otro
aspecto importante fue el hecho de que el microprocesador, con el encapsulado elegido, requiere de pistas
muy finas, por lo que deberán extremarse los cuidados a la hora de manufacturar esta placa, ya que habrá
muy poco lugar para márgenes de error. Un detalle a destacar es la incorporación de un buffer de entrada
y lugares para 3 pines dobles, que no fueron soldados en el prototipo. Esos componentes están pensados
para eventuales mejoras futuras del equipo, en el cual se incorporen tres entradas lógicas binarias para,
por ejemplo, dar aviso de que un líquido llegó a un nivel dado. Como las dimensiones permitían la
incorporación de estos componentes, y estos no modifican el valor de la placa impresa, se incorporaron
para evitar, en el futuro, tener que rediseñar esta placa.
3. DISEÑO MECÁNICO
El diseño mecánico del equipo está íntimamente relacionado con el diseño de las placas impresas.
El gabinete tiene una base de 17cm. x 35cm. La placa de potencia (17cm. x 20cm.) va atornillada con 6
tornillos (3 por cada lateral) en el la parte derecha del equipo. Esto deja un lugar de 17cm. x 15cm. para
albergar a la tecla de encendido, cable, porta fusible, cables de conexión, y transformador. La otra parte
del gabinete, la tapa, se atornillará a la base con 8 tornillos en la parte superior. A continuación, se
muestran las figuras que corresponden a las formas de la chapa de acero inoxidable. Las medidas de estas
partes corresponden a las especificaciones dimensionales expresadas en la sección correspondiente.
Fig. 19. Dibujos de los planos para recorte de la chapa del gabinete.
73
El resultado del prototipo armado, con sus componentes instalados, se muestra en la siguiente
imagen:
El primer paso en el proceso de montaje del equipo consiste en el soldado de todos los
componentes de las dos placas. El gabinete será tercerizado, y se dispondrá del mismo en dos partes, ya
dobladas, con todas las caladuras y agujeros que se muestran en el gráfico del ítem anterior. Los orificios
que serán roscados, tendrán un diámetro de 2,25mm., de modo que se pueda realizar una rosca por medio
de un macho. El resto de los orificios por donde pasarán tornillos serán de 3mm., de modo que tenga una
funcionalidad pasante. Todas las roscas con macho se harán en la parte base del gabinete, para los 8
tornillos de unión entre las partes del gabinete, y para los 6 tornillos de unión entre la base y la placa de
potencia. Una vez realizadas las roscas, se procederá a fijar la placa de potencia, con 6 tornillos con
arandela. En este paso, resulta muy importante fijar dos tiras de goma entre la placa y el gabinete, para
garantizar aislamiento eléctrico. Los tornillos pasarán a través de estas tiras de goma. Seguidamente, se
coloca el cable armado, con un pasa cable por el orificio asignado. Para afirmarlo, se coloca pegamento
de secado rápido entre el cable y el pasa cable, y se lo afirma con un precinto (con su excedente cortado).
Seguidamente se atornilla el porta fusible, con su fusible ya colocado (para el prototipo, se utilizó un
fusible de 10A, aunque su valor podrá variar según la instalación que se desee automatizar, y sus
periféricos). Luego, se procede a atornillar el transformador, con dos tonillos, dos tuercas y cuatro
arandelas. Seguidamente, se colocan las seis patas de goma, con 6 tornillos y sus tuercas
correspondientes. A continuación, se procede al armado de la tapa del gabinete. Para eso se coloca en
posición la placa lógica, y se la afirma a la tapa con 4 tornillos con sus respectivas tuercas. También se
coloca la tecla de encendido, que encastra a presión.
74
Una vez armadas las dos partes, se procede a realizar las conexiones eléctricas. En la línea de tierra
del cable armado, se coloca un terminal circular, que se fijará a la tuerca de la pata de goma más cercana
al cable. La línea de neutro llevará un terminal hembra que irá a la entrada de la tecla de encendido. La
línea del vivo se soldará a un terminal del porta fusible, y se cubrirá la soldadura con termocontraíble. Se
soldará al otro terminal del porta fusible, del mismo modo, una extensión de cable, y se fijará un terminal
hembra en su otro extremo, para conectarlo a la otra entrada de la tecla. En los os bornes de salida de la
tecla, irán conectados los cables del primario del transformador. Además, para este prototipo, se
incorporaron extensiones de cable para alimentar los 4 grupos de 6 salidas de potencia, conectándolos
también a la salida de la tecla de encendido. Todas estas conexiones se realizaron con los terminales
correspondientes, recubiertos adecuadamente con accesorios plásticos. Luego, se atornillan los cables del
secundario del transformador a la entrada de la placa de salida. Se conectan los dos cables de
alimentación de la placa lógica, y finalmente se arma l cable IDC y se conecta en ambas terminales de las
placas. De este modo, todo está listo para terminar de ensamblar el gabinete, uniendo sus dos partes, y
atornillándolos con los 8 tornillos correspondientes.
Aquí se muestra una imagen del prototipo con sus partes, justo antes de unir las partes del gabinete.
Se recuerda que el prototipo tiene leves diferencias estructurales en su gabinete, por la disponibilidad de
chapa, como, por ejemplo, la tapa se encuentra partida en dos.
75
XI. VALIDACIÓN DEL PROTOTIPO
Una vez concluidas y validadas las pruebas de cada módulo por separado, se desarrolló un
programa de prueba del prototipo, en el cual se lleva a cabo un proceso completo de producción de
cerveza, desde la molienda del grano, hasta el proceso de enfriado para comenzar la fermentación.
Para esto se armó un sistema basado en los recipientes disponibles en un equipo de cerveza casero. Se
utilizaron 2 barriles plásticos de unos 110 litros de capacidad cada uno, una olla de aluminio con una
capacidad de 98 litros, y dos fermentadores cónicos de 60 litros (de los cuales uno fue utilizado como
macerador, aislándolo térmicamente con lana de vidrio).
Para automatizar esta instalación, se utilizaron 4 bombas plásticas, dos electroválvulas comunes
(utilizadas en los lavarropas), y una electroválvula de mayor calidad para controlar el flujo de gas del
quemador. Todos estos periféricos fueron conectados y controlados desde el prototipo del equipo.
Además, se conectó un motor monofásico de unos 400W de potencia para accionar el molino de
malta, y se controló su encendido desde el equipo, con el fin de demostrar su capacidad de
funcionamiento con altas potencias.
76
Fig. 23. Esquema de la instalación utilizada para la validación del prototipo.
1.2. Medidas
Para medir el desempeño del equipo en un caso real, se preparó la instalación mencionada
previamente. Se procederá a describir brevemente el funcionamiento de la misma, haciendo uso de
esquemas que faciliten su comprensión. Dentro de la siguiente descripción se detalla el proceso de
elaboración de cerveza artesanal. En la sección correspondiente al software se detallan las acciones
programadas para llevar a cabo esta producción de ejemplo.
Ante todo, se deben mencionar los ingredientes que se utilizarán, en este caso, para elaborar unos
50 litros de cerveza rubia del estilo Pilsen Lager, una receta de origen germano.
Para esto se utilizan:
80 litros de agua
10 Kg de malta pálida
400 grs de malta levemente tostada (estilo Caramunich)
20 grs de lúpulo estilo Cascade
10 grs de lúpulo estilo Hellertauer
4 grs de clarificante de hervor (Whirlflock)
11,5 grs de levadura seca (estilo Saflager-W34/70)
Antes de dar comienzo al proceso automatizado, se preparan los ingredientes. Para ello, se divide
el agua en dos partes (iguales, para esta receta), y se ubican unos 40 litros en un depósito (controlado por
la válvula 16) y los otros 40 litros en la olla.
Por otra parte, debe molerse la malta. El proceso de molienda tiene dos objetivos fundamentales: el
primero, y más importante, es romper el grano para facilitar la disolución de los azúcares fermentables
77
durante la maceración. El segundo, tiene que ver con lograr una textura capaz de actuar como filtro
cuando se asiente, de modo que, al hacer recircular el agua azucarada desde el fondo del macerador hacia
la parte de arriba, el líquido se vaya librando de impurezas para lograr un producto cristalino.
En este caso, la molienda del grano se efectuó utilizando un motor de poco más de 400W de
potencia, y se lo conmutó desde una de las salidas del equipo, para verificar su funcionamiento con cargas
inductivas de una potencia considerable. El resultado fue un éxito, ya que el motor del molino pudo
controlarse sin inconvenientes.
La malta molida, entonces, se coloca en el recipiente macerador, dentro de una bolsa de tela que
ayudará al filtrado, y evitará que se escapen partículas muy grandes que puedan interferir con el
funcionamiento de las bombas. Una vez que la malta está molida en el macerador, y el agua se encuentra
dividida y ubicada en sus recipientes correspondientes, se da lugar al inicio del proceso.
En primer lugar, deben calentarse unos 40 litros de agua, para mezclar con la malta y realizar el
proceso de macerado. Para ello, se abre la válvula de gas (17) y se espera a que la temperatura del agua
alcance unos 70ºC.
Una vez que se ha alcanzado la temperatura deseada, se bombea el agua (10) hacia el macerador,
para dar inicio a la maceración. El tiempo de bombeo fue estimado midiendo previamente el flujo de la
bomba, y dejando un margen de seguridad importante, ya que a este tipo de bombas no les afecta trabajar
en vacío.
78
Fig. 25. Trasvase del agua hacia el macerador.
Una vez que se completó el trasvase, comienza la etapa de maceración. En esta etapa, de definirá la
calidad de la maceración, que determinará el rendimiento del grano. Una buena maceración debe poder
disolver la mayor cantidad posible de azúcar contenida en el grano, y lograr una especie de jarabe con la
mayor densidad posible. Se puede realizar una maceración pobre, pero utilizando más cantidad de malta
para lograr el mismo resultado, o bien, utilizar la misma cantidad, aunque el resultado tendrá menor
densidad, lo cual determinará un menor cuerpo para el producto final. Entonces, la calidad de la
maceración determinará el rendimiento. Para optimizarlo, es necesario seguir curvas de temperatura y
tiempo, establecidas por el maestro cervecero, que dependerán de las maltas utilizadas, el volumen de
cerveza a producir, el cuerpo que se desea en el producto final, etc. En este ejemplo, se optó por una
maceración de 90 minutos, trabajando 30 minutos a 65ºC, 30 minutos a 70ºC, y 30 minutos a 75ºC.
79
Fig. 26. Equipo durante la maceración.
En la olla se había calentado agua hasta unos 70ºC, pero el proceso de trasvase y el hecho de que la
malta se encuentra fría, reduce la temperatura inicial de maceración unos 60ºC aproximadamente. Cabe
destacar que el macerador fue cubierto con lana de vidrio aluminizada, para preservar mejor la
temperatura interna. En este punto (cuando finaliza el trasvase), el sistema comienza a trabajar para
respetar la curva de maceración especificada en el programa. Para ello, toma un margen de 2ºC
(programable) y el programa comienza un ciclo de calentamiento para elevar la temperatura, o para
cambiar a un nuevo escalón de temperatura. En este caso, esto se realizó bombeando el líquido de
maceración hacia la olla, prendiendo la válvula para que se encienda la llama que caliente el líquido, y
volviéndolo a bombear al macerador, como se muestra a continuación.
80
Fig. 27. Calentamiento del agua de maceración, para elevar su temperatura.
Este ciclo se ideó teniendo en cuenta la disponibilidad de equipamiento. En realidad, se prevé que
un equipo más completo disponga de otro quemador, y la maceración se lleve a cabo en una nueva olla,
de modo que calentar el líquido implica solamente abrir una válvula de gas.
Antes de que finalice la etapa de maceración, y una vez que el líquido alcance una temperatura de
75ºC (último escalón de la curva), se avanza con el siguiente paso (por este motivo, se programa el último
escalón en 15 minutos, teniendo en cuenta el tiempo de estos pasos). Con la olla vacía, se abre la válvula
16 para llenarla con los 40 litros de agua que se habían reservado. A su vez, se abre la válvula de gas (17)
para calentar estos 40 litros. Esta agua se denomina “agua de lavado”, porque será utilizada para lavar el
grano de la maceración.
81
Fig. 28. Recirculación, filtrado con el propio grano. Simultáneamente, se llena la olla.
Cuando el agua alcanza una temperatura de unos 80ºC, se bombea el agua (11) a un reservorio
ubicado encima del macerador. Mientras tanto, el agua de maceración de hace recircular, bombeándola
mediante la bomba 12, como se explicó previamente, para realizar un proceso de filtrado, utilizando el
mismo grano como filtro.
82
Fig. 29. Trasvase del agua de lavado, para desocupar olla.
Cuando la olla se ha vaciado, se comienza a llevar el líquido de maceración (en este punto, la
maceración está completa) hacia la olla, bombeándola con la bomba 9. Cuando se ha vaciado buena parte
del macerador, se comienza el proceso de lavado, dejando caer el agua de lavado (por medio de la válvula
18) sobre el grano depositado en el macerador. Este proceso de realiza en secuencias de 3 minutos (3
minutos encendido y 3 apagado) para que el grano se lave suavemente, sin inundar el macerador.
Mientras tanto, el líquido obtenido se sigue bombeando hacia la olla, y se va calentando, ya que el
siguiente proceso será el hervor, por lo que el almíbar producto de la maceración y lavado debe alcanzar
una temperatura de100ºC.
83
Fig. 30. Vaciado del macerador, y lavado de la malta.
Una vez que se ha alcanzado la temperatura de hervor en la olla, se deja de bombear el líquido de
maceración, ya que para este punto prácticamente se ha vaciado el macerador, aunque la malta absorbe
una cantidad considerable de agua, por lo que siempre goteará un poco. En este punto comienza la
cocción de la cerveza. Durante esta etapa se deben incorporar algunos aditivos, como lúpulo y
clarificante, en distintos momentos. Para esta receta, el tiempo de hervor es de 60 minutos, adicionando
los aditivos de la siguiente forma:
Se observa que la adición de lúpulo se realiza en distintos momentos, ya que cada adición tiene una
finalidad específica para aportar amargor, sabor o aroma al producto.
Para este ejemplo, el sistema simplemente ejecuta una alarma sonora en los momentos descriptos,
para que el usuario incorpore manualmente los aditivos. No obstante, cabe destacar que el equipo tiene
libre la mayoría de sus salidas, por lo cual podrían utilizarse 4 de éstas para controlar, por ejemplo,
pequeñas bobinas que, con un dispositivo adecuado, deje caer los aditivos, de modo de independizar al
usuario del proceso.
84
Fig. 31. Etapa de hervor: cocción del mosto.
Cuando finalizan los 60 minutos de cocción, el sistema le avisa al usuario para que realice lo que
en la jerga se denomina “Whirlpool”. Este proceso consiste en revolver el producto de hervor
intensamente de modo de formar una especie de remolino. Este proceso acumula las partículas sólidas (en
especial lúpulo no disuelto) en el fondo de la olla, en el centro, de modo que el producto gane mayor
claridad. Podría prescindirse de este paso (a costa de un producto de fermentación algo más turbio), o
bien podría realizarse mediante una bomba que recircule el líquido. Se recuerda que se han utilizado tan
solo 7 salidas de las 24 disponibles en el equipo.
El último paso del proceso es el enfriado del mosto (cerveza sin fermentar), mediando un enfriador
contracorriente, que consiste de una serpentina de acero inoxidable, cubierta por una manguera que
permite el paso de agua fría en dirección opuesta. De este modo, se enfría el mosto hasta su temperatura
de fermentación (unos 20ºC-25ºC para esta receta). Para este caso, se cambió de lugar una manguera para
que el sistema quede conectado para bombear agua hacia el fermentador. Esto podría remplazarse por una
válvula doble, por ejemplo, o una nueva bomba.
85
Fig. 32. Enfriado del mosto ya hervido, para iniciar fermentación.
1.3. Evaluación
El equipo fue puesto a prueba en un ejemplo concreto utilizando un equipamiento muy elemental.
Esto implicó que el proceso no se haya podido realizar en forma 100% automática, debiendo el usuario
actuar para la incorporación de aditivos de hervor, whirlpool, cambio de una manguera, y apertura de
válvula para circulación de agua fría. No obstante, solamente se utilizaron 7 de las 24 salidas disponibles
en el equipo, y en todos los casos se sugiere una alternativa para automatizar todos los pasos del proceso.
De esta forma, con los recursos económicos que permitan una instalación adecuada de equipamiento, se
puede lograr una automatización completa.
86
1.4. Resultados
Se realizó una prueba exitosa del equipo desarrollado, logrando la automatización de la mayor
parte del proceso cervecero, y logrando una producción de unos 50 litros de cerveza artesanal. El tiempo
de producción resultó similar a otras ocasiones en las que se desarrolló la misma receta, pero de forma
manual. No obstante, las intervenciones del usuario fueron mínimas, dejando lugar para el desarrollo de
otras actividades mientras se produce la cerveza con control electrónico.
Si bien la automatización no fue completa, sí se controló gran parte del proceso de forma
automática, y se dieron alternativas para automatizar la totalidad del proceso, en el caso de que se
disponga de los recursos necesarios.
Otra observación pertinente es que el sistema empleado para controlar la temperatura de
maceración resultó muy lento. El gradiente de temperatura alcanzado generó una curva de maceración
más lineal que escalonada, ya que se tardaba entre 15 y 20 minutos para aumentar los 5ºC de diferencia
entre un escalón u otro. Como se mencionó previamente, la mejora para este inconveniente reside en la
incorporación de una nueva olla con quemador, de modo que la temperatura se controle sencillamente con
una electroválvula de gas, y la capacidad de entregar calor sea mayor. Si bien este problema afecta al
ejemplo realizado, es un problema de instalaciones, y no del equipo en sí. Además, el producto logrado
tuvo una buena densidad final, y aunque la curva no haya sido óptima, se mejoró el rendimiento respecto
de otras producciones manuales, con temperatura constante (o levemente descendente, por imperfecciones
de aislación térmica), ya que se logró una densidad del mosto similar, pero utilizando más agua y
obteniendo alrededor de un 20% más de producto final, con la misma cantidad de malta.
87
XII. ESTUDIOS DE CONFIABILIDAD DE HARD Y SOFT
1. CONFIABILIDAD DE HARDWARE
Para llevar a cabo un estudio de fiabilidad, se consideraron los componentes electrónicos utilizados
en ambas placas, y se calcularon las tasas de fallas según la norma MIL-HDBK-217F. A continuación, se
muestra una tabla que resume los componentes considerados y la tasa de fallas calculada para cada uno
(en este caso, la tasa mostrada contempla el número de veces que se repite un componente). Las fórmulas
utilizadas son las correspondientes a la norma. Los valores considerados para los factores de cada
componente se detallan en una tabla en el anexo correspondiente.
88
P Potencia Capacitor 470u (25V) electrolítico 1 0,122304
P Potencia Capacitor 1n (630V) multicapa 24 2,1683376
P Potencia Conector IDC macho 26 1 0,5824
P Potencia Borneras dobles 30 0,546
P Potencia Zócalo DIP6 24 0,096768
P Potencia Triac BTA08600 TO220AB 24 0,6586272
P Potencia Regulador LM7805 TO220 3 2,3172
P Potencia Hex Buffer CD4050 DIP16 4 0,4472
P Potencia Optotriac MOC041 DIP6 24 4,488
P Potencia Diodo 1N4001 rectificador 4 0,0021384
P Potencia Led verde 3 mm. 24 0,24288
P Potencia Transformador 220-9 18W 0 0
P Potencia Conexiones en PCB 1 0,10374
TOTAL 25,79383803
Tabla 4. Resultados por componente de fiabilidad.
La tabla anterior establece un valor de p del sistema de 25,8 fallas por cada millón de horas de
uso. Entonces, se puede calcular el tiempo medio entre fallas como:
1 1 106 horas
MTBF . 38770.20hs 4,42años (14)
psist 25,793 fallas
2. CONFIABILIDAD DE SOFTWARE
Para el análisis de confiabilidad de software se utilizó el modelo de Bayesian Fault Rate Estimation
de la norma . Este no se centra en la cantidad de defectos netos sino en el cociente entre defectos y fallas
del sistema, entendiendo por la primera una acción no deseada y por la segunda una falla que hace que el
equipo deje de funcionar.
A continuacón se formulan las hipótesis del modelo:
1. El software es operable. Este ya fue probado y funciona correctamente por lo que no es necesario
desarrollar ninguna predicción sobre el mismo.
2. Los defectos aparecen en un momento indefinido con una tasa ( que tiene una distribución
Gamma con parámetros Xi y Fi+1 (con Xi la media de defectos encontrados por período y Fi+1 el
número de defectos esperados para el próximo período).
3. Los defectos son corregidos entre períodos de prueba y no durante la prueba.
4. El número total de defectos encontrados durante un solo período de prueba con duración t t
responden a una distribución de Poisson con parametro tt.
Para desarrollar el modelo se deben establecer los valores de los parámetros iniciales, es decir para
el período 0 (incluye todos los intervalos de prueba previos al inicio de la modelización, esto se denomina
experiencia), se habrán contabilizado f0 defectos.
89
Ahora se denomina Ti al tiempo total transcurrido entre todos los intervalos de medición de ese
período y Fi al numero total de defectos encontrados en el período. De esta manera, la confiabilidad queda
expresada por:
R(t) = [Ti-1/(Ti-1 + t)]Fi-1 (15)
Una desventaja con la que cuenta el modelo es que trabaja con valores de períodos de prueba
pasados para la estimación de confiabilidad requiriendo grandes tiempos de recopilación de datos.Como
no se tuvo en cuenta este anñalisis desde el inicio del diseño, se desarrolló la gran mayoria del software
sin tener en cuenta la confiabilidad del mismo. Por este motivo no se realizó un registro de los defectos
ocurridos niel tiempo empleado para su prueba.
Se recomienda para futuros diseños implementar esta ténica desde las distintas fases del proyecto
ya que muchas de ellas pueden subsistir durante su desarrollo.
90
XIII. CONCLUSIONES
Se logró de forma exitosa el diseño y construcción del prototipo de un equipo capaz de automatizar
un proceso de producción de cerveza.
Desde el punto de vista financiero, el costo de materiales utilizado para el diseño estuvo dentro de
los valores previstos. En materia académica, los autores se vieron favorecidos en su formación,
empleando dispositivos que no habían utilizado antes, ganando no solamente experiencia técnica, sino
también práctica en el desarrollo de un producto, completando toda su etapa de diseño.
El proyecto resultó viable desde el punto de vista económico, según el análisis propuesto, que
considera un volumen de venta bajo, es decir, suponiendo bajos ingresos. También se demostró la
viabilidad técnica, ya que se construyó un prototipo y se puso a prueba en una aplicación real. En este
sentido, se puede afirmar que el proyecto está en condiciones de constituir un producto apto para la
comercialización.
El fallo principal de este proyecto fue el retraso en el tiempo que insumió el desarrollo del
proyecto, respecto de la planificación, extendiéndose el plazo a casi el doble de tiempo de lo que se había
previsto. No obstante, no se trabajó durante el verano, con lo cual el retraso real no resultó tan rave,
aunque sí considerable. Este inconveniente surgió por un imprevisto técnico, que llevó al rediseño de toda
la etapa de potencia del equipo. Este error pudo haberse evitado, o al menos disminuido, si se hubiese
profundizado la etapa de pruebas de los distintos módulos. Evidentemente, no se validó el funcionamiento
de los módulos en conjunto de forma adecuada. Se recomienda, para futuros diseños, insistir en la prueba
de los circuitos, en especial cuando se trabaja combinando baja y alta potencia de trabajo.
El inconveniente técnico deja lugar a la duda de cómo debe implementarse un control de relés
mecánicos basados en un microcontrolador. En este sentido, la experiencia ganada durante el presente
trabajo puede afirmar dos cuestiones. En primer lugar, resulta fundamental considerar cuestiones de ruteo
cuando se combinan altas y bajas potencias. En segundo lugar, cuando se switchean altas tensiones, se
generarán necesariamente picos de tensión, con transitorios de alta frecuencia, de carácter
electromagnético en las pistas cercanas. Resulta altamente recomendable, para estos casos, implementar
algún dispositivo que retrase la conmutación hasta que haya un cruce por cero, ya sea de tensión o de
corriente, para minimizar estos picos problemáticos, en especial, cuando el tiempo de conmutación no es
crítico.
Desde el punto de vista de la programación, no se logró implementar una interfaz sencilla y
amigable para programar las secuencias del proceso. Hasta esta etapa del diseño, la programación debe
hacerse en lenguaje C, y actualización del firmware (que se realiza por USB) implica desarmar el
gabinete para cambiar un jumper. Por este motivo, la programación debe realizarse de forma previa a la
instalación del equipo, y adaptarse a cada proceso en particular. No obstante, se prevé realizar una
actualización del firmware de modo que sea capaz de comunicarse con una PC mientras el equipo está en
uso, o bien leer un pen drive que pueda contener archivos de texto con las acciones y recetas grabadas.
Estas actualizaciones son posibles, ya que el microcontrolador tiene la capacidad desde el punto de vista
del hardware (USB OTG), y la memoria flash interna de programa no se ha utilizado en su totalidad.
91
XIV. ANEXOS
A continuación, se detalla una tabla con la totalidad de los componentes considerados en el análisis
de fiabilidad del equipo. Como se estableció anteriormente, las fórmulas empleadas corresponden a las
establecidas por la norma:
92
Capacitor 7 0,00099 10 10 10 1 1 1,3 1 0,81 1 1 1 1 0.104247 0.729729
Capacitor 3 0,00012 10 10 10 1 1 1,3 1 1,9 1 1 1 1 0.02964 0.08892
Capacitor 1 0,00012 10 10 10 1 1 1,3 1 4,9 1 1 1 1,6 0.122304 0.122304
Capacitor 24 0,00099 10 10 10 1 1 1,3 1 0,54 1 1 1 1,3 0.0903474 2.1683376
Conector IDC
macho 1 0,04 8 8 2 1 1 0,91 1 1 1 1 1 1 0.5824 0.5824
Borneras
dobles 30 0,007 1 1 2 1 1 1,3 1 1 1 1 1 1 0.0182 0.546
Zócalo 24 0,00064 3 3 1 1 1 1 1 1 1 1 2,1 1 0.004032 0.096768
Triac 24 0,0022 6 6 5,5 1,4 1 1 0,27 1 1 1 1 1 0.0274428 0.6586272
Regulador 3 0,0025 2 2 1 1 1 0,77 1 1 1 0,0012 1 1 0.7724 2.3172
Hex Buffer 4 0,01 2 2 1 1 1 0,1 1 1 1 0,0059 1 1 0.1118 0.4472
Optotriac 24 0,017 2 2 5,5 1 1 1 1 1 1 1 1 1 0.187 4.488
Diodo 4 0,003 6 6 5,5 1 1 1 0,0054 1 1 1 1 1 0.0005346 0.0021384
Led verde 24 0,00023 8 8 5,5 1 1 1 1 1 1 1 1 1 0.01012 0.24288
Transformador 0 0,14 1 1 3 1 1 1,1 1 1 1 1 1 1 0.462 0
Conexiones en
PCB 1 0,00007 2 2 1 1 1 1 1 1 1 741 1 1 0.10374 0.10374
Total 25.79383803
93
Tuercas para ensamblado 4 0,2 0,8
Gabinete (en 2 partes) 1 160 160
Placa impresa lógica 1 65 65
Placa impresa de salida 1 80 80
Tiras de goma (en cm.) 40 0,03 1,2
Bordes de goma (en cm.) 40 0,04 1,6
Termocontraíble (en cm.) 5 0,26 1,3
Alambre de estaño (en cm.) 10 0,01 0,1
Transformador (220V-9V; 2A) 1 60 60
Cable rojo (en cm.) 20 0,03 0,6
Cable negro (en cm.) 20 0,03 0,6
Conectores IDC26 hembra 2 0,9 1,8
Cable plano 26 vías (en cm.) 20 0,08 1,6
SUBTOTAL 415,79
Tabla 6. Costos de los componentes del equipo
94
Botón grande para placa (pulsador) 7 1,4 9,8
Resistencia SMD 2K2 7 0,2 1,4
Tornillos para atornillar LCD 3 0,25 0,75
Tuercas para atornillar LCD 3 0,2 0,6
Jumpers 2 0,08 0,16
Conectores polarizados machos (3 vías) 14 0,57 7,98
Alambre de estaño (por m.) 2 1 2
SUBTOTAL 111,714
Tabla 7. Costos de los componentes de la placa lógica.
95
Conectores polarizados hembra (3 vías) 3 0,42 1,26
Terminales para polarizado 9 0,02 0,18
Termocontraíble (2,4mm) (en cm.) 36 0,17 6,12
SUBTOTAL 86,4
Tubo acero inoxidable AISI 340 (en m.) 2,1 11,73 24,633
Bulones acero inoxidable 3 2,5 7,5
Pasta Teflón (por 1 gr.) 6 0,03 0,18
SUBTOTAL 118,713
Tabla 9. Costos de los elementos utilizados para los termómetros.
96
Bolsa de maceración de tela 1 25 25
Tapón con Airlock para fermentador 1 25 25
SUBTOTAL 2010
Tabla 11. Valor estimado de las instalaciones utilizadas en la validación dl prototipo.
3. SOFTWARE COMPLETO
A continuación se presenta la totalidad de los archivos de programa (tanto los de código (.c) como
los headers (.h) correspondientes al software del proyecto. No se transcriben aquellos archivos generados
automáticamente por el entorno de programación (como el header con las definiciones y macros del
procesador utilizado).
Además, cabe destacar la siguiente aclaración. A diferencia de un programa común, este código
comienza por ejecutar la función “void _Entry(void)”, que verifica el estado de un pin. En condiciones
normales, este pin se encuentra en alta impedancia, por lo que el programa salta a la función “main()”, a
partir de la cual se ejecuta el programa de usuario. No obstante, si se conecta este pin a masa mediante un
jumper, el programa entra en lo que se llama “modo Bootloader” que es un programa que, por medio de
una interfaz USB, permite cagar un nuevo programa compilado en el procesador. Esta función
(suministrada por la empresa Freescale, de forma gratuita y con formato “Open Source”, se mantiene en
el proyecto final, ya que permite una actualización sencilla del firmware. Las librerías de esta aplicación
tampoco se transcriben en el presente anexo.
97
3.2. Archivo usr_entry.c
/*********************************************************************
****
* DISCLAIMER *
* Services performed by FREESCALE in this matter are performed
*
* AS IS and without any warranty. CUSTOMER retains the final decision
*
* relative to the total design and functionality of the end product.
*
* FREESCALE neither guarantees nor will be held liable by CUSTOMER
*
* for the success of this project. FREESCALE disclaims all
warranties, *
* express, implied or statutory including, but not limited to,
*
* implied warranty of merchantability or fitness for a particular
*
* purpose on any hardware, software ore advise supplied to the
project *
* by FREESCALE, and or any product resulting from FREESCALE services.
*
* In no event shall FREESCALE be liable for incidental or
consequential *
* damages arising out of this agreement. CUSTOMER agrees to hold
*
* FREESCALE harmless against any and all claims demands or actions
*
* by anyone on account of any damage, or injury, whether commercial,
*
* contractual, or tortuous, rising directly or indirectly as a result
*
* of the advise or assistance supplied CUSTOMER in connection with
*
* product, services or goods supplied under this Agreement.
*
**********************************************************************
***/
#include "derivative.h"
#include "JM128_Bootloader.h"
void _Entry(void)
{
byte i;
98
for(i=0;i<3;i++) {
__RESET_WATCHDOG();
}
if(PTGD_PTGD0)
{
asm (JMP USR_ENTRY_ADDR); // jump to user entry
}
else
{
__asm {
move.w #0x2700,sr
lea __SP_AFTER_RESET,a7;
lea _SP_INIT,a7
movea.l #0,a6
link a6,#0
lea _SDA_BASE,a5
lea _END_BSS, a0
lea _START_BSS, a1
suba.l a1, a0
move.l a0, d0
beq __skip_bss__
lea _START_BSS, a0
__skip_bss__:
}
MCGC2 = 0x36;
while(!(MCGSC & 0x02)){}; //wait for the OSC stable
MCGC1 = 0x98;
while((MCGSC & 0x1C )!= 0x08){}; // external clock is selected
MCGC3 = 0x48;
while ((MCGSC & 0x48) != 0x48){}; //wait for the PLL is locked
MCGC1 = 0x18;
while((MCGSC & 0x6C) != 0x6C){};
//USBTRC0 = 0x44;
Bootloader_Main();
}
}
99
unsigned int dias=0;
int second_counter;
int index=1;
int lcd_counter = REPETICIONES;
int buzzer_counter = REPETICIONES;
if (Programa_activo() == ON)
{
second_counter++;
segundos++;
if (segundos > 59)
{
minutos++;
segundos = 0;
}
line2[14]=(char) segundos/10+48;
line2[15]=(char) segundos-10*(segundos/10)+48;
if (minutos > 59)
{
horas++;
minutos = 0;
}
line2[11]=(char) minutos/10+48;
line2[12]=(char) minutos-10*(minutos/10)+48;
if (horas > 23)
{
dias ++;
horas = 0;
}
line2[8]=(char) horas/10+48;
line2[9]=(char) horas-10*(horas/10)+48;
if (get_flag()==0)
EscribeCadenaDisplay(2, line2);
if (Get_lcd_flag() == TRUE)
{
if ((lcd_counter--) >0)
LCD_LIGHT = ~LCD_LIGHT;
else
100
{
lcd_counter = REPETICIONES;
Set_lcd_flag (FALSE);
}
}
if (Get_buzzer_flag() == TRUE)
{
if ((buzzer_counter--) >0)
BUZZER = ~BUZZER;
else
{
buzzer_counter = REPETICIONES;
Set_buzzer_flag (FALSE);
}
}
}
}
//Servicios
unsigned int get_segundos (void)
{
return segundos;
}
unsigned int get_minutos (void)
{
return minutos;
}
unsigned int get_horas (void)
{
return horas;
}
unsigned int get_dias (void)
{
return dias;
}
101
(pFun)&dummy_ISR, // vector_11 Vunilfop
(pFun)&dummy_ISR, // vector_12 Vdbgi
(pFun)&dummy_ISR, // vector_13 VReserved13
(pFun)&dummy_ISR, // vector_14 Vferror
(pFun)&dummy_ISR, // vector_15 VReserved15
(pFun)&dummy_ISR, // vector_16 VReserved16
(pFun)&dummy_ISR, // vector_17 VReserved17
(pFun)&dummy_ISR, // vector_18 VReserved18
(pFun)&dummy_ISR, // vector_19 VReserved19
(pFun)&dummy_ISR, // vector_20 VReserved20
(pFun)&dummy_ISR, // vector_21 VReserved21
(pFun)&dummy_ISR, // vector_22 VReserved22
(pFun)&dummy_ISR, // vector_23 VReserved23
(pFun)&dummy_ISR, // vector_24 Vspuri
(pFun)&dummy_ISR, // vector_25 VReserved25
(pFun)&dummy_ISR, // vector_26 VReserved26
(pFun)&dummy_ISR, // vector_27 VReserved27
(pFun)&dummy_ISR, // vector_28 VReserved28
(pFun)&dummy_ISR, // vector_29 VReserved29
(pFun)&dummy_ISR, // vector_30 VReserved30
(pFun)&dummy_ISR, // vector_31 VReserved31
(pFun)&dummy_ISR, // vector_32 Vtrap0
(pFun)&dummy_ISR, // vector_33 Vtrap1
(pFun)&dummy_ISR, // vector_34 Vtrap2
(pFun)&dummy_ISR, // vector_35 Vtrap3
(pFun)&dummy_ISR, // vector_36 Vtrap4
(pFun)&dummy_ISR, // vector_37 Vtrap5
(pFun)&dummy_ISR, // vector_38 Vtrap6
(pFun)&dummy_ISR, // vector_39 Vtrap7
(pFun)&dummy_ISR, // vector_40 Vtrap8
(pFun)&dummy_ISR, // vector_41 Vtrap9
(pFun)&dummy_ISR, // vector_42 Vtrap10
(pFun)&dummy_ISR, // vector_43 Vtrap11
(pFun)&dummy_ISR, // vector_44 Vtrap12
(pFun)&dummy_ISR, // vector_45 Vtrap13
(pFun)&dummy_ISR, // vector_46 Vtrap14
(pFun)&dummy_ISR, // vector_47 Vtrap15
(pFun)&dummy_ISR, // vector_48 VReserved48
(pFun)&dummy_ISR, // vector_49 VReserved49
(pFun)&dummy_ISR, // vector_50 VReserved50
(pFun)&dummy_ISR, // vector_51 VReserved51
(pFun)&dummy_ISR, // vector_52 VReserved52
(pFun)&dummy_ISR, // vector_53 VReserved53
(pFun)&dummy_ISR, // vector_54 VReserved54
(pFun)&dummy_ISR, // vector_55 VReserved55
(pFun)&dummy_ISR, // vector_56 VReserved56
(pFun)&dummy_ISR, // vector_57 VReserved57
(pFun)&dummy_ISR, // vector_58 VReserved58
(pFun)&dummy_ISR, // vector_59 VReserved59
(pFun)&dummy_ISR, // vector_60 VReserved60
(pFun)&dummy_ISR, // vector_61 Vunsinstr
(pFun)&dummy_ISR, // vector_62 VReserved62
(pFun)&dummy_ISR, // vector_63 VReserved63
(pFun)&dummy_ISR, // vector_64 Virq
(pFun)&dummy_ISR, // vector_65 Vlvd
(pFun)&dummy_ISR, // vector_66 Vlol
(pFun)&dummy_ISR, // vector_67 Vspi1
(pFun)&dummy_ISR, // vector_68 Vspi2
(pFun)&dummy_ISR, // vector_69 Vusb
(pFun)&dummy_ISR, // vector_70 VReserved70
(pFun)&dummy_ISR, // vector_71 Vtpm1ch0
102
(pFun)&dummy_ISR, // vector_72 Vtpm1ch1
(pFun)&dummy_ISR, // vector_73 Vtpm1ch2
(pFun)&dummy_ISR, // vector_74 Vtpm1ch3
(pFun)&dummy_ISR, // vector_75 Vtpm1ch4
(pFun)&dummy_ISR, // vector_76 Vtpm1ch5
(pFun)&dummy_ISR, // vector_77 Vtpm1ovf
(pFun)&dummy_ISR, // vector_78 Vtpm2ch0
(pFun)&dummy_ISR, // vector_79 Vtpm2ch1
(pFun)&dummy_ISR, // vector_80 Vtpm2ovf
(pFun)&dummy_ISR, // vector_81 Vsci1err
(pFun)&dummy_ISR, // vector_82 Vsci1rx
(pFun)&dummy_ISR, // vector_83 Vsci1tx
(pFun)&dummy_ISR, // vector_84 Vsci2err
(pFun)&dummy_ISR, // vector_85 Vsci2rx
(pFun)&dummy_ISR, // vector_86 Vsci2tx
(pFun)&dummy_ISR, // vector_87 Vkeyboard
(pFun)&dummy_ISR, // vector_88 Vadc
(pFun)&dummy_ISR, // vector_89 Vacmpx
(pFun)&dummy_ISR, // vector_90 Viic1x
(pFun)&RTC_UpdateTime, // vector_91 Vrtc
(pFun)&dummy_ISR, // vector_92 Viic2x
(pFun)&dummy_ISR, // vector_93 Vcmt
(pFun)&dummy_ISR, // vector_94 Vcanwu
(pFun)&dummy_ISR, // vector_95 Vcanerr
(pFun)&dummy_ISR, // vector_96 Vcanrx
(pFun)&dummy_ISR, // vector_97 Vcantx
(pFun)&dummy_ISR, // vector_98 Vrnga
(pFun)&dummy_ISR, // vector_99 VReserved99
(pFun)&dummy_ISR, // vector_100 VReserved100
(pFun)&dummy_ISR, // vector_101 VReserved101
(pFun)&dummy_ISR, // vector_102 VReserved102
(pFun)&dummy_ISR, // vector_103 VReserved103
(pFun)&dummy_ISR, // vector_104 VL7swi
(pFun)&dummy_ISR, // vector_105 VL6swi
(pFun)&dummy_ISR, // vector_106 VL5swi
(pFun)&dummy_ISR, // vector_107 VL4swi
(pFun)&dummy_ISR, // vector_108 VL3swi
(pFun)&dummy_ISR, // vector_109 VL2swi
(pFun)&dummy_ISR, // vector_110 VL1swi
};
103
}
//Selección de clock
MCGC2 = 0x36;
while(!(MCGSC & 0x02)){}; //wait for the OSC stable
MCGC1 = 0x98;
while((MCGSC & 0x1C )!= 0x08){}; // external clock is selected
MCGC3 = 0x48;
while ((MCGSC & 0x48) != 0x48){}; //wait for the PLL is locked
MCGC1 = 0x18;
while((MCGSC & 0x6C) != 0x6C){};
//Deshabilito detección de baja tensión
SPMSC1_LVDE = 0;
//Habilito interrupciones
EnableInterrupts;
//Inicialización de interrupciones
IO_Init();
RTC_Init();
}
PTFDD_PTFDD6 = 1;
PTEDD_PTEDD0 = 1;
PTEDD_PTEDD1 = 1;
PTEDD_PTEDD2 = 1;
PTEDD_PTEDD3 = 1;
PTEDD_PTEDD4 = 1;
PTEDD_PTEDD5 = 1;
PTEDD_PTEDD6 = 1;
104
//Entradas de botones
PTADD_PTADD4 = 0;
PTADD_PTADD3 = 0;
PTEDD_PTEDD7 = 0;
PTADD_PTADD1 = 0;
PTGDD_PTGDD1 = 0;
PTADD_PTADD2 = 0;
PTADD_PTADD0 = 0;
//Otras salidas
//Buzzer
PTFDD_PTFDD2 = 1;
//LCD Light
PTFDD_PTFDD4 = 1;
//Otros servicios
void reset_counter (void)
{
second_counter = 0;
}
int counter_done (int seconds)
{
int ret;
if (second_counter==seconds)
ret=1;
else
ret=0;
return ret;
}
void Reset_Timer (void)
{
105
segundos = 0;
minutos = 0;
horas = 0;
dias = 0;
}
#include "display.h"
#include "MCF51JM128.h"
// Variables globales
const char gszSecInitDisplay[] = {0x3C, // 8 bits, 2 lines, 5x11 dots.
0x3C, // 8 bits, 2 lines, 5x11 dots.
0x3C, // 8 bits, 2 lines, 5x11 dots.
0x3C, // 8 bits, 2 lines, 5x11 dots.
0x08, // Display off, Cursor off,
Blinkink off.
0x01, // Display clear.
0x06, // Cursor moving direction,
shift off.
0x0C, // Display on, Cursor off,
Blinkink off.
0x00};
//********************************************************************
*********************
// Funcion : void Delay(int iDelay)
// Descripcion: Realiza un delay a traves de un for.
// Delay(DELAYCOM); Esto es aprox. 1ms
//********************************************************************
*********************
void Delay(int iDelay)
{
int iIndex,j;
for(iIndex = 0; iIndex < iDelay; iIndex++)
for (j=0; j<4; j++)
;
return;
}
//********************************************************************
*********************
// Funcion : void InicializarDisplay(void)
// Descripcion: Inicializa el display.
//********************************************************************
*********************
106
void InicializarDisplay(void)
{
char chIndice = 0;
while(gszSecInitDisplay[chIndice])
{
EscribeComandoDisplay(gszSecInitDisplay[chIndice]);
Delay(DELAYCONST);
chIndice++;
}
EscribeComandoDisplay(BORRAR_DISPLAY);
return;
}
//********************************************************************
*********************
// Funcion : void EscribeComandoDisplay(unsigned char chComando)
// Descripcion: Escribe un comando en el display.
//********************************************************************
*********************
void EscribeComandoDisplay(char chComando)
{
RSP = 0;
RWP = 0;
BuildData (chComando);
ENP = 1;
ENP = 0;
Delay(DELAYCOM);
return;
}
//********************************************************************
*********************
// Funcion : void EscribeDatoDisplay(char chDato)
// Descripcion: Escribe un dato en el display.
//********************************************************************
*********************
void EscribeDatoDisplay(char chDato)
{
RSP = 1;
RWP = 0;
BuildData (chDato);
ENP = 1;
ENP = 0;
Delay(DELAYCOM);
return;
}
//********************************************************************
*********************
// Funcion : void EscribeCadenaDisplay(char chLinea,
// char *pszCadena)
// Descripcion: Escribe una cadena, en la linea indicada en el
display.
//********************************************************************
*********************
void EscribeCadenaDisplay(char chLinea,char *pszCadena)
{
char chIndice = 0,
chOffSetPos = 0;
if(chLinea == 2)
chOffSetPos = 0x40;
while(pszCadena[chIndice])
107
{
EscribeComandoDisplay(0x80 | (chIndice + chOffSetPos));
EscribeDatoDisplay(pszCadena[chIndice]);
chIndice++;
}
return;
}
//********************************************************************
*********************
// Funcion : void BuildData (char ChByte)
// Descripcion: Arma un byte con el comando o dato de salida
//********************************************************************
*********************
void BuildData (char chByte)
{
if ((chByte & 1)==0)
DB0=0;
else
DB0=1;
#include "MCF51JM128.h"
#include "Temperatura.h"
#include "display.h"
108
#ifndef ONE_WIRE_C
#define ONE_WIRE_C
109
ONE_WIRE_PIN = 0;
delay_us(2); //pulso bajo para iniciar el time-
slot
ONE_WIRE_PIN = recorrer_dato (data,count);
delay_us(60); //espero que termine el time-slot
ONE_WIRE_PIN = 1; //pulso alto
delay_us(2); //espero al menos 1us
}
}
//Función que lee el bus y devuelve un dato de 8 bits
unsigned char onewire_read (void) {
int count,i;
char data_aux [16];
unsigned char data;
data=0;
}
//Función para leer un byte completo de dato
unsigned char onewire_readbyte()
{
//Definición de variables
int count,i;
char data_aux [8];
unsigned char data;
data=0;
110
//Inicializo data_aux[] en cero
for (i=0; i<8; i++)
data_aux [i] = 0;
ONE_WIRE_PIN_MODE = 0;
delay_us(10); //espero hasta que se estabilice
el dato
data_aux[7-count] = ONE_WIRE_PIN; //guardo el resultado
delay_us(70); //espero que termine el time-slot
}
//Actualizo variable de retorno
for (i=0; i<8; i++){
data = data<<1;
data += data_aux[i];
}
return data;
}
#endif /*ONE_WIRE_C*/
#ifndef DS1820_C
#define DS1820_C
//Inicialización en cero
for (i=0; i<16; i++)
temp_disp [i] = 0;
ds1820_configure(0x00, 0x00, 0x00); //Resolución de 9 bits
onewire_reset();
onewire_write(DS1822_CMD_MATCHROM);
//Inicio conversión de temperatura
for(i=0;i<8;i++)
onewire_write(rom[i]);
onewire_write(DS1822_CMD_CONVERTTEMP);
//Espero que termine el tiempo de conversión
delay_ms(80);
//Selecciono el dispositivo que quiero leer
onewire_reset();
onewire_write(DS1822_CMD_MATCHROM);
//Lectura del scratchpad (memoria interna que almacena valor de
temperatura)
for(i=0;i<8;i++)
onewire_write(rom[i]);
onewire_write(0xBE);
//Leo los dos bytes del scratchpad
for (i=0; i<2; i++)
111
scratchpad[i] = onewire_read();
//Convierto a un solo valor entero
temp1 = scratchpad[0];
temp2 = scratchpad[1];
temp3 = (temp2*256)+temp1;
//result = (int) (temp3 / 2); //Cálculo para resolución de 0.5
ºC
result = (int) temp3 / 16; //Cálculo para resolución de 12
bits
//delay_ms(200);
delay_ms(10);
onewire_reset();
return(result);
}
//Función de escritura de dato de configuración
//Argumentos: alarm trigger high, alarm trigger low, configuration
void ds1820_configure(unsigned char TH, unsigned char TL, unsigned
char config) {
onewire_reset();
onewire_write(0xCC); //Skip ROM
onewire_write(0x4E); //Escribo en el scratchpad
onewire_write(TH);
onewire_write(TL);
onewire_write(config);
}
//Fnción de servicio directo
//Devulve el valor de temperatura medido por el número correspondiete
de termómetro
int Get_Temp (int termometro)
{
int temperatura;
temperatura = ds1820_read (rom_temp[termometro-1]);
if ((temperatura<0)||(temperatura>120))
temperatura = TEMP_ERR;
return temperatura;
}
#endif /*DS1820_C*/
#include "Salida.h"
#include "Proceso.h"
#include "derivative.h"
112
else if (salida==6)
SALIDA6=1;
else if (salida==7)
SALIDA7=1;
else if (salida==8)
SALIDA8=1;
else if (salida==9)
SALIDA9=1;
else if (salida==10)
SALIDA10=1;
else if (salida==11)
SALIDA11=1;
else if (salida==12)
SALIDA12=1;
else if (salida==13)
SALIDA13=1;
else if (salida==14)
SALIDA14=1;
else if (salida==15)
SALIDA15=1;
else if (salida==16)
SALIDA16=1;
else if (salida==17)
SALIDA17=1;
else if (salida==18)
SALIDA18=1;
else if (salida==19)
SALIDA19=1;
else if (salida==20)
SALIDA20=1;
else if (salida==21)
SALIDA21=1;
else if (salida==22)
SALIDA22=1;
else if (salida==23)
SALIDA23=1;
else if (salida==24)
SALIDA24=1;
}
113
SALIDA9=0;
else if (salida==10)
SALIDA10=0;
else if (salida==11)
SALIDA11=0;
else if (salida==12)
SALIDA12=0;
else if (salida==13)
SALIDA13=0;
else if (salida==14)
SALIDA14=0;
else if (salida==15)
SALIDA15=0;
else if (salida==16)
SALIDA16=0;
else if (salida==17)
SALIDA17=0;
else if (salida==18)
SALIDA18=0;
else if (salida==19)
SALIDA19=0;
else if (salida==20)
SALIDA20=0;
else if (salida==21)
SALIDA21=0;
else if (salida==22)
SALIDA22=0;
else if (salida==23)
SALIDA23=0;
else if (salida==24)
SALIDA24=0;
}
if (salida==1)
ret=SALIDA1;
else if (salida==2)
ret=SALIDA2;
else if (salida==2)
ret=SALIDA2;
else if (salida==3)
ret=SALIDA3;
else if (salida==4)
ret=SALIDA4;
else if (salida==5)
ret=SALIDA5;
else if (salida==6)
ret=SALIDA6;
else if (salida==7)
ret=SALIDA7;
else if (salida==8)
114
ret=SALIDA8;
else if (salida==9)
ret=SALIDA9;
else if (salida==10)
ret=SALIDA10;
else if (salida==11)
ret=SALIDA11;
else if (salida==12)
ret=SALIDA12;
else if (salida==13)
ret=SALIDA13;
else if (salida==14)
ret=SALIDA14;
else if (salida==15)
ret=SALIDA15;
else if (salida==16)
ret=SALIDA16;
else if (salida==17)
ret=SALIDA17;
else if (salida==18)
ret=SALIDA18;
else if (salida==19)
ret=SALIDA19;
else if (salida==20)
ret=SALIDA20;
else if (salida==21)
ret=SALIDA21;
else if (salida==22)
ret=SALIDA22;
else if (salida==23)
ret=SALIDA23;
else if (salida==24)
ret=SALIDA24;
return ret;
}
#ifndef _JM128_BOOTLOADER_H_
#define _JM128_BOOTLOADER_H_
void Bootloader_Main(void);
#endif
115
Este archivo ya fue transcripto en el desarrollo del informe.
3.11. Archivo Temperatura.h
#ifndef __MAINDEFS_H_
#define __MAINDEFS_H_
//Definiciones de constantes
//Botones
char boton_disp_lcd [16] = "LCD ";
char boton_disp_menu [16] = "Menu ";
char boton_disp_enter [16] = "Enter ";
char boton_disp_up [16] = "Up ";
char boton_disp_down [16] = "Down ";
char boton_disp_left [16] = "Left ";
char boton_disp_right [16] = "Right ";
//Opciones de menu
char menu_iniciar [16] = "Iniciar ";
char menu_pausa [16] = "Pausa ";
char menu_editar_programa [16] = "Editar Programa";
char menu_cargar_programa [16] = "Cargar Programa";
char menu_control_manual [16] = "Control Manual ";
char menu_ir_a [16] = "Ir a accion ";
char menu_salir [16] = "Salir ";
char encendido_1 [16] = "Presione Entrar";
char encendido_2 [16] = " Para Iniciar ";
//Opciones de Pausa
char pausa_continuar [16] = "Continuar ";
char pausa_detener [16] = "Detener ";
char pausa_resetear [16] = "Resetear ";
char programa_detenido [16] = "Prog. Detenido ";
#endif // __MAINDEFS_H_
//Definiciones de hardware
#define BOTON_ENTER PTAD_PTAD4
116
#define BOTON_MENU PTAD_PTAD3
#define BOTON_LCD PTED_PTED7
#define BOTON_ARRIBA PTAD_PTAD1
#define BOTON_ABAJO PTGD_PTGD1
#define BOTON_IZQUIERDA PTAD_PTAD2
#define BOTON_DERECHA PTAD_PTAD0
//Prototipos
void check_botones (int boton);
void evento_botones (void);
void reset_counter (void);
int counter_done (int seconds);
int get_flag (void);
void set_flag (int valor);
117
#define SALIDA12 PTDD_PTDD6 //12
#define SALIDA2 PTBD_PTBD6 //2
#define SALIDA9 PTDD_PTDD5 //7
#define SALIDA1 PTBD_PTBD7 //1
#define SALIDA8 PTDD_PTDD4 //8
#define SALIDA4 PTDD_PTDD0 //6
#define SALIDA7 PTDD_PTDD3 //9
#define SALIDA5 PTDD_PTDD1 //5
#define SALIDA6 PTDD_PTDD2 //4
//Prototipos
void salida_on (int salida);
void salida_off (int salida);
void clear_all_outputs (void);
int get_out_state (int salida);
void switch_salida (int salida);
//Procesos posibles
#define UNUSED 0
#define MOLIENDA 1
#define MACERACION 2
#define CURVA_MACERACION 3
#define RECIRCULACION 4
#define LAVADO 5
#define HERVOR 6
#define ENFRIAMIENTO 7
#define FERMENTACION 8
#define FINAL_PROCESO 9
//Condiciones de accion
#define POR_TIEMPO 1
#define POR_TEMPERATURA 2
#define POR_EVENTO 3
#define POR_BOTON 4
#define POR_MANUAL 5
//Activación de alarma
#define ALARMA 25
//Otras definiciones
#define MAX_ACCIONES 100
#define ACIERTOS_TEMP 10
#define N_RECETAS 10
118
int Temperatura_maceracion_1;
int Tiempo_maceracion_1;
int Temperatura_maceracion_2;
int Tiempo_maceracion_2;
int Temperatura_maceracion_3;
int Tiempo_maceracion_3;
int Temperatura_maceracion_4;
int Tiempo_maceracion_4;
int Temperatura_maceracion_5;
int Tiempo_maceracion_5;
int Temperatura_maceracion_6;
int Tiempo_maceracion_6;
int Temperatura_maceracion_7;
int Tiempo_maceracion_7;
int Temperatura_maceracion_8;
int Tiempo_maceracion_8;
int Temperatura_maceracion_9;
int Tiempo_maceracion_9;
int Temperatura_maceracion_10;
int Tiempo_maceracion_10;
//Recirculado y lavado
int Tiempo_recirculado;
int Volumen_agua_lavado;
int Temperatura_agua_lavado;
//Adiciones durante el hervor
int Tiempo_aditivo_1;
int Tiempo_aditivo_2;
int Tiempo_aditivo_3;
int Tiempo_aditivo_4;
int Tiempo_aditivo_5;
int Tiempo_aditivo_6;
int Tiempo_aditivo_7;
int Tiempo_aditivo_8;
int Tiempo_aditivo_9;
int Tiempo_aditivo_10;
int Tiempo_de_hervor;
//Enfriado y Fermentación
int Tiempo_whirlpool;
int Temperatura_inicial_fermentacion;
int Tiempo_de_enfriamiento;
};
typedef struct receta rec_t;
119
struct alarma
{
char* mensaje; //Puntero al mensaje que acompaña la
alarma
BOOL sonido; //Activación de indicación sonora
(Buzzer)
BOOL titilar_lcd; //Activación de parpadeo del LED del LCD
durante la alarma
};
typedef struct alarma alarm_t;
//Definición de tipo tiempo
struct tiempo
{
unsigned int dia;
unsigned int hora;
unsigned int minuto;
unsigned int segundo;
};
typedef struct tiempo tiempo_t;
120
4. ESQUEMÁTICOS
A continuación, se adjuntan los esquemáticos de los circuitos de ambas placas. En primer lugar, se
detalla el circuito correspondiente a la placa lógica.
121
En segundo lugar, se detalla el circuito correspondiente a la placa de potencia. Cabe destacar que el
circuito de salida (triac y optotriac) se repite 24 veces. Su entrada se conecta a la salida de los buffers,
denominados “OData1… OData24”.
122
XV. BIBLIOGRAFÍA
1. Hojas de datos
http://www.freescale.com/files/32bit/doc/data_sheet/MCF51JM128.pdf
http://www.freescale.com/files/32bit/doc/ref_manual/MCF51JM128RM.pdf
http://cache.freescale.com/files/microcontrollers/doc/app_note/AN3748.pdf
http://pdf1.alldatasheet.com/datasheet-pdf/view/58557/DALLAS/DS18B20.html
http://www.datasheetcatalog.org/datasheets/150/363781_DS.pdf
http://pdf1.alldatasheet.com/datasheet-pdf/view/5039/MOTOROLA/MOC3041.html
http://www.dfrobot.com/image/data/FIT0127/datasheet.pdf
http://es.scribd.com/doc/18946526/Datasheet-Lcd-16x2
http://pdf1.alldatasheet.com/datasheet-pdf/view/50859/FAIRCHILD/CD4050.html
http://www.minicerveceria.com/back_office/system/file/curso_abc.pdf
http://www.minicerveceria.com/back_office/system/articulo/verArticulo.php?idArticulo=38
http://www.minicerveceria.com/back_office/system/articulo/verArticulo.php?idArticulo=50
http://www.minicerveceria.com/back_office/system/articulo/verArticulo.php?idArticulo=49
3. Libros
Mohan, U. et al, Power Electronics: Converters, Applications, and Design, Tercera Edición,
Editorial Wiley, 2003
123