PicManía by RedRaven
 

Búsqueda personalizada

Experimentos

 

  Todo Picmaníaco que se precie ha de comenzar por hacer Experimentos, docenas de ellos, cientos de ellos ... aquí os propongo unos cuantos para ir sacándole punta al lápiz y prepararnos para acometer asuntos de mayor enjundia.
Experimentos básicos:
 

   Para la realización de los distintos Experimentos que os propongo en esta sección hace falta un hardware específico. Como no tenéis la obligación de tener mi misma placa de desarrollo Edumic, ni nada que se le parezca, he decidido añadir una página con la descripción de este Hardware de Experimentos para que podáis, si queréis, montarlo y seguir así avanzando en esto de los PIC. Las notas accesorias a cada uno de ellos hacen referencia a esta página.

   Para la primera fase de estos experimentos vamos a intentar usar como los idiomas de programación PicBasic Pro de microEngineering Labs, Inc.; CCS PIC C de CCS y el ensamblador MPASM de Microchip ... para asuntos de mas enjundia abandonaremos el Basic y nos centraremos en el PIC C aunque algunas rutinas las haremos en ensamblador.

   Todos los fuentes publicados han sido compilados, o ensamblados según el caso, y programado el .hex correspondiente en el micro que se indica. Así que ... ¡Funcionan!


 

  • Barled Encender y apagar consecutivamente todos los leds conectados al PORTB, en sentido ascendente, del pin 0 al 7, si el estado del pin 0 de PORTA es Alto y descendente, del pin 7 al 0, en caso contrario.
    (Ver BarLed y Display de 7 Segmentos)
  • 7 Segmentos Haciendo uso del mismo hardware vamos a contar de 0 a F, o de F a 0 según esté el pin 0 del PORTA, haciendo uso de un Display de 7 segmentos conectado íntegramente de los pines del PORTB.
    (Ver BarLed y Display de 7 Segmentos)

 

 


Experimento básico 1: Wink

 

  • Probablemente hacer parpadear un Led conectado a un pin del microcontrolador sea el primer experimento que todos debemos hacer al menos una vez en la vida. Es equivalente al stdout('Hello') de la programación de alto nivel. Aquí está pues tan básico experimento que nos va a servir para comprobar que nuestro ensamblador o compilador están a punto, que nuestro sistema de programación de PIC's también lo está y que nuestra placa de desarrollo o nuestro montaje funciona correctamente.

 

 
Ejemplo en Picbasic Pro para el PIC 16F628:
 
 
 
'****************************************************************
'* winkbasic.bas '* Ejemplo de programa que hace parpadear un LED '* conectado al PORTB.0 -> Picbasic Pro '****************************************************************

@ DEVICE PIC16F628,WDT_OFF,PWRT_ON,MCLR_ON,BOD_OFF,CPD_OFF,PROTECT_OFF

Define OSC 4

loop: High PORTB.0  ' Turn on LED       Pause 250     ' Delay for .25 seconds       Low PORTB.0   ' Turn off LED       Pause 250     ' Delay for .25 seconds       Goto loop     ' Go back to loop and blink LED forever
End
 
 

Fuentes de Winkbasic

 
 
Ejemplo en CCS PICC para el PIC 16F628 (usando delay()):
 
 
  //****************************************************************
//* winkc.c
//* Ejemplo de programa que hace parpadear un LED
//* conectado al PORTB.0
//****************************************************************

#include <16f628.h>                    // Selecciona el PIC
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT // Opciones de configuración

#use delay(clock=4000000)              // Velocidad del Cristal : 4 Mhz

#byte port_b=6                         // Dirección del PORTB

void main() {
   set_tris_b(0);                      // Configura los Pines de Port B como salida
   port_b=0;

   while(TRUE) {                       // Bucle infinito
      port_b=0;                        // Apaga todos los Leds
      delay_ms(500);                   // Espera medio segundo
      port_b=1;                        // Enciende el bit 0.
      delay_ms(500);                   // Espera otro medio segundo
   }
}
 
 

Fuentes de Winkc   El Rincón del C

 

 

 
Ejemplo en CCS PICC para el PIC 16F628 (usando interrupción RTCC):
 
 
  // winkintc.c

#include <16f628.h>                    // Selecciona el PIC
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT // Opciones de configuración
#use delay(clock=4000000)              // Velocidad del Cristal : 4 Mhz
#use standard_io(B)                    // PORTB en estándar IO digital
#use fixed_io(b_outputs=PIN_B0)        // B0 como salida en PORTB

byte const NInts=7;                    // Numero de interrupciones para 0.25 Segundos

// VARIABLES GLOBALES

char C_Ints=0;                         // Contador de Interrupciones ocurridas
char Flag=0;                           // Flag que cambia cada NInts interrupciones
char K=0;                              // Estado anterior del Flag


#int_RTCC                              // Interrupción por desbordamiento
RTCC_isr() {                           // del TIMER0 RTCC

  if(C_Ints > NInts){                  // Si las ints ocurridas > ints para 0.25 seg.

    if(Flag==0){
      Flag=1;
    }
    else{
      Flag=0;
    }
    C_Ints=0;                          // Reinicializo Contador de Ints
  }
  ++C_Ints;                            // Incremento el número de interrupciones
}                                      // Ocurridas



void main(void) {

  setup_counters(RTCC_INTERNAL,RTCC_DIV_128); // TIMER0: Clock Interno, Presescaler 128
  setup_timer_1(T1_DISABLED);                 // para una RTCC cada 33.3 milisegundos
  setup_timer_2(T2_DISABLED,0,1);             // -> 1 Segundo = 30 RTCC
  setup_comparator(NC_NC_NC_NC);
  setup_vref(FALSE);
  enable_interrupts(INT_RTCC);                // Habilito Interrupción RTCC
  enable_interrupts(global);                  // Habilito Interrupciones

  output_low(PIN_B0);                         // Empiezo apagando el Led


  do{ // Bucle infinito

    if(Flag!=K)
    {                                         // si ha cambiado Flag ...
      if(Flag==0){ output_low(PIN_B0); }      // Si es 0 Apago el Led
      if(Flag==1){ output_high(PIN_B0); }     // si es 1 Enciendo el Led
      k=Flag;                                 // Guardo estado anterior de Flag
    }

  }While(TRUE);
}
 
 
 

Fuentes de WinkIntc   El Rincón del C

 

 

 
Ejemplo en MPASM para el PIC 16F628:
 
 
  ;****************************************************************
;* winkasm.asm
;* Ejemplo de programa que hace parpadear un LED
;* conectado al PORTB.0
;****************************************************************

   LIST p=16F628         ;Decimos al ensamblador qué micro estamos usando
   include "P16F628.inc" ;Incluimos las definiciones de nuestro micro
   ERRORLEVEL 0, -302    ;suprime mensajes de "bank selection" al ensamblar
   __config 0x3D18       ;configuramos (oscilador, tipo etc.)

   cblock 0x20           ;Comienzo de los registros de propósito general
                         ;RAM en el Bank0
   count1                ;Usado en la rutina Delay
   counta                ;ídem
   countb                ;idem
   endc

   org 0x0000            ;org coloca el programa en el origen, 0x0000 para el 16F628,
                         ;Por aquí es por donde empieza el programa a ejecutarse
   movlw 0x07
   movwf CMCON           ;Deshabilito los comparadores

   bsf STATUS, RP0       ;Selecciono el Bank 1 para poder acceder a TRISB
   movlw b'00000000'     ;Configuro todo el PortB para que sea de salida
   movwf TRISB
   bcf STATUS, RP0       ;Selecciono de nuevo el Bank 0

Loop
   movlw b'00000001'     ;Pongo a '1' el primer bit del PortB
   movwf PORTB
   nop                   ;nop no hace nada (nada mas que incrementar el p.c.)
   nop ;
   call Delay            ;Llamo a la rutina que espera ...
   movlw b'00000000'     ;Pongo a '0' el primer bit del PortB
   movwf PORTB
   call Delay            ;Llamo de nuevo a la rutina que espera ...
   goto Loop             ;Salto al comienzo y así hasta el infinito

Delay
   movlw d'250'          ;Espero 250 ms (Para un reloj de 4 MHz)
   movwf count1
d1 movlw 0xC7
   movwf counta
   movlw 0x01
   movwf countb
Delay_0
   decfsz counta, f
   goto $+2
   decfsz countb, f
   goto Delay_0

   decfsz count1 ,f
   goto d1
   retlw 0x00

   end
 
 

Fuentes de Winkasm   El Rincón del Ensamblador

 

 

   Haciendo Click sobre la imagen inferior puede verse un momento de la simulación realizada con el programa anterior haciendo parpadear el Led conectado a PORTB.0. (La simulación está realizada con el PIC Simulator IDE 5.22 de Oshon Soft)

Nota: En el programa utilizado para la simulación se ha utilizado un delay mínimo para poder ver el cambio de estado del pin PORTB.0 dentro del margen del osciloscopio.
 

 
   Y por último podéis ver exactamente lo que ocurre en el pin utilizando el arma definitiva: un osciloscopio:
 

 


Experimento básico 2: Barled

 

  • Barled consiste en encender y apagar consecutivamente todos los leds conectados al PORTB, en sentido ascendente, del pin 0 al 7, si el estado del pin 0 de PORTA es Alto y descendente, del pin 7 al 0, en caso contrario.

 
 
Ejemplo en Picbasic Pro para el PIC 16F628:
 
 
 
'****************************************************************
'* barledbasic.bas
'* Ilumina consecutivamente los LEDs conectados al
'* PORTB 0..7
'* Si PORTA.0 es 1 Va hacia arriba (De PORTB.0 a .7)
'* Si PORTA.0 es 0 Va hacia abajo (De PORTB.7 a .0)
'****************************************************************

@ DEVICE PIC16F628,WDT_OFF,PWRT_ON,MCLR_ON,BOD_OFF,CPD_OFF,PROTECT_OFF

Define OSC 4

BARLED Var PORTB
BARLED1 Var PORTB.0
BARLED2 Var PORTB.1
BARLED3 Var PORTB.2
BARLED4 Var PORTB.3
BARLED5 Var PORTB.4
BARLED6 Var PORTB.5
BARLED7 Var PORTB.6
BARLED8 Var PORTB.7

Inicio:
   CMCON = %00000111 ' Pines PortA a Digital (No comparator)
   TRISA = %00000001 ' El pin 0 de PORTA a Entrada
   TRISB = %00000000 ' Todos los pines del PORTB a Salida
   Gosub WashBarLed  ' Pone a Low todos los BARLED

Loop:
   if PORTA.0 = 1 then
   ' Hacia Arriba De BARLED1 a BARLED8
     High BARLED1 : Gosub WashBarLed
     High BARLED2 : Gosub WashBarLed
     High BARLED3 : Gosub WashBarLed
     High BARLED4 : Gosub WashBarLed
     High BARLED5 : Gosub WashBarLed
     High BARLED6 : Gosub WashBarLed
     High BARLED7 : Gosub WashBarLed
     High BARLED8 : Gosub WashBarLed
   else
     ' Hacia Abajo De BARLED8 a BARLED1
     High BARLED8 : Gosub WashBarLed
     High BARLED7 : Gosub WashBarLed
     High BARLED6 : Gosub WashBarLed
     High BARLED5 : Gosub WashBarLed
     High BARLED4 : Gosub WashBarLed
     High BARLED3 : Gosub WashBarLed
     High BARLED2 : Gosub WashBarLed
     High BARLED1 : Gosub WashBarLed
   Endif

Goto Loop

WashBarLed:
   Pause 500
   BARLED = %00000000
   return

End  
 
 

Fuentes de Barledbasic

 
 
Ejemplo en MPASM para el PIC 16F628:
 
 
  ;****************************************************************
;* barledasm.asm
;* Ilumina consecutivamente los LEDs conectados al
;* PORTB 0..7
;* Si PORTA.0 es 1 Va hacia arriba (De PORTB.0 a .7)
;* Si PORTA.0 es 0 Va hacia abajo (De PORTB.7 a .0)
;****************************************************************

   LIST p=16F628         ;Decimos al ensamblador qué micro estamos usando
   include "P16F628.inc" ;Incluimos las definiciones de nuestro micro
   ERRORLEVEL 0, -302 ;suprime mensajes de "bank selection" al ensamblar
   __config 0x3D18       ;configuramos (oscilador, tipo etc.)

   cblock 0x20           ;Comienzo de los registros de proposito general
                         ;RAM en el Bank0
        count1           ;Usado en la rutina Delay
        counta           ;idem
        countb           ;idem
   endc

   org 0x0000            ;org coloca el programa en el origen, 0x0000 para el 16F628,
                         ;Por aquí es por donde empieza el programa a ejecutarse
   movlw 0x07
   movwf CMCON           ;Deshabilito los comparadores
   bcf STATUS, IRP
   bcf STATUS, RP1
   bsf STATUS, RP0       ;Selecciono el Bank 1 para poder acceder a TRISA y TRISB
   movlw 0x00
   movwf TRISB           ;Configuro todo el PortB para que sea de salida
   movlw b'00000001'
   movwf TRISA           ;Configuro el bit 0 de PortA para que sea de entrada
   bcf STATUS, RP1
   bcf STATUS, RP0       ;Selecciono de nuevo el Bank 0
   Call WshBarled        ;Pongo a cero todo el PortB

Loop
   btfss PORTA, 0        ;Salta si el bit 0 de PortA es '1'
   Call Up
   btfsc PORTA, 0        ;Salta si el bit 0 de PortA es '0'
   Call Down
   goto Loop             ;Salto al comienzo y así hasta el infinito

Up
   Call Pb0              ;Poner a '1' el Bit 0 de PortB el resto a '0'
   Call Pb1              ;Poner a '1' el Bit 1 de PortB el resto a '0'
   Call Pb2              ;Poner a '1' el Bit 2 de PortB el resto a '0'
   Call Pb3              ;Poner a '1' el Bit 3 de PortB el resto a '0'
   Call Pb4              ;Poner a '1' el Bit 4 de PortB el resto a '0'
   Call Pb5              ;Poner a '1' el Bit 5 de PortB el resto a '0'
   Call Pb6              ;Poner a '1' el Bit 6 de PortB el resto a '0'
   Call Pb7              ;Poner a '1' el Bit 7 de PortB el resto a '0'
   retlw 0x00

Down
   Call Pb7              ;Poner a '1' el Bit 7 de PortB el resto a '0'
   Call Pb6              ;Poner a '1' el Bit 6 de PortB el resto a '0'
   Call Pb5              ;Poner a '1' el Bit 5 de PortB el resto a '0'
   Call Pb4              ;Poner a '1' el Bit 4 de PortB el resto a '0'
   Call Pb3              ;Poner a '1' el Bit 3 de PortB el resto a '0'
   Call Pb2              ;Poner a '1' el Bit 2 de PortB el resto a '0'
   Call Pb1              ;Poner a '1' el Bit 1 de PortB el resto a '0'
   Call Pb0              ;Poner a '1' el Bit 0 de PortB el resto a '0'
   retlw 0x00

Pb0
   movlw b'00000001'     ;Pongo a '1' el bit 0 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

Pb1
   movlw b'00000010'     ;Pongo a '1' el bit 1 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

Pb2
   movlw b'00000100'     ;Pongo a '1' el bit 2 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

Pb3
   movlw b'00001000'     ;Pongo a '1' el bit 3 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

Pb4
   movlw b'00010000'     ;Pongo a '1' el bit 4 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

Pb5
   movlw b'00100000'     ;Pongo a '1' el bit 5 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

Pb6
   movlw b'01000000'     ;Pongo a '1' el bit 6 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

Pb7
   movlw b'10000000'     ;Pongo a '1' el bit 7 de PortB
   call SendPB           ;Manda al Port B, espera, limpia, espera y vuelve
   retlw 0x00

SendPB
   movwf PORTB           ;Mando el contenido de W al PortB
   call Delay            ;Llamo a la rutina que espera ...
   call WshBarled        ;Llamo a la rutina que pone a cero PortB
   call Delay            ;Llamo de nuevo a la rutina que espera ...
   retlw 0x00

WshBarled
   movlw b'00000000'     ;Pongo a '0' todos los bits de PortB
   movwf PORTB
   retlw 0x00

Delay
   movlw d'250'          ;Espero 250 ms (Para un reloj de 4 MHz)
   movwf count1
d1 movlw 0xC7
   movwf counta
   movlw 0x01
   movwf countb
Delay_0
   decfsz counta, f
   goto $+2
   decfsz countb, f
   goto Delay_0

   decfsz count1 ,f
   goto d1
   retlw 0x00

   end
    
 
 

Fuentes de Barledasm   El Rincón del Ensamblador

 

 


Experimento básico 3: 7 Segmentos


  •    El único avance que vamos a alcanzar en 7 Segmentos consiste en mandar las tramas que dibujan cada número o letra en este tipo de displays. Cada pin del PORTB está conectado a cada uno de los terminales del Display en la forma 0-a 1-b 2-c 3-d 4-e 5-f 6-g y 7-dp (decimal point).
     


 

  •    Así un 0 (cero) se representa mediante la iluminación de los segmentos a b c d e y f por lo que deberemos sacar por el PORTB el número en binario '00111111'  pero si queremos representar por ejemplo la letra F debemos iluminar los segmentos a e f y g, o lo que es lo mismo mandar al PORTB un '01110001'. Fácil y sencillo como juego de chiquillo.
     
 
 
Ejemplo en Picbasic Pro para el PIC 16F628:
 
 
  '****************************************************************
'* 7segbasic.bas
'* Cuenta de 0 a F en un Display de 7 Segmentos
'* conectado directamente a PORTB
'* Si PORTA.0 1 Va hacia arriba (De 0 de F)
'* Si PORTA.0 0 Va hacia abajo (De F a 0)
'****************************************************************

@ DEVICE PIC16F628,WDT_OFF,PWRT_ON,MCLR_ON,BOD_OFF,CPD_OFF,PROTECT_OFF

Define OSC 4

'* Variables ****************************************************

MyNUM VAR Byte

'* Redefiniciones ***********************************************

V7SEG Var PORTB

'* Constantes ***************************************************

ND0 Con %00111111
ND1 Con %00000110
ND2 Con %01011011
ND3 Con %01001111
ND4 Con %01100110
ND5 Con %01101101
ND6 Con %01111101
ND7 Con %00000111
ND8 Con %01111111
ND9 Con %01100111
NDA Con %01110111
NDB Con %01111100
NDC Con %00111001
NDD Con %01011110
NDE Con %01111001
NDF Con %01110001
NDP Con %10000000
NDW Con %00000000

Inicio:
   CMCON = %00000111 ' Pines PortA a Digital (No comparator)
   TRISA = %00001111 ' Los pines 0 a 3 del PORTA a Entrada
   TRISB = %00000000 ' Todos los pines del PORTB a Salida
   Gosub WV7SEG ' Pone a Low todos los Segmentos
   Pause 1500 ' Espera 1.5 Segundos antes de comenzar
   MyNUM = 0
Loop:
   if PORTA.0 = 1 then
      Gosub IncMyNUM
   else
      Gosub DecMyNUM
   Endif
   Gosub WV7SEG
   Gosub Act7SEG
   Gosub Pausa
   Goto Loop

WV7SEG:
   V7SEG = NDW
   return

IncMyNUM:
   MyNUM = MyNUM + 1
   if MyNUM = 16 then MyNUM = 0
   return

DecMyNUM:
   if MyNUM = 0 then MyNUM = 16
   MyNUM = MyNUM - 1
   return

Act7SEG
   SELECT Case MyNUM
          Case 0
             V7SEG = ND0
          Case 1
             V7SEG = ND1
          Case 2
             V7SEG = ND2
          Case 3
             V7SEG = ND3
          Case 4
             V7SEG = ND4
          Case 5
             V7SEG = ND5
          Case 6
             V7SEG = ND6
          Case 7
             V7SEG = ND7
          Case 8
             V7SEG = ND8
          Case 9
             V7SEG = ND9
          Case 10
             V7SEG = NDA
          Case 11
             V7SEG = NDB
          Case 12
             V7SEG = NDC
          Case 13
             V7SEG = NDD
          Case 14
             V7SEG = NDE
          Case 15
             V7SEG = NDF
          Case Else
             V7SEG = NDP
   END SELECT
   return

Pausa:
   Pause 333
   return

End
  
 
 

Fuentes de 7SegBasic

 
 
Ejemplo en MPASM para el PIC 16F628:
 
 
  ;7SEGasm

   LIST p=16F628         ;Decimos al ensamblador qué micro estamos usando
   include "P16F628.inc" ;Incluimos las definiciones de nuestro micro
   ERRORLEVEL 0, -302    ;suprime mensajes de "bank selection" al ensamblar
   __config 0x3D18       ;configuramos (oscilador, tipo etc.)

   cblock 0x20           ;Comienzo de los registros de propósito general
                         ;RAM en el Bank0
      MiNum              ;Mi número contador
      count1             ;Usado en la rutina Delay
      counta             ;ídem
      countb             ;ídem
   endc

SEG_PORT Equ PORTB       ;Redefino Puerto donde conecto el Display 7Segmentos
SEG_TRIS Equ TRISB

   org 0x0000            ;org coloca el programa en el origen, 0x0000 para el 16F628,
                         ;Por aquí es por donde empieza el programa a ejecutarse
   movlw 0x07
   movwf CMCON           ;Deshabilito los comparadores
   bcf STATUS, IRP
   bcf STATUS, RP1
   bsf STATUS, RP0       ;Selecciono el Bank 1 para poder acceder a SEG_TRIS
   movlw 0x00
   movwf SEG_TRIS        ;Configuro todo el SEG_PORT para que sea de salida
   bcf STATUS, RP1
   bcf STATUS, RP0       ;Selecciono de nuevo el Bank 0
   clrf SEG_PORT         ;Limpio Display antes de empezar
Inicio
   movlw 0x00            ;Inicializo MiContador
   movwf MiNum

Main
   movfw MiNum           ;Cargo en W Offset actual
   Call Display
   Call Del250           ;Espero 1/2 segundo
   Call Del250
   incf MiNum, f         ;Incremento MiNumero
   movf MiNum, w         ;Cargo MiNum en W para testearlo y no pasarme de F
   sublw 0x10            ;Le resto 10 Hex
   btfss STATUS, Z       ;Si el resultado es cero salto a Inicio
   goto Main             ;En caso contrario a Main
   goto Inicio

Display
   Call LED_Table        ;Cargo W con el Pattern a dibujar
   movwf SEG_PORT        ;Envío el Pattern al PORT_SEG
   retlw 0x00

LED_Table
   addwf PCL,f           ;Sumo el Offset cargado al Program Counter (Low byte)
                         ;para saltar al retorno que me interesa
   retlw b'00111111'     ;0
   retlw b'00000110'     ;1
   retlw b'01011011'     ;2
   retlw b'01001111'     ;3
   retlw b'01100110'     ;4
   retlw b'01101101'     ;5
   retlw b'01111101'     ;6
   retlw b'00000111'     ;7
   retlw b'01111111'     ;8
   retlw b'01100111'     ;9
   retlw b'01110111'     ;A
   retlw b'01111100'     ;B
   retlw b'00111001'     ;C
   retlw b'01011110'     ;D
   retlw b'01111001'     ;E
   retlw b'01110001'     ;F


;** Rutinas de Delay *************************************

Del250
   movlw d'250' ;delay 250 ms
   goto Delay

Delay
   movwf count1
d1 movlw 0xC7 ;delay 1mS
   movwf counta
   movlw 0x01
   movwf countb
Delay_0
   decfsz counta, f
   goto $+2
   decfsz countb, f
   goto Delay_0

   decfsz count1 ,f
   goto d1
   retlw 0x00

   END
   
 
 

Fuentes de 7Segasm   El Rincón del Ensamblador

 

 


Experimento básico 4: 4 x 7 Segmentos multiplexados

 


 

  • Para este experimento solo debemos tener en cuenta que es el integrado 74LS48 el que se encarga de dibujar los dígitos en cada display. Nosotros solo debemos limitarnos a pasarle en binario el número que deseamos ver en el display. Tambien debemos tener en cuenta que los cuatro displays están conectados en paralelo y que para activar cada uno de ellos debemos poner en alto un bit distinto de un puerto.
     

  • De hecho los valores a representar, de 0 a 9, los vamos a escribir en los cuatro bits inferiores del PORTB, de RB0 a RB3 y que los cuatro superiores los vamos a utilizar, cada uno de ellos, para activar el display por donde queremos ver el dato.

 
 
Ejemplo en Picbasic Pro para el PIC 16F628:
 
 
  '****************************************************************
'* 7seg_x4.bas
'* Cuenta de 0000 a FFFF en cuatro Display's de 7 Segmentos
'* mediante Driver
'* RB0..RB3 Datos, RB4..RB7 Selector del Display multiplexado
'****************************************************************

@ DEVICE PIC16F628,WDT_OFF,PWRT_ON,MCLR_ON,BOD_OFF,CPD_OFF,PROTECT_OFF

Define OSC 4

i Var byte
j Var byte
k Var byte
l Var byte

Inicio:
TRISB = %00000000 ' Todos los pines del PORTB a Salida
Pause 1500 ' Espera 1.5 Segundos antes de comenzar

Loop:
  for l=9 to 0 step -1
    for k=9 to 0 step -1
      for j=9 to 0 step -1
        for i=9 to 0 step -1
          Gosub Muestra
        next i
      next j
    next k
  next l

Goto Loop

Muestra:
  PORTB = i
  PORTB.4=1 : PORTB.5=0 : PORTB.6=0 : PORTB.7=0 :Pause 4
  PORTB = j
  PORTB.4=0 : PORTB.5=1 : PORTB.6=0 : PORTB.7=0 :Pause 4
  PORTB = k
  PORTB.4=0 : PORTB.5=0 : PORTB.6=1 : PORTB.7=0 :Pause 4
  PORTB = l
  PORTB.4=0 : PORTB.5=0 : PORTB.6=0 : PORTB.7=1 :Pause 4
  return

End
 
 
 

Fuentes de 7Seg_x_4

 

 


Experimento básico 5: Interrupción Externa por RB0

 


  •    En los PIC's tenemos disponible una interrupción llamada Interrupción Externa que se dispara cuando un pulso lógico se presenta en el pin RB0. Por defecto, tras un Reset por ejemplo y si está habilitada esta interrupción se inicializa de forma que se dispare con el flanco de subida (rising edge), pero podemos hacer que se dispare con el de bajada (falling edge) con solo cambiar de 1 a 0 el bit 6 de OPTION_REG, el denominado bit INTEDG (interrput edge)
     
  •    Podemos utilizar esta interrupción para realizar algo en nuestro PIC con un nivel de control muy superior al de controlar si un PIN de entrada está a 1 o a 0 en un momento dado. Este experimento muestra como utilizar la interrupción externa de forma muy básica y sencilla.
     
  •    Para la realización de este experimento usamos el hardware específico de que dispongo en mi tarjeta Edumic y que describimos en Circuito para detección Interrupción Externa (Que no deja de ser un simple botón conectado al pin RB0 y a masa) El programa que os propongo está realizado en CCS PICC y lo que hace es marcar un FLAG cada vez que se produce la interrupción, y al mismo tiempo incrementar el número de veces que se produce. Con el FLAG en alto transmito por RS232 al PC el texto IntExt y el estado del Contador en ese momento y vuelvo a poner el FLAG en bajo...

 

 
Control de Interrupción Externa. Ejemplo en CCS PICC
 
 
  // ExtInt

#include <16f876a.h>                          // Selecciona el PIC
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT  // Opciones de configuración
#use delay(clock=4000000)                     // Velocidad del Cristal : 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// Definición del RS232


int fEXT=0x00;
int nEXT=0x00;

#int_EXT
EXT_isr() {
  fEXT=0x01;
  ++nEXT;
}

void main() {

  enable_interrupts(int_ext);
  enable_interrupts(global);

  nEXT=0x00;

  printf("\r\n\IntExt waiting ... \r\n");

  do {
    if(fEXT==0x01){
      fEXT=0x00;
      printf("IntExt %u \r\n",nEXT);
    }
  } while (TRUE);
}
 
 
 

Fuentes de ExtInt

 

 

  •    La ejecución de ExtInt en nuestro PIC podemos monitorizarlo desde nuestro PC y produce los efectos siguientes:
     


 

 


Experimento básico 6: Escribiendo en el LCD lo recibido por la RS-232


  •    Este experimento hace uso del hardware LCD de mi placa Edumic. También es imprescindible la librería flex_lcd.c publicada en esta misma página en la sección El Rincón del C.
     

 

 
Escribiendo en el LCD. Ejemplo en CCS PICC
 
 
 
#include <16f876a.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use standard_io(b)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#define LCD_DB4 PIN_B4
#define LCD_DB5 PIN_B5
#define LCD_DB6 PIN_B6
#define LCD_DB7 PIN_B7

#define LCD_RS PIN_C0
#define LCD_RW PIN_C1
#define LCD_E PIN_C2

#include "flex_lcd.c"


char Keypress=' ';

#int_rda
void serial_isr() {

  Keypress=0x00;
  if(kbhit()){
    Keypress=getc();
  }
}


void main() {

  enable_interrupts(global);
  enable_interrupts(int_rda);

  printf("\r\n\LCD driver monitor\r\n");

  lcd_init();

  do {

    if(Keypress!=0x00){
      lcd_putc(Keypress);
      putc(Keypress);
      Keypress=0x00;
    }

  } while (TRUE);
}
 
 
 

Fuentes de lcd_232.c

 

 

 

 


Experimento básico 7: Conectando con un periférico mediante I2C

  •    Este experimento hace uso del hardware Conversor AD/DA de mi placa Edumic, hace uso del integrado PCF8591 que es capaz de realizar conversiones Analógico a Digital y Digital a Analógico. Datasheet del PCF8591
     
  •    Su funcionamiento es muy básico ya que únicamente se dedica a enviar vía I2C los valores necesarios para una conversión Digital a Analógico.
     
  •    En nuestro caso vamos a generar una salida analógica variando desde 0V a 5v (Vcc) en 256 pasos sucesivos.  A la salida AOUT del PCF8591 nos aparecerá una preciosa onda de sierra.
     
  da_i2c.c  
 
#include <16f876a.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)

int  analogico=0x00;

#use i2c(master,sda=PIN_C4, scl=PIN_C3) // Configuración del I2C como Master
                                        // y los pines del MSSP en mi 16F876A.
void main() {

   i2c_start();           // Inicio la comunicación I2C
   i2c_write(0b10010000); // Envío Dirección I2C del PCF8591
   i2c_write(0b01000000); // Envío Configuración del PCF8591 para Conv. DA

   do {
      i2c_write(++analogico); // Envío Valor digital 0x00->0V, 0xFF->Vcc
   } while (TRUE);
}
 
 
 

Fuentes de da_i2c.c

 

 

 

 


Experimento básico 8: Conversión AD de luminosidad y temperatura.

  • La placa Edumic dispone también de una resistencia conectada eléctricamente a RB5 y situada físicamente junto al LM35A. Al activar RB5 la resistencia se calienta y podemos así detectar la correspondiente subida de temperatura en el LM35A.
     
  •    Su funcionamiento es muy básico ya que únicamente se dedica a realizar las dos conversiones AD en los pines AN0 y AN1 cuando le enviamos la correspondiente orden mediante el comando "t" vía RS232. Por este mismo medio podemos enviarle asimismo los comandos "1" y "0" para activar o desactivar la resistencia de calentamiento conectada a RB5.
     
  _ad_temperatura_lm35a_232.c  
 

#include <16f876a.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use standard_io(b)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

int adc_luminosidad=0x00;
int adc_temperatura=0x00;

int grados_temperatura=0;

char Keypress=' ';

#int_rda
void rda_isr() {

  Keypress=0x00;
  if(kbhit()){
    Keypress=getc();
  }
}


void toma_adc_y_transmite(void){

  // Lectura del canal 0 -> AN0 LDR
  set_adc_channel(0);
  delay_ms(1);
  adc_luminosidad=read_adc();
  delay_ms(1);

  // Lectura del canal 1 -> AN1 LM35a
  set_adc_channel(1);
  delay_ms(1);
  adc_temperatura=read_adc();
  delay_ms(1);

  grados_temperatura = (int) ((adc_temperatura * 391) / 1000);

  printf(" L = %u T = %u (adc= %u)\n",adc_luminosidad,grados_temperatura,adc_temperatura);
}

void main() {

  setup_adc(ADC_CLOCK_INTERNAL);
  setup_adc_ports(RA0_RA1_ANALOG_RA3_REF);
  output_low(PIN_B5);

  enable_interrupts(int_rda);
  enable_interrupts(global);


  printf("\n\ AD - LM35a - Monitor\n\n");

  do {

    if(Keypress!=0x00){

      switch(Keypress){

        case 't': toma_adc_y_transmite();
                  break;
        case '0': output_low(PIN_B5);
                  printf(" 0 - Calentador OFF\n");
                  break;
        case '1': output_high(PIN_B5);
                  printf(" 1 - Calentador ON\n");
                  break;
      }
      Keypress=0x00;
    }
  } while (TRUE);

}
 
 
 

Fuentes de ad_temperatura_lm35a_232.c

 
 
  •    En la imagen inferior puede verse el resultado de una serie de muestras tomadas a intervalos de varios segundos, conectando la resistencia de calentamiento y desconectándola posteriormente: (Los valores de T son en Grados  Centígrados)
     

 

 

 


Experimento básico 9: Rastreando un Teclado Matricial 4x4 y enviando a la RS232

 

  •    Como podéis comprobar el programa es muy simple y solo hace un rastreo continuo del teclado y en caso de detectar un tecla pulsada la envía al puerto serie RS232.
  KBD_Test en CCS PIC C  
 
#include <16f876a.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use fast_io(b)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#include "kbd_lib.c"

void main(){
   char mitecla=0;
   kbd_init();
   printf("\rKeyboard 4x4 monitor\r\r");
   do {
      mitecla=kbd_getc();
      if(mitecla!=0){
         putc(mitecla);
      }
   } while (TRUE);
}
 
 
 

 

 
 
  • Los resultados sobre el monitor serie del PC podeis verlo en la fotografía inferior:

 


Experimento básico 10: Escribiendo una EEPROM externa vía I2C

 
  •    Lo prometido es deuda, y aquí está. En nuestro artículo anterior Recibiendo del RS232 sobre un Buffer y procesandolo posteriormente hablábamos de una aplicación para estos comandos. La idea que me asaltó fue la de hacer un menú estilo Telnet que me posibilitase comunicarme con el PIC desde el PC vía RS232 y al que pudiese dar ordenes complejas.
  •    Este experimeto va a mostrar como utilizar este método de comunicación para manejar una EEPROM externa al PIC y conectada a él vía I2C. Con nuestros comandos vamos a poder enviar datos (textos) al PIC para que los guarde en la EEPROM o reclamarle el contenido de la misma y que nos la presente en el PC.
  •    El programa va guardando lo recibido vía RS232 en un buffer. Con la tecla [Intro] mandamos procesar su contenido. Con [Retroceso] y [Escape] podemos borrar el último caracter del buffer o borrarlos todos respectivamente.
  •    Con /r leemos el contenido completo de la EEPROM, con /i dir escribimos un indice en la posición de memoria 00h de la EEPROM a partir de cual podemos escribir los datos enviados con /w data, que a su vez escribe el contenido data del buffer a partir de la posición que este escrita en 00h.
  • Con /B podemos formatear, borrar, todo el contenido de la EEPROM incluso la posición indice 00h poniendo todas sus posiciones a 0.
El programa queda tal como sigue:
 
  eeprom_rs232_i2c.c  
 
#include <16f876a.h>                          // Definiciones del PIC 16F876A
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT  // Los Fuses de siempre
#use delay(clock=4000000)                     // Oscilador a 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// Definición RS232 estándar
#use i2c(master,sda=PIN_C4, scl=PIN_C3)       // Definición I2C estándar
#include <ctype.h>
#include <string.h>

// CONSTANTES /////////////////////////////////////////////////////////////////

int const lenbuff=32;                  // Longitud de buffer, Ajustar
                                       // a lo que desees (o te sea posible)
int const address_EEPROMR=0b10100001;  // Dirección I2C de EEPROM (para lectura)
int const address_EEPROMW=0b10100000;  // Dirección I2C de EEPROM (para escritura)
                                       // 1010 000 0
                                       // ---- --- -
                                       //   |   |  R/W
                                       //   |  Hard
                                       // Fijo
int const ddeeeprom=32;                // Delay Default EEPROM

// VARIABLES EN RAM ///////////////////////////////////////////////////////////

int  xbuff=0x00;                       // Índice: siguiente char en cbuff
char cbuff[lenbuff];                   // Buffer
char rcvchar=0x00;                     // último carácter recibido
int1 flagcommand=0;                    // Flag para indicar comando disponible

// Declaración de Funciones ///////////////////////////////////////////////////

void presmenu(void);                   // Presenta el menú
void inicbuff(void);                   // Borra buffer
int  addcbuff(char c);                 // añade carácter recibido al buffer
void echos(char c);                    // Eco selectivo sobre RS232
void comando(void);                    // Procesa comando
int  ascii2hex(char d);                // Convierte un carácter ascii a hex
void i2cw1(int i2cdev, int i2cdir, int i2cdat); // Rutina de escritura I2C completa
void i2cw2(int i2cdev, int i2cdat);             // Rutina de escritura I2C parcial
int  i2cr(int i2cdev);                          // Rutina de lectura I2C

// INTERRUPCIONES /////////////////////////////////////////////////////////////

#int_rda
void serial_isr() {                    // Interrupción recepción serie USART
   rcvchar=0x00;                       // Inicializo carácter recibido
   if(kbhit()){                        // Si hay algo pendiente de recibir ...
      rcvchar=getc();                  // lo descargo y ...
      addcbuff(rcvchar);               // lo añado al buffer y ...
      echos(rcvchar);                  // hago eco (si procede).
   }
}

// Desarrollo de Funciones ////////////////////////////////////////////////////

void presmenu(void){                   // Presenta el menú --------------------
   delay_ms(25);
   printf("\r\n" );
   printf("** EEPROM I2C OS **\r\n\n" );  // Presenta menú
   printf("** Control del buffer:\r\n" );
   printf("[Enter]  Procesa comando\r\n" );
   printf("[Escape] Borra todo el buffer\r\n" );
   printf("[Delete] Borra último carácter del buffer\r\n" );
   printf("\n" );
   printf("** Comandos EEPROM:\r\n" );
   printf("/? Presenta Menú\r\n" );
   printf("/B Formatea (borra) eeprom iniciando <indice> a 0.\r\n" );
   printf("/r Lee contenido completo de eeprom y vuelca a RS232.\r\n" );
   printf("/w <dat> Escribe <dat> en eeprom a partir de <indice>.\r\n" );
   printf("/i <dir> Coloca índice de eeprom a <0xdir> sin borrar contenido.\r\n" );
   printf("\n" );
   delay_ms(25);
}

int addcbuff(char c){                  // Añade a cbuff -----------------------
      switch(c){
         case 0x0D:                    // Enter -> Habilita Flag para procesar
            flagcommand=1;             // Comando en Main
            break;
         case 0x08:                    // Del   -> Borra último carácter del Buffer
            if(xbuff>0) cbuff[--xbuff]=0x00;
            break;
         case 0x01B:                   // Esc   -> Borra el Buffer completamente
            inicbuff();
            break;
         default:
            cbuff[xbuff++]=c;          // Añade carácter recibido al Buffer
            if(xbuff>lenbuff) xbuff=lenbuff;
      }
}

void echos(char c){                    // Echo selectivo ----------------------
   int i;
   switch(c){
      case 0x0D: printf("\r\n" );       // Si he pulsado la tecla [Intro]
                 break;
      case 0x08: printf("\r%s \b",cbuff); // Si he pulsado la tecla [Retroceso]
                 break;
      case 0x1B: printf("\r" );         // Si he pulsado la tecla [Escape]
                 for(i=0;i<lenbuff;i++){
                   printf(" " );        // Borra display (en la longitud del buffer)
                 }
                 printf("\r" );
                 break;
      default:   putc(rcvchar);        // Echo de cualquier otro carácter
   }
}

void inicbuff(void){                   // Inicia a \0 cbuff -------------------
   int i;
   for(i=0;i<lenbuff;i++){             // Bucle que pone a 0 todos los
      cbuff[ i ]=0x00;                   // caracteres en el buffer
   }
   xbuff=0x00;                         // Inicializo el índice de siguiente
                                       // carácter
}

int  ascii2hex(char d){                // Convierte un carácter ascii a hex ---
   int r=0x00;
   if(isxdigit(d)){
      if(isdigit(d)){
         r=d-'0';
      }
      if(isalpha(d)){
         d=toupper(d);
         r=10+(d-'A');
      }
   }
   return(r);
}

void i2cw1(int i2cdev, int i2cdir, int i2cdat){ // Rutina de escritura I2C completa
   i2c_start();                  // Inicializo comunicación I2C
   i2c_write(i2cdev);            // Envío Dirección de dispositivo I2C + R/W
   i2c_write(i2cdir);            // Envió address eeprom donde escribir
   i2c_write(i2cdat);            // Envío byte a escribir
   i2c_stop();                   // Cierro comunicación
   delay_ms(ddeeeprom);          // Espero a que escriba correctamente
}

void i2cw2(int i2cdev, int i2cdat){             // Rutina de escritura I2C parcial
   i2c_start();                  // Inicializo comunicación I2C
   i2c_write(i2cdev);            // Envío Dirección de dispositivo I2C + R/W
   i2c_write(i2cdat);            // Envío byte a escribir
   i2c_stop();                   // Cierro comunicación
   delay_ms(ddeeeprom);          // Espero a que escriba correctamente
}

int  i2cr(int i2cdev){           // Rutina de lectura I2C
   int r=0x00;
   i2c_start();                    
   i2c_write(i2cdev);
   r=i2c_read();
   i2c_stop();
   delay_ms(ddeeeprom);
   return(r);
}

// Programa Principal /////////////////////////////////////////////////////////

void main() {
   inicbuff();                         // Borra buffer al inicio
   presmenu();                         // Presenta el menú
   enable_interrupts(int_rda);         // Habilita Interrupción RDA
   enable_interrupts(global);          // Habilita interrupciones
   delay_ms(25);
   do {
      if(flagcommand) comando();       // Si hay comando pendiente
                                       // de procesar ... lo procesa.
   } while (TRUE);
}

// Procesador de Comandos /////////////////////////////////////////////////////

void comando(void){
   int  i,j,u,h;
   int1 flagvalido=0;                  // Flag para detectar comandos inválidos
   char arg[lenbuff];                  // Argumento de comando (si lo tiene)
   disable_interrupts(int_rda);        // Deshabilito Interrupción RDA durante procesado
   flagcommand=0;                      // Desactivo flag de comando pendiente.
   for(i=0;i<lenbuff;i++){             // Limpia el argumento (por si lo hay)
      arg[ i ]=0x00;
   }
   // Comando /?
   if(cbuff[0]=='/'&&cbuff[1]=='?'){   // Comparo inicio del buffer con comando "/?"
      flagvalido=1;                    // Marco comando válido
      presmenu();                      // Presenta el menú
   }
   // Comando /B
   if(cbuff[0]=='/'&&cbuff[1]=='B'){   // Comparo inicio del buffer con comando "/B"
      flagvalido=1;                    // Marco comando válido
      printf("\r\nFormateando " );
      j=0;
      for(i=0;i<255;i++){
         i2cw1(address_EEPROMW,i,0x00);// Envío Dirección de dispositivo I2C + Escribir
                                       // Envío address eeprom donde escribir
                                       // Envío byte a escribir (0x00)
         if(++j>14){                   // Monitorizo en bloques de 15 bytes
            j=0;
            putc('.');
         }
      }
      printf("\r\nFormateado Ok.\r\n\r\n" );
   }
   // Comando /r
   if(cbuff[0]=='/'&&cbuff[1]=='r'){   // Comparo inicio del buffer con comando "/R"
      flagvalido=1;                    // Marco comando válido
      i2cw2(address_EEPROMW,0x00);     // Inicializo dirección a partir de la que leer
                                       // que fijo desde el primer byte
      for(i=0;i<16;i++) printf("%X ",i); // Pongo cabecera de direcciones
      printf("\r\n" );
      for(i=0;i<16;i++) printf("== " );
      printf("\r\n" );
      j=0;
      i2c_start();                     // Realizo la lectura completa de la EEPROM
      i2c_write(address_EEPROMR);
      for(i=0;i<255;i++){
         u=i2c_read();
         printf("%X ",u);              // y vuelco en bloques de 15 bytes
         if(++j>15){
            j=0;
            printf("\r\n" );
         }
      }
      i2c_stop();
      printf("\r\n\r\n" );
   }
   // Comando /w dat
   if(cbuff[0]=='/'&&cbuff[1]=='w'){   // Comparo inicio del buffer con comando "/w"
      flagvalido=1;                    // Marco comando válido
      i=3;
      do{                              // Extraemos argumento del buffer
         arg[i-3]=cbuff[ i ];            // a partir del 4º byte y hasta \0.
      }while(cbuff[++i]!=0x00);
      // recupero posición inicial <indice> para escribir
      i2cw2(address_EEPROMW,0x00);     // Inicializo dirección a partir de la que leer
                                       // que fijo desde el primer byte
      h=i2cr(address_EEPROMR);         // leo el valor de índice
      ++h;
      printf("\r\nEscribir %s a partir de %X\r\n\r\n",arg,h);
      i2cw2(address_EEPROMW,h);        // Inicializo dirección a partir de la que leer
                                       // que fijo desde el <indice>
      // escribo argumento a partir de índice
      i=0;
      do{
         i2cw1(address_EEPROMW,h,arg[ i ]);// Envío Dirección de dispositivo I2C + Escribir
                                         // Envío address eeprom donde escribir -> h
                                         // Envío byte a escribir -> arg[ i ]
         ++h;
         ++i;
      }while(arg[ i ]!=0x00);
      // Actualizo indice
      --h;
      i2cw1(address_EEPROMW,0x00,h);// Envío Dirección de dispositivo I2C + Escribir
                                    // Envío address eeprom donde escribir -> 0x00
                                    // Envío byte a escribir -> h
      printf("Buffer Escrito.\r\n\r\n" );
   }
   // Comando /i dir
   if(cbuff[0]=='/'&&cbuff[1]=='i'){   // Comparo inicio del buffer con comando "/i"
      flagvalido=1;                    // Marco comando válido
      i=3;
      do{                              // Extraemos argumento del buffer
         arg[i-3]=cbuff[ i ];            // a partir del 4º byte y hasta \0.
      }while(cbuff[++i]!=0x00);
      h=(16*ascii2hex(arg[0]))+(ascii2hex(arg[1])); // Convierto de Hex-Ascci-2-digitos
                                                    // al entero correspondiente
      i2cw1(address_EEPROMW,0x00,h);                // Escribo nuevo índice en 0x00
      printf("> i=%X hex (%u dec)\r\n",h,h);        // Monitorizo lo realizado.
   }
   // Retorno error o comando invalido
   if(!flagvalido) printf("¿%s?\r\n",cbuff);
   inicbuff();                         // Borro buffer.
   enable_interrupts(int_rda);         // Habilita de nuevo Interrupción RDA
}
 
 
 

Fuentes de eeprom_rs232_i2c.c

 
 
  • Unos ejemplos de dialogo entre el PC y el PIC con respecto a la EEPROM:

 


Nota: Esta página Web esta repleta de imágenes, textos, logotipos y demás material extraídos de los mas variados medios de los que no soy ni autor ni depositario de los correspondientes derechos de autor, uso y/o reproducción. Si Ud. es depositario de dichos derechos y desea que el material correspondiente sea eliminado de esta Web no dude en ponerse en contacto conmigo mediante e-mail y será inmediatamente retirado. Gracias.
 
Visitas
Totales : 81298 Hoy: 2 Activas: 1 Vistas: 81298

Esta página fue modificada el 07-08-2010 22:42:20

           
 DmSoft WAMP Escribir Unreal