Hola,
En este nuevo post, vamos a ver una pequeña instroduccion a la programacion de los microcotroladores LPC1768 que usa como procesador el ARM CORTEX M3.
El kit de desarrollo se usara el que nos proporciona Waveshare, (
http://www.wvshare.com/), que tiene las siguientes características:
LPC1768FBD100: the high performance ARM Cortex-M3 LPC MCU which features:
Core: Cortex-M3 32-bit RISC
Operating Frequency: 100MHz Max
Operating Voltage: 2.4-3.6V (3.3V typical)
Package: LQFP100
I/Os: 70
Memories: 512kB Flash, 64kB RAM
Communication Interfaces: 2 x SPI, 2 x SSP, 4 x UART, 3 x I2C, 1 x I2S, 6 x PWM, 8 x ADC, 1 x DAC
Debugging/Programming: supports SWD interfaces, supports ISP through UART
AMS1117-3.3: 3.3V voltage regulator
LM3526-L: USB power switch and over-current protection
Power switch
Power indicator
LEDs: convenient for indicating I/O status and/or program running state
USB communication indicator
Reset button
User keys: for I/O input test and/or program control
Joystick: five positions
12M crystal oscillator: enables the MCU run at 48M frequency by frequency multiplication
32.768K crystal oscillator: for internal RTC, also supports clock calibration
USB HOST port: for connecting USB flash drive, etc.
CAN2 interface: communicates with accessory boards which feature the CAN device conveniently
CAN1 interface: communicates with accessory boards which feature the CAN device conveniently
I2C0 interface: easily connects to I2C peripherals such as I/O expander (PCF8574), EEPROM (AT24Cxx), etc.
SPI0 | SPI1 interface: for connecting SPI peripherals, such as DataFlash (AT45DBxx), SD card, MP3, etc.
Ethernet interface: easily connects the MCU to ethernet network by using an additional ethernet module, such as DP83848 Ethernet Board, etc.
ONE-WIRE interface: easily connects to ONE-WIRE devices (TO-92 package), such as temperature sensor (DS18B20), electronic registration number (DS2401), etc.
I2S interface (including I2C1, I2C2 interfaces): easily connects to I2S and/or I2C peripherals such as Stereo Audio Device (UDA1380), FRAM (FM24CLxx), etc.
LCD interface: combined with an 8-bit to 16-bit adapter, supports connecting the 3.2 inch multi-color touch screen LCD (the adapter and LCD are included in Package A/B)
UART2 | UART3 interface: for connecting UART peripherals, such as RS232, RS485, USB TO UART, etc.
PS/2 interface: easily connects to PS/2 keyboard and/or mouse
Modem interface (including UART1 interface): for connecting Modem and/or UART peripherals, such as RS232, RS485, USB TO UART, etc.
8 I/Os interface (including 3-ch AD, 1-ch DA)
for connecting accessory boards which using I/O control, such as 8 Push Buttons, Motor, etc.
also integrates AD/DA function for AD/DA testing
ISP interface (including UART0 interface): for connecting ISP modules and/or UART peripherals, such as RS232, RS485, USB TO UART, etc.
USB port: communicating with PC
5V DC jack
5V/3.3V power input/output: usually used for power output, and/or common ground with other application board
MCU pins connector: all the MCU pins are accessible on expansion connectors for further expansion
JTAG/SWD interface: for debugging/programming
LEDs jumper
User keys jumper
Joystick jumper
PS/2 jumper
Configurando el Oscilador a 100Mhz.
Para iniciar con la aventura de este gran microcontrolador, configuraremos lo que es el oscilador de trabajo para que opere a 100Mhz.
Como vemos en la imagen de arriba es el block de generador de Reloj de los LPC17xx, para poderlo configurar hay que irnos por pasos.
Existe un registro que nos permite escojer 3 tipos de osciladores como son:
Oscilador Interno
Oscilador Principal
Oscilador RTC
En los siguientes tutoriales vamos a trabajar con el Oscilador principal, este oscilador puede operar en un rango de 1 Mhz a 25 Mhz, pero este se puede multiplicar con la ayuda de un PLL0 y alcanzar una frecuencia maxima del microcontrolador (100Mhz).
Si hemos de trabajar con el Oscilador principal, entonces el primer registro que hay que configurar es el:
Registro SCS (System Controls and Status) y configurarlo de la siguiente Forma:
Código:
LPC_SC->SCS = (1<<5); // Run with a External Crystal
while(!(LPC_SC->SCS & (1<<6)));//External Crystal is ready?
Como hemos entrada ya con el oscilador externo que consta de un cristal de 12 Mhz, ahora hay que configurar el block del PLL0, esto se llega usando el registro CLKSRCSEL, junto con el divisor de CCO.
Código:
LPC_SC->CCLKCFG = (1<<3); // FCCO / 4
LPC_SC->CLKSRCSEL = (1<<0); // PLL0 selected
El registro CCLKCFG nos permite dividir la frecuencia de salida del PLL0, por ejemplo, si la salida del PLL0 es 400 Mhz y seleccionamos un divisor de 4 entonces la salida hacia el CPU y los periféricos sera de 100Mhz.
El siguiente código es la configuración del PLL0 para alcanzar una frecuencia de 400 Mhz, esta frecuencia que queremos debe estar al rango con respecto el fabricante nos diga, el rango es de 275 Mhz
Código:
// M = (FCCO*N)/(2xFin)
// Where :
// N = 6
// FCC0 = 400 , in range 275 < 400 < 550 Mhz
// Fin = 12Mhz
// M = (400Mhz*6)/(2x12Mhz) = 100
LPC_SC->PLL0CFG = 0x00050063;
LPC_SC->PLL0FEED = 0xAA; //Sequence to take effect
LPC_SC->PLL0FEED = 0x55; //Sequence to take effect
LPC_SC->PLL0CON = (1<<0); //PLL enable
LPC_SC->PLL0FEED = 0xAA; //Sequence to take effect
LPC_SC->PLL0FEED = 0x55; //Sequence to take effect
while (!(LPC_SC->PLL0STAT & (1<<26))); //is Lock freq?
LPC_SC->PLL0CON = (1<<0) + (1<<1); //PLL enable and Connect
LPC_SC->PLL0FEED = 0xAA; //Sequence to take effect
LPC_SC->PLL0FEED = 0x55; //Sequence to take effect
while (!(LPC_SC->PLL0STAT & ((1<<26)|(1<<24)))); //is Lock freq?
LPC_SC->USBCLKCFG = 0; //USB shutdown
LPC_SC->CLKOUTCFG = 0; //CLKOUT by CPU Clock
Pero para obtener esta frecuencia se debe de insertar ciertas variables en unos registros para alcanzar una frecuencia de 400 Mhz con un cristal de 12 Mhz, la formula es:
M = (FCCOxN) / (2 x Fin)
Donde:
M = PLL0 Valor Multiplicador
N = PLL0 Pre-divisor
Fin = Frecuencia de entrada.
Entonces los valores de M y N al obtenerlos se debe de guardar en el registro PLL0CFG.
En el caso de querer una frecuencia de 100Mhz usando 12Mhz, entonces para sacar el valor de M y N, se hace lo siguiente:
M = (400Mhz x 6 ) / (2x12Mhz) = 100
El numero 6 que pertenece a la variable N, lo obtuve buscando un numero entero como resultado y que si lo multiplicamos por 5 tendremos un resultado tipo float (83.333), siempre hay que buscar un numero entero.
Entonces M = 100 y N = 6, entonces el registro quedaria de la siguiente manera:
LPC_SC->PLL0CFG = 0x00050063;
LPC_SC->PLL0FEED = 0xAA; //Sequence to take effect
LPC_SC->PLL0FEED = 0x55; //Sequence to take effect
Como ven he usado un registro llamado PLL0FEED que nos permite mandar una secuencia de bytes para guardar el cambio del registro PLL0CFG.
Los últimos detalles es habilitar el PLL0 y esperar hasta que la frecuencia alcance la frecuencia deseada, el registro para habilitar el PLL0 es PLL0CON y después se manda la secuencia para grabar los cambios.
Nuevamente habilitamos el PLL0 y el bit de Connect para llegara ser el oscilador principal del CPU.
Volviendo al principio con el registro CCLKCFG hemos visto que se ha configurado a un valor de 3 que es definido como divisor de FCC0 por 4, entonces como el PLL0 que hemos obtenido en la formula anterior, cuando esta frecuencia pase por el block divisor nuestra salida sera 400Mhz / 4 = 100 Mhz
Configurando el SysTick
Como sabemos en los Microcontroladores que usan la tecnología Arm como procesador, contiene un block de SysTick que nos provee una temporizacion estandar.
Para la configuracion de este modulo solo hay que usar la siguiente función:
Código:
void SysTick_Handler(void)
{
timer_tick++;
}
void delay_ms(uint32_t ms){
SysTick->LOAD = ((100000*ms)-1);
SysTick->CTRL |=(1<<0)+(1<<1)+(1<<2);
SysTick->VAL = 0;
while(!timer_tick);
timer_tick=0;
SysTick->CTRL =0;
}
El primero registro LOAD hemos de cargar un valor de un máximo de 24 bits, al cargar un valor que en ese caso hemos puesto ((100000*ms)-1), si la variable ms es 1 , entonces tendremos como resultado 99,999 ¿Que quiere decir esto? que al contar 99,999 ticks del oscilador (en este caso del oscilador principal) igual a 1 milisegundo obtendremos un evento que puede ser una interrupcion.
Codigo de Ejemplo usando la configuracon del Oscilador Externo + PLL0 y sistemas de retardos con SysTick.
Código:
uint32_t timer_tick;
void SetClockto100Mhz(void){
LPC_SC->SCS = (1<<5); // Run with a External Crystal
while(!(LPC_SC->SCS & (1<<6)));//External Crystal is ready?
LPC_SC->CCLKCFG = (1<<3); // FCCO / 4
LPC_SC->CLKSRCSEL = (1<<0); // PLL0 selected
// M = (FCCO*N)/(2xFin)
// Where :
// N = 6
// FCC0 = 400 , in range 275 < 400 < 550 Mhz
// Fin = 12Mhz
// M = (400Mhz*6)/(2x12Mhz) = 100
LPC_SC->PLL0CFG = 0x00050063;
LPC_SC->PLL0FEED = 0xAA; //Sequence to take effect
LPC_SC->PLL0FEED = 0x55; //Sequence to take effect
LPC_SC->PLL0CON = (1<<0); //PLL enable
LPC_SC->PLL0FEED = 0xAA; //Sequence to take effect
LPC_SC->PLL0FEED = 0x55; //Sequence to take effect
while (!(LPC_SC->PLL0STAT & (1<<26))); //is Lock freq?
LPC_SC->PLL0CON = (1<<0) + (1<<1); //PLL enable and Connect
LPC_SC->PLL0FEED = 0xAA; //Sequence to take effect
LPC_SC->PLL0FEED = 0x55; //Sequence to take effect
while (!(LPC_SC->PLL0STAT & ((1<<26)|(1<<24)))); //is Lock freq?
LPC_SC->USBCLKCFG = 0; //USB shutdown
LPC_SC->CLKOUTCFG = 0; //CLKOUT by CPU Clock
}
void SysTick_Handler(void)
{
timer_tick++;
}
void delay_ms(uint32_t ms){
SysTick->LOAD = ((100000*ms)-1);
SysTick->CTRL |=(1<<0)+(1<<1)+(1<<2);
SysTick->VAL = 0;
while(!timer_tick);
timer_tick=0;
SysTick->CTRL =0;
}
void delay_us(uint32_t us){
SysTick->LOAD = ((100*us)-1);
SysTick->CTRL |=(1<<0)+(1<<1)+(1<<2);
SysTick->VAL = 0;
while(!timer_tick);
timer_tick=0;
SysTick->CTRL =0;
}
int main(void){
SetClockto100Mhz();
LPC_GPIO0->FIODIR0 |= (1<<3);
LPC_GPIO0->FIODIR0 |= (1<<2);
LPC_GPIO0->FIODIR0 |= (1<<1);
LPC_GPIO0->FIODIR0 |= (1<<0);
while(1){
LPC_GPIO0->FIOSET0 |= (1<<3);
LPC_GPIO0->FIOSET0 |= (1<<2);
LPC_GPIO0->FIOSET0 |= (1<<1);
LPC_GPIO0->FIOSET0 |= (1<<0);
delay_ms(250);
LPC_GPIO0->FIOCLR0 |= (1<<3);
LPC_GPIO0->FIOCLR0 |= (1<<2);
LPC_GPIO0->FIOCLR0 |= (1<<1);
LPC_GPIO0->FIOCLR0 |= (1<<0);
delay_ms(250);
}
}
Saludos!