# Lenguaje ensamblador para PIC desde 0



## Leon Elec (Nov 6, 2008)

He comenzado a realiza un tutorial de ASM y lo voy a poner aquí también.

A pesar que mucho le escapan a este lenguaje, hay otros que nos sentimos cómodos con el .

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

Lo primero que hay que saber para este lenguaje, es que cada línea de código, es una instrucción que realiza el CP o un paso que realiza el CP. A diferencia de otros lenguajes, como por ejemplo el C, una línea de instrucción, puede llevar uno o mas pasos que el CP debe realizar. Más adelante entenderán el porqué.

A esto último dicho, nos explica el porqué de un programa escrito en ASM (assembler) lleva menos línea de instrucción que el mismo programa realizado en otro lenguaje. Esto se traduce que, cuanto menos líneas de código, menor consumo de la memoria de programa.

*Nota:* Entendemos por el mismo código, al realizar un software para realizar una dicha tarea. Una tarea que debe realizar nuestro microcontrolador, puede ser escrito en diferentes lenguajes, y es el programa que se utilizó quien lo traduce a formato hex (1 y 0).

Ahora, *¿Qué es el CP?*

El CP o PC es el Contador de Programa. Es el encargado de leer cada instrucción y realizar la acción solicitada por el software.

Para dar un ejemplo un poco más claro, digamos que el CP, es una persona. Esta persona se le da un papel que tiene diferentes tareas a realizar. Estas tareas, están una debajo de la otra y lee línea por línea y hace lo que le dice esa línea. Cada línea, posee una instrucción que se debe hacer. Cuando terminó de hacer esa instrucción, continua con la línea de abajo.

Supongamos que tenemos un papel que dice:

Levantar la mano izquierda.
Bajar la mano izquierda.
Saltar tres veces en el mismo lugar.
Levantar la mano derecha.
Saltar una vez en el mismo lugar.
Bajar la mano derecha.

El CP, hará esas tareas sin negarce y lo hará fielmente a lo que está escrito. Por lo que, si hace una tarea mal, es porque le pusimos una o más instrucciones mal.

Si bien, el CP hará lo que nosotros le pidamos, el CP nos pide que respetemos algunas cosas. Estas cosas, dependerán de cada microcontrolador y que debemos saber para poder programar en forma correcta al PIC.

PD: Tube que duplicar el nombre del tema, porque no me dejaba enviarlo (se ve que acepta un mínimo de caracteres)


----------



## Leon Elec (Nov 6, 2008)

*¿Cómo es la extructura del lenguaje ASM?*

Es muy fácil, posee 4 columnas bien diferenciadas y que no es problema acordarce.

* La primer columna se llama *ETIQUETA* y sirve para darle el nombre a una pocición de la memoria del programa al que se necesita apuntar. Los que no se den cuenta, ya lo haran.

* La segunda columna, se llama *INSTRUCCIÓN* y lo que justamente hace, es una instrucción a realizar por el CP.

* La tercera columna, se llama *OPERANDO* y es el operando de una instrucción, o sea, de la segunda columna. Hay instrucciones que no tienen operando y las veremos más adelante.

* Y la cuarta columna, se llama *OBSERVACIONES* y sirve solo para el programador, o sea, el usuario que está programando el PIC. En otras palabras, a nosotros. Siempre comienza con ; (punto y coma)

Si no sirve para el PIC, ¿Para qué complicarla más? Todo lo contrario, es para alludar al programador de que no se olvide que intentó hacer.

Todos los lenguajes, desde los más básicos, hasta los más avanzados, tienen esta características ya que en el, pondremos que es lo que queremos hacer o explicar el programa.

Si bien, estas observaciones se utilizan en la cuarta columna, no es obligación colocarla en esta columna, si no que puede ir al margen de la planilla en dónde estamos programando. Y es aquí dónde explicaremos la porción de una rutina para entenderlo más adelante.

Bien, veremos a continuación, como se escriben las columnas:

*ETIQUETAS           INSTRUCCIÓN            OPERANDO              ;OBSERVACIONES*

O, podemos verlo así:

*;OBSERVACIONES*
*;OBSERVACIONES*
*;OBSERVACIONES (y la cantidad que necesitemos)*

*ETIQUETAS           INSTRUCCIÓN            OPERANDO               ;OBSERVACIONES*


----------



## Leon Elec (Nov 6, 2008)

Antes de empezar a ver las intrucciones, debemos concentrarnos en los registros.

*¿Qué son los registros?*

Los registros, son pociciones de memoria el cual se utilizan para ir configurando el pic mientras se corre el programa, cambiar de bancos para acceder a otras partes de memorias, son banderas que nos van diciendo que está pasando con distintas operaciones, son habilitaciones o desabilitaciones para módulos que trae el PIC. Por ejemplo, conversor Analógico/Digital, PWM.

Estos registros, tienen una ancho de bit de acuerdo al PIC, que hay de 8 bit, 16 bit y 32 bit. Excepto la palabra configuración y que la veremos mas adelante. El tamaño de los registro depende del tipo de microcontrolador.

Cada registro, posee un nombre, y cada bit o un grupo de bit, se puede utilizar para lo descripto anteriormente (habilitar/desabilitar, etc).

A continuación, ver el archivo adjunto. Es la pocición de la RAM y sus 4 bancos. Este, es del PIC16F877

Como notarán, en las cuatro columnas, hay nombres. Estos nombres son todos los registros que posee este PIC. Verán que hay registros que se repiten. No quiere decir que estan duplicados, triplicados o cuatriplicados, si no que se puede acceder a ellos no importa en que banco estemos trabajando. (Ya lo entenderán, no se preocupen).

Cada vez que se programe el PIC y se necesite trabajar con uno de estos registros, se debe acceder al banco que esté dicho nombre. Es por eso, que esta tabla es muy importante a la hora de trabajar.

Aprovechando que subí esta tabla del pic, cuando dea ejemplo de programación, será sobre este pic.

Microchip provee en forma gratuita estos datasheet. Y hay uno por cada PIC. Por lo que si no tienen este datasheet, descargenlo de la página de Microchip. Cuando empezemos a programar, lo necesitarán.


----------



## Leon Elec (Nov 6, 2008)

Uno de los registros muy utilizado, es el registro de trabajo W, el cual, se utiliza para mover un dato/valor de un registro a otro, cargar valores en un registro, y con la ayuda de la ALU puede hacer operaciones matemáticas.

Observen con atención el diagrama de bloque dónde se encuentra W que está adjuntado.

Ahora bien, si W es un registro, _¿dónde está ubicado, ya que en el mapa de memoria no lo encuentro?_

El registro W no está implementado en la memoria ram en la posición 0x00 ni en otra posición. W es un registro independiente que tiene un bus directo con la ALU (un camino privado). La ALU es la única entidad que puede leer o escribir este registro de trabajo.

El direccionamiento indirecto hace uso de los registros FSR e INDF. INDF es completamente ajeno a W. Un direccionamiento indirecto usa el registro FSR como apuntador al contenido de otros registros. Cualquier instrucción que hace uso de INDF (0x00) como dirección invoca un direccionamiento indirecto.

Los microcontroladores PIC, poseen tres tipos de memorias.

** Memoria de Programa*
               Es la ubicación física dónde se guarda el firmware que hemos creado, o sea, nuestro programa. Y tiene un ciclo de 100.000 de lectura y/o escrituras antes de estropearce y es del tipo FLASH (pero este es flaco  :mrgreen: Es un chiste).

** Memoria de datos de uso general*
               Es la memoria RAM del PIC. Recordemos, que los registros están sobre la memoria RAM y la memoria de uso general, comienza después de los registros.

** Memoria de datos EEPROM*
               Es la memoria EEPROM que puede almacenar datos mas de 40 años sin energía y 1.000.000 de ciclos de escritura y lectura

El PIC 16F877, tiene una memoria de programa de 8Kb por un ancho de 14 Bits, o sea que cada pocición de memoria tiene 14 bit (B'11111111111111', o H'3FFF') Cada línea de instrucción ocupa una pocición en la memoria de programa, asi que, podemos poner hasta 8.192 instrucciones.

La RAM de uso general (mas los registros) trae 368 bytes con un ancho de 8 bit (B'11111111', o H'FF'). Esto quiere decir, que tenemos 368 pociciones para nuestro uso.

La EEPROM trae 256 bytes con un ancho de 8 bits. Tenemos 256 pociciones para nuestro uso.


----------



## Leon Elec (Nov 6, 2008)

Ahora, vamos a estudiar el registro *STATUS* y luego continuaremos con las instrucciones.


*Registro STATUS*

Si entendieron hasta acá, se acordará que los registros poseen un ancho de 8 bit. Cada bit, puede contener un 1 o 0.

Vemos ahora en detalle el registro STATUS (está adjuntado)

Este registro, tiene 3 Bit dedicado para las operaciones matemáticas, 3 bit dedicado al cambio de banco de memoria y 2 bit dedicado para saber que o quién produjo un Power Up (despertar del micro). Y se puede leer y escribrir en él (cambiar datos).

Los analizamos desde el más significativo (MSB) hasta el menos significativo (LSB).

*BIT 7:*

Se llama *IRP* y sirve para el direccionamiento indirecto para cambiar el banco de memoria.

1 = Banco 2 y 3
0 = Banco 0 y 1

*BIT 6 y BIT 5*

Se llaman *RP1 y RP0* respectivamente. Sirve para el direccionamiento directo para cambiar de banco de memoria.

00 = Banco 0
01 = Banco 1
10 = Banco 2
11 = banco 3

*BIT 4*

Se llama *TO (neg).* Este bit se utiliza para saber quién despertó al PIC.

1 = Después que despierta (power up) o por las instrucciones CLRWDT o SLEEP, se pone a 1 este bit.
0 = Se pone a 0 cuando el wachtdog o en castellano perro guardían (WDT) despierta al PIC.

*BIT 3*

Se llama *PD (neg).* Este bit se utiliza para saber si el pic estaba durmiendo.

1 = Después de que despierta (power up) o por la instrucción CLRWDT, se pone a 1
0 = Se pone a 0 cuando se ejecuta la instrucción SLEEP

*BIT 2*

Se llama *Z* y al igual que los dos bytes anteriores, es una bandera. Nos indica el resultado de una operación aritmética y lógica.

1 = La operación aritmética o lógica dió como resultado 0
0 = La operación aritmética o lógica no dió como resultado 0

*BIT 1*

Se llama *DC.* Digit carry/borrow (dígito llevar/prestar). Es afectado por las instrucciones ADDWF; ADDLW; SUBLW; SUBWF (Para la resta, la polaridad es inversa).

1 = Hubo un acarreo del 4to bit menos significativo al 5to bit.
0 = No hubo un acarreo del 4to bit menos significativo al 5to bit.

*BIT 0*

Se llama *C* carry/borrow. Es afectado por las mismas instrucciones que afectan al bit DC.

1 = Hubo un accareo del bit más significativo (Bit 7) o sea cuando se exede de H'FF'
0 = No hubo accareo del bit más significativo


----------



## Leon Elec (Nov 6, 2008)

*Breve instroducción de las INSTRUCCIONES*

Cada instrucción tiene una ancho de 14 Bits, es por eso que la memoria de programa tiene el mismo ancho. Justamente para poder alojar cada instrucción.

Las instrucciones, están divididas en tres grupos. Los cuales son:



*Byte-Oriented* operation (Byte-Orientando a la operación)
*Bit-Oriented* operation (Bit-Orientando a la operación)
*Literal and Control* operation (Control y Literal de la operación)

_Primer grupo Byte-Oriented operation_

Cada instrucción de este grupo está compuesta por:



OPCODE (Código)
DESTINATION (Destino)
FILE REGISTER ADDRESS (Dirección del archivo de registro)

El OPCODE o código, es el código de cada instrucción y que es única para cada instrucción. Está formada por los bit del 13 al 8.

El DESTINATION o el destino, indica en dónde se va a guardar el dato.Por ejemplo, si hacemos una suma, tenemos dos opciones dónde guardarlo, una puede ser el registro W y la otra opción puede ser otro registro cualquiera o una pocición de la RAM. Está formada por el bit 7.

La constante que nos indica esto es la letra d. Si esta letra es 0, la operación se guardará en el registro W. EN cambio si vale 1, la operación se guardará en el registro o pocición de memoria que estemos trabajando al momento de usar una instrucción.

Hay instrucciones, como veremos más adelante, que no es necesario indicar dónde queremos guardar la operación, ya que se hace en forma automática. Y hay otras instrucciones que si no se indica el destino, nos puede dar un error al compilar o el compilador lo eligirá el y por ende, nos puede ejecutar mal el programa.

Y por último, tenemos el FILE REGISTER ADDRESS que se carga con la dirección del registro a ser guardado. Está formada por los bit 6 al 0. La constante que nos indica esto, es la letra f

_Segundo grupo Bit-Oriented operation_

Cada instrucción de este grupo está compuesta por:



OPCODE (Código)
BIT ADDRESS (Bit de dirección)
FILE REGISTER ADDRESS (Dirección del archivo de registro)

OPCODE es igual al primer grupo. Está formado por los bits 13 al 10.

El BIT ADDRESS, se utiliza para direccionar la operación. Está formado por los bits 9 al 7. Como pueden observar, se sacrificó bit del opcode para dárcelo al bit address. La constante que nos indica esto es la letra b

Y por último tenemos FILE REGISTER ADDRESS, que es igual al primer grupo. Está formado por los bit 6 al 0. Igual que en el primer grupo. la constante que nos indica esto es la letra f.

_Tercer grupo Literal and Control_

Cada instrucción de este grupo, está compuesta por:



OPCODE
LITERAL

OPCODE es igual que en el primer grupo. Está compuesta por los bits 13 al 8. Exepto para las instrucciones CALL y GOTO que está compuesta por los bit 13 al 11 (prestar mucha atención a esto, cuando veamos estas dos instrucciones entenderán la importancia).

Y el LITERAL que puede ser un valor, por ejemplo para sumar, para restar, para cargar al registro W, en fin, un número decimal, binario o hexadecimal. O puede ser una valor de dirección a dónde apuntar para las instrucciones CALL y GOTO.

Está compuesta por los bits 7 al 0. Exepto para las instrucciones CALL y GOTO que está compuesta por los bit 10 al 0 (prestar mucha atención a esto, cuando veamos estas dos instrucciones entenderán la importancia).

Está adjuntado las 35 instrucciones agrupadas por los tres grupos

Si alcanzan a ver en la imagen, verán que algunas instrucciones afectan al registro STATUS y otras no.


----------



## FantasticalTale (Nov 6, 2008)

Hola Leon_Elec:
Primeramente muchas gracias por lo que estas haciendo, ya que muestras muchos puntos que yo queria aclararme y que no podía encontrar en ningún lugar, me da mucho gusto que te tomes la molestia de crear este post 

Mis dudas en cuanto microcontroladores son de lo más básico pero siento que no puedo seguir aprendiendo acerca de los micros si no entiendo como funciona el procesador.

Bueno espero que no te moleste resporderme algunas preguntas..

Estoy familiarizada con los PICs y más con el 16F887 asi que las preguntas que haga serán tomando en cuenta la arquitectura de los PICs

1.- Los PICs son de arquitectura Harvard, tienen la memoria de datos y la del programa por separado, mi pregunta es en qué lugar se tienen almacenadas las instrucciones ya que tengo entendido que los registros y variables están en la RAM porque su valor cambia durante la ejecución del programa, pero ¿de donde se sacan las instrucciones?. Sé que cuando ya acabas tu código todas las instrucciones utilizadas se guardan en la FLASH, pero ¿no hay un lugar en donde se tengan las 35 instrucciones almacenadas listas para extraerse? Serias tan amable de explicarme todo este proceso del ciclo Fetch? porque también he leido acerca de un Registro de Instrucción

2.- La memoria RAM del pic 16F887 es de 368 Bytes, está dividida en 4 bancos que deberían de ser de 92 bytes ¿correcto? pero si te fijas en las direcciones me dice que es de la 00h a la 1FFh lo cual serian 512 bytes ¿no? y todas las posiciones que son de 8 bits están ocupadas ya sea por registros o para almacenar variables que son los registros de propósito general... me podrias explicar eso?

3.- Otra que creo es muy tonta... Porque la frecuencia de trabajo 8MHz, 20MHz o cualquier valor se divide entre 4? Es que para los temporizadores cuando seleccionas un prescalador te indica que es la frecuencia de trabajo (Fosc/4) entre la seleccion de tu predivisor

Y por ultimo...
4.- Si se tiene una frecuencia de trabajo de 10MHz cada instrucción tarda 40ns en ejecutarse (a excepción de los saltos) entonces si en todo mi codigo tengo 8 instrucciones, el programa tardaría en ejecutarse 360ns? ¿Y esto no afecta al temporizador en caso de que se use? Me refiero que si se pierde tiempo por ejecutar otras interrupciones o si lo hace por su lado. Sé es super tonto preguntar eso pero necesito confirmarlooooooo¡¡¡¡

Muchas gracias de antemano¡ )


----------



## Meta (Nov 6, 2008)

Hola Leon_Elec:

Al final te animaste en publicarlo por aquí. Muchos estarán agradecido, que por su conocimiento y buen saber, nos aporta información a personas nóveles, en el cual se ha de agradecer por su tiempo y gran admirador de microcontroladores.

Muchas gracias Leon_Elec.

_Administrador:
Bajo mi punto de vista, esta información debería ir en chincheta para que no se pierda en las profundidades del foro._

PD:_Cuidado con las faltas de ortografía. En Word se puede corregir como 'Exepto' que en realidad es 'Excepto'._Alludar-ayudar, intrucciones-instrucciones,pociciones-posiciones, deshabilitar-desabilitar, desbiemos-desviemos, exede-excede,eligirá-elegirá etc, etc. Al menos en España es así. Si son fallos, hay que corregirlo con Word por si acaso, tampoco es muy fiable pero ayuda mucho.

*EDITO:*
He pasado este tutorial de Leon_Elec a PDF. Espero que os sirva de ayuda. Si lo quieren poner en sus Web, mejor pedirle permiso ya que él tiene sus derechos.
Leon_Elec:
Si deseas que modifique algo en la última página a tu estilo o agregar más información, etc, me lo dice y lo cambio. Otra cosa, muchos Webmaster no ponen estos artículos si no saben que el autor los autoriza por eso es bueno indicarlo al final de la página para que lo sepan.

*Versión PDF v1.3*


----------



## Leon Elec (Nov 7, 2008)

Para FantasticalTale:

1- Correcto, la arquitectura es Harvard, esto quiere decir, que posee un bus para la memoria de datos y otro bus para la memoria de programa y  memoria de instrucciones

Lee de nuevo mi mensaje número 4 y verás que la memoria de programa guarda el software que escribimos y que fue pasado en formato binario (archivo .hex) para que lo entienda el PIC, de echo, es el único lenguaje que entiende nuestro PIC, es el programador que utiliza otros lenguajes, o sea, otra arquitectura y orden para escribir el software, pero que luego, se traducirá al binario.

La memoria de instrucción, lo utiliza el CP para poder ejecutar una instrucción del programa. El programador, no tiene acceso a ella.

Cada instrucción, tiene un código binario que lo vimos en el mensaje anterior. Vimos como está compuesta cada instucción y en el adjunto, está el valor binario de cada instrucción. Justamente, es el valor binario, que nuestro programa escrive en el archivo .hex por cada instrucción que lee en nuestro programa.

Me explico. Tenemos el siguiente programa:


```
MOVLW   B'11111111'
MOVWF   TRISB
```

Cuando el MPLAB, más precisamente el MPASIM, lea estas instrucciones, las remplaza por su código binario que sería:


```
1100xx11111111
00000010000101
```

La xx es indefinido, por ende, desconozco que puede ir.

Ahora bien, ¿Cuál es más fácil de entender?, pues las instrucciones con palabras. El PIC, no tiene ni idea que es MOVLW, es por eso, que el MPASIM se encarga de ello. Sería terrorífico escribir todo un programa en binario  

2- La RAM, está dividida en 4 bancos en forma equitativa, pero la meroria RAM dispuesto para el usuario, no está dividida en forma equitativa.

La RAM posee una cierta cantidad de espacio de los cuales, una parte está reservada para el propio PIC, estén o no los registros. A continuación de estos registros, está la pocición para uso del usuario o del programador.

Hay bancos de memoria, que poseen más registros que otros, por ende, en los banco que tiene menos registros, tiene más memoria para uso del programador. A esto me refiero cuando digo que la memoria RAM para uso del usuario no está dividida en forma equitativa.

Si te fijas, en el banco 0, tenés disponible para vos desde la pocición 0x20 (20h) hasta 0x7f (7fh). Luego en el banco 1 tenés disponible desde la pocición 0xa0 hasta 0xbf y desde 0xf0 hasta 0xff. El espacio del medio, no lo podés usar.

En el banco 2 desde 0x170 hasta 0x17f y en el banco 3 desde 0x1f0 hasta 0x1ff.

Continua en el siguiente post.


----------



## Leon Elec (Nov 7, 2008)

3- Son dos preguntas en una.

El CP divide en 4 un ciclo de reloj, para poder hacer su trabajo. Esta división genera 4 relojes distintos, denominados Q1, Q2, Q3, Q4. En Q1, hace avanzar el CP una pocición más, Q2 y Q3 lee y decodifica la instrucción y Q4 Busca una instrucción anterior y guarda la nueva instrucción obtenida en Q2 y Q3. Así, en un ciclo de reloj del cristal, por ejemplo, Ejecuta una isntrucción anterior y lee la nueva instrucción.

Tenemos:

002 MOVLW  B'00100010'
003 MOVWF  PORTB
004 NOP

Cuando el CP se ubica en la línea 002, lee esta línea y ejecuta la línea 001, Q1 hace incrementar el CP a uno y pasa a la línea 003, acá lee esta instrucción, la decodifica, la pasa a la memoria de instrucción y ejecuta la ínstrucción almacenada correspondiente a la línea 002 y así sucesivamente y ciclicamente.

La otra pregunta de los temporizadores, es porque los TMR se incrementa junto con el CP, es por eso que se lo tiene que dividir por 4 la frecuencia de reloj.

4-Los TMR trabajan en forma independiente al CP, digamos que hay otra persona dedicada solo para el TMR0, otra para el TMR1 otra para el TMR2. SI tu programa tarda en ejecutarce 8ns y el TMR0 tiene que contar 1 seg, lo hará, no importa que el CP esté en un bucle infinito. Si son afectado los TMR por un sleep, es por eso que el TMR2 se lo puede seguir incrementando por medio de una pata externa, así cuando llega a su desbordamiento, despierta al PIC.
Los preescaler para cada TMR son para ajustar distintos tiempos.



> Sé es super tonto preguntar eso pero necesito confirmarlooooooo¡¡¡¡



Yo diría se es super tonto no preguntar, y un descerebrado aquel que se rie de otro por la pregunta echa.


----------



## Leon Elec (Nov 7, 2008)

Para META.

Si tenía miedito     No, jajaja. Me lleva mucho tiempo en escribir cada mensaje, y cuando termino, no tengo más ganas de seguir sentado frente al monitor. Tengo una hija, y tengo que cuidarla. Le presto más atención a ella que a esto y me atraso.

En cuanto a las faltas de ortografía, tengo muchísima, y por la misma razón anterior, no lo escribo en el word, o lo paso para corregirlo y luego volver a pegarlo.

No tengo drama que sea publicado en las web de cada uno, ni que sea pasado a PDF. Solo estaré encontra quien lucre por lo mío sin mi permiso. Si lo publican en internet deben mencionar el autor que obviamente, soy yo   

Meta, yo esperaría para hacer el PDF o deberás actualizarlo seguido, porque el tutorial de ASM no terminó, recine empieza.


----------



## Meta (Nov 7, 2008)

Hola:

Entendido. ME encargo de las faltas en Word y mi experiencia estudiantil. DE todas maneras eres humano y es fácil de entender. También tengo muchas.

Lo de tu hija, es más importante. El deber te llama.

*Lo de PDF lo actualizaré con gusto cuando quieras.* Por cierto, si quieres que alguien te haga sugerencia del manual porque muchos quieren comunicarse con el autor. Me dices que si pongo tu e-mail. Sólo falta eso.

Saludos.


----------



## Leon Elec (Nov 7, 2008)

Prefiero que lo hagan por acá, porque me va a llenar el correo de pregunta y no voy a tener tiempo para contestarla.

El día que  arme mi propia WEB te lo diré para que actualizes el PDF.

Se acepta críticas, sugerencia.


----------



## Meta (Nov 7, 2008)

Entendido.

Pondré el enlace de este tema aquí. Dentro un rato lo vuelvo a subir por si las moscas. Si quieres en otros foros acceder al tema donde presentas el tema, también lo pongo. Usted decide. Eso si, dejo bien claro de quién es el autor, por si acaso puedes decir cuantos nick usas para ser identificado.


----------



## Meta (Nov 7, 2008)

FantasticalTale dijo:
			
		

> Sé es super tonto preguntar eso pero necesito confirmarlooooooo¡¡¡¡



*Preguntar no es de tontos*

_Las personas tontas carecen del entendimiento necesario para preguntar, por ende, es tontería creer que 'preguntar es de tontos'._

Está extendida la creencia que preguntar es una demostración de incultura, de falta de conocimientos, … de ignorancia. Como en otras tantas creencias, nada de certeza hay en ella.

La pregunta es una herramienta de aprendizaje también para quién responde, pues las personas podemos aprender de nosotras mismas simplemente escuchándonos. Al responder a una pregunta, hacemos conscientes conocimientos que teníamos pero desconocíamos su existencia.

Un momento adecuado para preguntar es cuando estamos presentes en un diálogo donde se trata un tema del cual tenemos escasos o ningún conocimiento. De esta manera mostramos interés por él, lo que no incomoda al resto de participantes, y ayuda a abrir el diálogo a otras personas que permanezcan en silencio.

Preguntar es un claro síntoma de querer saber más y las personas ignorantes no quieren aprender más porque ya creen saberlo todo, las tontas, en cambio, no podrían aprender aunque quisieran.

_“Quien hace una pregunta, teme parecer ignorante durante cinco minutos. Quien no pregunta se mantiene ignorante toda la vida”_


----------



## FantasticalTale (Nov 7, 2008)

Muchas gracias Leon_Elec por tomarte el tiempo, tus respuestas no pudieron ser más claras, lo entendí perfectamente.

En cuanto al espacio de la RAM debí suponer que los 368 Bytes que viene en las especificaciones eran para los registros de propósito general (nuestras variables) y no todos los registros. 

Todo lo que escribiste me quedo super claro :    

Para Leon_Elec y Meta:

Muchas gracias por las respuestas tan motivadoras¡¡¡ me da mucho gusto que se tomen la molestia de leer mis mensajes y me compartan su conocimiento. Ya no tendré tanta pena en preguntar¡

Un abrazo


----------



## Leon Elec (Nov 7, 2008)

*Las instrucciones*

Les voy a arruinar el momento de alegría. Las instrucciones hay que estudiarlas de memoria. Si, leyeron bien, de *memoria.* Lo que tienen que saber sobre las instrucciones, es como se escriben, que hace cada instrucción y lo más importante que bit del *REGISTRO* afecta.

Vamos a ir viéndolo por orden alfabético. Y otra cosita más, como es de esperarce, están en INGLES o son abreviaturas pero en INGLES.

Recordemos que: .123 o D'123' es en decimal; 0x7B o 7Bh o H'7B' es en Hexadecimal; B'01111011' es en binario.

*ADDLW*

Suma un valor designado por el programador al registro W

_Ejemplo_


```
ADDLW      .128
```

Si W tenía cargado un valor = .5, después de la instrucción W tiene cargado el valor .133

Para recordar, *ADD* es sumar, *L* es *L*iteral y *W* es el registro *W*

*Afecta a:*



*Z* Se pone a 1 si la operación es 0
*DC* Se pone a 1 si hubo un acarreo del bti 3 al 4
*C* Se pone a 1 si hubo desbordamiento, o sea, cuando se supera H'FF'

*ADDWF*

Suma el valor del registro W con el valor de un registro cualquiera. El destino de esta suma, lo elige el programador.

_Ejemplo:_


```
ADDWF      TEMP,W
```

Si W tenía guardado .133 y la pocición de la RAM llamada TEMP tenía el valor cargado con .2, W vale .135 y TEMP continúa valiendo .2

Ahora si hubiera puesto así:


```
ADDWF       TEMP,F
```

TEMP valdría .135 y W valdría .133

Para recordar, *F*, es *F*ile Register Address.

_NOTA:_ Para indicar la dirección de dónde se guarda, también se puede poner 0 o 1 en vez de W o F. 0, corresponder guardarlo en el registro W y 1 en el registro TEMP (para este caso).

*Afecta a:*



*Z* Se pone a 1 si la operación es 0
*DC* Se pone a 1 si hubo un acarreo del bti 3 al 4
*C* Se pone a 1 si hubo desbordamiento, o sea, cuando se supera H'FF'

*ANDWF*

Realiza la operación AND entre W y un registro designado por el programador. El destino de esta operación lo elije el programador.

_Ejemplo:_


```
ANDWF       TEMP,F
```

Si antes de la instrucción W vale B'11100011' y TEMP vale B'00111010' Después de la instrucción TEMP vale B'00100010' y W vale B'11100011'

*Afecta a:*



*Z* Se pone a 1 si la operación es 0


----------



## pic-man (Nov 7, 2008)

Leon Elec muchas gracias por esta serie de posts que estás haciendo para explicar ASM desde cero, si te sirve aquí están las instrucciones de los pic 16F en español.
http://micropic.wordpress.com/2007/02/15/conjunto-de-instrucciones-del-pic-16f628-16f628a/

Tal vez te puede servir ahora que vas en esa parte por tu manual.


----------



## Andres Cuenca (Nov 8, 2008)

Leon Elec, agradezco tu excelente aporte a la comunidad, y claro, merece que se destaque!

Meta, gracias por hacerlo portable.

Saludos.


----------



## Leon Elec (Nov 9, 2008)

Muchas gracias PIC MAN, cuando me trabe en alguna traducción me fijo en ese enlace.

Y gracias también a Li-ion por destacarlo.


----------



## Leon Elec (Nov 9, 2008)

*BCF*

Pone a 0 el bit de un registro. El bit debe ser indicado por el programador.

_Ejemplo:_


```
BCF      TEMP,2
```

Antes de la instrucción TEMP vale B'11111111'. Después de la instrucción TEMP vale B'11111011'

Para recordar, *B*it *C*lear es borrar *F*ile es archivo o registro

No afecta ningún bit del registro Status.

*BSF*

Pone a 1 el bit de un registro. El bit debe ser indicado por el programador.

_Ejemplo:_


```
BSF      TEMP,0
```

Antes de la instrucción TEMP vale B'01110110'. Después de la instrucción TEMP vale B'01110111'

Para recordar, *B*it *S*et es poner a 1 *F*ile Archivo o registro

No afecta a ningún Bit del registro Status.

*BTFSC*

Salta un línea si el bit de un registro es cero. El bit debe ser indicado por el programador.

_Ejemplo:_


```
BTFSC    TEMP,5
         BCF        PORTA,0
         BSF        PORTB,0
```

_Caso 1:_

TEMP vale B'00011110'. El CP analizará solo el Bit 5 del registro TEMP, como es 0, salta la instrucción BCF   PORTA,0 y ejecuta la siguiente línea que es BSF  PORTB,0 y continua haciendo la instrucción.

_Caso 2:_

TEMP vale B'00111000'. El CP analizará solo el Bit 5 del registro TEMP, como es 1 no salta la instrucción y hará la instrucción BCF  PORTA,0 y luego continua con la instrucción BSF  PORTB,0

Para recordar *B*it *T*est es chequear *F*ile *S*kip es salto *C*lear

No afecta a ningún Bit del registro Status.

*BTFSS*

Salta una línea si el bit de un registro es 1. EL bit debe ser indicado por el programador.

_Ejemplo:_


```
BTFSS    TEMP,3
         ADDWF   PORTC
         ANDWF   NODO
```

_Caso 1:_

TEMP vale B'01101100'. El CP analizará solo el Bit 3 del registro TEMP, como es 1, salta la instrucción ADD   PORTC y ejecuta la siguiente línea que es ANDWF NODO y continua haciendo la instrucción.

_Caso 2:_

TEMP vale B'11110000'. El CP analizará solo el Bit 3 del registro TEMP, como es 0 no salta la instrucción y hará la instrucción ADD  PORTC y luego continua con la instrucción AND  NODO.

Para recordar *B*it *T*est es chequear *F*ile *S*kip es salto *S*et

No afecta a ningún Bit del registro Status.


Normalmente, continuando las instrucciones BTFSS y/o BTFSC va un GOTO o CALL pero no la he puesto porque aún no se explicaron estas instrucciones.


----------



## Leon Elec (Nov 15, 2008)

*CALL*

Se digire a una dirección de la memoria de programa designado por el programador. En otras palabras, se utiliza para dirigirce a una rutina o tarea. Su principal ventaja es que una vez que finalizó la tarea, vuelve al punto siguiente desde dónde se llamo.

_Ejemplo :_


```
CALL       ESC_PORTB
```

Para recordar, *CALL* es llamada.

No afecta ningún bit del registro Status.

---------------------------------------------------------------------------------------------------------------------------------------------
Hacemos una exepción con respecto a ver las instrucciones por orden alfabético y veremos la instrucción GOTO.
---------------------------------------------------------------------------------------------------------------------------------------------

*GOTO*

Se digire a una dirección de la memoria de programa designado por el programador. En otras palabras, se utiliza para saltar instrucciones que no queremos que se ejecuten. A diferencia de la instrucción CALL, no hay forma de volver cuando se ejecuta la instrucción.

_Ejemplo:_


```
GOTO       INICIO
```

Para recordar *GO* *TO* es ir a.

No afecta a ningún bit del registro Status.

_Extendiendo la explicación._

Vamos a hablar del PC para entender bien sobre estas instrucciones. La excelente explicación que continua, por desgracia no es mía (ojalá fuera mi explicación), son de las personas Maunix y BrunoF (del foro todopic)

En la siguiente imagen, vemos el diagrama de bloques del PC o CP.







El PC es de 13 bits en este caso(8kwords). 14 son los bits de cada "word" o instrucción que se graban en cada posición de la FLASH (memeria de programa).
El PC se reparte en: sus 8 bits de menor peso en el registro PCL, y los 5 restantes en el registro PCLATH.

Los pics al tener un set de instrucciones reducido no puede en una sola instrucción meter todos los bits necesarios para direccionar toda su memoria.

EL program counter son 2 registros, el PChigh y PCLow .  Cuando haces un CALL o un GOTO, solo se rellenan 11 bits (los 8 del PClow y 3 del PChigh) y los restantes 2 los rellenas con el PCLATH (para completar los 13bits).    

El STACK (pila)  tiene toda la dirección, no solo parcial.  Si haces un call desde la pagina 0 a la página 3 y luego un return el código *SI volverá a la página 0*, pero el PCLATH sigue apuntando a la página 3, entonces si usas otro goto o call, debes tener en cuenta de modificar el PCLATH.

Entónces, dijimos que:

El PC = ProgramCounter o CP = Contador de Programa, tiene 13 bits; del 0 al 12.

Al ejecutar un call o un goto, se copian del 0 al 10, y los otros 2 bits se copian del registro PCLATH.  El pclath solo estará allí para esa situacion. 

En un return o retfie la microelectronica del PIC, pega la dirección del PC que estaba guardada.

Lo vemos con un ejemplo


1.
   STACK = vacío
   PC = 0x00A0 
   PCLATH = 0b0000*11*000
   Ejecutas un CALL 0x230

2. El STACK tiene en su posición 0 la dirección 0x00A0.
   PC = *11*1000110000

3. Se ejecuta la subrutina y en ese punto el PC ya quedó en 
   PC = *11*1000110111

4. Viene un RETURN.
   La microelectrónica del PIC copiará el stack tal cual en el program counter + 1 
   Valor STACK 0x00A0 + 1 --> PC = 0x00A1

5. EL código sigue ejecutandose en la página 0 pero hay que tener en cuenta que el PCLATH apunta a la página 3 por ello si harás otro CALL o GOTO, deberás cambiar de nuevo el PCLATH si la subrutina no está en la página 3.

_Para cerrar el tema_

Vamos a entrar a todo detalle en el Program Counter(PC) para que se vayan todas las dudas ya que es muy importante. Vayamos al tema tema del PC, computed goto(lo que algunos llaman "tabla"), call, returns y goto.

El Program Counter(PC) está conformado en esta familia de uC(y refiriendonos a la familia 16F, las otras poseen más o menos bits implementados) por 13 bits repartidos entre dos registros: PCH y PCL.
El PCL es legible/escribible directamente a traves del registro físico PCL(valga la redundancia). En cambio, el PCH no es directamente accesible. No puede ser leído, y sólo puede se grabado mediante un buffer que contiene el valor temporalmente(oh! aquí aparece nuestro famoso PCLATH). Entonces, recordar: El PCLATH es sólo un buffer temporal que almacena los 5 bits de mayor peso del PC para ser escritos cuando se ejecute una instrucción que lo requiera. 

Ahora, hay dos situaciones posibles en las que el PC debe ser cargado de manera distínta: una es cuando queremos trabajar con tablas y otra cuando realizamos un call o un goto que no esté en el mísmo banco.

1era situación: Tabla(Comuted Goto)

La tabla es una situación de uso del PC en la que se afecta directamente al registro PCL. Cuando se afecte directamente al PCL mediante una instrucción, es necesario que el usuario asegure que PCLATH tenga sus 5 bits pre-cargados adecuadamente.

Hago un ejemplo:

Mal:

   org 0x000
   movlw  0x01
   call tabla


   org 0x300
tabla   
    addwf  PCL,F
    retlw 0x03
    retlw 0x01
    retlw 0x0F
    .....

Bien:
   org 0x000
   movlw 0x03
   movwf PCLATH
   movlw  0x01
   call tabla


   org 0x300
tabla   
    addwf  PCL,F
    retlw 0x03
    retlw 0x01
    retlw 0x0F
    .....

Mejor:
   org 0x000
   pageselw  tabla         
   movlw  0x01
   call tabla


   org 0x300
tabla   
    addwf  PCL,F
    retlw 0x03
    retlw 0x01
    retlw 0x0F
    .....

Pageselw es una instrucción del MPASM que genera dos instrucciones: un movlw literal y un movwf PCLATH. El valor del literal es automáticamente seleccionado por el ensamblador según la etiqueta(o posición de memoria) que se le especifique. En el caso anterior   pageselw  tabla    generaría estas dos instrucciones:

movlw 0x03
movwf PCLATH

Si no aseguramos que los 5 bits del PCLATH estén correctamente seteados al momento de afectar al PCL mediante alguna instrucción(generalmente es la addwf, pero puede usarse subwf y muchas otras) entonces el programa saltará a una posición indeseada. 


2da situación: CALL y GOTO

En esta familia de uC, cada instrucción es codificada en 14 bits. En el caso de las instrucciones CALL y GOTO, su estructura es la siguiente:

            F2 F1 F0 K10 K9 K8 K7 K6 K5 K4 K3 K2 K1 K0

Donde las F indican cuál instrucción es la que debe ejecutarse(100 para la CALL 101 para la GOTO), y las k corresponden a la dirección a la cual queremos llamar(con un CALL) o saltar(con un GOTO).

Aquí se ve claramente un problema. Podemos ver que un CALL o un GOTO sólo almacena 11 bits de la dirección a la cual debe ir. 11 bits es un máximo 2048 posiciones. ¿Qué pasa cuando un uC posee más de 2k de memoria Flash entonces? Por ejemplo, un 16F877A posee 8k de memoria Flash. ¿Cómo haría para llamar a una subrutina que está más allá de la posición 2047 de la flash?
La solución nuevamente se encuentra en el PCLATH(y es nuevamente el usuario el que tiene el deber de pre-cargar el valor adecuado).

Entonces, dijimos que el PC contiene 13 bits de longitud. 13 bits son hasta 8kwords(una word es en esta familia un conjunto de 14 bits que conforman una instrucción la cual se aloja en la memoria FLASH del uC). Un CALL o un GOTO sólo contienen los 11 bits de menor peso de la dirección a la cual ir, por lo que los 2 bits restantes deberán ser pre-cargados en los bits 4 y 3 del registro PCLATH por el usuario programador.

Cuando se ejecuta una instrucción CALL o GOTO, es imprescindible que el registro PCLATH esté correctamente precargado. La instrucción a la que el uC irá estará conformada por 13 bits y ellos serán:

PCLATH,4   PCLATH,3   K10 K9 K8 K7 K6 K5 K4 K3 K2 K1 K0

Cabe mencionar que el uC carga a PC<10:0> con el valor pasado por los 11 bits de K, y a PC<12:11>  con el valor de los bits  PCLATH<4:3>.  El registro PCLATH no es modificado de ninguna manera. Sólo se leen esos dos bits.

Por ejemplo, en un uC de 8kWords hay 4 páginas. Una página cada 2048 words. Si se está en una página y se quiere ir a otro es necesario precargar antes dichos bits del PCLATH para poder hacerlo.

El usuario no debe preocuparse por precargar el PCLATH en dos sitaciones: 
Si el uC no posee más de 2kWords de memoria Flash;
O si en el código creado por el usuario, no se utiliza la memoria FLASH más allá de la posición 2047(0x7FF).

Si ocurre al menos uno de esos dos casos, es suficiente con asegurar que los bits PCLATH<4:3> se encuentren ambos en cero.

Vamos con un par de ejemplos:

Mal:

   org 0x000          ;Esto es página0
   call cruzo


   org 0x800         ;Esto ya es el página1
cruzo
    retlw 0xFF
    .....


Bien:

   org 0x000          ;Esto es página0
   movlw 0x08       
   movwf PCLATH
   call cruzo


   org 0x800         ;Esto ya es el página1
cruzo
    retlw 0xFF
    .....


Mejor:

   org 0x000          ;Esto es página0
   pagesel cruzo     ;automaticamente seleccionar banco
   call cruzo


   org 0x800         ;Esto ya es el página1
cruzo
    retlw 0xFF
    .....



Pagesel es una instrucción del MPASM que genera dos instrucciones: un bcf/bsf PCLATH,3 y un bcf/bsf PCLATH,4. El software ensamblador selecciona automáticamente la instrucción bcf o bsf según el banco en el cual se encuentra la etiqueta(o posición de memoria) que se le especifique. En el caso anterior    pagesel cruzo    generaría estas dos instrucciones:

bsf    PCLATH,3
bcf    PCLATH,4

Ya que la subrutina cruzo se encuentra en la página1. 

Finalmente, cuando se ejecuta una instrucción CALL, se carga en el STACK el valor de la posición actual más 1(es decir, se guarda en el STACK el valor PC+1). Se guardan los 13 bits, por lo que durante las instrucciones RETURN,RETLW y RETFIE no es necesario precargar al PCLATH. 

Para más información, ver el esquema sección 2.3 del datasheet de los PIC16F87XA que habla de cómo cargar al PC según cada situación.


----------



## Meta (Nov 15, 2008)

Actualizado a *asm_desde_cero_v1.3.pdf*


----------



## Leon Elec (Nov 21, 2008)

*CLRF*

Borra el contenido de un registro seleccionado por el programador. La forma en que lo hace, pone en 0 los 8 bit del registro. Este registro, puede ser cualquiera de la pocición de la RAM.

_Ejemplo:_


```
CLRF             PORTB
```

Antes de la instrucción PORTB vale B'11000111'. Después de la instrucción PORTB vale B'00000000'

Para recordar *CL*ea*R* es limpio *F*ile es archivo o registro.

*Afecta a:*



*Z* Se pone a 1

*CLRW*

Borra al registro W. La forma en que lo hace, pone en 0 los 8 bit del registro.

_Ejemplo:_


```
CLRW
```

Antes de la instrucción W vale B'00000111'. Después de la instrucción W vale B'00000000'.

Para recordar *CL*ea*R* es limpiar *W*ork es trabajo.

*Afecta a:*



*Z* Se pone a 1

*CLRWDT*

Borra al WDT. La forma en que lo hace, pone en 0 al mismo.

_Ejemplo:_


```
CLRWDT
```

Antes de la instrucción WDT vale B'11111110'. Después de la instrucción vale B'0000000'.

Para recordar *CL*ea*R* es limpiar *W*atch *D*og es perro guardian *T*imer es contador.

*Afecta a:*



*TO (neg)* Se pone a 1
*PD (neg)* Se pone a 1


*NOTA:* El WDT o el contador perro guardian, sirve para destrabar al PIC. Cada vez que se desborda, o sea, cada vez que pasa de H'FF' a H'00', produce un reset, y como es un reset, se dirige a la pocición 0h de la memoria de programa.
La forma de trabajar con el, es ir poniendo en lugares estratégicos la instrucción ya explicada, de esta manera evitamos el desborde del contador. Si el CP se traba en algún bucle o algo similar, al no limpiar el contador, el WDT desbordará y llevará al CP a la pocición 0h de la memoria de programa.

Muchas veces se evita de usar esta herramienta por no tener que calcular por todo el programa dónde y cuando limpiar al WDT. Es recomendable su uso.


----------



## Meta (Nov 22, 2008)

Muchas gracias, cada vez más poco a poco va tomando forma.
Buen trabajo.


----------



## adrian_escolar (Nov 30, 2008)

Hola, soy nuevo en esto de los pic y estos tratando de entender como hacer un programa.
Todavia no logro entender el asembler, asi que mientras sigo el avance del foro, estoy jugando con el niple que es mas facil de manejar.
Comence haciendo destellar a un led con un pic 16F84A, y ahora estoy tratando de hacer encender 3 led en secuencia como las luces del auto fantastico, pero se me presento un problemita.

Cargue el programa en el pic pero no se comporta como yo esperaba, en vez de seguir la secuencia esperada (encendiendo los led de a uno de izquierda a derecha y de derecha a izquierda) sigue otra secuencia distinta, pero si provoco un falsocontacto a la resistencia que polariza al reset (pin 4), la secuencia cambia a la esperada y se mantiene asi hasta que se desconecta la alimentacion.

No entiendo porque, si en teoria siempre sigue el mismo programa.
Adjunto los archivos del programa, para que alguien que entienda me ayude a ver donde esta el error.

El foro esta buenisimo, sigan asi.
saludos.


----------



## Leon Elec (Dic 3, 2008)

Yo tube un inconveniente parecido y me estaba andando mal los condensadores de desacoplo del cristal. Se los saqué y santo remedio. Ovbiamente, los reemplazé por otros.

Cheque eso.


----------



## Leon Elec (Dic 3, 2008)

*COMF*

Realiza el complemento de un registro.


```
COMF       TEMP,F
```

Si TEMP tenía guardado B'00111101' luego de ejecutar la instrucción TEMP vale B'11000010'. Notese, que aquí también podemos elegir el destino y esto nos deja guardarlo en el registro W si así lo requerimos.

Para recordar *COM*lement es complemento *F*ile es registro.

*Afecta a:*



*Z* Se pone a 1 si la operación da 0

*DECF*

Decrementa en una unidad, o lo que es lo mismo, resta 1 el contenido de un registro


```
DECF     DECENA,W
```

Si antes de la instrucción DECENA vale .255, después de la instrucción W vale .254 y DECENA vale .255 Si por el contrario, hubieramos elegido el destino F, después de la instrucción DECENA vale .254

Para recordar *DEC*rement es decremento *F*ile es registro.

*Afecta a:*



*Z* se pone a 1 si la operación es 0

*DECFSZ*

Decrementa en uno, o lo que es lo mismo, resta en 1 el contenido de un registro y cuando este vale 0, el CP salta una instrucción


```
LOOP    DECFSZ      TEMP
           GOTO     LOOP
           BCF      PORTB,0
```

El CP descontará en 1 el registro TEMP y evalúa el valor, si no es cero, ejecuta línea siguiente que es GOTO LOOP, el cual se dirige de nuevo a la línea LOOP   DECFSZ   TEMP el cual volverá a descontar en 1 y evalua el valor, si es cero salta la línea GOTO LOOP y ejecuta la instrucción BCF PORTB. Esta última línea, el programador pondrá la instrucción que necesite ejecutar.
Este pequeño programa que acabamos de ver, es un temporizador o un retardo que tardará en salir del bucle dependiendo de la frecuencia de reloj y el valor cargado en TEMP.

NOTA: Esta instrucción, también hay que elegirle el destino. En el caso que no se exprese, como en este caso, el MPLAB dará por sentado que el resultado se guardará en el registro F y no en W.

Para recordar *DEC*rement es decremento *F*ile es registro *S*kip es salto *Z*ero que es cero.

No afecta ningún bit del registro STATUS.


----------



## Meta (Dic 3, 2008)

Portado (3 de Diciembre de 2008).
Revisión: v1.5


----------



## Meta (Dic 4, 2008)

Corregido desde 0 la asm_desde_cero v1.6.pdf


----------



## Leon Elec (Dic 5, 2008)

*INCF*

Incrementa en 1, o suma 1, el contenido de un registro elegido por el programador.


```
INCF          INDF,F
```

Si antes de la instrucción INDF vale H'29', después de la instrucción INDF vale H'2A'. Notese que también podemos elgeir el destino. Si hubieramos elegido W, después de la instrucción W vale H'2A' y INDF vale H'29'.

Para recordar *INC*rement es incremento *F*ile es registro.

*Afecta a:*



*Z* se pone a 1 si el resultado es 0

*INCFSZ*

Incrementa en 1, o suma en 1, el contenido de un registro elegido por el programador y cuando este es 0, el CP salta una instrucción.


```
VOLVER          INCFSZ          CONTADOR
                GOTO            VOLVER
                INCF            PORTA
```

El CP incrementará en 1 el registro CONTADOR y evalúa el valor, si no es cero, ejecuta línea siguiente que es GOTO VOLVER, el cual se dirige de nuevo a la línea VOLVER   INCFSZ   CONTADOR el cual volverá a incrementar en 1 y evalua el valor, si es cero salta la línea GOTO VOLVER y ejecuta la instrucción INCF  PORTA. Esta última línea, el programador pondrá la instrucción que necesite ejecutar.
Este pequeño programa que acabamos de ver, es un temporizador o un retardo que tardará en salir del bucle dependiendo de la frecuencia de reloj y el valor cargado en CONTADOR. Normalmente, se utiliza el retardo con DECFSZ pero este también es válido.

NOTA: Esta instrucción, también hay que elegirle el destino. En el caso que no se exprese, como en este caso, el MPLAB dará por sentado que el resultado se guardará en el registro F y no en W.

Para recordar *INC*rement es incremento *F*ile es registro *S*kip es salto *Z*ero es cero.

No afecta ningún bit del registro STATUS.

*IORLW*

Realiza la operación OR entre W y un literal elegido por el programador. El resultado se guarda en W. La operación es W OR L.

Si antes de la instucción W vale B'01110100' y el literal elegido es B'00011111', después de la instrucción W vale B'01111111'.

Para recordar *I*nclusive es inclusivo *OR* es la operación binaria OR *L*iteral es literal *W* es el registro trabajo.

*Afecta a:*



*Z* se pone a 1 si la operación da 0


----------



## Meta (Dic 5, 2008)

Listo:
Tutorial asm_desde_cero v1.7.pdf


----------



## Leon Elec (Dic 6, 2008)

*IORWF*

Realiza la operación lógica OR entre el registro W y un registro elegido por el programador. La operación es W OR F.


```
IORWF      PORTC,F
```

Si antes de la instrucción W vale B'01111111' y PORC vale B'00001111' después de la instrucción PORTC vale B'01111111' y W vale B'01111111'. Notese que podemos elegir el destino y la otra opción, como ya se dieron cuenta por las instrucciones pasadas, puede ser W.

Para recordar *I*nclusive es inclusivo *OR* es la operación binaria OR *W* es el registro trabajo y *F*ile es registro.

*Afecta a:*



*Z* se pone a 1 si el resultado es 0

*MOVLW*

Carga al registro W con un literal elegido por el programador para luego hacer una operación matemática o moverlo a otro registro como veremos más adelante. Sin duda alguna, una de las instrucciones más usadas en la programación ASM.


```
MOVLW        .255
```

Si antes de la instrucción W vale .15, después de la instrucción W vale .255.

Para recordar *MOV*e es mover *L*iteral es literal *W* es el registro trabajo.

Como es de esperar, no afecta ningún bit del registro STATUS.

*MOVF*

Mueve el contenido de un registro a otro registro elegido por el usuario.


```
MOVF        RETARDO,W
```

Si antes de la instrucción W vale H'2A' y RETARDO vale H'FF', después de la instrucción W vale H'FF'. Notese que aquí podemos elegir el destino, y tenemos la posivilidad de elegir el destino al propio registro RETARDO. Al principio parece innecesario, pero  se puede tomar como una verificación, ya que se ve afectado el registro STATUS bit Z.

Para recordar *MOV*e es mover y *F*ile es registro.

*Afecta a:*



*Z* Se pone a 1 si la operación es 0

Anteriormente, habíamos dicho que esta instrucción se la puede tomar como vereficación, para saber si se guardó con el mismo valor que tenía, el bit Z se pone a 1 si el valor es igual al que tenía cargado.


----------



## diego_z (Dic 6, 2008)

> *NOTA:* El WDT o el contador perro guardian, sirve para destrabar al PIC. Cada vez que se desborda, o sea, cada vez que pasa de H'FF' a H'00', produce un reset, y como es un reset, se dirige a la pocición 0h de la memoria de programa.
> La forma de trabajar con el, es ir poniendo en lugares estratégicos la instrucción ya explicada, de esta manera evitamos el desborde del contador. Si el CP se traba en algún bucle o algo similar, al no limpiar el contador, el WDT desbordará y llevará al CP a la pocición 0h de la memoria de programa.
> 
> Muchas veces se evita de usar esta herramienta por no tener que calcular por todo el programa dónde y cuando limpiar al WDT. Es recomendable su uso.



disculpa la entromicion o duda , pero cual seria la forma de descolgar el pic y que retorne a la misma posicion de memoria ? tengo entendido que es con el wd , saludos diego


----------



## Meta (Dic 7, 2008)

*asm_desde_cero v1.8*


----------



## Leon Elec (Dic 7, 2008)

diego_z dijo:
			
		

> > *NOTA:* El WDT o el contador perro guardian, sirve para destrabar al PIC. Cada vez que se desborda, o sea, cada vez que pasa de H'FF' a H'00', produce un reset, y como es un reset, se dirige a la pocición 0h de la memoria de programa.
> > La forma de trabajar con el, es ir poniendo en lugares estratégicos la instrucción ya explicada, de esta manera evitamos el desborde del contador. Si el CP se traba en algún bucle o algo similar, al no limpiar el contador, el WDT desbordará y llevará al CP a la pocición 0h de la memoria de programa.
> >
> > Muchas veces se evita de usar esta herramienta por no tener que calcular por todo el programa dónde y cuando limpiar al WDT. Es recomendable su uso.
> ...



Haber si te entiendo. Tu lo que quieres saber como volver al lugar dónde se produjo el reset.

En realidad, no necesitas volver a dónde estaba e CP justo en el momento que el WDT produjo un desbordamiento. En una operación normal del programa, jamás debe ocurrir esto ya que se está continuamente borrando el WDT.

La finalidad del WDT es, si el CP quedó en un bucle infinito y no sale de el, si no estubiera el WDT, el operador tiene que resetear manuelamente al PIC. Para esto, MICROCHIP puso a nuestra dispocición el WDT, para que no dependiera de un operador.

Si te fijas en el registro STATUS, hay dos bit que se ven afectado cuando un reset del WDT a ocurrido. entónces al principio de tu programa, y antes de empezar la parte normal del programa, deberas chequear alguno de esos bit, y dependiendo de tu programa y detectas que el reset se produjo por el WDT lo llevarás a una parte de tu programa en caso contrario, continuará por la parte normal.


----------



## diego_z (Dic 7, 2008)

bien comprendido , gracias por tu respuesta 
saludos diego


----------



## Leon Elec (Dic 8, 2008)

*MOVWF*

Mueve el contenido del registro W a un registro cualquiera elegido por el programador. Sin duda alguna, esta instrucción, es otra muy usada en la programación ASM


```
MOVWF       ADCON0
```

Si antes de la intrucción W vale B'10000001' y ADCON0 vale 0x0, después de la instrucción ADCON0 vale 0x81.

Para recordar *MOV*e es mover *W* es el registro W y *F*ile es registro.

No afecta ningún bit del registro STATUS.

*NOP*

No realiza ninguna operación. Solo consume un ciclo de instrucción.


```
NOP
```

Para recordar *N*o es no y *OP*eration es operación.

No afecta ningún bit del registro STATUS.

*RETFIE*

Carga al CP con el valor de la parte alta de la pila para volver al lugar dónde se encontraba el CP antes de atender la interrupción. Al mismo tiempo, pone a 1 el bit GIE para activar de nuevo las interrupciones.


```
RETFIE
```

Para recordar *RE*turn es retornar *F*rom es de la *I*nt*E*rrupt es interrupción.

No afecta ningún bit del registro STATUS.


----------



## Meta (Dic 8, 2008)

Veo que estás a tiro hecho. Ahora estoy con openSUSE, ya lo pasaré cuando pueda a PDF. 

Una curiosidad.
Cuando acabes las 35 instrucciones de los PIC 16F de gama media. ¿Te meterás a los 18F de gama alta algún día?

Ya es mucho pedir.


----------



## Leon Elec (Dic 9, 2008)

Por ahora no. Hay mucho por aprender en la programación ASM. Como armar un programa, concejos a la hora de hacerlo.

Utilización de las rutinas y como armar librerías, luego como adjuntarlas a nuetro nuevo programa, etc.


----------



## Meta (Dic 9, 2008)

Entendido.

¿También explciará algo de diagrama de flujo?


----------



## Meta (Dic 11, 2008)

Publicada asm_desde_cero v1.9.pdf


----------



## Leon Elec (Dic 12, 2008)

A Meta: Si, explicaría como hacer un diagrama de flujo. Pero es obvio que cada uno tiene su forma de programar.

A electroaficionado: Por mi no hay problema.

Saludos.


----------



## fernandob (Dic 13, 2008)

me vengo de otro tema y te paso el texto:
https://www.forosdeelectronica.com/f24/temporizacion-maxima-pic-17560/


cuando hice el curso de PIC con mi querido profe "pablin" hace mucho comenzo el mismo con programas lineales sin interrupciones , obvio que no se quedo en ellos . 
pero quizas haya gente que estudio por su cuenta y si, se quedaron en un concepto lineal . 
por eso (y esto va a quienes estan con el curso de ASM desde cero ) creo que es mas importante esas cosas que explicar las instrucciones que ya estan explicadas en la data.
si a lo mucho un buen enlace y copy paste , pero dedicar tiempo a lo que si lo vale.

es solo una opinion y nada de critica, ya que no se puede criticar algo que se esta haciendo MUY BIEN y con muy buena onda como lo que estan haciendo uds.  

saludos


----------



## Meta (Dic 13, 2008)

fernandob dijo:
			
		

> es solo una opinion y nada de critica, ya que no se puede criticar algo que se esta haciendo MUY BIEN y con muy buena onda como lo que estan haciendo uds.



Es opinión y crítica, y si, si se puede criticar algo que se está haciendo muy bien. Dentro del término bien y no tan bien.


----------



## Leon Elec (Dic 14, 2008)

Fernandob. El curso está dedicado a los que no saben nada de ASM, por eso se llama ASM desde cero. Les sirve también a las personas que ya saben del tema y se quieran perfeccionar o ver las cosas desde otro punto de vista.
Si haría copiar y pegar, no sería un curso echo por mi si no de otro autor y tener que pasear por toda la web para buscar un buen material, me perdería mucho tiempo por algo que se y que me gusta explicar.

Si bien es verdad lo que decis, hay muchos puntos muy importantes para ver en el curso de ASM, y te digo que la programación de ASM es lineal, siempre se comienza desde arriba hacia abajo y se puede cambiar el curso del CP por medio de interrupciones, goto, call. Todo esto, hace que el programa sea cíclico (como todo programa).

Las interrupciones, es una explicación que va a estar en el curso como así los módulos del 16f877 que son bastantes y que sirven para otras familias.

Es verdad lo que dice META, es una crítica y por eso, te estoy agradecido, ya que son muy pocas personas que dan alguna crítica y sugerencias (toda crñitica debe estar acompañado de una sugerencia si no, no la tomo y la desacrtaré) y todo esto, ayuda a que el curso se mejore.

Y para cerrar, vino muy bien que explique las instrucciones, porque son en muy, pero en muy poco lugares, la explicación del goto y call y el porqué y como trabajar con las páginas de los PIC que a todos nos trae problema. Critican a microchip por esto, cuando ellos nos dieron la solución para trabajar más cómodamente y por la instrucción pageselw que solo MPASM lo lee.

Y vuelvo a repetir, no estoy enojado, si no agradecido por haber dado tu opinión y una crítica y sugerencia.

Manonline. Jamás trabajé con los pic de gama baja, por lo que por ahora, no tocaré nada del tema. Una vez que termine con este, veré si paso a los de gama baja o alta (de seguro, pasaré a los dspic si para entónces ya estoy trabajando con ellos)


----------



## Moyano Jonathan (Dic 14, 2008)

Estaría bueno que trabajaras con el módulo usb de los PIC18FXX5X. Yo lo estoy trabajando en C de CCS, pero me gustaría ver las diferencias (A nivel de velocidad de trabajo). 
Como tal es un comentario. Si lo llegas a llevar a cabo te podría llegar a dar una mano.


----------



## Leon Elec (Dic 15, 2008)

Lamentablemente, aún no he trabajado con USB. Ni siquera se programación de PC, pero ya estoy estudiando así puedo hacer aplicaciones con estos dispositivos que ya tengo varias cosas en mente.

Por ahora, voy a terminar lo que empezé y hablo de este curso. Luego veré que puedo seguir aportando.


----------



## Leon Elec (Dic 17, 2008)

*RETLW*

Carga al CP con el valor de la parte alta de la pila para volver al lugar dónde se encontraba el CP desde dónde se llamó a la subrutina y al retornar, lo hace con un literal cargado en W especificado por el programador. Esta instrucción, se utilizan en las tablas (para más detalle, ver la explicación del GOTO y CALL).


_Ejemplo:_


```
RETLW          'L'
```

En este ejemplo, el MPLAB, carga en W el código ASCII correspondiente a la letra L

_Extendiendo el ejemplo:_


```
PAGESELW       TABLA             ;CONFIGURA AL PCLATH PARA VOLVER AL LUGAR CORRECTO
           MOVFW            contador        ;CARGA A W LA POCICIÓN A LEER EN LA TABLA POR EJEMPLO 3
           CALL               TABLA            ;LLAMA A LA RUTINA TABLA
           CALL               LCD_DATO      ;LLAMA A LA RUTINA PARA MOSTRA AL LCD
           NOP
;
;
TABLA   ADDWF             PCL,F            ;SUMA AL PCL CON EL CONTENIDO DE W POR EJEMPLO 3
           RETLW              '1'                ;RETORNA CON 1 ASCII
           RETLW              '2'                ;RETORNA CON 2 ASCII
           RETLW              '3'                ;RETORNA CON 3 ASCII
           RETLW              'T'                ;RETORNA CON 4 ASCII
```

Este es un ejemplo sencillo de como utilizar RETLW. Para interpretar este código empezamos desde PAGESELW, supongamos que el CP está en esta instrucción (que está explicado que hace) luego pasa a la instrucción MOVFW     contador    y suponemos que tiene cargado 3 en decimal, por lo que W pasará a tener 3 en decimal. El CP continua con CALL     TABLA, el CP saltará por encima a todas las demás instrucciones y se dirige a la etiqueta TABLA y ejecuta la instrucción ADDWF     PCL,F En el código hablamos que le suma 3 al PCL, por lo que saltará al RETLW  '3' cargando a W con el código ASCII 3. Retorna justo debajo del CALL   TABLA, o sea retorna a CALL  LCD_DATO y ejecuta la rutina correspondiente, cuando termina, regresa al NOP (que puede ser cualquier instrucción que necesite el programador.
Si en cambio, contador hubiera tenido cargado 4 en decimal cuando llegue a la tabla y le sume al PCL este apuntará a RETLW    'T'  cargando en W el código correspondiente ASCII.

Para recordar *RET*urn es retornar *L*iteral es literal *W* es el registro de trabajo W

No afecta ningún bit del registro STATUS.

*RETURN*

Carga al CP con el valor de la parte alta de la pila para volver al lugar dónde se encontraba el CP cuando se llamó a la rutina o subrutina. 

La diferencia con RETLW es que RETURN regresa sin cambiar a W. Este se utiliza para terminar una rutina y no se necesite ningún dato. Por ejemplo en la rutina CALL   LCD_DATO no nos sirve que vuelva con ningún valor ya que es una rutina para enviar datos a un LCD, asi que esta rutina tendrá implementada RETURN

_Por ejemplo:_


```
RETURN
```

No afecta ningún bit del registro STATUS

Para recordar *RETURN* es retornar.


----------



## Meta (Dic 17, 2008)

asm_desde_cero v1.10.pdf


----------



## Leon Elec (Dic 21, 2008)

*RLF*

Rota hacia la izquierda los bit de un registro seleccionado por el programador. El destino de la operación se puede elegir. Cada rotación equivale a multiplicar por 2 si el bit C del registro STATUS es 0.

_Ejemplo:_


```
RLF         PORTC,F
```

Si antes de la instrucción PORTC vale B'00001000', después de la instrucción vale B'00010000'. Si se hubiera elegido como destino W, PORTC después de la instrucción continua valiendo B'00001000' y W vale B'00010000'

Para recordar *R*otate es rotar *L*eft es izquierda *F*ile es el registro.

*Afecta a:*



*C* se pone a 1 si hubo acarreo

*RRF*

Rota hacia la derecha los bits de un registro seleccionado por el programador. El destino de la operación se puede elegir. Cada rotación equivale a dividir por 2 si el bit C del registro STATUS es 0.

_Ejemplo_


```
RRF         PORTB,F
```

Si antes de la instrucción PORTB vale B'10000000' después de la instrucción PORTB vale B'0100000'. Si se hubiera elegido como destino W, PORTB después de la instrucción continua valiendo B'10000000' y W vale B'01000000'

Para recordar *R*otate es rotar *R*ight es derecha *F*ile es el registro.

*Afecta a:*



*C* se pone a 1 si hubo acarreo

_Extendiendo la explicación de las instrucciones RRF y RLF_

A la hora de utilizar estas dos instrucciones, hay que prestarle atención al bit C del registro STATUS. La razón de esto, es porque la rotación se hace atravéz del bit C.

Supongamos que tenemos lo siguiente:



BIT C = 0
TEMP = B'00010000'

Ejecutamos la instrucción RRF y TEMP vale B'00001000'. O si ejecutamos la instrucción RLF TEMP vale B'00100000'

Pero si ahora tenemos:



BIT C = 1
TEMP = B'00010000'

Ejecutamos la instrucción RRF y TEMP vale B'10001000'. O si ejecutamos la instrucción RLF TEMP vale B'00100001'

Vemos como rota los bit dependiendo del valor del bit C. Pero anteriormente, avíamos dicho que estas dos instrucciones afectan al bit C. La actualización del bit C, lo hace después de la rotación. Lo vemos con un ejemplo:


```
MOVLW  B'10001001'
           MOVWF  temp
           BCF   STATUS,C        ;PONEMOS A 0 AL BIT C
           RLF   temp,F          ;ROTAMOS A LA IZQUIERDA
```

Al ejecutar este programa, nos dará lo siguientes resultados:



TEMP = B'00010010'
BIT C = 1

Y para ver la diferencia vemos lo siguiente:


```
MOVLW  B'00000001'
           MOVWF  temp
           BCF   STATUS,C        ;PONEMOS A 0 AL BIT C
           RLF   temp,F          ;ROTAMOS A LA IZQUIERDA
```

Al ejecutar este programa, nos dará lo siguientes resultados:



TEMP = B'00000010'
BIT C = 0

Algo que me había olvidado de mencionar pero que MIGSANTIAGO del foro de TODOPIC estuvo atento es que estás dos instrucciones, nos sirve para enviar datos en forma serial utilizando el bit C que lo veremos más adelante.

Recordemos que, para utilizar estas instrucciones para multiplicar o dividir, debemos asegurarnos de que el bit C, esté en 0.

*SLEEP*

Pone al microcontrolador en bajo consumo.


```
SLEEP
```

Para recordar *SLEEP* es dormir.

*Afecta a:*



*TD* se pone a 1
*PD* se pone a 1

*SUBLW*

Resta el contenido de W con un literal de hasta 8 bit (.255). El resultado se guarda en W.


```
SUBLW       .20
```

Si antes de la instrucción W vale .23 después de la instrucción W vale .3 Para saber si el resultado es negativo o positivo, hay que chequear el bit C del registro Status. Si hay acarreo, el resultado es negativo, y por el contrario, si no hay acarreo es positivo.

Para recordar *SUB*traction es restar *L*iteral es literal y *W* es el registro W.

*Afecta a:*



*Z* se pone a 1 si el resultado es 0
*DC* se pone a  si hay acarreo del bit del 4 al 5 bit del registro (recordemos que en la resta, es distinto a la suma, por eso, se pone a 0 si hubo acarreo).
*C* se pone a 0 si hubo acarreo (recordemos que en la resta, es distinto a la suma, por eso, se pone a 0 si hubo acarreo).


----------



## Meta (Dic 23, 2008)

Actualización *asm_desde_cero v1.11.pdf* _Sólo 3 descargadas._

Sustituyo la v1.11 por la *v1.12*.


----------



## Leon Elec (Ene 9, 2009)

*SUBWF*

Resta el contenido de un registro seleccionado por el programador con el contenido del registro W. La fórmula es F - W = d. d es la dirección elegida por el programador en dónde se guardará el resultado que puede ser el registro W o el registro elegido por el programador.

_Ejemplo:_


```
SUBWF        MINUENDO,W
```

Si antes de la instrucción W vale .55 y MINUENDO vale .56, después de la instrucción, MINUENDO vale .56 y W vale .1

Para recordar *SUB*traction es resta *W* es el registro W y *F*ile es el registro elegido.

*Afecta a:*



*Z* se pone a 1 si el resultado es 0
*DC* se pone a 0 si hubo un acarreo del 4 bit al 5 bit (recordemos que en la resta, es distinto a la suma, por eso, se pone a 0 si hubo acarreo).
*C* se pone a 0 si hubo acarreo del 7 bit. (recordemos que en la resta, es distinto a la suma, por eso, se pone a 0 si hubo acarreo).

*SWAPF*

Intercambia los bits de un mismo registro elegido por el programador. Los 4 bit de menor peso, pasan a ser lo 4 bits de mayor peso, y los 4 bits de mayor peso, pasan a ser los 4 bits de menor peso. El destino puede ser seleccionado.

Cabe pensar que puede ser una instrucción de muy poco uso, pero todo lo contrario si se utilizan con las interrupciones. Microchips recomienda su utilización a la hora de salvar el contexto y restaurarlo en una interrupción ya que no modifica el registro STATUS. Cuando trabajemos con la interrupciones, se verá que es muy recomendable salvar el registro STATUS y W en la RAM para luego restaurarlos. Si utilizamos la instrucción MOVF, es afectado el bit Z, perdiendo su estado original en el momento de la interrupción. Esto se soluciona, utilizando la instrucción SWAPF. No se preocupen si no lo entiende por ahora. Lo entenderan cuando veamos ejemplo de interrupciones.

_Ejemplo:_



> SWAPF         STATUS,W



Si antes de la instrucción W bale H'55' y el registro STATUS vale B'00100100', después de la instrucción el registro STATUS vale H'24' y el registro W vale B'01000010'

Para recordar *SWAP* es intercambiar NIBBLE es porción *F*ile es el registro.

No afecta ningún bit del registro STATUS.

*XORLW*

Realiza la operación lógica XOR entre un literal o valor y el registro W. El resultado queda guardado en el registro W.

_Ejemplo:_


```
XORLW        B'11000101'
```

Si antes de la instrucción W vale B'11111000', después de la instrucción W vale B'00111101'.

Para recordar *XOR* es la operación lógica XOR *L*iteral es un valor *W* es el registro W.

*Afecta a:*



*Z* se pone a 1 si la operación es 0

*XORWF*

Realiza la operación XOR entre un registro elegido por el programador y el registro W. La operación es F XOR W = d. El resultado se puede elegir dónde será guardado.

_Ejemplo:_


```
XORWF        PORTB,F
```

Si antes de la instrucción PORTB vale B11111110' y W vale B'00000001', después de la instrucción W vale .1 y PORTB vale B'11111111'.

Para recordar *XOR* es la operación lógica XOR *W* es el registro W y *F*ile es el registro elegido.

*Afecta a:*



*Z* se pone a 1 si la operación es 0


----------



## Meta (Ene 11, 2009)

Actualizado a v1.13


----------



## Leon Elec (Ene 14, 2009)

Meta. actualizé un error que tenía la instrucción INCFSZ, que está en la página 5 del foro. Saludos.


----------



## Meta (Ene 14, 2009)

Leon Elec dijo:
			
		

> Meta. actualizé un error que tenía la instrucción INCFSZ, que está en la página 5 del foro. Saludos.



¿Te refieres sólo lo que indico abajo?

INCFSZ

Incrementa en 1, o suma en 1, el contenido de un registro elegido por el programador y cuando este es 0, el CP salta una instrucción.

Código: Seleccionar todo

    VOLVER          INCFSZ          CONTADOR
                    GOTO            VOLVER
                    INCF            PORTA



El CP incrementará en 1 el registro CONTADOR y evalúa el valor, si no es cero, ejecuta línea siguiente que es GOTO VOLVER, el cual se dirige de nuevo a la línea VOLVER INCFSZ CONTADOR el cual volverá a incrementar en 1 y evalua el valor, si es cero salta la línea GOTO VOLVER y ejecuta la instrucción INCF PORTA. Esta última línea, el programador pondrá la instrucción que necesite ejecutar.
Este pequeño programa que acabamos de ver, es un temporizador o un retardo que tardará en salir del bucle dependiendo de la frecuencia de reloj y el valor cargado en CONTADOR. Normalmente, se utiliza el retardo con DECFSZ pero este también es válido.

NOTA: Esta instrucción, también hay que elegirle el destino. En el caso que no se exprese, como en este caso, el MPLAB dará por sentado que el resultado se guardará en el registro F y no en W.

Para recordar INCrement es incremento File es registro Skip es salto Zero es cero.

No afecta ningún bit del registro STATUS.


----------



## Leon Elec (Ene 15, 2009)

Así es. Decía decremento en vez de incremento. EL error estaba en tres partes distintas en la explicación.


----------



## Leon Elec (Feb 8, 2009)

Una vez que vimos todas las instrucciones y asumiendo que ya se la saben de memoria (tiempo tuvieron de sobra) y si no lo han echo, es hora de hacerlo, empezemos a escribir programas muy sencillos. Pero ¿cómo?. Si se están asiendo esta pregunta, es hora de leer desde el primer mensaje. Tienen que acordarce las pociciones de las cuatro columnas.

Recuerden que vamos a escribir los programas en el MPLAB, por lo que si aún no lo tienen, es hora de descargarlo de la página de Microchip en forma gratuita e instalarlo en la PC. Lamentablemente, este programa trabaja solo bajo windows, por los que tienen mac o Linux, no lo prodrán hacer si no es por un emulador de windows y aún así, no se si funcione correctamente.

Empezaremos bien desde el principio y para ello, voy a explicar cosas nuevas. Todo programa tiene un  encabezado que se repite en cada programa nuevo y que varía según el PIC a utilizar y las prestaciones del programa a escribir. Muchas de las personas crean plantillas nuevas para ahorrarce el trabajo. Yo soy una de ellas y aquí les doy una plantilla que pueden utilizar sin problemas y modificarlas según su necesidad.

He aquí la plantilla que también está adjuntado.

			LIST		P=16F877A
			INCLUDE		<P16F877A.INC>
			__CONFIG	_CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC
;
;---------------------------------------------------------------
;VARIABLES.
;---------------------------------------------------------------
;
			CBLOCK		H'20'

			ENDC
;
RESET		ORG			H'00'
			GOTO		INICIO
			ORG			H'04'			;VECTOR INTERRUPCIÓN
INTERRUP
;
INICIO
;
			END

*Primera línea:*

LIST  P=16F877A

Esta línea, sive solo para el MPLAB y le indica que el PIC a utilizar es el 16F877A. Se debe actualizar según sea necesario.

*Segunda línea:*

INCLUDE   <P16F877A.INC>

Esta línea sirve a la hora de compilar ya que dentro de este archivo que viene incluido dentro del MPLAB, le indica al ensamblador dónde están ubicados cada registro y el nombre de cada bits del PIC a utilizar así, nos ahorra de hacerlo nosotros mismos. Si desean abrir el archivo, lo pueden hacer con el notepad o cualquier editor de texto. Se debe actualizar según sea necesario.

*Tercera línea:*

__CONFIG	_CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC

Linea muy importante pero se puede omitir. Aquí se configura la palabra de configuración del PIC y que está en el datasheet del pic correspondiente.
En mi caso, yo la omito y luego lo configuro desde el ICPROG a la hora de pasar el archivo .hex al pic. Es por eso que esta línea está incompleta para el pic a utilizar pero completa para el PIC 16F84.

Vemos en más detalle esta línea: __CONFIG Directiva para el ensamblador de que debe generar la palabra de configuración correspondiente de acuerdo a:

_CP_OFF la protección de lectura del PIC desabilitado.
_PWRTE_ON Reset de encendido habilitado
_WDT_OFF Perro guardián desabilitado.
_XT_OSC Oscilador a cristal.

El caracter & sirve para unir una directiva con otra.


----------



## Leon Elec (Feb 8, 2009)

*Cuarta línea:*

;
;---------------------------------------------------------------
;VARIABLES.
;---------------------------------------------------------------
;

Este no es más que un pequeño encabezado. El ensamblador omitirá estas líneas y solo porque empieza con ";". Recuerden que, toda línea que empieze con ";" es comentario y el ensamblador la omitirá.

Justo por debajo de este encabezado, penemos las variables. ¿a que le llamo variables? a pociciones de memorias o nombre de bit que yo quiera darle personalmente.
Por ejemplo, dentro del archivo P16F877A.INC a la pocición de memoria 0x05 le llama PORTA pero si yo quero llamarlo PUERTO_A, es quí dónde le indico al ensamblador.

*¿Cómo se hace?*

Es muy fácil y con la instrucción "EQU"

PUERTO_A   EQU  H'05'

Si al Bit RA6 lo quero llamar LED6 lo hacemos así:

LED6  EQU  6

Supongamos que quiero encender el LED 6 que está en el Puerto a bit 6, y este se activa con un 1 lógico, ponemos esta instrucción:

BSF   PUERTO_A,LED6

Y el ensamblador lo traducirá así: BSF  0X05,0X06

*Quinta línea:*

 CBLOCK      H'20'

         ENDC

A estas dos líneas las unifique porque van juntas. Entre ellas van los nombres de cada pocición de la RAM que nosotros querramos nombrar, o cada Registro que querramos renombrar.
Recuerden que los registros están implementados en la RAM y que la pocición de la RAM que puede utilizar el usuario para guardar cosas, empieza desde una pocición que está indicada en el datasheet. Para el caso de 16F877 empieza desde la pocición 0x20 como lo ven en la figura siguiente.

(Ver archivo adjunto)

La directiva CBLOCK      H'20' indica que empezamos a nombrar pociciones de memoria desde la ubicación 0x20. Por ejemplo

CBLOCK  H'20'
temperatura
demora1
demora2
ENDC

La pocición 0x20 se llama temperatura, la pocición 0x21 se llama demora1 y así sucesivamente. Tambien existe otra forma de nombrar o renombrar pociciones de memoria o registro, y ya lo vimos que es con la directiva EQU. por ejemplo:

temperatura   EQU   H'20'
demora1        EQU   H'21'
demora2        EQU   H'22'

*Sexta línea:*

RESET      ORG         H'00'
         GOTO      INICIO
         ORG         H'04'         ;VECTOR INTERRUPCIÓN
INTERRUP

He unificado estas líneas porque por lo general así se escribe en la mayoría de nuestros programas. Vemos con más detalles estas líneas.

RESET  ORG  H'00'

Aquí es en dónde empieza el CP cuando se enciende al PIC. Y estos se debe, porque cuando hay un reset por el pin MCLR o un reset interno producido por, encendido del PIC o, por ejemplo, del perro guardía, el vector reset se ubica en la pocición 0x00 de la memoria de programa.
Como pueden apreciar, hemos llamado a la pocición 0x00 con el nombre de RESET, ya que la primer columna es una etiqueta. Las etiquetas sirven para nombrar pociciones de memoria de programa.

ORG  H'00' indica que se empieza a escribir desde la pocición 0x00 por lo que la siguiente instrución, quedará alojada en la pocición 0x00. ORG es una directiva, y solo sirve para el ensamblador. Esta línea, *SIEMPRE* debe estar y no se puede modificar, salvo la etiqueta.

Siguiente línea es GOTO  INICIO, esta instrucción queda alojada en la pocición 0x00 y es un salto a la etiqueta INICIO. Este salto tiene que estar, porque en las siguientes pociciones de memoria de programa, está la interrupción, externa e interna del PIC. Por eso es necesario hacer el salto.

Siguiente línea nos encontramos con ORG  H'04', como se dieron cuenta, es una directiva. Esta pocición de memoria, indica el vector de interrupción. Cuando estén habilitadas las interrupciones y una de ella se activa, el CP apuntará aquí, a la pocición 0x04.

Siguiente línea nos encontramos con la etiqueta INTERRUP. Si trabajamos con las interrupciones, aquí es donde escribiremos lo que necesitamos hacer con ellas.

*Séptima línea:*

;
INICIO
;

Vemos la etiqueta INICIO. Aquí vendrá el CP cuando alla un reset gracias al GOTO INICIO ubicado en la pocición 0x00 de la memoria de programa. De esta manera, saltamos un montón de instrucciones que no debemos ejecutar antes. Como por ejemplo las interrupciones si hubiese.
Por ende, a partir de esta etiqueta, estará nuestro programa principal. Configuraremos los puertos, las insterrupciones y empezaremos a darle trabajo al CP.

*Octava línea:*

END

Esta es una directiva y solo sirve para el ensamblador indicando que después de esta directiva, se terminó el programa. Instruciones que estén por debajo de esta línea, no serán tenido encuenta. Y esta directiva es obligatoria colocarla.


----------



## Meta (Feb 10, 2009)

ASM desde cero v1.14


----------



## navarretemarce (Feb 16, 2009)

Excelente trabajo!. Me venia cabeceando con mil cosas que no lograba comprender y con este tutorial se fue aclarando y aclarando. 
Hay algo que aun no comprendo. 
Con las operaciones de SUBxx y el resultado es negativo comprendo lo del acarreo, pero el valor que adopta el registro  como es?
Lo pongo en un ejemplo
Si W= .10

SUBLW .5 

Cuanto vale W luego de la operacion? W=.0 ó W=.5 ó W=.250? o como lo calcula? 

Perdon por la molestia


----------



## Leon Elec (Mar 4, 2009)

> Con las operaciones de SUBxx y el resultado es negativo comprendo lo del acarreo, pero el valor que adopta el registro como es?
> Lo pongo en un ejemplo
> Si W= .10
> 
> ...


No es ninguna molestia.

El .10, no es con coma, si no quiere decir que el valor es decimal, o sea, en la forma en que nosotros escribimos los números (con base en 10).

Si estubiera expresado como W = 0x10, sería 10 en hexadecimal, y en decimal corresponde 15

¿Se entiende?


----------



## xxxlepexxx (Mar 5, 2009)

Hola, si 
W=.10 y haces 
SUBLW .5 
es lo mismo que decir W=5-10 ya que W siempre es el sustraendo, así que el resultado te da -5 teniendo en cuenta que el acarreo se pone en 0 después de esta operación.


----------



## Leon Elec (Mar 5, 2009)

Correcto. Chequeando el bit C, sabes si es negativo o positivo el resultado obtenido.


----------



## Meta (Mar 9, 2009)

También espero, porque la versión portable a PDF está muy bien...

Saludo.


----------



## Elvic (Abr 7, 2009)

saludos

bueno primero felicitarlos por el gran trabajo que han estado realizando tanto a Leon Elec y Meta.

El caso es que hace tiempo aprendía a escribir código en ensamblador usando MPLab, y pues  ahora que tengo tempo libre me puse a leer este tema *ASM desde 0*,  para recordar, el caso es que me surgió una duda y es la siguiente.

veo que al comenzar un código escriben la siguiente linea


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

en la cual no entiendo bien porque? se usa esto de  LIST P=16F877A,  yo solamente ponía lo de INCLUDE "P16F877A.INC"

Por ejemplo el siguiente código 


```
;código para el PIC 16F84A
              ;realiza lo operación lógica Xor del puerto A con el numero Hex 13
                 Include "p16f84a.inc"
                 org h'00'
                 bsf STATUS,5       ;se pone un 1 en el bit 5 del status para elegir el banco 1
                 movlw h'1F'         ;cargamos a w con el 1f
                 movwf TRISA       ;se carga trisA 1F para que puerto A sea de entrada
                 movlw h'00'	     ;cargamos a W con 00
                 movwf TRISB       ;se manda el 00 al TrisB para que el puerto B sean salidas
                 bcf STATUS,5       ;cambiamos al banco 0 poniendo un 0 en el bit 5 del Status
    	         movf PORTA,0      ;leemos el puerto A y cargamos en W
                 xorlw h'13'	     ;realiza la operación lógica
                 movwf PORTB      ;Movemos el resultado al PuertoB
                  end
```

Esto lo note puesto que quise simular paso a paso con Proteus ISIS pero me marca error,  pues no incluyo el LIST; aunque el código que les muestro si lo compila y simula bien en MPLAB IDE v7.20.

el caso es : como debo cambiar el programa anterior para incluirle lo de LIST y simularlo paso a paso con proteus v7.4, ya he probado otros códigos que baje del Internet que incluyen el LIST y si simula bien en proteus, así que no creo que sea la instalación del software.

espero me puedan responder y sigan adelante con este fantástico trabajo.


----------



## Meta (Abr 8, 2009)

Prueba con el MPLAB 8.30...


----------



## Elvic (Abr 8, 2009)

saludos

gracias por responder, y por la sugerencia meta

y si ya pude solucionar mi problema (hasta el momento) solamente que de otra manera; Lo que requería era ir viendo la ejecución de cada linea de código conforme aparecían cambios de estado en el circuito dibujado en proteus es decir ejecutarlo paso a paso.

el problema se me presentaba desde un principio al querer utilizar la opción *build All* que se encuentra en la barra menú de proteus (source), pues marca muchos errores.

al estar buscando encontré la  solución en el debugger directamente del MPLAB en el se dan varias opciones, la que solía utilizar era  la *MPLAB SIM*, pero vi que también aparece habilitad la opción *Proteus VSM *, así que comencé a utilizarlo y me funciono para lo que necesitaba.

supongo que esto pasa por que se vinculan los programas MPLAB y proteus automáticamente con la instalación

bien pero pues aun tengo la duda de la linea LIST p=pic16fxxx

PD: vi un apartado donde hablan sobre un problema sobre la simulación con PIC, quizá les pueda servir esta solución y un moderador lo quiera mover para no volver copiar y pegar este mensaje en aquel post y que no aparezca repetido

suerT


----------



## arzu inc (May 13, 2009)

Hola a todos

una pregunta ¿es posible pasar un archivo compilado .HEX nuevamente a .ASM ?y si es posible como podría hacerlo

gracias


----------



## Meta (May 13, 2009)

Hay varias formas. Sea con ic-prog o WinPic800.

Mira este manual en la página 43 que te dice como.

http://www.slideshare.net/Metaconta/manual-bsico-winpic800-presentation?src=embed

Hay otra forma de hacerlo con MPLAB v8.30 es:
Archivo-->Import... (Busca el hex). Después, View-->Program Memory, ya puedes ver el código máquina.


----------



## andy1 (Jun 16, 2009)

hola cpmpas , como comente soy nuevo en eso de la progamacion de pics , quisiera que alguien me explique bien calro la intruccion btfsc y btfss , en que momento realiza el salto?
gracias por su colaboracion 
attndres


----------



## Chico3001 (Jun 16, 2009)

BTFSS y BTFSC realizan una prueba de bit y saltan una instruccion si el resultado es cero o uno (dependiendo de la instruccion que uses), por ejemplo digamos que tienes la localidad de memoria "prueba" cargada con un numero 0b00101001 (19h) si ejecutas el siguiente programa:

BTFSS prueba,3
goto bitnocero
goto bitcero

Como el bit 3 de prueba es 1, el PIC va a saltar la instruccion "goto bitnocero" y ejecutara la instruccion "gotobitcero" en cambio si el bit 3 fuera cero el PIC ejecutara la instruccion "goto bitnocero"

Exactamente lo contrario ocurriria si usaras BTFSC... en ese caso la siguiente instruccion SOLO se ejecutara si el bit que pruebes del registro es igual a uno


----------



## andy1 (Jun 18, 2009)

gracias pana estubo muy claro
attndrest


----------



## elosciloscopio (Jul 7, 2009)

Pues eso aquí les dejo 37 programas para ensamblador.
Algunos son muy sencillos, algunos muy complejos, y otros simplemente no sirven para nada.

Pertenecen al disco que viene con el sistema de desarrollo "Pic laboratory", de la firma Microchip
Tengo información adicional que les puedo enviar en caso de que la soliciten.

Indice de programas

1.   Simple manejo de las entradas y salidas
2.   función lógica NOT
3.   Decodificador combinacional
4.   Simulación de una máquina de torneado
5.   Simulación de una maniobra de taladrado
6.   Simulación de máquina de envasar
7.   Simulación de máquina de envasar mejorada
8.   Juego de luces (chaser)
9.   Interrupción del TMR0
10. Interrupción externa RB0/INT
11. El display de 7 segmentos
12. Decodificador BCD a 7 segmentos
13. Contador decimal "UP/DOWN"
14. EL TMR0 como contador de pulsos externos
15. La memoria EEPROM; La máquina "su turno"
16. Introducción al manejo de la pantalla LCD
17. Maejo del LCD
18. Visualizando mensajes en el LCD
19. El teclado
20. Teclado y LCD
21. Interrupción por cambiode estado
22. Interrupción del TMR1 cada cierto tiempo
23. El TMR1 como contador de evemtos externos
24. El TMR1 como divisor de frecuencia
25. Frecuencímetro
26. Interrupción periódica del TMR2
27. El TMR2; temporizaciones ajustables
28. Capturas con el módulo CCP1, midiendo períodos
29. Capturas con el módulo CCP1, midiendo la anchura de un pulso
30. Capturas con el módulo CCP1, midiendo el tiempo entre pulsos
31. Capturas con el módulo CCP1, contador de eventos ajustables
32. Modulación de la anchura de pulsos (PWM) mediante el módulo CCP1
33. PWM mediante el módulo CCP1, Variando la anchura
34. El convertidor ADC
35. Modulación PWM, mediante el convertidor ADC
36. El USART, transmitiendo caracteres
37. El USART, transmisión/recepción de caracteres

Notas:

-Todos los programas vienen en .asm
-Algunos de los programas sólo pueden usarse con el "PIC laboratory"
-Para ser usados con otro entrenador los programas deben ser modificados.
-La explicación detallada del programa está escrita al principio de este.


----------



## albermillan69 (Jul 28, 2009)

Como hago para realizar una division? algun ejemplo porfa!


----------



## Chico3001 (Jul 28, 2009)

Busca la division en esta nota de aplicacion de Microchip:

http://ww1.microchip.com/downloads/en/AppNotes/00544d.pdf


----------



## albermillan69 (Jul 28, 2009)

Chico3001 dijo:
			
		

> Busca la division en esta nota de aplicacion de Microchip:
> 
> http://ww1.microchip.com/downloads/en/AppNotes/00544d.pdf




Pero con el 16F877?


----------



## Chico3001 (Jul 28, 2009)

es exactamente lo mismo... la nota de aplicacion aplica para cualquier micro de la serie 16 o 17.... incluso con adaptaciones menores cualquiera de microchip


----------



## Ariel28 (Nov 6, 2009)

antes que nada quisiera agradecerles por el tiempo que invierten para compartir sus conocimientos!!!!
Cuando me propuse a programar PICS comence aprendiendo ASM del libro de ANGULO conocidos por muchos comence como todos con el famoso programa de prender y apagar un LED cosa que me resulta toda una lucha  pero al final venci y asi fue subiendo la comp`lejidad de los programas hasta que un dia me propusieron hacer un programa muy complejo con transmision serial, usando el ADC y asi practicamente exprimiendo al 16F877a y he ahi comenzo mi pesadilla que acontinuacion les comento.
Empece con el soft todo bien hasta que empece a uasr los temporizadores tmr1, tmr2, tmr0 los cuales usaba para muchas tareas a la vez que paso??
Era como que al micro le confundia me explico mejor, recibia una trama de datos por el puerto serial entonces comenzaba a correr el TMR0 para comparar y ver que es lo que habia recibido para tomar una descision que pasaba, si el TMR1 desbordaba cuanbdo se cumplia la interrupcion de TMR0 muchas veces no todas el 
PIC  hacia cualquier otra cosa es como que no le gustaba eso!!! pero ustedes me diran las banderas de interrupcion GIE, ...etc estaban correctas? y les contesto que siiiiii!!!! Hacia mi debuger con el siumulador de micro PIC SIMULATOR IDE lo cual muestra el estado de cada registro y bits que maneja al pic asi fui puliendo mi soft pero me di cuenta de que tal programa de simulador no era tan bueno porque al poner el pic en el circuito suponiendo claro que el soft del micro funcionaba como ya queria segun el simulador NO FUNCIONABA!!!!! en realidad al final tuve que borrar todo el soft hacer mi debugger en mi circuito cosa que casi me mato imaginense todo el trabajo ahora yo pregnto a los entendidos!!! Que puede estar mal? La forma que encare las lineas? Saben como termino esta tragedia pero no el fin de mi pesadilla les cuento!!! Compre el niple programa que sirve para proframar micro de la gama 16FXXX, me soluiciono en parte mi problema ya que funciono mi soft pero........
Me acarreo otro problemas 
1ero) Para soft muy largos y en ciertas formas complicados no genera todo el ASM por lo tanto tuve que revisar linea a linea todo hasta encontrar el error
2do)) No da la libertad de manejar a gusto como lo hace el ASM
3ero) No sirve para la paginacion de memorias otro dia les comentos por detalles que me sucedio pero rapidamente les digo que descompone y se vuelve loco el niple cuando se llena la pagina 0 de la memoria y se debe pasar a la pagina 1 Mi pregunta nuevamente es QUE puedo hacer para solucionar mi problema que pautas debo seguir algun suitio que me puedan orientar ya que Leon ELEC escribio:
******************************************************************
El CP, hará esas tareas sin negarce y lo hará fielmente a lo que está escrito. Por lo que, si hace una tarea mal, es porque le pusimos una o más instrucciones mal
******************************************************************
Yo se que en alguna parte algo esta mal como darme cuenta de eso 
desde ya agradecido por cualquier ayuda


----------



## lucasd92 (Dic 12, 2009)

Ariel, lamento no poder ayudarte porque recien hice la plaquetita pra programar un pic 16f84a... si siquiera hice la prueba del led porque me quedo una duda que para uds debe ser simple pero yo no estoy seguro...el valor del registro "w" es el valor que toman las salidas del pic?
no se si el tutorial lo decia en alguna parte pero no me cerraba ese tema. hace creo que 3hs que estoy leyendo esto y entendi bastante solo me quedaria saber eso, con el tutorial se como cambiar los valores del registro pero que hago con eso? donde van a parar??


fuera de eso quiero agradecerles a loen elec y a meta por su ayuda, es la primer guia que encuentro en internet de la que entendi porque todas hacen los programas como si todo el mundo naciera sabiendo:enfadado:

les doy un merecido

la verdad que son de lo mejor!! 

gracias!!!


----------



## osac99 (Dic 17, 2009)

para Ariel 
Cuando se trabaja con todas las interrupciones al mismo tiempo es mejor trabajar con los PIC de la familia 18, los cuales tienen interrupcion de prioridad y asi evitas estos problemas


----------



## kapotik (Dic 18, 2009)

buenas a todos, alguien podria decirme como recibir una cadena de caracteres por rs 232 para luego analizarla?

Gracias


----------



## Meta (Dic 18, 2009)

kapotik dijo:


> buenas a todos, alguien podria decirme como recibir una cadena de caracteres por rs 232 para luego analizarla?
> 
> Gracias




```
;***************************** Librería "RS232.INC" *************************************
;
;    ===================================================================
;      Del libro "MICROCONTROLADOR PIC16F84. DESARROLLO DE PROYECTOS"
;      E. Palacios, F. Remiro y L. López.        www.pic16f84a.com
;       Editorial Ra-Ma.  www.ra-ma.es
;    ===================================================================
;
; Estas subrutinas permiten realizar las tareas básicas de control de la transmisión
; seríe asincrona según normas RS-232.
;
; Los parámetros adoptados para la comunicación son los siguientes:
; - Velocidad de transmisión de 9600 baudios. La duración de cada bit será 104 µs.
; - Un bit de inicio o Start a nivel bajo.
; - Dato de 8 bits.
; - Sin paridad.
; - Dos bits de final o Stop a nivel alto.
;
; El tiempo entre bit y bit  debe coincidir con el periodo de la señal leída o enviada.
; Como la velocidad de transmisión o recepción es de 9600 baudios, el periodo será:
; 1/9600 Baudios = 104 µs. Se utilizará pues la subrutina Retardos_100micros.

    CBLOCK
    RS232_ContadorBits
    RS232_Dato
    ENDC

#DEFINE  RS232_Entrada    PORTA,4        ; Línea por la que se reciben los datos.
#DEFINE  RS232_Salida    PORTA,3        ; Línea por la que se envían los datos.
;
; Subrutina "RS232_Inicializa" -------------------------------------------------------------
;
; Configura las líneas de salida y entrada del microcontrolador.

RS232_Inicializa
    bsf    RS232_Salida                ; Al principio salida en alto para resposo.
    bsf    STATUS,RP0
    bsf    RS232_Entrada                ; Esta línea se configura como entrada.
    bcf    RS232_Salida                ; Esta línea se configura como salida.
    bcf    STATUS,RP0
    return

; Subrutina "RS232_LeeDato" -------------------------------------------------------------
;
; El microcontrolador lee el dato por la línea de entrada comenzando por el bit de menor
; peso. El dato leído se envía finalmente en el registro de trabajo W.
;
; El ordenador parte siempre de un nivel alto, que es el estado que tiene cuando no 
; envía información. La secuencia utilizada es:
; 1º    Espera que se ejecute el pulso negativo del bit Start o flanco de bajada.
; 2º    Deja pasar un tiempo una y media veces mayor que el periodo de transmisión para
;        saltarse el bit de Start y lee el primer bit en su mitad.
; 3º    Lee el resto de los bits de datos, esperando un tiempo igual a la duración del
;        período entre lectura y lectura para testearlos en mitad del bit.
;
; Salida: En el registro de trabajo W el byte leído.

RS232_LeeDato
    movlw    d'8'                    ; Número de bits a recibir.
    movwf    RS232_ContadorBits
RS232_EsperaBitStart
    btfsc    RS232_Entrada             ; Lee la entrada y espera a que sea "0".
    goto    RS232_EsperaBitStart    ; No, pues espera el nivel bajo.
    call     Retardo_100micros        ; El primer bit debe leerlo un tiempo igual a una
    call     Retardo_50micros         ; vez y media el periodo de transmisión.
RS232_LeeBit
    bcf        STATUS,C                ; Ahora lee el pin. En principio supone que es 0.
    btfsc    RS232_Entrada            ; ¿Realmente es cero?
    bsf        STATUS,C                ; No, pues cambia a "1".
    rrf        RS232_Dato,F            ; Introduce el bit en el registro de lectura.
    call    Retardo_100micros        ; Los siguientes bits los lee un periodo más tarde.
    decfsz     RS232_ContadorBits,F    ; Comprueba que es el último bit.
    goto    RS232_LeeBit            ; Si no es el último bit pasa a leer el siguiente.
    call    Retardo_200micros        ; Espera un tiempo igual al los 2 bits de Stop.
    movf    RS232_Dato,W            ; El resultado en el registro W.
    return    

; Subrutinas "RS232_EnviaDato" y "RS232_EnviaNúmero" -------------------------------------
;
; El microcontrolador envía un dato por la línea de salida comenzando por el bit de menor
; peso. En dato enviado será el que le llegue a través del registro de trabajo W.
; 1º.    Envía un "0" durante un tiempo igual al periodo de la velocidad de transmisión.
;        Este es el bit de "Start".
; 2º.    Envía el bit correspondiente:
;         - Si va a enviar un "0" permanece en bajo durante el periodo correspondiente.
;        - Si va a escribir un "1" permanece en alto durante el periodo correspondiente.
; 3º.    Envía dos bits "1" durante un tiempo igual al período de la velocidad de 
;        transmisión cada uno. Estos son los dos bits de Stop.
;
; Entrada:    En (W) el dato a enviar.

RS232_EnviaNumero                    ; Envía el código ASCII de un número.
    addlw    '0'                        ; Lo pasa a código ASCII sumándole el ASCII del 0.
RS232_EnviaDato
    movwf    RS232_Dato                ; Guarda el contenido del byte a transmitir.
    movlw    d'8'                    ; Este es el número de bits a transmitir.
    movwf    RS232_ContadorBits
    bcf        RS232_Salida            ; Bit de Start.
    call    Retardo_100micros
RS232_EnviaBit                        ; Comienza a enviar datos.
    rrf        RS232_Dato,F            ; Lleva el bit que se quiere enviar al Carry para
    btfss    STATUS,C                ; deducir su valor. ¿Es un "1" el bit a transmitir?
    goto    RS232_EnviaCero            ; No, pues envía un "0".
RS232_EnviaUno
    bsf        RS232_Salida            ; Transmite un "1".
    goto    RS232_FinEnviaBit
RS232_EnviaCero
    bcf        RS232_Salida            ; Transmite un "0".
RS232_FinEnviaBit
    call    Retardo_100micros        ; Este es el tiempo que estará en alto o bajo.
    decfsz     RS232_ContadorBits,F    ; Comprueba que es el último bit.
    goto    RS232_EnviaBit            ; Como no es el último bit repite la operación.
    bsf        RS232_Salida            ; Envía dos bits de Stop.
    call    Retardo_200micros
    return
    
;    ===================================================================
;      Del libro "MICROCONTROLADOR PIC16F84. DESARROLLO DE PROYECTOS"
;      E. Palacios, F. Remiro y L. López.        www.pic16f84a.com
;       Editorial Ra-Ma.  www.ra-ma.es
;    ===================================================================
```


----------



## kapotik (Dic 22, 2009)

gracias meta estudio pruebo y te comento si pude lograrlo
Gracias nuevamente


----------



## tecnogirl (Ene 26, 2010)

Yo aporto un conjunto de plantillas para programar PICs en ensamblador mplab/mpasm. Solo se busca la plantilla que corresponde al modelo de PIC a programar y se rellena. Estas plantillas vienen en el paquete gratuito de MPLAB de Microchip luego no hay problema con los derechos de propiedad intelectual. Salu2.


----------



## Chico3001 (Ene 27, 2010)

Me tome la libertad de unir este tema con el de ASM desde Cero para que todos puedan localizar mas facilmente los ejemplos

Saludos...


----------



## 0rland0 (Mar 2, 2010)

Escribo para agredecer la el trabajo de leon_elec y meta por su trabajo. Su explicacion fue *la mejor* para estudiar para el primer parcial de micros. Seria bueno q se metieran con los timers ahora :S


----------



## 0rland0 (Mar 7, 2010)

Bueno el TMR0 tiene un tiempo maximo de 65.536ms, y en el 1er ejemplo q dan lo calculan para 1seg osea 1000ms y sacam un valor nose como...
En el 2do ejemplo lo calculan para el tiempo maximo q puede tener y les da 65.566ms... tampoco entiendo porq, como dije antes... es de 65.536ms el maximo.

Claro todo esto para un cristal de 4Mhz.

Si tuve algun error corriganme q mi examen es mañana


----------



## Meta (Mar 7, 2010)

Tiene que ser varias veces 65.536 ms si quieres que te salgan muchos segundos e incluso minutos. Para estas cosas mejor usar un Timer de 16 Bits, dura mucho más tiempo.


----------



## 0rland0 (Abr 6, 2010)

Hola, tengo una duda... Puedo manejar el registro W como si fuera otro registro mas de la ram? y asi poder ejecutar insctrucciones con él? Por ejemplo...

COMF	 W,W; Complementa W y lo guarda en W


----------



## Meta (Abr 7, 2010)

0rlandissim0 dijo:


> Hola, tengo una duda... Puedo manejar el registro W como si fuera otro registro mas de la ram? y asi poder ejecutar insctrucciones con él? Por ejemplo...
> 
> COMF     W,W; Complementa W y lo guarda en W



El *W* no tiene nada que ver como registro de la RAM, funciona en modo independiente. Pero puedes meter los datos que desees desde el registro a la RAM y al revés.


----------



## dcastibl1 (Abr 9, 2010)

buenas tengo una duda con un programa donde tengo que rotar 8 leds conectados en el puerto b cada segundo pero con timer 0 el programa lo acabe de hacer y pues no tengo el mplab para saber si esta bien el programa es el sgte:


```
LIST		P=16F84A
	INCLUDE		<P16F84A.INC>


             CBLOCK 0x0C
             REGISTRO50ms
             ENDC

             TMR0_CARGA50ms   EQU d'256'-d'195'
             CARGA_1S    EQU  d'20'

             ORG  0
             GOTO INICIO
             ORG  4
             GOTO TIMER0_INTERRUPCION		

INICIO              
             BSF STATUS,5
             CLRF TRISB
            ;MOVLW b'00000001'
            ;MOVWF TRISA
             MOVLW b'00000111'
             MOVWF OPTION_REG
             BCF STATUS,5
             MOVLW TMR0_CARGA50ms
             MOVWF TMR0
             MOVLW CARGA_1S
             MOVWF REGISTRO50ms
             MOVLW b'10100000'
             MOVWF INTCON
PRINCIPAL
             GOTO $

;SUBRUTINA "TIMER0_INTERRUPCION"


TIMER0_INTERRUPCION
             BSF STATUS,C
             MOVLW TMR0_CARGA50ms
             MOVWF TMR0
             DECFSZ REGISTRO50ms,F
             GOTO FININTERRUPCION
             RLF PORTB,F
             GOTO TIMER0_INTERRUPCION

FININTERRUPCION
             BCF INTCON,TOIF
             RETFIE
```


----------



## FRYCK (Abr 15, 2010)

hola dcastibl1  este  programa  esta  haciendo lo que quieres  solo le  modifique  tres lineas de código   ya que  en lugar  de rotar agregaba  otro uno cada segundo


----------



## dcastibl1 (Abr 16, 2010)

gracias fryck_80 por el programa ahora lo probare y luego te comento ya lo habia abandonado porq estoy lleno de trabajos en la u saludos


----------



## SERCH17 (May 5, 2010)

Hola soy nuevo en el foro, pero tengo que agradecerles que este tutorial me ha servido de mucho, pero hay una duda que no he podido resolver y espero que me ayuden y es sobre las instrucciones sublw y subwf y como afectan el flag DC ya que no entiendo exactamente que hace el programa para activar y desactivar este flag. Por ejemplo: cuando resto 0xF - 0xF= 0 la bandera se activa, pero cuando hago 0x10-0x10=0 no se activa y no entiendo por que.  Gracias por anticipado


----------



## FRYCK (May 5, 2010)

SERCH17 dijo:


> Hola soy nuevo en el foro, pero tengo que agradecerles que este tutorial me ha servido de mucho, pero hay una duda que no he podido resolver y espero que me ayuden y es sobre las instrucciones sublw y subwf y como afectan el flag DC ya que no entiendo exactamente que hace el programa para activar y desactivar este flag. Por ejemplo: cuando resto 0xF - 0xF= 0 la bandera se activa, pero cuando hago 0x10-0x10=0 no se activa y no entiendo por que.  Gracias por anticipado



Hola *SERCH17 * las  restas tanto sublw y subwf en los micros utilizan  complemento a dos de un número binario es el número resultante de invertir los UNOS y CEROS de dicho número. Por ejemplo si:  N = 0000110100101 obtenemos su complemento a uno invirtiendo ceros y unos, con lo que resulta: C1N = 1111001011010 y para   su complemento a dos es: C2N = C1N + 1 = 1111001011011 si te das cuenta  al  esta operacion realizar  ese complemento  afecta  tanto  el bit deDigit Carry "DC" como el Zero "Z" y el carry "C"  ya que  se puede decir que  hace parte del registro que se modifica   colocando a uno.
saludos


----------



## Antcamgar (Jun 12, 2010)

Hola a todos, me acabo de registrar  y me gustaría preguntarles a los que llevan mas tiempo en este foro si habéis tratado con ASM de algún microprocesador (aun que este tema sea de microcontroladores pero creo que es lo mas parecido que e encontrado por aquí, a lo mejor no he buscado suficiente) y cómo programarlos ,  o si sabéis de la existencia de otro foro en la web que encaje mas con lo que digo. 
Mi objetivo es saber la utilidad que tiene programar en ensamblador y como hacerlo sin ningún software que sea un emulador del µP(alguna utilidad más que aplicaciones matemáticas y código ASCII) con respecto a periféricos de la misma familia del microprocesador. Y donde probarlo ¿en una placa de trabajo compatible con el microprocesador, en una placa madre...? 
Un saludo a todos.


----------



## Biker12 (Jun 12, 2010)

Hola Antcamgar,

Yo he trabajado un poco con ASM x86, para programar en este no necesitas nada más que tu propio computador (siempre y cuando sea compatible IBM) y algun compilador y debbuger como el TASM. De esta forma puedes llegar a usar todos los recursos que tiene tu computador (creo que esta es la mejor placa de prueba que hay je je je). Con respecto a su utilidad... yo se la encuentro más a programación de microcontroladores ya que ahí los recursos son muy limitados y en algunas ocasiones críticas es necesario aprovecharlos al máximo. 

Si lo que quieres es aprender a programar y en específico ASM x86 te recomiendo este libro: *IBM* _*PC Assembly Language and Programming*_ de Peter Abel.

Saludos,

Biker12


----------



## Daniel B17 (Ago 22, 2010)

Bueno despues de un tiempo ausente voy a subir una pequeña introducción del temas y yo creo que en uno o dias empezare a subir el primer bloque de instrucciones. Mi idea es dividir este documento en 3 partes: registros especiales, las instrucciones y ejemplos, y cada vez ire actualizando cada una de estas partes para que quede completo y despues pasar a los DSPIC's.

PD. Para cualquier duda estoy a su disposicion...

Saludos


----------



## crosshad (Oct 1, 2010)

ayuda con el comanda incf
al pulsar una entrada, en la salida debe incrementarse y prender un led de forma secuensial.


Rotación con intervalos de retardo de 1 segundo.


----------



## osac99 (Oct 1, 2010)

Y cuantos led tienes que prender???. Si vas a usar una secuencia es mejor realizar con la rotacion por el carry (rlcf)


----------



## crosshad (Oct 1, 2010)

mira tengo 5 pulsadores cada 1 activa una secuencia de 8 led (rb0-rb7) 
ra0 no hace nada
ra1 activa la secuencia 10000000 ese valor tiene que pasar de forma secuencia por las otras salidas (rb0-rb7)
ra2 con la secuencia 11001100 y hace lo mismo incrementa el valor de la misma forma
y asi con cada secuencia activada por cada pulsador.
aaaaaa.al presionar 2 pulsadores no ocurre nada xd avance esto
List P=16F84A
Include <P16F84A.INC>
inicio
bsf STATUS,RP0
clrf TRISB
movlw b'11111111'
movwf TRISA
bcf STATUS,RP0

Principal

 btfsc PORTA,0
 call uno
 btfsc PORTA,1
 call dos
 btfsc PORTA,2
 call tres
 btfsc PORTA,3
 call cuatro
 btfsc PORTA,4
 call encender_5
 call resetear

uno
 btfsc PORTA,1
 call resetear
 btfsc PORTA,2
 call resetear
 btfsc PORTA,3
 call resetear
 btfsc PORTA,4
 call resetear
 call encender_1

dos

 btfsc PORTA,2
 call resetear
 btfsc PORTA,3
 call resetear
 btfsc PORTA,4
 call resetear
 call encender_2
tres

 btfsc PORTA,3
 call resetear
 btfsc PORTA,4
 call resetear
 call encender_3
cuatro

 btfsc PORTA,4
 call resetear
 call encender_4

con esto activo cada entrada sin dejar funcionar las demas falta el programa con las salidas incrementadas ayuda no entiendoel comando xd


----------



## 0rland0 (Oct 1, 2010)

crosshad dijo:


> mira tengo 5 pulsadores cada 1 activa una secuencia de 8 led (rb0-rb7)
> ra0 no hace nada
> ra1 activa la secuencia 10000000 ese valor tiene que pasar de forma secuencia por las otras salidas (rb0-rb7)
> ra2 con la secuencia 11001100 y hace lo mismo incrementa el valor de la misma forma
> ...



Hola, no entendi que quieres hacer con el programa, pero si vas a usar el puerto A tienes que configurarlo digital antes de usarlo, ya que ese puerto se puede usar como analogico o digital. Y lo otro es, que hace resetear? y donde esta esa etiqueta?


----------



## perru (Oct 9, 2010)

Hola, soy nuevo en este fantastico foro, por el que os tengo que felicitar a todos los que lo haceis posible. Descubri por casualidad el mundillo de los PICs y me fascino, estoy intentando aprender por mi cuenta pero me esta costando un triunfo, y escribo este mensaje para ver si alguien me puede ayudar.

Para ir poco a poco me he propuesto realizar un programilla sencillo (y aun asi no consigo que funcione), se trata de un 16F874A con tres pulsadores conectados en el puerto A (RA0, RA1 y RA3) y dos leds conectados en el puerto B (RB0 y RB1) y consiste en que al pulsar P1 encienda L1, si pulso P2 encienda L2 y si pulso P3 encienda ambos L1 y L2. EEl codigo es el siguiente, pero no me enciende ningun led, lo he cambiado y recambiado pero no consigo encender ningun led. ¿Que es lo que estoy haciendomal?

LIST P = 16F874A

PORTA	EQU	0X05
PORTB 	EQU	0X06
ESTADO	EQU	0X03
W		EQU 0		

ORG		0

bsf		ESTADO,05		;pongo a 1 el bit 5 de estado (RP0)
bcf		ESTADO,06		;y a 0 RP1 para direccionar al banco 1
movlw	0xff			;meto en W la congiguracion de los pines
movwf  	PORTA		;los establezco en el puerto a como entrada los tres primeros
movlw 	0x00			;meto la configuracion para el puerto b en W
movwf 	PORTB		;pongo todsa como salidas
bcf		ESTADO,05		;vuelvo a poner a 0 RP0 (ahora apunta al banco 0 de la memoria)

inicio	        btfsc	PORTA, 0			;chequea el bit 0 de PORTA, si es 0 salta una instruccion
		movf 	PORTB, 0x01		;mete un 1 en el pin 0 del puerto b (enciende el led)
		btfsc	PORTA, 1			;chequeo el bit 1 de la puerta A
	        movf 	PORTB, 0x02		;si esta pulsado P2 enciende L2
		btfsc	PORTA, 2			;chequeo P3
		movf 	PORTB, 0x03		;si esta pulsado P3 endiende ambos leds
		goto  	inicio 			;bucle infinito
		end


----------



## ByAxel (Oct 9, 2010)

perru dijo:


> Hola, soy nuevo en este fantastico foro, por el que os tengo que felicitar a todos los que lo haceis posible. Descubri por casualidad el mundillo de los PICs y me fascino, estoy intentando aprender por mi cuenta pero me esta costando un triunfo, y escribo este mensaje para ver si alguien me puede ayudar.
> 
> Para ir poco a poco me he propuesto realizar un programilla sencillo (y aun asi no consigo que funcione), se trata de un 16F874A con tres pulsadores conectados en el puerto A (RA0, RA1 y RA3) y dos leds conectados en el puerto B (RB0 y RB1) y consiste en que al pulsar P1 encienda L1, si pulso P2 encienda L2 y si pulso P3 encienda ambos L1 y L2. EEl codigo es el siguiente, pero no me enciende ningun led, lo he cambiado y recambiado pero no consigo encender ningun led. ¿Que es lo que estoy haciendomal?
> 
> ...



Bienvenido:
Te equivocas a usar la instrucción *movf*; lo que hace es mover o copiar el contenido del registro a dos posibles lugares que son 'W' (al acumulador o registro de trabajo) o al mismo registro. *MOVF* solo permite el valor '0' y '1' que son 'W' y el mismo registro respectivamente; al poner otros valores mayores de '1', compila bien pero al ser mayor lo considera como '1'.

Para escribir en los registros usa:

```
movlw   0x03
movwf   PORTB
...
...
bsf       PORTB,0
...
bcf       PORTB,0
```

Luego, los registros donde se establece entradas/salidas de un puerto son en los TRISx (TRISA, TRISB, etc.) que se encuentran en el Banco 1 que empieza en la dirección 0x80. Entonces:

-Para establecer I/O de pines en este PIC usa.
TRISA equ 0x85
TRISB equ 0x86
- Para leer o escribir en los puertos del PIC usa.
PORTA equ 0x05
PORTB equ 0x06

PD: Para que no estés declarando cada registro, usa la directiva *#INCLUDE <P16F874A.INC>* debajo de *LIST*.

saludos.


----------



## perru (Oct 9, 2010)

Gracias x contestar ByAxel, creo que me estoy liando yo solo con el chequeo de los pines de PORTA y con la escritura de los de PORTB. He realizado las siguientes modificaciones, me lo podrias corregir como al ignorante que soy? 

LIST P = 16F874A

#INCLUDE <P16F874A.INC>

TRISA 	EQU 0x85
TRISB 	EQU 0x86
PORTA	EQU	0X05
PORTB 	EQU	0X06
ESTADO	EQU	0X03
W		EQU 0		

ORG		0

bsf		ESTADO,05		;pongo a 1 el bit 5 de estado (RP0)
bcf		ESTADO,06		;y a 0 RP1 para direccionar al banco 1
movlw	0xff			;meto en W la congiguracion de los pines
movwf  	TRISA		;los establezco en el puerto a como entrada los tres primeros
movlw 	0x00			;meto la configuracion para el puerto b en W
movwf 	TRISB			;pongo todsa como salidas
bcf		ESTADO,05		;vuelvo a poner a 0 RP0 (ahora apunta al banco 0 de la memoria)

inicio	        movlw	0x01
		btfsc	        PORTA,00 			;chequea el bit 0 de PORTA, si es 0 salta una instruccion
		movwf 	PORTB
		movlw	0x02				;mete un 1 en el pin 0 del puerto b (enciende el led)
		btfsc	        PORTA,01			;chequeo el bit 1 de la puerta A
	        movwf 	PORTB			;si esta pulsado P2 enciende L2
		movlw	0x03
		btfsc	        PORTA,02 			;chequeo P3
		movwf 	PORTB			;si esta pulsado P3 endiende ambos leds
		goto  	inicio 				;bucle infinito
		end


----------



## mauryras (Oct 9, 2010)

HOLA A TODOS..no era este el lugar pero nose como abrir un tema tengo un problema quiero hacer un control de temp con un lm 35 le pongo un amplificador y a los 50 grados me entrega 3 volt eso lo meto a la entrada A0 del conversor del PIC 16f88 pero nunca lo encuentra a ese valor osea queria prender un led para probar cuando llege a ese valor mi duda es si estoy configurando mal el pic o que este es el codigo:

int16 a;
int16 b;

void main()
{
b=0x0265;
   setup_adc_ports(sAN0|VSS_VDD);
   setup_adc(ADC_CLOCK_DIV_2);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_ccp1(CCP_OFF);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
set_tris_a(1);
set_tris_b(0);
set_adc_channel(0);
a=read_adc();
output_b(0);
if(a>=b){
while(true){
output_b(1);
delay_ms(2000);
}

}
Soy principiante en esto ... uso PICC eso me olvide de decir por favor necesito ayuda gracias a todos ..y perdon si me fui de tema lei lo de ASM y esta muy interesante..


----------



## ByAxel (Oct 9, 2010)

En caso de usar el *#INCLUDE <P16F874A.INC>*, ya no necesitas declarar los registros (PORTx, TRISx, etc...) ya que todos se encuentran en el archivo *P16F874A.INC*, si quieres revisarlo por lo general se encuentra en la carpeta de microchip *"C:\Program Files\Microchip\MPASM Suite"* incluidos los Fuses que son importantes y que no los veo en tu ejemplo.


```
LIST P = 16F874A

#INCLUDE <P16F874A.INC>
        __CONFIG _CP_OFF & _DEBUG_OFF & _WDT_OFF & _LVP_OFF & _XT_OSC 
                        ; Config es para configurar los fuses y el resto lo encuentras al último del archivo
                        ; P16F874A.INC. Para saber que son debes de revisar el Datasheet del PIC en
                        ; "CONFIGURATION WORD" donde explica que hace cada uno.
```

Ya no es necesario que declares *ESTADO	EQU	0X03*, puedes o no usarlo ya que dentro del archivo *P16F874A.INC*, se le conoce como *STATUS* y de esa forma lo encontraras en todo sitio al igual que *W  EQU 0* que también está declarado en el archivo.

Todo eso no genera código, es para uso exclusivo del compilador... ahora todo lo considerado como instrucción que escribas entre *ORG 0x00* y *END*, eso si genera código.

El resto esta bien, solo una duda... una vez enciendas uno o todos los LEDs ¿Como los apagas? ya que aria falta un grupo de código que mantenga apagados los LEDs mientras las entradas sean cero (0) 

saludos.


----------



## perru (Oct 9, 2010)

No fastidies que va a ser ese el problema!!!!!!! me faltan las instrucciones para que se apaguen? A ver la idea es un circuitillo con tres pulsadores en PORTA y dos leds en PORTB, los leds quiero que esten siempre apagadoe excepto kuando accione los pulsadores (L1 enciende cuando pulso P1, L2 cuando pulso P2 y ambos leds cuando pulso P3) igual es una idea feliz mia, pero he dado x sentado que si los pulsadores estan abiertos los leds estaran apagados, el pic no funciona igual que si haria el montaje analogico?, es decir estimulando los pulsadores en PORTA no meto y quito corriente en los pines de PORTB? Menudo cacao tengo montao!!!!!!!


----------



## ByAxel (Oct 9, 2010)

Eso de...





perru dijo:


> quiero que esten siempre apagadoe excepto kuando accione los pulsadores


 es lo que hace falta...
Los pulsadores en PORTA deben de tener una resistencia conectado a GND (Pull-down), así el PIC leerá cero (0) y los pulsadores conectados a +V, cuando pulses, el PIC leerá uno (1)... lo que hace falta es el código que apague el/los LEDs cuando el pulsador respectivo se encuentre abierto...

Algo así para solo una entrada...

```
Loop:
  btfsc   PORTA,0
  bsf     PORTB,0
  btfss   PORTA,0
  bcf     PORTB,0
  goto    Loop
```

Se me olvidaba... ese PIC tiene un ADC (convierte de análogo a digital) ubicado en el PORTA, pero debes de desactivar ese módulo ya que el PORTA los usas como entrada *digital*, para eso pones en el banco 1:

movlw  0x07        ; PORTA como I/O digitales.
movwf  ADCON1

saludos


----------



## perru (Oct 10, 2010)

Joer ByAxel, no sabes lo ke te agradezco la ayuda, eres la primera persona ke enkuentro dispuesta a enseñarme. He cambiao lo ke me has komentao y ke no hay manera, en la simulacion no vario los pines de PORTB, las resistencias ya las tenia puestas lo ke pasa ke en la linea power, las he cambiao pero nada. No sera ke tengo algo mal en la simulacion??? me extraña x ke es un circuito muy simple pero visto lo visto no descarto nada. Igual me he puesto un ejercicio demasiado komplejo para no tener ni idea . Ahora mismo lo tengo asi:

LIST P = 16F874A

#INCLUDE <P16F874A.INC>

ORG		0

bsf		ESTADO,05		;pongo a 1 el bit 5 de estado (RP0)
bcf		ESTADO,06		;y a 0 RP1 para direccionar al banco 1
movlw	0xff			;meto en W la congiguracion de los pines
movwf  	TRISA			;los establezco en el puerto a como entrada los tres primeros
movlw 	0x00			;meto la configuracion para el puerto b en W
movwf 	TRISB			;pongo todsa como salidas
bcf		ESTADO,05		;vuelvo a poner a 0 RP0 (ahora apunta al banco 0 de la memoria)
movlw 	0x07 			; PORTA como I/O digitales.
movwf 	ADCON1

inicio	btfsc	PORTA,0 		;chequea el bit 0 de PORTA, si es 0 salta una instruccion
		bsf 	PORTB,0			;mete un 1 en el pin 0 del puerto b (enciende el led)
		btfss	PORTA,0			
		bcf		PORTB,0
		btfsc   PORTA,1			;chequeo el bit 1 de la puerta A
  		bsf     PORTB,1			;si esta pulsado P2 enciende L2
  		btfss   PORTA,1
  		bcf     PORTB,1
		btfsc	PORTA,2			;chequeo P3			
	    bsf     PORTB,2			;si esta pulsado P3 enciende ambos leds
 		btfss   PORTA,2
  		bcf     PORTB,2				 	
		goto  	inicio 			;bucle infinito
		end


----------



## ByAxel (Oct 10, 2010)

He comentado la mayoría:

```
ORG	0x00
	bsf	 STATUS,5	 	; Banco 1
	bcf	 STATUS,6	 	;  
	movlw 	0x07 			; PORTA como I/O digitales.
	movwf 	ADCON1
	movlw	0xFF	 		; meto en W la congiguracion de los pines
	movwf 	TRISA	 		; >> Con 0xFF estableces todo el puerto como ENTRADAS
	movlw 	0x00	 		; meto la configuracion para el puerto b en W
	movwf 	TRISB	 		; pongo todsa como salidas
	bcf	 STATUS,5	 	; Banco 0

BucleA
	clrf	PORTB			; borra todo el PORTB (mantiene apagados los LEDs)
BucleB	
	btfsc	PORTA,0 		; Chequea RA0, ¿Es cero? => salta una instrucción
	bsf 	PORTB,0	 		; de lo contrario pone a '1' el RB0 (LED1)
	btfss	PORTA,0			; Chequea RA0, ¿Es uno? => salta una instrucción
	bcf	 PORTB,0			; de lo contrario pona '0' el RB0
	btfsc 	PORTA,1	 		; ... RA1?
	bsf 	PORTB,1	 		; pone a '1' el RB1	(LED2)
	btfss 	PORTA,1			; ... RA1?
	bcf 	PORTB,1			; pone a '0' el RB1
	
	btfss	PORTA,2			; Chequea RA2, ¿Es uno? => salta una instrucción
	goto	BucleA			; de lo contrario salta a Bucle externo donde...
	bsf	PORTB,0			; pone a '1' RB0 y RB1 (LED1 y LED2)
	bsf	PORTB,1
	goto	BucleB			; Continua con el bucle interno...
	
	END
```

Recuerda que si incluyes el archivo *P16F874A.INC* ya no es necesario declarar ningún registro, es por eso que yo uso *STATUS*... y el registro *ADCON1* está en el banco 1.
El MPLAB revisa sintaxis, mayúsculas, minúsculas, declaraciones, instrucciones y en base a eso genera los mensajes/errores, por lo que no te va a avisar si alguna secuencia la haces mal, aunque todo esté bien escrito y el PIC no funcione, es un indicio que algo se ha configurado mal... (error humano  ).

La parte de encender ambos LEDs lo he resuelto de esa forma, pero hay muchas más... cada programador tiene su estilo... poco a poco usa las 35 instrucciones y veras que haces maravillas 

saludos.


----------



## perru (Oct 10, 2010)

Definitivamente tiene ke ser la simulacion!!!! No hay modificaciones en el PORTB cuando pulso, de hecho no se modifica nada en el watch window y para mayor desesperacion mia veo que el estado de las lineas de los pulsadores si cambian (estan con el cuadradito en rojo y cuando pulso cambia a azul pero no cambia ningun bit de ningun registro). Muchas gracias x animarme pero si me estoy volviendo loko ya kon los pulsadores estos no te digo nada para llegar a realizar el proyecto ke tengo en mente...uffff. En la simulacion tengo: los tres pulsadores a tierra x un extremo y por el otro a +5v con una resistencia de 10k intercaladas a cada uno y estos conectados respectivamente a PORTA y a la salida de PORTB los leds con una res de 680. Madre mia!!! kon lo facil ke me las prometia y lo ignorante ke me estoy sintiendo.


----------



## ByAxel (Oct 10, 2010)

Supongo, ya lo simule (Stimulus RA0, RA1, RA2 + WatchWindow + Simulación en StepInto) y si corre... hay varias cosas a tomar en cuenta que obligado harán que el código crezca pero para ser como prueba funciona.


----------



## perru (Oct 10, 2010)

le puedes echar un vistazo?


----------



## ByAxel (Oct 10, 2010)

En el programa checas a que una entrada esté a '1' (pulsador cerrado) para encender el LED respectivo pero en el esquema hace lo contrario... cambia las resistencias a GND y los pulsadores a +V.
Las resistencias para los LED (solo en la simulación) ponlo a 100 Ohm para que se vea el brillo y hace falta poner a V+ la pata del MCLR.
- Te dejo el code que use..., ahora queda mejorar la parte donde encienden dos LED a la vez..

saludos


----------



## perru (Oct 10, 2010)

Ke grande eres!!!!!!!, ya funciona, pero lo has conseguido tu . Lo ke si al kompilarlo me he fijado ke no te kaska ningun warning y a mi me saltaban 6 o asi, ¿eso es x la configuracion de la cabecera? supongo ke sera x lo de errorlevel -302. Se lo ke son algunas instrucciones (_XT_OSC y _WDT_OFF) pero eske eso a mi me keda muy lejos todabia, veras estoy siguiendo un libro y en el principio (donde estoy) no mete esas cabeceras de momento, lo mismo ke el include, me suenan de haber leido codigo x ahi pero hasta ke llegue... kon la ke he liao pa tres pulsadores y dos leds... 

Muchas gracias otra vez, el codigo kuando lo leo, lo sigo mas o menos pero escribirlo ya es otra kosa, sobre todo kuando no tienes kien te diga lo ke estas haciendo mal. ¿te seria mucha molestia mandarme programas para hacer? tengo mucho interes x aprender y poder ir progresando en hacer kosillas mas komplikadas y utiles, pero ke sean basicos gg, a ver si te vas a pasar


----------



## ByAxel (Oct 10, 2010)

Esto: http://www.pic16f84a.org/, es un libro muy usado por el que empieza... es un PIC algo viejo pero sirve bastante la información...
- Siempre ten a la mano el datasheet del PIC que uses.
- Los PIC16F son en su mayoría compatibles por lo que "Si aprendes a programar uno, aprendes a programar todos" los de la serie 16F.
- Las diferencias están es sus registros, fuses, por eso "Siempre revisa el datasheet".
- Introduccion a la programacion de PIC's (assembler).

Suerte.
saludos...


----------



## perru (Oct 10, 2010)

Me referia a la hora de korregir, toda esa informacion la tengo y es realmente util, pero en kuanto me salgo un poko del guion y me atasko... El codigo kon el ke habia empezado estaba bien estructurao, kitando el mal uso del movf, pero si tu no me lo llegas a komentar no hubiera salido de ese bucle para mi estaba bien y segun el libro tb.


----------



## Alerce (Oct 19, 2010)

hay una duda que tengo con respecto a como se realizan los salto indexados cuando se tiene una tabla que sobrepase las 256 pociciones.¿Como se trabaja en esos casos los registros PCLATCH,PChigh y PClow para acceder a la posicion de la tabla deseada?
podrian realizar un ejemplo al respecto.

Felicitaciones a Leon Elec y Meta por este curso.

saludos.


----------



## Meta (Oct 19, 2010)

Puedes encontrar la respuesta por aquí.

http://www.pic16f84a.org/index.php?...gina-de-256-bytes&catid=38:pic16f84&Itemid=60

Saludo.


----------



## Alerce (Oct 20, 2010)

Gracias Meta lo estudiare...


----------



## Lord Chango (Nov 19, 2010)

Hola a todos! Antes que nada quiero agradecer a quienes están haciendo este tutorial, ya que es extremadamente útil para quienes sabemos muy poco sobre assembler.

Vine para resaltar lo que creo es un error, y cito:



Leon Elec dijo:


> El CP *incrementará* en 1 el registro CONTADOR y evalúa el valor, si no es cero, ejecuta línea siguiente que es GOTO VOLVER, el cual se dirige de nuevo a la línea VOLVER   INCFSZ   CONTADOR el cual volverá a incrementar en 1 y evalua el valor, si es cero salta la línea GOTO VOLVER y ejecuta la instrucción INCF  PORTA. Esta última línea, el programador pondrá la instrucción que necesite ejecutar...



en la página 28 del pdf, lo resaltado en negrita figura como "*descontará*", lo que produce confusión. Yo sé que es obvio que si la función se llama "Increment" es que suma, pero mas vale prevenir  que curar.

Saludos!


----------



## Psyco83 (Dic 5, 2010)

Hola a todos, excelente el tuto... una consulta, la instrucción *movf registro,w *al mover el valor desde *registro* a *w*, borra lo que hay en *registro*?


----------



## ByAxel (Dic 5, 2010)

Psyco83 dijo:


> Hola a todos, excelente el tuto... una consulta, la instrucción *movf registro,w *al mover el valor desde *registro* a *w*, borra lo que hay en *registro*?


No.
La única instrucción que borra o pone a cero un registro es *clrf*, el resto solo hace una copia.
saludos


----------



## Psyco83 (Dic 5, 2010)

Gracias compañero.... justamente ya hallé la falla en mi programa en *asm* que hacía que me visualizara *0* en una LCD al leer el *registro*, escribí *movf  registro *y eso mueve el dato sobre si mismo y no a w que es el que uso para enviar el dato a la LCD, la forma correcta es *movf registro,w*.

Gracias y Saludos


----------



## Meta (Dic 6, 2010)

Psyco83 dijo:


> Hola a todos, excelente el tuto... una consulta, la instrucción *movf registro,w *al mover el valor desde *registro* a *w*, borra lo que hay en *registro*?



En los PIC16F el que borra registro es clrf y el que borra registro de trabajo W es el clrw.

En los PIC18F tiene el clrf pero no se incluye el clrw, para borrar el W directamente hay que cargar un valor cero como movlw 0, movwf registro.


----------



## Lord Chango (Feb 12, 2011)

Hola a todos! Aprovecho este tema para postear una duda, estoy haciendo  un conversor de binario a decimal separados por digitos, es decir, de un  numero binario obtengo unidades, decenas y centenas, pero no puedo hacerla funcionar.
Esto es lo que tengo ahora:



			
				Lord Chango dijo:
			
		

> MOVF        VF,W                ;CENTENA
> SUBLW        .100
> BTFSS        STATUS,C
> GOTO        $+4                    ;-->DECENA
> ...




VF es el valor a convertir; FI, FX y FC son las unidades, decenas y centenas respectivamente.
Esta mal el planteamiento? Porque no hay forma de hacerlo funcionar en el MPLAB.

Desde ya gracias!


----------



## dukex (Feb 15, 2011)

Lord Chango dijo:


> Hola a todos! Aprovecho este tema para postear una duda, estoy haciendo  un conversor de binario a decimal separados por digitos, es decir, de un  numero binario obtengo unidades, decenas y centenas, pero no puedo hacerla funcionar.
> Esto es lo que tengo ahora:
> 
> 
> ...



Hola, hace tiempo que no trabajo con PIC's por que me pasé a ATMEL pero creo que esta me la sé....el problema parece estar en las instrucciones:

SUBLW XXX 
BTFSS STATUS,C

ya que tu quieres que vaya quitando bloques decimales desde el mayor al menor "CENTENAS, DECENAS,UNIDADES" Restandole "100,10,1" respectivamente para así sumar cuantos se dejó restar de cada uno. Ahora bien, no sé en cual de las intrucciones tuviste el error, si es que no te acuerdas que el bit C de STATUS tiene polaridad inversa cuando se útiliza SUBLW ó  no sabes que SUBLW resta a W del literal(k - W --> W) y no al contrario como parece que pensaste(estas son las pistas claves).

NO VOY A DARTE EL CODIGO... pero aquí va algo para tener en cuenta:

trata de utilizar un registro intermedio para almacenar el valor de W en el cual estás trabajando, para luego almacenar en W 100,10 ó 1 según se el caso y posteriormente restar con SUBWF ese valor 100,10 ó 1 del registro.

quedando algo como:

MOVF VF,W 
MOVWF R1 ;
MOVLW XX ;"100,10 ó 1"
SUBFW R1,F 
BTFSS STATUS,C
GOTO XXX ;-->"DECENA, CENTENA ó UNIDAD"
MOVF R1
MOVWF VF
INCF XX,F ;INCREMENTA DIGITO "DECENA, CENTENA ó UNIDAD"
GOTO XX ;-->"DECENA, CENTENA ó UNIDAD


Ya con esto puedes seguir,  debes tener en cuenta de pasar el registro en el que esas trabajando R1 por que si no VF  no cambiará......  hay otra manera mas eficiente utilizando directamente VF en vez de R1 espero  te dés cuenta, mucha suerte ....




Saludos.


----------



## Lord Chango (Feb 15, 2011)

Hola dukex, gracias por contestar!

Si se que C=0 cuando el resultado de la resta es menor que cero, pero  parece que me pase por alto que SUBLW K --> W= K - W... Ya me pongo a  cambiar esto




dukex dijo:


> NO VOY A DARTE EL CODIGO... pero aquí va algo para tener en cuenta


Nunca pedi el codigo, por eso publique el mio... Todavia tengo ganas de renegar por mi cuenta... Por ahora...

Gracias y saludos!


----------



## OZONO (Feb 24, 2011)

Buenas tardes compañeros:
Me encuentro hoy con un dilema bastante raro. 
Quisiera descargar los contenidos publicados por el compañero "elosciloscopio" en la página 6 de éste tema, pero hete aqui el problema, dicha página es la única que no me deja ingresar como usuario. Tras reiterados intentos desistí de intentar loguearme.

El problema (utilizando el imaginativo) pueda deberse a que dicho contenido se descargó 555 veces. Un número bastante singular en el ambiente electrónico (vaya coincidencia si lo es). Por eso apelo a el archivo de ustedes compañeros para volver a colgar estos contenidos.

Muchas Gracias
Saludos. Mauricio

La respuesta a este dilema es otro dilema, pero ésta vez funcional.
la solución fué incrementarle en un valor a la página de la descarga que redirecciona el link. (tal vez estoy divulgando información acerca del algoritmo de comprobación de identidad utilizado por los programadores de la página, pero ayudará a otros usuarios a resolver el mismo problema).

Saludos.
Mauricio


----------



## principiantetardio (May 10, 2011)

Hola a todos, ando un poco perdido con este temita, hice una rutina para contar cada vez que pulso un boton y funciona de maravillas solo que llega a 255 y luego pasa a cero, no doy con la idea para hacer que cuente indefinidamente.
Pongo el tramo de programa que me esta maltratando a ver si entre tantas mentes hay alguna que me tire una idea.


btfss		porta,5		;;controlo si el porta se puso alto
goto		$-1		;;si es no vuelvo atras hasta que pulse
btfsc		porta,5		;;si pulso vino aqui y controlo que se ponga bajo
goto		$-1		;;si es no vuelvo atras hasta que suelte
incf		CONTADOR1	;"si solto incremento el contador 
call		conversion1	;;llamo a "conversion1" 
call		lectura		;;y llamo a lectura hasta que termine de leer
goto		$-4		;"vuelvo a ver si se pone en alto otra vez para aumentar
				;"el contador

Con la rutina lectura logro ver el conteo en un display.


Gracias


----------



## 0rland0 (May 10, 2011)

principiantetardio dijo:


> Hola a todos, ando un poco perdido con este temita, hice una rutina para contar cada vez que pulso un boton y funciona de maravillas solo que llega a 255 y luego pasa a cero, no doy con la idea para hacer que cuente indefinidamente.
> Pongo el tramo de programa que me esta maltratando a ver si entre tantas mentes hay alguna que me tire una idea.
> 
> 
> ...



Hola, bueno me parece raro que te funcione por ese ultimo goto, porque cuando termine con la funcion lectura retrocedera 4 direcciones de memoria y queda en un goto $-1 (donde verificas que soltaron la tecla). La funcion conversion supongo que es para convertir a BCD o 7seg, si es asi, solo tienes que aumentar el numero de digitos a convertir modificando tu funcion conversion. No puedes hacer que cuente hasta infinito porque no tienes memoria infinita, podrias usar toda la memoria del pic para tener un contador bastante grande. 

Yo le pondria etiquetas al programa

CICLO
btfss		porta,5		;;controlo si el porta se puso alto
goto		CICLO		;;si es no vuelvo atras hasta que pulse
Revisar_PULSADOR
btfsc		porta,5		;;si pulso vino aqui y controlo que se ponga bajo
goto		Revisar_PULSADOR		;;si es no vuelvo atras hasta que suelte
incf		CONTADOR1	;"si solto incremento el contador 
call		conversion1	 ;;llamo a "conversion1" 
call		lectura		;;y llamo a lectura hasta que termine de leer
goto		*CICLO*		;"vuelvo a ver si se pone en alto otra vez para aumentar
				;"el contador

Por cierto, que hace la funcion lectura?


----------



## Chico3001 (May 10, 2011)

principiantetardio dijo:


> Hola a todos, ando un poco perdido con este temita, hice una rutina para contar cada vez que pulso un boton y funciona de maravillas solo que llega a 255 y luego pasa a cero



 no... pues no puedes resolver tu problema... nunca vas a poder hacer que el PIC cuente mas de 255 ya que es un procesador de 8 bits... y el numero mas grande que puedes representar en 8 bits es 255, para poder guardar valores mas grandes necesitas hacerlo en varios registros, con 2 tienes 16 bits y [LATEX]2^1^6[/LATEX]=65535, usa esta rutina como ejemplo:


```
incf contador_l,f;
btfsc status,z;
incf contador_h_f;
```


----------



## principiantetardio (May 10, 2011)

0rland0 dijo:


> Hola, bueno me parece raro que te funcione por ese ultimo goto, porque cuando termine con la funcion lectura retrocedera 4 direcciones de memoria y queda en un goto $-1 (donde verificas que soltaron la tecla).
> 
> Disculpas,  puse una de las tantas pruebas, la real lo hace volver 6 direcciones y funciona bien
> 
> ...



 Justamente lee las Centenas, Decenas y Unidades y luego de haberlas transformado en ASCII  las muestra en el LCD 2x16.



Chico3001 dijo:


> no... pues no puedes resolver tu problema... nunca vas a poder hacer que el PIC cuente mas de 255 ya que es un procesador de 8 bits... y el numero mas grande que puedes representar en 8 bits es 255, para poder guardar valores mas grandes necesitas hacerlo en varios registros, con 2 tienes 16 bits y [LATEX]2^1^6[/LATEX]=65535, usa esta rutina como ejemplo:
> 
> 
> ```
> ...



Hola, me oriento mas por tu respuesta, gracias  

contador_h_f  seria el segundo contador?

deberia ampliar la rutina de conversion1 para las unidades de mil y las decenas de mil?

no me doy cuenta de como implementar ambos contadores, deberia leer 16 bits y no se como, ya me supera 

Para mas info te copio la rutina de conversion que es de lo mas simple



Gracias por el aporte


----------



## Chico3001 (May 11, 2011)

principiantetardio dijo:


> contador_h_f  seria el segundo contador?
> 
> deberia ampliar la rutina de conversion1 para las unidades de mil y las decenas de mil?
> 
> no me doy cuenta de como implementar ambos contadores, deberia leer 16 bits y no se como, ya me supera



error de dedo.... en vez de ser contador_h_f debe ser contador_h,f y si... contiene la parte alta del conteo

Y efectivamente, hay que actualizar todas las rutinas para que puedan manejar el nuevo contador de 16 bits, incluyendo la de conversion de binario a BCD, en este momento no tengo una a la mano pero las hay por todos lados... solo hay que encontrar una y pegarla en el codigo

Recuerda que con 16 bits vas a poder contar hasta 65535, si necesitas un numero mas alto hay que seguir añadiendo variables y las rutinas van a ser cada vez mas complejas.... es el precio a pagar por usar 8 bits..


----------



## principiantetardio (May 11, 2011)

Chico3001 dijo:


> error de dedo.... en vez de ser contador_h_f debe ser contador_h,f y si... contiene la parte alta del conteo
> 
> Recuerda que con 16 bits vas a poder contar hasta 65535, si necesitas un numero mas alto hay que seguir añadiendo variables y las rutinas van a ser cada vez mas complejas.... es el precio a pagar por usar 8 bits..






Bien vere como trabajar con esto de la parte alta y la baja, no lo tengo claro.
Se que serian como "0000 0000" para contador_h y "0000 0000 " para contador_l y que estarian en una sola palabra de "0000 0000 0000 0000" pero no se como unirlos, de alli en mas es sencillo para mi, pero es como la fiesta del otro lado del rio y no se nadar :enfadado:

Ja, por ahora estoy pagando con horas de sueño  , espero que me salga sino las ojeras me van a llegar a la cintura  

Gracias


         

Lo logre, y hasta lo entendi por eso los aplausos son para mi  me los merezco despues de lo que me costo Y ERA TAN SIMPLE.

Bueno GRACIAS A TODOS por sus aportes y guias


----------



## timh (Sep 27, 2011)

Bueno pues mi duda es como dice el título valga la redundancia, ¿como puedo implementar estas estructuras de control en mpasm para utilizarlas en un pic?, la verdad es que apenas empiezo con eso de los pics y pues la verdad todavía no se me da mucho eso del ensamblador y pues les agradeceria una ayudadita si no es mucha molestia.

Bueno pues saludos y que tengan buen día.


----------



## Chico3001 (Sep 28, 2011)

tienes que implementarlas con operaciones logicas.... de tal manera que el resultado de cero

por ejemplo, un 

if (reg1=5)


```
movlw	H'05'
	xorfw	reg1,w
	btfss	status,z
	goto	false
	goto	true
```

for (reg1=0; reg1<10; reg1++)


```
clrf	reg1		;reg1=0
ciclo:	....
	....
	....
	incf	reg1		;reg1++
	movlw	H'10'		;reg1<10?
	subwf	reg1,w		;
	btfss	status,z	;
	goto	ciclo
```


----------



## Meta (Abr 15, 2012)

Hola:

Hay códigos que veo con un ~.

Ejemplo:

```
movlw low ~.12500
movwf TMR1L
movlw high ~.12500
movwf TMR1H
```

¿Qué significa el ~?

Buscando aquí al final, te dice complemento y se queda ahí. No me dice nada.
http://www.terra.es/personal/fremiro/directivas.htm

Saludo.


----------



## Gustavo.gmb (Nov 29, 2013)

bueno es una pregunta sencilla

se puede utilizar esta instruccion de esta manera?


```
btfss        Pulsador,0
```

Pulsador es una variable, y queria comprobar si es 1 el primer bit de esa variable, o es de otra manera?


----------



## Dano (Nov 29, 2013)

gusfavio dijo:


> bueno es una pregunta sencilla
> 
> se puede utilizar esta instruccion de esta manera?
> 
> ...



https://www.forosdeelectronica.com/f24/instrucciones-btfss-btfsc-funcionan-44894/


----------



## emz102009 (Sep 23, 2014)

Hola. ¿Me podrían explicar en cada línea para que sirve cada parte del código?


```
list P=16f628A
   include <P16f628A.INC>
   
   __config _CP_OFF& _WDT_OFF& _PWRTE_ON& _MCLRE_ON& _LVP_OFF& _INTOSC_OSC_NOCLKOUT ; desavilito perro guardion pin 5 no se puede usar, habilito puerto B(lvp_of),habiita el oscilador interno y no el de salida (_intosc_osc_noclkouñ
     
   org 0
       goto CONFIG_PUERTOS

   org 5

CONFIG_PUERTOS

            


       clrF PORTA 
       clrf PORTB

       bcf STATUS,RP1
       bsf STATUS,RP0

       movlw 0x07
       movwf CMCON
       
       
       movlw b\'11111111\'
       movwf TRISA
       
       movlw 0x00 ;clrf trisb
       movwf TRISB
  
       bcf STATUS,RP1
       bcf STATUS,RP0
   
PRINSIPAL 
    
       movf PORTA,W 
       movwf PORTB 
    
     goto PRINSIPAL

     end
```


----------



## Miembro eliminado 356005 (Sep 23, 2014)

```
list P=16f628A              ; informa al ensamblador a qué PIC nos dirigimos
    include <P16f628A.IN>       ; incluye las constantes de ese PIC para usarlas más adelante

                                ; ajusta los fusibles de funcionamiento general
    __config _CP_OFF & _WDT_OFF & _PWRTE_ON & _MCLRE_ON & _LVP_OFF & _INTOSC_OSC_NOCLKOUT

    org     0                   ; origen del programa

    goto    CONFIG_PUERTOS      ; saltar a configurar los puertos

    org     5

CONFIG_PUERTOS:                 ; aquí empieza lo interesante

    clrf    PORTA               ; inicialiazamos el valor de los puertos A y B
    clrf    PORTB

    bcf     STATUS,RP1          ; cambiamos al banco 1
    bsf     STATUS,RP0

    movlw   b'0111'             ; le ponemos 7 al CMCON: desactivamos el modo de comparación en puerto A
    movwf   CMCON


    movlw   b'11111111'         ; configuramos el puerto A como entrada
    movwf   TRISA

    movlw   b'00000000'         ; configuramos el puerto B como salida
    movwf   TRISB

    bcf     STATUS,RP1          ; cambiamos al banco 0
    bcf     STATUS,RP0

PRINCIPAL:                      ; inicio del bucle

    movf    PORTA,W             ; leemos el valor presente en el puerto A
    movwf   PORTB               ; lo escribimos en el puerto B

    goto    PRINCIPAL           ; y repetimos

    end
```


----------



## emz102009 (Sep 25, 2014)

Hola soy nuevo en lenguaje en ensamblador quiero hacer un retardo de tiempo con NOP pero aun no se bien como se utiliza, lo que he encontrado en internet es que necesito esto MOVLW .100 , MOVWF cnt, este es mi planteamiento de programa.


PRINCIPAL

	MOVLW .100
	MOVWF cnt
	BSF LED ;ENCIENDE  
        NOP


	BCF LED ; apaga 
        NOP
	GOTO PRINCIPAL
	END 

según lo que tengo entendido es que el led va a estar encendido al mismo tiempo que va a estar apagado, pero lo simulo en proteus y aveces dura mas encendido que apagado y viceversa, quisiera saber cual es el problema del programa ...


----------



## Daniel Meza (Sep 25, 2014)

Hola, lo que sucede es que el tiempo de ejecución de la instrucción GOTO inserta un tiempo más de ejecución provocando que el led permanezca más tiempo apagado que prendido, para compensarlo pon 2 NOP's más después de BSF LED


----------



## Miembro eliminado 356005 (Sep 25, 2014)

El problema es que la instrucción nop apenas dura un  ciclo de reloj, así que apenas estás poniendo una espera.

Depende de la velocidad del reloj con el que estés alimentando el PIC, que tendrás que poner una cantidad mayor o menor al contador.

Y, lo principal: debes realizar el proceso de descuento del contador: un bucle vacío (con uno o varios nop dentro) que se ocupe solo de decrementar el contador.

La instrucción decfsz (decrementar una posición de memoria y saltar una instrucción si es cero) es la adecuada para estos casos:


```
PRINCIPAL:

    BSF LED ; enciende

    MOVLW .100
    MOVWF cnt
BUCLE1:
    NOP
    DECFSZ cnt
    GOTO BUCLE1

    BCF LED ; apaga

    MOVLW .100
    MOVWF cnt
 BUCLE2:
    NOP
    DECFSZ cnt
    GOTO BUCLE2

    GOTO PRINCIPAL
    END
```
*Edito:* Se me adelantó Meza


----------



## Daniel Meza (Sep 25, 2014)

Bueno,  es cierto, faltó aclarar el uso de los registros contadores, yo me fui más por la parte de que el lazo infinito mantuviera al LED prendido y apagado el mismo lapso de tiempo... si lo que se desea es crear retardos variables por software sigue el consejo de Joaquín... ahora creo recordar que aquí en el foro hace tiempo publicaron un link donde se tenía un calculador automático de valores para cargar en los contadores; incluso el mismo calculador generaba la porción de código correspondiente, será cuestión de buscar


----------



## Miembro eliminado 356005 (Sep 25, 2014)

Retardo básico en un pic

Yo también participé en la discusión,  e hice mi propia versión del generador de Golovchenko (ya que no explicaban cómo lo hacían).

A modo de ejemplo, si tenemos un PIC a 4 Mhz y queremos una espera de 500 ms:

```
$ ./delay_pic.pl 4Mhz 500ms -s
; ----------------------------------------------------------------------------------------------------
; Espera = 500ms
; Frecuencia de reloj = 4Mhz
;
; Espera real = 0.5 segundos = 500000 ciclos
; Error = 0.00 %

        cblock 0x70
                Espera_d1
                Espera_d2
                Espera_d3
        endc

Espera:
                                        ;499994 ciclos
                movlw   0x03
                movwf   Espera_d1
                movlw   0x18
                movwf   Espera_d2
                movlw   0x02
                movwf   Espera_d3

Espera_loop:
                decfsz  Espera_d1, f
                goto    $+2
                decfsz  Espera_d2, f
                goto    $+2
                decfsz  Espera_d3, f
                goto    Espera_loop

                                        ;2 ciclos
                goto    $+1

                                        ;4 ciclos (incluyendo la llamada)
                return

; Generado por delay_pic.pl (Joaquín Ferrero. 2014.07.22)
; ./delay_pic.pl 4Mhz 500ms -s
; jue 25 sep 2014 19:55:09 CEST
; http://perlenespanol.com/foro/generador-de-codigos-de-retardo-para-microcontroladores-pic-t8602.html
; ----------------------------------------------------------------------------------------------------
```


----------



## MrAlphonse (Nov 8, 2014)

Hola a todos.

Estoy realizando un proyecto: un temporizador para una válvula que puede configurarse como N/A o N/C, operación manual o automática, y el control automático puede ser a ciclo continuo o en un número determinado de ciclos, a un máximo y un mínimo de tiempo programable, crea un historial de operación del dispositivo y, en caso de falla de energía, manda a condición segura la válvula y poderse iniciar automáticamente en caso de restablecerse el servicio. Todo esto con el buen PIC16F876A.

Hasta el momento, a estirones y golpes con el Datasheet y el Manual de Referencia de los PIC's de Medio Rango he logrado sacar adelante el proyecto. Pero me topé con un pequeño problemita:

Puedo realizar bien el paginado del programa, manejar PCLATH y la llamada de rutinas de una página a otra sin ningún problema, es más, he puesto 3 tablas para manejar mensajes en caso de que las EEPROM del historial lleguen a fallar (1 tabla por cada una de las páginas en las que he trabajado por el momento, aun no toco la 4a página del programa del pic, son como 4914 lineas de código).

El problema viene aquí: En uno de los menús necesito acceder a otro submenú y en ese sub menú a otro submenú, y en este, a su vez, realizar la selección de dos opciones.

Por lo que logré entender (con mi ingles mordisqueado y con ayuda de San Google traslator) en el Manual de Referencia del PIC (en la página 100) me establece que...

"No puedo realizar más de 8 llamadas anidadas, pues el stack sufre un sobre flujo (es un buffer circular)"

En otras palabras:

No puedo realizar más de 8 llamadas, una dentro de otra, dentro de otra, dentro de otra... etc. porque si no, la novena llamada realizada se ingresara en la primera posición del STACK y eso me provocaría la perdida de la dirección del PC de la primer llamada que yo realice.

Mi consulta es esta: Estoy en lo cierto??? De no ser así, tengo un problema para entender las cosas y mi ingles no me sirve de mucho. De ser cierto, sugiero, humildemente, que lo pongan dentro del tutorial, pues, al menos a mí, que estoy haciendo este proyecto completamente en Assembler, me hubiese sido de mucha ayuda, pues estuve 2 días batallando con ese problemita.


El "overflow" y "underflow" del STACK causan problemas en el ciclo continuo del programa, entonces, al menos en los PIC's de Rango Medio, no se deben de sobrepasar mas de 8 llamadas, incluido el servicio de interrupciones (sin finalizar con su debido RETURN, RETLW o RETFIE ) para asegurar un correcto funcionamiento del STACK.


----------



## Daniel Meza (Nov 8, 2014)

Hola


> El problema viene aquí: En uno de los menús necesito acceder a otro submenú y en ese sub menú a otro submenú, y en este, a su vez, realizar la selección de dos opciones.



¿y cada submenú corresponde a un nivel de pila? o ¿cómo es tu estructura del menú?, pudieses usar otra alternativa para acceder a menús sin necesidad de utilizar intensamente la pila.


----------



## MrAlphonse (Nov 8, 2014)

Si, la verdad desconocia esto, en este momento estoy modificando las rutinas y en lugar de CALL estoy usando GOTO, al final de cuentas, no es una rutina que pueda usar en cualquier otra parte del programa, son solo rutinas para mostrar algunos mensajes del menu y guardar las configuraciones propias de ese submenu. Con esto espero poder liberarme de este problema.

No, no todos los niveles de pila corresponden a un submenu, algunos corresponden a rutinas de retardos que usan otras rutinas de manejo de LCD o de escritura en la EEPROM del PIC, maximo 4 son usadas para los submenus, las otras restantes son para las rutinas ya mencionadas.


----------



## Daniel Meza (Nov 8, 2014)

Lo que también te sugiero es que clasifiques tus subrutinas en función de lo que hagan, temporización, memoria, lcd, etc y que procures en lo posible que sean independientes unas de otras, esto evitará el anidamiento de subrutinas


----------



## leouu155 (Dic 12, 2014)

Muy buena tu ayuda, me va a servir de mucho, gracias. Estoy intentando hacer un programa que efectue una acción (un efecto de luces) que se repetida una x cantidad de veces y que luego pase a otro efecto. .Alguien sabe como podría hacer esto? he intentado hacerlo con la instrucción DECFSZ pero no me a resultado como quería, ya que hace el primer efecto, no lo repite, y luego salta al siguiente pero queda en un bucle infinito mezclado con otro efecto... les agredecería que me pudieran ayudar ya que me encuentro bastante confundido con esto..


----------



## MrAlphonse (Dic 13, 2014)

Pon el codigo para ver como manejas la rutina. Quizas estan utilizando un solo registro para todo el proceso y no estas cargando adecuadamente el numero de veces que quieres que se repita una secuencia. Pero es solo imaginacion mia, mejor, pon el codigo para poderte ayudar.


----------



## leouu155 (Dic 13, 2014)

MrAlphonse dijo:


> Pon el codigo para ver como manejas la rutina. Quizas estan utilizando un solo registro para todo el proceso y no estas cargando adecuadamente el numero de veces que quieres que se repita una secuencia. Pero es solo imaginacion mia, mejor, pon el codigo para poderte ayudar.


 acá está, sepan disculpar lo mal que programo :/ todavía no lo domino bien... 


```
LIST P=16F628A
		INCLUDE <P16F628A.INC>
		__config _BODEN_OFF & _CP_OFF & DATA_CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _MCLRE_ON & _XT_OSC 
			ORG 0
			BSF STATUS, RP0
			MOVLW B'00000000'
			MOVWF TRISB 
			BCF STATUS, RP0
EFECTO1		EQU H'21'
			MOVLW .15
			MOVWF .15	
			MOVLW B'00001111'
			MOVWF PORTB 
			CALL DEMORA
			MOVLW B'11110000'
			MOVWF PORTB
			CALL DEMORA
			DECFSZ .15
			GOTO EFECTO2
EFECTO2  	EQU H'21'
			MOVLW .15
			MOVWF .15
			MOVLW B'10101010'
			MOVWF PORTB
			CALL DEMORA 
			MOVLW B'01010101'
			MOVWF PORTB
			CALL DEMORA
			DECFSZ .15 
			GOTO EFECTO3
EFECTO3		EQU H'21'
			MOVLW .15
			MOVWF .15
			MOVLW B'10000001'
			MOVWF PORTB 
			CALL DEMORA
			MOVLW B'11000011'
			MOVWF PORTB
			CALL DEMORA 
			MOVLW B'11100111'
			MOVWF PORTB
			CALL DEMORA
			MOVLW B'11111111'
			MOVWF PORTB
			CALL DEMORA
			MOVLW B'11100111'
			MOVWF PORTB
			CALL DEMORA
			MOVLW B'11000011'
			MOVWF PORTB
			CALL DEMORA
			MOVLW B'10000001'
			MOVWF PORTB
			CALL DEMORA
			DECFSZ .15
			GOTO EFECTO1
DEMORA		  
			MOVLW   .100          ;RETARDO DE 250 MILISEGUNDOS
        	MOVWF   H'7E'           ;
TOPB    	MOVLW   .250            ; TIMING ADJUSTMENT VARIABLE
        	MOVWF   H'7F'
TOP2B   	NOP                   
        	NOP
        	NOP
        	NOP
        	NOP
        	NOP
        	DECFSZ  H'7F',F        ; INNER LOOPS COMPLETE?
        	GOTO    TOP2B          ; NO, GO AGAIN
            DECFSZ  H'7E',F        ; OUTER LOOPS COMPLETE?
        	GOTO    TOPB           ; NO, GO AGAIN
			RETURN		       ; YES, RETURN FROM SU
			END
```


----------



## Daniel Meza (Dic 13, 2014)

Hola, ya vi el error, está en que no permites que el micro entre al lazo de efecto, mira intenta con esto:




```
MOVLW .15
			MOVWF .15
EFECTO1		EQU H'21'
				
			MOVLW B'00001111'
			MOVWF PORTB 
			CALL DEMORA
			MOVLW B'11110000'
			MOVWF PORTB
			CALL DEMORA
			DECFSZ .15,F
			GOTO EFECTO1
```

Como verás también, la instrucción que carga el registro contador de lazos lo puse fuera del bucle para que el programa no entre en un lazo interminable


----------



## leouu155 (Dic 13, 2014)

gracias, ahora entiendo como funciona la cosa  pero tengo otra duda... porqué cuando compilo un programa me salta siempre el error de "Messagge [302] : Register in operand not in bank 0.  Ensure that bank bits are correct.?
cuando hago doble click sobre el error me aparece esta liena 
	
	



```
MOVWF TRISB
```
sin embargo abajo me dice que no hay errores y la compilación se hace correctamente. esto afecta al funcionamiento del programa?


----------



## Daniel Meza (Dic 13, 2014)

no es un error como tal, si no un "warning" ya que el compilador no sabe que anteriormente cambiaste de banco con los bits RP0 y RP1. Para quitarlo coloca un ^0x80 después de las instrucciónes que trabajen con registros en otros bancos. Por ejemplo:


```
movwf   TRISX^0x80

clrf        INTCON^0x80

movf     EEDATA^0x100
```


----------



## Miembro eliminado 356005 (Dic 13, 2014)

Hay otra forma de hacerlo: diciendo al enlazador que omita ese mensaje.

Pon esto al principio:


```
errorlevel  -302                        ; Turn off banking message
```

Eso sí... con eso te estás comprometiendo a que vas a llevar un control férreo de en qué banco estás trabajando en cada momento.

Más información en el manual del ensamblador del PIC.


----------



## MrAlphonse (Dic 13, 2014)

No soy vidente pero le atine, estabas usando un solo registro para el mismo proposito, en este caso el TMR1. 

El compañero Daniel Meza ya te dio la respuesta a tu problema y el compañero JoaquinFerrero ya te dijo como eliminar los "warning". 

Solo como sugerencia, nunca utilices los registros de uso especial del PIC como registros cualquiera de la RAM, a menos que no los vayas a utilizar para nada y que tu proceso requiera una cantidad de RAM que ya sobrepasaste y que solo tocando esos registros puedas solucionar ese problema. 

Los registros de uso especial pueden verse afectados por el uso de otros registros especiales y cuando menos te lo esperas te crean un "problema" que no contemplas. En este caso no tienes habilitado el TMR1 ni estas utilizando interrupciones ni cosa por el estilo, asi que no hay problema. Lo recomendable es usar la RAM a partir de la direccion donde inician los registros de proposito general (para este PIC, la 20h para el Banco 0). 

Ademas, en un futuro podrias necesitar ese registro y tendrias que modificar tu programa, mejor declara tu bloque de variables:


```
CBLOCK	0x20
	CONTADOR			;EN LUGAR DE PONER	"MOVWF	.15"	PUEDES PONER	"MOVWF	CONTADOR"
	ENDC
```


----------



## dragon33 (Ene 18, 2015)

Tengo una duda, a ver si los expertos en ensambler me ayudan. Imaginaos, cargo un registro con el número 25 y ese número deseo sacarlo de la siguiente forma por el PORTB, el 5 sacarlo por un nibble que puede ser desde RB0 a RB3, y el 2 por otro nibble desde RB4 a RB7 ¿Como lo haría?. Lo que pretendo es poder sacar cualquier número entre 0 a 99 y poder visualizarlo por dos displays de 7 segmentos.


----------



## fdesergio (Ene 18, 2015)

dragon33 dijo:


> Tengo una duda, a ver si los expertos en ensambler me ayudan. Imaginaos, cargo un registro con el número 25 y ese número deseo sacarlo de la siguiente forma por el PORTB, el 5 sacarlo por un nibble que puede ser desde RB0 a RB3, y el 2 por otro nibble desde RB4 a RB7 ¿Como lo haría?. Lo que pretendo es poder sacar cualquier número entre 0 a 99 y poder visualizarlo por dos displays de 7 segmentos.



Si no hay problema con codigo extenso puedes usar una tabla, cargas el valor en W, llamas la tabla, retornas con el codigo en W y pasas al PORTB, asi de facil, chaooooo


----------



## Miembro eliminado 356005 (Ene 18, 2015)

Lo primero que debes hacer es transformar el número en binario a BCD. Por estos foros hay algunos hilos que lo explican.

Hay dos formas básicas:


Hacemos un bucle por el que necesitamos sacar el número de decenas restando 10 al valor original. Si es menor que 10, entonces lo que quedan son las unidades. Cuando tenemos los dos valores, componemos el nuevo valor en BCD haciendo una rotación del valor de las decenas en el nibble alto, y luego haciendo un OR con el valor de las unidades
La otra forma es un poco más de matemáticas, pero más breve que la anterior. Consiste primero en saber cuántas decenas hay. Eso lo podemos sacar haciendo lo mismo que antes, con restas sucesivas. Una vez que lo sabemos, multiplicamos ese valor por 6, y el resultado se lo sumamos al valor original. Eso nos da el valor original ya transformado a BCD. Naturalmente, esta es la solución literal, pero en la práctica se usan trucos, como por ejemplo: cada vez que sumamos una decena más, lo que hacemos en realidad es sumar 6 a un sumador aparte. De esa manera, al mismo tiempo que hacemos la división de las decenas, estamos haciendo la multiplicación por 6 de las decenas. Solo quedaría hacer la suma del valor original con ese multiplicando.


----------



## savad (Sep 18, 2015)

Leyendo el foro, observé que la mayoría de ustedes no comenta su código, y como es un tema para principiantes, deben de hacerlo para que todo mundo los siga sin dificultad.

También observé que cuando quieren mostrar un dato binario en un display multiplexado, tienen varias dudas de cómo hacerlo.

Si pueden leer inglés, les recomiendo éste sitio:
http://www.islavici.ro/cursuriold/conducere sist cu calculatorul/PICbook/0_Uvod.htm
Tiene la versión on-line del libro gratis "PIC microcontrollers, for beginners too"

Si no, aquí les dejo el macro corregido y programa de prueba como aparece en ese libro. (No lo he simulado)
Si alguien quiere simularlo y comentar su resultado, sería muy bueno.

```
; Ejemplo de uso de macro

        PROCESSOR    P16F84
        #include    "p16f84.inc"
        _CONFIG        _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC

; Macro para extraer info para mostrar en el Display
; Dig1 = MSD (centenas) Dig2= decenas  Dig3= LSB (unidades)
; par0 = variable a ser mostrada LSB 8-bits (0-255)

digibyte macro par0
    local Loop_C
    local Loop_D
    local Pozitiv
    
    clrf    Dig1        ; Limpiamos contadores que contienen lo
    clrf    Dig2        ; que se va a mostrar en el display
    clrf    Dig3        ; En este caso 3 digitos (0 a 255)

Pozitiv
    movf    par0,w        ; Guarda el número a ser separado
    movwf    Digtemp        ; en forma temporal
;
; Convierte a BCD
; La técnica consiste en dividir el parámetro entre una mascara
;   (100,10,1). para obtener asi el digito en decimal.
; Se implementa la división - subtrayendo al parametro la
;    mascara contando cuantas veces lo hemos hecho, hasta que el
;    resultado de la resta sea menor a la mascara. 
; como empesamos con el contador en 1 debemos substraer 1 antes de
; salir. Para tener en el contador con el resultado correcto
;
; Empesamos con las centenas 
    movlw    .100        ; mascara para las centenas
Loop_C
    incf    Dig1,f      ; Centenas = Centenas + 1
    subwf    Digtemp,f   ; Parametro = Parametro - .100
    btfsc    STATUS,C    ; Si es menor a zero (c=0) salta 1 linea
    goto    Loop_C        ; Si no regresa hasta que ocurra
    decf    Dig1,f        ; Resta 1 alcontador antes de salir
                        ; Digtemp contiene el cosiente de la division
; Seguimos con las decenas
    movlw    .10         ; Ajustamos la mascara para decenas
Loop_D
    incf    Dig2,f        ; Decenas = Decenas + 1
    subwf    Digtemp,f
    btfsc    STATUS,C
    goto    Loop_D
    decf    Dig2,f      ; Dig2 contiene las Decenas y
                        ; Digtemp contiene ahora las unidades
; y finalizamos con las unidades
    movf    Digitemp,w    ; Mueve las unidades a su contador
    movwf    Dig3
    endm
        
        org    0x00
        goto    main        ; Inicio del programa
        
        org    0x04
        goto    ISR            ; Inicio del vextor de interrupción
        
        Cblock 0x0C
        Dig1                ; Centenas del numero binario D
        Dig2                ; Decenas  del numero binario D
        Dig3                ; Unidades del numero binario D
        Digtemp             ; auxiliar en conversion bin-.Dec
        D                    ; numero a desplegar
        One                    ; auxiliar para el multiplexado del Display
        W_temp                ; copia de W en interrupciones
        endc
        
Main
;    banksel    TRISA
        bsf STATUS,5        ;se pone un 1 en el bit 5 del status para
                            ;   elegir el banco 1
         movlw    b'11111100'    ; RA0 & RA1 son salida para multiplexado
        movwf    TRISA
        clrf    TRISB        ; Puerto B son las salidas de los segmentos
        movlw    b'10000100'    ; Prescaler = 32-> TMR0 incrementa cada 32ms
        movwf    OPTION_REG    ;   Asumimos que se usa un cristal de 4 MHz4
;        banksel    PORTA
        bcf STATUS,5  n     ; cambiamos al banco 0 poniendo un 0 en el
                            ;   bit 5 del Status
        movf PORTA,0nn      ; leemos el puerto A y cargamos en W
        movlw    ,96            ; TMR0 inicia en 96
                            ; de manera que una interrupcion ocurre
                            ; cada (255-97)* 32 ms = 5.088 ms
        movwf    TMR0
        movlw    b'10100000'    ; Habilita interrupcion del timer 0
        novwf    INTCON
        
        movlw    .21            ; Despliega el numero 21
        movwf    D
        clrf    One
        clrf    PORTA        ; Apaga ambos digitos al inicio
Loop
        goto Loop        ; Loop principal -> salimos con una interrupcion
        
ISR                            ; Rutina de interrupcion
        movwf    W_temp        ; Salva W
        movlw    .96            ; Inicializa TMR0 para que tenga la siguiente
        movwf    TMR0        ; interrupcion en aprox 5ms
        bcf        INTCON, T0IF ;Limpia bandera de Overflow del TMR0
                            ; para ser capaz de reaccionar a la siguiente
                            ; interupcion
        bcf        PORTA, 0    ; Digito en A0, apagado
        bcf        PORTA, 1    ; Digito en A1, encendido
        movf    One, f        ; Ajusta banderas
        btfsc    STATUS,Z    ; Cual display debe prenderse?
        goto     Msd_on
Lsd_on                        ; Si el MSD estaba anteriormente ON
        bcf        One,0        ; indica que LSD esta ON
        movlw    High Bcdto7seg ; Antes de usar el macro tenemos que ir
                            ; a la tabla de conversion, PCLATH
        movwf    PCLATH        ; W debe sr inizializado con la direccion
                            ; mas alta de la tabla
                            ; para prever errores de direccionamiento
        digibyte    D        ; llama a la macro para separar digitos de D
        movf    Dig3, w        ; W = Unidades
        call    Bcdto7seg    ; llama la tabla de conversion para 7-seg
                            ; para displays que son catodo común
                            ; regresa W con el codigo correspondiente
        movwf    PORTB        ; y prepara los segmentos
        bsf        PORTA, 1    ; Enciende el Digito de las Unidades
                            ; por ~ 5ms, hasta que la siguiente
                            ; interrución ocurra
        movf    W_temp,w    ; Recupera W alvalor que tenia antes de
                            ; entrar a la rutina de interrupción.
        retie
        
Msd_On
        bsf        One, 0        ; Indica que el MSD esta encendido
        movlw    High Bcdto7seg ; Antes de usar el macro tenemos que ir
                            ; a la tabla de conversion, PCLATH
        movwf    PCLATH        ; W debe sr inizializado con la direccion
                            ; mas alta de la tabla
                            ; para prever errores de direccionamiento
        digibyte    D        ; llama a la macro para separar digitos de D
        movf    Dig2, w        ; W = Unidades
        call    Bcdto7seg    ; llama la tabla de conversion para 7-seg
                            ; para displays que son catodo común
                            ; regresa W con el codigo correspondiente
        movwf    PORTB        ; y prepara los segmentos
        bsf        PORTA, 0    ; Enciende el Digito de las Decenas
                            ; por ~ 5ms, hasta que la siguiente
                            ; interrución ocurra
        movf    W_temp,w    ; Recupera W alvalor que tenia antes de
                            ; entrar a la rutina de interrupción.
        retie
    
    org    0x300                ; Inicio de la tabla de conversion
                            ; puesta al inicio de la tercer banco de RAM4
                            ; Otras paginas son posibles pero la tabla
                            ;debe estar en una sola pagina (banco) de RAM

Bcdto7seg                    ; Tabla de conversion para 7-seg
        addwf    PCL,f        ; Ajusta W al valor de tabla + offset
        dt        0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f
;        retlw    0x3f        ; número 0
;        retlw    0x06        ; número 1
;        retlw    0x5b        ; número 2
;        retlw    0x4f        ; número 3
;        retlw    0x66        ; número 4
;        retlw    0x6d        ; número 5
;        retlw    0x7d        ; número 6
;        retlw    0x07        ; número 7
;        retlw    0x7f        ; número 8
;        retlw    0x6f        ; número 9
```


----------



## savad (Sep 19, 2015)

Les dejo una traducción e interpretación del codigo de conversión de un número de 16-bits a BCD
Este código se puede usar en cualquier PIC, y esta publicado en la nota de aplicación de microchip  
AN526 PIC16C5X KPIC16CXXX Math Utility Routines.
Espero que les sea útil.


```
LIST P = 16C54, n = 66
;
;********************************************************************
;  Programa para convertir un número Binario de 16-Bit a BCD
; Esta rutina convierte un número binario de 16-Bit a un  número
; BCD  de 5 Digitos. Esta rutina es útil puesto que los PIC16C55 & ;
;  PIC16C57 tienen dos puertos de 8-bit y uno de 4-bit (RA) 
; para mostrar el resultado de los 5 digitos BCD)
;
; El número binario de 16-bit es colocado en variables RAM
;  H_byte & L_byte, con el byte más significativo en H_byte.
; El número BCD de 5-digitos es regresado en R0, R1 & R2 
;  con R0 conteniendo el MSD en su nibble alto (el mas a la derecha) 
; Usa la técnica Shift - Add 3
;
; Performance :
; Program Memory : 35
; Clock Cycles : 885
;
;
; Program: B16TOBCD.ASM
; Revision Date:
; 1-13-97 Compatibility with MPASMWIN 1.40
;
;*******************************************************************;
;
		Cuenta equ 16		; Contador para número de bits
		temp   equ 17
;
		H_byte equ 10
		L_byte equ 11
		R0     equ 12 		; RAM Assignments
		R1     equ 13
		R2     equ 14
;
	include “p16c5x.inc”
	LIST
;P16C5X.INC Standard Header File, Ver. 3.30 Microchip Technology, Inc.
    LIST
;
B2_BCD
		bcf STATUS,0 	        ; carry bit = 0
		movlw .16		; Cuenta=16  bits
		movwf Cuenta
		clrf R0			; Limpia Registros
		clrf R1
		clrf R2
loop16
		rlf L_byte, F      ; Shift a la izquierda un bit del dato en 
		rlf H_byte, F	;   los registros del resultado
		rlf R2, F
		rlf R1, F
		rlf R0, F
;
		decfsz Cuenta, F ; Terminamos todos los16 bits
		goto adjDEC         ;  No: Continua procesando
		RETLW 0               ; Si:  Regresa con W = 0
;
adjDEC				  ; Despues de cada shift left
					  ; Debemos checar cada nibble del Registro
		movlw R2		  ;  no sea mayor a 7 y si es así hay que
                                          ; sumarle 3
		movwf FSR	  ;  empesamos con R2
		call adjBCD			
;
		movlw R1		  ; y seguimos con R1
		movwf FSR			
		call adjBCD
;
		movlw R0		 ; y al último con R0
		movwf FSR
		call adjBCD
;
		goto loop16
;
adjBCD
		; 
		movlw 3			; Suma 3 a LSB
		addwf 0, W		; y sumamos al registro
		movwf temp		;
		btfsc temp, 3		; test if LSB > 7
							;  Si :  LSD = LSD + 3 
		movwf 0				;  No :  no alteramos LSD
		movlw 30			; Suma 30 al MSB
		addwf 0, W		;
		movwf temp
		btfsc temp, 7 		; test if MSB > 7
							;  Si  :  MSD = MSD + 3 
		movwf 0				;  No : no alteramos MSD
		RETLW 0
;
;********************************************************************
; Test Program
;*********************************************************************
main
		movlw 0FF		; número binario a convertir = FFFF
		movwf H_byte	        ; 
		movwf L_byte
		call B2_BCD 	        ; Despues de la conversion del número
						; a BCD obtenemos
; 						;      R0,R1,R2 = 06,55,35
;
self
		goto self
;
		org 1FF			; Reset vector
		goto main
		
		END
```


----------



## savad (Sep 20, 2015)

Disculpen por el error en la macro para descomponer un numero binario de 8-bits usando division entre 10
aqui esta el codigo correcto


```
; Macro para extraer info para mostrar en el Display
; Dig1 = Centenas, Dig2= Decenas  Dig3= LSB (unidades)
; par0 = variable 8-bits a ser convertida a BCD  (0-255)

digibyte macro par0
	local ExitC			; Declaramos las etiquetas internas
	local ExitD
	
	clrf	Dig1		; Limpiamos contadores que contien
	clrf	Dig2		;    en decimal lo que se va a mostrar en
	clrf	Dig3		;     el display

; Convierte a BCD
; La técnica consiste en dividir el parámetro entre una mascara
;   (100,10). para obtener así el digito en decimal.
; Se implementa la división - subtrayendo al parametro la
;    mascara y contando cuantas veces lo hemos hecho, hasta que el
;    resultado de la resta sea menor a la mascara. 
; como empesamos con el contador en 1 debemos substraer 1 antes de
; salir y sumar la mascara al parametro. Para tener en el contador
; con el resultado correcto
;
	movf	par1,w		; Guarda el número a ser
	movwf	Digtemp	;    separado en registro de conversión
;
; Empesamos con las Centenas
;
	movlw	.100        ; mascara para las centenas
ExitC
	incf	Dig1, f		; Centenas = Centenas + 1
	subwf	Digtemp, f	; Parametro = Parametro - .100
	btfsc	STATUS, C	; Si es menor a zero (c=0) salta 1 linea
	goto	ExitC		; Si no regresa hasta que ocurra
	addwf	Digtemp, f	; Agrega 100 ya que restamos de más 
	decf	Dig1, f		;    y Resta 1 al contador antes de salir
	                    ; Digtemp contiene del número a procesar
						;    solo las Decenas y Unidades
; Seguimos con las decenas
;
	movlw	.10			; Ajustamos la mascara para decenas
ExitD
	incf	Dig2, f		; Decenas = Decenas + 1
	subwf	Digtemp, f
	btfsc	STATUS, C
	goto	ExitD
	addwf	Digtemp, f	; Agrega 10 ya que restamos de más 
	decf	Dig2, f		;    y Resta 1 al contador antes de salir
	                    ; Digtemp contiene del número a procesar
						;    solo las Unidades
; y finalizamos con las unidades
	movf	Digitemp,w	; Mueve las unidades a su contador
	movwf	Dig3
	endm
```


----------



## savad (Oct 1, 2015)

Cambiando un poco, Supongan que deseamos colocar 4 datos,  en el ejemplo números, en 4 localidades de memoria RAM consecutivas, en el ejemplo empesamos en 10h. Facil verdad...? un ejemplo de clase o tarea. Les dejo aqui 3 formas diferentes de hacerlo. Fuerza bruta si son números, uso de pseudo-instruccion, y el importante direccionamiento indirecto.


```
;****************************************************
;                                                                    *
;    Filename:	    Dir_Indexado.asm                 *
;    Date:          Septiembre 29,2015                *
;    File Version:  1.0                                       *
;                                                                    *
;    Author:	    David Sanchez                      *
;    Company:       David Sanchez                     *
;                                                                    * 
;                                                                    *
;****************************************************
;    Descripción :                                              *
; Prueba de movimiento de Datos usando          *
;   diferentes metodos de direccionamiento        *
;   direccionamiento indexado                            *
;       Dirección = Reg4(FSR) + Reg0 (INDF)      *
;       Contenido de Dirección  en W0                 *
;                                                                     *
;   Para ello se coloca los números 1,2,3 & 4     *
;   En localidades continuas de RAM                 *
;   iniciando en 0x010h                                      *
;                                                                     *
;                                                                     *
;*****************************************************

	list      p=16c54             ; directiva para definir el procesador
	#include <p16c5x.inc>         ; definición  de las variables specificas
                                 ; del procesador 

	__CONFIG   _CP_OFF & _WDT_OFF & _RC_OSC

; '__CONFIG'  Esta directiva es useda para implantar una palabra  de configuración en el archivo  *.asm
; Las etiquetas que siguen la directiva estan localizadas en el archivo que describe el procesador *.inc 
;
;***** DEFINICION DE CONSTANTES
Zero	    EQU	0	    ; zero
;
;***** DEFINICION DE VARIABLES RAM
temp	    EQU 0x07        ; 
Prueba	    EQU	0x10        ; Seis localidades seguidas
;    
;***** Macros
; INCW Incrementa W
INCW MACRO
    MOVWF   temp	; Salva W
    INCF    temp, w	; temp = temp + 1
    ENDM
;   
;       Shift PORTA to the left 
;	SLCPA   MACRO
;	MOVLW   1
;	BCF     STATUS, C       ; Clear carry
;	RLF     (PORTA),F       ; Rotate left
;        ENDM        
   
;*****
	ORG     0x000

start	                    ; Inicio del Programa
	CALL CLR_REG	    ; Limpia registros
		
	; Fuerza bruta ... ponemos 1 en 0x10, 2 en 0x11. 3 en 0x12, & 4 en 0x13
	INCF	Prueba         ; 1 en 0x10
	INCF	Prueba +1      ; 1 en 0x11
	INCF	Prueba +2      ; 1 en 0x12
	INCF	Prueba +3      ; 1 en 0x13
	
	INCF	Prueba +1      ; 2 en 0x11
	INCF	Prueba +2      ; 2 en 0x12
	INCF	Prueba +3      ; 2 en 0x13
	
	INCF	Prueba +2      ; 3 en 0x12
	INCF	Prueba +3      ; 3 en 0x13

	INCF	Prueba +3      ; 4 en 0x13
	; aqui terminamos Fuerza bruta
    ;	     ... 10 localidades de Memoria del Programa
	;            ...  0 localidades de RAM
	;            .  10 cyclos de ejecución.
	; Si queremos escribir oto dato a la siguiente localidad
	;    usa 5 más localidades de memoria del program x dato
        ;       o sea "n" localidades de mas por cada dato adicional
	;                  donde "n" es el número de datos
;
	; Exploremos otra forma:
	CALL CLR_REG	    ; Limpia registros
	
	; alternativa 1 de fuerza bruta   usa Pseudo instrucción INCW
	; Una Pseudo instrucción es una instrucción que no es parte de
	;        grupo de instrucciones que vienen en la Hoja de datos.
    ;        Se declaran como macros.	
	MOVLW	1
	MOVWF	Prueba      ;      coloca 1 en 0x10
	INCW		         ; W=2
	MOVWF	Prueba+1    ;      coloca 2 en 0x11
	INCW		         ; W=3
	MOVWF	Prueba+2    ;      coloca 3 en 0x12
	INCW		         ; W=4
	MOVWF	Prueba+3    ;      coloca 4 en 0x13
;
	; INCW no existe asi que tiene que ser implementada
	; por un macro o sustutida por 
	; MOVWF temp
	; INCF  temp ,w
;
	; aqui terminamos  alternativa 1
    ;	... 11 localidades de Memoria del Programa
	;       ...  1 localidades de RAM
	;     ...  11 cyclos de ejecución.
	; Si queremos escribir oto dato a la siguiente localidad
	;    usa solo 2 más localidades de memoria del program x dato
;  Aparentemente Fuerza bruta es más rápida y ocupa menos Memoria,
;    pero solo para pocos movimientos de datos ....si movemos más datos
;    ... alternativa 1 es más eficiente
;
	CALL CLR_REG	    ; Limpia registros
;
	; Alternativa Direccionamiento Indexado
	MOVLW	Prueba	    ; Base = 10h
	MOVWF	FSR         ; FSR = 111 1 0000
	MOVLW	1
	MOVWF	INDF        ; Write W en base         1 en 0x10
	INCF	FSR	    ; Base = base + 1		    ; W = 01h
	INCW		    ;   W = 02h
	MOVWF	INDF	    ;   Write 02 en base+1    2 en 0x11
	INCF	FSR	    ; Base = base + 2
	INCW		    ;   W = 03h
	MOVWF	INDF	    ;   Write 03 en base+2    3 en 0x12
	INCF	FSR	    ; Base = base + 3
	INCW		    ;   W = 04h
	MOVWF	INDF	    ;   Write 04 en base+3    4 en 0x13
;
	;aqui terminamos  Direccionamiento indexado
    ;	... 13 localidades de Memoria del Programa
	;       ...  0 localidades de RAM
	;     ...  13 cyclos de ejecución.
	; Si queremos escribir oto dato a la siguiente localidad
	;    usa solo 3 mas localidades de memoria del programa x dato
	;
;    Aparentemente Alternativa 1 es más rápida pero usa 1 preciosa
;      localidad de Memoria RAM
;  Asi que ha usar el direccionamiento indexado.

; SUBRUTINAS
;
CLR_REG	; Limpia Registros Prueba
	CLRW		   
	MOVWF	temp	    ; temp = 0
	MOVWF	Prueba	    ; Prueba = 0
	MOVWF	Prueba +1   ; Prueba +1 = 0
	MOVWF	Prueba +2   ; Prueba +2 = 0
	MOVWF	Prueba +3   ; Prueba +3 = 0
	RETLW	0	    ;  Regresa con w=0
;
; TABLAS

; ***** Reset
	ORG     0x1FF       ; processor reset vector
	goto    start

	END
```


----------



## savad (Oct 14, 2015)

Hola a todos,
Necesitaba una rutina para el PIC16x54 que me hiciera la  conversión de binario (16-bits) a BCD. Hay muchas en el web pero para PICs más caros y poderosos que usan la instrucción SUBLW ... que el 16x54 carece. Asi que cambie un poco su implementación. Les dejo aqui la rutina en Ensamblador probada en MPLAB X. y espero sus comentarios.


```
;****************************************************
;                                                   *
;    Filename:	    ShiftAdd3.asm                   *
;    Date:          Octubre 14,2015                 *
;    File Version:  1.0                             *
;                                                   *
;    Author:	    David Sanchez                   *
;    Company:       David Sanchez                   *
;                                                   * 
;                                                   *
;****************************************************
;    Implementado para el PIC16x54                  *
; Toma un número binario de 16-bit y lo convierte en *
; 5 digitos BCD almacenados en forma consecutiva    *
;  MSB en la localidad más baja                     * 
;****************************************************

	list      p=16c54            ; directiva para definir el procesador
	#include <p16c5x.inc>        ; definición  de las variables specificas
                                 ; del procesador 

	__CONFIG   _CP_OFF & _WDT_OFF & _RC_OSC
;
;***** DEFINICION DE CONSTANTES
;
;***** DEFINICION DE VARIABLES RAM
temp      EQU 0x07   ; variable de trabajo
binH      EQU 0x08   ;número binario a convertir - high byte
binL      EQU 0x09   ;número binario a convertir - low byte
PackBCD_x_4      EQU 0x10   ;resultado en packed BCD - digitos altos
PackBCD_3_2      EQU 0x12   ;resultado en packed BCD - digitos de enmedio
PackBCD_1_0      EQU 0x14   ;resultado en packed BCD - digitos bajos
counter   EQU 0x0A   ;High nible of PackBCD_x_4  - variable de trabajo
BCD_1     EQU 0x13   ;UnPackBCD
BCD_3     EQU 0x11   ;UnPackBCD
;
;***** Macros
;
; Programa principal 
;*****
	ORG     0x000
;
start
; Escribimos valor que deseamos convertir 3AF3=15091  
    movlw   0xF3
    movwf   binL
    movlw   0x3A
    movwf   binH
	    
; Inicia la converción Binario a BCD con el algorithmo shift-Add3     
  ; Limpia los registros BCD     
     clrf   PackBCD_1_0     
     clrf   PackBCD_3_2     
     clrf   PackBCD_x_4
     clrf   BCD_1
     clrf   BCD_3
  
     ; loop por 15 rotaciones con la corrección "add 3"     
     movlw    .15     
     movwf    counter    
     ; convierte binario  (16 bits) a BCD packed     
Bin2Bcd_Loop     
     bcf        STATUS, C     ; preparamos carry flag para RLF   
     ; Rotamos todos a la izquierda     
     rlf        binL, F     
     rlf        binH, F     
     rlf        PackBCD_1_0, F     
     rlf        PackBCD_3_2, F     
     rlf        PackBCD_x_4, F     
     ; Prueba los digitos BCD que tengan un valor binario >= 5     
     ;  - Si son >=5 add3     
Test_Digit_0
     movlw   0x04
     movwf   temp
     movlw   0x0f     
     andwf   PackBCD_1_0, W
;     sublw    0x04
     subwf   temp, w 
      ;
     btfsc   STATUS, C
     goto    Test_Digit_1      ; digito 0 < 5
     ; Add3 a digito 0
     movlw   0x03
     addwf   PackBCD_1_0, F    ;  Nunca hay un acarreo de digitos
Test_Digit_1
     movlw   0x40
     movwf   temp
     movlw   0xf0
     andwf   PackBCD_1_0, W
     ; sublw   0x40
     subwf   temp, w
     btfsc   STATUS, C
     goto    Test_Digit_2      ; digito 1 < 5
     ; Add3 a digito 1
     movlw   0x30
     addwf   PackBCD_1_0, F
Test_Digit_2
     movlw   0x04
     movwf   temp
     movlw   0x0f
     andwf   PackBCD_3_2, W
     ;sublw   0x04
     subwf   temp, w
     btfsc   STATUS, C
     goto    Test_Digit_3      ; digito 2 < 5
     ; Add3 a digito 2
     movlw   0x03
     addwf   PackBCD_3_2, F    ; Nunca hay un acarreo de digitos
Test_Digit_3
     movlw   0x40
     movwf   temp
     movlw   0xf0     
     andwf   PackBCD_3_2, W
     ;sublw   0x40
     subwf   temp, w
     btfsc   STATUS, C
     goto    Test_Digit_4     ; digito 3 < 5
     ; Add3 a digito 3
     movlw   0x30
     addwf   PackBCD_3_2, F
Test_Digit_4
     movlw   0x40
     movwf   temp
     movlw   0x0f
     andwf   PackBCD_x_4, W
     ;sublw   0x40
     subwf   temp, w
     btfsc   STATUS, C
     goto    Test_Digits_End   ; digito 4 < 5
     ; Add3 a digito 4
     movlw   0x03
     addwf   PackBCD_x_4, F    ; Nunca hay un acarreo de digitos
Test_Digits_End
     decfsz  counter, F
     goto    Bin2Bcd_Loop
     ; Ultima rotación (16th) sin adiciones correctivas
     bcf     STATUS, C        ; preparamos carry flag para RLF
     ; Rotamos todos a la izquierda
     rlf     binL, F
     rlf     binH, F
     rlf     PackBCD_1_0, F
     rlf     PackBCD_3_2, F
     rlf     PackBCD_x_4, F
     ; la converción ha terminado
     movlw    0x0f                ; limpiamos los nibble no usados
     andwf    PackBCD_x_4, F
 ;
 ; * Ahora ya tenemos los 5 digitos BCD pero agrupados
 ; *  Si es necesario procedemos a desagruparlos
 ;
 ; Unpack los digitos BCD
     swapf   PackBCD_1_0, W        ; extrae digito 1
     andlw   0x0f
     movwf   BCD_1
     swapf   PackBCD_3_2, W        ; extrae digito 3
     andlw   0x0f
     movwf   BCD_3
     ; limpia los high nibles en BCD_0 y BCD_2
     movlw   0x0f
     andwf   PackBCD_1_0, F
     andwf   PackBCD_3_2, F
 ;
halt_loop     
     nop     
     goto    halt_loop         
 	
; ***** Reset
	ORG     0x1FF       ; processor reset vector
	goto    start

	END
```


----------



## savad (Oct 25, 2015)

Leyendo "MANUAL DE MICROCONTROLADORES PIC" que consegui en la web, como no trae otra referencia mas alla del titulo no puedo darle el reconocimiento que se merece el author,  me encontre con esta muy eficiente rutina de Shift-Add3 para la conversion de números binarios a BCD. Ya la probe y trabaja muy bien y usa solo 47 localidades de la memoria del programa. Usa direccionamiento indirecto en una forma muy eficiente.

Aqui les dejo la rutina con mis comentarios añadidos

```
;****************************************************
;                                                   *
;    Filename:	    ShiftAdd3.asm                   *
;    Date:          Octubre 24,2015                 *
;    File Version:  1.1                             *
;                                                   *
;    Author:	    David Sanchez                   *
;    Company:       David Sanchez                   *
;                                                   * 
;                                                   *
;****************************************************
;                                                   *
;  Convierte número binario 16-bits a 5 digitos BCD *
;     Usando la técnica de Desplaza-Suma3           *
;****************************************************

	list      p=16c54             ; directiva para definir el procesador
	#include <p16c5x.inc>         ; definición  de las variables especificas
                                      ; del procesador 

	__CONFIG   _CP_OFF & _WDT_OFF & _RC_OSC
;
;***** DEFINICION DE CONSTANTES
;
#DEFINE ZERO  STATUS,2   ; Bit de zero
#Define Carry STATUS,0   ; Bit de carry
;
;***** DEFINICION DE VARIABLES RAM
temp      EQU 0x07   ; variable de trabajo
binH      EQU 0x08   ;número binario a convertir - high byte
binL      EQU 0x09   ;número binario a convertir - low byte
counter   EQU 0x0A   ; contador de número de bits (shifts) procesados
BCD_x_5   EQU 0x0B   ;resultado en packed BCD - digitos altos
BCD_4     EQU 0x0C   ;UnPackBCD - digito 4
BCD_4_3   EQU 0x0D   ;resultado en packed BCD - digitos de enmedio
BCD_2     EQU 0x0E   ;UnPackBCD - digito 2
BCD_2_1   EQU 0x0F   ;resultado en packed BCD - digitos bajos

; Programa principal 
; ***** Reset
	ORG     0x1FF       ; processor reset vector
	goto    start

	ORG     0x000
;
Main
  goto start

;
; --- subrutina B2_BCD
; Inicia la converción Binario a BCD con el algorithmo Desplaza-Suma3     
   ; loop por 15 rotaciones con la corrección "Suma 3"     

B2_BCD
    bcf Carry       ;Prepara carry a zero
    movlw .16       ; número de bits = 16
    movwf counter 
    clrf BCD_x_5    ; limpia registros
    clrf BCD_4_3    ; donde almacenamos
    clrf BCD_2_1    ; la conversión
    clrf BCD_2
    clrf BCD_4
    
    ;
Loop16
    rlf binL      ; desplaza todos los registros
    rlf binH      ; una vez a la izquierda
    rlf BCD_2_1   ; aqui el orden es importante
    rlf BCD_4_3   ; por el ajuste del bit Carry
    rlf BCD_x_5
;
    decfsz counter  ; Hemos terminado la conversion?
    goto adjDEC     ;    No:  asi que continua procesando
    retlw 0         ;    Si: ya terminamos asi que salte de la rutina
;
adjDEC
    movlw BCD_2_1   ; apunta a los digitos de menor peso
    movwf FSR      
    call adjBCD     ; y ajustalos
;
    movlw BCD_4_3   ; ahora los siguientes dos digitos 
    movwf FSR
    call adjBCD     ; y ajustalos
;
    movlw BCD_x_5   ; por último los 2 de mayor peso
    movwf FSR
    call adjBCD     ; y ajustalos
;
    goto Loop16
;
adjBCD
    movlw 3         ; Suma 3 al Low-nibble
    addwf 0,W       ;  w = reg_x_y + 3
    movwf temp      ;  y prueba el Low-nibble
    btfsc temp,3    ;   si es mayor a 7
    movwf 0         ;     Si: necesitamos ajustar el high-nibble 
	            ;        vuelve a leer reg_x_y  y
    movlw 30        ;     No: Suma 3 al High-nibble
    addwf 0,W       ;  w = reg_x_y + 30
    movwf temp      ;  y prueba el High-nibble
    btfsc temp,7    ; si es mayor a 7
    movwf 0         ;    Si: Suma 3 a reg_x_y
    retlw 0         ;    No: terminamos los ajustes asi que Regresa con w=0


start
; Escribimos valor que deseamos convertir 3AF3=15091  
    movlw   0xF3
    movwf   binL
    movlw   0x3A
    movwf   binH
    call    B2_BCD
   
     ; la converción ha terminado     
     movlw    0x0f                ; Ajusta a zero el High-nibble     
     andwf    BCD_x_5, F      ;  ya que no lo usamos
 ; * Ahora ya tenemos los 5 digitos BCD pero agrupados     
 ; *  Si es necesario procedemos a desagrurarlos     
 
 ; Unpack los digitos BCD     
     swapf   BCD_2_1, W        ; extrae digito 2     
     andlw   0x0f     
     movwf   BCD_2     
     swapf   BCD_4_3, W        ; extrae digito 4     
     andlw   0x0f     
     movwf   BCD_4     
     ; limpia los high nibles en BCD_2_1 y BCD_4_3     
     movlw   0x0f     
     andwf   BCD_2_1, F     
     andwf   BCD_4_3, F
 
     
	END
```


----------



## elelectronico2012 (Ene 9, 2017)

Hola a todos los amigos del foro soy nuevo en el foro y vengo a dejar mi primera aportación 
es un material para programar y aprenderse las 35 instrucciones del PIC16F84A con ejemplos prácticos y explicados uno a uno ya se que hay mucha información acerca de esto pero nunca esta demás tener algo extra 

También les recomiendo el libro  ¨ "Microcontrolador PIC16F84  Desarrollo de proyectos" 
 espero que le sea de ayuda a los que incursionan en este maravilloso mundo de los PIC un saludo al usuario META My respect for you


----------



## principiantetardio (Sep 13, 2022)

Hola, hace mucho no ando por aqui y veo que han cambiado algunas cosas .
Me esta complicando un programa en ASM y necesitaba asistencia de alguien con mas experiencia.
Adjunto el codigo y comento que el problema es que se cuelga la Interrupcion por TIMER1, funciona un par de veces y luego no reacciona mas.
No he podido encontrar la falla.
Algun almita caricativa que le de un vistaso y me oriente donde me equivoque ? 
GRACIAS


----------



## Lord Chango (Sep 13, 2022)

principiantetardio dijo:


> Hola, hace mucho no ando por aqui y veo que han cambiado algunas cosas .
> Me esta complicando un programa en ASM y necesitaba asistencia de alguien con mas experiencia.
> Adjunto el codigo y comento que el problema es que se cuelga la Interrupcion por TIMER1, funciona un par de veces y luego no reacciona mas.
> No he podido encontrar la falla.
> ...


Hola, te acomodo el código para que sea más facil de acceder.


```
;            CONTROL DE ARRANQUE Y FUNCIONAMIENTO DE GENERADOR HOGAR OBRERO           
;08/09/2022  CAMBIO EN LA INT CONTROL DE ACEITE POR CONTROL DE CONTACTO funciono bien el timer1
;pongo la cantidad de horas calculadas
;cambio el config con _XT_OSC
;10/09/2022  Prueba Conta2 = 1=1.4 min ; 10=16.40 min; 36=1 hora
;Dias 10=
;
    List    p=16F877A        ;Tipo de procesador
    include    "P16F877A.INC"    ;Definiciones de registros internos

    __CONFIG    _CP_OFF & _PWRTE_ON & _XT_OSC & _WDT_ON & _LVP_OFF & _BODEN_ON

cblock    0x20
    Delay        ;Variable de temporización
    Conta1        ;Para usar en TIMER1
    Conta2        ;Para usar en TIMER1
    Conta3        ;Para usar en temporizadores largos TIMER0
    Dias        ;Para usar en TIMER1
    Reintentos    ;
    Vigilia        ;
    MarchaMin    ;

endc

        org    0x00        ;Vector de Reset
        goto    Inicio
        
        
        org    0x04
        goto Int_TMR1

Inicio
;Esta configuracion me setea PORTD 0-1-2-3 como entradas digitales
;PORTD 4-5-6-7 mas PORTC 3-4-5-6-7 como salidas.
;Tambien prepara el prescaler para TIMER0
;Prepara los ADCs que se usaran en AN0, AN1 , AN2 , AN4 , AN5 , AN6 , AN7
;COMANDOS DEL LCD por puerto C
        bsf        STATUS,RP0    ;Selecciona banco 1
        bcf        STATUS,RP1    ;
                ;Configuracion de puertos
        movlw    0xFF        ;Puerto A y E se deben tomar como entradas setear en 1
        movwf    TRISA        ;
        movlw    0x07        ;
        movwf    TRISE        ;
        movlw    b'00001111'    ;Puerto D la parte baja va como entrada seteado en 1 y
        movwf    TRISD        ;la parte alta como salida seteada en 0
        movlw    0x00        ;
        movwf    TRISB        ;
        movwf    TRISC        ;
                        ;Configuracion del ADC
        movlw    b'10000001'    ;Justificación dcha 1,Seleccion de Clock 0,No usados 00
        movwf    ADCON1    ;(Todas Analogicas RA3/AN3/Vref+/Pin5) 0001
                    ;Si voy a usar RA o RE como Digital debo configurarlo en
                    ;el momento con "b'00000110' en ADCON1" y dps volver aqui
        bsf        PIE1,ADIE    ;Activa interrupción del convertidor ADC
                        ;Configuracion del TIMER
        movlw    b'10000111'    ;
        movwf    OPTION_REG    ;Preescaler de 256 asociado al TMR0
                        ;Configuracion del TIMER1 con interrupcion
        bsf        PIE1,0        ;Habilita el TIMER1
        
        bcf        STATUS,RP0    ;Selecciona banco 0
        clrf    PORTB        ;Limpia las salidas
        clrf    PORTC        ;
        clrf    PORTD        ;
                        ;Completo configuracion del TIMER1 con interrupcion
        bsf        T1CON,5        ;
        bsf        T1CON,4        ;Elijo Prescaler de 8 (maximo)
        bcf        T1CON,1        ;TIMER1 como temporizador con reloj interno (Fosc/4)
        bsf        T1CON,0        ;Baja la bandera de desborede del TIMER1
        bsf        INTCON,7    ;Habilita interrupciones globales
        bsf        INTCON,6    ;Habilita interrupciones perifericas
        bcf        PIR1,0        ;pone a cero la bandera de desborde del TIMER1
                ;Calculo de tiempo de interrupcion del TIMER1
        ;TMR1 = 65535-((Tiempo deseado (mS) * Fosc (KHz))/(Prescaler * 4))- 1
        ;53034 = 65535-((100*4000)/(8*4))-1 =0xCF24  Son 100mS
        movlw    0x0B
        movwf    TMR1H        ;Cargo parte alta del TIMER1
        movlw    0xDA       
        movwf    TMR1L        ;Cargo parte baja del TIMER1
        movlw    .200        ;Los valores utilizados en este programa estan mal
        movwf    Conta1        ;calculados por eso hubo que ajustar algunas repeticiones
        movlw    .1        ;Se deberia de haber usado 0xCF24
        movwf    Conta2        ; = 1 hora
        movlw    .1            ;Cargo la cantidad de hora del intervalo
        movwf    Dias        ;120hs / 24hs = 5 dias

;----------------------------------------------------------------------------------
Control
            clrwdt            ;Restaura/Limpia el WDT
        bcf        PORTD,4        ;Aseguro contacto desconectado
         bsf        PORTC,3        ;Conecto rele entrada se enclava con el de salida

        btfss    PORTD,1        ;Abrio detector de entrada?
        goto    Control        ;No, vuelvo a controlar
        call    Retardo1s    ;Si, hago un retardo de 1 segundo
        
        btfss    PORTD,1        ;Vuelvo a preguntar para confirmar
        goto    Control        ;No, Falsa alarma, vuelvo a controlar
        
        movlw    .3            ;Si, cargo Reintentos de arranque
        movwf    Reintentos    ;
Encendido
        movlw    .3            ;Cargo Vigilia que repite la espera a que se estabilice
        movwf    Vigilia        ;

        decfsz    Reintentos    ;decremento Reintentos y pregunto si llego a 0
        goto    Arranque    ;No, voy a Arranque
        goto    Alarma        ;Si voy a Alarma

Arranque
        bsf        PORTD,4        ;Conecto contacto
        call    Retardo1s    ;Espera 1 segundo antes de iniciar el arranque   
        bsf        PORTC,4        ;Acciono burro
        bsf        PORTC,5        ;Acciono cebador
        bcf        PORTC,3        ;Desconecto rele entrada se enclava con el de salida
        call    Retardo3s    ;Funciona burro y cebador 3 segundos
        bcf        PORTC,4        ;Apago burro
        bcf        PORTC,5        ;Apago cebador

Estabilizacion
        call    Retardo3s    ;Espera 3 segundos a que se estabilice
        btfss    PORTD,0        ;Controlo si tengo salida
        goto    Espera        ;No, no tengo salida, voy a Espera               
        btfss    PORTD,0        ;Si, tengo salida, Controlo de nuevo
        goto    Alarma        ;No, voy a alarma
        goto    Marcha        ;Si, tengo salida voy a la rutina de marcha

Espera
        decfsz    Vigilia            ;Decrementos Vigilia
        goto    Estabilizacion    ;No llego a Cero, Vuelvo a Estabilizacion
        goto    Encendido        ;Si, llego a Cero y no levanto presion de aceite

Marcha
        bsf        PORTC,6        ;Conecto rele salida que esta enclavado con el rele
                            ;de entrada e inicio bucle de marcha

Bucle10m
        movlw    .10            ;recargo marcha minima
        movwf    MarchaMin    ;

Marcha_Min
        call    Retardo1m    ;hago un retardo de 1 minuto que repetire 10 veces
        btfss    PORTD,0        ;Controlo si tengo salida
        goto    Alarma        ;No tengo salida
        decfsz    MarchaMin    ;Si, hay salida, decremento marcha minima
        goto    Marcha_Min    ;aun no agoto marcha minima

        btfsc    PORTD,1        ;Agoto bucle de marcha. Cerro detector de entrada?           
        goto    Bucle10m    ;No, recargo marcha minima y le doy otros 10 minutos
        goto    Detener        ;Si, voy a detener el generador y restaurar el sistema

Detener
        bcf        PORTD,4        ;Apago generador Desconecto contacto
        call    Retardo1s    ;Espera 1 segundo antes de iniciar transferencia
        bcf        PORTC,6        ;desconecto rele salida me habilita rele de entrada
        bcf        PORTC,4        ;aseguro burro apagado
        bcf        PORTC,5        ;aseguro cebador apagado
        bsf        PORTC,3        ;reconecto rele entrada me inhabilita rele de salida
        goto    Control        ;reinicio control de estado

;;---------------------------------------------------------------------------------
Alarma
        bcf        PORTD,4        ;Apago generador Desconecto contacto
        call    Retardo1s    ;Espera 1 segundo antes de iniciar transferencia
        bsf        PORTC,3        ;reconecto rele entrada
        bcf        PORTD,7        ;Apago aviso de prueba
        bcf        PORTC,6        ;desconecto rele salida
        bcf        PORTC,4        ;aseguro burro apagado
        bcf        PORTC,5        ;aseguro cebador apagado
         bsf        PORTC,7        ;conecto alarma audible
        call    Retardo1m    ;hago un retardo de 1 minuto
        bcf        PORTC,7        ;desconecto alarma audible
        goto    Control        ;       

;----------------------------------------------------------------------------------
;Temporizar 1/2 segundo mediante el TMR0. Este, con un preescaler de 256, evoluciona cada
;256uS. Se carga con 158 para realizar una temporización de 25mS. Los 50 mS se repiten 40
;veces para conseguir un segundo aproximadamente.
Retardo1s                        ; 1 segundo
        movlw    .40
        movwf    Delay
Delay_1        movlw    .158
        movwf    TMR0        ;Inicia el TMR0
        bcf        INTCON,T0IF    ;Restaura el flag del TMR0
Delay_2        clrwdt                ;Restaura/Limpia el WDT
        btfss    INTCON,T0IF    ;Han pasado 50 mS ??
        goto    Delay_2        ;Todavía no
        decfsz    Delay,F        ;Se ha repetido 10 veces ??
        goto    Delay_1        ;No
            return            ;Si, vuelve
;------------------------------------------------------------------------------
Retardo3s                    ; 3 segundos
        movlw    .255        ;SI BAJA BAJA EL TIEMPO 
        movwf    Delay
Delay_12    movlw    .210        ;Con 194 da 4 segundos. Con 210 da 3 segundos
        movwf    TMR0        ;Inicia el TMR0
        bcf        INTCON,T0IF    ;Restaura el flag del TMR0
Delay_22        clrwdt                ;Restaura/Limpia el WDT
        btfss    INTCON,T0IF    ;Han pasado 50 mS ??
        goto    Delay_22        ;Todavía no
        decfsz    Delay,F        ;Se ha repetido 100 veces ??
        goto    Delay_12        ;No
            return            ;Si, vuelve
;;--------------------------------------------------------------------
Retardo1m                ; 1 minuto
        movlw    .15            ;SI BAJA BAJA EL TIEMPO 
        movwf    Conta3        ;Con .255 aca aprox 70.75 min
Retardo3_1
        movlw    .255        ;SI BAJA BAJA EL TIEMPO
        movwf    Delay        ;
Delay_123    movlw    .195    ;Con .1 aca aprox 4 min. SI BAJA SUBE EL TIEMPO
        movwf    TMR0        ;Inicia el TMR0
        bcf        INTCON,T0IF    ;Restaura el flag del TMR0
Delay_223        clrwdt            ;Restaura/Limpia el WDT
        btfss    INTCON,T0IF    ;Cambio el Flag?
        goto    Delay_223    ;Todavía no
        decfsz    Delay,F        ;Se agoto el Delay ??
        goto    Delay_123    ;No

        decfsz    Conta3        ;Si,
        goto    Retardo3_1    ;
        RETURN

;----------------------------------------------------------------------
Int_TMR1
        clrwdt                ;Restaura/Limpia el WDT
Conta_1
        decf    Conta1,f    ;Decremento segundo contador y controlo
        btfsc    STATUS,Z    ;si desbordo mediante el Bit Z
        goto    Conta_2        ;Si voy a controlar el proximo contador,
        goto    Salgo1        ;No salgo recargando los valores para el TIMER
                            ;y los decrementados

Conta_2
        decf    Conta2,f    ;Decremento segundo contador y controlo
        btfsc    STATUS,Z    ;si desbordo mediante el Bit Z
        goto    Conta_3        ;Si voy a controlar el proximo contador, ya pasaron
                            ;Conta2 * Conta1 * interrupciones
        goto    Salgo2        ;No salgo recargando los valores para el TIMER
                            ;y los decrementados

Conta_3
        decf    Dias,f        ;Decremento segundo contador y controlo
        btfsc    STATUS,Z    ;si desbordo mediante el Bit Z
        goto    Fin            ;Si voy a controlar el proximo contador, ya pasaron
                            ;Dias * Conta2 * Conta1 * interrupciones
        goto    Salgo3        ;No salgo recargando los valores para el TIMER
                            ;y los decrementados


Salgo1
        bcf        PIR1,0        ;pone a cero la bandera de desborde del TIMER1
    ;Recargo los valores del TIMER1
        movlw    0x0B
        movwf    TMR1H        ;Cargo parte alta del TIMER1
        movlw    0xDA       
        movwf    TMR1L        ;Cargo parte baja del TIMER1
Ret_int       
        RETFIE
Salgo2
        movlw    .200        ;
        movwf    Conta1        ;Recargo el Conta1
        goto    Salgo1        ;

Salgo3
        movlw    .1            ;
        movwf    Conta2        ;Recargo el Conta2
        goto    Salgo2        ;
Fin
        movlw    .1    ;
        movwf    Dias        ;Recargo Dias

Pru_Func
        btfsc    PORTD,0        ;Mira si esta activo el PORTD,0 Hay señal de salida?
        goto    Ret_int        ;Si esta conectado esta en marcha NO HACE FALTA PRUEBA
                            ;Retorno de la Interrupcion sin hacer nada
        bsf        PORTD,7        ;No continuo en la interrupcion Aviso de Prueba

        movlw    .3            ;Cargo Reintentos de arranque
        movwf    Reintentos     ;

Arranque_2
        bsf        PORTD,4        ;Conecto contacto
        bsf        PORTC,4        ;Acciono burro
        bsf        PORTC,5        ;Acciono cebador
        call    Retardo3s    ;Funciona burro y cebador 3 segundos
        bcf        PORTC,4        ;Apago burro
        bcf        PORTC,5        ;Apago cebador

        call    Retardo3s    ;Espera 3 segundos a que se estabilice
        btfss    PORTD,0        ;Controlo si tengo salida
        goto    Espera_2    ;No, no tengo salida, voy a Espera_2
        call    Retardo1m    ;Si, tengo salida, fubciona 1 minuto
        bcf        PORTD,4        ;Apago generador Desconecto contacto
        bcf        PORTD,7        ;Desconecto aviso de prueba

        goto    Salgo3        ;Prueba terminada, salgo de la interrupcion

Espera_2
        decfsz    Reintentos    ;Decrementos Reintentos
        goto    Arranque_2    ;No llego a Cero, Vuelvo a Arranque_2
        goto    Alarma        ;Si, llego a Cero y no levanto presion de aceite
        
;-------------------------------------------------------------------------
            end
```

¿Estás seguro de que el problema es el Timer1 y no el WTD?¿Has probado desabilitar el WTD para ver si funciona todo correctamente?

No revisé el código en su totalidad porque es muy largo y me tendría que poner a indentarlo para tratar de entenderlo, pero mi consejo es que, si no podés _debugearlo_ en el simulador, trates de aislar el problema anulando de a partes el programa hasta encontrar adónde está la falla.

PD: Tenés comentado que T1CON,0 es la bandera de desborde del Timer1, cuando en realidad es el encendido del mismo. No te va a afectar al funcionamiento del programa, pero te puede confundir.


----------



## principiantetardio (Sep 13, 2022)

Hola, ya hice un monton de pruebas y nada. Voy a comenzar con una lista de variaciones para poder descartar prolijamente.
El resto del programa funciona correctamente, ya lo probe y no tuve inconvenientes.
En algun momento le anule el WDT , por eso figura que lo refresco en algunos lados, pero no di con la solucion.

Continuare hasta la victoria !!!!!!!


----------



## unmonje (Sep 13, 2022)

principiantetardio dijo:


> Hola, hace mucho no ando por aqui y veo que han cambiado algunas cosas .
> Me esta complicando un programa en ASM y necesitaba asistencia de alguien con mas experiencia.
> Adjunto el codigo y comento que el problema es que se cuelga la Interrupcion por TIMER1, funciona un par de veces y luego no reacciona mas.
> No he podido encontrar la falla.
> ...


Independientemente de la causa de su problema, le sugiero que escriba *TODA* la rutina de* interrupción* en el* BANCO 0 *de memoria y para *retornar* bien , se asegure que lo hace al BANCO CORRECTO controlando el BANCO de SALTO correcto constantemente durante el programa. Es tediosos pero vale la pena hacerlo para no llorar.

Cuando ocurre que se cuelga con  una *interrupción* es porque no se vuelve, porque la condición no se ha dado, o se vuelve al banco INCORRECTO, por mal control de las variables , a las que está sujeta. Tambien por desbordamiento de la PILA DEL  PC- STACK  ( ese chip tiene solo 3)

_------------------------------------------------------------------------------------------
       org    0x00        ;Vector de Reset
        goto    Inicio


        org    0x04
     goto Int_TMR1  *<-  QUITE ESTA LINEA y escriba la rutina de interrupción justo debajo*
-----------------------------------------------------------------------------------------

Int_TMR1
        clrwdt                ;Restaura/Limpia el WDT
Conta_1
        decf    Conta1,f    ;Decremento segundo contador y controlo
        btfsc    STATUS,Z    ;si desbordo mediante el Bit Z
        goto    Conta_2        ;Si voy a controlar el proximo contador,
        goto    Salgo1        ;No salgo recargando los valores para el TIMER
                            ;y los decrementados

Conta_2
        decf    Conta2,f    ;Decremento segundo contador y controlo
        btfsc    STATUS,Z    ;si desbordo mediante el Bit Z
        goto    Conta_3        ;Si voy a controlar el proximo contador, ya pasaron
                            ;Conta2 * Conta1 * interrupciones
        goto    Salgo2        ;No salgo recargando los valores para el TIMER
                            ;y los decrementados

Conta_3
        decf    Dias,f        ;Decremento segundo contador y controlo
        btfsc    STATUS,Z    ;si desbordo mediante el Bit Z
        goto    Fin            ;Si voy a controlar el proximo contador, ya pasaron
                            ;Dias * Conta2 * Conta1 * interrupciones
        goto    Salgo3        ;No salgo recargando los valores para el TIMER
                            ;y los decrementados


Salgo1
        bcf        PIR1,0        ;pone a cero la bandera de desborde del TIMER1
    ;Recargo los valores del TIMER1
        movlw    0x0B
        movwf    TMR1H        ;Cargo parte alta del TIMER1
        movlw    0xA       
        movwf    TMR1L        ;Cargo parte baja del TIMER1
Ret_int       
        RETFIE
---------------------------------------------------------------------------------------_


principiantetardio dijo:


> Hola, ya hice un monton de pruebas y nada. Voy a comenzar con una lista de variaciones para poder descartar prolijamente.
> El resto del programa funciona correctamente, ya lo probe y no tuve inconvenientes.
> En algun momento le anule el WDT , por eso figura que lo refresco en algunos lados, pero no di con la solucion.
> 
> Continuare hasta la victoria !!!!!!!


Use el MODO TRACE y va a encontrar el problema, tiene que poner puntos de *stop* en el programa para *debugearlo *y asi ver que es lo que está mal para retornar.


----------



## Lord Chango (Sep 13, 2022)

Me faltó poner eso en mi respuesta anterior.

Vi varias modificaciones de variables sin llamar al banco correspondiente (banksel _VARIABLE_). Me ha pasado de solucionar errores raros simplemente agregando el llamado al banco en las variables.

Para no renegar, siempre hago los llamados antes de modificar una variable, por más que ya haya llamado al banco antes.


----------



## principiantetardio (Sep 13, 2022)

Bien, unmonje estoy probando lo que decis, 

Use el MODO TRACE y va a encontrar el problema, tiene que poner puntos de *stop* en el programa para *debugearlo *y asi ver que es lo que está mal para retornar.
NO SE COMO


----------



## unmonje (Sep 13, 2022)

principiantetardio dijo:


> Bien, unmonje estoy probando lo que decis,
> 
> Use el MODO TRACE y va a encontrar el problema, tiene que poner puntos de *stop* en el programa para *debugearlo *y asi ver que es lo que está mal para retornar.
> NO SE COMO


Antes de ponerse a PROGRAMAR hay que aprender a usar los recursos del *editor/compilador/emulador*
EL emulador es quien le permite hacer esas COSAS.
No se apure porque aprender a usar la herramienta, me llevo varios* meses.*
Casi nada, si se compara el tiempo que le llevo a los creadores de la aplicación terminarla.

Al ponerse en MODO TRACE, se ejecuta un solo paso de programa a la vez y luego se continua con la siguiente instrucción,  sea esta verdadera o falsa y sus resultado. Si hubo un salto, salta a la linea de programa correspondiente segun el procesador que usted usa.

Le estoy dado una pista de solución, pero la VIA de ese tren, la tiene que encontrar vuestro cerebro.


----------



## principiantetardio (Sep 13, 2022)

unmonje dijo:


> Antes de ponerse a PROGRAMAR hay que aprender a usar los recursos del *editor/compilador/emulador*
> EL emulador es quien le permite hacer esas COSAS.
> No se apure porque aprender a usar la herramienta, me llevo varios* meses.*
> Casi nada, si se compara el tiempo que le llevo a los creadores de la aplicación terminarla.
> ...


Llevo un tiempo programando (no demasiado en verdad) y nunca tuve la ocasión o necesidad de usar una interrupcion por eso es que me esta costando, igual que me costo aprender todo lo anterior je je.
Me pondre a investigar para aprender un poco mas del emulador, lo suyo es un consejo muy apropiado.

Por ahora con las opiniones y pistas recibidas logre que funcione casi correctamente. Hice unas variaciones por mi cuenta, en vez de volver a Control vuelvo a inicio y asi hace casi lo que corresponde.
Y digo casi por que "algunas veces" al cumplir el ciclo del TIMER  se saltea la rutina que debe cumplir y comienza de nuevo. El problema lo veo en Pru_Func
Ya lo voy a vencer , solo es cuestion de aprender mas.
Gracias mil


----------



## switchxxi (Sep 13, 2022)

Revisa bien el código. Me maree un poco pero en la rutina de interrupción, cuando saltas a "fin" chequeas "btfsc  PORTD,0" si es 0 pasa a saltar a varios lados y de ahí se va al programa inicial, no llega a "salir" nunca de la interrupción y por salir me refiero a dar por terminada esa etapa.

Puede que ese no sea el problema pero ahí veo un error feo.

Resumido: Puede pasar a "Alarma" y de ahí se va a "Control" sin ver un "RETFIE" jamás.

Además, busca en internet y añade la rutina para salvar el registro "STATUS" y "W" al entrar a la interrupción, evitara que el programa se vuelva loco. Este puede ser el segundo problema.


----------



## principiantetardio (Sep 13, 2022)

switchxxi dijo:


> Revisa bien el código. Me maree un poco pero en la rutina de interrupción, cuando saltas a "fin" chequeas "btfsc  PORTD,0" si es 0 pasa a saltar a varios lados y de ahí se va al programa inicial, no llega a "salir" nunca de la interrupción y por salir me refiero a dar por terminada esa etapa.
> 
> Puede que ese no sea el problema pero ahí veo un error feo.
> 
> ...


Que torpe, tenes razon, podria ir a salir de la interrupcion, la alarma en este punto no es relevante.
Pregunta sin pensar, no puedo poner un RETFIE en la rutina de alarma que esta en el programa principal?
Ya lo vere, por ahora voy avanzando gracias a estas pistas de ayuda.
Gracias


----------



## switchxxi (Sep 13, 2022)

principiantetardio dijo:


> Pregunta sin pensar, no puedo poner un RETFIE en la rutina de alarma que esta en el programa principal?



Si quieres que el programa haga cualquier cosa por desbordarse la pila, adelante.... 

Respuesta seria: Nooooooooooo.


----------



## principiantetardio (Sep 13, 2022)

switchxxi dijo:


> Si quieres que el programa haga cualquier cosa por desbordarse la pila, adelante....
> 
> Respuesta seria: Nooooooooooo.


Era, SIN PENSAR


----------



## unmonje (Sep 13, 2022)

principiantetardio dijo:


> Llevo un tiempo programando (no demasiado en verdad) y nunca tuve la ocasión o necesidad de usar una interrupcion por eso es que me esta costando, igual que me costo aprender todo lo anterior je je.
> Me pondre a investigar para aprender un poco mas del emulador, lo suyo es un consejo muy apropiado.
> 
> Por ahora con las opiniones y pistas recibidas logre que funcione casi correctamente. Hice unas variaciones por mi cuenta, en vez de volver a Control vuelvo a inicio y asi hace casi lo que corresponde.
> ...


Preste atención a lo que le sugieren los colegas.....El registro * "STATUS" , "W"* y cualquier otro en uso, deben ser siempre* salvados *con las instrucciones PUSH & POP en todos los procesadores del UNIVERSO para atender una interrupción


----------



## Dr. Zoidberg (Sep 13, 2022)

principiantetardio dijo:


> Llevo un tiempo programando (no demasiado en verdad) y nunca tuve la ocasión o necesidad de usar una interrupcion


Con lo que comentas y el código que escribís, te recomiendo aprender un lenguaje de alto nivel... específicamente el lenguaje C. En esos lenguajes las estructuras de control te impiden hacer el desastre de saltos a cualquier lado que estas haciendo.
Y si has programado en assembler sin usar interrupciones...es que has programado poco y mal.


----------



## principiantetardio (Sep 13, 2022)

Dr. Zoidberg dijo:


> Con lo que comentas y el código que escribís, te recomiendo aprender un lenguaje de alto nivel... específicamente el lenguaje C. En esos lenguajes las estructuras de control te impiden hacer el desastre de saltos a cualquier lado que estas haciendo.
> Y si has programado en assembler sin usar interrupciones...es que has programado poco y mal.


----------



## el990025 (Sep 14, 2022)

switchxxi dijo:


> Revisa bien el código. Me maree un poco pero en la rutina de interrupción, cuando saltas a "fin" chequeas "btfsc  PORTD,0" si es 0 pasa a saltar a varios lados y de ahí se va al programa inicial, no llega a "salir" nunca de la interrupción y por salir me refiero a dar por terminada esa etapa.
> 
> Puede que ese no sea el problema pero ahí veo un error feo.
> 
> ...


El compañero tiene razón en que debes salvar el registro STATUS y el registro W, sin esto alteras el contenido de los registros en la subrutina y cuando retorna tiene valores diferentes. En las hojas de especificaciones viene el código que se recomienda para guardar STATUS y W.
Al inicio va el nombre de tu rutina de interrupcion y al final RETFIE.


----------

