Analógica 5: amplificador con transistores

Aquí sólo vamos a tratar con transistores BJT y JFET. ¿Sólo? El BJT va a tener algo más de miga que el JFET, pero más o menos todo funciona igual.

¿Por qué nos interesa amplificar con transistores? Pues porque mientras los amplificadores operacionales están bastante limitados en el rango de tensiones, entrega de corriente y frecuencia, un transistor puede emplearse con mucha potencia y a frecuencias de GHz (no todos, claro está)

BJT

Lo primero que hay que tener claro es la polarización en DC. Si no sabes cómo se polariza un transistor, dirígete al apartado Teoría, Transistores, BJT, ahí está explicado (o se intenta). Necesitarás saber también sobre JFET cuando llegue el momento.

El caso es que ahora iB = Ib (polarización DC) + ib (señal que amplificamos)

Lo que buscaremos será polarizar en DC el transistor más o menos a la mitad de la recta de carga, para que al introducir la señal se pueda amplificar en su totalidad sin que haya cortes a la salida por entrar en zona de saturación y/o corte. Gráficamente:

polarizacion

Como se ve, se polariza el transistor en una zona intermedia de forma que al introducir una alterna a la base, la corriente de colector también varíe como ésa alterna, produciendo una variación en la Vce. De ésta forma obtenemos a la salida la señal amplificada (multiplicada por una ganancia) más un nivel de continua, que se puede eliminar con un condensador de desacoplo.

Como ya dije en la introducción a transistores, es importante que nuesto circuito de polarización sea estable, pues la variación de B no debería afectarnos demasiado. Ésto da lugar a circuitos de este tipo:

No os asustéis por el condensador en el emisor (C2), en realidad es un tipo majo. Lo veremos enseguida.

Ya sabemos cómo polarizar un transistor en continua. Supongamos que sabéis, porque deberíais. ¿Qué hacemos ahora? Lo siguiente es un análisis de pequeña señal en frecuencias medias, y obtener el cuadripolo equivalente.

He dicho que C2 es un tipo majo, y no sin razón, porque a frecuencias medias se comporta como un cortocircuito. ¿Y qué ocurre con el cortocircuito? Que Re también se va. Y éso es una resistencia menos para el cálculo. Lo mismo pasa con C1 pero no afecta demasiado. Ahora lo que hacemos es hallar los parámetros híbridos del cuadripolo equivalente (hacemos una linealización).

hie = Vbe/ib cuando Vce=cte. También lo calculamos como hie = Vt/ib(Q), Vt es una constante que suele dar el fabricante, e ib(Q) es la corriente de base en la polarización continua. El resultado es una resistencia en ohmios.

hfe = ic/ib que es la ganancia en alterna, lo suele dar el fabricante.

Con ésto, el cuadripolo equivalente del transistor es éste:

transeqPero, como podréis deducir, el transistor no se vale de sí mismo para amplificar, hay que conectar lo que tiene a su alrededor para hacer un cuadripolo del sistema completo, que de verdad nos ayude a determinar las características. Aquí es donde pondréis a juego vuestros conocimientos electrónicos, eléctricos y matemáticos. Si seguimos con el ejemplo del circuito de polarización de 4 resistencias, y nos ponemos a analizar, lo primero será cortocircuitar la fuente de continua, de forma que R1 y R2 se quedan en paralelo y en paralelo con hie. Rc también queda entre el colector y tierra. Re ya hemos dicho antes que no juega con nosotros, así que el emisor queda puesto a tierra directamente.

transeq2

La primera reacción es hacer el equivalente entre Rbb y hie, que sería hie//(R1//R2), pues muy bien. Se hace y se queda R3 para futuras referencias.

Los parámetros que hay que calcular serán:

  • Impedancia de entrada: puesta de fuente a la entrada (como la que ya está), cálculo de la corriente que drena y Ze = V/I
  • Impedancia de salida: abrimos la fuente de corriente, puesta de fuente a al salida, cálculo de la corriente que drena y Zs =V/I
  • Ganancia: para ello necesitas Ve y Vs. Ponemos una  fuente de tensión Ve a la entrada, calculamos ib, con ib calculada, sale el producto hfe*ib, una vez sabes la corriente que recorre ésa malla, puedes calcular la tensión de salida Vs.

Cuando no tengamos C2 éste proceso va a ser un poco más complejo, porque Re afectará en prácticamente todos los valores del cuadripolo.

Por cierto, si nos damos cuenta la fuente de corriente está apuntando hacia abajo, no problem, lo único que indica es que el amplificador es inversor, ya que la ganancia en tensión será negativa. Ya que tenemos todos los parámetros, vamos a hacer el cuadripolo equivalente final:

transeq3

¡Qué bonito! Pero no nos vamos a olvidar de las características de un sistema real, porque nos hemos olvidado de nuestros amigos los condensadores. Lo que hay que haces es un análisis en frecuencias bajas, para determinar la frecuencia de corte inferior del amplificador. En el caso de C1 no pasa nada, porque si nos fijamos está fuera del cuadripolo, lo ponemos y se calcula la nueva ganancia Gfb, con su frecuencia de corte. Con C2 ya está más complicado, porque habrá que tener en cuenta el efecto de Re y C2 al a vez, ya que éstos se encontrarían dentro del cuadripolo. El proceso es el mismo. Y si hubiera un condensador a la salida, al igual que con C1 no alteramos nada, pero habría que poner una carga para ver realmente su efecto.

Veremos que con el condensador C2 puesto, la ganancia aumenta considerablemente.

Por último, una vez obtenidas las frecuencias de corte inferiores, la frecuencia de corte inferior del sistema es la mayor de entre las que hemos obtenido.

Y ahora van las buenas noticias. Para el JFET la cosa funciona exactamente igual, la diferencia es que no hay una hie, por el tema de que la impedancia de puerta se puede considerar infinita, así que la consideramos un circuito abierto, y que el producto de su fuente de corriente dependiente es gm*Vgs, y gm:

gm = 2*(Idss/Vp) *(1+(Vgs(Q)/Vp))

¿A que es genial?

Por último, añadir que los transistores no son todopoderosos a alta frecuencia, sino que se crean capacidades entre sus terminales que afectan a la ganancia. Pero lo dicho, alta, muy alta frecuencia.

 

 

Analógica 1: introducción

En ésta asignatura se aborda el diseño de un sistema de procesado capaz de resolver el problema que se nos plantea mediante las especificaciones que se piden. La forma que usamos para abordar el problema es el diseño top-down:

  • Tenemos un problema del que extraemos las especificaciones del proyecto
  • Se diseña el sistema
  • ¿Cumple éste con las especificaciones?
  • ¿Resuelve el problema?

Para ello se lleva a cabo el análisis y diseño en distintas etapas, que aquí son un tema, o cada uno de los artículos de éste apartado, como se quiera ver.

En primer lugar se analiza la entrada (Analógica 2)

  • Equivalente de Thevenin o Norton (visto en electricidad)
  • Caracterización, catalogación y topología
  • Nivel, rango ancho de banda, impedancia de salida

El segundo paso es definir unas características del sistema de procesado (Analógica 3)

  • Función de transferencia
  • Impedancia de entrada, adaptación
  • Cuadripolo equivalente
  • Errores, modelización de comportamiento real

En tercer lugar, definimos unos bloques funcionales (Analógica 4)

  • Amplificador o atenuador
  • Sumador o restador
  • Convertidores
  • Filtraje en frecuencias

Ésto nos guiará en el siguiente paso, el diseño de los bloques (Analógica 5)

  • Componentes pasivos
  • Amplificadores operacionales
  • Transistores BJT, JFET…

Y de momento  hasta aquí vamos a llegar. Ahora enseguida va el capítulo 2.

Tr. FET: MOSFET

Éstos se llaman Metal-Oxide-Semiconductor Field Effect Transistor, que sería algo así como “transistores de efecto de campo de metal óxido-semiconductor”.

Son unos hijos de … … … y se estropean de sólo tocarlos. Sí, de sólo tocarlos y sin exagerar, ya que son sensibles a la electricidad estática que podemos tener en nuestra mano. Para ello utilizamos pulseras antiestáticas o simplemente rezamos.

Y resulta que hay 2 tipos de MOSFET, y 2 subtipos por tipo. Vamos con el primero y más sencillo

MOSFET de deplexión o empobrecimiento

Lejos de ser problemáticos, éstos transistores se asemejan a los JFET, de hecho funcionan igual y su única diferencia es que se admite una región en la que Ids es mayor que Idss. Ésto se daría cuando en un canal N la tensión Vgs es positiva, de forma que el signo menos en la ecuación pasa a ser un más. El problema es que el aumento en la corriente es mucho mayor con menor diferencia de tensión, y puede llegar a explotar. Y sí, tenemos los subtipos canal N y canal P. Fácil ¿eh?

Utilizamos las mismas fórmulas y expresiones que en los transistores JFET, así que lo mejor es dirigirse a ése apartado y leer sobre ellos si no se ha hecho ya.

Sus símbolos son:

Nada más que añadir, señoría.

MOSFET de acumulación o enriquecimiento

Éstos tienen curvas y funcionamientos similares a las de un BJT. Tenemos un valor mínimo de Vgs a partir del cual la Ids va creciendo, llamado Thresold Voltage, Vt. El canal N necesitará tensiones Vgs positivas y el canal P Vgs negativas. Lógico y normal como debe ser.

Como la curva ya no es la misma a la de los JFET o MOSFET de empobrecimiento, necesitamos otra ecuación.

Ids = K*(Vgs – Vt)^2

¿Y qué es K? Es un parámetro que nos da el fabricante, en realidad un punto de la curva en la gráfica determinado experimentalmente.

K = Ids(on)/(Vgs(on) – Vt)^2

También trabajan en 2 regiones:

– Fuente de corriente controlada por tensión cuando Vds > Vgs – Vt

– Resistencia variable controlada por tensión cuando Vds < Vgs – Vt

Los símbolos que utilizamos son casi idénticos a los de deplexión, pero hay pequeños huecos en el dibujo:

¿Y por qué nos interesa utilizar MOSFET y JFET en vez de BJT? Porque

– No siempre vamos a tener la corriente necesaria en la base pero sí la tensión en la puerta.

– Los MOSFET y JFET son capaces de aguantar una cantidad de corriente enorme, de decenas de amperios, y tensiones Vds de cientos de voltios.

– Por otro lado, los BJT tienen ciertas ventajas en velocidad de conmutación, pero las diferencias hoy en día son mínimas.

– Los BJT son más conocidos por su sencillez y bajo coste, del orden de 5 céntimos la unidad, mientras que los MOSFET bien pueden costar 1 euro el más barato.

– Los MOSFET tienen una mayor posibilidad de integración, es por ésto que se utilizan hoy en día en computación. Trabajando en conmutación, un MOSFET puede “guardar” su estado indefinidamente. Bueno, más bien durante unos 40 años. Ésto lo comprobamos cuando hacemos encender un LED en el drenador con una tensión en la puerta positiva, y soltamos la puerta del generador. Se quedará encendido el LED hasta que pongamos la puerta a 0 lógico, a tierra.

Tr. FET: JFET

Juncture Field Effect Transistor. Así es como se llaman éstos transistores, traducido como “transistor de efecto de campo de unión”. Para lo que son interesantes es debido a su misma descripción: el efecto campo.

En el apartado de BJT aprendimos que los transistores tienen colector, base y emisor. Introducíamos una corriente en la base y por el colector hasta el emisor aparecía una corriente proporcional. Las diferencias son:

– Las patillas de los JFET se llaman Drenador, Puerta y Surtidor (Drain, Gate y Source en inglés)

– No introduciremos una corriente en la puerta, cuya impedancia es enorme (al igual que en los amplificadores operacionales, se considera infinita), sino que originaremos una diferencia de potencial entre puerta y surtidor, Vgs. De ésta forma, lo que tenemos es una fuente de corriente controlada por tensión. Además, el consumo es mínimo.

¿Y qué tipos de JFET hay? Los hay canal N y canal P. Y por raro que suene éste es el mundo al revés. La flechita para dentro indica canal N y para fuera es un canal P. El canal N, con una Vgs de 0 voltios, deja pasar toda la corriente que es capaz, mientras que con una Vgs negativa el canal “se va cerrando” y deja pasar menor corriente. Con el canal P es algo similar: con 0 Vgs deja pasar toda la corriente y según va aumentando positivamente el canal “se va cerrando”. A diferencia de los MOSFET que veremos a continuación, no es posible que pase más corriente drenador-surtidor de la máxima, Idss.

Ahora os presento los símbolos que usan éstos aparatejos en sus esquemas:

Cabe puntualizar que en el N-Canal la corriente va de drenador a surtidor, mientras que en el P-Canal, ésta va de surtidor a drenador, o sencillamente de drenador a surtidor negativa.

Ahora veremos cómo calculamos la corriente drenador-surtidor, Ids, que pasa por nuestro transistor en nuestro circuito. Es un poco rollo, porque aparecerán ecuaciones de segundo grado, pero vale la pena. La ecuación a recordar es la siguiente:

OJO, sólo útil para cuando el transistor JFET está en “saturación”, es decir, se está comportando como fuente de corriente controlada por tensión. Ahora comprobaremos cuándo.

Como vemos hay un parámetro que no conocemos, Vp, es la tensión de estrangulamiento, “punch voltage”, valor de Vgs con el que el canal se cierra del todo. Tanto los valores de Vgs como Vp se suelen dar como módulos, de forma que el signo menos detrás del 1 siempre se mantiene, SIEMPRE, de forma que Id siempre será menor que Idss. (En los JFET).

¿Y en qué estados puede estar el transistor? Analizaremos, una vez calculado todo, Vds y Vgs – Vp.

– Cuando Vds >= Vgs – Vp, el transistor está funcionando como fuente de corriente controlada por tensión.

– Cuando Vds <Vgs – Vp, el transistor está funcionando como una resistencia variable controlada por tensión.

Ahora vamos a hacer un problema con un esquema sencillito y los datos que a mí me vengan a la mente…

jfet

Supondremos que |Vp| = 5V, Idss = 10mA, Vcc = 12V. Calcularemos el punto de polarización: Vgs, Ids, Vds.

Y os pensaréis que Id = Idss porque Vgs = 0 porque está conectado a tierra. Error. La puerta está conectada a tierra, Vg = 0, pero el surtidor está conectado a una resistencia por la que pasa corriente, Vs = R2*Ids. Y por ésto es que es más bonito y útil conectar el surtidor a tierra y usar una fuente en puerta. Entonces:

Vgs = Vg – Vs = 0 – R2*Ids

Bienvenidos de nuevo al análisis de circuitos, y enhorabuena, hemos conseguido Vgs negativas con una fuente positiva. La resistencia de 1Mohm en la puerta está de decorativo, no hay que preocuparse por ello. Y ahora, nada, sustituimos en la ecuación que, efectivamente, va a ser de segundo grado.

Ids = 10 * (1-(R2*Ids/Vp))^2

Como veis, he utilizado los módulos para que se mantenga siempre en negativo. Ahora utilizamos el solucionador de ecuaciones despejamos hasta encontrar 2 valores posibles de Ids.

Ids = 4.3 mA

Ids = 36.32 mA

Aquí está lógico, porque sabemos que Ids es siempre menor que Idss, entonces descartamos el valor de 36.32 mA.

Bien, ya hemos hallado el valor de Id = 4.3 mA. Ahora calculamos el valor de Vds.

12 – R1*Ids – Vds – R2*Ids = 0

De aquí despejamos y sale que Vds =7.7V

¿Pero sabemos que efectivamente el transistor está en “saturación”? No, pero lo sabremos enseguida:

Vgs = – 0.4*4.3 =- 1.72V

Vgs – Vp =  |Vp| – |Vgs| = 3.28V

Vds > 3.28V

Así que afirmamos que el transistor está en saturación (fuente de corriente controlada por tensión) y que su punto de polarización es: Vgs = -1.72V, Ids = 4.3 mA y Vds = 7.7V.

Si tenéis alguna duda o sugerencia, o quizás veis que me he equivocado en algo, podéis comentar. Gracias.

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);
}

PIC and EEPROM programmer

Testeado y funcionando con PIC16F84A, PIC16F877A

Programado con PICPgm

Éste finde (si contamos el jueves como finde), he estado haciendo éste “pequeño” proyecto para ver si paso de la simulación al manejo de PICs a mano y en casa. Quizás el curso de arduino haya sido un incentivo porque, sin lugar a dudas, uno empieza a pensar cuántas cosas se pueden hacer y se lia.

Empezaré por el 16F84A, tampoco es un avance muy fuerte…

Bien, al caso, he estado haciendo éste juguetito que extraje de ésta web que a su vez salió de una web inglesa que figura al final…

http://www.elotrolado.net/hilo_programador-de-pics-12-16-18-y-eeproms-24cxx_1505958

Tiene una lista ENORME de PICs y EEPROMs que puede programar. Es un programador tipo JDM, debe ser muy bueno, aún lo tengo por probar porque me falta el cable de puerto serie. Se utiliza con los programas ICPROG y WinPIC PICPgm (que vale para W7, es menos pesado y más independiente). Si alguien quiere hacerlo y no tiene puerto serie, puede comprar un adaptador de USB a serie, pero se advierte que el funcionamiento puede no ser correcto.

Mi objetivo sería programar PICs para jugar y practicar y por último hacer un programador USB cuya interface sea otro PIC.

Comentar que en la web está el archivo descargable con los pdf del bottom, top y jumpers de la placa, y un esquemático. Cuando lo vayáis a imprimir, el bottom ya está con efecto espejo, imprimir y aplicar, pero el top, que es la capa de componentes, no.

Hace falta tener cierta experiencia con el taladro y el soldador porque es complicado hacer los agujeros rectos y que luego no se junten varios pines. Yo lo he conseguido por los pelos.

A mí no me ha quedado tan bonito porque no he cortado la placa pero allá va:

Introducción a la programación en C

Mucho más sencillo y organizado que el ensamblador, que ya dejé atrás hace unos meses pero que no dejaré de utilizar este curso supongo… Hay que tener algo de experiencia en programación en C.

Sin embargo C tiene las mismas historias: necesita seguir un esquema y unas partes, con sus funciones, declaración de variables, puertos, etc. Por lo tanto haré esta introducción para tener la base a mano.

Necesitaréis un programa para compilar lo que hagáis, yo estoy utilizando PIC C Compiler. Recordar con cualquier compilador que hay que seleccionar el micro que se va a usar.

Declaración de librerías, fuses, puertos y constantes

 
#include <pic.h>

Donde pic es el numero del micro, por ejemplo 16F84A

#fuses …

Aquí podemos poner varias cosas que queremos o no que nuestro micro use, separadas por comas:

– LP, XT, HS, RC (Usar cristal externo hasta 200 khz, de 4 a 10 Mhz, a partir de 8 Mhz o un oscilador resistencia-condensador)

– NOWDT (desactiva el watchdog timer)

– PWRT (delay al encender el micro para estabilizar tensión)

– CP (code protect, que no se pueda copiar el código de la memoria)

Éstos son los básicos y más necesarios pero hay bastantes más que se especifican en la datasheet de cada micro.

#byte x=y (nombre=dirección)

Para nombrar una dirección de un byte. Así se nombrarán puertos, por ejemplo PORT_A suele estar en la dirección 0x05: #byte PORT_A=0x05

Para evitar problemas que dan a veces simuladores y micros también declararemos aquí el estado del puerto (E o S), de ésta forma:

#byte TRIS_A=getenv(“SFR:TRISA”);

También podemos declarar bits (patillas) mediante:

#bit x=y.z (nombre=puerto.numero)

Declaración de funciones, variables, interrupciones y programación

Llegados a este punto lo que hacemos es declarar las variables (int, char, float…) y las funciones que programaremos después. Ésto no es necesario si no se van a usar.

Después de declararlas programamos la interrupción poniendo #Nombredeinterrupcion y en la linea siguiente programando la función de interrupción que se llevará a cabo. Por lo general el nombre de la interrupción ya está definido por las posibilidades del micro, por ejemplo int_ext (RB0/INT) o int_rb (RB4-RB7) dependiendo de donde cambie el estado del pin.

A continuación programamos las funciones que hemos declarado antes de las interrupciones.

Main, cuerpo del programa

Aquí programamos lo que queremos que el micro haga con todo lo que hemos declarado antes. Se empieza por un “void main() {” y se cierra } al final. Las funciones las iré explicando poco a poco según ponga ejemplos.

Sin duda hay mucho más que se puede hacer, puerto esclavo con conexion a ordenador, LCD, lectura analógica, y otras utilidades, pero éste es el cuerpo básico de un programa y se puede sacar mucho jugo de 4 funciones cualquiera. Ánimo y a por ello.

 

 

Control de motor DC con puente H

Con éste sencillo circuito podemos controlar con la pequeña corriente que sale de un circuito lógico, un motor DC bastante potente si se toman los componentes adecuados.

Éstos circuito vienen en integrados, pero es simple y cualquiera podría hacerlo. En “inversión” introduciremos valores lógicos 1 o 0 para que vaya en sentido horario o antihorario. En “marcha” introduciremos pulsos de reloj. La velocidad máxima del motor dependerá del ciclo de trabajo de éstos pulsos, para ello podremos utilizar un 555 perfectamente. En Vcc introduciremos la tensión que llegará al motor. Ésta tensión llegará según los transistores se abran o se cierren. Lo mejor es no pasarse de lo que especifica el motor.

En mi caso utilizaré un motor de 12 V máximo, posiblemente le ponga 5, y lo controlaré mediante un 555 astable con un potenciómetro para variar el ciclo de trabajo. Los transistores serán BC548 y BC557 si no puedo conseguir los BC558. Las resistencias que se suelen poner son de 330 o 390 ohmios cuando se trata de salidas de microcontroladores.

También se recomiendan éste y otros diseños completamente a transistores NPN cuyo control es izquierda o derecha, poniendo los pulsos en éstos mismos pines directamente. No tan lógico pero útil por igual ya que con imaginación tiene otras aplicaciones:

Los diodos que aparecen en éste circuito también se pueden poner en el anterior exactamente igual. No sé a ciencia cierta para qué son pero seguro que protegerán de más de un susto.