Manejo de puertos de E/S – Parte 2

 

En este nuevo ejemplo aprenderemos a configurar el reloj interno del microcontrolador y a seguir aplicando lo que aprendimos en el post anterior.

Configuraremos el microcontrolador para que cuando presionemos un pulsador se encienda y apague un LED a una velocidad de aproximadamente 300ms, y que deje de parpadear luego de soltarlo.

Utilizaremos el reloj interno del micro, por lo que no hace falta agregar ningún oscilador externo. Por default, el reloj está funcionando a 32Mhz y la frecuencia de reloj del bus a 8Mhz. Para modificarlo debemos configurar el registro ICSC1(Internal Clock Source Control Register 1). Este es un registro de 8 bits y en la siguiente imagen se puede ver su estructura.Para obtener una frecuencia de bus de 2Mhz, haremos lo siguiente. Los bits 7 y 6 que seleccionan la fuente del reloj, los dejaremos en 00. Los bits 5 al 3 son para elegir el valor por el que se divide el reloj, entonces los pondremos en 010 para dividir por 4 (32Mhz/4=8Mhz). El bit 2 lo pondremos en 1 para seleccionar la referencia de reloj interna. Y por último, los bits 1 y 0 los dejaremos en 0.

De esta manera, obtenemos una frecuencia de reloj de 8Mhz. En la familia HCS08, la frecuencia de reloj del bus es la frecuencia de reloj dividido 2, por lo que en este caso obtendríamos una frecuencia de 4Mhz, pero en el registro ICSC2 podremos seleccionar el divisor que por default viene en 2, obteniendo así una frecuencia de bus de 2Mhz. Por lo que no deberemos modificar el registro ICSC2.

El código en asembler para esto sería:

 ASM |  copy code |? 
1
mov #%00010100,ICSC1

Como ya sabemos del ejemplo anterior, el # indica que el operando es un número y el % indica que está en formato binario.

El siguiente paso será configurar un bit de un puerto de entrada/salida paralelo como salida. En nuestro caso utilizaremos el puerto B, por lo que debemos configurar el registro PTBDD de la siguiente manera:

 ASM |  copy code |? 
1
bset 0,PTBDD

Para el switch utilizaremos el bit 0 del puerto A, y para no usar resistencias externas, configuraremos el registro PTAPE para activar la resistencia pull-up interna del microcontrolador correspondiente al bit 0 del puerto A. A continuación vemos el código:

 ASM |  copy code |? 
1
bclr 0,PTADD ;Configuro el bit 0 del puerto A como entrada                 
2
lda #1                 
3
sta PTAPE     ;Habilito la resistencia pull-up del pin 0 del puerto A

Al principio del programa debe hacerse la declaración de variables. En este caso creamos las variables de 8 bits RETARDOEXT y RETARDOINT que serán utilizadas en la subrutina de retardo.

 ASM |  copy code |? 
1
RETARDOEXT: DS.B   1        ;Variable de 1 byte para retardo                   
2
RETARDOINT: DS.B   1        ;Variable de 1 byte para retardo

A continuación analizaremos el programa principal.

 ASM |  copy code |? 
01
mainLoop:               
02
SW1: brset 0,PTAD,SW1               
03
 jsr RETDEB               
04
 brset 0,PTAD,SW1               
05
 
06
        bset 0,PTBD ;Pongo el bit 0 del puerto B en 1               
07
        jsr RETARDO ;Salto a la subrutina de retardo               
08
        bclr 0,PTBD ;Pongo el bit 0 del puerto B en 0               
09
        jsr RETARDO               
10
        BRA    mainLoop ;Vuelvo al inicio del programa principal

Primero veremos la parte de encendido y apagado del LED y luego analizaremos el código para manejar el switch.

Luego de encender el LED, con la instrucción jsr saltamos a una subrutina de retardo. Lo que hace el programa es buscar la etiqueta RETARDO y ejecutar el código que ahí se encuentra, para luego volver al programa principal una vez que terminó. En la siguiente instrucción apagamos el LED, para después volver a llamar a la subrutina de retardo y finalmente con bra se vuelve al principio del programa principal.

Ahora analizaremos la subrutina de retardo:

 ASM |  copy code |? 
1
RETARDO: ;Subrutina que genera un retardo de 359ms            
2
     mov #255,RETARDOEXT ;4 ciclos            
3
loop1       mov #255,RETARDOINT ;4 ciclos            
4
loop2       brn * ;3 ciclos            
5
            nop ;1 ciclo            
6
            dbnz RETARDOINT,loop2         ;7 ciclos            
7
            dbnz RETARDOEXT,loop1         ;7 ciclos            
8
 
9
            rts ;vuelvo al programa principal

Con esta rutina generamos el tiempo de espera que queremos que tenga el LED entre que se prende y se apaga. Al lado de cada instrucción se puede ver la cantidad de ciclos que utiliza. De esta manera, se puede calcular el tiempo haciendo algunas cuentas sencillas. Como nuestra frecuencia de bus es de 2Mhz entonces el T de bus será igual a 0.5us. Haciendo la cuenta 300ms dividido 0.5us obtenemos la cantidad de ciclos que lleva obtener ese retardo, por lo tanto debemos obtener la cantidad de ciclos que le toma al micro ejecutar la instrucción de retardo. Esto se hace de la siguiente manera:

4+255*(4+255*(3+1+7)+7) + 5 = 718089

Multiplicando este número por 0.5us obtenemos un tiempo de retardo de 359ms, que es aproximadamente igual al retardo de 300ms que buscábamos.

Veamos más de cerca la rutina. Primero, la instrucción mov (4 ciclos) copia el valor 255 en decimal en la variable RETARDOEXT. Luego, armamos un bucle indicado por la etiqueta loop1. De esta manera, el loop1 se repetirá 255 veces (4 + 225*(la suma de los ciclos dentro del loop1)). A su vez, creamos otro bucle más (loop2), el cuál también se repetirá 255 veces (4 + 255*(4 + 255*(3 + 1 + 7) + 7). La instrucción brn pierde 3 ciclos, mientras que la instrucción nop pierde 1. Con dbnz (7 ciclos) se decrementa la variable RETARDOINT, y si el resultado no es 0, se produce un salto a loop2. Repitiéndose así este bucle por 255 veces. Una vez que terminó de ejecutarse, utilizamos nuevamente dbnz para decrementar la variable RETARDOEXT, y como en el caso anterior, el bucle se ejecutará nuevamente 255 veces, hasta que la variable sea 0.

Una vez que terminó de ejectuarse la rutina de retardo, se vuelve al programa principal con la instrucción rts (5 ciclos). Quedando finalmente la cuenta 4+255*(4+255*(3+1+7)+7) + 5 = 718089

 Ahora observemos la sección de código dedicada al interruptor, ubicada al comienzo del programa principal:

 ASM |  copy code |? 
1
SW1: brset 0,PTAD,SW1          
2
 jsr RETDEB          
3
 brset 0,PTAD,SW1

La instrucción brset chequea el bit 0 del puerto A, si este vale 1, sigue chequeando, pero si vale 0, quiere decir que el interruptor  fue presionado y continúa el flujo normal del programa.  El código que sigue después es para evitar el efecto de los rebotes producidos al presionar el switch real. Se llama a una subrutina de retardo de 25ms aproximadamente y luego vuelve a chequearse la tecla, para estar seguros de que realmente se presionó.

A continuación se encuentra el programa completo:

 ASM |  copy code |? 
01
; Include derivative-specific definitions         
02
            INCLUDE 'derivative.inc'         
03
 
04
;         
05
; export symbols         
06
;         
07
            XDEF _Startup         
08
            ABSENTRY _Startup         
09
 
10
;         
11
; variable/data section         
12
;         
13
            ORG    Z_RAMStart         ; Insert your data definition here         
14
RETARDOEXT: DS.B   1   ;Variable de 1 byte para retardo         
15
RETARDOINT: DS.B   1   ;Variable de 1 byte para retardd         
16
 
17
;         
18
; code section         
19
;         
20
            ORG    ROMStart         
21
 
22
 
23
_Startup:         
24
            LDHX   #RAMEnd+1        ; initialize the stack pointer         
25
            TXS         
26
            lda #$02 ;Deshabilito watchdog         
27
            sta SOPT1         
28
 
29
*****************                     
30
*Configuro reloj*                   
31
*****************                  
32
            mov #%00010100,ICSC1 ;Reloj interno, divido por 4 (CLK=8Mhz,BUS_CLK=2Mhz)         
33
*************************************                     
34
*Configuro puertos de entrada/salida*                   
35
*************************************         
36
     bset 0,PTBDD ;Configuro el bit 0 del puerto B como salida         
37
            bclr 0,PTADD ;Configuro el bit 0 del puerto A como entrada         
38
            lda #1         
39
            sta PTAPE     ;Habilito la resistencia pull-up del pin 0 del puerto A                 
40
******************************                     
41
*Inicio de programa principal*                   
42
******************************                     
43
 
44
mainLoop:         
45
SW1:     brset 0,PTAD,SW1     ;Me fijo si PTAD0 está en 1 (switch no presionado), si lo está, salto a SW1 para volver a repetir el proceso,          
46
                                ;si está en 0, continúo con el programa         
47
     jsr RETDEB           ;Salta a la subrutina de retardo para evitar rebotes         
48
     brset 0,PTAD,SW1     ; vuelvo a leer el switch         
49
 
50
            bset 0,PTBD ;Pongo el bit 0 del puerto B en 1         
51
            jsr RETARDO ;Salto a la subrutina de retardo         
52
            bclr 0,PTBD ;Pongo el bit 0 del puerto B en 0         
53
            jsr RETARDO         
54
            BRA    mainLoop ;Vuelvo al inicio del programa principal         
55
 
56
RETDEB: ;Subrutina de retardo para evitar efecto de rebote en el switch (27ms)         
57
    mov #50,RETARDOEXT         
58
loop11      mov #100,RETARDOINT         
59
loop22      brn *         
60
            nop         
61
            dbnz RETARDOINT,loop22         
62
            dbnz RETARDOEXT,loop11         
63
 
64
            rts                       
65
 
66
RETARDO: ;Subrutina que genera un retardo de 359ms         
67
     mov #255,RETARDOEXT ;4 ciclos         
68
loop1       mov #255,RETARDOINT ;4 ciclos         
69
loop2       brn * ;3 ciclos         
70
            nop ;1 ciclo         
71
            dbnz RETARDOINT,loop2 ;7 ciclos         
72
            dbnz RETARDOEXT,loop1 ;7 ciclos         
73
 
74
            rts ;vuelvo al programa principal         
75
 
76
 
77
;**************************************************************         
78
;* spurious - Spurious Interrupt Service Routine.             *         
79
;*             (unwanted interrupt)                           *         
80
;**************************************************************         
81
 
82
spurious: ; placed here so that security value         
83
 NOP ; does not change all the time.         
84
 RTI         
85
 
86
;**************************************************************         
87
;*                 Interrupt Vectors                          *         
88
;**************************************************************         
89
 
90
            ORG $FFFA         
91
 
92
 DC.W  spurious ;         
93
 DC.W  spurious ; SWI         
94
 DC.W  _Startup ; Reset

Ahora solo nos queda probar el programa. Si no contamos con un programador, simplemente podemos correr la simulación por software como en el ejemplo anterior y observar como se modifican los valores en los registros. En el siguiente video se puede observar el programa corriendo con USBDM y una placa con un SH8.

En el próximo post realizaremos este mismo programa, pero utilizando código en lenguaje C. 

Tags: , , , , , ,

{ 1 comment to read ... please submit second! }

{ 1 Pingbacks/Trackbacks }

  1. Manejo de puertos de E/S – Parte 3 » vloox

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>