; vim: set filetype=asx8051 et sw=4 sts=4 : ; Módulo y opciones .module leds .optsdcc -mmcs51 --model-small ; Constantes "públicas" LEDS_MAX_COLS = 32 ; Cantidad máxima de columnas ; Variables públicas .globl _leds_matrix_len ; unsigned char .globl _leds_matrix ; unsigned int[LEDS_MAX_COLS] ; Funciones públicas .globl _leds_init ; void leds_init(); .globl _leds_test ; void leds_test(); .globl _leds_write ; void leds_write(unsigned int); .globl _leds_write_low ; void leds_write_low(unsigned char); .globl _leds_write_high ; void leds_write_high(unsigned char); .globl _leds_delay_update ; void leds_delay_update(); .globl _leds_lock ; void leds_lock(); .globl _leds_unlock ; void leds_unlock(); .globl _leds_timer2_isr ; void leds_timer2_isr() interrupt 5; ; Constantes LEDS_LOW = 0x0080 ; posición de xdata donde está el latch 1 LEDS_HIGH = 0x00c0 ; posición de xdata donde está el latch 2 DELAY_FACTOR = 0 ; base del contador para el retardo DELAY_BASE = 13 ; punto medio del retardo DELAY_DIVISOR = 3 ; divisor para la cantidad de columnas ; Área de bancos de registros .area REG_BANK_0 (REL,OVR,DATA) .ds 8 ; Usamos siempre banco 0 ar0 = 0x00 ar1 = 0x01 ar2 = 0x02 ar3 = 0x03 ar4 = 0x04 ar5 = 0x05 ar6 = 0x06 ar7 = 0x07 ; Variables es memoria RAM común .area DSEG (DATA) _leds_matrix_len:: ; Cantidad de columnas de la matriz .ds 1 curr_col: ; Columna que se está dibujando actualmente .ds 1 ; Variables en memoria RAM extendida indirecta (8052) .area ISEG (DATA) _leds_matrix:: ; Matriz a dibujar .ds LEDS_MAX_COLS * 2 ; 2 bytes por columna ; Variables de bit .area BSEG (BIT) lock: ; Variable utilizada para el 'locking', indica si el timer .ds 1 ; estaba andando al momento de lockear para que el unlock ; pueda reactivarlo de ser necesario ; Configuramos el vector de interrupciones para atender el timer2 ;.area INTV (ABS, CODE) ; .org 0x002b ; clr tf2 ; limpio bit de interrupción porque para el timer2 no es autom. ; ljmp timer2_isr ; Área de código del programa .area CSEG (CODE) ; Inicializa leds. ; Primitiva de C: ; void leds_init(); ; ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_init:: ; guardo registros que uso push ar0 push ar1 push ar2 ; leo de la ROM el tamaño por default mov dptr, #DEFAULT_MATRIX_LEN clr a movc a, @a+dptr mov _leds_matrix_len, a clr c rlc a ; multiplicamos por 2 porque hay 2 bytes por columna mov r2, a ; tamaño en bytes de la matriz ; Cargo milisegundos mov rcap2l, #-DELAY_FACTOR ; base del retardo lcall _leds_delay_update ; copio imagen por default de la ROM a la RAM mov dptr, #DEFAULT_MATRIX mov r0, #0 ; indice del "array" en la ROM mov r1, #_leds_matrix ; dirección de memoria de la RAM mov a, r0 proximo$: movc a, @a+dptr ; leo de la ROM con el índice mov @r1, a ; escribo en el puntero a la RAM inc r1 ; incremento puntero inc r0 ; incremento índice mov a, r0 ; para comparar cjne a, ar2, proximo$ ; veo si quedan más bytes por leer mov t2con, #0x00; setup del timer2 (auto-reload), no lo arrancamos setb tr2 ; largo a correr el timer2 mov curr_col, #0 ; inicializo el contador de columna en 0 ; Limpiamos stack pop ar2 pop ar1 pop ar0 ret ; Hace una prueba simple de los leds ; Primitiva de C: ; void leds_test(); ; ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_test:: ; escribo patrones en los leds mov dptr, #0xffff lcall _leds_write lcall sleep mov dptr, #0xaaaa lcall _leds_write lcall sleep mov dptr, #0x5555 lcall _leds_write lcall sleep mov dptr, #0x0000 lcall _leds_write lcall sleep ret ; Escribe en los leds. ; Primitiva de C: ; void leds_write(unsigned int); ; ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_write:: ; guardamos en r0 la parte alta que imprimimos despues push ar0 mov r0, dph ; parte baja mov a, dpl ; de C me viene la parte baja del argumento en el dpl mov dptr, #LEDS_LOW cpl a ; complemento para ver encendidos los "1" movx @dptr, a ; parte alta mov a, r0 ; de C me viene la parte alta del argumento en el dph mov dptr, #LEDS_HIGH cpl a ; complemento para ver encendidos los "1" movx @dptr, a ; devolvemos r0 pop ar0 ret ; Escribe en los leds del primer latch. ; Primitiva de C: ; void leds_write_low(unsigned char); ; ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_write_low:: ; parte baja mov a, dpl ; de C me viene el argumento en el dpl mov dptr, #LEDS_LOW cpl a ; complemento para ver encendidos los "1" movx @dptr, a ret ; Escribe en los leds del segundo latch. ; Primitiva de C: ; void leds_write_high(unsigned char); ; ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_write_high:: ; parte baja mov a, dpl ; de C me viene el argumento en el dpl mov dptr, #LEDS_HIGH cpl a ; complemento para ver encendidos los "1" movx @dptr, a ret ; Actualiza el retardo de la matriz según la cantidad de columnas ; Primitiva de C: ; void leds_delay_update(); ; ; La fórmula utilizada es: ; DELAY_FACTOR * (DELAY_BASE - (leds_matrix_len / DELAY_DIVISOR)) ; En realidad ya está cargado rcap2l DELAY_FACTOR (constante) y en rcap2h se ; carga el resultado de: ; DELAY_BASE - (leds_matrix_len / DELAY_DIVISOR) ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_delay_update:: push b ; uso b, lo guardo mov a, _leds_matrix_len mov b, #DELAY_DIVISOR div ab ; en a me queda leds_matrix_len / DELAY_DIVISOR mov dpl, a mov a, #DELAY_BASE subb a, dpl ; en a me queda DELAY_BASE - leds_matrix_len / DELAY_DIVISOR jnb cy, 1$ ; Si leds_matrix_len / DELAY_DIVISOR > DELAY_BASE mov a, #1 ; ponemos 1 para que no quede nulo el intervalo (o 'negativo') 1$: ; Si no, seguimos como siempre mov dpl, a ; complemento a la base mov a, #0 subb a, dpl mov rcap2h, a ; Cargo el nuevo retardo pop b ; devuelvo b ret ; Bloquea el timer de los leds cuando se accede a una zona crítica (P0 y/o P2). ; Primitiva de C: ; void leds_lock(); ; ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_lock:: mov c, et2 ; En lock me queda si está el timer andando o no. mov lock, c clr et2 ; Sea como sea, lo paro. ret ; Desbloquea el timer de los leds. ; Primitiva de C: ; void leds_unlock(); ; ; C se encarga de hacer push y pop del dptr, a y psw si lo necesita. _leds_unlock:: jnb lock, 1$ ; Si no estába andando, no hago nada setb et2 ; Si estaba andando lo prendo 1$: ret ; Manejador de la interrupción del timer2 para el uso de los leds. ; Primitiva de C: ; void leds_timer2_isr() interrupt 5; ; _leds_timer2_isr:: ; limpiamos flag del timer2 clr tf2 ; guardamos en el stack el estado actual de los registros que vamos a usar push acc push psw push ar0 push dpl push dph ; vemos si hay que empezar a leer por la 1ra columna de nuevo mov a, curr_col cjne a, _leds_matrix_len, 1$ ; hay que empezar de nuevo mov curr_col, #0 mov a, curr_col ; dejamos en a la columna actual 1$: ; multiplicamos por 2 porque hay 2 bytes por columna clr c rlc a ; uso r0 como puntero al comienzo de la matriz mov r0, #_leds_matrix add a, r0 ; le sumo al puntero el offset actual segun la columna mov r0, a ; imprimo en LEDS_HIGH mov a, @r0 ; leo el contenido de la matriz mov dptr, #LEDS_HIGH cpl a ; complemento para ver encendidos los "1" movx @dptr, a ; imprimo en LEDS_LOW inc r0 ; busco proximo byte de la columna mov a, @r0 ; leo el contenido de la matriz mov dptr, #LEDS_LOW cpl a ; complemento para ver encendidos los "1" movx @dptr, a ; avanzamos a la proxima columna mov a, curr_col inc a mov curr_col, a ; sacamos nuestra basura del stack pop dph pop dpl pop ar0 pop psw pop acc reti ; listo! seguimos viaje... ; Provoca un retardo corto. ; Usamos dpl y dph porque son "C-safe". sleep: mov a, #0xff 2$: mov dph, #0xff 1$: mov dpl, #0xff djnz dpl, . djnz dph, 1$ djnz acc, 2$ ret ; Matriz por default DEFAULT_MATRIX_LEN: .db 16 ;.db 32 DEFAULT_MATRIX: .dw 0b0000111111110000 ; columna 0 .dw 0b0011111111111100 ; columna 1 .dw 0b0111000000001110 ; columna 2 .dw 0b0110000000000110 ; columna 3 .dw 0b1100001100000011 ; columna 4 .dw 0b1100011000110011 ; columna 5 .dw 0b1100110000110011 ; columna 6 .dw 0b1100110000000011 ; columna 7 .dw 0b1100110000000011 ; columna 8 .dw 0b1100110000110011 ; columna 9 .dw 0b1100011000110011 ; columna 10 .dw 0b1100001100000011 ; columna 11 .dw 0b0110000000000110 ; columna 12 .dw 0b0111000000001110 ; columna 13 .dw 0b0011111111111100 ; columna 14 .dw 0b0000111111110000 ; columna 15 ; .dw 0b0000011111100000 ; .dw 0b0001111111111000 ; .dw 0b0011100000011100 ; .dw 0b0111110000000110 ; .dw 0b0110111000000110 ; .dw 0b1100011100000011 ; .dw 0b1100001110000011 ; .dw 0b1111111111111111 ; .dw 0b1111111111111111 ; .dw 0b1100001110000011 ; .dw 0b1100011100000011 ; .dw 0b0110111000000110 ; .dw 0b0111110000000110 ; .dw 0b0011100000011100 ; .dw 0b0001111111111000 ; .dw 0b0000011111100000 ; .dw 0b1111000000001111 ; columna 0 ; .dw 0b1100000000000011 ; columna 1 ; .dw 0b1000111111110001 ; columna 2 ; .dw 0b1001111111111001 ; columna 3 ; .dw 0b0011001111111100 ; columna 4 ; .dw 0b0011100111001100 ; columna 5 ; .dw 0b0011110011001100 ; columna 6 ; .dw 0b0011110011111100 ; columna 7 ; .dw 0b0011110011111100 ; columna 8 ; .dw 0b0011110011001100 ; columna 9 ; .dw 0b0011100111001100 ; columna 01 ; .dw 0b0011001111111100 ; columna 00 ; .dw 0b1001111111111001 ; columna 01 ; .dw 0b1000111111110001 ; columna 03 ; .dw 0b1100000000000011 ; columna 04 ; .dw 0b1111000000001111 ; columna 05 ; .dw 0b0000001111100000 ; columna 0 ; .dw 0b0000111110000000 ; columna 1 ; .dw 0b0111111000000000 ; columna 2 ; .dw 0b1111000000000000 ; columna 3 ; .dw 0b0111100000000000 ; columna 4 ; .dw 0b0011110000000000 ; columna 5 ; .dw 0b0001111000000000 ; columna 6 ; .dw 0b0000111100000000 ; columna 7 ; .dw 0b0000011110000000 ; columna 8 ; .dw 0b0000001111000000 ; columna 9 ; .dw 0b0000000111100000 ; columna 10 ; .dw 0b0000000011110000 ; columna 11 ; .dw 0b0000000001111000 ; columna 12 ; .dw 0b0000000000111100 ; columna 13 ; .dw 0b0000000000011110 ; columna 14 ; .dw 0b0000000000001111 ; columna 15 ;end