De ASM a JALv2. Tutorial.

Hace tres días atrás, volví al mundo de la electrónica :) luego de haber estado sin tocar nada desde hace tres años; a excepción de alguna reparación mínima o la creación de unos circuitos para la restauración de mi tocadisco que pronto actualizaré.

He vuelto con el proyecto de invernadero que casualmente, fue en lo último en lo que trabajé.

¿Por qué cuento todo esto? porque me he visto en la necesidad de tratar de entender mi pasos, avances y mis programas, para poder seguir avanzando, y no volver a cometer los mismo errores.

Con esto viene el primer concejo, y REGLA DE ORO que deseo transmitirles SIEMPRE COMENTAR LOS PROGRAMAS
Por suerte, es algo que yo siempre hice, hasta incluso anoto los pasos a seguir para hacer dicha tarea. Esto es un ejemplo de lo que hablo:

;MEDIMOS LA HUMEDAD.
;------------------
;PASOS A SEGUIR.
;
;1- CONFIGURAR EL CANAL A SENSAR.
;2- MEDIR EL ADC DE LA HUMEDAD Y GUARDAMOS EL VALOR EN RAM
; TENEMOS QUE APLICAR LA FÓRMULA SIGUIENTE PARA OBTENER LA HUMEDAD HR = (Vout - 0,8) / 0,031
;3- PARA ELLO, DEVEMOS MULTIPLICAR EL ADC POR 196 PARA PASARLO A TENSIÓN (EN REALIDAD DEBERÍA SER 195 PERO CON ESTE, EL ERROR ES MAS GRANDE).
;4- LUEGO LO VOLVEMOS A MULTIPLICAR POR 1.000 PARA TRABAJAR CON LA SIGUIENTE FÓRMULA HR = (Vout - 800) / 31
;5- APLICAMOS LA FORMULA HR = (Vout - 800) / 31,
;5- EL RESULTADO LO DIVIDIMOS POR 10.000 PARA OBETENER LA HUMEDAD RELATIVA EN 2 DÍGITOS (O 1.000 PARA OBTENER 3 DÍGITOS, ANALIZAMOS MAS ADELANTE ESTO)
;6- LO GUARDAMOS EN LA RAM
;7- TOMAMOS 42 MUESTRAS
;8- HACEMOS EL PROMEDIO, PARA ELLO SUMAMOS LAS 42 MUESTRAS ENTRE SI Y LUEGO LA DIVIDIMOS POR 42
;9- EL RESULTADO DEL PROMEDIO LO GUARDAMOS EN LA RAM humedad

Nunca estuve más contento de haber echo esto, que al principio me pareció una gran estupidez y pérdida de tiempo, ahora me quiero auto-besar.

Si bien no soy una persona experta en programación, sino más bien un aficionado en la programación con experiencia casi nula; recuerdo más como se programa en ASM (que fue el primer lenguaje que aprendí para los PIC y en mi vida) que el JALv2. Uno diría, seguí en ASM y listo; pero sería tonto, porque jamás pude ni intenté hacer un programa en ASM y conectar el pic al USB, pero que en JALv2, se hace muy simple; al igual que con cualquier lenguaje de alto nivel.
Así que, el siguiente tutorial será un equivalente del ASM a JALv2. Intentaré ser lo más claro posible y dar ejemplos.

Para finalizar mi primer mensaje de este tema, quiero pedirles que participen; me corrijan; me den concejos; o cualquier otra cosa que haga, de este tutorial, una herramienta útil para cualquiera.



Lo primero que hay que hacer, es descargar la última versión del compilador JALv2 e integrarlo al entorno IDE MPLAB

Para los que quieran ir aprendiendo a programar en JALv2, les dejo este tutorial donde hay muchos ejemplos para hacer. Se los recomiendo.

Software, herramientas y ejemplos de programación para JALv2

NOTA MUY IMPORTANTE:

Si ustedes tienen una entrenadora o alguna placa lista para practicar la programación del PIC con este lenguaje; y por consiguiente es distinto al mío, tengan la precaución de cambiar los pines a utilizar según su placa; y garanticen que no se produzca un cortocircuito al hacer un cambio. Por supuesto que no me hago cargo de cualquier cosa que se les pueda romper. Esa es su responsabilidad.

Eso es todo por hoy. En el siguiente mensaje, empezaremos a ver como pasar de ASM a JALv2.
 
Última edición:
Variables; constantes y posiciones de memoria RAM.

En ASM es raro hablar de variables y/o constantes sino que se habla de posiciones de memoria. Esto es así, porque es el usuario (el que programa) como se van a usar las diferentes posiciones de memoria. El usuario puede tratar a una posición de memoria como constantes, y luego cambiarla; lo que haría perder el estado de constante para convertirse en una variable.
En JALv2, esto no pasa. Si el usuario nombra a una posición de memoria (o varias) como constante, así quedará por todo el programa. Si uno intenta modificar una constante, el compilador dará un error; pues para ello tenemos variables, en las que una posición de memoria puede cambiar todo el tiempo. Justamente una variable no garantiza que el valor de la memoria se mantenga por todo el programa. En ASM, es el usuario quien debe tomar la precaución de no modificar una posición de memoria que se necesita en algún momento.

Veamos como se nombran y trabajan las posiciones de memoria.

ASM

El MPASM, o sea, el compilador de assembler de Microchips, acepta dos formas básicas de nombrar posiciones de memoria RAM con nombres.

La primer manera es:

CBLOCK H'xx'

Lo que hace es renombrar las posiciones de memoria, en forma consecutiva a partir de lo especificado en H'xx'
Veamos un ejemplo.

CBLOCK H'20'
rotar
subir
bajar
ENDC

rotar es la posición de memoria 0x20
subir es la posición de memoria 0x21
bajar es la posición de memoria 0x22

ENDC indica que se terminó de renombrar posiciones de memoria. Su uso es obligatorio.

La segunda opción es:

nombre EQU H'xx'

A diferencia de la anterior, aquí solo se puede renombrar una posición de la RAM de una vez por vez; teniendo que repetir toda la misma sentencia por cada posición de memoria a renombrar. Por ejemplo

subir EQU H'30'
bajar EQU H'25'
rotar EQU H'50'

Como podrán observar, se puede asignar nombres en forma desordenada, y no como la anterior que es consecutiva.
Hay que prestar atención a que posición de memoria estamos nombrando, porque sin querer podemos estar trabajando sobre un registro sin darnos cuentas. El usuario tiene que asegurarse de que esté haciendo la reserva en la posición de memoria asignada para el usuario.

JALv2

Hay que tener en claro que es lo que se necesita, si los datos de la posición de memoria a usar sea momentánea o fija.
En el caso de que sea fija, la posición de memoria deberá ser llamada como constante, pero si es momentánea o varía su valor, se debe llamar variable.
Veamos un ejemplo de su uso.

var byte subir
var byte rotar

const byte bajar

Noten que aquí, no se asigna la dirección de la memoria. JALv2, es el que se encarga de asignar la dirección de la memoria a renombrar.

Como se han dado cuenta, var indica que subir/bajar es una variable; mientras con bajar es una constante.

Byte indica el tamaño de la posición de memoria a usar.
Algo que hay que tener en cuenta es que el tamaño de cada posición de la RAM de un PIC, es de un byte (8 bit), pero el compilador nos permite nombrar una variable más grande que un byte; por ejemplo del tamaño de un WORD. ¿Cómo hace esto? muy fácil, utiliza tantas posiciones de memoria consecutivas según lo especificado.
Por ejemplo VAR WORD TEMPORAL.
La variable temporal, va a ocupar dos posiciones de memoria; ya que word equivale a dos BYTE.
 
Continuación de variables, constantes y posiciones de memoria RAM

JALv2

Veamos a continuación, de que tamaño pueden ser las variables o constantes y sus signos.


BIT1 1 bit boolean value 0..1
SBIT1 1 bit signed value -1..0
BYTE1 8 bit unsigned value 0..255
SBYTE1 8 bit signed value -128..127
WORD 16 bit unsigned value 0..65,535
SWORD 16 bit signed value -32,768..32,767
DWORD 32 bit unsigned value 0..4,294,967,296
SDWORD 32 bit signed value -2,147,483,648..2,147,483,647
Recuerden que esto lo maneja el compilador; y no podremos saber en que posición de memoria estamos trabajando. De echo, no es importante saberlo. Para ello se le asigna nombre para poder recordarlo mejor. Lo importante, es no sobrepasar la cantidad de memoria utilizada según la capacidad del pic a trabajar.

Hay que saber también, que como el compilador trata de optimizar todo, incluso el uso de la memoria de programa, hay que indicarles que deseamos hacer para que no haga nada indebido. Es muy importante indicar todo y no dejar que el compilador adivine. Si hacen esto, puede llegar a pasar que el programa no ande bien y no puedan encontrar el error.

Por ejemplo, en el manual de JALv2, dice que si a una variable del tipo booleano (1;0) se asigna 0; el valor de dicha variable (BIT) será 0; pero que si le agregamos un valor distinto a cero, por ejemplo 32 (no importa si es en decimal, hexadecimal, octal, ect) el compilador lo asignará 1. Por lo tanto, hay que prestar atención a estos detalles.
Las posiciones de memorias que nombramos, pero que no asignamos ningún valor, serán cargadas con valor 0; y si no la utilizamos nunca, no serán compiladas.

Constantes

Las constantes, tiene además una función más. Se les puede o no agregar lo siguiente:


Secuencia Valor
"\0qqq" Constante Octal (octal constant)
"\a" Campana (hace sonar un tono de aviso por los parlantes)
"\b" Espacio (backspace) es el valor 0x20 en ASCII
"\f" Formateado (formfeed)
"\n" Avance de linea (line feed)
"\r" Retorno del carro/cursor (carriage return)
"\t" Tabulación horizontal (horizontal tab)
"\v" Tabulación vertical (vertical tab)
"\xdd" Constante hexadecimal (hexidecimal constant)

ASM

En Assembler, esto no existe. Todo esto se hace a nivel de programación. De echo, en JALv2 (como en cualquier otro lenguaje de alto nivel) esto también se traduce en líneas de programa, solo que es el compilador lo hace por nosotros.
 
Última edición por un moderador:
JALv2

Variables volátiles (var volatile)

Esto es un verdadero dolor de cabeza tanto para ser explicarlo como para entenderlo.
De hecho, a la fecha, me parece que lo he entendido; pero lamentablemente no estoy seguro.
He estado buscando explicación por Internet, y he dado con las siguientes páginas donde lo explican mejor:

Página 1
Página 2

Algo que hay que dejar en claro, es que la palabra VOLATILE no es una instrucción; por lo tanto no aparece en ASM pero si se trabaja; incluso sin que el usuario lo sepa. Esto es a nivel de compilación.
Las ventajas que tienen los lenguajes de alto nivel, es la optimización de nuestro programa escrito.
Incluso, hay compiladores en los que se puede deshabilitar la optimización y ver si nuestro programa funciona.
Por ejemplo, tenemos dos variables asignadas de tamaño byte y llamadas AA y ZZ respectivamente.
De acuerdo a nuestro programa, estas dos variables pueden llegar a compartir la misma posición de memoria RAM.
Otra optimización muy importante, es la generación del código en lenguaje de máquina, y es en ASM donde el usuario debe buscar una buena optimización.

Volatile, que puede ser traducido como temporal (ya que se evapora), es en realidad la indicación a nuestro compilador que deseamos que NO se debe optimizar. En el ejemplo de las dos variables anteriores (AA y ZZ) si las declaramos como volatile obligamos al compilador que no compartan la misma ubicación en la RAM. (Por dar un ejemplo).

NOTA: En la optimización de las variables, no quiere decir que todas las variables que creamos vayan a compartir una sola dirección en la memoria RAM.
Esto lo analizará el compilador, viendo si se afectan unas con otras; si se cruzan o no, ect.

A nivel de generación del código en lenguaje de máquina, lo que también se garantiza es que no se modifique dicha varaible declarada volatile.
Por ejemplo, podemos hacer lo siguiente en ASM:

AA equ 0x20 ; AA ubicada en la posición de memoria RAM 0x20
MOVLW 0X10 ; Cargo con W 0x10
MOVWF AA ; Y se lo paso a AA
MOVLW 0x09 ;Cargo con W 0x09
ADDWF AA,F ; Sumo 0x09 + 0x10 (AA) y el resultado lo guardo en AA

O lo siguiente:

AA equ 0x20 ; AA ubicada en la posición de memoria RAM 0x20
MOVLW 0X10 ; Cargo con W 0x10
MOVWF AA ; Y se lo paso a AA
MOVLW 0x09 ;Cargo con W 0x09
ADDWF AA,W ; Sumo 0x09 + 0x10 (AA) y el resultado lo guardo en W

No hay nada raro en hacer esto, y lo escribiremos de una manera u otra si necesitamos o no, que la variable AA sea o no sea modificada.
Al menos en JALv2 (y versiones anteriores) esto no se puede elegir.
En ningún momento podemos optar si queremos que un resultado sea guardado en el registro de trabajo en la memoria RAM. De hecho, cada vez que hagamos una operación matemática, siempre guardaremos el resultado en la RAM:

X = X +1
X = C + B
X = B + X
W = W + X (W es una variable y no el registro de trabajo).

Concejos:

Todas las variables que tiene un efecto (cuando se lee o se escribe) que es leída fuera del programa debe ser declarada volátil. Ejemplos evidentes son los registros de funciones especiales.
Para mostrar el caso extremo: un programa que no accede a las variables volátiles también podría estar vacío, ya que no tiene efectos leídos. En otras palabras, si no se la declara volatile, el compilador lo puede omitir no generándola y haciendo que nuestro programa funcione mal y no saber por que es.

Ante la duda, declarar las variables como volatile; o al menos las que más se usan o son de mayor importancia.
Por ejemplo, la variable que se le carga un valor para ser enviado por el puerto serial; o donde se le cargan los valores de lectura a través del puerto serial.

Si un programa no funciona como debe ser y no es posible encontrar el error, hacer las variables volátiles. Esto es un motivo por el cual, la creación de variables deben ser hechas todas juntas, o en el mismo lugar y no desparramado por todo el programa.

Hoy trataré de terminar con las variables y constantes.
 
Última edición por un moderador:
Aún no se ha terminado con las variables y constantes en JALv2.
Vimos como el compilador de JALv2, trata de ahorrar toda posición de memoria RAM para no derrochar posiciones de memoria. Incluso, si creamos una variable pero no la usamos, no la compilará. Esto no es algo maravilloso, ya que nombrar una posición de RAM no la reserva. En ASM pasa lo mismo; yo puedo asignarles nombres a todas las posiciones de la RAM pero eso no quiere decir que las vaya a usar a todas.

La optimización en ASM pasa por el programador. Alguien que anota para que es cada uso de posiciones de RAM podrá usar muy poca memoria. Todo dependerá de cuantos valores en RAM necesitamos que se mantengan intactas a lo largo de nuestro programa. Cualquier programador en ASM nombrará una o varias posiciones de la RAM como temporal1; temporal2, ect. (o parecido) en que solo la usará para hacer operaciones matemáticas; por dar un ejemplo. Es normal que estas posiciones de memoria la utilicen muchas rutinas y/o subrutinas. Justamente esto, es una optimización por parte del programador.

NOTA y ACLARACIÓN A TENER EN CUENTA: Si tienes un pic que trabaja por años (o cualquier otro microcontrolador), notarán que de un momento a otro empieza a fallar, y da la impresión de ser sin motivo alguno. El hardware anda bien, no hay interferencias y aún así no hay explicaciones para saber la falla. Son esas fallas en la que se soluciona cambiando el pic. Lo que puede estar fallando es que estas posiciones de la RAM que se utilizan muchísimos, leyendo y escribiendo, han sobrepasado su límite de escritura/lectura. En ASM se soluciona fácil. Solo se debe asignar estas posiciones de la RAM a otra dirección en donde no se utilicen y listo. En JALv2, no es posible hacer esto. Pero hay otra manera. Se puede acceder al ASM generado por el compilador, cambiar las posiciones de memoria y compilar el ASM modificado y regrabar al pic.
Esta solución es temporal, pero útil cuando no se dispone de otro pic de iguales características y el hardware debe ser arreglado enseguida. Es temporal porque será cuestión de tiempo en que empiece a fallar otra posición de la RAM.

Continuemos con las variables y constantes.

Alias; Locación; Tablas o arreglo (array).

JALv2

Alias.

Esto es aplicable a VARIABLES, CONSTANTES y SUB-PROGRAMAS (las veremos más adelante).
Lo importante a tener en cuenta sobre un alias, es que solo se hereda la DIRECCIÓN, y si una constante o variable a sido asignada como volatile, esto no se hereda. Por lo tanto la indicación de la NO OPTIMIZACIÓN, no será ejecutada.

Su aplicación es muy sencilla. Se coloca un IS entre las variables, constantes o subprogramas y listo.

var byte volatile i2c_clock is pin_a3
var byte volatile i2c_data_in is pin_a4
var byte volatile i2c_data_out is pin_a4_direction

La variable i2c_clock, hereda la dirección de pin_a3; lo mismo le sucede a 12c_data_in que hereda la dirección de pin_a4; e i2c_data_out que hereda la dirección de pin_a4_direction.
Noten que, si bien pin_a3 pudo haber sido declarada como volatile, 12c_clock no lo heredará; entonces, en la creación de dicha variable, se la declara como volatile.

Supongamos lo siguiente:

var byte volatile carga -- Se crea la variable carga como byte y que no sea optimizada.

var byte recarga is carga -- Se crea la variable recarga como byte y hereda la dirección de carga; pero recarga no es volatile.

Pero si hacemos lo siguiente:

var byte volatile recarga is carga -- Se crea la variable recarga como byte, es volatile y hereda la dirección de carga.

Locación

El compilador nos permite dar la dirección de una variable o constante dentro de la misma variable o constante respectivamente, o sea, a que bit hacemos referencia; o incluso podemos darle la dirección a una memoria RAM. La forma de hacerlo es interponiendo la palabra AT entre las variables o constantes.
Por ejemplo:

var byte volatile port_a at 0x06 -- pic IO port A
var bit volatile status_z at 3 : 2 -- pic zero flag

El ejemplo no requiere de explicación, ya que es muy evidente. No obstante, para aquel que está aprendiendo, lo explicaré.

Creamos la varaible byte port_a y le asignamos la dirección 0x06 (hexadecimal). Este corresponde a las entrada y salida del puerto A.
Para el segundo ejemplo, vemos que creamos una variable bit con el nombre status_z y lo asignamos a la dirección 3 (0x03 (status)) bit 2.

En este ejemplo, también se lo puede escribir así, y que será más legible: var bit volatile _z at _status : 2

Incluso, se pueden dar múltiples direcciones:

var volatile byte _status AT {0x0003, 0x0083, 0x0103, 0x0183} -- noten que se han encerrado en llaves para indicar varias direcciones.

Tablas o arreglos (array)

Las tablas pueden ser con variables o con constantes. No son iguales unas a otras y hay que tenerlo en cuenta. Por lo general, es recomendable hacer tablas con constantes; pero la circunstancia nos dirán cual utilizar.
Si utilizamos tablas con constantes, las mismas se grabarán en la memoria de programa, tal como en ASM; pero si usamos tablas con variables, las mismas serán echas en la memoria RAM, que también se utilizan en ASM.

Tablas con constantes.

Es la más común y la más usada. Está limitada a un tamaño máximo de 255 valores.
Su estructura es simple:

const byte mensaje [5] = {"M", "2", 24, 3, 10}

Cuando creamos una tabla, no es obligatorio darle inmediatamente los valores, pudiendo crearlo de la siguiente manera:

const byte mensaje [5]

Como notarán, creamos un arreglo o tabla llamado mensaje con una longitud 5. Como mensaje es un byte, quiere decir que utilizamos 5 posiciones de memoria de programa.

Hay dos cosas a saber al momento de crear una tabla o array. Si al momento de crearla no le asignamos los valores, es obligatorio asignarle un tamaño; pero si al momento de crearlo le asignamos los valores, no es necesario indicar su tamaño. Por ejemplo:

const byte mensaje [5] -- Aquí indicamos el tamaño de la tabla
const byte mensaje [] = {"M", "2", 24, 3, 10} -- Aquí no le asignamos el tamaño de la tabla ya que le asignamos los valores. El compilador sabrá que el tamaño máximo es de 5 posiciones de memoria de programa.

Para leer y/o escribir en una dirección del array, se procede de una forma muy sencilla. Podemos crear una constante y asignarle un valor; luego crear una variable o constante y guardar el dato.

Leer un array

var byte dato
const byte direccion
direccion = 2

dato = mensaje[direccion] -- En la variable dato se recuperará el valor que hay en la dirección 3 del array mensaje. Dato obtendrá el valor 24

¿Por qué 3 y no 2? porque la dirección del array arranca desde el 0. No confundir con el tamaño del array que si arranca del 1.

Escribir un array

Es muy parecido. Veamos

var byte dato
const byte direccion
direccion = 2
dato = "H"

mensaje[direccion] = dato -- Ahora la posición 3 del array mensaje tiene el valor de H (ascii) y no 24.

Por supuesto que el index (lo que está entre corchetes) se puede escribir con el número directamente.

var byte dato

dato = mensaje[2]

Tablas con variables.
Está limitada al tamaño del banco de la memoria RAM. Atento a no abusar de su tamaño, ya que podremos quedarnos sin memoria.

Al igual que las tablas echa con constantes, se lee y se escribe de la misma manera. Hay que recordar que el index (lo que está entre corchetes) debe ser una constante.

var byte mensaje [5] = {3, 25, "H", "C", 0}
var byte mensaje = {3, 25, "H", "C", 0}

var byte mensaje [5]

dato = mensaje[direccion] -- Recordar que el index (direccion en este caso), debe ser una constante.

mensaje[direccion] = dato
 
Última edición:
Algo que me faltó aclarar, es que JALv2 no acepta ARRAY de bit. Si hay librerías para hacerlo; pero es un trabajo para el CP del PIC; por lo que su uso debe ser cauto; ya que puede consumir memoria y tener que cambiar de pic, cuando con un poco de ingenio se puede solucionar sin esta opción. No obstante, si es necesario su uso, pueden buscar en la carpeta de librería la librería de array con bit.

Al principio del tutorial, habíamos visto el tamaño de las variables y constantes que podíamos utilizar; pero fue muy superficial su explicación. Hoy veremos más detalladamente las diferentes opciones de tamaño y signos de las variables y constantes.

Los números, están expresados en decimales:

[tt]BIT 0 .. 1
BYTE 0 .. 255
SBYTE -128 .. 127
WORD 0 .. 65.535
SWORD -32.768 .. 32.767
DWORD 0 .. 4,294,967,296
SDWORD -2,147,483,648 .. 2,147,483,647[/tt]

Pero el compilador, puede aceptar que lo escribamos de la siguiente manera:

var byte*2 aa -- Esto equivale a word (var word aa)

De la misma manera podemos escribir:

byte*4 que es un dword

sbyte*2 que es un sword

sbyte*4 que es un sdword

Para el bit, es diferente. Poner bit*2 indica que se trabaje con dos bit en una variable. Como una posición de RAM del PIC es de 8 bit (byte) implica que usaremos solo dos bit de una posición de la RAM y serán los dos menos significativos.

Por ejemplo:

var bit*2 cc

El compilador realiza el siguiente trabajo interno

cc = (valor & 0x03)

Lo que realiza es una máscara a la posición de la RAM con la función AND. O sea:

cc = 0b00110101 (53 en decimal)

Como CC es bit*2; el compilador realiza lo siguiente:

[tt] 0b00110101
and
0b00000011
---------------
0b00000001[/tt]

cc = 0b01 (0x01) y directamente ese valor será asignado a cc, aunque trabaje con dos bit. Recordemos que, si tenemos una variable/constante declarada como bit, cualquier valor asignado distinto de 0; será asignada como 1:

var bit bb = 25

El compilador, internamente lo cambiará y lo asignará como 1, por lo que bb = 1

Casting

En algún momento de nuestro programa, será necesario pasar una variable a otra; y que ambas variables sean de distintos tamaños.

VAR WORD xx
VAR BYTE yy

Si hacemos lo siguiente:

yy = xx

El compilador nos dará un aviso de advertencia (warning) informando la asginación a un tipo más pequeño y que el trucamiento es posible (warning: assignment to smaller type; truncation possible)
Para eliminar la advertencia debemos proceder de la siguiente manera:

yy = byte(xx)

Pero ¿como lo toma el compilador?
Lo que hace es la eliminación de los 8 bit más significativos de la variable word, para pasarlo al más pequeño. Por ejemplo:

xx = 0x0eff
yy = xx -- yy obtendrá como valor 0xff, eliminando 0x0e

En definitiva, cuando tengan que pasar de una variable más grande a una más chica, se tienen que asegurar que entre en el más pequeño. Es normal usar variables grandes para hacer operaciones matemáticas. En este caso, el resultado de esa operación matemática, tendrá que entrar en la variable más chica, ya que sino perderemos datos valiosos.

Pero esto es solo un caso a la hora de usar casting.
Podemos hacer lo siguiente:

VAR WORD xx
VAR BYTE yy

xx = yy * yy -- Esto no es lo uno realmente quiere.

Recuerde que un operador sólo ve sus dos operandos, no tiene otro contexto. Digamos que el valor de yy es 255.
En este caso xx será asignado un valor de 1: los ocho bits inferiores del resultado. 255 * 255 = 65.025 ( 0b1111111000000001)

xx = word(yy) * word(yy)

En este caso el valor de yy es promovido a una palabra, por lo xx se le asignará 65.025 que es más probable a lo que realmente deseaba hacer.
 
Y para finalizar este tema; veremos como se pueden ingresar valores a nuestras variables y constantes. Son parecidos que en ASM, así que las mencionaremos por separados.

JALv2

Tipo de valor | Como se escribe.
Decimal | 12
Hexadecimal | 0xC
Octal | 0q14
Binario | 0b1100
ASCII | "A"

ASM

Tipo de valor | Como se escribe.
Decimal | .12
| d'12'
| D'12'
Hexadecimal | 0xC
| h'c'
| H'c'
Octal | 0q14
| q'14'
| Q'14'
Binario | 0b1100
| b'1100'
| B'1100'
ASCII | "A"

Con respecto al código ACSII, en ambos casos, el compilador remplaza dicho valor ingresado, por el correspondiente al código ASCII. Esto nos ahorra el trabajo de buscar el valor que deseamos. Además, el código se hace más legible.
 
PRAGMAs

Los PRAGMAs son directivas para indicarle al compilador, como vamos a trabajar, con qué vamos a trabajar y de que manera vamos a trabajar.
Algún PRAGMA omitido, o mal definido; pueden dar numerosos errores y/o un comportamiento erróneo de nuestro pic. Cuando digo una mala definición, no hablo de un error de sintaxis, sino de habilitar algo, cuando en realidad deberíamos deshabilitarlo.

Especificación del PIC a usar (TARGET CHIP)

ASM

LIST P=16F877A ; Utilizaremos el PIC 16F877A

JALv2

PRAGMA TARGET CHIP nombredelpic

Donde nombredelpic se debe reemplazar por el nombre del pic. Por ejemplo:

PRAGMA TARGET CHIP 16F877A -- Utilizaremos el PIC 16F877A

Por supuesto, que debemos reemplezar el nombre del PIC por el que vamos a utilizar. Hay otra manera de hacerlo:

CONST _target_chip = cexpr

CONST _target_chip = 18F4550


No debemos preocuparnos por especificar el tipo de PIC a utilizar porque el compilador no lo utiliza para hacer la compilación.

Especificación del reloj a usar (TARGET CLOCK)

Se debe indicar en Hz la frecuencia del reloj, con que va a trabajar nuestro microcontrolador.

JALv2

PRAGMA TARGET CLOCK cexpr

PRAGMA TARGET CLOCK 20_000_000 -- Utilizaremos una frecuencia de 20Mhz.

PRAGMA TARGET CLOCK 48_000_000 -- Utilizaremos una frecuencia de 48Mhz.

Como notarán, solo es necesario modificar el valor de la frecuencia, por el que vamos a utilizar.
Otra manera de especificarlo es:

CONST _target_clock = cexpr

CONST _target_clock = 48_000_000

Hay que prestar atención en aquellos PIC donde por medio de PLL, se cambian la frecuencia para obtener otra. Por ejemplo los Microcontroladores que tiene USB interno. Al USB se lo hace trabajar con 96Mhz, pero al PIC se lo puede hacer trabajar con 48Mhz aún teniendo un cristal de 20Mhz. En definitiva, hay que especificar 48Mhz y no 20Mhz.

La especificación de la frecuencia es necesaria especificarla, cuando se utilice la función _usec_delay(xx); o de las demoras que están en la librería delay.jal

Configuración del PIC (TARGET FUSES)

Los microcontroladores, al ser diseñado para uso general, necesitan una configuración para realizar lo que nosotros queramos.

ASM

__CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC (etcétera).

JALv2

PRAGMA TARGET FUSES [cexpr0] cexpr

cexpr0 sólo se utiliza cuando existen varias palabras config en cuyo caso 0 es la primera palabra de configuración, 1 el segundo, y así sucesivamente. Noten que empieza de 0.

Otra manera de hacerlo es la siguiente:

CONST _config = cexpr
CONST _config [cexpr0] = cexpr

Como verán, se debe reemplazar cexpr con el valor correspondiente.
Un concejo. Chequeen la configuración que aparecen en los ejemplos que viene con el programa. Están en la carpeta llamada sample.

Configuración del PIC (TARGET opt tags)

PRAGMA TARGET opt tags

Esta es la opción más utilizada a la hora de realizar la configuración de nuestro PIC.

pragma target WDTPS P32K -- watch dog saler setting
pragma target WDT CONTROL -- no watchdog
pragma target CCP2MUX ENABLED -- CCP2 pin C1
pragma target PBADEN DIGITAL -- digital input port<0..4>
pragma target LPT1OSC LOW_POWER -- low power timer 1
pragma target MCLR EXTERNAL -- master reset on RE3
(etcétera)

Para saber como configurarlo, además de chequear la ficha técnica (data sheet), podemos chequear el archivo donde están todos los nombres de los registros. Este archivo se incluye en nuestro programa para poder manejar dicho pic. Es el archivo cuyo nombre es igual al pic a utilizar. Al final de este archivo, se pueden ver todas las configuraciones del PIC. Por ejemplo 16F84A.jal; 16F877A.jal 18F4550.jal
 
Última edición:
Interrupción (INTERRUPT)

Sirve para generar el tratamiento de las interrupciones. Al momento, JALv2 no soporta interrupciones de bajo nivel (para aquellos pic que tienen más de un nivel de interrupciones). JALv2 trata a todas las interrupciones como de alto nivel. En los PIC que solo posean un solo nivel de interrupciones, es indistinto.

PRAGMA INTERRUPT { FAST | RAW | NORMAL | }

Según la configuración que elijamos { FAST | RAW | NORMAL | }, nos puede ahorrar trabajo a la hora de hacer el tratamiento de las interrupciones.

NORMAL:

W, STATUS, PCLATH, FSR, TBLPTR y _picstate son salvados dentro del tratamiento de la interrupción (ISR) y restaurados al salir.

PRAGMA INTERRUPT NORMAL

FAST:

_pic_state no es salvado ni restaurado. En este caso, el procedimiento de la interrupción debe ser escrito enteramente en ASM (ASSEMBLER) para evitar que se corrompa el área pic_state.

RAW:

No se salva ningún registro principal ni el _pic_state. Esta opción, solo nos garantiza que se entre en el vector de interrupción.

El tratamiento de la interrupción, debe ser escrito dentro de un procedimiento procedure(). Por ejemplo:

procedure interrupcion() is
pragma interrupt
if (INTCON_TMR0IF) then
if usb_iniciado == 1 then
if usb_en_uso == 0 then
usb_serial_flush()
end if
end if
tmr0=64000
INTCON_TMR0IF=0
end if
end procedure

Más adelante, veremos como está compuesto el procedimiento [procedure()]

En ASM, se debe escribir el tratamiento de la interrupción dentro del vector de interrupción, por ejemplo:

ORG 0x04
nop
nop
nop

Donde ORG 0x04 nos indica que lo que sigue, el compilador debe escribirlo a partir de la posición de memoria 0x04 (que en algunos pic, corresponde al vector de interrupción).

ERROR

Este tipo de pragma, se utiliza mucho a la hora de crear librerías y funciones para nuestro PIC. En él, se puede dar un aviso de error de compilación debido a una mala configuración de la librería.

PRAGMA ERROR

Por ejemplo:

Dentro de la librería glcd_common.jal está el siguiente pragma error:

pragma error "GLCD_COLOR_BITS VALUE NOT SUPPORTED"

Si especificamos algo incorrecto, el compilador nos dará el error con el siguiente mensaje: "GLCD_COLOR_BITS VALUE NOT SUPPORTED"



Solo he echo especificación de los PRAGMAs más importantes. Si desean saber más sobre el resto, deben consultar el manual de JALv2. Por ejemplo, están los PRAGMA para cancelar diferentes tipos de advertencias y/o errores, de los cuales no recomiendo desactivar nunca. Las advertencias, son tan importantes como los errores. Una advertencia pasada por alto, puede implicar que nuestro PIC no se comporte como nosotros queramos.
 
Me olvidé de agregar uno muy importante.

INLINE

PRAGMA INLINE se utiliza dentro de una función function() o procedimiento procedure()

Cuando se llama a una función o a un procedimiento, el compilador lo hace por medio de un CALL y posteriormente sale con un RETURN. Pero su a una función y/o procedimiento es declarado como inline, obligará al compilador que se lo compile a continuación.

En ASM a un procedimiento o una función, se lo conoce como rutina. Las rutinas pueden ser ejecutadas con un CALL o con un GOTO. Pero si lo declaramos como pragma inline, un procedimiento o función, se comportará como una MACRO (en ASM); o sea, se copiará tantas veces como sea llamado. Lo malo de esto, es que podemos quedarnos sin memoria de programa; algo que también nos puede pasar en JALv2.
La estrategia de usar pragma inline es dentro de una función que llama a otra función; que a su vez puede llamar a un procedimiento; justamente nos evitará consumir mucha pila (STACK) del pic. Por cada llamada, se ocupará una posición en la pila (stack) del PIC. En algunos PIC tenemos hasta 8 niveles; por lo que podemos llamar hasta 8 veces a un función procedimiento. Y si usamos interrupción, ya no podrán ser 8, sino 7; porque debemos dejar uno libre para la interrupción.

procedure sumar () is
a = a + b
end procedure

function restar () is
pragma inline
z = z - M
end function
 
Atrás
Arriba