Práctica 2Lab 2

C + ASM
Control del LCD

Lectura de lcd.asm y construcción de main.cReading lcd.asm and building main.c

Inicializar → convertir caracteres → desplazar → controlar iconos → completar el programaInitialize → convert characters → shift → control icons → complete the program
Enunciado 2026/27 · §3.2–3.3

Qué queremos escribir desde C

main.c · servicios que vamos a entender
lcdIni();lcdTest();espera_decimas(20);lcdBorraTodo(); /* Un paso de la animación */lcdPintaIzq('5');lcdBateria(BIT5);espera_decimas(2);
Enunciado · §3.3

Cómo se enlazan C y ensambladorLinking C and assembly

lcd.h / main.c · extractos
/* lcd.h */unsigned int lcda2seg(char c);void lcdIni(void);void lcdPintaIzq(char c);void lcdPintaDer(char c); /* main.c */#include "lcd.h"lcdPintaIzq('5');
Enunciado · §3.1–3.2

Por qué el proyecto usa modelo small

CCS · condiciones para este código ASM
; Modelo small: direcciones y llamadas de 16 bits.call #lcda2segmov.w Tab14Seg(R13), R12ret /* Esperas del ejercicio: optimización off. */
Enunciado · §3.1; opciones Processor y Optimization

Qué pantalla estamos controlandoThe display we are controlling

lcd.asm
DIG1 .equ LCDM10DIG2 .equ LCDM6DIG3 .equ LCDM4DIG4 .equ LCDM19DIG5 .equ LCDM15DIG6 .equ LCDM8
SLAU627A · figura 8 · p. 11
SLAU627A · figura 8 · p. 11 · clic para ampliarclick to enlarge
lcd.asm · DIG1..DIG6

De la tabla del LaunchPad a DIG1From the LaunchPad table to DIG1

SLAU627A · tabla 4 · p. 13
SLAU627A · tabla 4 · p. 13 · clic para ampliarclick to enlarge
lcd.asm
DIG1 .equ LCDM10; byte bajo: LCDM10; byte alto: LCDM11

La fila LCDM10 controla los segmentos bajos de A1. LCDM11 contiene los restantes y sus símbolos.

TI SLAU627A · tabla 4

lcdIni: seleccionar pines y relojlcdIni: pins and clock

lcd.asm · lcdIni
lcdIni:  mov.w #0xFFD0, &LCDCPCTL0  mov.w #0xFC3F, &LCDCPCTL1  mov.w #0x00F8, &LCDCPCTL2   mov.w #LCDDIV_1|LCDPRE__16|LCD4MUX|LCDLP, &LCDCCTL0
lcd.asm · lcdIni

De dónde sale una máscara como 0x00F8

lcdIni · selección de líneas del controlador
mov.w #0xFFD0, &LCDCPCTL0mov.w #0xFC3F, &LCDCPCTL1mov.w #0x00F8, &LCDCPCTL2 ; LCDCPCTL2: bit n selecciona la línea S(32+n).
LCD.pdf · LCDCPCTL0/1 p. 6 y LCDCPCTL2 p. 7; SLAU627A · tabla 4

Cómo interpretar LCDDIV y LCDPREReading LCDDIV and LCDPRE

msp430.h · constantes usadas en lcd.asm
; Campos usados por lcdIniLCDDIV_1   ; divisor 2LCDPRE__16 ; divisor 16LCD4MUX    ; modo 4MUXLCDLP      ; bajo consumo
LCD.pdf · LCDCCTL0 · p. 1
LCD.pdf · LCDCCTL0 · p. 1 · clic para ampliarclick to enlarge
lcdIni · LCDCCTL0

Una constante puede activar varios bits

msp430.h · macros que usa lcdIni
LCDDIV_1  = 0x0800   ; campo DIV=1: dividir por 2LCDPRE__16= 0x0400   ; campo PRE=4: dividir por 16LCD4MUX   = 0x001C   ; LCDMX1 | LCDMX0 | LCDSONLCDLP     = 0x0002 ; OR de las cuatro constantes: 0x0C1E; Después se activará LCDON con BIS.
Cabecera MSP430FR6989; LCD.pdf · LCDCCTL0 p. 1

lcdIni: tensión, borrado y activaciónlcdIni: voltage, clear and enable

lcd.asm · final de lcdIni
  mov.w #VLCD_1|VLCDREF_0|LCDCPEN, &LCDCVCTL  mov.w #LCDCPCLKSYNC, &LCDCCPCTL  mov.w #LCDCLRM, &LCDCMEMCTL  bis.w #LCDON, &LCDCCTL0  ret
LCD.pdf · LCDCVCTL pp. 4–5; LCDCCPCTL p. 7; enunciado §2.1

Qué llega a ASM cuando escribimos un carácter

C y ASM · lcdPintaIzq llama a lcda2seg
/* C: */lcdPintaIzq('5'); ; Dentro de lcdPintaIzq, R12 contiene 0x0035.call #lcda2seg; Al volver, R12 contiene 0x00B7.
Enunciado · §3.2.2–3.2.3; Tab14Seg

Convertir ASCII en un índice y leer dos bytes

lcd.asm · lcda2seg
lcda2seg:  mov.b R12, R13  sub.b #PRIMERO, R13  jn    lcda2segErr  cmp.b #ULTIMO-PRIMERO+1, R13  jge   lcda2segErr  rla.w R13  mov.w Tab14Seg(R13), R12  jmp   lcda2segFinlcda2segErr: mov.w #SEGERR, R12lcda2segFin: ret
Enunciado · §3.2.2

Por qué dos bytes B7 y 00 forman 0x00B7

lcd.asm · Tab14Seg (extracto)
;       abcdefgm   hjkpq-n-Tab14Seg:  ; ... entradas anteriores ...  .byte 01100111b, 00000000b ; '4'  .byte 10110111b, 00000000b ; '5'  .byte 10111111b, 00000000b ; '6'  ; ... entradas posteriores ...
Tab14Seg · carácter ASCII 53

lcdPintaIzq: validar antes de desplazarlcdPintaIzq: validate before shifting

lcd.asm · entrada y salida de lcdPintaIzq
lcdPintaIzq:  call  #lcda2seg  cmp.w #SEGERR, R12  jeq   lcdPintaIzqFin  mov.b #BIT2|BIT0, R13  ; después: copiar DIG2 a DIG1,  ; DIG3 a DIG2, ... e insertar en DIG6lcdPintaIzqFin:  ret
Enunciado · §3.2.3

Por qué se conservan precisamente los bits 0 y 2

lcd.asm · máscara del byte alto
mov.b #BIT2|BIT0, R13 ; Bits de símbolos: 0000 0101 = 0x05; Bits del carácter: 1111 1010 = 0xFA; 0xFA es el complemento de 0x05 en ocho bits.
SLAU627A · tabla 4, LCDM11/LCDM10; enunciado §3.2.3

Copiar desde la izquierda evita perder las fuentes

lcdPintaIzq · orden de las copias, bytes bajos
mov.b &DIG2, &DIG1mov.b &DIG3, &DIG2mov.b &DIG4, &DIG3mov.b &DIG5, &DIG4mov.b &DIG6, &DIG5mov.b R12, &DIG6  ; insertar el byte bajo nuevo
lcdPintaIzq · cada copia también trata el byte alto

lcdPintaIzq: copiar una posición completalcdPintaIzq: copy one position

lcd.asm · bloque repetido por cada pareja
; R13 = BIT2|BIT0 = 0x05; DIG1 <- DIG2mov.b &DIG2,   &DIG1xor.b &DIG2+1, &DIG1+1and.b R13,     &DIG1+1xor.b &DIG2+1, &DIG1+1
SLAU627A · tabla 4, filas LCDM11/LCDM10 · p. 13
SLAU627A · tabla 4, detalle de LCDM11 (arriba) y LCDM10 (abajo) · p. 13 · clic para ampliar
lcdPintaIzq · copia DIG2→DIG1

Qué queremos conseguir antes de estudiar XOR

Equivalente en C · no es código adicional del proyecto
uint8_t conservar = destino & 0x05;uint8_t copiar    = fuente  & 0xFA;uint8_t nuevo     = conservar | copiar; /* El ASM hace lo mismo con XOR, AND, XOR. */
Enunciado §3.2.3; equivalencia del bloque de lcdPintaIzq

Seguir la mezcla sin perder los símbolos

lcdPintaIzq · mismos operandos, valores ilustrativos
; Fuente=0x5A; destino=0xA5; R13=0x05xor.b fuente, destinoand.b R13, destinoxor.b fuente, destino
Ejemplo numérico del bloque anterior

Por qué hay que intercambiar los bytes de R12

lcdPintaIzq · comienzo de la inserción del5
; R12=0x00B7, patrón devuelto por lcda2segmov.b R12, &DIG6swpb R12 ; Las operaciones .b siguientes usan el byte bajo.
lcdPintaIzq · inserción en DIG6

lcdPintaIzq: insertar el carácter nuevolcdPintaIzq: insert the new character

lcd.asm · final de lcdPintaIzq
; Las cinco copias anteriores ya han terminadomov.b R12, &DIG6swpb  R12xor.b &DIG6+1, R12bic.b R13, R12xor.b &DIG6+1, R12mov.b R12, &DIG6+1ret
lcdPintaIzq · inserción en DIG6

Hacia la derecha, el orden de copia se invierte

lcd.asm · orden de los bloques de lcdPintaDer
; Orden de los bytes bajos en lcdPintaDer:mov.b &DIG5, &DIG6mov.b &DIG4, &DIG5mov.b &DIG3, &DIG4mov.b &DIG2, &DIG3mov.b &DIG1, &DIG2; Cada copia incluye también la mezcla del MSB.mov.b R12, &DIG1  ; insertar el byte bajo nuevo
Enunciado · §3.2.4

lcdBorra y lcdBorraTodo no son equivalenteslcdBorra and lcdBorraTodo differ

lcd.asm · rutinas de borrado
lcdBorra:  mov.w #BIT2|BIT0, R12  clr.b &DIG1  and.b R12, &DIG1+1  ; repetir para DIG2 ... DIG6  ret lcdBorraTodo:  mov.w #LCDCLRM, &LCDCMEMCTL  ret
LCD.pdf · LCDCLRM · p. 4
LCD.pdf · LCDCLRM · p. 4 · clic para ampliarclick to enlarge
Enunciado · §3.2.5–3.2.6

Desde C: un mapa nuevo y un estado anterior

C y ASM · recorrido de lcdBateria(0x03)
uint8_t anterior = lcdBateria(0x03); ; Entrada de la función ASM: R12=0x03lcdBateria:  mov.w #TabIcBat, R13  jmp   MapIconos
lcd.asm · lcdBateria y MapIconos

Tres servicios usan el mismo mapeadorThree services share one mapper

lcd.asm · selección de tabla
lcdBateria:  mov.w #TabIcBat, R13  jmp   MapIconoslcdIconos:  mov.w #TabIconos, R13  jmp   MapIconoslcdInterDigito:  mov.w #TabIcIntDig, R13  jmp   MapIconos
Enunciado · §3.2.7–3.2.9

Cómo leer TabIcBatReading TabIcBat

lcd.asm · primeras seis entradas de TabIcBat
TabIcBat:  .word LCDM18, BIT5 ; B1  .word LCDM14, BIT5 ; B2  .word LCDM18, BIT6 ; B3  .word LCDM14, BIT6 ; B4  .word LCDM18, BIT7 ; B5  .word LCDM14, BIT7 ; B6
SLAU627A · tabla 4, filas de batería · p. 13
SLAU627A · tabla 4, filas de batería · p. 13 · clic para ampliarclick to enlarge
TabIcBat; enunciado · §3.3

Por qué no basta con leer una palabra de la tabla

TabIcBat · ubicación frente a estado solicitado
TabIcBat: .word LCDM18, BIT5  ; B1          .word LCDM14, BIT5  ; B2 ; Con R13=0:mov.w TabIcBat(R13), R12; R12 recibe la dirección LCDM18, no un dibujo.
TabIcBat; SLAU627A · tabla4

Dos lecturas de palabra consumen una fila

TabIcBat y comienzo de MapIconos
TabIcBat: .word LCDM18, BIT5  ; B1          .word LCDM14, BIT5  ; B2          ; ... seis filas más mov.b #8, R11mov.w @R13+, R14mov.w @R13+, R15
lcd.asm · lcdBateria y MapIconos

Qué comprueba BIT.B R15, 0(R14)

MapIconos · dirección y máscara obtenidas de la tabla
mov.w @R13+, R14  ; R14 = direccion LCDM18mov.w @R13+, R15  ; R15 = BIT5 = 0x20bit.b R15, 0(R14)rrc.b R12jc    MapIcSet1
MapIconos · BIT comprueba; BIS activa; BIC borra

MapIconos: leer, rotar y escribir cada bitMapIconos: read, rotate and write each bit

lcd.asm · MapIconos
MapIconos: mov.b #8, R11MapIcBuc:  mov.w @R13+, R14           mov.w @R13+, R15           bit.b R15, 0(R14)           rrc.b R12           jc    MapIcSet1           bic.b R15, 0(R14)           jmp   MapIcFinBucMapIcSet1: bis.b R15, 0(R14)MapIcFinBuc: dec.b R11           jnz   MapIcBuc           ret
MapIconos · bucle completo

El carry cambia de significado durante la vuelta

MapIconos · primera vuelta, B1 inicialmente apagado
; R14=&LCDM18, R15=BIT5, R12=0x03bit.b R15, 0(R14)rrc.b R12jc    MapIcSet1bic.b R15, 0(R14)jmp   MapIcFinBucMapIcSet1: bis.b R15, 0(R14)
lcd.asm · lcdBateria y MapIconos

Tras ocho vueltas, R12 devuelve el mapa anterior

MapIconos · recorrido; las escrituras se resumen aquí
mov.b #8, R11MapIcBuc: mov.w @R13+, R14          mov.w @R13+, R15          bit.b R15, 0(R14)          rrc.b R12          ; BIS o BIC según carry          dec.b R11          jnz MapIcBucret
lcd.asm · lcdBateria y MapIconos

Avanzar la batería sin salir de sus seis barrasAdvance within the six battery bars

main-guiado.c · propuesta
static void avanza_bateria(void) {  if (barra == BIT0) direccion_bateria = 1;  else if (barra == BIT5) direccion_bateria = -1;  if (direccion_bateria > 0)    barra = (uint8_t)(barra << 1);  else    barra = (uint8_t)(barra >> 1);  lcdBateria(barra);}
Enunciado · §3.3

Un carácter, un avance de batería y una pausaOne character, one battery step and one delay

main-guiado.c · propuesta
static void imprime_paso(char c, int izquierda) {  if (izquierda) lcdPintaIzq(c);  else           lcdPintaDer(c);  avanza_bateria();  espera_decimas(2);}
Enunciado · §3.3

Qué significa esperar unas dos décimas

main-guiado.c · espera activa aproximada
static void espera_decimas(unsigned decimas) {  volatile unsigned i;  while (decimas--) {    for (i = 10000; i; --i) {      __no_operation();    }  }}
Enunciado · §3.3, nota1; main-guiado.c

Los tres recorridos del mensajeThe three passes through the message

main-guiado.c · dentro del bucle principal
for (i = 0; i < longitud; ++i)  imprime_paso(mensaje[i], 1); for (i = longitud - 7; i >= 0; --i)  imprime_paso(mensaje[i], 0); for (i = 6; i < longitud; ++i)  imprime_paso(mensaje[i], 1);
Enunciado · p. 6, nota 2

De dónde salen longitud − 7 y el índice 6

main-guiado.c · puntos de cambio de sentido
/* Al terminar el primer recorrido */i = longitud - 7;imprime_paso(mensaje[i], 0); /* Al terminar el recorrido hacia la derecha */i = 6;imprime_paso(mensaje[i], 1);
Enunciado · p.6, nota2; cadena de main-guiado.c

Cómo queda el cuerpo de mainThe final main structure

main-guiado.c · estructura final
WDTCTL = WDTPW | WDTHOLD;lcdIni();PM5CTL0 &= ~LOCKLPM5;lcdTest();espera_decimas(20);for (;;) {  lcdBorraTodo();  barra = BIT5;  direccion_bateria = -1;  lcdBateria(barra);  /* los tres bucles de la diapositiva anterior */}
main-guiado.c · propuesta

Qué corregir antes de reutilizar la carpetaCorrections before reusing the old project

lcd.asm · corrección de etiquetas
; Original: nombre repetido al llegar a RETjeq lcdPintaIzq; ...lcdPintaIzq ret ; Copia de apoyo: salida diferenciadajeq lcdPintaIzqFin; ...lcdPintaIzqFin ret
Revisión estática de la carpeta recibida

Código y documentación para la prácticaCode and documentation

Fuentes y alcance de la revisión