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.
electrónica, freescale, hc08, hcs08, micro, microcontroladores, motorola

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