Control de Matriz Estática

Este programa sirve para demostrar el control de una matriz de 5x7 de forma estática, se crearon 8 letras en el estándar ASCII para matrices de 5x7, la palabra escrita es "ELECTGPL " se puede modificar por cualquier letra o bien agregarle mas o quitarle algunas.
El programa se realiza en dos etapas, la principal donde se realiza el multiplexado de las columnas y se muestran los valores de cada fila a una velocidad de 10ms y la interrupción por desbordamiento del timer, dentro de la interrupción se alojan los caracteres dentro de un switch el cual servirá para ir cambiando entre cada letra, ya que al final de cada caso tiene un contador "letra++" el cual será el valor a testear por el switch, a medida que se recorra cada una de las 5 columnas se incrementara en 1 la variable letra, de esta forma pasara al siguiente paso, por consiguiente la siguiente letra, la ultima letra en lugar de incrementar en 1, iguala a cero la variable letra, entonces comenzara de nuevo.
El tiempo esta dado por set_timer1(1000), es totalmente configurable a gusto del desarrollador.
Como el circuito utilizado es el más sencillo posible para que se encienda un Led debe existir un 1 y un 0 de cada lado de la matriz, se podría haber usado el selector de columnas como 0b11111110, 0b11111101, etc... Pero para comprenderlo fácil se utilizo un 1 donde se quiere encender, para que no salga la letra negada en la matriz se utilizo el operando "~" delante del vector de las filas, de esta forma complementa el valor de la variable para que se vea correctamente.
Para utilizar más columnas debería utilizarse mas salidas, como podemos imaginarnos, podríamos hacer una matriz de 8x8 con dos puertos del PIC, pero se puede hacer más grande implementando registros de desplazamiento serie/paralelo, como el 74164, 74595, 4094, etc... Estos integrados reciben los datos de las columnas de forma serial y lo despliegan en paralelo en sus salidas, los mismos se pueden concatenar para hacer matrices más grandes, estos integrados se controlan mediante un Clock de sincronismo, un Dato donde se enviaran los valores seriales, y un Reset que pondrá el puerto paralelo de los registros a ceros.




#include <16f883.h>
#fuses XT,NOWDT,NOPROTECT,PUT,NOLVP,NOMCLR,INTRC
#use delay (int=4000000)
int fila[5],letra=0;
#INT_TIMER1
void isr()
{
   switch(letra){
      case 0: fila[0]=0b01000001;
              fila[1]=0b01000001;
              fila[2]=0b01001001;
              fila[3]=0b01001001;
              fila[4]=0b01111111;
              letra++;
              break;
      case 1: fila[0]=0b00000001;
              fila[1]=0b00000001;
              fila[2]=0b00000001;
              fila[3]=0b00000001;
              fila[4]=0b01111111;
              letra++;
              break;
      case 2: fila[0]=0b01000001;
              fila[1]=0b01000001;
              fila[2]=0b01001001;
              fila[3]=0b01001001;
              fila[4]=0b01111111;
              letra++;
              break;
      case 3: fila[0]=0b01000001;
              fila[1]=0b01000001;
              fila[2]=0b01000001;
              fila[3]=0b01000001;
              fila[4]=0b00111110;
              letra++;
              break;
      case 4: fila[0]=0b01000000;
              fila[1]=0b01000000;
              fila[2]=0b01111111;
              fila[3]=0b01000000;
              fila[4]=0b01000000;
              letra++;
              break;
      case 5: fila[0]=0b00000110;
              fila[1]=0b01001001;
              fila[2]=0b01001001;
              fila[3]=0b01000001;
              fila[4]=0b00111110;
              letra++;
              break;
      case 6: fila[0]=0b00110000;
              fila[1]=0b01001000;
              fila[2]=0b01001000;
              fila[3]=0b01001000;
              fila[4]=0b01111111;
              letra++;
              break;
      case 7: fila[0]=0b00000001;
              fila[1]=0b00000001;
              fila[2]=0b00000001;
              fila[3]=0b00000001;
              fila[4]=0b01111111;
              letra++;
              break;
      case 8: fila[0]=0b00000000;
              fila[1]=0b00000000;
              fila[2]=0b00000000;
              fila[3]=0b00000000;
              fila[4]=0b00000000;
              letra = 0;
              break;
     }
     set_timer1(1000);

}
void main(void)
{
   enable_interrupts(INT_TIMER1);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
   set_timer1(65535);
   enable_interrupts(GLOBAL);
   set_tris_c(0x00);
   set_tris_a(0x00);
   while(TRUE){
      output_a(0b00000001);
      output_c(~fila[0]);
      delay_ms(10);
      output_a(0b00000010);
      output_c(~fila[1]);
      delay_ms(10);
      output_a(0b00000100);
      output_c(~fila[2]);
      delay_ms(10);
      output_a(0b00001000);
      output_c(~fila[3]);
      delay_ms(10);
      output_a(0b00010000);
      output_c(~fila[4]);
      delay_ms(10);
   }
}


Adaptador de Matriz de 5 x 7 puntos.

Luego de comprar esta matriz y darme cuenta de que los pines no seguían un orden lógico... he desarrollado un adaptador para que los pines queden en orden, tal que con solo montarla en el protoboard sea posible aplicarla a los proyectos directamente.
Note se que los pines fueron soldados hacia abajo para poder montarla en el protoboard y así poder conectarla directamente, por ejemplo en el circuito que monte para testerla en esta nota, se puede ver que el ULN2003 que utilice como driver esta conectado directamente a los pines como se ve, no es necesario utilizar puentes extra.
Una mejora seria la de integrar el ULN y los 5 transistores directamente en el PCB. 
Estos componentes extra se consiguen en SMD por lo que sera sencillo y no necesitara mayores dimensiones en el PCB.
El firmware de esta nota no viene al caso pero igualmente lo subiré para completar la nota.



#include <16F883.h>
#use delay(int=4000000)
void main(){
int i;
byte const FILA[05] = {0b00000000,0b01101011,0b00101011,
                       0b01101001,0b00000000};
byte const COLU[05] = {0b00000001,0b00000010,0b00000100,
                       0b00001000,0b00010000};
   while(1){
     if(i>5){
       i=0;}
     else{
       OUTPUT_B(
FILA[i]);
       OUTPUT_C(COLU[i]);
       delay_ms(1);
       i++;}}}

Manejo de tablas

Si bien las tablas se crean de la misma forma que en cualquier programa en C, la idea de esta nota es la de mostrar como se puede crear una tabla secuencial en este caso, aunque también puede ser indexada.
El programa que se detalla aquí es el de una tabla que se recorrerá ciclicamente, es decir el índice de la tabla sera incrementado dentro del while con el acumulador i = i + 1, y con un if se reiniciara el índice hasta que sea igual al largo de la tabla.
se puede notar que los valores ingresados en la tabla están en binario, pero se pueden ingresar en cualquier base, en este ejemplo se eligió el binario porque se mueve directamente el valor al puerto B, pero podría haber ingresado un valor decimal.
La longitud de la tabla es el valor que recorrerá el vector, entonces si ingresamos por ejemplo 20 valores en la tabla la variable longitud tendrá que ser de 20, sino el bucle se reiniciara antes o después.
La tabla no necesariamente tiene que ser automática (velocidad dada por delay) puede ser indexada por ejemplo podríamos alojar el valor en binario de los segmentos encendidos de un display de 7 segmentos, por ejemplo si el numero cero del display de 7 segmentos requiere que en el puerto b del micro exista el valor 01110111, lo que hacemos es poner en la dirección 0 de la tabla ese valor, luego en la dirección 1 pondremos los bit en 1 que sean necesarios para formar el 1 en los segmentos del display. y así sucesivamente, entonces en lugar de usar un ciclo para recorrer el vector podemos simplemente moverle el numero que necesitamos que nos devuelva en el puerto, por ejemplo SALIDA[3] y el puerto tendrá los bit seteados para que el display muestre el 3.
Esta es una de las tantas cosas que podemos hacer, también podemos guardar caracteres, o bien podemos usar dos vectores en lugar de uno, y de esa forma mediante un burbujeo podemos crear una matriz de 2 dimensiones.


#include <16F628A.h>
#FUSES NOWDT
#FUSES INTRC
#FUSES NOPUT
#FUSES NOPROTECT
#FUSES NOBROWNOUT
#FUSES NOMCLR
#FUSES NOLVP
#FUSES NOCPD
#use delay(int=4000000)
void main()
{
   int i, LargoTabla = 7;
   byte const SALIDA[10] = {0b00001111,
                            0b11110000,
                            0b00110011,
                            0b11001100,
                            0b01010101,
                            0b10101010,
                            0b00000000};
   while(1)
      {
      if (LargoTabla == i)
         {
         i = 0;
         }
      else
         {
         OUTPUT_B(SALIDA[i]);
         delay_ms(100);
         i = i + 1;
         }
      }
}


PWM sin CCP


Esta publicación fue realizada con el fin de conseguir una segunda opción para los MCU que no poseen módulos PWM o bien para aquellos desarrollos que requieren mas de los que posee el MCU.
Si bien ya sabemos que los MCU mas baratos no poseen estos módulos PWM pero con esta simple rutina es posible modular una salida cualquiera para lograr el PWM.
Dejando de lado los micros que poseen PWM, como sabemos necesitamos setear el setup_timer_2(...), y luego mover un valor decimal a la función set_pwm1_duty(XX) donde XX es el valor que movemos y es el ciclo de trabajo pwm que entregara el MCU por su salida PWM, pero el problema es cuando necesitamos mas salidas, por ejemplo en esta nota he utilizado el PIC16F883 no es de alta gama pero es bastante completo y mantiene un costo bajo, pero a pesar de ello solo posee dos salidas PWM.
La idea de esta rutina es manipular tantas salidas como tenga el MCU si se quisiera.
La el programa es el siguiente:

#include <16F883.h>
#device adc=8
#FUSES NOWDT
#FUSES INTRC_IO
#FUSES NOPUT
#FUSES NOMCLR
#FUSES NOPROTECT
#FUSES NOCPD
#FUSES NOBROWNOUT
#FUSES IESO
#FUSES FCMEN
#FUSES NOLVP
#FUSES NODEBUG
#FUSES NOWRT
#FUSES BORV40
#use delay(int=8000000)
void main()
{
   setup_adc_ports(sAN0|VSS_VDD);
   setup_adc(ADC_CLOCK_DIV_2);
   int8 tOFF, tON;
   while(1){
      set_adc_channel(0);
      tOFF = read_adc();
      tON = 255 - tOFF;
      delay_us(tOFF);     
      output_low(PIN_C0);
      delay_us(tON);
      output_high(PIN_C0);
      }
}


El circuito es muy simple solamente se utiliza un pin de entrada y uno de salida, pero de todas formas lo subiré para completar la nota.





La primera parte del código (include del micro a utilizar, rango del ADC, configuración del MCU "FUSES", delay) es genérica, lo que nos interesa es el código marcado en color rojo, lo que podemos ver es que la lectura del ADC (alimentado por el potenciómetro) se guarda en tOFF, luego tON será el máximo del ADC como lo seteamos en 8bit será de 0 a 255, por ende el valor del ADC será restado del 255 y nos dará el tON, luego utilizaremos el tON como variable del delay_us() y de la misma forma el tOFF, esto quiere decir que primero tOFF tendrá el tiempo de apagado del Led output_low(PIN_C0) y luego tON tendrá el tiempo de encendido output_high(PIN_C0).
Un ejemplo de este calculo seria:  si por ejemplo situamos el potenciómetro en el centro el ADC estaría leyendo +/-2,5V que traducidos al ADC seria el valor 127, ese valor estaría en la variable tOFF, luego aparece el calculo 255 - tOFF = tON, entonces seria 255-127 = 128, tON valdría 128, podríamos decir que el ciclo es +/-50%, ahora si variamos el potenciómetro por ejemplo a 1,25V seria el valor 64 del ADC, tOFF seria igual a 64 y tON seria 255 - 64 = 191, entonces podemos decir que el ciclo de trabajo seria del 75%, de esa forma trabaja nuestra rutina, la cual se podría expandir para cada salida prolijamente dentro de una función o bien dentro de una interrupción.


DutyCycle 95%


DutyCycle 50%


DutyCycle 5%




 

Regulador 78XX >2A

Esta fuente la he desarrollado por la necesidad de un cargador de 5V 2A para una tablet, como el cable que trae es USB ya sabemos que el USB de la PC máximo nos entrega 500mA, por lo que seria absurdo conectar 4 cables USB a una pc solo para cargar un dispositivo.
La otra opcion es conectar este cable directo a los 5V de la fuente de PC que sabemos que le sobran mas de 2A para drenar.
Pero en mi caso no quería dejar la fuente de la PC encendida solo para cargar este dispositivo.
El problema es que el 7805 solo drena 1,5Amax con un disipador muy generoso y ventilado, por esa razón le baje el máximo de consumo a 1A para que no trabaje tan exigido, ahora viene la forma de obtener 2A, si bien en el datasheet nos muestra un circuito de mayor corriente con un transistor en base común alimentado por un 7805 y una resistencia de 1W, pero la verdad es que ya tenia los componentes en mi casa y quería salir del apuro, aparte creo que es mas sencillo mi circuito que el que presenta el datasheet.
En Internet cuando uno busca fuente de 5V de 2 o 3A encuentra que muchos circuitos ponen directamente los reguladores en paralelo (pin a pin) pero ese diseño me hizo un poco de ruido y tras analizarlo un poco, cuando vemos el datasheet el circuito interno del 7805 (en el datasheet que puse aquí en la pagina 10)
podemos ver que el transistor Q16 es la etapa de salida pero a su vez el divisor resistivo que tiene en su emisor genera una realimentacion hacia Q14 para "estabilizar" y también re-alimenta el espejo de corriente formado por Q18 y Q1. Resumiendo, la idea de este regulador es entregar tensión regulada, no de ingresarle tensión (por la salida) ya que podría modificar el funcionamiento interno.
Entonces cuando ponemos dos reguladores en paralelo, si unimos los pines de entrada no hay problema porque son entrada de alimentación, si unimos gnd no hay problema porque es común a los dos, pero si unimos las salidas, ambos reguladores estarán enviando 5V a la salida del otro, y como sabemos, no son componentes ideales por ende uno enviara corriente que el otro, entonces alguno de los dos funcionara mal o tendera a calentar mas, etc...
Lo que yo hice para solucionar eso, es simplemente poner un diodo a la salida de cada regulador y la unión de estos dos es en los cátodos, entonces la corriente de cada uno de los reguladores queda en la unión de los diodos, de esta forma el diodo no permite que la corriente vuelva al regulador, así preservamos los reguladores. Como sabemos los diodos de silicio tienen una caída de tensión de 0,7V pero en la practica es un poco menos (aprox +/-0,5V) por eso a la salida de los reguladores en lugar de 5V tendríamos 4,5V. Para compensar eso lo que hacemos es "acercar" la referencia a gnd del regulador, esto se realiza con un diodo entre gnd de la fuente y gnd del regulador, lo que hace es acercar 0,5V (la caída de tensión del diodo) al gnd de la fuente, entonces ahora los 0,5 que teníamos de mas en la salida los restamos en gnd, entonces el regulador compensara la salida y nos dará 5V.
NOTA: La idea de poner diodos en gnd se aplica para variar la tensión de salida de cualquier regulador 78XX y 79XX, se pueden poner diodos en serie para que las variaciones sean mas grandes, o diodos zener.
El diodo que emplee en el circuito es un 1N5400 (es un diodo de 3A) se puede emplear cualquiera de esa corriente, este es de 50V porque no tiene sentido poner uno de 400V o 1000V..., no cambiara el funcionamiento si se pone otro.