| |
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 |
| |
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 |
|
|
- 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. |
|
|
- También
hacemos uso de la resistencia LDR conectada a RA0. Por RA3 conectamos a
través de un potenciómetro a VCC y GND con el ajustaremos el VREF
necesario para nuestras conversiones exactamente a 1 V.
|
- 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.
|
- La EEPROM que vamos a
usar es la PCF8582C de Phillips que tiene 256 bytes y es conectable vía
I2C. Aquí podéis ver su
Datasheet
|
- Vamos a tener dos tipos
de comandos: los de teclado con los que editamos el buffer que va
manejando el PIC para configurarle los distintos comandos y los de alto
nivel que son los encargados de manejar la EEPROM:
|
- 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 | |