Potencia 4: control de fuentes conmutadas

El objetivo del control de una fuente conmutada es mantener al mínimo la variación de tensión a la salida, siguiendo la referencia que le pongamos, a pesar de los cambios en la corriente necesaria, variaciones en la carga, o en la alimentación.

En primer lugar, veamos un esquema básico de un control de tensión:

control

En él podemos ver que la etapa de potencia contiene los parámetros tensión de entrada (Vi), tensión de salida (Vo) y se le añade la acción de control tras un acondicionador, no estrictamente necesario.

Una realimentación, que puede estar formada perfectamente por un divisor resistivo (con ganancia beta), lleva la tensión a un amplificador de error, que toma la referencia de tensión que nosotros elegimos y da a la salida una tensión que es comparada de nuevo con una señal en diente de sierra. Donde el error es mayor que el diente de sierra la salida del comparador es positiva, y por lo tanto activará el conmutador.

Éste es el control más simple y puede llevarse a cabo mediante un integrado tipo SG2524 o SG3524, que incorpora casi todo lo mencionado anteriormente. Aquí tenemos el circuito de una fuente tipo buck:

control2

Sin embargo, éste tipo de integrados no tienen en cuenta las verdaderas propiedades del sistema. Nosotros podemos diseñar un control a medida para el tipo que necesitemos, teniendo en cuenta sus puntos flojos, las inestabilidades, las variaciones mencionadas anteriormente, y podremos hacer que tenga una reacción más rápida y que siga al 100% la referencia, mediante el diseño de un regulador PID que sustituirá el amplificador de error.

Para proceder a diseñar un PID, necesitaremos la función de transferencia del sistema. Tomaremos como ejemplo una fuente tipo Buck. Podemos hallar un modelo en pequeña señal que deja el circuito de la siguiente forma:

control3

“Simplemente” hemos extraido el conmutador y se ha sustituido por un modelo lineal de su funcionamiento.

control4

control5

De ésta forma hemos llegado al circuito anteriormente visto. A partir de éste podremos sacar las funciones de transferencia, por ejemplo, de la tensión de salida respecto a delta cuando ningún otro parámetro está variando (las variaciones de tensión de entrada y carga se consideran nulas).

control6

También se podrán obtener la corriente por la bobina respecto de delta (Gid), la audiosusceptibilidad, que es la tensión de salida respecto a las variaciones de la tensión de entrada (A) y la impedancia de salida (Z)

Todas estas funciones de transferencia dan a conocer parámetros importantes de cada fuente, como son las frecuencias de sus polos y ceros naturales. Datos útiles para el diseño del control con un PID.

Para cada tipo de fuente conmutada la función de transferencia es distinta, por suerte, hace tiempo que las resumí en unas tablas, donde D es delta:

control7

control8

control9

Procedamos al diseño del regulador en modo tensión (solo necesitaremos Gvd). Lo haremos mediante el “método K” de Venable, para ello necesitaremos conocer ciertas condiciones del diseño:

  • Frecuencia de conmutación (Fs): frecuencia a la que se hará conmutar el dispositivo de conmutación, normalmente un transistor. Es un parámetro fijo.
  • Frecuencia de los polos (Fn): es la frecuencia natural del sistema, obtenida de las expresiones de las tablas anteriores (wn/(2*pi)), y cambia según la inductancia y el condensador que se le ha puesto al filtro.
  • Frecuencia de los ceros (Fz): al igual que en el caso anterior viene dado por los componentes. En éste caso lo más normal es que haya dos ceros de frecuencias distintas.
  • Frecuencia de cruce (Fc): es la frecuencia donde el bode de magnitud pasa por 0 dB. Si se le da un ancho de banda grande, tendremos una respuesta más rápida aunque se verá más afectada por los ruidos. Ponemos una frecuencia que estará entre:
    • 0.1*Fs < Fc < 0.2*Fs y que cumpla que Fc > 10*Fn para fuentes sin ceros en el semiplano derecho (Buck, Forward)
    • 3*Fn < Fc < 0.3*Fz (del cero en el semiplano derecho) para fuentes con ceros en el semiplano derecho (Boost, Buck-Boost, Flyback)
  • Margen de fase: la diferencia de desfase que hay entre el punto en el bode de fase a la frecuencia de cruce con -180º. El margen de fase ideal está entre 60 y 75 grados, pero en el peor de los casos hay que asegurar que sea mayor de 45 grados.
  • Modelo del modulador: el comparador que crea la señal PWM a partir de una triangular se modeliza teniendo en cuenta el valor máximo de la señal triangular (Vm), de forma que Fm = 1/Vm

Existen tres tipos de compensadores útiles para éste regulador, y todos se pueden construir mediante un amplificador operacional, resistencias y condensadores:

  • Tipo 1: es un simple integrador, su función de transferencia es Av:

control10

Éste tipo lo único de lo que se encarga es de amplificar cualquier error por pequeño que sea, de forma que se sigue siempre la referencia. Es para lo que se usan los integradores puros. Por lo general hace crecer demasiado la señal de error, por lo que son inestables. Lo único que podemos controlar será la frecuencia de cruce variando wp0c, el margen de fase será la fase obtenida en Gvd(Fc) – 90º + 180º. Por ello se suele utilizar cuando arg(Gvd(Fc)) (fase de Gvd a la frecuencia de cruce) es mayor de -30º.

Wp0c se calcula teniendo en cuenta que la función de transferencia en bucle abierto, a la frecuencia de cruce, debe tener una ganancia de 0  dB:

wp0c3

  • Tipo 2: tiene un integrador, un polo y un cero. Al contrario que en el caso anterior, podemos controlar el margen de fase, pero únicamente se usa con arg(Gvd(Fc)) entre -90 y -30º.

Para conocer la posición del polo y el cero usaremos un invento llamado Aumento de Fase (AUFA), que determinará un factor K que, dividido a la frecuencia de cruce, nos da la frecuencia exacta a la que se deben situar para obtener el margen de fase requerido. También se diseñará wp0c para que la frecuencia de cruce sea la deseada.

En primer lugar se calcula AUFA:

AUFAIntroducimos su valor en la ecuación del factor K:

factorK

Con  éste valor ya podemos calcular la frecuencia a la que se colocará el polo y el cero:

poloyceroo

También calculamos wp0c, teniendo en cuenta que el módulo de la función de transferencia en bucle abierto (Tv) debe ser 1 a la frecuencia de cruce que hemos elegido:

wp0c

Con ésto ya tenemos completa la función de transferencia del regulador:

reg2

Y podemos montarlo físicamente mediante el siguiente circuito:

reg32

Si se piensa, o se simula y se obtiene como resultado, que el tipo 1 es inestable, es aconsejable usar un tipo 2, que también será válido.

  • Tipo 3: éste contiene el integrador, dos polos y dos ceros. El método es igual que el tipo 2 pero el cálculo del factor K y las frecuencias cambia un poco. Se usa para cuando el arg(Gvd(Fc)) está entre -90 y -180:

AUFA2

frec

wp0c2

Con lo que obtenemos la función de transferencia:

func2

Y podemos implementarlo:

tipo3

Aunque diría que éste tipo no es tan estable (ni tan simple, lógicamente) como el tipo 2, sí que es bastante robusto. Lo mejor de éstos dos últimos tipos mostrados es que funcionan, los he comprobado tanto en simulación como en el laboratorio y realmente hacen su trabajo.

En un entorno de computación matemática como matlab, si tenéis la ocasión, podéis comprobar que al construir la función de transferencia en lazo abierto (Tv(s) = Gvd(s)*Fm*beta*Av(s)) y al hacer el bode de ésta, en la frecuencia de cruce diseñada hallaréis que se encuentra la ganancia 0 dB y el margen de fase deseado.

Si bien para fuentes conmutadas es más sencillo usar un integrado simplemente, en el caso de los inversores DC/AC sí que son realmente útiles.

También se puede usar un sistema mixto integrado/regulador en el que el regulador se implementa externamente y el integrado crea una referencia y una modulación por señal triangular con mucha precisión.

Técnicas de control 2: discretización de un regulador

Para discretizar un proceso se usa un ZOH o un FOH, pero para un regulador hay muchos métodos, todos válidos, unos mejores que otros. La expresióndiscretizada de un regulador es lo que introducimos en un ordenador que hace de regulador.

En primer lugar, se diseña el regulador en el dominio de Laplace como ya se vio en automática. En segundo, se aplica uno de los siguientes métodos, basados en la integración numérica:

  • Rectángulo anterior: como un ZOH,

Sustituimos s por:

rectanterior

  • Rectangulo posterior: también parecido al ZOH

rectangposterior

  • Bilineal: más similar a un FOH, mayor calidad, mayor complejidad

bilineal

  • Mantenimiento de la respuesta: aquí lo que se busca es conseguir una respuesta similar a la del sistema continuo (no discretizado), y depende de la entrada:

manten

  • Emparejamiento de polos y ceros: se hace corresponder los polos y ceros de la función continua con los de la función discreta.

empar

El lugar de las raíces al aplicar uno de éstos métodos cambia. Antes teníamos dos planos, real e imaginario, positivo y negativo, entre los cuales diferenciábamos bien cual es la zona de inestabilidad (el plano positivo). Ahora también tenemos éstos, pero la zona de estabilidad se define por una circunferencia centrada en 0 y de radio 1. El método que usemos afectará a los polos y ceros llevándolos dentro o fuera de ésta:

  • Rectángulo anterior: algún regulador analógico estable puede hacerse inestable.
  • Rectángulo posterior: todos los reguladores analógicos estables son discretos estables, e incluso puede haber algún analógico inestable que se estabilice.
  • Bilineal: todos los reguladores analógicos estables son discretos estables.

Automática 3: más reguladores

Hasta ahora hemos visto que todo es muy bonito, que el punto de diseño que queríamos nos ha coincidido con el lugar de las raíces y que con ésto hemos sacado un regulador P, que es suficiente para darnos por satisfechos.

Sin embargo, ¿qué ocurre cuando no coincide el lugar de las raíces? Forzamos a cambiar al LDR hasta que pasa por el punto que queremos, añadiendo un derivador (cero). Qué mejor manera de saber en qué punto poner el cero que con el criterio del argumento.

Regulador PD

Pongamos por ejemplo el último sistema que hemos tomado, su lugar de las raíces va a ser el que sigue y queremos que el tiempo sea menor a 1 segundo y su sobreoscilación menor al 5%.

bien

En ningún momento pasa el LDR por la zona válida de especificaciones (la blanca). Calculamos un punto de diseño que va a ser s = -4+4i (en realidad es +4.19i pero podemos aproximar con total tranquilidad de conciencia, ya que al añadir un cero aumentaremos la sobreoscilación).

A continuación, vamos a ver dónde colocar el cero, calculamos los ángulos con los polos que tenemos: a1=104.0362º, a2=126.8698º.

Hacemos la suma y resta: 104.0362 + 126.8698 – z = 180. Despejamos z y obtenemos: az = 50.906º. De la misma forma que hemos calculado el ángulo de los polos dada su posición, calculamos la posición del cero: tg(angulo) = 4/dz -> dz=3.25 (distancia al punto, se le suma la parte real del punto para saber la distancia al 0 del plano) -> dz=7.25. Colocamos el cero y cambia el LDR:

cero2

Ahora calculamos, mediante el criterio del módulo, la ganancia del regulador, que es aproximadamente 29.

Expresamos el cero del derivador como un tiempo de derivación: Td = 1/z = 1/7.25 = 0.138

Y expresamos la función de transferencia del regulador: Gr = 29*(1+0.138*z).

La respuesta que obtenemos con éste regulador es:

cero3

Como ya estaba predicho, la sobreoscilación es mayor a la que habíamos pensado porque hemos incluido un cero.

Otra de las cosas que nos preocupan de éste sistema es que, como se ve en la figura, se establece en 0.9. Ésto es el error de posición, que como ya expliqué anteriormente, se soluciona con un integrador, a riesgo de inestabilizar el sistema.

Regulador PI-PID

En caso de no necesitar cambiar el LDR pero sí hacer el error de posición 0, usaríamos un regulador PI, nos olvidamos de poner un derivador. En éste caso, hemos necesitado un PID para cambiar el LDR. Simplemente, una vez puesto el cero del derivador en su sitio, incluimos un polo en el punto 0.

inte

Como era de esperar, un integrador “tal cual” nos ha inestabilizado el sistema. Para volver a estabilizarlo, añadimos otro cero, que llamamos cero del integrador, cuya posición es parte real del punto de diseño dividido por 10:

inte2

El programa considera que la pareja polo-cero del integrador afectan al LDR y a la ganancia y cambia. Por lo general nos podemos quedar con la que nos da nada más calcular el derivador, ya que la pareja está “muy próxima” entre sí. Sin embargo, también se puede calcular la nueva ganancia, que daría aproximadamente 12.5. Entonces, expresamos el integrador como un tiempo de integración que es Ti = 1/zi = 1/0.4 = 2.5. Expresamos el regulador completo como:

Gr = 12.5*(1+0.138*s)*(1+1/(2.5*s))

Su respuesta en éste caso ha mejorado mucho en sobreoscilación, pero el tiempo de establecimiento se ha hecho mucho mayor, saliéndose de las especificaciones:

inte3

Para arreglar el “estropicio”, lo que haremos será un “ajuste fino” (vamos a ir probando) hasta obtener una relación de sobreoscilación-tiempo de establecimiento que nos convenza (más bien al comprador).

¿Cómo implemento un PID?

“Antiguamente” (hace 2 días porque ésto ha evolucionado mucho), se usaban métodos analógicos para hacer un regulador. Con métodos analógicos me refiero a un circuito (caso regulador electrónico) con amplificadores operacionales que hacen de amplificador (ganancia), integrador y derivador.

Hoy en día, usamos ordenadores con mucha capacidad que muestrean las señales y hacen salir otra de acuerdo a los parámetros que nosotros le configuremos. Además, hay varias formas de expresar éstos parámetros: la forma de cálculo, que es la que he puesto anteriormente, y la forma comercial, que se calcula mediante ésta tabla:

tabla

En industria se usan aparatos caros como éste:

Y ésto es todo lo básico de lo básico de automática de 2º. Ya sabemos diseñar un regulador, ahora queremos saber cómo implementarlo en un ordenador y que sea funcional y eficiente: lo llaman técnicas de control.

Cosas “chulas” que podemos llegar a hacer con un PID: control de posición de un helicóptero.

Escritura en LCD

Continuamos con el “cursillo” de PIC en C con algo que he conseguido simular hoy. Tan simple como utilizar funciones predefinidas en una librería y tan importante como otra nueva salida al exterior de la información que maneja nuestro PIC. Se trata de escribir en una LCD un par de frases y un relojito. Algo tal que así aunque luego me ocuparé de mejorarlo:

lcd

 

Como no se ve muy bien, he conectado RB0 al pin Enable (6), RB1 a RS(4) y RB2 a RW(5).

La configuración de la alimentación de la LCD es la siguiente: alimentamos directamente VDD y ponemos VSS a tierra. VEE es el control del contraste de nuestra pantalla (que en simulación no funciona). Tomamos de VDD a un potenciómetro (normalmente de 10K Ohm) y conectamos el pin intermedio (el variable) a VEE. El otro extremo del potenciómetro va a tierra.

En programación: hay que declarar qué puerto usar antes de añadir la librería, en éste caso he utilizado la lcd.c que viene por defecto en CCS, aunque existen por internet versiones flex_lcd.c en las que defines los pines uno a uno. Allá vamos. También hay dos formas de controlar el LCD: por 8 o 4 bits. He utilizado la configuración de 8. La LCD es de 2×16. 16 caracteres y 2 líneas.

#include <16f84a.h>
#use delay(clock=4000000)
#fuses NOWDT,NOPROTECT
#define LCD_DATA_PORT getenv("SFR:PORTB") //Se define el puerto B como datos
#include <lcd.c> //incluimos la librería por defecto
int i; //variable que utilizaremos para contar segundos
void main(){
 lcd_init(); //Éste comando inicializa la LCD automáticamente
 delay_ms(20); //*1
 i=0; //inicializamos variable a 0
 while(1){
 lcd_putc("\fHola mundo"); //limpia la pantalla y escribe Hola mundo
 lcd_gotoxy(1,2); //baja a primer caracter segunda linea
 printf(lcd_putc,"By jmth %ds",i); //imprime By jmth y la variable entera i
 delay_ms(1000); //retardo de 1s
 i++; //subimos 1 a la var
 if(i>60) i=0; //si pasa de 1 min que vuelva a 0
 }
}
//*1: En usos reales se deberá hacer un pequeño delay entre cada instrucción
// debido a lo que tarda el controlador de LCD en procesar cada una

También cabe añadir las otras 2 funciones de putc o printf, \b, que retrocede un caracter, y \n, que pasa a la siguiente línea. Si no tenéis la librería lcd.c aquí la dejo:

///////////////////////////////////////////////////////////////////////////////
//// LCD.C ////
//// Driver for common LCD modules ////
//// ////
//// lcd_init() Must be called before any other function. ////
//// ////
//// lcd_putc(c) Will display c on the next position of the LCD. ////
//// The following have special meaning: ////
//// \f Clear display ////
//// \n Go to start of second line ////
//// \b Move back one position ////
//// ////
//// lcd_gotoxy(x,y) Set write position on LCD (upper left is 1,1) ////
//// ////
//// lcd_getc(x,y) Returns character at position x,y on LCD ////
//// ////
//// CONFIGURATION ////
//// The LCD can be configured in one of two ways: a.) port access or ////
//// b.) pin access. Port access requires the entire 7 bit interface ////
//// connected to one GPIO port, and the data bits (D4:D7 of the LCD) ////
//// connected to sequential pins on the GPIO port. Pin access ////
//// has no requirements, all 7 bits of the control interface can ////
//// can be connected to any GPIO using several ports. ////
//// ////
//// To use port access, #define LCD_DATA_PORT to the SFR location of ////
//// of the GPIO port that holds the interface, -AND- edit LCD_PIN_MAP ////
//// of this file to configure the pin order. If you are using a ////
//// baseline PIC (PCB), then LCD_OUTPUT_MAP and LCD_INPUT_MAP also must ////
//// be defined. ////
//// ////
//// Example of port access: ////
//// #define LCD_DATA_PORT getenv("SFR:PORTD") ////
//// ////
//// To use pin access, the following pins must be defined: ////
//// LCD_ENABLE_PIN ////
//// LCD_RS_PIN ////
//// LCD_RW_PIN ////
//// LCD_DATA0 ////
//// LCD_DATA1 ////
//// LCD_DATA2 ////
//// LCD_DATA3 ////
//// LCD_DATA4 ////
//// ////
//// Example of pin access: ////
//// #define LCD_ENABLE_PIN PIN_E0 ////
//// #define LCD_RS_PIN PIN_E1 ////
//// #define LCD_RW_PIN PIN_E2 ////
//// #define LCD_DATA0 PIN_D4 ////
//// #define LCD_DATA1 PIN_D5 ////
//// #define LCD_DATA2 PIN_D6 ////
//// #define LCD_DATA3 PIN_D7 ////
//// ////
///////////////////////////////////////////////////////////////////////////////
//// (C) Copyright 1996,2009 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS C ////
//// compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, reproduction ////
//// or distribution is permitted without written permission. ////
//// Derivative programs created using this software in object code ////
//// form are not restricted in any way. ////
///////////////////////////////////////////////////////////////////////////
typedef struct 
{ // This structure is overlayed
 BOOLEAN enable; // on to an I/O port to gain
 BOOLEAN rs; // access to the LCD pins.
 BOOLEAN rw; // The bits are allocated from
 BOOLEAN unused; // low order up. ENABLE will
 int data : 4; // be LSB pin of that port.
 #if defined(__PCD__) // The port used will be LCD_DATA_PORT.
 int reserved: 8;
 #endif
} LCD_PIN_MAP;
#if defined(__PCB__)
 // these definitions only need to be modified for baseline PICs.
 // all other PICs use LCD_PIN_MAP or individual LCD_xxx pin definitions.
/* EN, RS, RW, UNUSED, DATA */
 const LCD_PIN_MAP LCD_OUTPUT_MAP = {0, 0, 0, 0, 0};
 const LCD_PIN_MAP LCD_INPUT_MAP = {0, 0, 0, 0, 0xF};
#endif
#ifndef LCD_ENABLE_PIN
 #define lcd_output_enable(x) lcdlat.enable=x
 #define lcd_enable_tris() lcdtris.enable=0
#else
 #define lcd_output_enable(x) output_bit(LCD_ENABLE_PIN, x)
 #define lcd_enable_tris() output_drive(LCD_ENABLE_PIN)
#endif
#ifndef LCD_RS_PIN
 #define lcd_output_rs(x) lcdlat.rs=x
 #define lcd_rs_tris() lcdtris.rs=0
#else
 #define lcd_output_rs(x) output_bit(LCD_RS_PIN, x)
 #define lcd_rs_tris() output_drive(LCD_RS_PIN)
#endif
#ifndef LCD_RW_PIN
 #define lcd_output_rw(x) lcdlat.rw=x
 #define lcd_rw_tris() lcdtris.rw=0
#else
 #define lcd_output_rw(x) output_bit(LCD_RW_PIN, x)
 #define lcd_rw_tris() output_drive(LCD_RW_PIN)
#endif
#ifndef LCD_DATA_PORT
 #if defined(__PCB__)
 #define LCD_DATA_PORT 0x06 //portb
 #define set_tris_lcd(x) set_tris_b(x)
 #elif defined(__PCM__)
 #define LCD_DATA_PORT getenv("SFR:PORTD") //portd
 #elif defined(__PCH__)
 #define LCD_DATA_PORT getenv("SFR:PORTD") //portd
 #elif defined(__PCD__)
 #define LCD_DATA_PORT getenv("SFR:PORTD") //portd
 #endif 
#endif
#if defined(__PCB__)
 LCD_PIN_MAP lcd, lcdlat;
 #byte lcd = LCD_DATA_PORT
 #byte lcdlat = LCD_DATA_PORT
#elif defined(__PCM__)
 LCD_PIN_MAP lcd, lcdlat, lcdtris;
 #byte lcd = LCD_DATA_PORT
 #byte lcdlat = LCD_DATA_PORT
 #byte lcdtris = LCD_DATA_PORT+0x80
#elif defined(__PCH__)
 LCD_PIN_MAP lcd, lcdlat, lcdtris;
 #byte lcd = LCD_DATA_PORT
 #byte lcdlat = LCD_DATA_PORT+9
 #byte lcdtris = LCD_DATA_PORT+0x12
#elif defined(__PCD__)
 LCD_PIN_MAP lcd, lcdlat, lcdtris;
 #word lcd = LCD_DATA_PORT
 #word lcdlat = LCD_DATA_PORT+2
 #word lcdtris = LCD_DATA_PORT-0x02
#endif
#ifndef LCD_TYPE
 #define LCD_TYPE 2 // 0=5x7, 1=5x10, 2=2 lines
#endif
#ifndef LCD_LINE_TWO
 #define LCD_LINE_TWO 0x40 // LCD RAM address for the second line
#endif
BYTE const LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xc, 1, 6};
 // These bytes need to be sent to the LCD
 // to start it up.

BYTE lcd_read_nibble(void);
BYTE lcd_read_byte(void)
{
 BYTE low,high;
#if defined(__PCB__)
 set_tris_lcd(LCD_INPUT_MAP);
 #else
 #if (defined(LCD_DATA0) && defined(LCD_DATA1) && defined(LCD_DATA2) && defined(LCD_DATA3))
 output_float(LCD_DATA0);
 output_float(LCD_DATA1);
 output_float(LCD_DATA2);
 output_float(LCD_DATA3);
 #else
 lcdtris.data = 0xF;
 #endif
 #endif
 
 lcd_output_rw(1);
 delay_cycles(1);
 lcd_output_enable(1);
 delay_cycles(1);
 high = lcd_read_nibble();
 
 lcd_output_enable(0);
 delay_cycles(1);
 lcd_output_enable(1);
 delay_us(1);
 low = lcd_read_nibble();
 
 lcd_output_enable(0);
#if defined(__PCB__)
 set_tris_lcd(LCD_INPUT_MAP);
 #else
 #if (defined(LCD_DATA0) && defined(LCD_DATA1) && defined(LCD_DATA2) && defined(LCD_DATA3))
 output_drive(LCD_DATA0);
 output_drive(LCD_DATA1);
 output_drive(LCD_DATA2);
 output_drive(LCD_DATA3);
 #else
 lcdtris.data = 0x0;
 #endif
 #endif
return( (high<<4) | low);
}
BYTE lcd_read_nibble(void)
{
 #if (defined(LCD_DATA0) && defined(LCD_DATA1) && defined(LCD_DATA2) && defined(LCD_DATA3))
 BYTE n = 0x00;
/* Read the data port */
 n |= input(LCD_DATA0);
 n |= input(LCD_DATA1) << 1;
 n |= input(LCD_DATA2) << 2;
 n |= input(LCD_DATA3) << 3;
 
 return(n);
 #else
 return(lcd.data);
 #endif
}
void lcd_send_nibble(BYTE n)
{
 #if (defined(LCD_DATA0) && defined(LCD_DATA1) && defined(LCD_DATA2) && defined(LCD_DATA3))
 /* Write to the data port */
 output_bit(LCD_DATA0, BIT_TEST(n, 0));
 output_bit(LCD_DATA1, BIT_TEST(n, 1));
 output_bit(LCD_DATA2, BIT_TEST(n, 2));
 output_bit(LCD_DATA3, BIT_TEST(n, 3));
 #else 
 lcdlat.data = n;
 #endif
 
 delay_cycles(1);
 lcd_output_enable(1);
 delay_us(2);
 lcd_output_enable(0);
}
void lcd_send_byte(BYTE address, BYTE n)
{
 lcd_output_rs(0);
 while ( bit_test(lcd_read_byte(),7) ) ;
 lcd_output_rs(address);
 delay_cycles(1);
 lcd_output_rw(0);
 delay_cycles(1);
 lcd_output_enable(0);
 lcd_send_nibble(n >> 4);
 lcd_send_nibble(n & 0xf);
}
void lcd_init(void) 
{
 BYTE i;
#if defined(__PCB__)
 set_tris_lcd(LCD_OUTPUT_MAP);
 #else
 #if (defined(LCD_DATA0) && defined(LCD_DATA1) && defined(LCD_DATA2) && defined(LCD_DATA3))
 output_drive(LCD_DATA0);
 output_drive(LCD_DATA1);
 output_drive(LCD_DATA2);
 output_drive(LCD_DATA3);
 #else
 lcdtris.data = 0x0;
 #endif
 lcd_enable_tris();
 lcd_rs_tris();
 lcd_rw_tris();
 #endif
lcd_output_rs(0);
 lcd_output_rw(0);
 lcd_output_enable(0);
 
 delay_ms(15);
 for(i=1;i<=3;++i)
 {
 lcd_send_nibble(3);
 delay_ms(5);
 }
 
 lcd_send_nibble(2);
 for(i=0;i<=3;++i)
 lcd_send_byte(0,LCD_INIT_STRING[i]);
}
void lcd_gotoxy(BYTE x, BYTE y)
{
 BYTE address;
if(y!=1)
 address=LCD_LINE_TWO;
 else
 address=0;
 
 address+=x-1;
 lcd_send_byte(0,0x80|address);
}
void lcd_putc(char c)
{
 switch (c)
 {
 case '\f' : lcd_send_byte(0,1);
 delay_ms(2);
 break;
 
 case '\n' : lcd_gotoxy(1,2); break;
 
 case '\b' : lcd_send_byte(0,0x10); break;
 
 default : lcd_send_byte(1,c); break;
 }
}
 
char lcd_getc(BYTE x, BYTE y)
{
 char value;
lcd_gotoxy(x,y);
 while ( bit_test(lcd_read_byte(),7) ); // wait until busy flag is low
 lcd_output_rs(1);
 value = lcd_read_byte();
 lcd_output_rs(0);
 
 return(value);
}

Nueva categoría empresa

Con razón del próximo examen de dicha asignatura me voy a currar unos PDF de la teoría. Es una asignatura mayoritariamente teórica sin demasiados puntos prácticos, sin embargo, es necesario saber de qué va, por éso he pensado que quizá a bastante gente le vendría bien tener apuntes.

No os extrañe si empiezo a subir en orden inverso porque voy a empezar por lo sencillo. No avisaré cuando suba cada uno de los archivos ya que lo haré directamente como link en el menú.

¡Saludos!