Lenguaje ensamblador para pic ?

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 :confused:
 
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.;)
 
Hola amigos. Soy estudiante y sé 0 de todo lo relacionado con un PIC.
Necesitaría ayuda para comprender desde 0 este código:

PHP:
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.
 
Última edición por un moderador:
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.
 
PHP:
        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).
 
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!! :):)
 

Adjuntos

  • pull.png
    pull.png
    259.5 KB · Visitas: 21
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.
 
Última edición por un moderador:
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.
 
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.
 
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.
 
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:

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.
 
Última edición por un moderador:
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:

Código:
	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.

Código:
	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):

Código:
	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:

Código:
	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).
 
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
Código:
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
Código:
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)
 
Última edición por un moderador:
Atrás
Arriba