# Lenguaje ensamblador para pic ?



## kooks (Mar 7, 2011)

Hola. Saludos a todos. Apenas es mi primer post y me doy la bienvenida con un problemita...

Estoy usando un pic16f84 y no sé cómo programar una idea que tengo.
Estoy configurando RA0-RA3 como entradas y RB0-RB7 como salidas.
Pero quiero que RB0-RB3 comiencen estando encendidas y que dependiendo de lo que yo haga con mis entradas, estos valores cambien.

ESTADO			EQU		03h
PORTA			EQU		05h
TRISA			EQU		05h
PORTB			EQU		06h
TRISB		         	EQU		06h

				org		00h

				bsf		ESTADO,5;
				bcf		TRISB,0;
				bcf		TRISB,1;
				bcf		TRISB,2;
				bcf		TRISB,3;
				bcf		TRISB,4;
				bcf		TRISB,5;
				bcf		TRISB,6;
				bcf		TRISB,7;
				bsf		TRISA,0;
				bsf		TRISA,1;
				bsf		TRISA,2;
				bsf		TRISA,3;
				bcf		ESTADO,5;
INICIO			bsf		PORTB,0;
				bsf		PORTB,1;
				bsf		PORTB,2;
				bsf		PORTB,3;

Listo. Hasta ahí todo bien.

Ahora quiero que si mi entrada RA0 se activa (conectandole RB0), me desactive RB1, RB2, RB3; y que active a RB4.
Entonces lo hago así:

Disp1			        btfsc	PORTA,0;
				goto	ESUNO;
				goto	ESCERO;
				goto	Disp1;

ESUNO			bsf		PORTB,4;
				bcf		PORTB,1;
				bcf		PORTB,2;
				bcf		PORTB,3;
				goto	        Disp1;
ESCERO			bcf		PORTB,4;
				bsf		PORTB,1;
				bsf		PORTB,2;
				bsf		PORTB,3;
				goto	        Disp1;

Y funciona.

Peeeeeeeero.... mi problema es el siguiente...

Quiero que también haga algo similar si en lugar de activar RA0, activo RA1. Osea que me desactive RB0, RB2 y RB3 y que active RA5.

Lo hago de la misma manera que la parte anterior:

Disp2      btfsc PORTA,1;
             goto ESUNO2;
             goto ESCERO2
              goto Disp2;

ESUNO2   bsf PORTB,5;
              bcf PORTB,0;
              bcf PORTB,2;
              bcf PORTB,3;
              goto Disp2;
ESCERO2  bcf PORTB,5;
              bsf PORTB,0;
              bsf PORTB,2;
              bsf PORTB,3;
              goto Disp2;
              end


Pero aquí ya no funciona... Lo he simulado y no me toma en cuenta a "Disp2", como si el programa terminara en Disp1.

Y tengo que hacer operaciones similares hasta llegar a Disp9...

¿Qué es lo que estoy haciendo mal? Estoy seguro de que es algo muy simple, pero soy principiante en esto... Espero que alguien me pueda ayudar. Ya me desespereee


----------



## rodrigo_6 (Mar 17, 2011)

hola! me pasas el programador, please?
Gracias


----------



## Gradmaster (Mar 17, 2011)

hola te dejo unos archivos que te pueden interesar


----------



## silicon blood (Mar 20, 2011)

Hola no se si ya solucionaste, pero igual te voy a ayudar...

primero voy a reescribir tu codigo de manera que sea mas eficiente (menos lineas de programa)

ESTADO EQU 03h
PORTA EQU 05h
TRISA EQU 05h
PORTB EQU 06h
TRISB EQU 06h

org 00h

bsf ESTADO,5;
clrf TRSIB
bsf TRISA,0;
bsf TRISA,1;
bsf TRISA,2;
bsf TRISA,3;
bcf ESTADO,5;

INICIO movlw 0x0f
movwf PORTB

Disp1 btfsc PORTA,0;
goto ESUNO;
goto ESCERO;
goto Disp1;

ESUNO bsf PORTB,4;
bcf PORTB,1;
bcf PORTB,2;
bcf PORTB,3;
goto Disp1;
ESCERO bcf PORTB,4;
bsf PORTB,1;
bsf PORTB,2;
bsf PORTB,3;
goto Disp1;

ahora aqui esta tu problema, haces un bucle infinito que te lleva de vuelta a Disp1 e imposibilita el avance hacia el codigo posterior a este goto...

La solucion:



Compara MOVF	PORTA,W          ;lee el porta
        MOVWF	DATO               ;lo mueve a una variable	
        MOVF	        DATO,W            ;lo lleva al W
	XORLW	B'00000001'       ;compara lo que leiste del porta con tu condicion
	BTFSC	ESTADO,2         ;verifica el Z flag del registro estatus
	CALL	Disp1                       ;si es uno llama a Disp1
	MOVF	DATO,W                   ;si es cero pasa a comparar con la siguiente condicion
	XORLW	B'00000010'
	BTFSC	ESTADO,2
	CALL	Disp2
	.
	.
	.
	.
	goto	Compara                  ;regresa al principio y comienza de nuevo

Disp1	bsf PORTB,4;
	bcf PORTB,1;
	bcf PORTB,2;
	bcf PORTB,3;
	RETURN
Disp2	.
	.
	.
	.
	RETURN

Lo mas importante, que ya depende de tu codigo y de lo que pretendas hacer es que no se mezclen las combinaciones de bits, por que como dices que quieres llegar a disp9 quiere decir que vas a considerar dos o mas bits del PORTA activos, asi que eso podria hacer ciertas dos condiciones simultaneamente... bueno espero haber ayudado.


----------



## rodrigo_6 (Mar 20, 2011)

alguien me puede pasar el "ensamblador" por favor? 

Gracias de antemano!!!

Saludos!!


----------



## silicon blood (Mar 20, 2011)

rodrigo_6 dijo:


> alguien me puede pasar el "ensamblador" por favor?
> 
> Gracias de antemano!!!
> 
> Saludos!!



http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en019469&part=SW007002

ahi tienes para descargar el MPLAB IDE v8.63 que es la herramienta de programacion en ensamblador desarrollada por microchip.


----------



## Olmecatronica (Mar 20, 2011)

En la página de Microchip puedes descargar gratuitamente el software de Mplab, saludos.


----------



## gabriel_sand (Mar 31, 2011)

Es de libre distribucion en la pagina oficial.


----------



## PIC102 (Feb 25, 2015)

Hola amigos. Soy estudiante y sé 0 de todo lo relacionado con un PIC.
Necesitaría ayuda para comprender desde 0 este código:


```
List    p=16F876
include    "P16F876.INC"
org    0x05    

Inicio    clrf PORTB    
bsf    STATUS,RP0    
clrf    TRISB    
movlw    0x06
movwf    ADCON1    
movlw    b'00111111'    
movwf    TRISA    
bcf    STATUS,RP0    

Loop    movf    PORTA,W    
movwf    PORTB    
goto    Loop    

end
```
A ver si me podéis ayudar por favor.
Gracias.


----------



## Nuyel (Feb 25, 2015)

dejando lo primero antes de INICIO (son necesarios para el ensamblador pero no afecta en sí durante el programa)
CLRF es para borrar una dirección de memoria que en el primero se refiere a PORTB, entonces borras el valor en la salida PORTB,
BSF establece un bit en el registro, en este caso es el bit RP0 del registro STATUS, en este caso selecciona el banco de memoria 1, debido al valor de 8 bits no se puede manejar toda la RAM en una sola línea, así que se divide en bancos para poder operarlos.
el siguiente es lo mismo que el primero, limpias TRISB, si no mal recuerdo cuando el bit es 0 se indica como salida el pin.
MOVLW mueve un valor literal (en este caso 0x06) al registro de trabajo (W, siempre los datos que vas a trabajar para mover de un lado a otro tienen que pasar aquí por el reducido juego de instrucciones)
MOVWF mueve el registro de trabajo al registro ADCON1 (igual revisa la hoja de datos, pero creo que con ese valor es para desactivar las entradas análogas y dejarlo en digital).
Luego otra ves cargamos en W el valor binario 00111111 que sería el equivalente a 0x3F
Después movemos W al registro TRISA, los bits seleccionados actuarán como entradas, en este caso será de PORTA0 hasta PORTA5
BCF borra el bit, aquí borramos en STATUS el bit correspondiente a  RP0, con eso nos volvemos a regresar al banco de memoria 0

Ahora en pesamos el Loop
MOVF PORTA,W mueve el valor de PORTA a W, así leemos las entradas y almacenamos
MOVWF mueve el valor de W a PORTB, así es como pasamos el dato a las salidas
GOTO LOOP le dice al programa que se mueva a LOOP y entonces se repite el proceso
End ya es el final del archivo, para entender bien lo que se está haciendo al programar de esta manera debes leer la hoja de datos y verificar que hace cada bit en cada registro, así como la ubicación de los registros en los bancos de memoria y el set de instrucciones que tiene el chip para poder manejarlo.


----------



## Miembro eliminado 356005 (Feb 25, 2015)

```
list p=16F876           ; ajustar opciones de listado para ese micro
        include "P16F876.INC"   ; incluir en el ensamblado las definiciones de ese micro

        org 0x05                ; el programa comienza en esa dirección

; Bank 0
Inicio  clrf PORTB              ; ponemos a 0 el puerto B

; Bank 1
        bsf STATUS,RP0          ; pasamos al banco 1
        clrf TRISB              ; definimos el puerto B como salida

        movlw 0x06              ; b'00000110'
        movwf ADCON1            ;       011X : todas las entradas puerto A son digitales

        movlw b'00111111'       ; definimos PORTA0-PORTA5 como entradas. PORTA6 y PORTA7 como salidas
        movwf TRISA

; Bank 0
        bcf STATUS,RP0          ; volvemos al banco 0

; Bucle
Loop    movf PORTA,W            ; leemos el puerto A
        movwf PORTB             ; lo escribimos en el puerto B
        goto Loop               ; y repetimos, infinitamente

        end                     ; fin de código
```
Lo que hace el programa es definir de qué puerto se lee y a qué puerto se escribe, y en un bucle infinito va copiando de uno a otra (solo los 6 bits más bajos).


----------



## PIC102 (Mar 1, 2015)

Hola amigos, primero de todo , gracias por ayudarme y contestarme sobre el codigo que me dieron en la asignatura de microprocesadores,está siendo un dolor de cabeza.

En la preparación de mi informe para entregar, estoy con unas pequeñas dudas.

1)En mi codigo, cuando decimos :
clrf TRISB:Ya en el banco 1, limpio todo el registro de TRISB poniendo todos
sus bits del registro a 0.
Pero como se que el registro TRISB se ha convertido en  una salida? (he subido una foto que alomejor puede ser eso)

2)El valor literal de b ’ 00111111 ’ ; definimos PORTA0 - PORTA5 como
entradas . PORTA6 y PORTA7 como salidas. ¿Es un valor predeterminado?¿Como sabes que hace eso?

3)En el bucle , w , es un registro o una memoria interna para guardar unos datso temporales para pasarlos a PORTB?

Muchísimas gracias por su ayuda!!


----------



## Miembro eliminado 356005 (Mar 1, 2015)

1) En los microcontroladores PIC, las patillas que pueden ser entradas y salidas (E/S), se define que son entradas cuando se pone un '1' en su correspondiente bit del correspondiente registro del correspondiente puerto. Y se define como salida, si ponemos un '0' en ese bit.

Entonces, al hacer el *clr TRISB*, estamos definiendo todos los pines del *PORTB* como salida.

Si queremos saber la definición actual de E/S de un puerto, solo tenemos que leer el correspondiente registro *TRISx*.

2) Cada bit corresponde a un pin del puerto de salida. *PORTA0* corresponde al bit 0 del registro *PORTA*, *PORTA1*, al 1. Y así. Cuando vemos el valor binario *b00111111*, entonces vemos que los seis primeros bit están definidos como entradas -están a '1'- y los dos últimos, como salidas -están a '0'-

3) Los PIC solo tienen un registro de propósito general, que es *W*. Como no hay, en el repertorio del lenguaje ensamblador del PIC, ninguna instrucción que sea capaz de llevarnos el valor de un registro a otro, tenemos que dividir la operación en dos. Primero leemos el registro que nos interesa y almacenar su valor en algún sitio. Podría ser un registro de propósito general, o un registro de la memoria flash o cualquier sitio donde podamos guardar algo de forma temporal. Como el dato solo lo necesitamos un momento, lo guardamos en el registro *W* del procesador. La siguiente instrucción escribe el contenido del registro *W* al registro destino.


----------



## PIC102 (Mar 4, 2015)

Hola.
A ver si esto si se puede resolver. 
En  este código,lo estoy intentando entender  y lo he comentado, no se si esta bien , a ver si tengo un error,¿Me lo podeis revisar?

*movlw 0x04 *     //Queremos mover el valor literal 4 al registro W
*movwf N*           //El valor 4 almacenado en el registro W , lo pasamos al registro N, donde ahora N=4

                        //A partir de este comienzo es donde empezaremos a tener en cuenta el ciclo de operacion

*REPETIR* * movf N,W*
                       //estamos colocando el valor de W=N
*decfsz N,f*
                       //esta instruccion equivale a decrementar el valor en 1 del registro N
*goto REPETIR*
                       //si N es difernte a 0 , ir a REPETIR 
*movf PORTA,W*
                       //se cumple cuando sea N=0


Tampoco se, como sabe el código que cuando N es diferente de 4 tiene que hace el goto e ir a la etiqueta de REPETETIR. Y cuando es N=4 se cumpla la indicación.

Gracias.


----------



## MrAlphonse (Mar 4, 2015)

Hola PIC102.

A lo que podemos ver en tu ultimo mensaje, tu codigo no identifica si la variable N es mayor, menor o igual a cualquier otra variable, en este caso el 4 que tu mencionas.

Lo que haces es esto:

- Cargas el valor de w al registro N 
- Entras a una rutina ciclica de decremento del registro N, en la cual, cada vez que se ejecuta cargas a W con el valor de N, por lo tanto:
- En el primer ciclo, N=4 y W=4, se genera el decremento de N y, como el registro no es cero (N=3) no se activa el bit Z del registro STATUS, regresa a la etiqueta REPETIR.​- En el segundo ciclo, N=3 y W=3, se genera el decremento de N y, como el registro no es cero (N=2) no se activa el bit Z del registro STATUS, regresa a la etiqueta REPETIR.​- En el tercer ciclo, N=2 y W=2, se genera el decremento de N y, como el registro no es cero (N=1) no se activa el bit Z del registro STATUS, regresa a la etiqueta REPETIR.​- En el cuarto ciclo, N=1 y W=1, se genera el decremento de N y, como el registro ahora ya es cero entonces carga a W con el valor de PORTA.​
Para poder checar si un registro es mayor, menor o igual a otro registro o literal (constante) debes realizar ya sea una resta entre estos registros o una funcion XOR e inspeccionar los bits C y Z del registro STATUS, que es lo que, en este caso de la instruccion decfsz, se esta haciendo.

DECFSZ=_Decrement f, Skip if 0_ = Decrementa el registro, salta si es cero.

Busca como realizar comparacion de registros, el metodo que yo uso es por medio de restas.


----------



## Miembro eliminado 356005 (Mar 4, 2015)

Un poco más de detalle sobre decfsz.

La operación

decfsz N,f

lo que hace es:

1. decrementa el registro en RAM (f) en la posición N

2. si el resultado de la resta es 0, el contador de programa "salta" la instrucción que sigue a decfsz, y ejecuta la siguiente instrucción

Si el resultado no es 0, el micro ejecuta la instrucción siguiente, que suele ser un goto, como en tu caso, por lo que salta de nuevo al principio del bucle.

Como ves, es una forma cómoda de realizar dos operaciones básicas de un bucle: decrementar y hacer una comprobación de si hemos llegado al final.


----------



## PIC102 (Mar 7, 2015)

Hola, gente! Buenas tardes. Llevo toda las tarde con el microcontrolador y la verdad que es un rompecabezas.
Tengo unas dudas.
Lo de antes ya lo entiendo perfectamente, es decir:
La  instrucción *decfsz N,f *solo funciona cuando N=0, cuando se cumple ésto, salta a la siguiente instrucción.
Por eso en nuestro código, cuando N=0, salta al goto y el valor se mete en la puerta A.

En esta tarde me han surgido unas duda con este código. Os explico:

Este código hará el retardo de 1 segundo utilizando el procesador.
Deberemos hacer siempre por Timer, pero en esta vez se utilizará de esta manera por explicación del profesor ya que no hemos llegado al temario del Timer.
Esta noche me meteré para intentar sacar la fórmula de los tiempos(cuando es un periodo o dos las instrucciones) para saber cuanto tarda.

Inciso (por si voy mal encaminado): 
El tiempo de operación son 4 por 1 tiempo de reloj, que un tiempo de reloj es 1/f=1/20 MHz = 50 ns
4 por 50 ns = 200 ns por instrucción.

A ver lo importante, este código:


```
CO1            equ   0x20
                       CO2            equ   0x21
                       CO3            equ   0x22
                       RETARD1  equ   0xDA
                       RETARD2  equ   0x07

Delay_1seg:
                     movlw  RETARD1
                     movwf  CO2 
                     movlw  RETARD2
                     movwf  CO3
                     clrf   CO1

avanza:
                 decfsz  C01,F
                 goto    avanza
                 decfz   C02,F
                 goto    avanza
                 decfsz  C03,F
                 goto    avanza
                 return
```
La equ :  equ es asignar un valor a un nombre convirtiéndolo en una constante.

- Pero en la siguiente parte, no entiendo por que llama F a una a variable (contador1, contador2 y contador 3)
- Tampoco entiendo por que quita el valor de por ejemplo CO1 y le mete el valor de RETARD1
- Y en la tercera parte: creo que funciona -> lo que se busca es decrementar todas las variables contadores y cuando esto pase irse a otro fichero llamado return.
¿Entonces, este código seguiría o con el return acaba? Es que no veo el end y estoy confundido.

Bueno, sé que esto es complicado, pero si me podéis ayudar, os lo agradecería.
Gracias.


----------



## MrAlphonse (Mar 7, 2015)

Primero: La F que pone después de la coma indica el destino del resultado de la operación. Existen dos posibles destinos para una operación:
---------------------------------------------------------------------------------
- El registro de trabajo W:


```
MOVF 	DIRECCION,W
	ADDWF 	DIRECCION,W
	SUBWF 	DIRECCION,W
	INCF 	DIRECCION,W
	DECF 	DIRECCION,W
	ANDWF	DIRECCION,W
	COMF	DIRECCION,W
	IORWF	DIRECCION,W
	RLF	DIRECCION,W
	RRF	DIRECCION,W
	SWAPF	DIRECCION,W
	XORWF	DIRECCION,W
	DECFSZ	DIRECCION,W
	INCFSZ  DIRECCION,W
```

Que también podría reemplazarse por un 0 (cero) en lugar de la W.


```
MOVF 	DIRECCION,0
	ADDWF 	DIRECCION,0
	SUBWF 	DIRECCION,0
	INCF 	DIRECCION,0
	DECF 	DIRECCION,0
	ANDWF	DIRECCION,0
	COMF	DIRECCION,0
	IORWF	DIRECCION,0
	RLF	DIRECCION,0
	RRF	DIRECCION,0
	SWAPF	DIRECCION,0
	XORWF	DIRECCION,0
	DECFSZ	DIRECCION,0
	INCFSZ  DIRECCION,0
```

En este caso, el resultado de la operación se deposita en el registro W y el registro DIRECCION no se ve modificado para nada.

---------------------------------------------------------------------------------
- El registro con el que se esta trabajando (F):


```
MOVF 	DIRECCION,F
	ADDWF 	DIRECCION,F
	SUBWF 	DIRECCION,F
	INCF 	DIRECCION,F
	DECF 	DIRECCION,F
	ANDWF	DIRECCION,F
	COMF	DIRECCION,F
	IORWF	DIRECCION,F
	RLF	DIRECCION,F
	RRF	DIRECCION,F
	SWAPF	DIRECCION,F
	XORWF	DIRECCION,F
	DECFSZ	DIRECCION,F
	INCFSZ  DIRECCION,F
```

O también puedes poner un 1 (uno) en lugar de F:


```
MOVF 	DIRECCION,1
	ADDWF 	DIRECCION,1
	SUBWF 	DIRECCION,1
	INCF 	DIRECCION,1
	DECF 	DIRECCION,1
	ANDWF	DIRECCION,1
	COMF	DIRECCION,1
	IORWF	DIRECCION,1
	RLF	DIRECCION,1
	RRF	DIRECCION,1
	SWAPF	DIRECCION,1
	XORWF	DIRECCION,1
	DECFSZ	DIRECCION,1
	INCFSZ  DIRECCION,1
```

En ambos casos, el resultado de la operación va a ser depositado en el registro DIRECCION.

--------------------------------------------------------------------------------------------------------

Ahora, con respecto a lo de Return. Return es utilizado para finalizar una función o rutina. Si has programado en algún otro lenguaje de programación y creaste funciones debiste de haber finalizado alguna de ellas con un return. Bueno, pues es lo mismo. Si no existe un END es por que les dio la función para que solo vengas y hagas un llamado a esa función cada que la necesites, la anexas al archivo ASM que estés utilizando antes del END y listo, solo utilizas un CALL para llamarla y en ese momento se ejecutara una pausa de 1 segundo.

Y lo de porqué carga a los registros CO2 y CO3 con las constantes de RETARDO1 y RETARDO2 te lo dejo de tarea (aunque ya casi te dije de que se trata).


----------



## Miembro eliminado 356005 (Mar 7, 2015)

¿Cómo usar el TMR0 correctamente?

_Retardo básico en un pic_


----------



## PIC102 (Mar 7, 2015)

Hola. Aún sigo por aquí estudiando.
Me estoy estudiando los sistemas de memoria de programa.
Tengo una par de cosas, pero antes quiero entender bien esto.
A ver.El código de arriba:
Estoy llamando variables constantes y dándoles un valor a CO1, CO2, RETARD1 y RETARD2

```
CO1 = 100000
CO2 = 100001   
CO3 = 100010       
RETARD1 = 11011010
RETARD2 = 00000111
```
Están en hexadecimal y los he pasado a binario.
Supongo que es mejor tenerlo en binario para saber que registros pondremos a 0 y a cuales 1

Ahora vamos a (creo que se llama así) rutina que le llamaremos: Delay_1seg

```
movlw  RETARD1
movwf  CO2 
movlw  RETARD2
movwf  CO3
clrf   CO1
```
Vale. Aquí está mi gran duda. Intentaré ir por pasos.
1) Yo digo que CO1,CO2,CO3 (contadores ) que como le hemos puesto equ, son constantes.
Es decir, equ, según la definición: 


> "El nombre viene de la palabra "equal", (igual)". La directiva EQU permite al programador "igualar" nombres personalizados a datos o direcciones.
> Los nombres utilizados se refieren generalmente a direcciones de dispositivos, datos numéricos, direcciones de comienzo, direcciones fijas, posiciones de bits, etc.
> Un nombre es más descriptivo que una simple dirección y la tarea de programar se hará mucho más sencilla.
> También podemos asignar un nombre a una instrucción que repitamos varias veces a lo largo de un algoritmo, de manera que sea mucho más sencilla la programación.
> A estos nombre que asignamos mediante esta directiva se les denomina constantes, ya que el registro al que apuntan no variará durante el programa.


Entonces usted me ha dicho que los contadores son registros. (Que es lógico por que la rutina utiliza la F para hacerse referencia a ello)
Por lo que en mi definición, no ponía que EQU se podía utilizar para registros.
Bueno, hago acto de fe y digo que los contadores son registros.
2) Retardo es una constante, en eso coincidimos.
3) Por lo que en el código vemos que el valor de la constante la metemos en el acumulador (W) y luego lo metemos en un fichero (F) que en estos casos son los contadores.

Pues vale. A su pregunta: ¿Por qué carga a los registros CO2 y CO3 con las constantes de RETARDO1 y RETARDO2?

(No sería si digo una tontería)
Estamos dando unos valores a los registros, que hacen poner unos bits de dentro del registro a 0 o a 1 según sea el valor  del literal.
Y lo que tenga que hacer el registro, dure lo que valga el retardo y luego por arte de magia se limpia el registro CO1

A ver si en su contestación usted o alguien  me responde mis dudas de porqué contador es un registro y que hace realmente delay_1 segundo (incluyendo de lo que porqué se limpia el registro CO1)

Bien. Según por lo que he podido leer, avanza.
¿Es una subrutina a la que hemos llegado a ella con un CALL que pone todos los contadores a 0 y se vuelve a la rutina de donde esta el CALL (que nos ha llevado hasta avanza) y empieza a ejecutar la instrucción siguiente del CALL?


Gracias a la gente que está aclarando mis dudas.
Voy a seguir con los sistema de memoria de programa. (Esta noche soñaré con el WatchDog. Jajaja)


----------



## MrAlphonse (Mar 7, 2015)

Mira, sucede esto:

Cuando pones:


```
CO1	equ	0x20
	CO2	equ	0x21
```

Estas haciendo dos cosas a la vez:

Estas asignando a la dirección de RAM 0x20 el nombre de CO1 y estas determinando una constante llamada CO1 que tiene el valor de 0x20, lo mismo pasa con CO2, la dirección RAM 0x21 se llamara CO2 y puedes usar a el nombre CO2 como una constante.

¿Como es esto posible?

Te lo explico con un ejemplo:
---------------------------------------------------------------------------------------------------------
Cargaremos a W con el valor del registro de la RAM 0x20, es decir con el valor de RAM de CO1:


```
MOVF	CO1,W
```

Aqui, lo que contenga la RAM con direccion 0x20 sera depositado en W y podra ser un valor entre 0 y 255, dependiendo de lo que haya sido puesto en el con anterioridad.
---------------------------------------------------------------------------------------------------------
Cargaremos a W con el valor constante de CO1, es decir el valor constante de 0x20, no cargaremos al valor de RAM que es un valor variable, si no el constante que vendría siendo un 32 decimal.


```
MOVLW	CO1
```
---------------------------------------------------------------------------------------------------------

Entonces, lo que podrás deducir con esto, es que en el siguiente código:


```
movlw	RETARD1
	movwf	CO2
```

Tu profesor esta cargado al Registro RAM con dirección 0x20, cuyo nombre con el que lo bautizaste es CO1, el valor constante de RETARD1, que viene siendo un 218 decimal.

Si se pone la siguiente instrucción:


```
movf	RETARD1,W
	movwf	CO2
```

Entonces estaríamos cargando el valor de la RAM con dirección 0A, que puede ser cualquier número aleatorio entre 0 y 255.


CO1, CO2, CO3, RETARD1 y RETARD2 son nombres con los que, principalmente, vas a recordar ciertas direcciones RAM, digo, no creo que vayas a querer estar recordando que la dirección 0x20 es de un contador, la 0x21 es otro contador y asi sucesivamente, por eso se les asignan nombres. Pero, de manera implícita, el nombre lleva un valor constante, que vendría siendo el valor constante de la dirección RAM, no el valor del registro propio.

Espero que me haya explicado de manera clara. Si no fue así, entonces renunciare a mi intención de ser maestro  .


----------



## Miembro eliminado 356005 (Mar 7, 2015)

PIC102 dijo:


> Están en hexadecimal y los he pasado a binario.
> Supongo que es mejor tenerlo en binario para saber que registros pondremos a 0 y a cuales 1


Pues no... en este caso no nos interesa el valor en binario, sino su valor entero, porque lo único que vamos a hacer es... dar vueltas en una serie de bucles.



PIC102 dijo:


> Ahora vamos a (creo que se llama así) rutina que le llamaremos: Delay_1seg
> 
> ```
> movlw  RETARD1
> ...


No. Los contadores son registros (posiciones de la memoria RAM.



PIC102 dijo:


> Pues vale. A su pregunta: ¿Por qué carga a los registros CO2 y CO3 con las constantes de RETARDO1 y RETARDO2?
> 
> (No sería si digo una tontería)
> Estamos dando unos valores a los registros, que hacen poner unos bits de dentro del registro a 0 o a 1 según sea el valor  del literal.


Más sencillo: inicializamos los contadores con unos valores. No estamos inicializando los contadores con valores constantes, si no que solo se inicializan con unos valores. A partir de ahí, el resto de programa va modificando los contadores (por efecto de los decrementadores).



PIC102 dijo:


> Y lo que tenga que hacer el registro, dure lo que valga el retardo y luego por arte de magia se limpia el registro CO1


¿Magia? Por desgracia, aquí no hay magia.

Si un contador se inicializa a 0 (con un clr, por ejemplo), entonces es lo mismo que si lo hubiéramos inicializado a 256, ya que la primera ejecución de decfsz lo hará pasar de 0 a 255. Seguirá decrementando hasta que llegue a 0, y en ese momento sí que terminará el bucle.



PIC102 dijo:


> Bien. Según por lo que he podido leer, avanza.
> ¿Es una subrutina a la que hemos llegado a ella con un CALL que pone todos los contadores a 0 y se vuelve a la rutina de donde esta el CALL (que nos ha llevado hasta avanza) y empieza a ejecutar la instrucción siguiente del CALL?


Sí. La presencia del 'return' así lo indica.

Te aconsejo que leas los enlaces que te he dejado antes.



PIC102 dijo:


> Este código hará el retardo de 1 segundo utilizando el procesador.
> 
> Inciso (por si voy mal encaminado):
> El tiempo de operación son 4 por 1 tiempo de reloj, que un tiempo de reloj es 1/f=1/20 MHz = 50 ns
> 4 por 50 ns = 200 ns por instrucción.


¿Estás completamente seguro de que este código es para un microcontrolador funcionando a 20 Mhz?

He sumado los ciclos que consumen los bucles y me da una cifra cercana a un millón de ciclos, por lo que, para ser el retardo de 1 s, es necesario que el microcontrolador funcione a 4 Mhz.

Si está a 20 Mhz, ese retardo será de 1/5 s.

(Salvo que me haya equivocado en el cálculo de los ciclos.)


----------



## PIC102 (Mar 9, 2015)

Hola, el jueves de la semana que viene tengo el examen de esta asignatura, entra hasta el tema 4, y todo esto es del tema 3, necesito entender bien esto y pasar ya al siguiente tema. Ademas, este jueves tengo examen de sistemas operativos y ademas de otros trabajos que entregar.La verdad es que estoy temblando de lo que me viene encima, de lo difícil de entender y del poco tiempo que tengo.
Una vez que me he desahogado vamos a lo importante.

Voy hacer un resumen de todo el código que incluya todo lo que me habéis dicho y todas las explicaciones paso por paso para intentar entenderlo bien.
Allá voy:
--------------------------------------------------
Tenemos el siguiente código=

```
CO1            equ   0x20
                       CO2            equ   0x21
                       CO3            equ   0x22
                       RETARD1  equ   0xDA
                       RETARD2  equ   0x07

Delay_1seg:
                     movlw  RETARD1
                     movwf  CO2 
                     movlw  RETARD2
                     movwf  CO3
                     clrf   CO1

avanza:
                 decfsz  C01,F
                 goto    avanza
                 decfz   C02,F
                 goto    avanza
                 decfsz  C03,F
                 goto    avanza
                 return
```
--------------------------------------------------
Vamos a estructurar nuestro código en 3 partes:

*Parte 1*

```
CO1            equ   0x20
                       CO2            equ   0x21
                       CO3            equ   0x22
                       RETARD1  equ   0xDA
                       RETARD2  equ   0x07
```
Podemos decir que esta parte es la de inicialización y la creación de las variables

*A)* Nos fijamos en este "trozo de código", y vemos que tenemos 5 elementos. Por _intuición_ vemos que a 3 valores: CO1,CO2 y CO3 (son abreviaciones de la palabra  contadores) se está utilizando el comando equ, 

"cuya utilización es para darle unos valores a estos nombres y convertirlos en unas constantes, para que , igual de recordar valores necesarios que utilizamos repetidamente, utilizamos nombres, ya que es mas fácil de recordar/utilizar"

Estos valores que se le están asignando, _por sus valores que se les dá_  son posiciones de la memoria RAM libres, que , como no hemos hecho ningun cambio, son posiociones libres de la memoria RAM ubicados en el trozo del BANCO 0.

Hasta aquí, hemos hecho= Alquilar unas posiciones de la memora RAM libres del BANCO 0 y asignales un nombre(Recordar que:No hemos puesto ningun valor dentro de los contadores) por lo tanto, ahora podemos decir que CO1,CO2 y CO3 , son *registros*


*B)* Para las otras 2 'cosas' (ya se que no se le llaman cosas pero lo digo en sentido del primer momento que lo miramos) que vemos en un principio, por _intuición_ vemos que son NOMBRES con equ con unos valores, pero valores en sentido de LITERAL, por lo tanto RETARD1 y RETARD2  , podemos decir que son constantes

*Parte 2*


```
Delay_1seg:
                     movlw  RETARD1
                     movwf  CO2 
                     movlw  RETARD2
                     movwf  CO3
                     clrf   CO1
```

Esta parte, como dice su instrucción en ingles, la llamaremos , retraso 1 segundo.

Después de pegarle un ojo al pequeño código,  a primera vista, se trata de meter el valor de la constantes RETARD en los registros(Recordemos que están vacíos, no hemos metido nada- aun así antes de todo podríamos hacer una limpieza de los registros contadores por si acaso)

Okey, pues a grosso modo, lo que se hace es , metemos el valor de RETARD1 en el acumulador(w) y despues lo pasamos al contador 2 , y hacemos luego lo mismo con RETARD2.(Recordemos= el acumulador no almacena datos, se sobrescribe y lo que estaba se pierde) metemos su valor en el contador 3. La ultima instrucción es de limpiar / poner a 0 el contaor 1(esto lo hacemos por que será necesario para el siguiente trozo de codigo <- de esto no estoy seguro)

Resumen= hemos colocado los valores de RETARD1 y RETARD2 en los registros C02 y C03 respectivamente. Tambien hemos hemos limpiado el registro CO1 para hacer la funcion :

"Si un contador se inicializa a 0 (con un clr, por ejemplo), entonces es lo mismo que si lo hubiéramos inicializado a 256, ya que la primera ejecución de decfsz lo hará pasar de 0 a 255. Seguirá decrementando hasta que llegue a 0, y en ese momento sí que terminará el bucle."" <- *ESTO NO LO ENTIENDO*(mirar haber si lo digo bien en la parte 3)

*Parte 3*


```
avanza:
                 decfsz  C01,F
                 goto    avanza
                 decfz   C02,F
                 goto    avanza
                 decfsz  C03,F
                 goto    avanza
                 return
```
Vale, ultima parte
Si hacemos una simple vista podemos decir que , al haber un return, esto es una subrutina de alguna rutina que tiene un CALL y nos manda aqui(cuando en esta subrutina se llegue a return, volveremos a la rutina que tenia ese CALL, pero ya empezaremos por la ejecución siguiente/o la de debajo , de ese CALL)

Pues bien, decrementamos el registro C01, que según la explicación de Joanquin, habrá que hacerlo 255 veces. *recordad que no lo entiendo esto bien*, me he leido la explicación 20 veces y creo entender esto:
el registro C01 de forma predeterminada esta en la memoria RAM vació , con un valor '0'. Vale, este valor 0=256.
Por lo tanto, la primera instrucción será: 0->255, la siguiente instrucción 255->254, la siguiente instrucción 254->253....y así sucesivamente. 
Peró ¿para que hacemos esto? Supongo que para la ecuación de los tiempo , saldrá que los ciclos del registro C01 deberá ser 256 veces (uno arriba, uno abajo)

Sigamos, pues cuando en el registro C01 halla de nuevo el valor 0=256, saltará el goto(hay que sumarle 1 al tiempo de poner a 0) y pasará a la instrucción de decrementar el registro contador C02 , pero aquí será menos tiempo, aquí se tardará 0A->(base decimal) 218 ciclos + 1 ciclo/tiempo de saltar el goto.
Hacemos lo mismo para 

```
decfsz  C03,F
                 goto    avanza
                 return
```
Y FINAL, nos vamos a la rutina principal , a seguir con la instruccion del codigo debajo del CALL.


-----------------------------------------------------------------------------------------------------------------------

Buenoo gente , espero que esté bien, y me podáis confirmar que lo entiendo y que puedo pasar al siguiente tema.
Cualquier cosa que diga mal corregidmela por que lo he detallado todo al máximo para intentar plasmar todo el conocimiento que tengo.

Gracias.(ahora mientras me contestais , empezaré con la ultima parte del tema 3 que es calcular los tiempos de este codigo y estudiar para el examen de Sistemas Operativos de jueves.)


----------



## Miembro eliminado 356005 (Mar 9, 2015)

Por intuición, nada. O se sabe, o no se sabe.

'equ' es la abreviatura de 'equal', que podemos traducir por 'igual a'.

Sirve para asociar un valor o texto a una constante, dentro del ámbito del programa en ensamblador.

'Delay_1seg' no es el nombre de la primera parte del código, sino de toda la subrutina.

Sobre CO1, olvídate del 256. Piensa que se inicializa en 0 (por efecto del clr). Entonces, siguiendo con la ejecución del programa, al llegar al primer decfsz, al ser 0, pasa a 255 (es un byte, y si vale 0, al restar 1, pasa a ser -1, que vemos como un 255 -repasa la numeración en complemento a 2-).

Por eso, el primer bucle son 256 vueltas. De las cuales se computan 255 vueltas que consumen 3 ciclos, y una más (la última) que consume 2 ciclos.

Lo mismo se aplica a los otros dos bucles.


----------



## PIC102 (Mar 9, 2015)

*-*Entonces,quitando la instuición, como sabes que C01 será un registro y RETARD una constante , por que los dos tienen equ y los dos tienen un valor.....Lo siento, pero sigo sin entenderlo.

-okey, entendido!  delay_1seg y avanza estan relacionados.

Gracias.


----------



## Miembro eliminado 356005 (Mar 9, 2015)

Tu piensa que 'equ', lo que hace, es crear definiciones.

Entonces, *CO1 equ 0x20* quiere decir que, el ensamblador, cuando esté interpretando el código, cuando se encuentre con 'CO1', debe sustituirlo por '0x20'.

Por ejemplo, si en el código tenemos

```
clrf   CO1
```

es como si realmente hubiéramos escrito

```
clrf   0x20
```

O sea, que ponemos a cero el contenido de la dirección 0x20.

Como, para nosotros los humanos, es más fácil acordarnos de que CO1 significa 'contador 1', en lugar de usar 0x20, que para nosotros no es más que un número, pues esa es la razón de usar esas definiciones. Son solo etiquetas.

En otra parte del código, pone

```
movlw  RETARD1		# Cargamos el retardo 1
    movwf  CO2			# en contador 2
```

en realidad, es como si hubiéramos escrito

```
movlw  0xDA		# cargamos el entero 0xDA
    movwf  0x21		# en la dirección 0x21
```

Como ves, es más sencillo para nosotros los humanos usar las etiquetas, que los números.


----------



## castro (Mar 9, 2015)

pic102  el "equ#" que se usa en ensamblador es como dice juaquinferrero pero ademas esta ligado con la memoria de datos del pic que uses...me explico por ejemplo en la memoria de datos del pic 16f84 esta vacia desde el registro 0C hasta el registro 4F ..entonces cuando usted declara alguna variable por ejemplo

casa   equ    0Ch

significa que en la posiscion de memoria de datos 0C se guardara la variable casa y cada vez que la varible casa  se modifique el valor se guardara en el registro 0C,  esto se hace para facilitar el desarrollo del programa......yo personalmente tengo muchos años que no  programo en ensamblador....pero yo creo que uno puede usar directamente los registro hexadesimales


----------



## PIC102 (Mar 13, 2015)

Hola a todos, ya he pasado mi examen que tenia de jueves,me fue bien, ahora me queda una semana a tope para preparar el examen del jueves de Microprocesadores.

Ademas de eso, me han mandado un problema de entregar que mas adelante lo haré.
El examen es del tema 1,2,3 y 4

-------------------------(Como siempre, yo pongo todo lo que se, cualquier fallo decidmelo por favor)-----------------------

Una vez acabada la pequeña introducción, quiero acabar de una vez con este código: 


```
CO1            equ   0x20
                       CO2            equ   0x21
                       CO3            equ   0x22
                       RETARD1  equ   0xDA
                       RETARD2  equ   0x07

Delay_1seg:
                     movlw  RETARD1
                     movwf  CO2 
                     movlw  RETARD2
                     movwf  CO3
                     clrf   CO1

avanza:
                 decfsz  C01,F
                 goto    avanza
                 decfz   C02,F
                 goto    avanza
                 decfsz  C03,F
                 goto    avanza
                 return
```
--------------------------------

Veamos, según el texto (me equivoque cuando lo dije la otra vez) el PIC trabaja con 4 MHz ( es decir, el oscilador).

Vale , pues nos diponemos a calcular el *ciclo de instrucción.*

*Un ciclo de instrucción* :es el período que tarda la unidad central de proceso en ejecutar una instrucción de lenguaje máquina.

En otras palabras, el tiempo que tarda el procesador, micro, en este caso, en procesar/determinar *una instrucción* 

Una instrucción=4 veces el tiempo del relol
1 tiempo del reloj =1/frecuencia
frecuencia = 4 MHz(4*10^6)
1 tiempo del reloj =(1/4)*(1/1*10^6)
(1/1*10^6)= 1 nanosegundo
1 tiempo del reloj = 0.25 nanosegundos
4*0.25= 1 nanosegundos = *1*10^-6 segundos*


1 instrucción, me tarda : 1*10^-6 segundo

Y nuestro código, quiero que me retarde 1 segundo( como dije anteriormente-si, ahora es cuando decimos, los retardos mas simples en ensamblador , es ir restando un numero tantas veces hasta que me salga 0 y este tiempo de ir restando valores, hacer que tarde 1 segundo, antes de pasar a la siguiente rutina/trozo de codigo/instrucción o lo que sea. )

Voy a comprobar que este código me tarda como dice el enunciado, 1 segundo.

Concepto:
1 instrucción = 1 tiempo de instrucción
Especiales:
decfsz -> Si su valor que restamos es diferente de 0,tarda 1 tiempo de instrucción
         -> Si su valor que restamos es 0, no se hace resta y salta la que tenemos abajo(que suele ser un goto): 2 tiempo de instrucción
goto-> Siempre 2 ciclos de instrucción.

------------------
he pensado que antes de explicar este, intento explicar uno  mas sencillo, lo dejo aqui para cuando me ponga con el
este de aquí abajo es el sencillo:
CONTADOR= valor cualquiera

```
Delay:  decfsz   COMPTADOR,F
                    goto delay
```


Voy a intentar explicarlo:
decfsz valdra 1 instruccion cuando su su resultado sea diferente de 0 y 2 cuando sea 0
(CONTADOR -1 )x1ciclo+2 ciclos  <- hemos representado el tiempo del decremento.
Ahora le sumo el tiempo del goto
por la variable goto, pasar tantas veces como se reste 1 y no sea 0, es decir CONTADOR-1 y ahora le multiplicamos x2 por que nos lo dice la definicion.
(CONTADOR-1)x2

CONTADOR = n

[(n-1)x1+2] +(n-1)x2= (n-1)+2(n-1)+2= 3(n-1)+2

-------------------------------
a ver si me lo podeis revisar y decidme si esta bien calculado y la explicación esta bien.Si me lo confirmais, pasaré a calcular la formula del código grande 


Gracias


----------



## Miembro eliminado 356005 (Mar 13, 2015)

Sí, en los PIC, se suele tener que el ciclo de instrucción ocupa cuatro ciclos de reloj.

Sobre el cálculo de los ciclos de instrucción, creo que te lías un poco.

```
delay:  decfsz   CONTADOR,F    ; 1 o 2 ciclos
        goto     delay         ; 2 ciclos
```
En el documento DS31029A, en la página 29-22, donde se comenta el funcionamiento de DECFSZ, dice:

«_Los contenidos del registro 'f' se decrementa. Si 'd' es 0, el resultado se coloca en el registro W. Si 'd' es 1, el resultado se vuelve a colocar en el registro 'f'. Si el resultado es 0, la siguiente instrucción (leída durante la siguiente ejecución de instrucción) se descarta y se ejecuta en su lugar un NOP, haciendo de ella una instrucción de dos ciclos_». (Si no, se trata de una instrucción de un ciclo).

Entonces... mientras CONTADOR no llegue a 0, tenemos que DECFSZ consume 1 ciclo y ejecuta la siguiente instrucción (el GOTO), que consume 2 ciclos. Cuando CONTADOR llega a 0, DECFSZ consume 1 ciclo, más otro, para "saltar" el GOTO. Por eso decimos que si CONTADOR se inicializa a 'n', el número de ciclos consumidos es:  (n-1) * 3 + 2

Todo esto está explicado _anteriormente_.


----------



## PIC102 (Mar 13, 2015)

OKEY! entendido, pues en el codigo :


```
CO1            equ   0x20
                       CO2            equ   0x21
                       CO3            equ   0x22
                       RETARD1  equ   0xDA
                       RETARD2  equ   0x07

Delay_1seg:
                     movlw  RETARD1
                     movwf  CO2 
                     movlw  RETARD2
                     movwf  CO3
                     clrf   CO1

avanza:
                 decfsz  C01,F
                 goto    avanza
                 decfz   C02,F
                 goto    avanza
                 decfsz  C03,F
                 goto    avanza
                 return
```

Tenemos 4 instrucciones en el Delay que tardan 1 cada una , entonces el delay = 4 ciclos de instruccion.

En el avanza, para calcularlo, hacemos:
Contador1 = n1 = 0 =256
Contador2 = n2 = Retardo1= 218
Contador3 = n3 = Retardo = 7

Entonces, el tiempo de ciclos será = 
([(n1-1)x1+2] +(n1-1)x2 )+( [(n2-1)x1+2] +(n2-1)x2 )+( [(n3-1)x1+2] +(n3-1)x2 ) +4

¿Está bien?


----------



## Miembro eliminado 356005 (Mar 13, 2015)

Pues... no 

Sigue las instrucciones una a una... fíjate que los bucles están anidados...


----------



## PIC102 (Mar 14, 2015)

Lo siento Joaquin pero le estoy dando vueltas toda la mañana y me sale lo mismo que mi ultimo mensaje. No se resolverlo. A ver si me puedes ayudar.
Gracias


----------



## Miembro eliminado 356005 (Mar 14, 2015)

A ver...

Cuando la CPU llega a la etiqueta avanza, comienza el bucle sobre CO1.

Cuando termina ese bucle, decrementa en uno el valor de CO2. Y como no aún no ha llegado a 0 (pues comenzó en 218), regresa a avanza. Y allí se vuelve a encontrar con el primer bucle.

Se decrementa CO1 otra vez, que pasa de 0 a 255 y realiza OTRO bucle entero de 256 vueltas. Y cuando termina, vuelve a decrementar CO2 (pasa de 217 a 216) y regresa a avanza.

Como ves, el bucle de CO2 manda repetir 218 veces el bucle de CO1.

Y esto lo aplicamos a CO3: mandará repetir TODO lo anterior 7 veces. Por eso hablamos de bucles anidados. Pero, ¡ojo!, a la segunda vez que CO3 manda repetir los dos bucles internos, CO2 NO empieza en 218, sino que empieza en 0, por lo que CO2 será un bucle de 256 vueltas. Esto complica un poco más el cálculo.

Te repito que todo esto está explicado en el enlace que te puse en el mensaje #29, más arriba. Aunque se refiere a un tipo de anidamiento ligeramente distinto, el cálculo es muy parecido.

Muéstranos qué tienes hecho hasta ahora.


----------



## PIC102 (Mar 14, 2015)

Me he leído de arriba abajo tu documento varias veces.Está genial explicado.

He visto que conoces una pagina que nos dio nuestro profesor para consultar los ciclos. Aun no se muy bine como funciona la pagina , pero creo que es interesante.3

http://www.piclist.com/techref/piclist/codegen/delay.htm

Vale. pues ahora ya te entiendo mejor. Cuando acabamos el primer bucle del contador 1 pasamos al contador 2 y este cuando su resultado es diferente de 0, vuelve al principio de delay, volviendo activar el bucle del contador 1.

Tambien tengo que tener en cuenta que el contador1 siempre será 0 cuando pasemos por él.Mientras el contador 2, en cuando volvamos a pasar por el a causa del contador 3, ya estará a 0, y tardará 255 ciclos.

Entonces, mis conclusiones.
Un bucle de decremento = 3(n-1)+2 por defecto

Hay bucle anidado, por lo que cada goto es 1+2xG
G= al numero de goto que vemos por debajo del decremento que nos localicemos.

Ejemplo, el decremento del contador 1 tiene por debajo 3 goto, H=3, por lo tanto 1+6 =7

Resuemiendo:

Contador1 = n1 
Contador2 = n2 
Contador3 = n3 

7[3(n1-1)+2]+5[3(n2-1)+2]+3[3(n3-1)+2]

Pero esta formula seria para un delay donde todos los contadores están inicializados a 0 desde un principio.

Pero ahora tengo dudas en como fragmento la formula 5[3(n2-1)+2] para que me cuente cuando el primer bucle son 218 y luego 256

Por lo que creo que se sería : 1[3(*218*-1)+2] + 4[3(*256*-1)+2]


No se si estoy bien encamiendo.

Gracias


----------



## Miembro eliminado 356005 (Mar 14, 2015)

Según el StopWatch de MPLABX, la cifra final, sin contar el 'return', debe ser de 1 350 598 ciclos de instrucciones (cifra obtenida según los parámetros 0, 218 y 7, iniciales).

Si la primera vuelta del ciclo más exterior tenía inicialmente C02 = 218, entonces, 7-1 vueltas siguientes C02 valdrá 256.


----------



## PIC102 (Mar 15, 2015)

Hola, buenos dias

Esta mañana estoy con este código



```
comprueba_10:
             sublw  0x0A
             btfsc  STATUS,Z
             retlw  0x00
             btfss  STATUS,C
             retlw  0xFF
             retlw  0x01
```

Entiendo que lo que hace es mirar si lo que tenemos dentro dentro del acumulador es 10 o no.
Resta 10 a w, si es 0, devuelve el valor 0, sino es 0, mirar si el resultado es negativo, si es asi, devuelve -1 y sino , devuelve 1.

Lo que no entiendo porque ponemos el registro STATUS, si lo que usamos es para cambiar de banco, y tambien me lia lo de C,Z,DC, no entiendo bien esto del carry .

A ver si me podeis aclarar un poco.

Gracias


----------



## Miembro eliminado 356005 (Mar 15, 2015)

No, no es así.

No estamos restando 10 a W. Es justo al revés: estamos restando W a 10, y el resultado queda en W.


```
comprueba_10:
        sublw  0x0A        ; 10 - (W) => W
        btfsc  STATUS,Z    ; ¿el resultado es 0?            (salta si Z=0)
        retlw  0x00        ; sí, regresa con W =  0
        btfss  STATUS,C    ; no, ¿el resultado es positivo? (salta si C=1)
        retlw  0xFF        ; no, regresa con W = -1
        retlw  0x01        ; sí, regresa con W =  1
```

El registro *STATUS* (03h) no solo sirve para cambiar de banco, sino que también almacena los bits *C* y *Z*, que guardan el resultado de la última operación.

*Z = 1* : el resultado de una operación aritmética o lógica es cero
*Z = 0* : el resultado de una operación aritmética o lógica no es cero

*C = 1* : ha ocurrido un acarreo en el bit de mayor peso después de la operación anterior
*C = 0* : no ha ocurrido el acarreo

El bit *DC* es lo mismo que *C*, pero referido a operaciones de _nibbles_ (4 bit).

Al hacer una resta, si la operación dio un número negativo, no ocurre el acarreo, y *C* se pone a 0. ¡Ojo! No vale con decir que mirando solamente el valor de *C* podemos saber si el resultado es positivo o negativo. Hay que mirar siempre ANTES el valor de *Z*.

Resumen:


C | Z | significado
0 | 0 | negativo
1 | 0 | positivo
x | 1 | es 0
Lo tienes explicado es las páginas de manual de SUBLW.

(Cuidado: el significado de C cambia si la operación es ADDLW o ADDWF)


----------



## PIC102 (Mar 16, 2015)

Increible explicación, super clara. Gracias Joaquin


----------



## PIC102 (Mar 16, 2015)

Hola Joaquin.
He tenido tutoria con el profesor para aclararme de una vez lo del tiempos. Y bueno, me ha dicho que habia un pequeño error, que deberia haber puesto que se volviera a cargar el valor en CO2 el que estaba predeterminado.

(y que sí , que tenia razon-tu tenias razon- que cuando volviera a pasar , el contador era 0 y que era follon hacer el calculo)

Es decir, en un principio teniamos esto:


```
CO1            equ   0x20
                       CO2            equ   0x21
                       CO3            equ   0x22
                       RETARD1  equ   0xDA
                       RETARD2  equ   0x07

Delay_1seg:
                     movlw  RETARD1
                     movwf  CO2 
                     movlw  RETARD2
                     movwf  CO3
                     clrf   CO1

avanza:
                 decfsz  C01,F
                 goto    avanza
                 decfz   C02,F
                 goto    avanza
                 decfsz  C03,F
                 goto    avanza
                 return
```

Pues me ha dicho que seria así:

```
CO1            equ   0x20
                       CO2            equ   0x21
                       CO3            equ   0x22
                       RETARD1  equ   0xDA
                       RETARD2  equ   0x07

Delay_1seg:
                     movlw  [COLOR="Green"]RETARD2[/COLOR]
                     [COLOR="green"]movwf  CO3[/COLOR] 
[COLOR="Red"]Carga_CO2 [/COLOR]          [COLOR="green"]movlw  RETARD1[/COLOR]
                     [COLOR="green"]movwf  CO2[/COLOR]
                     clrf   CO1

avanza:
                 [COLOR="Blue"]decfsz  C01,F
                 goto    avanza[/COLOR]
                 [COLOR="DarkSlateGray"]decfz   C02,F
                 goto    avanza[/COLOR]
                 [COLOR="yellow"]decfsz  C03,F
                 goto    CARGA_CO2[/COLOR]
                 return
```

y el los tiempos quedarían :

Tendriamos: (he agrupado en colores los decrementos)
A (   por ejemplo siendo A-> 3(CO1-1)*TIEMPOCICLO+2*TIEMPOCICLO     )
B
C

y segun el quedaria así: 
5*TIEMPOCICLO+(A*B*C)*TIEMPOCICLO * 2*TIEMPOCICLO

Tu crees que así esta bien?

Gracias.
---------------------------------------------------------------------------------------------------------------------------
Ya que estamos a ver si puedes ayudarme con el direccionamiento indirecto, que esto es un autentico lio.

Primeramente , no se que quiere decir/ o para que sirve.
Lo unico que encuentro es que es para mover cosas de sitios y  no hacerlo directamente pero sigo sin entenderlo.


En este ejemplo,que es un subrutina que inicializa las posiciones de memoria.No consigo entender el direccionamiento. 


```
inicializa:
            movlw  0x30 //
             movwf  FSR // el valor 48 -> al acumulador -> y lo paso al registro FSR
             
repite:
            clrf   INDF,F //  limpio INDF es un registro, pero ese F , creo que sobra
            incf  INDF,F//Incrementa el contenido de INDF en una unidad.
 El resultado se almacena de nuevo en INDF si F=1 y en W si F=0(en este caso INDF no varía) 

¡¡PERO NO SABEMOS EL VALOR DE F!!!



             incf   FSR,F // lo mismo de antes 
             btfss  FSR,6 // aqui ya me pierdo
             goto   repite
             return
```

Joaquin a ver si me ayudas a saber como tira esto de direccionamiento indirecto, ya que lo veo ilógico .

Gracias por todo.


----------



## Miembro eliminado 356005 (Mar 16, 2015)

PIC102 dijo:


> He tenido tutoría con el profesor para aclararme de una vez lo del tiempos. Y bueno, me ha dicho que había un pequeño error, que debería haber puesto que se volviera a cargar el valor en CO2 el que estaba predeterminado.
> 
> (y que sí, que tenía razón -tú tenías razón- que cuando volviera a pasar, el contador era 0 y que era follón hacer el cálculo).


De follón, nada. Es cuestión de aplicar las matemáticas.

Adjunto hoja de cálculo con el resultado del cálculo. Haciendo

C01 = 172
C02 = 19
C03 = 6

se obtienen exactamente un millón de ciclos de espera, que funcionando en un micro a Fosc = 4 Mhz (Fcyc = 1 Mip) obtenemos exactamente 1 s de espera:

```
;
; Prueba de delays con bucles anidados
;

    processor   16F877A                     ; 4 Mhz
    radix       dec
    errorlevel  -302                        ; Turn off banking message


;*******************************************************************************
; Bibliotecas

    include p16f877a.inc

    __CONFIG   _CP_OFF &  _WDT_OFF & _PWRTE_ON & _XT_OSC	; Configuración

    ORG     0x0000


	cblock 0x70
        C01
        C02
        C03
	endc


Delay:
                        ; 6 ciclos
        movlw	172
        movwf	C01
        movlw	19
        movwf	C02
        movlw	6
        movwf	C03

bucle:
                        ; 999 989 ciclos
        decfsz  C01,F
        goto    bucle

        decfsz  C02,F
        goto    bucle

        decfsz  C03,F
        goto    bucle

        nop             ; 1 ciclo
;       goto    $+1     ; 2 ciclos

                        ; 4 ciclos, 2 del return y 2 ciclos del call
        return

        END
```
Con más detalle:

6 ciclos que se consumen en la carga de los registros,
999 989 ciclos que se consumen en los tres bucles anidados,
4 ciclos extra que tenemos por la ejecución del return y del call,
1 ciclo extra que necesitamos con la ayuda de un nop.

Todo eso suma un millón de ciclos.

La fórmula general del cálculo de ciclos O para un determinado nivel n de profundidad es:

O = O(n-1|par.1) + (par.n -1) * O(n-1|256) + (par.n -1) * 3 + 2



> ciclos consumidos en este nivel =
> 
> ciclos consumidos por el nivel anterior, inicializado con el parámetro +
> 
> ...





PIC102 dijo:


> Tendríamos: (he agrupado en colores los decrementos)
> A (   por ejemplo siendo A-> 3(CO1-1)*TIEMPOCICLO+2*TIEMPOCICLO     )
> B
> C
> ...


Pues... no sé de dónde sale esa fórmula. No conozco el desarrollo.

A mi me sale otro resultado. Ejecutando este segundo código en el MPLAB X IDE, con los parámetros 0, 218 y 7, me sale que generan 1 175 063 ciclos (sin contar otros 4 del return+call).

En este caso, hay que tener en cuenta que hay que sumar 4 ciclos de la recarga de los registros C02 y C01, multiplicados por C03.

En la hoja dejo el desarrollo. Con los parámetros

C0 = 112
C1 = 217
C2 = 6

más un nop y 5 goto $+1 extras, ya me sale un millón de ciclos, coincidente con lo que sale en MPLAB X IDE:

```
;
; Prueba de delays con bucles anidados con recarga
;

    processor   16F877A                     ; 4 Mhz
    radix       dec
    errorlevel  -302                        ; Turn off banking message


;*******************************************************************************
; Bibliotecas

    include p16f877a.inc

    __CONFIG   _CP_OFF &  _WDT_OFF & _PWRTE_ON & _XT_OSC	; Configuración

    ORG     0x0000


	cblock 0x70
        C01
        C02
        C03
	endc


Delay:
                        ; 2 ciclos
        movlw	6
        movwf	C03
                        ; 999 983 ciclos
Delay_recarga:
        movlw	217
        movwf	C02
        movlw	112
        movwf	C01

Delay_bucle:
        decfsz  C01,f
        goto    Delay_bucle

        decfsz  C02,f
        goto    Delay_bucle

        decfsz  C03,f
        goto    Delay_recarga

        nop             ; 1 ciclo
        goto    $+1     ; 2 ciclos
        goto    $+1     ; 2 ciclos
        goto    $+1     ; 2 ciclos
        goto    $+1     ; 2 ciclos
        goto    $+1     ; 2 ciclos

                        ; 4 ciclos, 2 del return y 2 ciclos del call
        return

        END
```


----------



## PIC102 (Mar 17, 2015)

La verdad que lo tuyo es mas logico Joaquin. Me lo voy a estudiar y quizas se lo comente al profe.

Y..sobre mi segunda parte del mensaje, sabrias deducir el código.Te lo pego.

---------------------------------------------------------------------------------------------------------------------------
Ya que estamos a ver si puedes ayudarme con el direccionamiento indirecto, que esto es un autentico lio.

Primeramente , no se que quiere decir/ o para que sirve.
Lo unico que encuentro es que es para mover cosas de sitios y  no hacerlo directamente pero sigo sin entenderlo.


En este ejemplo,que es un subrutina que inicializa las posiciones de memoria.No consigo entender el direccionamiento. 


```
inicializa:
            movlw  0x30 //
             movwf  FSR // el valor 48 -> al acumulador -> y lo paso al registro FSR
             
repite:
            clrf   INDF,F //  limpio INDF es un registro, pero ese F , creo que sobra
            incf  INDF,F//Incrementa el contenido de INDF en una unidad.
 El resultado se almacena de nuevo en INDF si F=1 y en W si F=0(en este caso INDF no varía) 

¡¡PERO NO SABEMOS EL VALOR DE F!!!



             incf   FSR,F // lo mismo de antes 
             btfss  FSR,6 // aqui ya me pierdo
             goto   repite
             return
```

Joaquin a ver si me ayudas a saber como tira esto de direccionamiento indirecto, ya que lo veo ilógico .

Gracias por todo.

Con todo lo que se ahora creo que el examen del jueves me irá bien


----------



## Miembro eliminado 356005 (Mar 17, 2015)

> 6.3.4 Direccionamiento indirecto, registros INDF y FSR
> 
> El direccionamiento indirecto es un modo de direccionamiento de la memoria de datos, donde la dirección de la memoria de datos en la instrucción, no es fija. El registro FSR se usa como puntero a la posición de la memoria de datos que va a ser leída o escrita. Ya que es un puntero en RAM, los contenidos se pueden modificar por programa. Esto puede ser útil para tablas de datos, en la memoria de datos. La figura 6-7 muestra la operación del direccionamiento indirecto. Muestra el movimiento del valor a la dirección de la memoria de datos especificada por el valor del registro FSR.
> 
> El direccionamiento indirecto es posible usando el registro INDF. Cualquier instrucción que use el registro INDF, lo que realmente hace es acceder al registro apuntado por el _File Select Register_ (FSR). Leyendo indirectamente (FSR = '0') el propio registro INDF, se leerá 00h. Escribiendo indirectamente al registro INDF resultará en una no-operación (aunque afectará a los bits de estado). Una dirección de efectiva de 9 bit se genera por la concatenación del bit IRP (STATUS<7>) con los 8 bit del registro FSR, como se muestra en la figura 6-8.




```
inicializa:
            movlw  0x30    ; inicializamos FSR = 0x30
            movwf  FSR

repite:
            clrf   INDF    ; pone a 0 el valor apuntado por FSR
            incf   INDF,f  ; pone a 1 el valor apuntado por FSR

            incf   FSR,f   ; incrementamos el puntero FSR

            btfss  FSR,6   ; ¿Hemos llegado a 0x40? (salta si FSR<6> = 1)

            goto   repite  ; no, repetimos el bucle

            return         ; sí, salimos
```
Lo que hace este código es poner a 1 todos los bytes que van de la dirección 0x30 a la 0x3F (suponiendo que bit IRP = 0, claro).


----------



## PIC102 (Mar 18, 2015)

Una duda rápida.

tabla:
 addwf PCL,F ; 
 retlw b'11000000' ; regresa con 0 
 retlw b'11111001' ; regresa con 1 
 retlw b'10100100' ; regresa con 2 
 retlw b'10110000' ; regresa con 3 
 retlw b'10011001' ; regresa con 4 
 retlw b'10010010' ; regresa con 5 
 retlw b'10000010' ; regresa con 6 
 retlw b'11111000' ; regresa con 7 
 retlw b'10000000' ; regresa con 8 
 retlw b'10010000' ; regresa con 9  


Si por ejemplo:
-el contador del programa esta en la posicion 5
-hacemos de molvw 2
-entonces addwf PCL = 6(posicion siguiente) + 2 = 8
-vamos al `regresa 2´, volvemos al programa y metemos en W= 10100100

Mi duda es :
La cosa se queda aquí  o automaticamente  vuelve ha sumar el PCL + W , siendo W ahora = 10100100


Gracias.


----------



## Miembro eliminado 356005 (Mar 18, 2015)

No, la instrucción *retlw* hace regresar inmediatamente, con el valor de W puesto al literal indicado.

retlw (*ret*orno con un *l*iteral en *W*)


----------



## PIC102 (Mar 18, 2015)

Muchas gracias Joaquin, ya entiendo el programa.Lo que he puesto era solo un trozo.
Lo explico por si alguien tiene interés.

Cuando copilamos todo el código en el programa ,escribimos en memoria, y activamos uno o mas interruptores de nuestro PIC, este interruptor o esta combinacion de interruptores, hace dar un valor a W .Consecuentemente , esto nos hace ir una posición de la tabla y recoger el valor que tenemos dentro,el cual activará una serie de displays en la pantalla. (El numero al lado de `regreso´


----------



## PIC102 (Mar 20, 2015)

El examen me fue bastante bien, aunque hubo alguna pregunta casi imposible, pero por lo demas todo bien.

No me tengo que relajar y , tengo que seguir trabajando y estudiando!!

Muchas gracias por la ayuda!!!


----------



## Miembro eliminado 356005 (Mar 20, 2015)

¿Pregunta imposible?

¿¿¿Cuál???


----------



## PIC102 (Mar 21, 2015)

Fue una pregunta que no tenia nada que ver con lo que había estudiado y me pilló en esa.
Te la adjunto en imagen.

Pero bueno, ya ha pasado.


----------



## Miembro eliminado 356005 (Mar 21, 2015)

Madre mía... ¡Y con faltas de ortografía!

Pongo aquí el texto corregido:

«La velocidad de giro de un disco compacto se puede medir a partir de la lectura del código de barras situado en la pista interior._La lectura de las barras generan un pulso por cada avance de 2_°, que se utiliza como señal de interrupción *a un *PIC._Asumiendo un lector de velocidad 24x, que equivale a un máximo *de velocidad *de giro de 7200 revoluciones por minuto*. ¿Cuál* es el valor máximo de la RSI en el peor de los casos?*.* Asuma una frecuencia de trabajo de 4 MHz».

No estoy muy seguro, pero sería algo así:

Si la velocidad más alta de giro es 7200 rev./m, tenemos

7200 rev./m / 60 s/m = 120 rev./s

Si cada rev./s son 360°,

120 rev./s * 360 °/rev. = 43 200 °/s

Si hay un pulso cada 2 °, tenemos que la velocidad más alta de pulsos es

43 200 °/s / 2 °/pulso = 21 600 pulsos/s

A partir de ahí hay que calcular la RSI, que, a propósito, no sé qué significa


----------



## Nuyel (Mar 21, 2015)

No se si será una versión en español para ISR o sea Rutina de Servicio de Interrupción.


----------



## PIC102 (Abr 20, 2015)

Hola a todos!!  
Estoy de vuelta con esta asignatura.Me fue bien el examen y la he dejado un poco abandonada y me tengo que poner de vuelta con ella ya que tengo que entregar en un par de semanas un proyecto y tengo un examen y necesito aprender temario.

Vale, pues estamos ahora en el temario de :
Interficie de circuitos externos y comunicaciones.

Hemos dado la explicación de lo:"Maestro y esclavo"
Lo de llamar a un periférico para darle una información y este te conteste o te haga algo.Lo del : SCLK,SS,MOSI y MISO.
Pues bien, en clase solo hemos dado teoria y por eso os pedia si teneis algún código "sencillo" de principiante para entender esto de maestro y esclavo para ver como funciona, ya que no tengo ejemplos

Muchas gracias !!!


----------



## bpenword (Ago 12, 2020)

Bueno, recién estudiando ensamblador con PIC quise probar manejar un poco las operaciones por condición.
Quiero dividir cierto numero entre 2 ó 4 dependiendo de si es mayor a otro.
Me parece que dividir corriendo los bits, con la instrucción RRF, sería más fácil.

```
__CONFIG    0X1F39
    INCLUDE <P16F877A.INC>
    LIST P=16F877A

    CBLOCK  0X50
NUMBER1
NUMBER2
TOTAL
MP
NP
    ENDC   

NUMB3    EQU 0X58

    ORG   0
    BSF   STATUS,5
    MOVLW B'11111111'
    MOVWF TRISB
    MOVLW B'11111111'
    MOVWF TRISC
    MOVLW B'00000000'
    MOVWF TRISD
    MOVLW D'6'
    MOVWF ADCON1
    BCF   STATUS,5
    CLRF  PORTB
    CLRF  PORTC
    CLRF  PORTD
    MOVLW D'20'
    MOVWF NUMB3

  
PRINCIPAL
    MOVF  PORTB,0
    MOVWF NUMBER1
    MOVF  PORTC,0
    MOVWF NUMBER2
    MOVF  NUMBER1,0
    ADDWF NUMBER2,0 ;SUMA NUM2 CON W-->Y LO PASA A W
    MOVWF TOTAL
;;;;;;;CONDICION;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;     
    MOVF     NUMB3,0
    SUBWF    TOTAL,W ;COMPARO NUMB3 CON TOTAL
    BTFSC    STATUS,Z
    GOTO     IGUALES
    BTFSC    STATUS,C
    GOTO     MAYOR
    GOTO     MENOR
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

IGUALES
    MOVF  TOTAL,0
    MOVWF PORTD
    GOTO  PRINCIPAL

MAYOR
    MOVF TOTAL,0
    MOVWF MP       
    RRF   MP,1      ;DIVIDO MP ENTRE 2
    MOVF  MP,0
    MOVWF PORTD
    GOTO  PRINCIPAL

MENOR
    MOVF TOTAL,0
    MOVWF NP
    RRF   NP,1        ;DIVIDO NP ENTRE 4
    RRF   NP,1
    MOVF  NP,0
    MOVWF PORTD
    GOTO  PRINCIPAL

    END
```
Pero sucede que el PIC parece saltar nada mas a la parte de si el numero es menor.
¿Qué estoy implementando mal, la parte de la condición o uso mal la instrucción RRF?
Gracias.


----------



## switchxxi (Sep 9, 2020)

Recuerda que en el caso de una resta el bit CARRY del registro STATUS funciona con lógica invertida.


----------

