Cartel de Leds en ASM.Aquí voy a mostrar una forma sencilla de hacer un cartel de leds en asm que tendrá algunas limitaciones, pero entendiendo como es el funcionamiento y el esfuerzo de ustedes podrán romper esas barreras!

Para mostrar un ejemplo sencillo vamos a usar una tabla para guardar el mensaje a mostrar y una sola variable (8 bits) para indicar el largo y control de la posición a enviar. Por ello el largo del mensaje estará restringido a 255 bytes que se reducen en 6 por la posición de la tabla en la memoria de programa.-
La forma de guardar cada letra del mensaje será la siguiente:

Cada byte indica una columna de la letra, donde un 0 es led apagado y 1 led encendido.
Nota: Es una forma de hacerlo, la cual la aprendí del amigo BrunoF en uno de sus tantos aportes

Luego el mensaje puede ser mayor al cartel utilizado para mostrarlo así que es necesario efectuar un desplazamiento del mensaje sobre el cartel o lo que es lo mismo desplazar el cartel sobre el mensaje.
Para ello se utilizará una variable que indica la posición inicial dentro del mensaje, en la cual comienza a mostrarse en el cartel. Como deseamos que el mensaje sea rotativo el problema se presenta en las últimas posiciones donde se debe mostrar la parte final del mensaje y empezar a mostrar el inicio

Lo que hacemos es dividir en 2 la forma de mostrar el mensaje, cuando Posición Inicial + Largo del Cartel sea menor a Largo del mensaje y cuando sea mayor:

Bueno, más o menos se ha explicado como vamos a trabajar con el mensaje, ahora se debe entender como va a funcionar el multiplexo de los leds. Vamos a realizar la multiplexación por filas, ósea que vamos a seleccionar una fila y vamos a actualizar sus valores por medio de registros de desplazamientos. El tiempo de actualización no debe superar los 20ms!
Para actualizar usaremos una variable que indique que fila ha de actualizarse, por ejemplo para actualizar la fila uno la variable FilaActual será 00000001. Ahora para saber que valores tenemos que mandar a los registros de desplazamiento iremos tomando cada una de las columnas (PosicionEnviar) a actualizar, aremos un AND con FilaActual y determinaremos si enviar un 1 o un 0.-
( PosicionEnviar (AND) FilaActual ) = FilaActual?
Si -> Enviamos un 1
No-> Enviamos un 0
Hardware para simulación:
; CARTEL DE LEDS. de 32x8.
; Si las letras son de 8x8, maxima cantidad de letras en el mensaje 31.-
; **** Encabezado ****
list p=16F628A ; Microcontrolador utilizado.-
#include P16F628A.inc ; Definicion de registros SFR.-
__CONFIG _HS_OSC & _PWRTE_ON & _WDT_OFF & _CP_OFF & _MCLRE_ON & _BOREN_OFF & _LVP_OFF & _DATA_CP_OFF
;**** Definicion de Variables ****
CBLOCK 0x20 ; En esta posicion se declaran los registros de usuario (GPR)
STATUS_Temp ; Registro para guardar temporalmete STATUS
W_Temp ; Registro para guardar temporalmete W
Banderas ; Registro para almacenar banderas.-
FilaActual ; Fila Actual a refrescar.-
PosicionInicial ; Posicion inicial dentro del mensaje a visualizar en el cartel.- (Entre 0 y LargoMsj-1)
PosicionEnviar ; Posicion del registro a enviar.-
CuentaRefrescos ; Cuenta la cantidad de refrescos realizados.
LargoCartel ; Guarda largo del cartel.- 32
LargoMsj ; largo del mesaje a visualizar-> Max 249 Bytes.-Para aumentar modificar manejo de tabla.-
TempH ; Temporal para guardar calculos matemáticos.-
TempL ; Temporal...
VelMovimiento ; Para fijar velocidad de movimiento.-
ENDC
Velocidad equ d'4' ; Para fijar velocidad de desplazamiento.-
kbhit_tiempo equ 0 ; Bandera que indica que han pasado 2ms.-
kbhit_cuadro equ 1
BData equ 0
BClock equ 1
Strobe equ 2
;**** Inicio del Microcontrolador ****
Reset
org 0x00
goto Inicio ; Salto a inicio del programa.-
;**** Vector de Interrupcion ****
org 0x04
goto Inicio_ISR ; Atiendo Interrupcion.-
;**** Mensaje a Mostrar *****
org 0x05
Mensaje
addwf PCL,1 ; Se incrementa el contador del programa.-
DT 0x38, 0x7C, 0xFE, 0x82, 0x82, 0x82, 0x44, 0x00 ; C
DT 0x60, 0xF4, 0x94, 0x94, 0xFC, 0xF8, 0x00, 0x00 ; a
DT 0xFC, 0xFC, 0x08, 0x04, 0x04, 0x00, 0x00, 0x00 ; r
DT 0x04, 0xFE, 0xFE, 0x84, 0x40, 0x00, 0x00, 0x00 ; t
DT 0x78, 0xFC, 0x94, 0x9C, 0x58, 0x00, 0x00, 0x00 ; e
DT 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; l
DT 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Espacio
DT 0x78, 0xFC, 0x84, 0x48, 0xFE, 0xFE, 0x00, 0x00 ; d
DT 0x78, 0xFC, 0x94, 0x9C, 0x58, 0x00, 0x00, 0x00 ; e
DT 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Espacio
DT 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; l
DT 0x78, 0xFC, 0x94, 0x9C, 0x58, 0x00, 0x00, 0x00 ; e
DT 0x78, 0xFC, 0x84, 0x48, 0xFE, 0xFE, 0x00, 0x00 ; d
DT 0x88, 0x9C, 0x94, 0xF4, 0x64, 0x00, 0x00, 0x00 ; s
DT 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Espacio
DT 0x8C, 0x9E, 0xFE, 0xFA, 0x62, 0x00, 0x00, 0x00 ; S
DT 0x7C, 0xFC, 0x80, 0x80, 0xFC, 0xFC, 0x00, 0x00 ; u
DT 0xFE, 0xFE, 0x10, 0x38, 0x64, 0xC4, 0x80, 0x00 ; k
DT 0x04, 0x8C, 0xB8, 0x70, 0x30, 0x08, 0x04, 0x00 ; y
DT 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Espacio
DT 0x82, 0xC1, 0xA1, 0x91, 0xCE, 0x00, 0x00, 0x00 ; 2
DT 0x80, 0x86, 0x85, 0x89, 0x71, 0x00, 0x00, 0x00 ; 5
DT 0xC0, 0x3C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 ; /
DT 0x81, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 ; 1
DT 0x7E, 0x81, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00 ; 0
DT 0xC0, 0x3C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 ; /
DT 0x7E, 0x81, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00 ; 0
DT 0x8E, 0x91, 0x51, 0x71, 0x1E, 0x00, 0x00, 0x00 ; 9
DT 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Espacio
;**** Programa principal ****
Inicio
;---- Configuraciones ----
bsf STATUS,RP0 ; Banco 1.-
movlw 0xF8
movwf TRISA ;eeeeesss
movlw 0x00
movwf TRISB ;ssssssss
movlw 0xDF
movwf OPTION_REG ; Pull-Up deshabiltado, , Timer0|interno, 1:1
bcf STATUS,RP0 ; Banco 0.-
movlw 0x07 ; Configuro Comparador Analogico
movwf CMCON ; V1in-=GND, V1in+=GND, C1out=Off| V2in-=GND, V2in+=GND, C2out=Off
clrf PORTA
clrf PORTB
movlw 0x4D
movwf T2CON ; Timer2: Pre=1:4, Post=1:10
movlw Velocidad ; Cargamos velocidad de desplazamiento.-
movwf VelMovimiento
movlw d'32' ; Cargamos largo del cartel.-
movwf LargoCartel
movlw d'232' ; Cargamos largo del mensaje.-
movwf LargoMsj
clrf PosicionInicial
clrf Banderas
movlw 0x01
movwf FilaActual
movlw 0x08
movwf CuentaRefrescos
clrf PIR1 ; Limpiamos Banderas
bsf STATUS,RP0 ; Banco 1.-
bsf PIE1,TMR2IE ; Habilitamos interrupcion por Timer2
bcf STATUS,RP0 ; Banco 0.-
bsf INTCON,GIE ; Habilitacion general de interrupciones
bsf INTCON,PEIE ; Habilitacion de interrupciones Perifericas
Bucle
btfss Banderas,kbhit_tiempo ; Se espera a que pasen 2 ms.-
goto $-1
bcf Banderas,kbhit_tiempo
call ActualizarCartel ; Se actuliza fila del cartel.-
goto Bucle
;**************************************************************************
;**** Rutina de servicio de Interrupcion ****
Inicio_ISR
;---- Guardado de registro W y STATUS ----
movwf W_Temp ; Copiamos W a un registro Temporario
swapf STATUS,W ; Invertimos nibles de STATUS
movwf STATUS_Temp ; Guardamos STATUS en un registro temporal
;---- Interrupciones ----
btfsc PIR1,TMR2IF ; Interrupcion por Timer2?
goto ISR_TIMER2 ; Si, se trata interrupcion
;.............................
Fin_ISR
;---- Restauramos los valores de W y STATUS ----
swapf STATUS_Temp,W
movwf STATUS
swapf W_Temp,f
swapf W_Temp,W
retfie
;.......................
ISR_TIMER2
; Tratamiento de Interrupcion
decfsz CuentaRefrescos,1 ; Se refrescaron las 8 filas (Momento adecuado para realizar algun cambio.-)
goto SetBandera ; No, seguimos..
decfsz VelMovimiento,1 ; Si, ya pasaron 16ms x VelMovimiento?
goto RecargarData ; No, seguimos...
movlw Velocidad ; Recargamos Velocidad
movwf VelMovimiento
incf PosicionInicial ; Si, incrementamos PosicionInicial para efectuar "movimiento"
movfw LargoMsj ; Verificamos si ya se llego al final del Mensaje.-
subwf PosicionInicial,0
btfss STATUS,Z
goto RecargarData ; No, seguimos..
clrf PosicionInicial ; Si, reseteamos PosicionInicial.-
RecargarData
movlw 0x01
movwf FilaActual
movlw 0x08
movwf CuentaRefrescos
SetBandera
bsf Banderas,kbhit_tiempo ; Indicamos que han pasado 2 ms, y es necesario actualizar.-
bcf PIR1,TMR2IF ; Borramos bandera
goto Fin_ISR
;.......................
;*************************************************************************
ActualizarCartel
; Es (PosicionInicial + LargoCartel <= LargoMsj)?
clrf TempH ; Borramos Byte alto de variable de 16bits temporal.-
bcf STATUS,C ; Aseguramos que el carry sea 0 :P
movfw PosicionInicial ; Sumamos PosicionInicial + largoCartel.-
addwf LargoCartel,0 ; Y lo guardamos en W.-
movwf TempL ; Cargamos el resultado bajo en variable temporal.-
btfsc STATUS,C ; y si hay carry incrementamos registro alto del resultado temporal.-
incf TempH ; (Esto se da porque el resultado es mayor a 255)
btfsc TempH,0 ; Es mayor a 255?
goto $+4 ; (Solo lo hacemos así porque LargoMsj debe ser menor o igual a 255)
movfw TempL ; No es mayor a 255, verficamos Byte bajo.-
subwf LargoMsj,0
btfss STATUS,C
goto DividoEnDosPartes ; Es mayor...
;****************************************************************************
; Es menor...
; Hay que de la posicion actual sumar el largo de leds y despues comenzar a actualizar el cartel.-
movfw TempL ; Cargamos posicion de donde comenzar a cargar mensaje.-
movwf PosicionEnviar
CicloEnvio
call EnviaBit ; Se envia bits actual del byte seleccionado.-
decf PosicionEnviar,1 ; Se decrementa direccionador del byte a enviar.-
movfw PosicionInicial ; Es igual a la posicion inicial de donde se comienza a visualizar mensaje??
subwf PosicionEnviar,0
btfss STATUS,Z ;
goto CicloEnvio ; No, seguimos con el siguiente byte.-
goto Efectivizamos ; Ahora se habilitará Strobe para efectivizar los cambios y activaremos la Fila correspondiente.-
;******************************************************************************
; Es mayor...
DividoEnDosPartes
;(LargoMsj - PosicionInicial):
movfw PosicionInicial
subwf LargoMsj,0
movwf TempL ; Guardamos resultado de la resta en registro temporal.-
;[LargoCartel - (LargoMsj - PosicionInicial)]:
movfw TempL
subwf LargoCartel,0
movwf PosicionEnviar ; Guardamos desde donde comenzamos a enviar disminuyendo hasta 1...
CicloEnvio_1
call EnviaBit ; Se envia bits actual del byte seleccionado.-
decf PosicionEnviar,1 ; Se decrementa direccionador del byte a enviar.-
movlw 0x00
subwf PosicionEnviar,0 ; Es igual a 0x00??
btfss STATUS,Z
goto CicloEnvio_1
; Enviamos Segunda parte.-
movfw LargoMsj
movwf PosicionEnviar
CicloEnvio_2
call EnviaBit ; Se envia bits actual del byte seleccionado.-
decf PosicionEnviar,1 ; Se decrementa direccionador del byte a enviar.-
movfw PosicionInicial ; Es igual a la posicion inicial de donde se comienza a visualizar mensaje??
subwf PosicionEnviar,0
btfss STATUS,Z
goto CicloEnvio_2 ; No, seguimos con el siguiente byte.-
goto Efectivizamos ; Ahora se habilitará Strobe para efectivizar los cambios y activaremos la Fila correspondiente.-
;************************************************************************************
EnviaBit
decf PosicionEnviar,0 ; Cargamos posicion en W del byte a visualizar.-
call Mensaje ; Cargamos en W el byte actual a refrescar.-
andwf FilaActual,0 ; Hacemos ByteActual (AND) FilaActual para determinar el bit a enviar.-
subwf FilaActual,0 ; [ByteActual(AND)FilaActual] - FilaActual, si esto es cero el bit a enviar es 1, sino 0.-
bcf PORTA,BData ; Primero cargamos un 0.-
btfsc STATUS,Z ; La operacion anterior es 0?
bsf PORTA,BData ; Si, cargamos un 1 en el bit
bsf PORTA,BClock ; Generamos clock.-
nop ; demora
bcf PORTA,BClock ;..
return
;..........................................................
Efectivizamos
movlw 0xFF ; Descactivamos todas las filas.-
movwf PORTB
bsf PORTA,Strobe ; Efectivizamos los datos en los registros de desplazamiento.-
nop
bcf PORTA,Strobe
comf FilaActual,0 ; Complementamos FilaActual para activar Fila actual a refrescar.-
movwf PORTB
bcf STATUS,C ;
rlf FilaActual,1 ; Rotamos Fila actual para proxima actualizacion.-
return
end
Un Video de muestra:Consideraciones para construcción real:Para la construcción real se debe agregar la parte de “potencia” dado que el PIC y según el registro de desplazamiento que se utilice no son capaces de manejar las corrientes necesarias, para ello se colocan transistores para el control de las filas y se agrega el ULN2803/2003 en serie a los registros de desplazamiento, una forma de hacerlo sería la siguiente:

Y cambiar rutina de efectivizar a :
Efectivizamos
clrf PORTB ; Desactivamos todas las filas.-
bsf PORTA,Strobe ; Efectivizamos los datos en los registros de desplazamiento.-
nop
bcf PORTA,Strobe
movfw FilaActual ;Refrescamos fila actual.-
movwf PORTB
bcf STATUS,C ;
rlf FilaActual,1 ; Rotamos Fila actual para proxima actualizacion.-
return
Otra forma:

Pero deben tener en cuenta que para encender un led se debe enviar al registro de desplazamiento un cero!