TodoElectrodo
lunes, 14 de noviembre de 2050
martes, 20 de octubre de 2026
¿Ensamblador?¿Obsoleto?
Programación en ensamblador de microcontroladores PIC: por qué todavía importa
En una época dominada por Arduino, ESP32 y lenguajes de alto nivel, hablar de ensamblador puede parecer hablar de arqueología informática. Y sin embargo, en el mundo de los microcontroladores (y en particular en la familia PIC de Microchip) el ensamblador sigue teniendo un papel importante. Yo reconozco que cuando lo vi en el temario de la asignatura de la Universidad... Pensé... ¿Estamos locos? Con lo maravilloso que es Python o lo fácil que puede ser programar un Arduino. xD
En esta entrada vamos a ver por qué merece la pena aprenderlo, qué son los registros de un PIC explicados de forma sencilla, y cómo se estructura un programa en ensamblador con varios ejemplos comentados línea a línea.
¿Por qué aprender ensamblador si "está obsoleto"?
Es cierto que hoy en día casi nadie desarrolla aplicaciones completas en ensamblador. Compiladores de C, C++ e incluso Python (con MicroPython) generan código lo bastante bueno para la mayoría de usos. Pero el ensamblador conserva ventajas que ningún lenguaje de alto nivel te ofrece:
Entiendes realmente cómo funciona el microcontrolador. Con ensamblador no hay capas de abstracción: tú mueves los datos entre registros, tú activas los bits de configuración, tú controlas cada ciclo de instrucción. Aprendes qué hace la máquina, no solo qué hace el compilador.
Control absoluto del tiempo de ejecución. En un PIC cada instrucción tarda 4 ciclos de reloj (1 µs a 4 MHz, salvo saltos que tardan el doble). Eso permite temporizaciones exactísimas, algo crítico en comunicaciones como RS-232 "a mano" o generación de señales.
Eficiencia máxima en recursos. Los PIC más pequeños (10F, 12F) tienen muy poca memoria flash y RAM. En ensamblador el programa es tan pequeño como tú lo escribas; no hay librerías ni código de arranque innecesario.
Es la mejor herramienta didáctica. Para quien estudia electrónica o informática industrial, el ensamblador conecta el hardware con el software: registros, interrupciones, timers, puertos… todo queda a la vista.
Depuración y trabajo a bajo nivel. Cuando hay que analizar o parchear código máquina, entender ensamblador es imprescindible.
En resumen: no es el lenguaje más productivo, pero es el que más te enseña y el que más control te da.
Los registros de un PIC, explicados de forma sencilla
Puedes imaginar un microcontrolador PIC como una pequeña oficina: dentro hay cajones (los registros) donde se guardan temporalmente datos e instrucciones, y unas notitas pegadas en cada cajón (los bits de configuración) que dicen qué hace cada cosa.
Un registro no es más que una memoria muy rápida, normalmente de 8 bits, que está dentro del propio chip. Cada registro tiene una dirección y una función.
En un PIC hay dos tipos de registros:
SFR (Special Function Registers, registros de función especial): son los "cajones oficiales" del chip, con una misión concreta asignada por el fabricante. Cada bit tiene un significado: configurar un puerto, arrancar un timer, activar una interrupción… No los eliges tú: vienen fijos de fábrica en direcciones concretas de la RAM. Los de la tabla de abajo son todos SFR.
GPR (General Purpose Registers, registros de propósito general): son los "cajones libres": posiciones de RAM sin función asignada que tú usas como variables del programa. Cuando en un ejemplo reservamos
ContadorconCBLOCK, lo que hacemos es darle nombre a un GPR. A partir de ahí, es solo un cajón donde guardas y lees datos.
Dicho de otra forma: los SFR son el panel de control del microcontrolador (tú escribes en ellos para manejar el hardware) y los GPR son tu block de notas (tú decides qué guardar en cada uno). Físicamente ambos viven en la misma RAM, solo se distinguen por su función y dirección.
Los SFR más importantes:
Registro | Nombre | ¿Para qué sirve? |
|---|---|---|
W | Working register | El "cajón" de trabajo. Casi todas las operaciones pasan por él. Es como la calculadora de mano del micro. |
STATUS | Estado | Guarda banderas (flags) que informan del resultado de las operaciones: si hubo acarreo (C), si el resultado fue cero (Z), etc. |
PORTA / PORTB | Puertos de E/S | Reflejan el estado físico de los pines: escribir en ellos pone pines a 1 o 0; leerlos te dice si hay tensión en cada pin. |
TRISA / TRISB | Dirección de los pines | Configuran cada pin como entrada (1) o salida (0). "TRIS" viene de triestado. |
OPTION_REG | Opciones | Configura el temporizador TMR0, el prescaler y las interrupciones por flanco en RB0. |
INTCON | Control de interrupciones | Activa/desactiva interrupciones y guarda sus flags. |
TMR0 | Temporizador 0 | Un contador que se incrementa solo y permite medir tiempos. |
FSR / INDF | Puntero indirecto | Sirven para acceder a posiciones de memoria RAM "por dirección", como un puntero. |
Dos ideas clave para entenderlo todo:
Todo en un PIC son registros. Leer un pin, mandar un dato por el puerto serie o cambiar la velocidad de un timer se hace siempre igual: escribiendo o leyendo bits de un registro.
El registro W es el protagonista. Cargar un dato en W (
MOVLW), moverlo a un registro (MOVWF), sumarlo (ADDWF)… el flujo normal de un programa en ensamblador es un ir y venir de datos entre W y el resto de registros.
Las directivas principales del ensamblador (MPASM)
Las directivas no son instrucciones del PIC: son órdenes que se le dan al ensamblador (MPASM, el de Microchip) para que prepare el código. Las más usadas:
LIST P=16F84A— Indica el modelo de PIC para el que se ensambla.#INCLUDE <P16F84A.INC>— Incluye el fichero de Microchip con los nombres de todos los registros y bits, para no tener que memorizar direcciones.__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF— Graba los fusibles de configuración: tipo de oscilador, watchdog desconectado, etc.ORG 0x00— Indica en qué dirección de memoria empieza el código (0x00 es el vector de reset; 0x04, el de interrupción).EQU— Define constantes con nombre (por ejemplo, para dar nombre a una variable).CBLOCK ... ENDC— Reserva posiciones de RAM para variables.END— Marca el fin del código fuente.
Además, conviene recordar los modos de direccionamiento de las instrucciones, porque en los ejemplos aparecen constantemente:
W→ el destino es el registro W (ej.:ADDWF REG, Wguarda el resultado en W).F→ el destino es el propio registro (ej.:MOVWF PORTB).LW→ "literal en W": el dato viaja directamente (ej.:MOVLW 0xFF).Etiquetas → direcciones de salto (ej.:
GOTO Bucle).
Las instrucciones más comunes del ensamblador PIC
Un PIC16F solo entiende 35 instrucciones, pero con menos de 20 se escribe el 90 % de los programas. Estas son las que usarás a diario, agrupadas por lo que hacen:
Mover datos:
MOVLW→ MOVe Literal to W. Carga una constante en W. Ej.:MOVLW d'255'.MOVWF→ MOVe W to File. Copia W a un registro. Ej.:MOVWF PORTB.MOVF→ MOVe File. Copia un registro a W (MOVF Contador, W) o a sí mismo. Es la forma de leer variables.SWAPF→ SWAP File. Intercambia los dos nibbles (4 bits) de un registro. Su uso estrella es restaurar W sin tocar las banderas.
Aritmética y lógica:
ADDWF→ ADD W to File. Suma W a un registro.SUBLW→ SUBtract W from Literal. Resta W de una constante (guardando el resultado en W).ANDWF/IORWF/XORWF→ Operaciones lógicas AND, OR y XOR entre W y un registro. Imprescindibles para poner, borrar o invertir bits concretos.INCF/DECF→ INCrement / DECrement File. Suman o restan 1 a un registro.COMF→ COMplement File. Invierte todos los bits de un registro.RLF/RRF→ Rotate Left/Right through Carry. Rotan los bits a izquierda o derecha pasando por la bandera C (multiplicar/dividir por 2, registros de desplazamiento).
Manipular bits:
BSF→ Bit Set File. Pone a 1 un bit de un registro. Ej.:BSF PORTB, 0.BCF→ Bit Clear File. Pone a 0 un bit. Ej.:BCF STATUS, RP0.BTFSC→ Bit Test File, Skip if Clear. Si el bit vale 0, salta la instrucción siguiente (equivale a "si… entonces").BTFSS→ Bit Test File, Skip if Set. Lo mismo pero al revés: salta si el bit vale 1.
Saltos y control del programa:
GOTO→ GO TO. Salto incondicional a una etiqueta. Con él se construyen los bucles.CALL/RETURN→ Llaman a una subrutina y vuelven de ella.RETFIE→ RETurn From Interrupt and Enable. Vuelve de una interrupción reactivándolas.DECFSZ→ DECrement File, Skip if Zero. Resta 1 y salta la siguiente instrucción si el resultado es cero. Es LA instrucción de los bucles de repetición y los retardos.INCFSZ→ Igual que la anterior pero sumando 1.CLRW/CLRF→ Ponen a 0 el registro W o un registro completo (ej.:CLRF TRISB).NOP→ No OPeration. No hace nada; sirve para perder un ciclo, como hueco o para temporizaciones finas.
Truco para recordarlas: el nombre casi siempre es la contracción de su significado en inglés (BSF = Bit Set File, DECFSZ = DECrement File Skip if Zero). Cuando interiorices esa lógica, el instruction set se lee solo.
_________________________________________________________________________________
Unos ejemplillos comentados línea a línea
Todos los ejemplos son para el PIC16F84A, un clásico de aprendizaje. Fíjate en cómo cada instrucción hace una sola cosa muy simple: la potencia viene de combinarlas.
Ejemplo 1: Hola mundo — encender un LED
LIST P=16F84A ; Indicamos el modelo de PIC
#INCLUDE <P16F84A.INC> ; Nombres de registros y bits
__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
; Oscilador a cristal, watchdog OFF
ORG 0x00 ; El programa empieza en la dirección 0
BSF STATUS, RP0 ; Paso al banco 1 de memoria (RP0=1)
MOVLW b'11111110' ; Cargo en W el valor 11111110 (RB0 salida)
MOVWF TRISB ; Lo copio a TRISB: RB0 salida, resto entradas
BCF STATUS, RP0 ; Vuelvo al banco 0 (donde está PORTB)
BSF PORTB, 0 ; Pongo RB0 a "1": el LED se enciende
Bucle:
GOTO Bucle ; Bucle infinito: no hacemos nada más
END ; Fin del código fuenteQué hace: configura RB0 como salida y lo pone a 1, dejando el LED encendido para siempre. Nótese que b'11111110' es binario: el bit 0 vale 0, y en TRIS un 0 significa salida.
Ejemplo 2: Parpadeo de un LED
LIST P=16F84A
#INCLUDE <P16F84A.INC>
__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
ORG 0x00
BSF STATUS, RP0 ; Banco 1
MOVLW b'11111110' ; RB0 como salida
MOVWF TRISB
BCF STATUS, RP0 ; Banco 0
Parpadeo:
BSF PORTB, 0 ; LED ON
CALL Retardo ; Espero un tiempo (subrutina)
BCF PORTB, 0 ; LED OFF
CALL Retardo ; Espero otro rato
GOTO Parpadeo ; Repito eternamente
Retardo: ; --- Subrutina de retardo ---
MOVLW d'255' ; W = 255 (d'255' = 255 en decimal)
MOVWF 0x0C ; Lo guardo en una posición de RAM (contador)
Decremento:
DECFSZ 0x0C, F ; Resto 1 al contador; si da 0, salto la
; siguiente instrucción (SZ = skip if zero)
GOTO Decremento ; Todavía no es cero: sigo restando
RETURN ; Contador a cero: vuelvo al programa principal
END¿Qué hace este programilla? el LED parpadea. El truco del retardo es DECFSZ: resta 1 bit y salta la instrucción siguiente si el resultado es cero. Es una de las instrucciones más usadas del PIC.
Ejemplo 3: Leer un botón y actuar sobre un LED
LIST P=16F84A
#INCLUDE <P16F84A.INC>
__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
ORG 0x00
BSF STATUS, RP0 ; Banco 1
MOVLW b'11111110' ; RB0 salida (LED)...
MOVWF TRISB ; ...y RB1..RB7 de entrada (ahí irá el botón)
MOVLW b'00010000' ; RA4 como entrada (ahí conecto el botón)
MOVWF TRISA
BCF STATUS, RP0 ; Banco 0
Espera:
BTFSC PORTA, 4 ; "Bit Test File, Skip if Clear":
; si RA4 es 0 (botón pulsado),
; salto la siguiente instrucción
GOTO Espera ; RA4 sigue a 1 (botón sin pulsar): sigo esperando
BSF PORTB, 0 ; Botón pulsado: enciendo el LED
Suelta:
BTFSS PORTA, 4 ; "Skip if Set": si RA4 vuelve a 1
; (botón soltado), salto la siguiente
GOTO Suelta ; Sigue pulsado: espero
BCF PORTB, 0 ; Botón soltado: apago el LED
GOTO Espera ; Vuelvo a esperar otra pulsación
END¿Qué hace este nuevo programa?: espera a que pulses el botón conectado a RA4 para encender el LED, y lo apaga al soltarlo. BTFSC y BTFSS son las dos instrucciones de test de bits, las "responsables" de casi toda la toma de decisiones en ensamblador.
Ejemplo 4: Contador en el puerto B (8 LEDs en binario)
LIST P=16F84A
#INCLUDE <P16F84A.INC>
__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
CBLOCK 0x0C ; A partir de la dirección 0x0C de RAM...
Contador ; ...reservo una variable llamada "Contador"
GuardaRet ; ...y otra para guardar contadores de retardo
ENDC
ORG 0x00
BSF STATUS, RP0 ; Banco 1
CLRF TRISB ; TRISB = 0: todo el puerto B como salida
BCF STATUS, RP0 ; Banco 0
CLRF Contador ; Contador = 0
Bucle:
MOVF Contador, W ; Copio Contador a W (MOVF mueve de registro a W)
MOVWF PORTB ; Lo saco por el puerto B: 8 LEDs muestran
; el valor en binario
INCF Contador, F ; Contador = Contador + 1 (INCrement File)
CALL Retardo ; Espero un poco para que se vea
GOTO Bucle ; Siguiente valor
Retardo:
MOVLW d'50' ; Bucle doble para alargar la espera
MOVWF GuardaRet
Bucle1:
MOVLW d'255'
MOVWF Contador ; Reutilizo Contador como variable interna
Bucle2:
DECFSZ Contador, F ; Bucle interno (255 vueltas)
GOTO Bucle2
DECFSZ GuardaRet, F ; Bucle externo (50 vueltas)
GOTO Bucle1
RETURN
ENDPoco a poco lo vamos complicando, ¿Que hace ahora nuestro código?: cuenta de 0 a 255 sin parar mostrando cada valor en binario sobre 8 LEDs conectados al puerto B. Fíjate en MOLF… perdón, en MOVF: mientras MOVLW carga una constante en W, MOVF copia el contenido de un registro (¡importante para leer variables!).
Ejemplo 5: Interrupción por cambio en RB0
LIST P=16F84A
#INCLUDE <P16F84A.INC>
__CONFIG _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
CBLOCK 0x0C
GuardaW ; Aquí guardaré W al entrar en la interrupción
GuardaSTATUS ; ...y aquí STATUS (¡imprescindible!)
ENDC
ORG 0x00 ; Vector de RESET: por aquí se empieza
GOTO Inicio ; Salto sobre el vector de interrupción
ORG 0x04 ; Vector de INTERRUPCIÓN: la CPU salta aquí
; automáticamente cuando ocurre
MOVWF GuardaW ; Salvo W (la interrupción puede llegar
; en cualquier momento del programa)
MOVF STATUS, W ; Copio STATUS a W...
MOVWF GuardaSTATUS ; ...y lo salvo también
BCF INTCON, INTF ; Borro el flag que avisó de la interrupción
; (si no, saltaría eternamente)
BTFSC PORTB, 0 ; ¿RB0 está a 0?
GOTO Fin ; No: no hago nada
BSF PORTB, 7 ; Sí: enciendo el LED de RB7
Fin:
MOVF GuardaSTATUS, W ; Restauro STATUS...
MOVWF STATUS
SWAPF GuardaW, F ; SWAPF intercambia nibbles sin tocar las
SWAPF GuardaW, W ; banderas: truco clásico para restaurar W
RETFIE ; Retorno de interrupción (RETurn From
; Interrupt and Enable)
Inicio:
BSF STATUS, RP0 ; --- Configuración ---
MOVLW b'00000001' ; RB0 entrada, resto del puerto B salida
MOVWF TRISB
BCF OPTION_REG, INTEDG ; Interrupción en flanco de bajada de RB0
BCF STATUS, RP0
BSF INTCON, INTE ; Activo la interrupción de RB0
BSF INTCON, GIE ; Activo las interrupciones en general
Bucle:
GOTO Bucle ; El programa "duerme": todo lo hace la ISR
ENDNuestro último programilla... el programa principal no hace nada; cuando el pin RB0 detecta un flanco de bajada (por ejemplo, un botón con lógica negativa), la CPU salta sola a la dirección 0x04, se ejecuta la rutina de interrupción y luego RETFIE devuelve el control. Observa el ritual de guardar W y STATUS al entrar y restaurarlos al salir: sin eso, la interrupción "corrompería" el programa principal. El truco de SWAPF dos veces se usa porque SWAPF no afecta a las banderas del registro STATUS.
Conclusiones
El ensamblador en los PIC no ha muerto: ha cambiado de papel. Ya no es la forma habitual de desarrollar productos, pero sigue siendo la mejor escuela posible para entender qué pasa de verdad dentro de un microcontrolador: registros, bancos de memoria, temporizaciones exactas e interrupciones sin capas de por medio. Si algún día programas en C un PIC (o cualquier otro micro), verás que lo que hace el compilador por debajo ya no será magia para ti… porque habrás escrito eso mismo a mano.
Tengo más códigos y ejemplos que puedo compartir, si necesitas mas ayuda no dudes en escribirme. Obviamente esto son ejemplos básicos, una vez que empezamos a utilizar bucles, interrupciones o "funciones" elaborar el código de control puede complicarse bastante.
He utilizado MPLAB X para crear los códigos y compilar, si necesitas ayuda para utilizarlo no dudes en escribirme también.
¡Espero que te sirva! ¡Un saludo!
lunes, 19 de octubre de 2026
Control de ventilación en un CPD con PIC16F876: el proyecto completo
Control de ventilación en un CPD con PIC16F876: el proyecto completo
El contexto: por qué un CPD necesita "aire a medida"
Un CPD (Centro de Proceso de Datos) es básicamente una sala llena de RACKs de servidores trabajando sin parar, y donde hay procesadores trabajando… hay calor. Mucho calor. Para mantenerlo a rayo se usan los CRAC (Computer Room Air Conditioner), unidades de aire acondicionado específicas para salas de servidores, que crean los clásicos pasillos fríos (por donde entra el aire que respiran los servidores) y pasillos calientes (por donde expulsan el aire calentado).
El proyecto parte de un problema real: los CRAC por sí solos no siempre consiguen llevar el aire frío hasta el final de los pasillos. La solución propuesta es añadir ventilación adicional dirigida mediante la incorporación de 2 CRAC, y aquí viene lo interesante: un ventilador bajo cada pasillo frío, instalado en el suelo técnico, que empuja el aire frío justo donde hace falta.
La lógica de control: la gráfica Temperatura / Velocidad
El ventilador no gira a una velocidad fija: su potencia se ajusta según la temperatura de los pasillos calientes adyacentes, de modo que su comportamiento se ajusta a la gráfica. Cada pasillo frío tiene dos pasillos calientes adyacentes, y cada pasillo caliente dispone de 2 sondas de temperatura. La temperatura a compensar se considera la media aritmética de las 4 medidas.
Esa media se traduce en velocidad de giro siguiendo una curva Temperatura/Velocidad (T/V):
El cerebro: un microcontrolador, no un PC
Para gobernar cada ventilador se propone un microcontrolador (un chip programable de toda la vida, sin sistema operativo ni nada parecido) capaz de capturar el valor de dichas temperaturas y generar una señal PWM que controle la velocidad de giro de su motor de corriente continua, siguiendo la curva T/V antes mencionada.
Requisitos funcionales del proyecto
Aquí va la lista oficial de requisitos, que es donde está la "chicha" técnica:
- Microcontrolador: se usará un PIC16F876 con cristal de 12 MHz.
- Alimentación: la tensión de alimentación del microcontrolador estará en el intervalo [4,5 V – 5,5 V].
- Sondas de temperatura: cada sonda entrega una tensión de salida en el intervalo [0 V, 4 V], directamente proporcional a la temperatura ambiente en [0 °C, 100 °C].
- Salida PWM: una vez calculada la media de las 4 temperaturas, se generará una señal PWM cuyo ciclo de trabajo (respecto al período de la señal) es igual al % de potencia obtenido en base a la curva T/V.
- Referencia del conversor AD: la tensión de referencia Vref = 4 V se consigue de la tensión de alimentación gracias a un circuito de adaptación. La tensión de referencia negativa será GND.
- Muestreo: las 4 señales analógicas de las sondas han de muestrearse cada 100 períodos PWM. Tanto la lectura de los canales analógicos como la cuenta del tiempo de muestreo se implementarán obligatoriamente mediante interrupciones.
- Generación PWM: la señal PWM la proporciona el PIC mediante uno de los módulos CCP en modo PWM. La frecuencia de la señal será, en kHz, el resultado de sumar 1 al último número de tu DNI (o documento equivalente), siempre que se garantice una resolución mínima de 10 bits en la señal PWM.
El "kit de parámetros" de un vistazo
| Parámetro | Valor |
|---|---|
| Microcontrolador | PIC16F876 |
| Cristal | 12 MHz |
| Alimentación | 4,5 – 5,5 V |
| Salida de sondas | 0 – 4 V (proporcional a 0 – 100 °C) |
| Vref (AD) | 4 V (Vref− = GND) |
| Muestreo | Cada 100 períodos PWM (obligatoriamente por interrupciones) |
| PWM | Módulo CCP en modo PWM, resolución mínima 10 bits |
| Frecuencia PWM | (última cifra del DNI + 1) kHz |
| Curva T/V | 10% hasta 20 °C · 30% a 40 °C · 60% a 60 °C · 100% a partir de 80 °C |
¿Por qué estos requisitos son tan interesantes?
Cada requisito esconde una decisión de diseño digna de análisis. El Vref de 4 V es una joya: coincide exactamente con el máximo de las sondas (0–4 V), de modo que el conversor A/D aprovecha todo su rango: 0 V → 0 °C y 4 V → 100 °C, sin desperdiciar ni un bit. El muestreo cada 100 períodos PWM obliga a "casar" dos tiempos de reloj distintos (y por eso exigen interrupciones, nada de bucles de espera improvisados). Y la condición de los 10 bits de resolución limita las frecuencias PWM elegibles: no vale cualquier kHz de tu DNI, solo los que el datasheet del PIC permita con esa resolución a 12 MHz.
___________________________________________________________________________________
¿Cómo lo haremos?
- Resumen y especificaciones solicitadas, así como soluciones y componentes a utilizar. Incluyendo cálculos.
- El diagrama de funcionamiento del sistema para implementar en el PIC.
- Cálculos previos a la programación (Fosc, TMR1, TMR2, TPWM) y cálculos adicionales para el funcionamiento.
- Simulación en Proteus
- Conclusiones finales.
1.
Resumen
de especificaciones solicitadas y consideraciones previas
a.
Resumen
de especificaciones y requisitos para tener en cuenta
·
Microcontrolador-> PIC16F876 con frecuencia
de cristal 12MHz
·
Tensión de alimentación del micro [4.5,5.5V]
·
Sonda de temperatura debe proporcionar una señal
de salida [0,4V], proporcional a [0ºC,100ºC].
·
Se calcula media de 4 sondas de temperatura, y
se generará salida PWM en función de la curva T/V para controlar los
ventiladores.
·
Frecuencia de PWM de 1KHz, según la condición
impuesta.
b.
¿Qué
elementos y componentes electrónicos usar?
Microcontrolador:
El microcontrolador a emplear será
el PIC16F876, este es un microcontrolador de 8 bits de la familia de Microchip
Technology, con un encapsulado de 28 pines.
Cuenta con arquitectura RISC y
memoria Flash reprogramable. Ofrece módulos periféricos integrados como ADC,
PWM, Timers (TMR0,TMR1 y TMR2), y comunicación serial (UART, SPI, I2C).
Es ampliamente utilizado en
aplicaciones embebidas de tamaño medio debido a su versatilidad, bajo costo y
facilidad de uso con compiladores. En este caso utilizaremos el software MPLAB
X IDE 5.35 y el compilador XC8 para la creación del algoritmo y simulación.
Además, como punto extra, he realizado un esquemático en PROTEUS 8.5 para realizar una simulación adicional más real que la que podemos hacer con MPLAB
Sensores de temperatura:
En el enunciado de la práctica se
indica que debemos utilizar 4 sensores de temperatura para la monitorización de
temperatura, sin dar más detalles.
Existen infinidad de sensores que
se podrían implementar, pero en mi caso en varios proyectos personales he
utilizado el clásico sensor de temperatura LM35, así que esa fue mi
elección.
Información de este sensor:
El sensor de temperatura LM35 es ligero,
fiable, compacto y sencillo de utilizar.
·
Encapsulado To92, con 3 pines Vcc(+5Vdc), GND y
Vout.
·
Vout, nos proporciona una salida de tensión
analógica, lineal con una pendiente de 10mV/ºC.
·
Rango de tensión de salida desde 0 Voltios(0ºC)
a 1.5 Voltios(150ºC).
·
Rango de medida de -55ºC a 150ºC.
·
Bajo consumo: inferior a 60uA
·
Alta precisión, ±0.5°C a 25°C.
Circuito de adaptación:
Como requisito, se pide que los
sensores de temperatura nos proporcionen una salida de 0-4 voltios. Como el
LM35 nos proporciona una tensión de salida de 0 a 1.5 voltios, es necesario
hacer un circuito de adaptación de niveles.
Mi idea, sería utilizar el
amplificador Operacional MCP6001 configurándolo como amplificador
no inversor.
Realizando unos sencillos
cálculos obtengo la ganancia que necesito y posteriormente los valores óptimos
de las resistencias para dicha configuración.
En las siguientes imágenes se
puede ver un ejemplo de la simulación en la adaptación de niveles.
La primera imagen, muestra como inyecto una tensión de
0Voltios obteniendo a la salida del Operacional 0 voltios.
En la segunda imagen, se establece la tensión máxima de
salida del sensor (1Voltio para 100ºC) y obtenemos una señal de salida de 4
voltios que conectaremos a nuestra entrada analógica del PIC.
Mi idea del Esquemático electrónico del sistema:
2. Diagrama de funcionamiento del sistema de refrigeración en el Micro
3. Cálculos realizados
previos a la programación
a. Tiempos y periodos a calcular
Frecuencia Oscilación = 12MHz
b.
Tiempos
y configuración de PWM.
Frecuencia PWM = 1KHz (Según la condición especificada)
Necesitamos comprobar que la frecuencia de la señal PWM
garantiza una resolución mínima de 10bits en la misma.
Tiempo para 10bits-> 1024 pasos-> 1024 x 0,333 us =
341,33 us
Se cumple 341,33us
< 1000us(1ms)
Es posible garantizar una resolución mínima de 10bits para
una señal PWM de 1KHz y un XT de 12MHz.
i.
Configuración
TMR2
El registro CCP1 para la generación de PWM, utiliza TMR2
para su temporización, por tanto, necesitaremos calcular el valor de PR2 en
función de los requisitos establecidos.
c.
Temporización
o muestreo
Según especificación, las 4 sondas de temperatura serán
muestreadas cada 100 periodos PWM, así que necesitaremos un modo de
temporización, para ello utilizaremos TMR1.
Como bien sabemos TMR1 en el PIC16F876 tiene una resolución de 16 bits, y visto que necesitamos una temporización elevada(100ms) necesitamos emplear un preescaler de 8.
d.
Cálculos
adicionales para el funcionamiento del algoritmo
Bien, en este momento, tenemos calculado y configurado: el
tiempo de muestreo o temporización, PWM&TMR2, configurado ADC.
Podríamos “tener preparado” la mayoría de los cálculos para hacer
la lectura de los voltios convertidos a bits que nos arrojan los sensores. Pero
necesitamos hacer una conversión de esos bits a una magnitud física que
entendamos.
Se nos ha facilitado la siguiente gráfica de conversión de
temperatura medida vs % de potencia de control para el ventilador.
Por tanto, para adaptar el modo de control a nuestro código
PRIMERO he optado por realizar 2 tablas de conversión.
Tabla 1:
Conversión grados-> Escala de resolución ADC(10bits)
|
Grados ºC |
Resolución(10bits) |
|
0 |
0 |
|
10 |
102 |
|
20 |
205 |
|
30 |
307 |
|
40 |
410 |
|
50 |
512 |
|
60 |
615 |
|
70 |
719 |
|
80 |
819 |
|
90 |
921 |
|
100 |
1024 |
Con esta tabla estoy haciendo una equivalencia del rango de
temperatura(0-100ºC) con el equivalente a la resolución de 10bits. Además,
sabemos que el sensor LM35 nos da una salida analógica de 10mv/ºC.
Tabla 2:
Registro una
comparación y equivalencia de grados-> % Pot. Ventilador
Así me iré aproximando a los valores que necesitaré para
escribir el DuttyC.
|
Grados ºC |
% |
Valor bits |
|
0 |
20 |
205 |
|
10 |
23 |
235 |
|
20 |
27 |
265 |
|
30 |
30 |
307 |
|
40 |
34 |
350 |
|
50 |
44 |
450 |
|
60 |
56 |
575 |
|
70 |
72 |
737 |
|
80 |
100 |
1023 |
|
>80 |
100 |
1023 |
Visto que tenemos una gráfica exponencial y debido a la
complejidad de insertar todos los puntos de la gráfica, opté por lo siguiente.
·
En primer lugar, pensé en dividir la temperatura
en tramos y aplicar una potencia fija al ventilador para todo ese rango. No
sería correcto, ya que, si la temperatura esta en el límite superior del tramo
y aplicamos una potencia al ventilador inferior a la necesitada, se produciría
un sobrecalentamiento y funcionamiento ineficiente. De igual manera, si
la temperatura está en el límite inferior y aplicamos una potencia de
ventilador máxima para ese rango, tendríamos un consumo en exceso de energía.
·
Solución aplicada.
o
He dividido la temperatura en segmentos,
apoyándome en la tabla 1.
§ 1º
Tramo Temperatura-> Para 0≤x≤20ºC
§ 2º
Tramo Temperatura-> Para 20≤x≤39ºC
§ 3º
Tramo Temperatura-> Para 39≤x≤53ºC
§ 4º
Tramo Temperatura-> Para 53≤x≤66ºC
§ 5º
Tramo Temperatura-> Para 66≤x≤72ºC
§ 6º
Tramo Temperatura-> Para 72≤x≤80ºC
§ 7º
Tramo Temperatura-> Para >80ºC
o
Para obtener la potencia del ventilador, he
realizado una función para cada intervalo mediante aproximación por segmentos
lineales. Apoyándome en la tabla 2 y los cálculos adicionales.
4. Funciones adicionales al programa
Terminado y comprobado el funcionamiento del programa, pensé
en posibles implementaciones adicionales.
o
Registro de Alarmas e indicación visual del
funcionamiento del sistema. Dependiendo de la temperatura leída, activaremos
unas determinadas salidas del puerto B.
o
Usamos la salida RB0 para una indicación visual
(led verde), del funcionamiento Óptimo del sistema.
o
La salida RB1, nos dará una salida óptima
adicional, mediante un LED Rojo. Nos Indicará un Warning de temperatura
elevada.
o
RB2, actuará también como salida y mediante un
Buzzer transistorizado, nos dará un aviso acústico, indicando TEMPERATURA MUY
ELEVADA.
o Comprobación de funcionamiento de las sondas parejas, realizando una comparación entre su medida. Si existe una disparidad elevada entre ellas, nos dará una alarma adicional de posible fallo de sonda, activando señal óptica+ acústica. (sin terminar de implementar)
5. Simulación en Proteus
Además de realizar las simulaciones del programa en MPLAB X, pensé usar Proteus para probar su funcionamiento, cargando el *.hex en el PIC virtual.
Nota: En estas simulaciones, apliqué una VREF de 1Voltio
en vez de 4V. Motivo… mi versión del software al intentar simular con los
operacionales me daba errores, así que opté por hacer la simulación con los
elementos justos.
Midiendo una temperatura de 20 grados en los 4 sensores.
Se puede ver la salida PWM en CCP1 capturada por el
analizador lógico, con un porcentaje de dutty en torno al 20%.
Medida de una temperatura de 75 grados en los 4 sensores.
Se puede ver la diferencia en la salida PWM en CCP1
respecto a la prueba en el primer rango de temperaturas.
Conclusiones:
Según las pruebas aplicadas en el funcionamiento, no he
encontrado fallos en el funcionamiento. Si he tenido fallos al simular en MPLAB X y Proteus en varias ocasiones. Entiendo que la gestión de las interrupciones
sobre la capa de Windows y el propio software no sean las condiciones más
favorables.
Quedaría pendiente implementar más funciones adicionales al
sistema para tener un sistema más completo, como la comprobación de sondas de
temperaturas defectuosas que mencioné anteriormente.
Incluso un sensor de corriente que leyera la corriente
consumida por el ventilador y si detectara un consumo bajo de corriente, el PIC
leyera una señal de entrada digital o analógica (del sensor) para registrar y
enviar un aviso de la avería de ventilador.
En caso de necesitar el código o el HEX, me podéis escribir sin problema.

























