lunes, 14 de noviembre de 2050

Inicio

Bienvenido al Blog Todoelectrodo!

Este Blog se creo con la finalidad de compartir mis conocimientos y experiencia en electrónica. Espero que encuentres aquí la ayuda que necesites y si no es así, escríbeme y veremos que se puede hacer.

Un saludo!





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 Contador con CBLOCK, 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:

  1. 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.

  2. 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.

En la siguiente tabla se puede ver el resumen de los registros de función especial(SFR) del PIC16F84A(extraído del datasheet)



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, W guarda 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).


En la siguiente imagen se puede ver un ejemplo de una práctica que tuve que realizar empleando MPLAB X(uso V5.35 por estabilidad en mi pc)



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 fuente

Qué 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

    END

Poco 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

    END

Nuestro ú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


Este es un proyecto que hice en la Universidad en una de las asignaturas. La verdad que me pareció muy interesante, pues combinada múltiples partes. Planificación de funcionamiento, diseño, programación y simulación.
He decidido compartir todo mi desarrollo para poder echar un cable a electrónicos que lo necesiten, así que aquí os lo dejo.
En la primera parte comenzaremos con el enunciado del programa y posteriormente el desarrollo del proyecto.

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:

  1. Microcontrolador: se usará un PIC16F876 con cristal de 12 MHz.
  2. Alimentación: la tensión de alimentación del microcontrolador estará en el intervalo [4,5 V – 5,5 V].
  3. 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].
  4. 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.
  5. 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.
  6. 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.
  7. 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?

Obviamente solo me pidieron el desarrollo del proyecto "sobre papel", cómo se podría implementar,  cálculos e ideas de mejora. 
Yo le quise dar un extra, así que hice los esquemáticos en Proteus, y simulación del código del programa. Así como las comprobaciones con el osciloscopio virtual del simulador.

Así que dividí el proyecto en las siguientes fases:
  1. Resumen y especificaciones solicitadas, así como soluciones y componentes a utilizar. Incluyendo cálculos.
  2. El diagrama de funcionamiento del sistema para implementar en el PIC.
  3. Cálculos previos a la programación (Fosc, TMR1, TMR2, TPWM) y cálculos adicionales para el funcionamiento.
  4. Simulación en Proteus
  5. 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.

Tramo 1 Temperatura(20 a 25ºC) 





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.

 

 *Bueno como siempre me despido de esta entrada y espero que os haya servido de ayuda. En caso de algún error o ideas de mejora siempre son recibidas para podernos ayudar entre todos.

En caso de necesitar el código o el HEX, me podéis escribir sin problema.