El LPC1768
Reloj, GPIO e interrupciones
De 12 MHz a 100 MHz, PCLK, PCONP, pines y NVIC
Idea clave. Antes de usar cualquier periférico del LPC1768 hay que hacer siempre lo mismo: alimentarlo (PCONP), elegir su reloj (PCLKSEL), llevarlo a sus pines (PINSEL) y, si va a interrumpir, habilitar su interrupción en el NVIC. Este tema explica cada paso. Los siguientes solo cambian el periférico.
El reloj: de 12 MHz a 100 MHz
La Mini-DK2 lleva un cristal de 12 MHz (el oscilador principal). Al arrancar, la función SystemInit() de system_LPC17xx.c monta esta cadena:
| Paso | Qué hace | Valor por defecto |
|---|---|---|
| CLKSRCSEL | Elige la fuente: oscilador principal, oscilador RC interno (4 MHz) o el del RTC | Oscilador principal, 12 MHz |
| PLL0 | Multiplica: | , → 400 MHz |
| Divisor de CPU | ÷4 → 100 MHz | |
| PCLKSEL0/1 | Reloj de cada periférico | ÷4 → 25 MHz |
Ese 25 MHz aparece en casi todas las cuentas de timers, PWM y UART. Si en un examen no dicen otra cosa, es el de partida.
| Bits | Campo | Qué hace |
|---|---|---|
| 3:2 | PCLK_TIMER0 | Reloj del Timer 0 |
| 5:4 | PCLK_TIMER1 | Reloj del Timer 1 |
| 7:6 | PCLK_UART0 | Reloj de la UART0 |
| 13:12 | PCLK_PWM1 | Reloj del PWM1 |
| 25:24 | PCLK_ADC | Reloj del ADC |
Cada par de bits vale 00 = CCLK/4 (el de reset), 01 = CCLK, 10 = CCLK/2 y 11 = CCLK/8. Timer 2/3 y UART 2/3 están en PCLKSEL1.
Alimentar el periférico: PCONP
Para ahorrar energía, no todos los periféricos arrancan encendidos. Cada uno tiene un bit en PCONP:
| Encendidos tras el reset | Apagados tras el reset |
|---|---|
Timer 0 y 1 (PCTIM0, PCTIM1), UART 0 y 1, PWM1, GPIO | Timer 2 y 3 (bits 22 y 23), UART 2 y 3 (bits 24 y 25), ADC (bit 12) |
LPC_SC->PCONP |= (1 << 22); // enciende el Timer 2 (PCTIM2)
El |= («o» bit a bit) pone a 1 solo el bit 22 y deja el resto como estaba. (1 << 22) es un 1 desplazado 22 posiciones: la máscara de ese bit.
Pines: función y modo
Cada pin del LPC1768 puede tener hasta cuatro funciones (GPIO o la de un periférico). Se elige con PINSEL0…10, con 2 bits por pin: 00 es GPIO y 01, 10, 11 son las funciones alternativas que da la tabla del manual (cap. 8).
PINMODE elige la resistencia interna del pin, también con 2 bits:
| PINMODE | Modo |
|---|---|
00 | Pull-up (el de reset) |
01 | Repetidor |
10 | Ni pull-up ni pull-down |
11 | Pull-down |
Por eso un pulsador entre el pin y masa funciona sin resistencia externa: en reposo el pull-up deja el pin a 1, y al pulsar se une a masa y lee 0. En la Mini-DK2 los pulsadores ISP, Key1 y Key2 están en P2.10, P2.11 y P2.12.
GPIO: entradas y salidas
Los puertos P0…P4 tienen sus registros en LPC_GPIO0…LPC_GPIO4:
| Registro | Para qué |
|---|---|
FIODIR | Dirección de cada bit: 1 = salida, 0 = entrada |
FIOSET | Escribir 1 en un bit pone ese pin a 1. Los 0 no hacen nada |
FIOCLR | Escribir 1 en un bit pone ese pin a 0. Los 0 no hacen nada |
FIOPIN | Leer el nivel de los pines (o escribirlos todos de golpe) |
FIOMASK | Bits a 1 = pines que los demás registros ignoran |
LPC_GPIO3->FIODIR |= (1 << 25); // P3.25 como salida
LPC_GPIO3->FIOSET = (1 << 25); // P3.25 a 1
LPC_GPIO3->FIOCLR = (1 << 25); // P3.25 a 0
if ((LPC_GPIO2->FIOPIN & (1 << 11)) == 0) // ¿P2.11 a 0? (pulsado)
FIOSET y FIOCLR se escriben con =, no con |=: como los ceros no hacen nada, no hace falta leer antes. La comprobación usa & con la máscara para quedarse solo con el bit 11.
Interrupciones: el NVIC
El NVIC (Nested Vectored Interrupt Controller) del Cortex-M3 decide qué interrupción se atiende y cuándo. Lo que hay que saber:
- Cada fuente tiene su rutina de atención (ISR) con un nombre fijo, que viene en
startup_LPC17xx.s:TIMER0_IRQHandler,UART0_IRQHandler,EINT1_IRQHandler… - Se habilita con
NVIC_EnableIRQ(TIMER0_IRQn). - Prioridad de 0 a 31, con
NVIC_SetPriority(): el número más bajo es el más prioritario. Una interrupción más prioritaria puede interrumpir a otra que se esté atendiendo (por eso nested, anidadas). Si tienen la misma prioridad, espera. - Dentro de la ISR hay que borrar el flag del periférico. Si no, al salir el NVIC ve que sigue pendiente y vuelve a entrar sin fin.
- La ISR tiene que ser corta: guardar un dato, poner una bandera y salir. Lo largo se hace en el programa principal.
- Las variables que comparten la ISR y el
mainse declaranvolatile, para que el compilador las lea de memoria cada vez.
Interrupciones externas EINT0–EINT3
Práctica 3 (Servomotor con un Timer) · conviene dominarlo
Son los pines P2.10–P2.13 en su primera función alternativa (01 en PINSEL4). Se configuran en el bloque de control del sistema (LPC_SC):
| Registro | Para qué |
|---|---|
EXTMODE | Bit a 0 = por nivel, bit a 1 = por flanco |
EXTPOLAR | Nivel bajo o flanco de bajada (0), nivel alto o flanco de subida (1) |
EXTINT | Flags. Se borran escribiendo un 1 en su bit |
void EINT1_IRQHandler(void) { // Key1 (P2.11)
LPC_SC->EXTINT = (1 << 1); // borrar el flag de EINT1
pulsado = 1; // volatile: lo lee el main
}
Por nivel, el flag no se puede borrar mientras el nivel siga activo, así que la interrupción se relanza. Para un pulsador se usa casi siempre flanco de bajada.
SysTick
Es un temporizador de 24 bits que lleva el propio Cortex-M3, pensado para dar un «tic» periódico. SysTick_Config(SystemCoreClock / 100) genera una interrupción cada 10 ms en SysTick_Handler. Para periodos que no caben en 24 bits, o para varias señales, se usan los Timers del tema siguiente.
Preguntas de examen
Explica el camino del reloj desde el cristal hasta un periférico, con las frecuencias por defecto.
Cristal de 12 MHz → la PLL0 multiplica a MHz → el divisor de CPU divide entre 4: CCLK = 100 MHz → cada periférico divide CCLK según sus bits de PCLKSEL (por defecto ÷4): MHz. (Respuesta redactada a partir de los apuntes.)
¿Qué pasos hay que dar para usar el Timer 2 con interrupción?
- Alimentarlo:
PCONP |= (1 << 22). - Elegir su reloj en PCLKSEL1 (o dejar CCLK/4).
- Si saca señal por un pin, elegir esa función en PINSEL.
- Configurar el timer (PR, MR, MCR) y arrancarlo.
NVIC_SetPriority(TIMER2_IRQn, …)yNVIC_EnableIRQ(TIMER2_IRQn).- En
TIMER2_IRQHandler, borrar el flag enIR.
(Respuesta redactada a partir de los apuntes.)
¿Por qué FIOSET y FIOCLR se escriben con «=» y no con «|=»?
Porque en esos registros un 0 no hace nada: solo actúan los bits que se escriben a 1. Con = se cambia únicamente el pin de la máscara, sin leer el registro antes y sin riesgo de tocar los demás pines. (Respuesta redactada a partir de los apuntes.)
Material del curso usado: Apuntes de Miguel · Resumen de interrupciones del LPC1768 (diapositivas de clase) · Resumen de GPIO del LPC1768 (diapositivas de clase) · UM10360 (manual del LPC17xx), cap. 4, 6, 8 y 9.