sábado, 26 de noviembre de 2011

Microsoft Office 2010





Este software ha sido subido para que prueben la utilidad de esta poderosa ofimática, si en verdad deseas a que sus desarrolladores sigan creando cosas maravillosas por favor compralo.
Nada publicado en este blog es lucrativo, publicamos por el simple hecho de querer compartir.

lunes, 21 de noviembre de 2011

HCS08 - Motorola Freescale - Convertidor AD enviado por Comunicación Serial

COMUNICACIÓN SERIAL Y CONVERTIDOR ANALÓGICO DIGITAL CON HCS08

Material:
* Micro HCS08 (MC9S08SE4CRL 0M82P).
* Open Source BDM (o con lo que programes el micro-controlador).
* Conector DB-9 (Tipo entrada [vulgarmente conocido como Hembra]).
* PC o Laptop (con el IDE para programar el micro-controlador.
* Un cable de comunicación serial (para comunicar a la Computadora con el puerto serial del micro-controlador).
* Un Circuito Integrado MAX232N o MAX233.
* Un potencio-metro de Precisión.
* Y los dispositivos que necesites para el integrado que decidas usar.

Objetivo
El objetivo de esta práctica es tomar el valor analógico de ADP1, procesarlo y mandar su resultado vía comunicación serial con la computadora.
En la entrada analógica se colocará un potenciómetro en forma de divisor de voltaje para que por medio de hacer un cambio en el voltaje el resultado del mismo se refleje en la computadora.

MAX233
Este circuito integrado permite la comunicación serial entre un microcontrolador y la computadora, esto debido a que el estándar de los voltajes lógicos que maneja la computadora es de BAJO: -12V y ALTO: 12V, en tanto que un microcontrolador maneja BAJO: 0V y ALTO: 5V.
Este circuito integrado adapta los voltajes de los pulsos de información que se intercambian durante la comunicación.


DB9
Este conector es el estándar usado para la comunicación serial.
El conector DB9 (originalmente DE-9) es un conector analógico de 9 clavijas de la familia de conectores D-Subminiature (D-Sub o Sub-D).
El conector DB9 se utiliza principalmente para conexiones en serie, ya que permite una transmisión asíncrona de datos según lo establecido en la norma RS-232 (RS-232C).


Se debe tener en cuenta que existen adaptadores DB9-DB25 (Serial-Paralelo) para convertir fácilmente un enchufe DB9 en uno DB25 y viceversa.
Clavijas
Número de clavija
Nombre
1
CD: Detector de transmisión
2
RXD: Recibir datos
3
TXD: Transmitir datos
4
DTR: Terminal de datos lista
5
GND: Señal de tierra
6
DSR: Ajuste de datos listo
7
RTS: Permiso para transmitir
8
CTS: Listo para enviar
9
RI: Indicador de llamada

Protección

Código
Podemos mandar el valor del convertidor en dos formatos, decimal y hexadecimal.

Decimal
;*******************************************************************
;* This stationery serves as the framework for a user application. *
;* For a more comprehensive program that demonstrates the more     *
;* advanced functionality of this processor, please see the        *
;* demonstration applications, located in the examples             *
;* subdirectory of the "Freescale CodeWarrior for HC08" program    *
;* directory.                                                      *
;*******************************************************************

; Include derivative-specific definitions
            INCLUDE 'derivative.inc'
;
; export symbols
;
            XDEF _Startup
            ABSENTRY _Startup

;
; variable/data section
;
            ORG    RAMStart         ; Insert your data definition here
ExampleVar: DS.B   1
conversion  equ     $85
unidades    equ     $82
decenas     equ     $81
centenas    equ     $80
tope        equ     $83
cuentaux    equ     $84
unidadesA   equ     $88
decenasA    equ     $87
centenasA   equ     $86

;
; code section
;
            ORG    ROMStart

_Startup:
            LDHX   #RAMEnd+1        ; initialize the stack pointer
            TXS
            CLI                     ; enable interrupts


            ; Insert your code here
            
;PROGRAMA que usa el convertidor analogico digital del micro
;usando la entrada por PTA1, ADP1
;obtenemos una lectura y la mandamos por puerto serial ajustando
;los valores para que se despliegue en decimal
;VREFH va a VCC
;VREFL va a GND
;creado por: Hazael Fernando Mojica García
;Matrícula: 1500724


;cabecera del programa o tambien podriamos decir, la programacion de puertos
;*********************               
cabecera:            
                
            ;Configuracion del ADC
            ;*************************************
           ;Configuracion del ADC
            LDA #$90          ;Cargamos $90=10010000
            STA ADCCFG        ;Colocamos en ADCCFG=bajo poder, clock /1,largo tiempo muestreo, resolucion 8 bits, 
            
            LDA #$00
            STA ADCSC2        
                                                                   
            LDA     #$21        ;$21=00100001, COCO y AIEN desactivados, Activada conversion continua, Canal 1 seleccionado
            STA     ADCSC1
            ;*************************************
            

            ;declaramos la comunicacion serial
            ;*************************************           
            LDA    #$0
            STA    SCIBDH
            LDA    #$1F
            STA    SCIBDL
            LDA    #$0
            STA    SCIC1
            LDA    #$0C
            STA    SCIC2
            ;*************************************
            
            ;acomodamos el codigo ascci en una secuencia de memorias
            ;*************************************
            MOV   #$30,$90        ;codigo ascci del $0 es $30 
            MOV   #$31,$91        ;codigo ascci del $1 es $31 
            MOV   #$32,$92
            MOV   #$33,$93
            MOV   #$34,$94
            MOV   #$35,$95
            MOV   #$36,$96
            MOV   #$37,$97
            MOV   #$38,$98
            MOV   #$39,$99
            MOV   #$41,$9A       ;codigo ascci del $A es $41 
            MOV   #$42,$9B
            MOV   #$43,$9C
            MOV   #$44,$9D
            MOV   #$45,$9E
            MOV   #$46,$9F  
            ;*************************************
            
            MOV   #$A,$8A       ;New Line
            MOV   #$D,$89       ;carriage return
            
;*******************************************************************
;Termina la cabecera            

;Inicia el main
;*******************************************************************
;*******************************************************************
main:
            feed_watchdog
            BSR   hacer_conversion
            LDA   conversion
            STA   tope
            BSR   separa
            BSR   ajusta
            BSR   manda_serial
            ;BSR   retardo

            BRA   main
;*******************************************************************
;*******************************************************************

;SUBRUTINA MANDA_SERIAL
;Esta subrutina se encarga de mandar por serial el contenido de las 3 memorias
;centenas, decenas y unidades en ese orden
;**********************************************************************
manda_serial:
            LDHX  #$86      ;apunto a la mem $80
            
lleno:
            feed_watchdog
            LDA   SCIS1       ;cargo el Status register
            AND   #$80   
            BEQ   lleno       ;Si ya se envio el dato anterior TDRE=0, mando otro

            LDA   ,X          ;cargo el contenido de la memoria apuntada actual
            STA   SCID        ;la coloco para enviar
            INCX              ;incremento el puntero
            CPHX  #$8A        ;si estoy apuntando a la 8B (palabra finalizada)
            BNE   lleno       ;si no, mando el siguiente caracter
            
            RTS 
;**********************************************************************
;Termina subrutina de Manda_serial



;SUBRUTINA DE CONVERSION
;******************************************
hacer_conversion:
            feed_watchdog
            LDA     ADCSC1          ;Se carga el contenido de ADCSC1 para verificar COCO
            CMP     #$A1            ;Comparamos con $A1=%10100001 si igual Z=1,(COCO activado, AIEN desactivado, Conversion continua activada, Canal 1), 
            BNE     hacer_conversion;si COCO no esta activado (Z=0), regresa (Branch) a main, con COCO debe ser: 
                        
            LDA     ADCRL               ;cargamos la conversion
            STA     conversion           ;la colocamos en nuestra variable            
            RTS
            
 ;TERMINA SUBRUTINA DE CONVERSION
;******************************************

;SUBRUTINA SEPARA (UNIDADES, DECENAS Y CENTENAS)
;SUBRUTINA QUE SEPARA EL NUMERO EN DOS MEMORIAS, UNA CON UNIDADES (unidades) Y OTRA CON DECENAS (decenas) Y OTRA CON CENTENAS (centenas)
;se tomara la memoria: tope, como el numero el cual se desea separar
;se usa una variable auxiliar llamada cuentaux que llevara la cuenta actual
;****************************
separa:           
                CLR   cuentaux
                CLR   unidades
                CLR   decenas
                CLR   centenas

checa_aux:
                LDA   cuentaux                        
                CMP   tope                          ;comparamos la cuenta actual, si ya llegamos a tope
                BEQ   sale_separa                   ;salimos de la subrutina, si no, incrementamos de nuevo
                BRA   incrementa_cuenta

incrementa_cuenta:
                feed_watchdog
                INC   unidades                      ;si las unidades completa una decena
                INC   cuentaux
                LDA   unidades                      ;regresamos a cero unidades y decenas +1
                CMP   #$A
                BEQ   una_decena_mas
                BRA   checa_aux                     ;si no hay incrementos de decenas nos vamos directo a checar el auxiliar
                                
una_decena_mas:
                MOV   #$00,unidades
                INC   decenas
                LDA   decenas                       ;si hay una decena mas, incremento en uno las centenas
                CMP   #$A                           ; y regreso a cero las decenas
                BEQ   una_centena_mas
                BRA   checa_aux
                
una_centena_mas:
                MOV   #$00,decenas
                INC   centenas
                BRA   checa_aux                
                                
sale_separa:    
                RTS                                
                
;**************************************
;TERMINA SUBRUTINA SEPARA


;SUBRUTINAS DE AJUSTE
;ajusta, es decir convierte el contenido de las memorias unidades, decenas y centenas
;a su valor hexadecimal ascii
;*********************************************

;.---------------------------Subrutinas de ajuste
ajusta:            
ajusta_unidades:
                LDHX    #$90                    ;apuntamos a la memoria $80, que es donde empiezan los valores de ajuste
                MOV     #$90,tope               ;colocamos $80 en la memoria que servira de tope
                LDA     unidades
                ADD     tope                    ;cargamos las unidades y las sumamos a tope obteniendo asi ($80+unidades) la direccion del numero para ajustar
                STA     tope                
ciclo_unidades:                             
                feed_watchdog                   ;recorremos desde la memoria $80 hasta que llegemos a la memoria tope($80+unidades)
                CPX     tope  
                BEQ     salir_unidades
                INCX
                BRA     ciclo_unidades               
salir_unidades:                                
                LDA   ,X
                STA   unidadesA                 ;el valor de la memoria tope la guardamos en unidadesA el cual sera el valor ajustado de la unidad
                
                
ajusta_decenas:
                LDHX    #$90                    ;misma explicacion que para unidades pero con decenas
                MOV     #$90,tope
                LDA     decenas
                ADD     tope
                STA     tope                
ciclo_decenas:
                feed_watchdog
                CPX     tope  
                BEQ     salir_decenas
                INCX
                BRA     ciclo_decenas               
salir_decenas:                                
                LDA   ,X
                STA   decenasA        


ajusta_centenas:
                LDHX    #$90                    ;misma explicacion que para unidades pero con decenas
                MOV     #$90,tope
                LDA     centenas
                ADD     tope
                STA     tope                
ciclo_centenas:
                feed_watchdog
                CPX     tope  
                BEQ     salir_centenas
                INCX
                BRA     ciclo_centenas               
salir_centenas:                                
                LDA   ,X
                STA   centenasA

                RTS
;*************************************************
;TERMINAN LAS SUBRUTINAS DE AJUSTE


;**************************************************************
;* spurious - Spurious Interrupt Service Routine.             *
;*             (unwanted interrupt)                           *
;**************************************************************
spurious:                           ; placed here so that security value
            NOP                     ; does not change all the time.
            RTI

;**************************************************************
;*                 Interrupt Vectors                          *
;**************************************************************
            ORG   $FFFA

            DC.W  spurious          ;
            DC.W  spurious          ; SWI
            DC.W  _Startup          ; Reset

Haxadecimal
;
;
; variable/data section
;
            ORG    RAMStart         ; Insert your data definition here
ExampleVar: DS.B   1
conversion  equ     $85
unidades    equ     $82
decenas     equ     $81
tope        equ     $83
cuentaux    equ     $84
unidadesA   equ     $88
decenasA    equ     $87

;
; code seconversion  equ     $85


;
            ORG    ROMStart

_Startup:
            LDHX   #RAMEnd+1        ; initialize the stack pointer
            TXS
            CLI                     ; enable interrupts

            ; Insert your code here

;PROGRAMA que usa el convertidor analogico digital del micro
;usando la entrada por PTA1, ADP1
;obtenemos una lectura y la mandamos por puerto serial ajustando
;los valores para que se despliegue en hexadecimal
;VREFH va a VCC
;VREFL va a GND
;creado por: Hazael Fernando Mojica García
;Matrícula: 1500724

;cabecera del programa o tambien podriamos decir, la programacion de puertos
;*********************               
cabecera:            
                
            ;Configuracion del ADC
            ;*************************************
           ;Configuracion del ADC
            LDA #$90          ;Cargamos $90=10010000
            STA ADCCFG        ;Colocamos en ADCCFG=bajo poder, clock /1,largo tiempo muestreo, resolucion 8 bits, 
            
            LDA #$00
            STA ADCSC2        
                                                                   
            LDA     #$21        ;$21=00100001, COCO y AIEN desactivados, Activada conversion continua, Canal 1 seleccionado
            STA     ADCSC1
            ;*************************************
            

            ;declaramos la comunicacion serial
            ;*************************************           
            LDA    #$0
            STA    SCIBDH
            LDA    #$1F
            STA    SCIBDL
            LDA    #$0
            STA    SCIC1
            LDA    #$0C
            STA    SCIC2
            ;*************************************
            
            ;acomodamos el codigo ascci en una secuencia de memorias
            ;*************************************
            MOV   #$30,$90        ;codigo ascci del $0 es $30 
            MOV   #$31,$91        ;codigo ascci del $1 es $31 
            MOV   #$32,$92
            MOV   #$33,$93
            MOV   #$34,$94
            MOV   #$35,$95
            MOV   #$36,$96
            MOV   #$37,$97
            MOV   #$38,$98
            MOV   #$39,$99
            MOV   #$41,$9A       ;codigo ascci del $A es $41 
            MOV   #$42,$9B
            MOV   #$43,$9C
            MOV   #$44,$9D
            MOV   #$45,$9E
            MOV   #$46,$9F  
            ;*************************************
            
            MOV   #$A,$8A       ;New Line
            MOV   #$D,$89       ;carriage return
            
;*******************************************************************
;Termina la cabecera            

;Inicia el main
;*******************************************************************
;*******************************************************************
main:
            feed_watchdog
            BSR   hacer_conversion
            LDA   conversion
            STA   tope
            BSR   separa
            BSR   ajusta
            BSR   manda_serial

            BRA   main
;*******************************************************************
;*******************************************************************

;SUBRUTINA MANDA_SERIAL
;Esta subrutina se encarga de mandar por serial el contenido de las 3 memorias
;centenas, decenas y unidades en ese orden
;**********************************************************************
manda_serial:
            LDHX  #$87      ;apunto a la mem $80
            
lleno:
            feed_watchdog
            LDA   SCIS1       ;cargo el Status register
            AND   #$80   
            BEQ   lleno       ;Si ya se envio el dato anterior TDRE=0, mando otro

            LDA   ,X          ;cargo el contenido de la memoria apuntada actual
            STA   SCID        ;la coloco para enviar
            INCX              ;incremento el puntero
            CPHX  #$8A        ;si estoy apuntando a la 8B (palabra finalizada)
            BNE   lleno       ;si no, mando el siguiente caracter
            
            RTS 
;**********************************************************************
;Termina subrutina de Manda_serial



;SUBRUTINA DE CONVERSION
;******************************************
hacer_conversion:
            feed_watchdog
            LDA     ADCSC1          ;Se carga el contenido de ADCSC1 para verificar COCO
            CMP     #$A1            ;Comparamos con $A1=%10100001 si igual Z=1,(COCO activado, AIEN desactivado, Conversion continua activada, Canal 1), 
            BNE     hacer_conversion;si COCO no esta activado (Z=0), regresa (Branch) a main, con COCO debe ser: 
                        
            LDA     ADCRL               ;cargamos la conversion
            STA     conversion           ;la colocamos en nuestra variable            
            RTS
            
 ;TERMINA SUBRUTINA DE CONVERSION
;******************************************

;SUBRUTINA SEPARA (UNIDADES, DECENAS)
;SUBRUTINA QUE SEPARA EL NUMERO EN DOS MEMORIAS, UNA CON UNIDADES (unidades) Y OTRA CON DECENAS (decenas)
;se tomara la memoria: tope, como el numero el cual se desea separar
;se usa una variable auxiliar llamada cuentaux que llevara la cuenta actual
;NOTA: se usa el termino UNIDADES para referirnos al primer digito del numero hexadecimal
;y el termino DECENAS para referirnos al segundo digito, ya que separa el numero por notacion desarrollada base 16
;no es que estemos trabajanod en decimal, solo es una forma de expresarlo
;****************************
separa:           
                CLR   cuentaux
                CLR   unidades
                CLR   decenas

checa_aux:
                LDA   cuentaux                        
                CMP   tope                          ;comparamos la cuenta actual, si ya llegamos a tope
                BEQ   sale_separa                   ;salimos de la subrutina, si no, incrementamos de nuevo
                BRA   incrementa_cuenta

incrementa_cuenta:
                feed_watchdog
                INC   unidades                      ;si las unidades completa una decena
                INC   cuentaux
                LDA   unidades                      ;regresamos a cero unidades y decenas +1
                CMP   #$10
                BEQ   una_decena_mas
                BRA   checa_aux                     ;si no hay incrementos de decenas nos vamos directo a checar el auxiliar
                                
una_decena_mas:
                MOV   #$00,unidades
                INC   decenas
                BRA   checa_aux
                                
                                
sale_separa:    
                RTS                                
                
;**************************************
;TERMINA SUBRUTINA SEPARA


;SUBRUTINAS DE AJUSTE
;ajusta, es decir convierte el contenido de las memorias unidades, decenas y centenas
;a su valor hexadecimal ascii
;*********************************************

;.---------------------------Subrutinas de ajuste
ajusta:            
ajusta_unidades:
                LDHX    #$90                    ;apuntamos a la memoria $80, que es donde empiezan los valores de ajuste
                MOV     #$90,tope               ;colocamos $80 en la memoria que servira de tope
                LDA     unidades
                ADD     tope                    ;cargamos las unidades y las sumamos a tope obteniendo asi ($80+unidades) la direccion del numero para ajustar
                STA     tope                
ciclo_unidades:                             
                feed_watchdog                   ;recorremos desde la memoria $80 hasta que llegemos a la memoria tope($80+unidades)
                CPX     tope  
                BEQ     salir_unidades
                INCX
                BRA     ciclo_unidades               
salir_unidades:                                
                LDA   ,X
                STA   unidadesA                 ;el valor de la memoria tope la guardamos en unidadesA el cual sera el valor ajustado de la unidad
                
                
ajusta_decenas:
                LDHX    #$90                    ;misma explicacion que para unidades pero con decenas
                MOV     #$90,tope
                LDA     decenas
                ADD     tope
                STA     tope                
ciclo_decenas:
                feed_watchdog
                CPX     tope  
                BEQ     salir_decenas
                INCX
                BRA     ciclo_decenas               
salir_decenas:                                
                LDA   ,X
                STA   decenasA                       

                RTS
;*************************************************
;TERMINAN LAS SUBRUTINAS DE AJUSTE

En la siguiente imagen se muestran las conexiones de los dispositivos usados:

En la siguiente imagen se observan los dos archivos que contienen ambos códigos en el programa (CodeWarrior IDE):


En la siguiente imagen se observa el debug del programa en decimal, el cual corre sin ningun mensaje de error o advertencia:


En la imagen de abajo el debug del código en Hexadecimal, sin errores o advertencias y corriendo:



Por cosas del destino parece ser que hay veces que no funciona la conexión serial
y puede deberse a la conexión del MAX233, recomiendo mejor usar el siguiente circuito:



Debido a la hoja de datos del MAX233


Powered by iGus

miércoles, 9 de noviembre de 2011

Generacion de PWM con PIC (Pic C de CSS)

Generacion de PWM con PIC (Pic C de CSS)

En muchas ocasiones necesitamos generar una onda PWM, y lo unico que deseamos ver en esa onda, no queremos calcular nada, no queremos rompernos la cabeza pensando en calcular Periodos y frecuencias, lo único que deseamos es ver en un osciloscopio un tren de pulsos PWM y ya!!!!!

Pues aquí el Santo Remedio!!!!!!!!!!!!!!!!!

En primera para que no les digan y no les cuenten, se esta usando la IDE PCWHD de CCS t programando en Pic C.

Usamos un Pic 16f627a, sus características son modestas, y también su costo, esto como parte de un proyecto más grande que después presentaré.





Generacion Rapida de PWM con PIC 
Frecuencia Constante y DutyCicle Variable


#include <16f627a.h>
#fuses INTRC, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP 
#use delay(clock=4000000)

void main()
{
   int i = 0;

   output_low(PIN_B3); //CCP1
   setup_ccp1(CCP_PWM);

   setup_timer_2(T2_DIV_BY_16, 255, 1);


   while(1)
   {
     
      for(i=0; i < 255; i++)
      {
         set_pwm1_duty(i);
         delay_ms(5);
      }
      for(i = 255; i > 0; i--)
      {
         set_pwm1_duty(i);
         delay_ms(5);
      }      
   }
}

Esquemático:
Esquematico en Proteus Isis
Descarga: Diseño de Isis Proteus Simulado


INFO:


Explico el código arriba presentado:

  • La función setup_timer_2(T2_DIV_BY_16, 255, 1) programa el timer2 del micro,  el primer parámetro T2_DIV_BY_16 es un preescaler, es decir, un divisor, divide la frecuencia del micro entre 16, así como T2_DIV_BY_4 divide entre 4. 
  • El segundo parámetro (255), es un numero de 8bits (de 0 a 255) que equivale al periodo   que deseas de tu PWM, si colocamos 255 quiere decir que deseamos todo el periodo que tu oscilador divido por el preescaler pude dar, si colocamos 123 indicamos que queremos la mitad del mismo y así.
  • El tercer parámetro son las veces que el timer se reiniciará antes de una interrupción, esto es, las veces que habrá una transición de nivel bajo a alto de tu PWM antes de una interrupción.



Generación Rapida de PWM con PIC 
Frecuencia Variable y DutyCicle Constante (50%)


#include <16f627a.h>
#fuses INTRC, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP 
#use delay(clock=4000000)

void main()
{
   int i = 0;

   output_low(PIN_B3); //CCP1
   setup_ccp1(CCP_PWM);
   setup_timer_2(T2_DIV_BY_16, 255, 1); 


   while(1)
   {
       for(i=0; i < 255; i++)
      {
         setup_timer_2(T2_DIV_BY_16, i, 1);
         set_pwm1_duty(i/2);
         delay_ms(5);
      }      
   }
}



DESCARGA: DISEÑO ISIS PROTEUS SIMULACION


De aqui puedes descargar las referencias del CCS (CCS manual):
http://hzsquare.blogspot.com/2011/10/libros-de-pics.html
o de su pagina oficial

martes, 8 de noviembre de 2011

Oscilador Interno de PIC (Pic C CCS) y #FUSES

Oscilador Interno de PIC (Pic C CCS)
Haz Parpadear un LED

Cuentas veces tenemos proyectos escolares o de trabajo donde no es necesaria tanta precisión en cuestiones de tiempo, es decir, no tenemos comunicación USB ni comunicación Serial o PWM que necesiten tiempos exactos de apagado - encendido y no queremos colocar un Oscilador externo, porque en primera, cuestan, da flojera conectarlos, ocupan espacio etc...

Bien ahora lo bueno, podemos usar el oscilador interno de nuestro PIC, si es que nuestro pic lo permite, que ahora la gran mayoría lo hace, antes que nada, voy a colocar un poco de introducción a los FUSES, así es, Fusibles en español, que son registros en el Pic que le indican al PIC como comportarse (tiene que ser buen chico!!! LOL ).






Coloco el siguiente fragmento:
La palabra fuse viene a significar fusible. Sonará bastante raro, que un PIC pueda tener fusibles, pero nos sonará más familiar cuando comprendamos su funcionamiento. Hay ciertos aspectos del PIC que han de ser activados o desactivados mediante hardware a la hora de programarlo. Esto quiere decir que no se pueden volver a cambiar  hasta que el chip no se reprograme de nuevo. Como no se pueden cambiar, de ahí le viene que sea como un fusible, el cual han fundido intencionadamente para proteger el modo de funcionamiento del procesador. Esto suena a tontería, pero en la realidad se aplica con fusibles de verdad. 
Extraído de:  Fuente



Ahora, como sabemos, de cajón tenemos que colocar un oscilador, así que hay que decidirnos el interno o el externo, como dice el título de esta entrada, usaremos en interno, por tanto el codigo para programar un PIC16f627A quedaría de la siguiente manera (el codigo se explica por sí mismo):




#include <16f627a.h>
#fuses NOWDT         //Deshabilita Watchdog
//#fuses INTXT            //Cristal Externo
//#fuses RC                //Oscilador RC (Resistencia Capacitor Externos)
//#fuses HS                //Modo Full Power de alta Velocidad, hasta 10MHZ, más depende el micro
//#fuses LP                //Modo Bajo Consumo hasta 200KHZ
#fuses INTRC         //Cristal Interno RC
#use delay(clock=4000000)
void main()
{
   set_tris_b(0);
   //setup_oscillator(OSC_4MHZ);       
      //No es necesaria esta instruccion debido a que al momento de usar INTRC y                    
      //delay(clock=...) el Oscilador interno se programa en automatico
   output_low(PIN_B1);
   
   while(1)
   {
      output_toggle(PIN_B1);           //Cambiamos el estado del Pin,
      delay_ms(100);                   //Esperamos a lo sonso 100 ms
   }
}

Pequeño esquematico

Recordar que una lista completa de los Fuses que soporta tu Pic está dentro del .h del pic mismo y un análisis más detallado lo encuentras en la datasheet del mismo.

Que disfruten!!!!!!!!

Control de Temperatura por PWM usando PIC (Pic C CCS)


Control de Temperatura por PWM usando PIC (Pic C CCS) con protección contra cortocircuito.


Imagen del circuito





Imagen de Muestra del Circuito de Control y Potencia en Proto




Parto de dos sencillas ideas:

  1. Un MOSFET permite la coducción de corriente de Source a Drain en proporción directa al voltaje existente en la base, por tanto se dice que el MOSFET es un dispositivo controlado por voltaje a diferencia del BJT que es controlado por corriente. (MOSFET)
  2. Un microcontrolador puede ser programado para mandar una señal PWM a frecuencia y DutyCicle variables. Si sabemos que podemos variar el voltaje promedio de esa señal PWM a la salida del microcontrolador (de 0v a 5v), amplificando esa señal y mandándola a la base de un MOSFET lograríamos controlar en cierto rango controlar la corriente que pasa de Source a Drain en el mismo.
Con las dos ideas anteriores podemos partir para hacer un controlador de corriente, pero para hacer esto un poco más práctico, si hacemos pasar esta corriente controlada por una resistencia de carga haríamos que esta se calentara, logrando ahora un control de la temperatura de la resistencia al variar la corriente que pasa a través de esta.

Bien, para que todo esto funcione, dividiré el desarrollo del proyecto en dos etapas:

  1. Etapa de control
  2. Etapa de Potencia (se incluye la protección contra cortos)




Etapa de Control

Material:
  • 4 potenciometros de 1kohm
  • 2 leds
  • 3 resistencias 330 ohms
  • 1 pic16f887
  • 1 pantalla lcd (JHD 162A)
  • 1 LM35 (sensor de Temperatura de Precisión)


Usando un microcontrolador PIC 16f887 lograremos hacer un control del DutyCicle del PWM conforme a la temperatura. Conforme la temperatura aumenta el DutyCicle del PWM disminuirá de tal forma que cuando la resistencia llegue a la temperatura deseada el DutyCicle será 0% por tanto la corriente que pasará a través de la resistencia será 0A haciendo que la temperatura se mantenga estable en ese punto, hasta que esta se enfríe y la señal PWM vuelva a polarizar el MOSFET.

He aquí el esquemático:

IMG1: Control de Temperatura, Etapa de Control

Como se muestra en la imagen IMG1 pueden armar el circuito de control de temperatura y simularlo en Isis Proteus descargando los archivos de DESCARGA1

Al momento de simularlo pueden:

  1. Variar la frecuencia de la señal PWM por medio del potenciómetro conectado a RA0.
  2. Variar la Temperatura por medio del sensor LM35 y ver como el DutyCicle del PWM disminuye conforme la temperatura se acerca al SetPoint (Temperatura deseada)
  3. Variar el SetPoint de 0ºC a 150ºC por medio del potenciómetro conectado a RA1.
IMG2: Muestra de la Simulación en Proteus


Codificación en C (Pic C de CCS):
Pantalla muestra del Codigo en PIC C de CCS PCWHD




/*
Hazael Fernando Mojica García
Carlos Alfredo Alanis González
Adriana Rodríguez Liñán
San Nicolás de los Garza 3/Nov/2011
*/

/*
Programa de Control de Temperatura con un PIC
Permite variar el DutyCicle y Frecuencia de una señal PWM
que controla la corriente que recibe una resistencia de carga

La Temperatura a la que llega dicha resistencia se puede variar de 0 a 150ºC (SetPoint)
La frecuencia del PWM va de 0Hz a 1KHz (Teoricamente ya que practicamente nunca baja de 477Hz)

INFO:
El comportamiento es simple, por medio del sensor LM35 se procede a medir la temperatura
si esta no ha llegado aún a la temperatura deseada (SetPoint) manda un PWM con un DutyCicle
proporcional a la diferencia entre la temperatura medida y la deseada, si la diferencia es grande
el DutyCicle tambíen lo será y viceversa
Al llegar a la teperatura deseada el Control la mantiene.

El circuito está diseñado sólo para calentar no para enfriar por tanto
si se elige un SetPoint abajo de temperatura ambiente, el DutyCicle siempre
permanecerá en 0%.

*/


#include <16f887.h>                   //Nuestro pic
#device adc = 10                       //Convertidor AD a 10bits
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay (clock=8000000)            //Fosc=8Mhz
#include <lcd.c>                      //Libreria de la lcd

//Declaracion de variables publicas
float TOC;
float Prescaler;


//Prototipos de Funciones
void Refresh_lcd(float _Frecuencia_, float _DutyCicle_, float _SP_, float _Temperatura_);
float Get_Lectura(int channel);
float Relacion_LineaRecta_m(float m, float valorEjeIndep);
float Calcula_PR2(float _Frecuencia);
void Refresh_PWM_Duty(float _DCValue);
void Refresh_PWM_Frec(float _Period2);
float Calcula_Value_DC(float _SP_, float _Temp_);
float CalculaPendiente (float Xmin, float Ymin, float Xmax, float Ymax);


void main()
{
   //int i = 0;

   float Value_DC = 0.0;
   float bits_frecuencia = 0.0;
   float bits_temperatura = 0.0;
   float bits_setPoint = 0.0;
   float bits_dutyCicle = 0.0;
   float SetPoint = 150.0;         //Valor que se desea de Temperatura
   float DutyCicle = 0.0;
   float Frecuencia = 0.0;
   float Temperatura = 0.0;
   float mFrec = 0.977517106;       //Pendiente de la recta para Frecuencia m = (1000 - 0)/(1023 - 0)
   float mT = 0.65625;                  //Pendiente de la temperatura m = ( 21 - 0 ) / ( 32 - 0 )
   float mVDC = 0.0;                 
   //float mDC = 0.000977517065;      //Pendiente de la recta para DutCicle m = (1 - 0)/(1023 - 0)
   float mDC = 0.392156862;         //Pendiente de la recta para DutCicle m = (100 - 0)/(255 - 0)
   float mSP = 0.146627566;         //Pendiente de la recta para SetPoint m = (150 - 0)/(1023 - 0)
  
  

   float Period2 = 0.0;
   float PWMValue = 0.0;
   float Period2_ = 0.0;      //Valor de Period2 inmediato anterior
   float PWMValue_ = 0.0;     //Valor de PWMValue inmediato anterior

   TOC = 0.000000125;                     // TOC = 1 / clock = 1 / 8000000
   Prescaler = 16.0;

   //Programar el oscilador interno del pic y usar CCP afecta al PWM
   //setup_oscillator(OSC_8MHZ|OSC_INTRC);   //Programamos oscilador interno del Pic a 8MHZ
   //setup_oscillator(OSC_8MHZ);
   set_tris_b(0);                            //Puerto B como salida
   output_low(PIN_C2);                       // Set CCP1 output low
   setup_ccp1(CCP_PWM);                      // Configure CCP1 as a PWM
   lcd_init();                               //Inicializamos LCD
   lcd_putc("Iniciando...");                //"Hola Mundo"
                      
  
   //Frecuencia del PWM
   setup_timer_2(T2_DIV_BY_16, 249, 1);  // 500 Hz
   set_pwm1_duty(500);
  
  
   delay_ms(5);
  
   while(1)
   {
  
      output_toggle(PIN_B5);              //Parpadeamos Led, output_toggle cambia el estado logico del pin pasado de parametro
    
      //Obtenemos las lecturas del convertidor A/D para cada parametro de entrada (frecuencia, temperatura y setPoint)
      bits_frecuencia = Get_Lectura(0);
      bits_temperatura = Get_Lectura(1);
      bits_setPoint = Get_Lectura(4);
    
      //Obtenemos el valor del eje dependienta pasando el valor del eje independiente (bits) y el valor de la pendiente
      Frecuencia = Relacion_LineaRecta_m(mFrec, bits_frecuencia);
      SetPoint = Relacion_Linearecta_m(mSP, bits_setPoint);
      Temperatura = Relacion_Linearecta_m(mT, bits_temperatura);
    
      //Calculamos el valor del DC
      Value_DC = Calcula_Value_DC(SetPoint, Temperatura);
    
      mVDC = CalculaPendiente(0, 0, SetPoint, 255);//Calculamos la pendiente
    
      bits_dutyCicle = Relacion_Linearecta_m(mVDC, Value_DC);
    
      DutyCicle = Relacion_Linearecta_m(mDC, bits_dutyCicle);

      PWMValue = bits_dutyCicle;
  
      Period2 = Calcula_PR2(Frecuencia);

    
    
      if(PWMValue != PWMValue_)
         {Refresh_PWM_Duty(PWMValue);}
        
      if(Period2 != Period2_)
         {Refresh_PWM_Frec(Period2);}
    
      Refresh_lcd(Frecuencia, DutyCicle, SetPoint, Temperatura);

      PWMValue_ = PWMValue;
      Period2_ = Period2;
    
      delay_ms(10);                     //Esperamos a lo sonso 250 ms
   }
}

//Metodo que calcula y regresa la pendiente para una recta que posee los puntos
//(Xmax,Ymax) y (Xmin,Ymin)
float CalculaPendiente (float Xmin, float Ymin, float Xmax, float Ymax)
{
   float m = 0.0;
   float num = 0.0;
   float den = 0.0;
    
   num = Ymax - Ymin;
   den = Xmax - Xmin;
  
   m = num / den;

   return m;
}


//Metodo que calcula el valor del DutyCicle que se enviará
//No es más que la diferencia entre el valor de temperatura que
//se desea (SetPoint _SP_) y el valor de temperatura actual (_Temp_)
float Calcula_Value_DC(float _SP_, float _Temp_)
{
   float Value_DC = 0.0;
   Value_DC = _SP_ - _Temp_;
  
   if(Value_DC < 0)
   { Value_DC = 0; }

   return Value_DC;
}

//Asigna un nuevo valor de DutyCicle al PWM usado
void Refresh_PWM_Duty(float _DCValue)
{
   set_pwm1_duty((int)_DCValue);
}

//Asigna un nuevo valor de frecuencia al PWM
void Refresh_PWM_Frec(float _Period2)
{
   setup_timer_2(T2_DIV_BY_16, _Period2, 1);
}


//Calcula el valor del Periodo(PR2) para la frecuencia
//pasada de parametro
float Calcula_PR2(float _Frecuencia)
{
   float _PR2 = 0.0;
   float div = 0.0;
  
   div = _Frecuencia * 4 * TOC * Prescaler;
   _PR2 = (1 / div) - 1;
  
   if(_PR2 < 0.0)
     { _PR2 = 0.0;}

   return _PR2;
}

//Metodo que regresa el valor del eje dependiente para un valor
//del eje independiente (valorEjeIndep) de una recta de pendiente m
//que pasa por el origen
float Relacion_LineaRecta_m(float m, float valorEjeIndep)
{
   float Valor = 0.0;
   Valor = (m * valorEjeIndep);        //Calculamos el Valor del eje dependiente
   return Valor;
}

//Obitiene una lectura del canal analógico pasado de parámetro
float Get_Lectura(int channel)
{
   float medicion=0;                     //variable entera que contendra la medicion del convertidor AD
   int1 done = 0;                        //Contendra 1 si el convertidor terminó de convertir
  
   setup_adc (adc_clock_internal);     //Usando el oscilador interno
   setup_adc_ports (all_analog);       //Todo el puerto analogico como entrada analogica   
   set_adc_channel (channel);          //Establecemos el canal de lectura analogica
   delay_ms(1);                       //a small delay is required after setting the channel
   medicion=read_adc ();               // Hace conversión AD
   done = adc_done();                  //done = 1 si el convertidor termino de convertir
   while(!done)
   {
      done = adc_done();               //Mientras no acabe de convertir se seguira ciclando
   }
   setup_adc (adc_off);                //detenemos al convertidor
   return medicion;                    //Regresamos la medicion
}


//Reasigana los valores en la pantalla lcd
void Refresh_lcd(float _Frecuencia_, float _DutyCicle_, float _SP_, float _Temperatura_)
{
   lcd_putc("\f");
   printf(lcd_putc,"F:%4.1f DC:%4.1f", _Frecuencia_,_DutyCicle_);
   lcd_putc("%");
   lcd_putc("\n");
   printf(lcd_putc,"SP:%4.1f T:%4.1f", _SP_, _Temperatura_);
}






Descarga2: Codigo en C para PIC C de CCS




Con respecto a la codificación cabe aclarar ciertas cosas:

  1. Se dejan comentadas ciertas lineas de codigo que no se usaron al final debido a que causaban errores o no era conveniente usarlas, esto por si alguien que desea modificar el código no cometa el mismo error.
    1. Oscilador Interno: Aún no estoy muy seguro del porque, pero la linea: //setup_oscillator(OSC_8MHZ|OSC_INTRC);   //Programamos oscilador interno del Pic a 8MHZ Me ocasionó que el PWM se saliera de control y actuara como loco.
  2. Se necesitan hacer calibraciones en la variable mT (linea 67) del programa de acuerdo a tu circuito y dispositivos (esta variable representa la pendiente que se forma en la recta Temperatura vs Bits del ADC), es sencillo, sigue estos pasos:
    1. Una vez tengas el circuito armado, cambia la linea 135 de codigo en el programa:
      1. Refresh_lcd(Frecuencia, DutyCicle, SetPoint, Temperatura);
      2. por: Refresh_lcd(Frecuencia, DutyCicle, SetPoint, bits_temperatura);
    2. Compila y programa el PIC
    3. Conectalo a VCC (5v) y ponlo a funcionar.
    4. Veras que en la pantalla LCD se mostrarán ahora los bits que arroja el convertidor analógico digital, si estas a Temperatura ambiente normalmente no pasan de 45 bits (si pasa de 45 es porque estas a una temperatura mayor no que este mal ok!!!). T: 45 como se ve en la simulación en ISIS IMG3. A este valor le llamaremos bits_Temperatura
    5. Mide el voltaje (con un Voltímetro) que te entrega el LM35 y deduce la temperatura a la que te encuentras. Ejemplo: si dice 0.021V están a 21ºC, si dice 0.035V estan a 35ºC que calor!!!!. A este valor le llamaremos Temperatura
    6. Haz la división sencilla de de Temperatura entre bits_Temperatura: Temperatura / bits_Temperatura.
    7. Coloca el valor de esa división en la linea correspondiente (linea 66), así: float mT = Valor_Division;
      1. Ejemplo: Para Temperatura = 21ºC y bits_Temperatura = 32, Valor_División = Temperatura / bits_Temperatura = 0.65625, por tanto la linea 65 es : float mT = 0.65625;
    8. Regresas la linea 135 a su valor original, compilas y Voila!!!!, ya tienes tu etapa de control lista.

IMG3: Calibrando el sensor de Temperatura
Si no tienes PIC C de CCS puedes descargarlo de aqui:
DESCARGA3: PIC C CCS






Etapa de Potencia

Como he explicado anteriormente esta etapa de potencia es muy sencilla, tomamos un MOSFET, sabemos que el MOSFET es un dispositivo controlado por voltaje, es decir, la corriente que pase de Source a Drain será proporcional al voltaje aplicado al Gate del mismo, sabiendo que el voltaje promedio que entrega un PWM depende de su DutyCicle donde un DutyCicle de 0% es 0V y uno de 100% es igual al voltaje por el cual es generado entonces podemos controlar la corriente. Esto lo hace fabuloso para poder usar un microcontrolador o algún otro dispositivo digital para generar y manipular esta señal y dado que el Gate del MOSFET está aislado físicamente del Source y Drain no tendremos que preocuparnos por aislar lo demás.


Hablando un poco sobre la Etapa de Potencia y la protección contra cortocircuito:




MATERIAL:



  • 2 resistencias 5Kohm
  • 1 interruptor (push button)
  • 1 relevador 
  • 1 transistor 2n3904 (o uno de mayor potencia, nosotros usamos un MOSFET IRF640)
  • 1 MOSFET IRFZ44
  • 1 Transformador 120V - 24V de 2Amperes
  • 2 Amplificadores Operacionales LM741
  • Puente de diodos de 4Amper
  • Una resistencia de Parrilla eléctrica, o regadera eléctrica (corta un pedazo de aprox unos 10cm y soldale cables para la protoboard)



IMG4: Diagrama de Protección contra Corto (el voltaje de alimentación
puede ser cualquier valor, siempre que los componentes lo permitan
se ejemplifica con 12V, para este proyecto se usan 24V) 


IMG5: Esquemático completo de la Etapa de Potencia con Protección contra Cortocircuitos
IMG7: Rectificación de Corriente Alterna

IMG8: Etapa de Potencia sin Proteccion contra Cortocircuitos


En la imagen IMG5 se aprecia la etapa de potencia completa, estamos emulando el voltaje promedio entregado por el PWM por medio del potenciómetro arriba a la derecha, este varía de 0 a 5v y es incrementado hasta 24v por medio de los amplificadores operacionales que no hacen otra cosa que llevarlo a saturación, si observamos con atención la salida de estos va directo al gate del MOSFET de Potencia IRFZ44, si no se requiere la protección contra cortocircuitos pueden quitarla y conectar el source del IRFZ44 directo a la salida del lado positivo del capacitor rectificador, después de los diodos tal como muestra la imagen IMG8.

Funcionamiento:

Como se puede observar en la figura, los 12V están conectados a la bobina del relevador y esta línea continua hacia el colector del transistor. La salida de nuestro circuito deberá de estar conectada al polo de nuestro relevador normalmente abierto. Así en un principio cuando no está polarizado nuestro transistor a la salida tendremos 0V.

Una vez que se presiona el push button el transistor se polariza, cerrando el circuito y la bobina del relevador se magnetizará, conmutando hacia el polo normalmente abierto, por lo cual ahora tendremos un voltaje a la salida de nuestro circuito.

Al tener un voltaje en la salida este se realimenta hacia la base del transistor manteniéndolo polarizado, por lo tanto no es necesario mantener presionado el push button.

Es importante aclarar que la protección contra cortocircuito solo se realiza después del relevador. Recordando que un cortocircuito sucede en el momento en que se cierra un circuito sin ninguna resistencia de por medio, es decir conectar la salida de nuestro circuito directamente a tierra. Observando la ley de Ohm:

I = V / R

Si la resistencia es igual a 0Ω, la corriente se dispararía hacia infinito.

En el momento que esto sucede, conectar la salida directamente a tierra, la base de nuestro transistor detecta 0V, por lo tanto se despolariza abriendo nuevamente el circuito e impidiendo que la bobina del relevador continúe magnetizada, regresando el polo a normalmente cerrada.

IMG: Implementando el circuito








Circuito Completo


Combinando ambas etapas obtenemos el circuito completo:


Esquemático del Circuito Completo