Sei sulla pagina 1di 153

utorial ARM Cortex-M3 - LPC1768

Cancelar suscripcin de este Tema

Buscar en este Tema

23Me Gusta
Pgina 1 de 6

1 2 3 4 5 > ltima
Herramientas

Calificar Tema

17/04/20
13

cosmef
ulanito

#1
[Aporte] Tutorial ARM Cortex-M3 - LPC1768

Hace poco compr este kit de desarrollo (LPC1768-Mini-DK2):

04

Fecha de
Ingreso:
octubre2009
Ubicaci
n: Bs.
As.
Mensaje
s: 2.508

Las caractersticas del kit:


fabricante del kit dijo:

- One serial interface, use CP2102 (USB to RS232 interface, support ISP
download).
- RJ45-10/100M Ethernet network interface (Ethernet PHY: LAN8720A).

- 2.8 inch TFT color LCD interface (SPI interface or 16Bit parallel interface).
- USB 2.0 interface, USB host and USB Device interface.
- TF SD / MMC card (SPI) interface.
- Two user button, One Reset button and ISP button , one INT0 button, two userprogrammable LED lights.
- Serial ISP download, Standard 20-pin JTAG download simulation debugging
interface.
- Can be choose external 5V power supply or USB 5V supply.
- Board size: 95mm * 78mm.
- Expansion all IO.

Realmente por las cosas que tiene, ms que nada los puertos Usb y
Ethernet (que todava no llegu a verlos), y el costo del mismo (casi $200
argentinos o u$d 32) creo que vale la pena para explorar estos bichos que
cada vez son ms utilizados por su bajo costo y su alta prestacin.
Yo vena de conocer los ARM7 y el cambio a esta familia realmente ni se
nota, en casi todas las cosas es muy similar y solo cambian pequeos
detalles haciendolos an ms sencillo a la hora de programar.
Esquemtico:

El conversor usb a rs232, nos permite trabajar con la uart0 y poder


programar el uC directamente desde un puerto usb. Obviamente adems
de este conector usb, se encuentra el puerto usb propiamente dicho del
uC.
Siguiendo con la idea de este tutorial sobre ARM7, tena pensado crear un
mini tutorial ms que nada orientado a cdigo y ejemplos sobre los
perifricos bsicos que se pueden encontrar en estas familias de uC.
En base a eso, tena planeado dividir el tutorial en varias partes las cuales
ya tengo resueltas:
- GPIO.
- PLL y Timers.
- Interrupcin externa.
- Uart.
- RTC.
- ADC.
- DAC.
- PWM.
- SPI y SSP (estos 2 ltimos an no pude probarlos con hard).
Nuevamente la idea es plantear un problema sencillo y resolver el cdigo,
para lo cual es fundamental tener la hoja de datos del LPC1768 a mano,

con lo cual voy a dejarla en este post para que puedan descargarla.
En el prximo mensaje subo el equemtico completo.
Archivos Adjuntos
UM10360 - LPC1768.pdf (4,94 MB (Megabytes), 55 visitas)
Me Gusta

A Basalto, Hellmut1956, george.manson.69 y a otros 5 les gusta esto.


ltima edicin por cosmefulanito04; 17/04/2013 a las 18:01

Mensaje inapropiado?

Citar

17/04/2013

cosmeful
anito04

#2
Primera parte - GPIO

Antes que nada, subo el esquemtico completo del kit.


Las herramientas a utilizar sern:
- Entorno de programacin Keil4, pueden descargar una versin de
evaluacin gratuita.

Fecha de

- Flash Magic, lo pueden descargar en forma gratuita.

Ingreso:
octubre2009

Diferencias que vamos a notar entre ARM7 y Cortex:

Ubicacin:
Bs. As.

Para el que viene de ARM7 (familia LPC21xx), se va encontrar que los

Mensajes:

registros se acceden de forma distinta, por ej. para acceder al registro

2.508

PINSEL0 el cdigo ser as:


Cdigo PHP:

//ARM7 - Familia LPC21xx


PINSEL0=0;
//ARM Cortex
LPC_PINCON->PINSEL0=0;
Se puede ver que ahora es necesario llamar a una estructura por
referencia para poder acceder al registro. La ventaja que le encuentro
es la comodidad de tener todos los registros de un perifrico

encapsulado, por lo que impide que nos confundamos de registros


provenientes de otro perifrico.
Otro cambio importante son las rutinas de interrupcin y como se
manejan, pero eso lo vamos a ver ms adelante.
Ejercicio propuesto:
Haciendo algo similar que en el tutorial de los ARM7, vamos a configurar
los puertos para poder encender y apagar los leds que figuran en el
esquemtico.
Volviendo al tema GPIO, en base al esquemtico, la idea es encender el
LED1 (P3.25) y apagar el LED2 (P3.26), para luego de un retardo
invertir los roles, apagar LED1 y encender LED2.
Como de momento no sabemos manejar el PLL, los timer ni las
interrupciones, el retardo lo realizaremos con 2 for (mtodo cabezn
).
Si bien se trata de trabajar en C, cabe aclarar que la idea es que
nosotros seamos capaces de crear nuestras propias funciones en base a
los registros y las recomendaciones que nos d la hoja de datos y no
depender de funciones hechas por terceros de la cuales no tenemos idea
que cambios realizan durante su ejecucin, de esta forma evitamos
delegar todo el control del uC.
Para poder realizar el programa es necesario:
- Ver el esquemtico y analizar como se encienden los leds (si el puerto
debe estar en estado bajo o alto).
- Leer la hoja de datos el captulo 8 (Chapter 8: LPC17xx Pin connect
block), pgina 104.
- Leer la hoja de datos el captulo 9 (Chapter 9: LPC17xx General
Purpose Input/Output (GPIO)), pgina 120.
Cdigo:

Cdigo PHP:

#include <LPC17xx.H>
#define LED_1 (1<<25)

//Puerto 3.25

#define LED_2 (1<<26)

//Puerto 3.26

int main()
{
unsigned int cont,cont2;
LPC_PINCON->PINSEL7=0;

//Declaro al puerto 3 c

omo GPIO -> La funcin de dichos puertos se manejan a travs


del PINSEL7 -> Bits 19:18 Puerto 3.25 y Bits 21:20 Puerto 3.2
6
LPC_GPIO3->FIODIR|=LED_2|LED_1;

//Defino al puerto com

o salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led

apagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led

encendido
while(1)
{
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3->FIOSET|
=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3->FIOCLR|
=LED_2;}
for(cont=0;cont<1524;cont++)
for(cont2=0;cont2<65536;cont2++); //Rutina de ret
ardo bastante precaria
}
}
Registros utilizados:

PINSEL: sirve para seleccionar la funcin de un PIN.


FIODIR: sirve para seleccionar si el PIN funciona como entrada o como
salida.
FIOSET: sirve para para fijar en "1" la salida del PIN.
FIOCLR: sirve para para fijar en "0" la salida del PIN.
FIOPIN: sirve para leer el estado del PIN, si est en "1" o en "0".
Para el prximo mensaje (les doy una semana para que lean y
entiendan este cdigo), voy a explicar como funciona el PLL y los timers,
para luego empezar a meternos con las interrupciones. Para los que
quieran adelantar, pueden ir al tema del ARM7 que resulta muy parecido
en esta familia.
Archivos Adjuntos
LPC1768-Mini-DK2_SCH.pdf (3,66 MB (Megabytes), 25 visitas)
Me Gusta

A Basalto, Sebastian1989, marceloalemachado y a otros 3 les gusta esto.

Mensaje inapropiado?

21/04/201
3

cosmeful
anito04

Citar
#3

Segunda Parte - PLL y timers (parte "terica" del PLL)

Clases de clocks
En esta familia de uC podemos encontrarnos con varios clocks distintos,
los cuales pueden ser:
- Cclk: core clock, el reloj que utilizar el ncleo del uC para ejecutar las

Fecha de
Ingreso:
octubre2009
Ubicacin:
Bs. As.

distintas instrucciones.
- Pclk: peripheral clock, el reloj que utilizarn los distintos perifricos, ej.
uart, timer, SPI, ADC, etc. Dicho reloj puede ser distinto en c/perifrico.
- Wd_clk: el reloj que utilizar el watch dog.
- Usb_clk: el reloj que utilizar el puerto USB.

Mensajes:
2.508

Osciladores para generar los clocks

Podemos elegir varias formas de generar nuestros relojes, usando:


- Cristal externo (1MHz hasta 25MHz): dependiendo del cristal nos
brindar una buena base de tiempo (relojes ms precisos)
- Oscilador interno RC (4MHz): no es una buena base de tiempo y no se
recomienda su uso para el Usb_clk.
- Cristal RTC (32,768kHz): cristal de baja frecuencia para una buena
base de tiempo usada para el RTC interno del uC. Tambin se lo puede
utilizar como generador de clock, con el costo de hacer trabajar al PLL al
mximo (mayor consumo e inestabilidad).

Diagrama en bloques de la generacin de los Clocks

Las etapas anuladas pertenecen al generador de clock del Usb (no es lo


que nos interesa en estos momentos).

De izquierda a derecha, siguiendo las flechas, podemos ver las opciones


que tenemos a la hora de generar nuestros relojes:
- Flecha roja: que clase de oscilador elegimos (registro CLKSRCSEL).
- Flecha verde: se evita el uso del PLL (registro PLL0CON).
- Flecha azul: se utiliza el PLL (registro PLL0CON).
- Flecha marrn: es el reloj a la salida del PLL (estando activado o no).
- Flecha roja: es el reloj del Cclk que pasar previamente por un divisor
de frecuencia "CPU CLOCK DIVIDER" (registro CCLKCFG).
- Flecha verde: sern los distintos Pclk de c/perifrico, que previamente
pasarn por un divisor de frecuencia propio "PERIPHERAL CLOCK
DIVIDER"(registros PCLKSEL0/1).
Por otro lado, el reloj del watch dog podr depender de:
- Oscilador interno.
- Oscilador por RTC.
- Un reloj externo.

Qu opcin vamos a elegir?


La idea del tutorial es llevar al uC a su mxima frecuencia de core
(100MHz) usando un cristal externo de 12MHz (a sabiendas de un mayor
consumo), para aprender la configuracin ms compleja que es usando
el PLL, ya que sin el uso del PLL nuestra mxima frecuencia solo
dependera del cristal externo que usemos (25MHz mximo).
Por lo tanto siguiendo el diagrama anterior, nuestro camino ser:
- Flecha roja => cristal externo de 12MHz
- Flecha azul => usaremos el PLL0
- Flecha marrn => al usar el PLL0 la PLLCLK podr estar en entre
275MHz y 550MHz (requerimiento propio del PLL0, ya lo vamos a ver).
- Flecha roja => en funcin de nuestro PLLCLK configuraremos el "CPU
CLOCK DIVIDER" para obtener un CCLK de 100MHz.
- Flecha verde => dejaremos configurados a todos los relojes de
perifricos en 25MHz usando el "PERIPHERAL CLOCK DIVIDER" en 4

(100MHz/25MHz=4).

PLL0
Diagrama en bloques:

Sin entrar en demasiados detalles, los elementos ms destacados son:


- Clock de entrada (32kHz hasta 25MHz).
- Divisor de frecuencia de entrada "N".
- Divisor de frecuencia dentro del lazo de realimentacin "M".
- Clock de salida que podr ir de 275MHz hasta 550MHz, tambin
llamada FCCO.
Bits de control:
- PLLC: conexin o no de PLL0.
- PLLE: habilitacin del PLL0.
- PLOCK: confirmacin de que el PLL0 enganch en la frecuencia
deseada.

Para poder manejar el PLL es necesario configurar correctamente sus


registro (cosa que veremos en el cdigo), por lo tanto recomiendo leer la
hoja de datos en la pgina 35. Los registros a utilizar sern:
- PLL0CON
- PLL0CFG
- PLL0STAT
- PLL0FEED
La hoja de datos recomienda usar el PLL a la menor frecuencia posible
por 2 motivos:
- Menor consumo.
- Menos errores de enganche => mejor reloj a la salida.
Por lo tanto recomienda que los valores del divisor M y el divisor N sean
los ms bajos posibles.

Procedimiento de configuracin del PLL0 (usando


polling)
Aclaracin: como la configuracin solo se d durante el arranque del uC
(es decir una vez), no le veo demasiada utilidad el hecho de utilizar
interrupciones para saber si el PLL enganch o no, entonces por sencillez
decid usar un polling para saber cuando el PLL enganch.
1- Definir la frecuencia de salida del PLL0 en funcin a la CCLK deseada.
Hay que recordar que del PLLCLK (flecha marrn) al CCLK (flecha roja)
hay un divisor de frecuencia ("CPU CLOCK DIVIDER"), por lo tanto del
diagrama en bloques del oscilador si partimos de derecha a izquierda
(de la salida al comienzo del bloque), debemos multiplicar la frecuencia:
Definimos CCLK => multiplicamos por el valor entero del divisor "CPU
CLOCK DIVIDER" => Obtenemos PLLCLK=FCC0
Si nuestra CCLK=100MHZ el "CPU CLOCK DIVIDER" quedar definido en
funcin del rango aceptable del FCCO (257MHz a 550MHz), mientras
menor sea el FCCO, mejor configurado estar el PLL0.

(fuera de
rango)
(fuera de
rango)
(dentro del
rango)
Llegamos a la conclusin que con un "CPU CLOCK DIVIDER" igual 3 se
consigue un FCCO dentro del rango del PLL. Por ej. un "CPU CLOCK
DIVIDER" igual 4 tambin sera vlido, pero la configuracin no sera la
ptima (como puse arriba, menor FCCO, mejor).
Resumiendo de "1-", hasta ahora sabemos que:
- CCLK=100MHz.
- "CPU CLOCK DIVIDER" igual 3.
- FCCO=300MHz.
- Frecuencia de entrada del PLL=12MHz (la frecuencia del cristal
externo).
2- Sabiendo el valor de FCCO y frecuencia de entrada, averiguamos el
valor de "M" imponiendo distintos valores de "N" hasta que el valor de
"M" sea un entero, segn la frmula que brinda la hoja de datos:

(valor no
entero, por lo tanto no es vlido)
(valor entero!)

En caso de no conseguir un valor de "N" que haga a "M" un valor entero,


se deber replantear la CCLK en funcin de la frecuencia de cristal que
se tenga.
3- Configurar el PLL0 segn estos pasos:
- Elegir el oscilador => registro CLKSRCSEL (en nuestro caso cristal
externo).
- Deshabilitar y desconectar el PLL => registro PLL0CON.
- Realizar Feeding => registro PLL0FEED (procedimiento que indica la
hojas de datos => PLL0FEED=0xAA y luego PLL0FEED=0x55).
- Definir los valores de "N-1" y "M-1" => registro PLL0CFG (en nuestro
caso 1 y 24 respectivamente).
- Realizar Feeding => registro PLL0FEED.
- Habilitar el el PLL => registro PLL0CON.
- Realizar Feeding => registro PLL0FEED.
- Hacer polling hasta que el PLL enganche => bit PLOCK en el registro
PLL0STAT.
- Conectar el PLL => registro PLL0CON.
- Realizar Feeding => registro PLL0FEED.
- Configurar el "CPU CLOCK DIVIDER" => registro CCLKCFG (en nuestro
caso 3 => 100MHz).
- Configurar el "PERIPHERAL CLOCK DIVIDER" => registros PCLKSEL0/1
(en nuestro caso 4 => 25MHz).
Eso sera la parte terica de como se deber configurar el PLL0 para
poder obtener una cierta frecuencia de Core. Ms adelante subir una
breve explicacin de como funcionan los timers para luego ir a un
ejercicio y ver como se configura todo desde el cdigo.
Me Gusta

A Basalto, erios, ciernes y a otros 1 les gusta esto.

Mensaje inapropiado?

22/04/20
13

Citar
#4

cosmef
ulanito
04

Segunda Parte - PLL y timers (parte "terica" interrupciones, consumo y


Timers)

Antes de meternos con los timers, es necesario ver como funcionan las
interrupciones y como funciona el control de consumo que tiene el uC.

Interrupciones
Para el que viene de ARM7, notar que en la arquitectura Cortex el vector
Fecha de

de interrupcin ya est definido y simplemente hay que realizar la

Ingreso:

llamada al handler correspondiente, a diferencia de la familia LPC21xx

octubre-

donde uno tena que hacer una definicin del handler algo similar a C

2009
Ubicaci
n: Bs.
As.
Mensajes
: 2.508

aplicado en PC.
Si nos metemos en el captulo 6 (pg. 72), podremos ver el vector de
interrupcin y obtener ms informacin de como funciona en esta
arquitectura, por ej. configurar el nivel de prioridad que tendrn las
distintas interrupcin (0-31, siendo 0 el nivel de mayor prioridad).
Resumiendo, a nosotros lo que nos va interesar es como se crea un
handler y como habilitar las interrupciones de c/perifrico (algunos
poseen habilitacin global y propia).
Habilitacin de la interrupcin de Timer0:
Cdigo PHP:

NVIC_EnableIRQ(TIMER0_IRQn); //Funcin propia de Keil, si bien


recomiendo evitar funciones de 3eros, est es corta y funciona
bien.

Ejemplo de un handler para el Timer0:


Cdigo PHP:

void TIMER0_IRQHandler (void)


{
// nuestra rutina se aconseja que sea lo ms corta posib
le
//Limpieza del flag de interrupcin, a diferencia de otras
familias de uC, los ARM requieren una limpieza por soft :(

Consumo
El uC tiene varios modos de bajo consumo:
- Sleep mode: el Cclk se detiene. Requiere de una interrupcin o Reset
para salir de ese modo.
- Deep Sleep mode: el oscilador principal se detiene al igual que el resto
de los clocks que dependen del mismo, la memoria flash entra en standby. Solo el RTC y el oscilador interno funcionan. Requiere una interrupcin
del RTC o del watch-dog (manejado por el oscilador interno) para salir de
ese modo.
- Power-down mode: funciona al igual que el Deep Sleep mode, pero
adems apaga la memoria flash y el oscilador interno. Requiere una
interrupcin del RTC para salir de ese modo.
- Deep Power-down mode: todo el integrado queda apagado salvo el RTC.
Requiere una interrupcin del RTC para salir de ese modo.
Nosotros en particular vamos a usar el Sleep mode cuando entremos en
un loop de polling, de esta forma evitamos exigir al mximo al uC y
reducimos su consumo.
Para poder entrar en este modo es necesario llamar la instruccin
assembler WFI (wait for interrupt), para lo cual usamos nuevamente una
funcin de Keil que nos permite llamar a dicha instruccin desde C:
Cdigo PHP:

__wfi();

//Sleep-Mode

Una vez que llamamos a la funcin, solo podremos salir de ella cuando se
produzca una interrupcin, de lo contrario el uC seguir en Sleep mode.
Para ms informacin de como entrar en los otros modos de bajo

consumo, ver el captulo 4 seccin 8 pgina 58.


Control de consumo en base a los perifricos:
Por otro lado, el uC nos permite reducir el consumo habilitando o no la
alimentacin de los distintos perifrico, con lo cual si solo vamos a usar el
Timer0 y la Uart0, se pueden apagar el resto de los perifricos no
utilizados. Para poder realizar esto, utilizaremos el registro PCONP y
colocando en 1 los distintos bits habilitaremos los perifricos y
colocandolos en 0 los deshabilitaremos (tendremos 32 bits para 32
perifricos distintos).
Por lo tanto, a la hora de utilizar un perifrico, lo primero que se debe
hacer es habilitar su alimentacin, de lo contrario no funcionar.

Timers
Diagrama en bloques de los Timers

Modo de uso
- Base de tiempo (timer convencional que se suele usar): usando como
patrn el reloj de perifrico previamente configurado, ir contando hasta
que haya un "matcheo" establecido (en espaol sera una igualacin) y se
produzca una seal de interrupcin.
- Captura: mediante una seal conectada a un puerto capture se realiza
un conteo cuando en la seal hay una presencia de un flanco
ascendente/descendente hasta que haya un matcheo establecido y se
produzca una seal de interrupcin.
La idea es dar una breve explicacin del timer como base de tiempo por
lo que les recomiendo que luego de esta prctica y usando la hoja de

datos, prueben el otro modo, no debera resultarles difcil.


En base al diagrama en bloques, vemos que el timer como base de
tiempo cuenta con un pre-escaler de 32 bits (2^32 cuentas), seguido del
contador propiamente dicho tambin de 32 bits (2^32 cuentas), esto
significa que una vez que se alcance la cuenta final del pre-escaler recin
se producir una cuenta en el contador y este proceso se repetir hasta
alcanzar el matcheo establecido (en los registros match, se puede usar
1 o varios como despus voy a mencionar) . Por lo tanto nuestro tiempo
quedar definido de la siguiente forma:

Por lo tanto, simplificando esa cuenta haciendo


Cuenta_inicial_preescaler=0 y Cuenta_inicial_contador=0, el tiempo
queda definido como:

Entonces en base a lo visto en el mensaje anterior Pclk=25MHz, si


quisieramos configurar al timer para que se produzca una interrupcin c/1
mSeg podramos hacer esto:

Si ahora que tenemos definida la base de tiempo de 1 mSeg, si


quisieramos configurar al timer para que se produzca una interrupcin c/1
Seg podramos multiplicar x1000 esa base de tiempo usando el contador:

Entonces resulta bastante sencillo de configurar y mediante el uso del


pre-escaler se puede fijar las escalas de tiempo:
Cuenta_final_pre-escaler=25 => uS
Cuenta_final_pre-escaler=250=> decenas de uS
Cuenta_final_pre-escaler=2500=> centenas de uS

Cuenta_final_pre-escaler=25000=> mS
Para luego usar el contador como ajuste fino del tiempo en la escala
deseada.
Tengan en cuenta que incluso se pueden usar mltiples matcheos, es
decir supongamos que en 100mSeg deseamos que salte una interrupcin
a los 10mSeg, 45mSeg y 100mSeg, simplemente usando varios registros
de match que los timers poseen, podemos fijar esos tiempos y recin
resetear la cuenta cuando se alcanza los 100mSeg.
Tambin permite generar una seal fsica en los puertos match del uC
cuando se alcanza un matcheo.
Como vern la cantidad de opciones que nos permiten los timers resulta
interesante y vasta.
Registros que usaremos en cdigo:
- TCR
- TC
- PR
- PC
- MR0/1..etc (puede ser cualquiera)
- MCR
Recomiendo leer la hojas de datos en la pgina 490.
Me Gusta

A erios le gusta esto.


ltima edicin por cosmefulanito04; 22/04/2013 a las 17:54

Mensaje inapropiado?

24/04/
2013

Citar
#5

cosm

Segunda Parte - PLL y timers (cdigo)

efula

En base a los mensajes anteriores, ya tenemos una cierta idea de como

nito0

funciona el oscilador generador de relojes, el PLL, el control consumo, las

rutinas de interrupcin y los timers. Ahora vamos a tratar de resolver el


primer ejercicio de los leds, pero usando un timer para fijar 10 Seg como
base de tiempo.
Para que se entienda mejor el cdigo, voy a separarlo en distintos archivos:

Fecha
de
Ingre
so:
octub
re-

- defines.c
- configuracion_PLL.c
- perifericos.c
- interrupciones.c
- main.c (voy a presentar dos posibles soluciones)
- delay.c (solo utilizado en una solucin)

2009
Ubica
cin:

defines.c

Bs.

Cdigo PHP:

As.

typedef

unsigned char u8;

Mensa

typedef

unsigned int u16;

typedef

unsigned long int u32;

jes:
2.508

#define LED_1 (1<<25)

//Puerto 3.25

#define LED_2 (1<<26)

//Puerto 3.26

#define TIEMPO_CAMBIO_DE_ESTADO

10

Lo ms destacado ah, es la nueva declaracin de los tipos de variables,


ahora en vez de tener que usar "unsigned char" a la hora de declarar ese
tipo de variables, puedo usar simplemente "u8".
configuracion_PLL.c
Cdigo PHP:

//------------- Configuracin PLL --------------//


#define

CLK_RC_INTERNAL

#define

CLK_XTAL

#define

CLK_XTAL_RTC

0
1
2

//------------- Configuracin PLL --------------//


#define PLOCK (1<<26)

void configurar_pll(u8 clk_source,u16 valor_m,u8 valor_n,u8 divi


sor_cclk,u32 divisor_per0,u32 divisor_per1)
{
LPC_SC->CLKSRCSEL=clk_source;

//Elijo la fuente de CLK pa

ra el PLL0
/*
Ejemplo de Configuracin del PLL:
XTAL => PLL => FCCO => DIV1 => CCLK================> Nc
leo
|=> DIV2 => FPCLK => Per
ifricos
FCCO = (2 M FIN) / N
M = (FCCO N) / (2 FIN)
N = (2 M FIN) / FCCO
M y N se configuran en el registro PLLCFG, bits:
.0 - 14 : M (Valores posibles: 6 a 512 -> Para Cristales
de alta frecuencia)
.23 - 16 : N (Valores posibles: 1 a 32)
Se configura DIV1 mediante el registro CCLKCFG:
.0 - 255: 1 a 256
Se configura con 2 bits c/perifrico el DIV2 mediante los re
gistros PCLKSEL0/1:
.00: FCLK/4
.01: FCLK
.10: FCLK/2
.11: FCLK/8

=> excepto CAN => FCLK/6

A tener en cuenta:
- Core-Clock mxima segn la especificacin del modelo

- 275MHz < FCCO < 550MHZ


- Cada cambio que se realice se deber completar con el
uso del registro PLLFEED segn la secuencia que indica la hoja d
e datos.
*/
/*
Configuracin del PLL:
Se desea Core-Clock=100MHz a partir de un cristal de 12MHz:
Se debe cumplir con 275MHz < FCCO < 550MHZ
FCCO=300MHz => un n entero de la frecuencia deseada => FCLK
=FCCO/3 => CCLKCFG=2 (DIV1)
M = (FCCO N) / (2 FIN)

=> M=(300MHz*2)/

(2*12MHz)=600/12=25
En el PLLCFG se debe ingresar como M-1=24 y N-1=1
*/
// Se desactiva el PLL
LPC_SC->PLL0CON=0;
LPC_SC->PLL0FEED=0xAA;
LPC_SC->PLL0FEED=0x55;
LPC_SC->PLL0CFG=((valor_n-1)<<16)+((valor_m&0x7fff)-1);
/Defino N y M

LPC_SC->PLL0FEED=0xAA;
LPC_SC->PLL0FEED=0x55;
// Se habilita el PLL
LPC_SC->PLL0CON=0x1;
LPC_SC->PLL0FEED=0xAA;
LPC_SC->PLL0FEED=0x55;
// Espera hasta que el PLL enganche a la frecuencia deseada
while(!(LPC_SC->PLL0STAT & PLOCK)) ;

// Se conecta el PLL para generar los clocks


LPC_SC->PLL0CON=3;
LPC_SC->PLL0FEED=0xAA;
LPC_SC->PLL0FEED=0x55;
// Se configura el divisor del Clock - DIV1
LPC_SC->CCLKCFG=divisor_cclk-1;

// divisor CPU-Clock

// Se configuran el clock de los perifricos en funcin del


Core-Clock - DIV2
LPC_SC->PCLKSEL0=divisor_per0;
LPC_SC->PCLKSEL1=divisor_per1;
}
Vean los pasos de configuracin:
1- Deshabilito y desconecto el PLL0 + Feed.
2- Fijo el valor de M y N + Feed.
3- Habilito el PLL0 + Feed.
4- Espero a que enganche el PLL0 a la frecuencia deseada.
5- Se conecta el PLL para generar los clocks.
6- Se fija el valor del divisor del Cclk.
7- Se fija el valor del divisor de c/u de los Pclk.
perifericos.c
Cdigo PHP:

//--------------------------------------- TIMERS ----------------------------------------------------------------//


#define PCTIM0

#define MR0I

#define MR0R

#define MR0S

#define COUNTER_EN
#define COUNTER_R

0
1

void configura_timer0(u32 preescaler,u32 matcheo)

{
LPC_SC->PCONP|=(1<<PCTIM0); //Habilito el timer0 en el contr
ol de consumo de los perifricos
/* Los timers tienen 2 contadores, 1 es el prescaler y el ot
ro es el contador en si mismo, ambos tienen registros son de 32
bits
por lo tanto por c/u puedo contar 2^32, osea como mximo
podria contar hasta 2^64*Base de tiempo ---> muchoooos das :)
El timer sera una cosa as:

Port-clk --> Prescaler -->

Contador (Nota: Fcristal --> PLL --> Core-clock --> Divisor -->
Port-clock)
- Prescaler -> 2 registros
. TxPR: Es el valor de la cuenta final del prescale
r.
. TxPC: la cuenta del prescaler, tambin permite fi
jar desde donde comienza a contar el prescaler, cuando sea igual
a TxPR, desborda y empieza de nuevo, mandandole una cuenta al ot
ro contador
- Contador -> 4 registros
. TxTCR: Es un registro de control, si vale:
-0: el contador no cuenta.
-1: el contador cuenta.
-2: reseteo el contador.
. TxTC: la cuenta del contador, tambin permite fijar
el valor de inicio del contador.
. TxMRx: Son varios registros (segun el timer pueden s
er 4 o 3), ac se pondr el valor de la cuenta q se desea alcanz
ar con el contador, cuando TxTC sea igual se produce un evento.
Son varios porq se lo puede configurar para q envie
un evento en cuentas distintas, ej:
- TxMR0 = 34; Al llegar ac se produce un evento, pe
ro el contador sigue
- TxMR1 = 45; Al llegar ac se produce otro evento
. TxMCR: Sirve para configurar q accin tomar cuando s
e produce un evento, es de 32 bits y c/3bits se configura un MRx

distinto:
S se produce un evento en MRx y TxMCR vale:
- 001: lanza una interrupcion
- 010: se resetea el contador
- 100: se para el contador
Las 3 acciones se pueden combinar, osea interrumpir
y resetear al mismo tiempo.
*/
//------------------ Configuracin del Timer ----------------------------------------------//
LPC_TIM0->TCR=0;

//

el contador no cuenta
LPC_TIM0->TC=0;

//

el contador comienza de 0
LPC_TIM0->PR=preescaler;

// configur

o la cuenta del prescaler tal q le mande una cuenta al contador


c/ 1 mSeg
LPC_TIM0->PC=0;

//

el prescaler comienza de 0
LPC_TIM0->MR0=matcheo;

// config

uro la cuenta del MR0 tal q cuente 1000 mSeg osea 1 seg
LPC_TIM0->MCR=(1<<MR0R)|(1<<MR0I);

// configuro q al prod

ucirse un evento en MR0, se lance una interrupcion y se resetee


el contador
//-----------------------------------------------------------------------------------------//
LPC_TIM0->TCR=(1<<COUNTER_EN);

// el contador em

pieza a contar
}
//--------------------------------------- TIMERS ----------------------------------------------------------------//
Vean los pasos de configuracin:
1- Habilito la alimentacin del Timer0.
2- Paro el contador.
3- Hago 0 la cuenta inicial del contador.

4- Fijo la cuenta final del pre-escaler.


5- Hago 0 la cuenta inicial del pre-escaler.
6- Fijo la cuenta final del contador mediante el Match0.
7- Configuro el evento una vez que se llegue a la cuenta de Match0 (reset e
interrupcin).
8- Arranco el contador.
interrupciones.c
Cdigo PHP:

void habilitar_interrupciones()
{
NVIC_EnableIRQ(TIMER0_IRQn);

//Timer 0

}
//--------- Rutina de la interrupcion timer0 --------------------//
void TIMER0_IRQHandler (void)
{
flag_timer0=1;
LPC_TIM0->IR=1; // Limpio la interrupcin por match0 --> Pag
. 493
}
//--------- Rutina de la interrupcion timer0 --------------------//
Rutina de interrupcin del timer 0, fijense que es muy importante limpiar el
flag de su interrupcin. Adems una funcin de habilitacin de las
interrupciones que ahora no tiene mucho sentido, pero que a medida que
vayamos agregando perifricos se irn agregando a esa funcin.
delay.c (usado solo para la primera variante de main)
Cdigo PHP:

void delay_us(u32 tiempo_us)


{
configura_timer0(25,tiempo_us);
y Xcuentas => XuSeg

//Pre-escaler 25 => 1uSeg

flag_timer0=0;
while(!flag_timer0)
__wfi();

//Sleep-Mode

LPC_TIM0->TCR=0; // paro el contador


}
void delay_ms(u32 tiempo_ms)
{
configura_timer0(25000,tiempo_ms);

//Pre-escaler 250000=>

1mSeg y Xcuentas => XmSeg


flag_timer0=0;
while(!flag_timer0)
__wfi();

//Sleep-Mode

LPC_TIM0->TCR=0; // paro el contador


}
void delay_s(u32 tiempo_s)
{
u32 cont;
for(cont=0;cont<tiempo_s;cont++)
delay_ms(1000);
}
Estas funciones son vlidas solo cuando se usa un Pclk=25MHz. Es
interesante ver que cuando se est esperando, se utiliza la funcin "__wfi"
lo que hace que el uC en todo ese tiempo est en modo sleep.
Por otro lado se puede ver que la cantidad de cuentas para generar 1uS es
de solo 25, por lo que se obtendr un error de cuantizacin de 1/25=0,04
cuentas. Para mejorar ese inconveniente, se podra aumentar el Pclk a
100MHz, logrando as que se necesiten 100 cuentas para obtener 1uS y
bajando el error de cuantizacin a 1/100=0,01 cuentas.

main.c (primera variante)


Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "delay.c"
int main()
{
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12MHz

=> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;

//Decl

aro al puerto 3 como GPIO


LPC_GPIO3->FIODIR|=LED_2|LED_1;

//Defino al p

uerto como salida


LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => le

d apagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => le

d encendido
habilitar_interrupciones();
while(1)
{
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3->FIOSET|

=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3->FIOCLR|
=LED_2;}
delay_s(TIEMPO_CAMBIO_DE_ESTADO);
}
}
Como vern el cdigo es bastante sencillo:
1- Configuro PLL para que use un cristal externo, M=25, N=2, divisor del
Cclk=3, divisor de todos los perifricos =4.
2- Inicializo los puertos como GPIO.
3- Habilito interrupciones.
4- Entro en el While principal donde cambiar el estado de los leds, esperar
con un delay de 10 seg y repetir el proceso una y otra vez.
Que desventaja tiene este cdigo?
Durante los 10 Seg del delay el uC no puede hacer absolutamente nada, es
decir que esa funcin delay es totamente bloqueante. Para este ejercicio,
realmente mucho no importa esto, pero si por ej. tuvieramos que estar
pendientes de otro proceso, no podramos hacer absolutamente nada, recin
una vez que pasen los 10 Seg podramos hacer algo, por lo tanto esta
solucin es muy precaria.
main.c (segunda variante)
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0;
//------- Variables globales para las interrupciones ------------//

#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
int main()
{
u8 cont=TIEMPO_CAMBIO_DE_ESTADO;
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12MHz

=> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;

//Declaro al puerto 3 como

GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;

//Defino al puerto co

mo salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => le

d apagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => le

d encendido
configura_timer0(25000,1000);

//Pre-escaler 250000 => 25M

Hz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg


habilitar_interrupciones();
while(1)
{
if(!cont)
{
cont=TIEMPO_CAMBIO_DE_ESTADO;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3->FIOSET|
=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3->FIOCLR|
=LED_2;}
}

__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;
cont--;
}
}
}
Pasos similares, pero ahora inicializo el timer0 para que genere una
interrupcin c/1 Seg.
Cuando entra al While principal pregunta si la variable "cuenta" es igual a 0,
de ser 0 cambia el estado del los leds y vuelve a la variable "cuenta" a 10,
de lo contrario sigue. Luego pregunta se se produjo o no una interrupcin
mediante el "flag_timer0", en caso afirmativo vuelve a 0 el "flag_timer0" y
resta a la variable "cuenta", de lo contrario el uC queda en sleep-mode
hasta que aparezca una nueva interrupcin.
Como vern, en esta variante, el uC esta disponible en la mayora del
tiempo y solo se necesita una interrupcin para sacarlo del sleep-mode, esto
en el prximo ejercicio resulta en una ventaja.
Para el prximo mensaje vamos a ver la interrupcin externa, pero ya
pudiendo manejar el PLL, el resto les va a resultar medianamente sencillo.
Me Gusta

ltima edicin por cosmefulanito04; 24/04/2013 a las 17:57

Mensaje inapropiado?

30/04/2
013

cosme

Citar
#6

Tercera Parte - Interrupciones externas

Si pudieron entender como configurar el PLL y los timers ya creo que estn

fulanit
o04

en condiciones de poder entender por si mismos la parte terica de las


interrupciones externas. Dicha teora la pueden sacar de la hoja de datos
en el "Chapter 3: LPC17xx System control" en la seccin 6 (pg. 23).
Lo ms destacado es:

Fecha
de

- Averiguar como se debe configurar el PIN para que trabaje como EXT"x",
registros PINSEL"x".

Ingreso
:

- Elegir el modo de disparo, registro EXTMODE.

octubre
-2009
Ubicaci
n: Bs.

- Elegir la polaridad del disparo, estado alto/flanco ascendente o estado


bajo/flanco descendente, registro EXTPOLAR.

As.
Mensaj
es:
2.508

A la larga, es simplemente ver la hoja de datos y saber que bits se de


c/registro se deben configurar, nada difcil despus de lo visto en los
anteriores mensajes.
Vayamos directamente al cdigo, ejercicio propuesto:
Se desea encender y apagar un led cada 5 segundos fijados con un timer,
mediante el pulsador 2 (key2 en el esquemtico) por cada vez que sea
presionado, agregar 5 segundos. Hacer lo mismo con el pulsador 1 (key1),
pero a la inversa, restar 5 segundos.
Los distintos archivos ".C" del anterior mensaje siguen siendo tiles, ahora
de esos archivos voy agregar las funciones necesarias teniendo las
anteriores que ya sub (obviamente ahora no voy a volver a subirlas).
Para poder solucionar este ejercicio es necesario tener en cuenta el rebote
que tendr el pulsador, para lo cual voy a dar dos posibles soluciones, la
sencilla usando un simple delay (ya sabemos las consecuencias que esto
puede traer en la optimizacin del cdigo) y la que utilizar una rutina de
anti-rebote ms compleja, pero mucho mejor.
define.C (se agrega a lo anterior)
Cdigo PHP:

// Defines => ver mensaje anterior

#define TIEMPO_TOGGLE_INICIAL

configuracion_PLL.c => no hay modificaciones.


perifericos.c(se agrega a lo anterior)
Cdigo PHP:

//--------------------------------------- TIMERS ----------------------------------------------------------------//


//Defines -> ver mensaje anterior
//Timer0 -> ver mensaje anterior
void configura_timer1(u32 preescaler,u32 matcheo)
{
LPC_SC->PCONP|=(1<<PCTIM1); //Habilito el timer0 en el cont
rol de consumo de los perifricos
//------------------ Configuracin del Timer ----------------------------------------------//
LPC_TIM1->TCR=0; // el contador no cuenta
LPC_TIM1->TC=0;

// el contador comienza de 0

LPC_TIM1->PR=preescaler; // configuro la cuenta del prescal


er tal q le mande una cuenta al contador c/ 1 mSeg
LPC_TIM1->PC=0;

// el prescaler comienza de 0

LPC_TIM1->MR0=matcheo; // configuro la cuenta del MR0


LPC_TIM1->MCR=(1<<MR0R)|(1<<MR0I); // configuro q al produc
irse un evento en MR1, se lance una interrupcion y se resetee e
l contador
//-----------------------------------------------------------------------------------------//
//LPC_TIM1->IR=1;

//Habilito la interrupcin del timer 1

por match0
LPC_TIM1->TCR=(1<<COUNTER_EN); // el contador empieza a
contar
}

//--------------------------------------- TIMERS ----------------------------------------------------------------//


//--------------------------------------- EXTERNAS ----------------------------------------------------------------//
//----------------- Tipo y polaridad del disparo EXT1 -----------------//
#define EXT1_MODO_NIVEL

#define EXT1_MODO_FLANCO

(1<<1)

#define EXT1_POL_NEG

#define EXT1_POL_POS

(1<<1)

//----------------- Tipo y polaridad del disparo EXT1 -----------------//


//----------------- Configuracin del PIN como EXT1 -------------------//
#define PINSEL_EXT1

(1<<22)

//Bit 23:22 PINSEL4 -> Funcio

nando como /EXT1


#define PIN_EXT1

(1<<11)

//----------------- Configuracin del PIN como EXT1 -------------------//


void configurar_externa_1(u32 modo_disparo,u32 polaridad_del_di
sparo)
{
LPC_PINCON->PINSEL4&=~((3<<22));
LPC_PINCON->PINSEL4|=PINSEL_EXT1;
LPC_SC->EXTMODE|=modo_disparo;
LPC_SC->EXTPOLAR|=polaridad_del_disparo;
}
//----------------- Tipo y polaridad del disparo EXT2 -----------------//
#define EXT2_MODO_NIVEL

#define EXT2_MODO_FLANCO

(1<<2)

#define EXT2_POL_NEG

#define EXT2_POL_POS

(1<<2)

//----------------- Tipo y polaridad del disparo EXT2 -----------------//


//----------------- Configuracin del PIN como EXT2 -------------------//
#define PINSEL_EXT2

(1<<24)

//Bit 25:24 PINSEL4 -> Funcio

nando como /EXT2


#define PIN_EXT2

(1<<12)

//----------------- Configuracin del PIN como EXT2 -------------------//


void configurar_externa_2(u32 modo_disparo,u32 polaridad_del_di
sparo)
{
LPC_PINCON->PINSEL4&=~((3<<24));
LPC_PINCON->PINSEL4|=PINSEL_EXT2;
LPC_SC->EXTMODE|=modo_disparo;
LPC_SC->EXTPOLAR|=polaridad_del_disparo;
}
//--------------------------------------- EXTERNAS ----------------------------------------------------------------//
Se puede ver las funciones que configurarn las EXT1 y 2, simplemente es
ver hoja de datos.
interrupciones.c (se agrega a lo anterior)
Cdigo PHP:

void habilitar_interrupciones() //Funcin modificada, ahora hab


ilita Timer0/1 y EXT1/2
{
NVIC_EnableIRQ(TIMER0_IRQn);

//Timer0

NVIC_EnableIRQ(TIMER1_IRQn);

//Timer1

NVIC_EnableIRQ(EINT1_IRQn);

//Externa 1

NVIC_EnableIRQ(EINT2_IRQn);

//Externa 2

}
//Rutina Timer0 => ver mensaje anterior

//--------- Rutina de la interrupcion timer1 --------------------//


void TIMER1_IRQHandler (void)
{
flag_timer1=1;
LPC_TIM1->IR=1; // Limpio la interrupcin por match0 --> Pa
g. 493
}
//--------- Rutina de la interrupcion timer1 --------------------//
//--------- Rutina de la interrupcion externa 1 --------------------//
void EINT1_IRQHandler (void)
{
flag_ext1=1;
LPC_SC->EXTINT|=(1<<1);

// Limpio la interrupcin extern

a 1
}
//--------- Rutina de la interrupcion externa 1 --------------------//
//--------- Rutina de la interrupcion externa 2 --------------------//
void EINT2_IRQHandler (void)
{
flag_ext2=1;
LPC_SC->EXTINT|=(1<<2);

// Limpio la interrupcin extern

a 2
}
//--------- Rutina de la interrupcion externa 2 --------------------//
main.c (solucin usando delays)

Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_ext1=0,flag_ext2=0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
int main()
{
u16 cont=TIEMPO_TOGGLE_INICIAL;
u8 tiempo_inicial_variable=1;
configurar_pll();// Cristal de 12MHz => CPU-CLK=100MHz y PCLK=25MHz
LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led a

pagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led e

ncendido
configura_timer0(25000,1000);

//Pre-escaler 250000 => 25

MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg


configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);
xterna 1 configurada para que detecte flancos descendentes

//E

configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);

//Ext

erna 2 configurada para que detecte flancos descendentes


habilitar_interrupciones();
while(1)
{
if(!cont)
{
cont=tiempo_inicial_variable*TIEMPO_TOGGLE_INICIAL;

if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3->FIOSET|
=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3->FIOCLR|
=LED_2;}
}
__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;
cont--;
}
if(flag_ext1)
{
flag_ext1=0;
configura_timer1(25000,200);

//Pre-escaler 25000

0=> 1mSeg y 200cuentas => 200mSeg


while(!flag_timer1);

//20mSeg para evitar rebote

flag_timer1=0;
LPC_TIM1->TCR=0; // paro el contador

if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}
if(flag_ext2)
{
flag_ext2=0;
configura_timer1(25000,200);

//Pre-escaler 25000

0=> 1mSeg y 200cuentas => 200mSeg


while(!flag_timer1);

//20mSeg para evitar rebote

flag_timer1=0;
LPC_TIM1->TCR=0; // paro el contador
tiempo_inicial_variable++;
}
}
}
Inconvenientes de esta solucin:
- Durante 200mSeg tenemos al uC bloqueado en sleep-mode.
- No hay verificacin alguna de que el pulsador realmente se presion y de
que no hubo ruido de por medio.
Para plantear la 2da solucin hay que entender que un rebote tiene esta
forma:

Por lo tanto un mtodo para saber si el pulsador realmente fue presionado


y descartar rebotes y ruido es hacer esto:
1- Detectada la interrupcin (en este caso por flanco descendente) esperar
5mSeg.
2- Pasados 5mSeg, preguntar si el PIN se encuentra en el estado correcto
(en este caso debe estar en estado bajo, por ser flanco descendente), para
agregar mayor confiabilidad esperar otros 5mSeg.
3- Pasados otros 5mSeg, nuevamente preguntar el estado del PIN, si es el
correcto dar como vlido la pulsacin, de lo contrario se trata de
ruido/rebote.
4- (Opcional) Esperar un cierto tiempo (ej. 300mSeg o 1Seg) y verificar el
estado del PIN, si sigue en el estado correcto, se toma como vlida otra
pulsacin (esta condicin habilita que el usuario aumente la cuenta
manteniendo el botn pulsado).
5- (Opcional) Esperar a que el PIN vuelva al otro estado para repetir la
secuencia desde 1, de lo contrario, se repite 4.
Entonces en base a eso, se realizarn las siguientes modificaciones:
defines.c => no hay modificaciones.
configuracion_PLL.c => no hay modificaciones.
perifericos.c(se agrega a lo anterior)
Cdigo PHP:

//--------------------------------------- TIMERS ----------------------------------------------------------------//


//Defines
//Timer0
//Timer1
void configura_timer2(u32 preescaler,u32 matcheo)
{
LPC_SC->PCONP|=(1<<PCTIM2); //Habilito el timer0 en el cont
rol de consumo de los perifricos
//------------------ Configuracin del Timer ----------------------------------------------//

LPC_TIM2->TCR=0; // el contador no cuenta


LPC_TIM2->TC=0;

// el contador comienza de 0

LPC_TIM2->PR=preescaler; // configuro la cuenta del prescal


er tal q le mande una cuenta al contador c/ 1 mSeg
LPC_TIM2->PC=0;

// el prescaler comienza de 0

LPC_TIM2->MR0=matcheo; // configuro la cuenta del MR0


LPC_TIM2->MCR=(1<<MR0R)|(1<<MR0I); // configuro q al produc
irse un evento en MR1, se lance una interrupcion y se resetee e
l contador
//-----------------------------------------------------------------------------------------//
LPC_TIM2->TCR=(1<<COUNTER_EN); // el contador empieza a con
tar
}
//--------------------------------------- TIMERS ----------------------------------------------------------------//
//--------------------------------------- EXTERNAS ----------------------------------------------------------------//
//Se mantiene lo anterior
//----------------- Estados Anti-Rebote -------------------------------//
#define ANTI_REB_IDLE
#define ANTI_REB_FLANCO_DETECTADO

0
1

#define ANTI_REB_5MSEG

#define ANTI_REB_10MSEG
#define ANTI_REB_300MSEG

3
4

//----------------- Estados Anti-Rebote -------------------------------//


int anti_rebote_ext1(u8* variable_estado)
endente
{
switch(*variable_estado)
{
case ANTI_REB_IDLE:

//Para flanco desc

{
configura_timer1(25000,5);

//Pre-escaler

250000=> 5mSeg y 5cuentas => 5mSeg


flag_timer1=0;
*variable_estado=ANTI_REB_5MSEG;
break;
}
case ANTI_REB_5MSEG:
{
if(flag_timer1)
{
flag_timer1=0;
if(!(LPC_GPIO2->FIOPIN&PIN_EXT1))

//

Verifico el estado Bajo despus de 5mSeg


*variable_estado=ANTI_REB_10MSEG;
else
{
LPC_TIM1->TCR=0; // Paro el con
tador
*variable_estado=ANTI_REB_IDLE;
// Vuelvo al 1er estado
LPC_PINCON->PINSEL4|
=PINSEL_EXT1;

//Habilito EXT1
flag_ext1=0;

o a esperar por otra interrupcin


}

//Rebote detectado

}
break;
}
case ANTI_REB_10MSEG:
{
if(flag_timer1)
{

//

Vuelv

flag_timer1=0;
if(!(LPC_GPIO2->FIOPIN&PIN_EXT1))

//

Verifico el estado Bajo despus de 5mSeg


{
configura_timer1(25000,300);
//Pre-escaler 250000=> 300mSeg y 300cuentas => 300
mSeg
*variable_estado=ANTI_REB_300MS
EG;

//Espero 1 seg. para confirmar si el botn sigue presion

ado
return 1;
//Pulsacin confirmada
}
else
{
LPC_TIM1->TCR=0; // Paro el con
tador
*variable_estado=ANTI_REB_IDLE;
// Vuelvo al 1er estado
LPC_PINCON->PINSEL4|
=PINSEL_EXT1;

//Habilito EXT1
flag_ext1=0;

//

Vuelv

o a esperar por otra interrupcin


}

//Rebote detectado

}
break;
}
case ANTI_REB_300MSEG:
{
if(flag_timer1)
{
flag_timer1=0;
if(!(LPC_GPIO2->FIOPIN&PIN_EXT1))

//

Verifico el estado Bajo despus de 5mSeg


return 1;
//Pulsacin repetida confirmada po

r botn apretado
else
{
LPC_TIM1->TCR=0; // Paro el con
tador
*variable_estado=ANTI_REB_IDLE;
// Vuelvo al 1er estado
LPC_PINCON->PINSEL4|
=PINSEL_EXT1;

//Habilito EXT1
flag_ext1=0;

//

Vuelv

o a esperar por otra interrupcin


}

//Pulsador liberado

}
break;
}
}
return -1;
}
int anti_rebote_ext2(u8* variable_estado)

//Para flanco desc

endente
{
switch(*variable_estado)
{
case ANTI_REB_IDLE:
{
configura_timer2(25000,5);

//Pre-escaler

250000=> 5mSeg y 5cuentas => 5mSeg


flag_timer2=0;
*variable_estado=ANTI_REB_5MSEG;
break;
}
case ANTI_REB_5MSEG:
{

if(flag_timer2)
{
flag_timer2=0;
if(!(LPC_GPIO2->FIOPIN&PIN_EXT2))

//

Verifico el estado Bajo despus de 5mSeg


*variable_estado=ANTI_REB_10MSEG;
else
{
LPC_TIM2->TCR=0; // Paro el con
tador
*variable_estado=ANTI_REB_IDLE;
// Vuelvo al 1er estado
LPC_PINCON->PINSEL4|
=PINSEL_EXT2;

// Habilito EXT2
flag_ext2=0;

//

Vuelv

o a esperar por otra interrupcin


}

//Rebote detectado

}
break;
}
case ANTI_REB_10MSEG:
{
if(flag_timer2)
{
flag_timer2=0;
if(!(LPC_GPIO2->FIOPIN&PIN_EXT2))

//

Verifico el estado Bajo despus de 5mSeg


{
configura_timer2(25000,300);
//Pre-escaler 250000=> 300mSeg y 300cuentas => 300
mSeg
*variable_estado=ANTI_REB_300MS
EG;

//Espero 1 seg. para confirmar si el botn sigue presion

ado
return 1;

//Pulsacin confirmada
}
else
{
LPC_TIM2->TCR=0; // Paro el con
tador
*variable_estado=ANTI_REB_IDLE;
// Vuelvo al 1er estado
LPC_PINCON->PINSEL4|
=PINSEL_EXT2;

// Habilito EXT2
flag_ext2=0;

//

Vuelv

o a esperar por otra interrupcin


}

//Rebote detectado

}
break;
}
case ANTI_REB_300MSEG:
{
if(flag_timer2)
{
flag_timer2=0;
if(!(LPC_GPIO2->FIOPIN&PIN_EXT2))

//

Verifico el estado Bajo despus de 5mSeg


return 1;
//Pulsacin repetida confirmada po
r botn apretado
else
{
LPC_TIM2->TCR=0;
// Paro el contador
*variable_estado=ANTI_REB_IDLE;
// Vuelvo al 1er estado
LPC_PINCON->PINSEL4|
=PINSEL_EXT2;

// Habilito EXT2
flag_ext2=0;
//

Vuelvo a esperar por otra interru

pcin
}

//Pulsador liberado

}
break;
}
}
return -1;
}
//--------------------------------------- EXTERNAS ----------------------------------------------------------------//
Estas rutinas irn efectuando el proceso de anti-rebote descrito
anteriormente y cuando se confirme una pulsacin devolver 1. Es
importante ver tres cosas:
- Necesita usar una variable por referencia (el que no sabe que es esto, le
recomiendo leer algo de C y sobre ese tema, pero bsicamente sirve para
evitar el uso de variables globales).
- No es bloqueante, como ya lo veremos en el main, es una funcin que
devuelve de inmediato el estado en el que est la rutina de anti-rebote.
- Una vez que el botn deja de ser presionado, se vuelve a configurar el
puerto como EXT"x" (en la rutina de interrupcin veremos que se
deshabilita).
Este tipo de funciones est realizado en base a una mquina de estado
bastante simple, ya que se adapta perfecto al procedimiento de antirebote.
interrupciones.c (se agrega y modifica a lo anterior)
Cdigo PHP:

void habilitar_interrupciones()
{
NVIC_EnableIRQ(TIMER0_IRQn);

//Timer0

NVIC_EnableIRQ(TIMER1_IRQn);

//Timer1

NVIC_EnableIRQ(TIMER2_IRQn);

//Timer1

NVIC_EnableIRQ(EINT1_IRQn);

//Externa 1

NVIC_EnableIRQ(EINT2_IRQn);

//Externa 2

}
//Rutinas de Timer0/1 ya vistas
//--------- Rutina de la interrupcion timer2 --------------------//
void TIMER2_IRQHandler (void)
{
flag_timer2=1;
LPC_TIM2->IR=1; // Limpio la interrupcin por match0 --> Pa
g. 493
}
//--------- Rutina de la interrupcion timer2 --------------------//
//--------- Rutina de la interrupcion externa 1 --------------------//
void EINT1_IRQHandler (void)
{
flag_ext1=1;
LPC_PINCON->PINSEL4&=~((3<<22));

//Vuelvo a configurar c

omo GPIO a la externa1 --> para preguntar el estado del PIN


LPC_SC->EXTINT|=(1<<1);

// Limpio la interrupcin extern

a 1
}
//--------- Rutina de la interrupcion externa 1 --------------------//
//--------- Rutina de la interrupcion externa 2 --------------------//
void EINT2_IRQHandler (void)

{
flag_ext2=1;
LPC_PINCON->PINSEL4&=~((3<<24));

//Vuelvo a configurar c

omo GPIO a la externa2 --> para preguntar el estado del PIN


LPC_SC->EXTINT|=(1<<2);

// Limpio la interrupcin extern

a 2
}
//--------- Rutina de la interrupcion externa 2 --------------------//
Dentro de lo ms destacado, vean que en la rutina EXT1/2 se deshabilita
dicho puerto para que trabaje como EXT y que lo haga como GPIO.
Por qu esto? debido a que es necesario saber el estado del PIN durante
la rutina de anti-rebote y para eso utilizaremos el registro FIOPIN que
pertenece a los GPIO, esto ltimo por ej. lo pueden ver en la linea de la
rutina "if(!(LPC_GPIO2->FIOPIN&PIN_EXT1))".
main.c (solucin usando rutina anti-rebote)
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_ext1=0,flag_e
xt2=0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
int main()

{
u16 cont=TIEMPO_TOGGLE_INICIAL;
u8 tiempo_inicial_variable=1,estado_anti_reb_ext1=ANTI_REB_
IDLE,estado_anti_reb_ext2=ANTI_REB_IDLE;
configurar_pll();// Cristal de 12MHz => CPU-CLK=100MHz y PCLK=25MHz
LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led a

pagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led e

ncendido
configura_timer0(25000,1000);

//Pre-escaler 250000 => 25

MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg


configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

//E

xterna 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);

//Ext

erna 2 configurada para que detecte flancos descendentes


habilitar_interrupciones();
while(1)
{
if(!cont)
{
cont=tiempo_inicial_variable*TIEMPO_TOGGLE_INICIAL;

if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3->FIOSET|
=LED_2;}
else

{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3->FIOCLR|
=LED_2;}
}
__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;
cont--;
}
if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}
}
if(flag_ext2)
{
if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
tiempo_inicial_variable++;
}
}
}
Como pueden ver las grandes ventajas de esta solucin son:
- No es bloqueante (no tengo que esperar 200mSeg para recuperar el
control del uC).
- Posee doble verificacin en 5mSeg y 10mSeg.
- Se puede mantener apretado el pulsador y c/300mSeg se tomar como
vlida una nueva pulsacin (el tiempo puede modificarse, en vez de
300mSeg podran ser 1Seg).

Para el prximo mensaje vamos a ver como funciona la Uart, mientras


tanto les subo el cdigo de estas dos soluciones y el cdigo del mensaje
anterior.
Archivos Adjuntos
Cdigo - Externa - Parte 3.rar (358,1 KB (Kilobytes), 34 visitas)
Cdigo - Timer - Parte 2.rar (350,2 KB (Kilobytes), 38 visitas)
Me Gusta

Mensaje inapropiado?

06/05/201
3

cosmefu
lanito04

Citar
#7

Cuarta Parte - Uart

Diagrama en bloques:

Fecha de
Ingreso:
octubre2009
Ubicacin:
Bs. As.
Mensajes:
2.508

Procedimiento de inicializacin:
1- Habilitar alimentacin de la Uartx (registro PCONP).
2- Configurar el pre-escaler de la Uartx (registro PCLKSEL0/1).
3- Configurar el baud rate mediante el Divisor Latch (registros DLL/M).
Para modificar el valor del Divisor Latch es necesario que el bit Dlab
del registro LCR se encuentre en 1.
4- Luego de configurar el baud rate, dejar en 0 el bit Dlab.
5- Habilitar o no el FIFO para utilizar DMA (opcional).
6- Configurar los Pines para que funcionen como Uartx (registro
PINSELx) y que funcionen como Pull-up (registro PINMODEx).
7- Habilitar la interrupcin por Rx y Tx (registro IER).
8- Configurar el DMA (opcional).

Baud Rate:

Se puede ver que la velocidad depende del Pclock de la Uartx y del


Divisor Latch.
DivAddVal y MulVal se utilizan para realizar un ajuste fino y de esta
forma obtener el menor error posible en la velocidad. Por lo tanto, en
funcin de la velocidad que se obtiene sin estos registros, se evala si
son necesarios o no utilizarlos. En este ejemplo, DivAddVal ser igual a
0.
Ejercicio propuesto:
En base al ejercicio anterior (usando interrupciones externas), se desea
enviar por puerto serie el valor de la cuenta c/1 segundo, mediante el
uso de la tecla "a" modificar la cuenta en forma ascendente y mediante
la tecla "d" en forma descendente. Se desea que el puerto serie trabaje
con 8bits de datos, sin bit paridad y a 9600bps.
Nuevamente usaremos las funciones que ya se desarrollaron
anteriormente y se agregarn las necesarias para resolver este ejercicio.
define.c => no hay modificaciones.
configuracion_PLL.c => no hay modificaciones.
perifericos.c (se agrega a lo anterior)
Cdigo PHP:

//--------------------------------------- TIMERS ----------------------------------------------------------------//


// Todo lo anterior
#define PCTIM3

23

void configura_timer3(u32 preescaler,u32 matcheo)

{
LPC_SC->PCONP|=(1<<PCTIM3); //Habilito el timer3 en el co
ntrol de consumo de los perifricos
//------------------ Configuracin del Timer ----------------------------------------------//
LPC_TIM3->TCR=0; // el contador no cuenta
LPC_TIM3->TC=0;

// el contador comienza de 0

LPC_TIM3->PR=preescaler; // configuro la cuenta del presc


aler tal q le mande una cuenta al contador c/ 1 mSeg
LPC_TIM3->PC=0;

// el prescaler comienza de 0

LPC_TIM3->MR0=matcheo; // configuro la cuenta del MR0


LPC_TIM3->MCR=(1<<MR0R)|(1<<MR0I); // configuro q al prod
ucirse un evento en MR1, se lance una interrupcion y se reset
ee el contador
//-----------------------------------------------------------------------------------------//
LPC_TIM3->TCR=(1<<COUNTER_EN); // el contador empieza
a contar
}
//--------------------------------------- TIMERS ----------------------------------------------------------------//
//--------------------------------------- UARTS -------------------------------------------------------------------//
//------------- Configuracin Uart0 ---------------//
#define UART_DATA_8_BIT
#define UART_DLAB_1
#define UART_THREI

3
(1<<7)
(1<<1)

#define UART_RXI

(1<<0)

#define UART_RLSI

(1<<2)

//------------- Configuracin Uart0 ---------------//


//------------- Baud Rates ------------------------//
#define UART_115200

14

#define UART_57600

27

#define UART_38400

41

#define UART_19200

81

#define UART_9600

163

#define UART_4800

325

#define UART_2400

651

#define UART_1200

1302

//------------- Baud Rates ------------------------//


//----------------- Configuracin del PIN como UART0 -------------------//
#define PINSEL_TX_UART0

(1<<4)

//Bit 5:4 PINSEL0 -> Fun

(1<<6)

//Bit 7:6 PINSEL0 -> Fun

cionando como /TXD0


#define PINSEL_RX_UART0
cionando como /RXD0
//----------------- Configuracin del PIN como UART0 -------------------//
void configurar_uart0(u16 divisor_latch)

//Velocid

ad (baudios)=PCLK/[16*256*DivisorLatch*(1+DivAddVal/MulVal)]
{
LPC_SC->PCONP|=(1<<3); //Habilito la uart0 en el control
de consumo de los perifricos
LPC_PINCON->PINSEL0&=~((3<<6)|(3<<4));
LPC_PINCON->PINSEL0|=PINSEL_RX_UART0|PINSEL_TX_UART0;
//Pines del puerto serie funcionando como tal
LPC_PINCON->PINMODE0&=~((3<<6)|(3<<4));
//Pines con Pull-up interno
LPC_UART0->LCR=UART_DLAB_1|UART_DATA_8_BIT;

//8bit

s de datos, 1 bit de stop, sin paridad, DLAB=1.


LPC_UART0->DLL=(divisor_latch&0xff);

/Parte baja
LPC_UART0->DLM=(divisor_latch>>8)&0xff;

//Par

te alta
LPC_UART0->LCR=UART_DATA_8_BIT;
//8bits de datos, 1 bit de stop, sin paridad, DLAB=0.
LPC_UART0->IER=UART_THREI|UART_RXI;
//Habilito interrupcin por Rx y Tx

}
int recibe_dato_uart0()
{
configura_timer3(25000,33);

//Pre-escaler 250000=> 1mS

eg y 3cuentas => 33mSeg


flag_timer3=0;
while((!flag_timer3)&&(!flag_uart0_rx))

//Espera a ten

er un dato Rx con un time-out de 32mSeg


__wfi();

//Sleep-Mode

if(flag_uart0_rx)
{
flag_uart0_rx=0;
LPC_TIM3->TCR=0; // paro el contador
flag_timer3=0;
return 1;
}
LPC_TIM3->TCR=0; // paro el contador
flag_timer3=0;
return -1;
}
int envia_dato_uart0(u8 dato)
{
configura_timer3(25000,33);

//Pre-escaler 250000=> 1mS

eg y 3cuentas => 33mSeg


flag_timer3=0;
while((!flag_timer3)&&(!flag_uart0_tx))

//Espera a ten

er el puerto este libre con un time-out de 33mSeg


__wfi();

//Sleep-Mode

if(flag_uart0_tx)
{

LPC_UART0->THR=dato;
flag_uart0_tx=0;
LPC_TIM3->TCR=0; // paro el contador
flag_timer3=0;
return 1;
}
LPC_TIM3->TCR=0; // paro el contador
flag_timer3=0;
return -1;
}
//--------------------------------------- UARTS -------------------------------------------------------------------//
Se agreg:
- Inicializacin del Timer 3, usado como time-out para la funciones de
envo y recepcin.
- Inicializacin de la Uart0.
- Funcin bloqueante envia_dato_uart0 con un time-out de 33mSeg.
Espera hasta 33mSeg que el puerto serie est disponible para enviar
datos.
- Funcin bloqueante recibe_dato_uart0() con un time-out de 33mSeg.
Espera hasta 33mSeg que el puerto serie reciba un dato (esta ltima
funcin no ser utilizada).
interrupciones.c (se agrega a lo anterior)
Cdigo PHP:

void habilitar_interrupciones()
{
NVIC_EnableIRQ(TIMER0_IRQn);

//Timer0

NVIC_EnableIRQ(TIMER1_IRQn);

//Timer1

NVIC_EnableIRQ(TIMER2_IRQn);

//Timer2

NVIC_EnableIRQ(TIMER3_IRQn);

//Timer3

NVIC_EnableIRQ(EINT1_IRQn);

//Externa 1

NVIC_EnableIRQ(EINT2_IRQn);

//Externa 2

NVIC_EnableIRQ(UART0_IRQn);

//UART 0

}
//... Rutinas ya vistas
//--------- Rutina de la interrupcion timer3 --------------------//
void TIMER3_IRQHandler (void)
{
flag_timer3=1;
LPC_TIM3->IR=1; // Limpio la interrupcin por match0 -->
Pag. 493
}
//--------- Rutina de la interrupcion timer3 --------------------//
//--------- Rutina de la interrupcion Uart 0 --------------------//
#define UART_INT_TX

(1<<6)

#define UART_INT_RX

(1<<0)

void UART0_IRQHandler (void)


{
u8 dummy=LPC_UART0->IIR;

//Limpiez

a del la interrupcin
if((LPC_UART0->LSR)&(UART_INT_RX))

//Pregunto la fuent

e de interrupcin => Rx o Tx
{
flag_uart0_rx=1;
dato_uart0=(u8)(LPC_UART0->RBR);

//Recibo el dato

}
else
{
flag_uart0_tx=1;
}
}
//--------- Rutina de la interrupcion Uart 0 ----------------

-----//
Lo ms destacado de la rutina de la uart es:
- La necesidad de limpiar el flag de interrupcin mediante una lectura
falsa al registro IIR.
- Averiguar la fuente de interrupcin, si es debido a que el Tx se
encuentra vaco o que se recibi un dato. Para lo cual se utiliza el
registro de estado LSR.
funciones_uart.c
Cdigo PHP:

#define fin_de_linea '\n'


#define fin_de_linea_para_pc '\n'
void convertir_digitos_u8_serie(u8 digitos[],u8 dato)
{
u8 aux;
for(aux=2;aux>0;aux--)
{
digitos[aux]=(dato%10)+0x30;
dato=(int)(dato/10);
}
digitos[0]=dato+0x30;
}
void convertir_digitos_u16_serie(u8 digitos[],u16 dato)
{
u8 aux;
for(aux=4;aux>0;aux--)
{
digitos[aux]=(dato%10)+0x30;
dato=(int)(dato/10);
}

digitos[0]=dato+0x30;
}
void enviar_string_uart0(u8 string[])
{
u8 cont=0;
do{
envia_dato_uart0(string[cont]);
cont++;
if(cont==0xff)
break;
}while(string[cont-1]!=fin_de_linea);
}
u8 envia_u8_string_uart0(u8 dato)

//Convierte un byte (0-

255) en 3 bytes strings


{
u8 digitos[3]={0,0,0};
convertir_digitos_u8_serie(digitos,dato);
envia_dato_uart0(digitos[0]);

//Envia el byte converti

do en un string de 3 bytes
envia_dato_uart0(digitos[1]);
envia_dato_uart0(digitos[2]);
return (digitos[2]+digitos[1]+digitos[0]);
}
u8 envia_u16_string_uart0(u16 dato)

//Convierte un u16 (0-

65536) en 5 bytes strings


{
u8 digitos[5]={0,0,0,0,0};
convertir_digitos_u16_serie(digitos,dato);

envia_dato_uart0(digitos[0]);

//Envia el u16 convertid

o en un string de 5 bytes
envia_dato_uart0(digitos[1]);
envia_dato_uart0(digitos[2]);
envia_dato_uart0(digitos[3]);
envia_dato_uart0(digitos[4]);
return (digitos[4]+digitos[3]+digitos[2]+digitos[1]+digit
os[0]);
}
Para poder trabajar con facilidad el puerto serie, se propone las
siguientes funciones:
- enviar_string_uart0: simplemente manda un string por puerto serie,
requiere el fin de linea '\n'.
- envia_u8_string_uart0: convierte un dato unsigned char en un string
(3 dgitos mx) y lo enva.
- envia_u16_string_uart0: convierte un dato unsigned int en un string (5
dgitos mx) y lo enva.
main.c
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,fl
ag_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_ua
rt0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"

#include "funciones_uart.c"
int main()
{
u16 cont=TIEMPO_TOGGLE_INICIAL;
u8 tiempo_inicial_variable=1,estado_anti_reb_ext1=ANTI_RE
B_IDLE,estado_anti_reb_ext2=ANTI_REB_IDLE,flag_ascendente=0;
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12M

Hz => M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led

apagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led

encendido
configura_timer0(25000,1000);

//Pre-escaler 250000 =>

25MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=


1Seg
configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

/Externa 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);
xterna 2 configurada para que detecte flancos descendentes

//E

configurar_uart0(UART_9600); //9600bps
habilitar_interrupciones();
while(1)
{
if(!flag_ascendente)
{
if(cont==1)
{

cont=tiempo_inicial_variable*TIEMPO_TOGGLE_IN
ICIAL;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
else
{
if(cont==(tiempo_inicial_variable*TIEMPO_TOGGLE_I
NICIAL)-1)
{
cont=0;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;
if(!flag_ascendente)
cont--;
else
cont++;

enviar_string_uart0("Contador= \n");
envia_u16_string_uart0(cont);
enviar_string_uart0(" \n");
}
if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}
}
if(flag_ext2)
{
if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
tiempo_inicial_variable++;
}
if(flag_uart0_rx)
{
flag_uart0_rx=0;
if(dato_uart0=='a')
flag_ascendente=1;

//Cuenta Ascendente

if(dato_uart0=='d')
flag_ascendente=0;
}
}
}

//Cuenta descendente

Me Gusta

A msolinas le gusta esto.


ltima edicin por cosmefulanito04; 06/05/2013 a las 14:43

Mensaje inapropiado?

12/05/2
013

cosme
fulanit

Citar
#8

Quinta Parte - RTC

Diagramas en bloques:

o04

- Alimentacin y oscilador

Fecha
de
Ingreso
:
octubre
-2009
Ubicaci
n: Bs.
As.
Mensaje
s:
2.508

Requiere un oscilador externo de 32,768 kHz para generar un clock de


1Hz. Mediante el uso de una batera de litio, se evita perder la hora

configurada.
- Contadores y alarmas internas

Procedimiento de inicializacin:
1- Habilitar alimentacin del RTC (registro PCONP).
2- Resetear cuenta y deshabilitar conteo (registro CCR).
3- Configurar hora inicial (registros SEC, MIN, HOUR. etc).
4- Habilitar conteo (registro CCR).
5- Configurar hora de alarma (registros ALSEC, ALMIN,. etc) [Opcional].
6- Realizar ajuste de calibracin (registro CALIBRATION) y habilitar
calibracin (registro CCR) [Opcional].
7- Habilitar interrupcin por conteo (registro CIIR) o por alarma (registro

AMR).
Procedimiento de calibracin (Opcional):
Mediante el pin 1.27 configurado en CLKOUT (registro PINSELx), se mide
el desajuste del oscilador del RTC en 1 seg usando un osciloscopio. Se
utiliza dicho valor en los bits CALVAL (registro CALIBRATION).
Almacenamiento de informacin (Opcional):
El RTC nos da la opcin de almacenar en 5 registros de 32 bits
(GPREG0/1/4), cualquier informacin que nos pueda ser de utilidad, de
forma tal que si operamos sin fuente de alimentacin, dicha informacin
seguir estando presente gracias a la batera de litio que utiliza el RTC
para no perder la hora.
Luego de hacer un brevsimo resumen (se recomienda profundizar el tema
con la hoja de datos, Chapter 27: LPC17xx Real-Time Clock (RTC) and
backup registers), seguimos con el cdigo.
Ejercicio parte 1:
Del ejercicio realizado anteriormente con la UART, se pide generar la base
de tiempo de 1 seg mediante el uso del RTC (en ejercicios anteriores
usamos el timer0 como base de tiempo).
defines.c => no hay modificaciones.
configuracion_PLL.c => no hay modificaciones.
funciones_uart.c => no hay modificaciones.
perifericos.c (se agrega a lo anterior)
Cdigo PHP:

//... Todo el cdigo visto anteriormente


//--------------------------------------- RTC ---------------------------------------------------------------------//
#define PCRTC

12

#define CLKEN

#define CTCRST

#define CCALEN

#define RTC_SEG

#define RTC_MIN

#define RTC_HOR

#define RTC_DIA_MES

#define RTC_DIA_SEMANA
#define RTC_DIA_ANIO

4
5

#define RTC_MES

#define RTC_ANIO

#define RTC_TAMANIO

void configurar_hora_rtc(u16 vector_hora_inicial[])


{
LPC_SC->PCONP|=(1<<PCRTC);
//Habilito la RTC en el control de consumo d
e los perifricos
LPC_RTC->CCR=(1<<CTCRST);
//Reseteo el RTC y no habilito la cuenta
LPC_RTC->SEC = vector_hora_inicial[RTC_SEG];
LPC_RTC->MIN = vector_hora_inicial[RTC_MIN];
LPC_RTC->HOUR = vector_hora_inicial[RTC_HOR];
LPC_RTC->DOM = vector_hora_inicial[RTC_DIA_MES];
LPC_RTC->DOW = vector_hora_inicial[RTC_DIA_SEMANA];
LPC_RTC->DOY = vector_hora_inicial[RTC_DIA_ANIO];
LPC_RTC->MONTH = vector_hora_inicial[RTC_MES];
LPC_RTC->YEAR = vector_hora_inicial[RTC_ANIO];
LPC_RTC->CCR=(1<<CCALEN)|(1<<CLKEN);
//Habilito la cuenta sin calibr
acion
}
void configurar_alarmas_rtc(u16 vector_alarma[])

{
LPC_RTC->ALSEC=vector_alarma[RTC_SEG];

//Car

go vector alarma
LPC_RTC->ALMIN=vector_alarma[RTC_MIN];
LPC_RTC->ALHOUR=vector_alarma[RTC_HOR];
LPC_RTC->ALDOM=vector_alarma[RTC_DIA_MES];
LPC_RTC->ALDOW=vector_alarma[RTC_DIA_SEMANA];
LPC_RTC->ALDOY=vector_alarma[RTC_DIA_ANIO];
LPC_RTC->ALMON=vector_alarma[RTC_MES];
LPC_RTC->ALYEAR=vector_alarma[RTC_ANIO];
}
void habilitar_interrupciones_rtc(u8 int_conteo,u8 int_alarma)
{
LPC_RTC->CIIR=int_conteo;
LPC_RTC->AMR=int_alarma;
}
void calibrar_rtc(u16 ajuste_conteo,u8 dir_correccion)
{
LPC_RTC->CALIBRATION=(1&(dir_correccion<<17))|
ajuste_conteo;
LPC_RTC->CCR&=~(1<<CCALEN);

//Habilito la calibracin y

sin parar la cuenta


}
void guardar_info_rtc(u32 datos_rtc[])
{
LPC_RTC->GPREG0=datos_rtc[0];
LPC_RTC->GPREG1=datos_rtc[1];
LPC_RTC->GPREG2=datos_rtc[2];
LPC_RTC->GPREG3=datos_rtc[3];
LPC_RTC->GPREG4=datos_rtc[4];
}
void leer_info_rtc(u32 datos_rtc[])
{
datos_rtc[0]=LPC_RTC->GPREG0;

datos_rtc[1]=LPC_RTC->GPREG1;
datos_rtc[2]=LPC_RTC->GPREG2;
datos_rtc[3]=LPC_RTC->GPREG3;
datos_rtc[4]=LPC_RTC->GPREG4;
}
void leer_hora_rtc(u16 vector_hora[])
{
vector_hora[RTC_SEG]=LPC_RTC->SEC;
vector_hora[RTC_MIN]=LPC_RTC->MIN;
vector_hora[RTC_HOR]=LPC_RTC->HOUR;
vector_hora[RTC_DIA_MES]=LPC_RTC->DOM;
vector_hora[RTC_DIA_SEMANA]=LPC_RTC->DOW;
vector_hora[RTC_DIA_ANIO]=LPC_RTC->DOY;
vector_hora[RTC_MES]=LPC_RTC->MONTH;
vector_hora[RTC_ANIO]=LPC_RTC->YEAR;
}
//--------------------------------------- RTC ---------------------------------------------------------------------//
Se pueden encontrar las siguientes funciones:
- Inicializacin y arranque del RTC.
- Configuracin de la hora de alarma.
- Habilitacin de interrupcin, por conteo o por alarma.
- Funcin de calibracin, agregar el offset medido, ya sea positivo o
negativo.
- Almacenamiento y lectura de los registros generales de almacenamiento
de informacin.
- Lectura de la hora actual del RTC.
Para la funciones se podra haber usado alguna estructura en vez de un
vector, el inconveniente que le encuentro a eso, es que en un futuro la
estructura no permitir una configuracin rpida como un vector con una
rutina FOR.
interrupciones.c (se agrega y modifica a lo anterior)
Cdigo PHP:

void habilitar_interrupciones()
{
NVIC_EnableIRQ(TIMER0_IRQn);

//Timer0

NVIC_EnableIRQ(TIMER1_IRQn);

//Timer1

NVIC_EnableIRQ(TIMER2_IRQn);

//Timer2

NVIC_EnableIRQ(TIMER3_IRQn);

//Timer3

NVIC_EnableIRQ(EINT1_IRQn);

//Externa 1

NVIC_EnableIRQ(EINT2_IRQn);

//Externa 2

NVIC_EnableIRQ(UART0_IRQn);

//UART 0

NVIC_EnableIRQ(RTC_IRQn);

//RTC

}
//... Todos los handlers vistos anteriormente
//--------- Rutina de la interrupcion RTC --------------------//
#define RTCCIF

#define RTCALF

void RTC_IRQHandler (void)


{
flag_rtc=1;
LPC_RTC->ILR|=(1<<RTCALF)|(1<<RTCCIF);

//Limpio las

interrupciones
}
//--------- Rutina de la interrupcion RTC --------------------//
main.c
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag

_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,
flag_rtc=0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_uart.c"
int main()
{
u16 cont=TIEMPO_TOGGLE_INICIAL;
u8 tiempo_inicial_variable=1,estado_anti_reb_ext1=ANTI_REB_
IDLE,estado_anti_reb_ext2=ANTI_REB_IDLE,flag_ascendente=0;
//-------------- Vectores RTC -----------------//
u16 vector_hora_inicial_rtc[RTC_TAMANIO]={0,0,0,1,0,1,1,201
3};
//u16 vector_alarma_rtc[RTC_TAMANIO]={0,5,0,1,0,1,1,2013};
//-------------- Vectores RTC -----------------//
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12MHz

=> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led a

pagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led e

ncendido
configurar_hora_rtc(vector_hora_inicial_rtc);
habilitar_interrupciones_rtc((1<<RTC_SEG),0xff);
ito interrupcin por c/cuenta de 1 Seg del RCT.

//Habil

//configura_timer0(25000,1000);

//Pre-escaler 250000 =>

25MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1S


eg
configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

//E

xterna 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);

//Ext

erna 2 configurada para que detecte flancos descendentes


configurar_uart0(UART_9600); //9600bps
habilitar_interrupciones();
while(1)
{
if(!flag_ascendente)
{
if(cont==1)
{
cont=tiempo_inicial_variable*TIEMPO_TOGGLE_INIC
IAL;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
else
{
if(cont==(tiempo_inicial_variable*TIEMPO_TOGGLE_INI
CIAL)-1)
{
cont=0;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3-

>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
__wfi();

//Sleep-Mode

//if(flag_timer0)
if(flag_rtc)

//RTC como base de tiempo => 1 interrup

cin por c/Seg


{
//flag_timer0=0;
flag_rtc=0;
if(!flag_ascendente)
cont--;
else
cont++;
enviar_string_uart0("Contador= \n");
envia_u16_string_uart0(cont);
enviar_string_uart0(" \n");
}
if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}
}
if(flag_ext2)
{
if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
tiempo_inicial_variable++;

}
if(flag_uart0_rx)
{
flag_uart0_rx=0;
if(dato_uart0=='a')
flag_ascendente=1;

//Cuenta Ascendente

if(dato_uart0=='d')
flag_ascendente=0;

//Cuenta descendente

}
}
}
El cambio ms destacado del anterior ejercicio, es que el timer0 no est
habilitado (comentado en el cdigo) y que ahora la base de tiempo de 1
Seg depende exclusivamente del RTC.
Ejercicio parte 2:
Del ejercicio realizado anteriormente con la UART, manteniendo al timer0
como base de tiempo de 1 seg, se pide:
- Configurar una alarma para que se enve por puerto serie un aviso
transcurrido los 5 minutos despus del reseteo.
- Si se enva por puerto serie el carcter r, enviar por puerto serie la
cuenta del RTC en el formato Da del mes/Mes/Ao Hora:Min:Seg c/1seg.
- Si se enva por puerto serie el carcter c, enviar por puerto serie la
cuenta implementada en los ejercicios anteriores para saber en qu
momento se dar cambio de estado de los leds.
defines.c => no hay modificaciones.
configuracion_PLL.c => no hay modificaciones.
perifericos.c => no hay modificaciones.

interrupciones.c => no hay modificaciones.


funciones_uart.c (se agrega y modifica a lo anterior)
Cdigo PHP:

//... Todas las funciones anteriores


void enviar_hora_rtc_uart0()
{
u16 vector_lee_hora_rtc[RTC_TAMANIO];
leer_hora_rtc(vector_lee_hora_rtc);
envia_u16_string_uart0(vector_lee_hora_rtc[RTC_DIA_MES]);
envia_dato_uart0('/');
envia_u16_string_uart0(vector_lee_hora_rtc[RTC_MES]);
envia_dato_uart0('/');
envia_u16_string_uart0(vector_lee_hora_rtc[RTC_ANIO]);
envia_dato_uart0(' ');
envia_u16_string_uart0(vector_lee_hora_rtc[RTC_HOR]);
envia_dato_uart0(':');
envia_u16_string_uart0(vector_lee_hora_rtc[RTC_MIN]);
envia_dato_uart0(':');
envia_u16_string_uart0(vector_lee_hora_rtc[RTC_SEG]);
envia_dato_uart0(' ');
}
main.c
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag
_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,
flag_rtc=0;
//------- Variables globales para las interrupciones ------------//

#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_uart.c"
int main()
{
u16 cont=TIEMPO_TOGGLE_INICIAL;
u8 tiempo_inicial_variable=1,estado_anti_reb_ext1=ANTI_REB_
IDLE,estado_anti_reb_ext2=ANTI_REB_IDLE,flag_ascendente=0,flag_
mostrar_hora=0;
//-------------- Vectores RTC -----------------//
u16 vector_hora_inicial_rtc[RTC_TAMANIO]={0,0,0,1,0,1,1,201
3};
u16 vector_alarma_rtc[RTC_TAMANIO]={0,5,0,1,0,1,1,2013};
//-------------- Vectores RTC -----------------//
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12MHz

=> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led a

pagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led e

ncendido
configurar_hora_rtc(vector_hora_inicial_rtc);
configurar_alarmas_rtc(vector_alarma_rtc);

//Al minuto 5

se espera una alarma.


habilitar_interrupciones_rtc(0,0);

//Habilito las mscar

as interrupcin por Alarma => en caso que la cuenta sea igual a


la alarma seteada.
configura_timer0(25000,1000);

//Pre-escaler 250000 => 25

MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg


configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

//E

xterna 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);

//Ext

erna 2 configurada para que detecte flancos descendentes


configurar_uart0(UART_9600); //9600bps
habilitar_interrupciones();
while(1)
{
if(!flag_ascendente)
{
if(cont==1)
{
cont=tiempo_inicial_variable*TIEMPO_TOGGLE_INIC
IAL;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
else
{
if(cont==(tiempo_inicial_variable*TIEMPO_TOGGLE_INI
CIAL)-1)
{
cont=0;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else

{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;
if(!flag_ascendente)
cont--;
else
cont++;
if(!flag_mostrar_hora)
{
enviar_string_uart0("Contador= \n");
envia_u16_string_uart0(cont);
enviar_string_uart0(" \n");
}
else
enviar_hora_rtc_uart0();
}
if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}
}
if(flag_ext2)
{
if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
tiempo_inicial_variable++;

}
if(flag_uart0_rx)
{
flag_uart0_rx=0;
if(dato_uart0=='a')
flag_ascendente=1;

//Cuenta Ascendente

if(dato_uart0=='d')
flag_ascendente=0;

//Cuenta descendente

if(dato_uart0=='r')
flag_mostrar_hora=1;

//Muestra el valor del

RTC c/1Seg.
if(dato_uart0=='c')
flag_mostrar_hora=0;

//Muestra el valor de

la cuenta c/1Seg.
}
if(flag_rtc)
{
flag_rtc=0;
enviar_string_uart0(" Alarma del RTC!!! - Pasaron
5 minutos desde el ltimo reseteo. \n");
}
}
}
En este cdigo, la base de tiempo de 1Seg sigue siendo el timer0, pero el
RTC se lo configura para que a los 5 minutos se enve un mensaje de
alarma.
Ac se puede ver en un "terminal", como se enva el valor que va tomando
el RTC c/1Seg, hasta que llega a los 5 minutos:

Si bien la hora del RTC no tiene los dgitos ajustados (todos los valores
tienen si o si 5 dgitos), para la prueba nos alcanza. Una forma de mejorar
la presentacin sera modificando la funcin "envia_u16_string_uart0" para
que enve una cierta cantidad de dgitos.
Les dejo el cdigo y para el prximo mensaje vamos a ver como funciona
el ADC.
Archivos Adjuntos
RTC - Parte 5.rar (412,7 KB (Kilobytes), 27 visitas)
Me Gusta

ltima edicin por cosmefulanito04; 12/05/2013 a las 11:07

Mensaje inapropiado?

18/05/2
013

cosme
fulanit

#9
Sexta Parte - ADC

Caractersticas principales:

o04

12 bit de resolucin hasta 200kHz.


8 canales disponibles.
Tensin de referencia positiva y negativa para mejorar en SPAN.
Modo burst, convierte todo el tiempo.
Conversin por una transicin de entrada o por un Timer-match.
Fecha

Citar

de

Procedimiento de inicializacin:

Ingreso
:
octubre
-2009
Ubicaci
n: Bs.
As.
Mensaje
s:
2.508

1- Habilitar alimentacin del ADC (registro PCONP).


2- Configurar el Pre-escaler del ADC (registro PCLKSEL0)
3- Configurar los pines como ADC (registros PINSELx) y que funcionen
como alta impedancia (registros PINMODEx)
4- Configurar Offset (registro ADTRM)[Opcional].
5- Habilitar interrupcin (regitro ADGINTEN).
6- Configurar el modo DMA[Opcional].
Los registros a utilizar sern:
ADTRM para fijar el offset.
ADINTEN para habilitar la interrupcin.
ADCR, para configurar el pre-escaler interno, el comienzo de la
conversin y el canal.
ADGDR, registro donde se almacena la conversin.
Para profundizar ms sobre el uso del ADC, dirigirse a la hoja de datos en
el Chapter 29: LPC17xx Analog-to-Digital Converter (ADC) pgina 574.
Ejercicio propuesto:
En base al ejercicio anterior (en el cual se us el timer0 como base de
tiempo), se pide que al enviar por el puerto serie el caracter 'q', se realice
una conversin de ADC c/1Seg por el canal 0 (P0.23) a una frecuencia de
clock mxima de 100kHz (para aprovechar los 12 bit de resolucin) y se
enve por el puerto serie el resultado de dicha conversin.
define.c (se agrega a lo anterior)
Cdigo PHP:

//.... Lo anterior
#define MOSTRAR_CONTADOR

#define MOSTRAR_HORA_RTC

#define MOSTRAR_CONVERSION_ADC

configuracion_PLL.c => no hay modificaciones.

perifericos.c (se agrega a lo anterior)


Cdigo PHP:

//... Todo lo anterior


//--------------------------------------- ADC ---------------------------------------//
#define PCADC

12

#define CLKDIV

#define PDN

21

#define ADC_START

24

#define ADCOFFS

#define ADGINTEN

void iniciar_adc(u8 canal,u8 offset)


{
LPC_SC->PCONP|=(1<<PCADC);
//Habilito el ADC en el control de consumo d
e los perifricos
switch(canal)
{
case 0:
{
LPC_PINCON->PINSEL1&=~((3<<14));
LPC_PINCON->PINSEL1|=(1<<14);

//Pin 0.23

funcionando como ADC


LPC_PINCON->PINMODE1&=~((3<<14));
LPC_PINCON->PINMODE1|=(2<<14);
3 funcionando sin pull-up/down
break;
}
case 1:
{

//Pin 0.2

LPC_PINCON->PINSEL1&=~((3<<16));
LPC_PINCON->PINSEL1|=(1<<16);

//Pin 0.24

funcionando como ADC


LPC_PINCON->PINMODE1&=~((3<<16));
LPC_PINCON->PINMODE1|=(2<<16);

//Pin 0.2

4 funcionando sin pull-up/down


break;
}
case 2:
{
LPC_PINCON->PINSEL1&=~((3<<18));
LPC_PINCON->PINSEL1|=(1<<18);

//Pin 0.25

funcionando como ADC


LPC_PINCON->PINMODE1&=~((3<<18));
LPC_PINCON->PINMODE1|=(2<<18);

//Pin 0.2

5 funcionando sin pull-up/down


break;
}
case 3:
{
LPC_PINCON->PINSEL1&=~((3<<20));
LPC_PINCON->PINSEL1|=(1<<20);

//Pin 0.26

funcionando como ADC


LPC_PINCON->PINMODE1&=~((3<<20));
LPC_PINCON->PINMODE1|=(2<<20);

//Pin 0.2

6 funcionando sin pull-up/down


break;
}
case 4:
{
LPC_PINCON->PINSEL3|=(3<<28);

//Pin 1.30

funcionando como ADC


LPC_PINCON->PINMODE3&=~((3<<28));
LPC_PINCON->PINMODE3|=(2<<28);
0 funcionando sin pull-up/down

//Pin 1.3

break;
}
case 5:
{
LPC_PINCON->PINSEL3|=0xC0000000;
LPC_PINCON->PINMODE3|=0x80000000;

//Pin

1.31 funcionando sin pull-up/down


break;
}
case 6:
{
LPC_PINCON->PINSEL0&=~((3<<6));
LPC_PINCON->PINSEL0|=(2<<6);

//Pin 0.3 f

uncionando como ADC


LPC_PINCON->PINMODE0&=~((3<<6));
LPC_PINCON->PINMODE0|=(2<<6);

//Pin 0.3

funcionando sin pull-up/down


break;
}
case 7:
{
LPC_PINCON->PINSEL0&=~((3<<4));
LPC_PINCON->PINSEL0|=(2<<4);

//Pin 0.2 f

uncionando como ADC


LPC_PINCON->PINMODE0&=~((3<<4));
LPC_PINCON->PINMODE0|=(2<<4);
funcionando sin pull-up/down
break;
}
default:{return;}
}

//Pin 0.2

LPC_ADC->ADTRM|=((offset&0x0f)<<ADCOFFS);
}
int convertir_adc(u8 canal,u8 pre_escaler_interno,u16 *valor_ad
c)
{
LPC_ADC->ADINTEN|=(1<<ADGINTEN);

//Habilito la interrupc

in
LPC_ADC->ADCR=(1<<ADC_START)|(1<<PDN)|
((pre_escaler_interno-1)<<CLKDIV)|(1<<canal);

//Elijo el can

al de conversin, divido el clock por 25 (25MHZ a 1MHz<13MHz)


configura_timer3(25000,1);

//Pre-escaler 250000=> 1mSeg

y 1cuenta => 1mSeg


flag_timer3=0;
while((!flag_timer3)&&(!flag_adc))//Espera a tener un dato
Rx con un time-out de 32mSeg
__wfi();
if(flag_adc)

//Sleep-Mode
//Pregunto si realmente termino la conversi

n en el canal deseado
{
LPC_TIM3->TCR=0; // paro el contador
flag_timer3=0;
*valor_adc=(u16)((LPC_ADC->ADGDR>>4)&0xfff);
return 1;
}
LPC_TIM3->TCR=0; // paro el contador
flag_timer3=0;
return -1;
}
//--------------------------------------- ADC ---------------------------------------//

- Funcin de inicializacin: tiene de argumentos el canal (necesario para


configurar el PINSEL"x" y el PINMODE"x") y el valor de offset. No habilita
la interrupcin (prefer habilitarla cuando se desee realizar la conversin).
- Funcin de conversin: tiene de argumentos el canal a convertir, el valor
del pre-escaler interno y la direccin de la variable tipo "u16" donde se
almacenar el resultado. Est funcin es bloqueante, hasta que termine de
realizar la conversin. Antes de comenzar la conversin, se habilita la
interrupcin, para luego deshabilitarla en su rutina de interrupcin.
interrupciones.c (se agrega a lo anterior)
Cdigo PHP:

void habilitar_interrupciones()
{
NVIC_EnableIRQ(TIMER0_IRQn);

//Timer0

NVIC_EnableIRQ(TIMER1_IRQn);

//Timer1

NVIC_EnableIRQ(TIMER2_IRQn);

//Timer2

NVIC_EnableIRQ(TIMER3_IRQn);

//Timer3

NVIC_EnableIRQ(EINT1_IRQn);

//Externa 1

NVIC_EnableIRQ(EINT2_IRQn);

//Externa 2

NVIC_EnableIRQ(UART0_IRQn);

//UART 0

NVIC_EnableIRQ(RTC_IRQn);

//RTC

NVIC_EnableIRQ(ADC_IRQn);

//ADC

}
//... Todo lo anterior
//--------- Rutina de la interrupcion ADC --------------------//
#define ADGINTEN

void ADC_IRQHandler (void)


{
u32 dummy=LPC_ADC->ADGDR;

//Lectura fals

a para limpiar interrupcin


flag_adc=1;
LPC_ADC->ADINTEN&=~(1<<ADGINTEN);
cin

//Deshabilito interrup

}
//--------- Rutina de la interrupcion ADC --------------------//
Lo ms destacado:
- Requiere una lectura falsa del registro ADGDR para limpiar la
interrupcin.
- Al finalizar la rutina, la interrupcin queda deshabilitada.
main.c
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag
_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,
flag_rtc=0,flag_adc=0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_uart.c"
int main()
{
u16 cont=TIEMPO_TOGGLE_INICIAL,valor_adc;
u8 tiempo_inicial_variable=1,estado_anti_reb_ext1=ANTI_REB_
IDLE,estado_anti_reb_ext2=ANTI_REB_IDLE,flag_ascendente=0,flag_
mostrar=MOSTRAR_CONTADOR;
//-------------- Vectores RTC -----------------//
u16 vector_hora_inicial_rtc[RTC_TAMANIO]={0,0,0,1,0,1,1,201

3};
u16 vector_alarma_rtc[RTC_TAMANIO]={0,5,0,1,0,1,1,2013};
//-------------- Vectores RTC -----------------//
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12MHz

=> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led a

pagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led e

ncendido
configurar_hora_rtc(vector_hora_inicial_rtc);
configurar_alarmas_rtc(vector_alarma_rtc);

//Al minuto 5

se espera una alarma.


habilitar_interrupciones_rtc(0,0);

//Habilito las mscar

as interrupcin por Alarma => en caso que la cuenta sea igual a


la alarma seteada.
configura_timer0(25000,1000);

//Pre-escaler 250000 => 25

MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg


configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

//E

xterna 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);

//Ext

erna 2 configurada para que detecte flancos descendentes


configurar_uart0(UART_9600); //9600bps
iniciar_adc(0,0);

//Inicio el ADC en el canal 0 sin offs

et
habilitar_interrupciones();
while(1)
{

if(!flag_ascendente)
{
if(cont==1)
{
cont=tiempo_inicial_variable*TIEMPO_TOGGLE_INIC
IAL;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
else
{
if(cont==(tiempo_inicial_variable*TIEMPO_TOGGLE_INI
CIAL)-1)
{
cont=0;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;

if(!flag_ascendente)
cont--;
else
cont++;
switch(flag_mostrar)
{
case MOSTRAR_CONTADOR:
{
enviar_string_uart0("Contador= \n");
envia_u16_string_uart0(cont);
enviar_string_uart0(" \n");
break;
}
case MOSTRAR_HORA_RTC:{enviar_hora_rtc_uart0();
break;}
case MOSTRAR_CONVERSION_ADC:
{
if(convertir_adc(0,250,&valor_adc)>0)
//Convierto en el canal 0, con un pre-escaler=250
{
enviar_string_uart0(" Conversion= \
n");
envia_u16_string_uart0(valor_adc);
}
}
}
}
if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}

}
if(flag_ext2)
{
if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
tiempo_inicial_variable++;
}
if(flag_uart0_rx)
{
flag_uart0_rx=0;
if(dato_uart0=='a')
flag_ascendente=1;

//Cuenta Ascendente

if(dato_uart0=='d')
flag_ascendente=0;

//Cuenta descendente

if(dato_uart0=='r')
flag_mostrar= MOSTRAR_HORA_RTC;
//Muestra el valor del RTC c/1Seg.
if(dato_uart0=='c')
flag_mostrar=MOSTRAR_CONTADOR;
//Muestra el valor de la cuenta c/1Seg.
if(dato_uart0=='q')
flag_mostrar=MOSTRAR_CONVERSION_ADC;

//Real

iza una conversin ADC y muestra su valor c/1Seg.


}
if(flag_rtc)
{
flag_rtc=0;
enviar_string_uart0(" Alarma del RTC!!! - Pasaron
5 minutos desde el ltimo reseteo. \n");
}
}

}
- Se configura el clock del ADC a la frecuencia mxima de 100kHz
(CCLK=100MHz => PCLK=1/4*CCLK=25MHz =>
ADC_CLK=1/250*PCLK=100kHz).
- El offset del ADC es 0 y se utiliza el canal 0 (P0.23) para realizar la
conversin.
- A diferencia del ejercicio anterior que se us la variable
"flag_mostrar_hora", en este se reemplaza dicha variable por
"flag_mostrar" y dependiendo de su valor mostrar el conteo, RTC o la
conversin del ADC.
- La alarma de 5 minutos del ejercicio anterior sigue estando habilitada.
Les dejo el cdigo y para el prximo mensaje vamos a ver como funciona
el DAC.
Archivos Adjuntos
ADC - Parte 6.rar (211,2 KB (Kilobytes), 32 visitas)
Me Gusta

Mensaje inapropiado?

18/05/201
3

Fogonaz

Citar
#10

! Gracias por el aporte Cosme

o
Moderado

Me Gusta

r general

Fecha de
Ingreso:

! ! ! Quien piensa distinto


NO es tu enemigo

enero2007
Ubicacin:

Mensaje inapropiado?

24/05/2
013

cosme
fulanit
o04

Citar
#11

Sptima Parte - DAC


Fogonazo dijo:

! Gracias por el aporte Cosme

Lo menos que uno puede hacer despus de todo lo que d este foro.
Sobre el ADC me falt agregar que su frecuencia mxima no puede
superar los 13MHz, as que ojo.
Fecha
de

DAC

Ingreso
:

Caractersticas principales:

octubre
-2009

10 bit de resolucin.

Ubicaci

Basado en red-2R.

n: Bs.
As.
Mensaj

Salida con buffer.


Velocidad mxima de 1MHz.

es:
2.508

Procedimiento de inicializacin:
1- Su alimentacin no requiere ser habilitada, siempre lo esta.
2- Configurar el Pre-escaler del DAC (registro PCLKSEL0)
3- Configurar el pin 0.26 como DAC (registros PINSELx) y que funcione
como alta impedancia (registros PINMODEx).
4- Configurar el modo DMA[Opcional].
Solo se usar el registro DACR en el cual se especificar el nivel de tensin
deseado y la velocidad de conversin (estar relacionada con la corriente

mxima que podr suministrar el buffer de salida), no es necesario utilizar


interrupciones.
Para profundizar ms el uso del DAC, leer la hoja de datos en el Chapter
30: LPC17xx Digital-to-Analog Converter (DAC) pgina 582.
Ejercicio propuesto:
En base al ejercicio anterior, se pide que adems de modificar el estado de
los leds, se modifique la tensin sobre el PIN 0.26 de 1V a 2V y viceversa
mediante el uso del DAC.
define.c => no hay modificaciones.
configuracion_PLL.c => no hay modificaciones.
perifericos.c (se agrega a lo anterior)
Cdigo PHP:

//Todo lo anterior
//--------------------------------------- DAC ---------------------------------------//
#define PCLK_DAC

22

#define VALUE_DAC

#define BIAS_DAC

16

#define MAX_UPDATE_BIAS
on una actualizacin de

0
1

uS

#define MIN_UPDATE_BIAS
una actualizacin de 2,5

//700uA corriente mxima c

//350uA corriente mxima con

uS

void iniciar_dac()
{
//Su conexin a fuente siempre est habilitada => no es nec
esario modificar el registro PCONP
LPC_PINCON->PINSEL1&=~((3<<20));
LPC_PINCON->PINSEL1|=(2<<20);
como DAC

//Pin 0.26 funcionando

LPC_PINCON->PINMODE1&=~((3<<20));
LPC_PINCON->PINMODE1|=(2<<20);

//Pin 0.26 funcionand

o sin pull-up/down
LPC_SC->PCLKSEL0&=~((3<<PCLK_DAC));
LPC_SC->PCLKSEL0|=(3<<PCLK_DAC);

//PCLK_DAC 100MHZ/8

=12,5MHz
}
void asignar_valor_dac(u16 valor_dac,u8 bias)
{
LPC_DAC->DACR=((bias&0x1)<<BIAS_DAC)|
((valor_dac&0x3ff)<<VALUE_DAC);
}
//--------------------------------------- DAC ---------------------------------------//
La funcin de inicializacin es bastante simple, solo configura los pines y
reduce al mximo la frecuencia de clock que le llega al DAC.
Nota aparte: el fabricante declara que el DAC funciona a una frecuencia
mxima de 1MHz, pero no dice nada sobre el mximo clock (o por lo
menos yo no encontr nada al respecto), por lo tanto si trabajamos con el
CCLK=100MHz, como mximo vamos a poder reducir su frecuencia a
PCLK=CCLK/8=12,5MHz, durante las pruebas no tuve ningn
inconveniente trabajando con ese clock.
interrupciones.c => no hay modificaciones.
main.c
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag

_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,
flag_rtc=0,flag_adc=0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_uart.c"
int main()
{
u16 cont=TIEMPO_TOGGLE_INICIAL,valor_adc;
u8 tiempo_inicial_variable=1,estado_anti_reb_ext1=ANTI_REB_
IDLE,estado_anti_reb_ext2=ANTI_REB_IDLE,flag_ascendente=0,flag_
mostrar=MOSTRAR_CONTADOR;
//-------------- Vectores RTC -----------------//
u16 vector_hora_inicial_rtc[RTC_TAMANIO]={0,0,0,1,0,1,1,201
3};
u16 vector_alarma_rtc[RTC_TAMANIO]={0,5,0,1,0,1,1,2013};
//-------------- Vectores RTC -----------------//
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12MHz

=> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led a

pagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led e

ncendido
configurar_hora_rtc(vector_hora_inicial_rtc);
configurar_alarmas_rtc(vector_alarma_rtc);

//Al minuto 5

se espera una alarma.


habilitar_interrupciones_rtc(0,0);

//Habilito las mscar

as interrupcin por Alarma => en caso que la cuenta sea igual a


la alarma seteada.
configura_timer0(25000,1000);

//Pre-escaler 250000 => 25

MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg


configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

//E

xterna 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);

//Ext

erna 2 configurada para que detecte flancos descendentes


configurar_uart0(UART_9600); //9600bps
iniciar_adc(0,0);

//Inicio el ADC en el canal 0 sin offs

et
iniciar_dac();
asignar_valor_dac(620,MIN_UPDATE_BIAS);

//Vref=3,3V => S

i quiero 2Volts => 2V/3,3V*1024=620,6 cuentas


habilitar_interrupciones();
while(1)
{
if(!flag_ascendente)
{
if(cont==1)
{
cont=tiempo_inicial_variable*TIEMPO_TOGGLE_INIC
IAL;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2; asignar_valor_dac(620,MIN_UPDATE_BIAS); /*Vref=
3,3V => Si quiero 2Volts => 2V/3,3V*1024=620,6 cuentas*/}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2; asignar_valor_dac(310,MIN_UPDATE_BIAS); /*Vref=
3,3V => Si quiero 1Volt => 1V/3,3V*1024=310,3 cuentas*/}

}
}
else
{
if(cont==(tiempo_inicial_variable*TIEMPO_TOGGLE_INI
CIAL)-1)
{
cont=0;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}
__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;
if(!flag_ascendente)
cont--;
else
cont++;
switch(flag_mostrar)
{
case MOSTRAR_CONTADOR:
{
enviar_string_uart0("Contador= \n");
envia_u16_string_uart0(cont);
enviar_string_uart0(" \n");
break;
}

case MOSTRAR_HORA_RTC:{enviar_hora_rtc_uart0();
break;}
case MOSTRAR_CONVERSION_ADC:
{
if(convertir_adc(0,250,&valor_adc)>0)
//Convierto en el canal 0, con un pre-escaler=250
{
enviar_string_uart0(" Conversion= \
n");
envia_u16_string_uart0(valor_adc);
}
}
}
}
if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}
}
if(flag_ext2)
{
if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
tiempo_inicial_variable++;
}
if(flag_uart0_rx)
{
flag_uart0_rx=0;
if(dato_uart0=='a')
flag_ascendente=1;

//Cuenta Ascendente

if(dato_uart0=='d')
flag_ascendente=0;

//Cuenta descendente

if(dato_uart0=='r')
flag_mostrar= MOSTRAR_HORA_RTC;
//Muestra el valor del RTC c/1Seg.
if(dato_uart0=='c')
flag_mostrar=MOSTRAR_CONTADOR;
//Muestra el valor de la cuenta c/1Seg.
if(dato_uart0=='q')
flag_mostrar=MOSTRAR_CONVERSION_ADC;

//Real

iza una conversin ADC y muestra su valor c/1Seg.


}
if(flag_rtc)
{
flag_rtc=0;
enviar_string_uart0(" Alarma del RTC!!! - Pasaron
5 minutos desde el ltimo reseteo. \n");
}
}
}
Lo ms destacado:
- Se inicializa el DAC con una salida de 2V.
- A medida que se modifican los leds, sucede lo mismo con la salida del
DAC, variando de 1V a 2V y viceversa.
- El resto del programa sigue comportndose igual que en el ejercicio
anterior.
Subo el cdigo de este ejercicio y en un rato (cuando el foro lo permita),
subo un "bonus track" en el siguiente mensaje sobre un generador de
seal basado en el DAC, para que vean sus lmites y los problemas cuando
el cdigo no est del todo optimizado.

Archivos Adjuntos
DAC - Parte 7.rar (212,8 KB (Kilobytes), 20 visitas)
Me Gusta

Mensaje inapropiado?

Citar

24/0
5/20
13

cos
mef

#12
Sptima Parte - Bonus Track

Ejercicio propuesto:

ula
nito
04

A partir de un proyecto totalmente nuevo (descartamos de lleno el main


anterior, pero seguimos usando las funciones que vinimos desarrollando), se
desea crear un generador de seales senoidal, triangular y diente de sierra a
partir del uso del DAC, para dichas seales se desea si es posible frecuencias
de 100Hz, 1kHz, 10kHz y 100kHz.

Fech
a de

Para modificar las frecuencias se utilizarn los pulsadores ya utilizados, tal


que con el pulsador 1 (EXT1) disminuya la frecuencia y con el pulsador 2

Ingr

(EXT2) aumente. En caso de sobrepasar los lmites de frecuencia (tanto

eso:

superior, como inferior), automticamente cambiar el tipo de seal, (ej si

octu

estamos en la seal senoidal 100Hz y presionamos pulsador 1, pase a una

bre-

diente de sierra de 100kHz).

200
9
Ubic

Antes de empezar a programar

aci
n:

De alguna forma necesitamos generar un vector que contenga los distintos

Bs.

valores que tomarn las seales, para lo cual yo utilic el programa Matlab

As.

para crear un vector (tambin se puede usar el Octave, programa GNU).

Men
saje
s:
2.50
8

Dicho vector fue creado para que almacenara 100 elementos posibles para
ser utilizadas en seales de bajas frecuencias. Una vez creado los vectores,
los transfer directamente al cdigo como si fueran variables u16
directamente en la memoria RAM como ya vern en el cdigo.
Adems puse a prueba la velocidad del DAC para comprobar si realmente
trabaja a 1uS, el resultado obtenido es sorprendente, el DAC trabaja incluso

por debajo del tiempo que declara el fabricante, consiguiendo una senoidal de
hasta 113kHz usando solo 16 puntos, dando una conversin cada 550nS
(luego subir el cdigo de prueba, en el cual se genera dicha senoidal).
Resumiendo la idea:
- Seal de 100Hz => usar el timer0 configurado en 100uS para muestrear el
vector (100 muestras).
- Seal de 1kHz => usar el timer0 configurado en 10uS para muestrear el
vector (100 muestras).
- Seal de 10kHz => usar directamente el mnimo tiempo que tarda DAC
como base de tiempo para generar la seal (no voy a tener un control de la
base de tiempo, por lo tanto la frecuencia resultante depender de la
cantidad de muestras, en un principio intent con 100, pero luego us
50) [Apunto a estar en el orden de la frecuencia].
- Seal de 100kHz => nuevamente usar directamente el mnimo tiempo que
tarda DAC como base de tiempo para generar la seal (no voy a tener un
control de la base de tiempo, solo se utilizarn 10 puntos de muestra)
[Apunto a estar en el orden de la frecuencia].
define.c (modificaciones realizadas)
Cdigo PHP:

typedef

unsigned char u8;

typedef

unsigned int u16;

typedef

unsigned long int u32;

#define CIEN_PTS
#define CINCUENTA_PTS

#define DIEZ_PTS

1
2

#define SENIAL_SENOIDAL

#define SENIAL_TRIANGULAR
#define SENIAL_DIENTE_DE_SIERRA

1
2

#define CIEN_HZ

#define MIL_HZ

#define DIEZ_MIL_HZ

#define CIEN_MIL_HZ

configuracion_PLL.c (se agrega a lo anterior un par de definiciones nuevas)


Cdigo PHP:

//Todo lo anterior
#define PCLK_TIMER0

#define DIVISOR_PERIFERICOS_1

#define DIVISOR_PERIFERICOS_2 2
#define DIVISOR_PERIFERICOS_4

#define DIVISOR_PERIFERICOS_6

#define DIVISOR_PERIFERICOS_8

//Solo CAN

Como la idea es tener la mejor base de tiempo, eleg usar el Timer0 a


100MHz y as obtener una resolucin de 100 cuentas por c/uS.
interrupciones.c => no hay modificaciones (salvo ciertas interrupciones
deshabilitadas).
funciones_de_senial.c
Cdigo PHP:

/*
Tiempo mnimo de actualizacin del DAC => 1uS
100Hz

=> 10k

pts posibles

1kHz

=> 1k

10kHz

=> 100

pts posibles

100kHz

=> 10

pts posibles

pts posibles

*/
u16 vector_senoidal_100_pts[100]={512,544,577,609,641,672,702,732
,761,789,816,841,865,888,909,929,947,963,978,990,1001,1010,1016,1
021,1023,1023,1022,1019,1013,1005,996,984,971,955,938,919,899,877
,853,828,802,775,747,717,687,656,625,593,561,528,496,463,431,399,
368,337,307,277,249,222,196,171,147,125,105,86,69,53,40,28,19,11,

5,2,0,1,3,8,14,23,34,46,61,77,95,115,136,159,183,208,235,263,292,
322,352,383,415,447,480,512};
u16 vector_triangular_100_pts[100]={0,20,41,61,82,102,123,143,164
,184,205,225,246,266,287,307,328,348,369,389,410,430,451,471,492,
512,532,553,573,594,614,635,655,676,696,717,737,758,778,799,819,8
40,860,881,901,922,942,963,983,1004,1023,1004,983,963,942,922,901
,881,860,840,819,799,778,758,737,717,696,676,655,635,614,594,573,
553,532,512,492,471,451,430,410,389,369,348,328,307,287,266,246,2
25,205,184,164,143,123,102,82,61,41,20};
u16 vector_diente_sierra_100_pts[100]={0,10,20,31,41,51,61,72,82,
92,102,113,123,133,143,153,164,174,184,194,205,215,225,235,246,25
6,266,276,286,297,307,317,327,338,348,358,368,379,389,399,409,419
,430,440,450,460,471,481,491,501,512,522,532,542,552,563,573,583,
593,604,614,624,634,644,655,665,675,685,696,706,716,726,737,747,7
57,767,777,788,798,808,818,829,839,849,859,870,880,890,900,910,92
1,931,941,951,962,972,982,992,1003,1013};
void generar_senial(u8 *indice_dato,u8 senial,u8 flag_pts)
{
switch(senial)
{
case SENIAL_SENOIDAL:
{
asignar_valor_dac(vector_senoidal_100_pts[*indice
_dato],MAX_UPDATE_BIAS);
break;
}
case SENIAL_TRIANGULAR:
{
asignar_valor_dac(vector_triangular_100_pts[*indi
ce_dato],MAX_UPDATE_BIAS);
break;
}
case SENIAL_DIENTE_DE_SIERRA:
{
asignar_valor_dac(vector_diente_sierra_100_pts[*i

ndice_dato],MAX_UPDATE_BIAS);
break;
}
}
*indice_dato+=1;
if(*indice_dato>99)
*indice_dato=0;
}
void configurar_timer_por_frecuencia(char frecuencia,u8 *flag_pts
)
{
switch(frecuencia)
{
case CIEN_HZ:
{
configura_timer0(100,99);

//Pre-escaler 25 =>

25MHz/25=1MHz => 100cuentas => 99 uSeg + 1uSeg del DAC


*flag_pts=CIEN_PTS;
break;
}
case MIL_HZ:
{
configura_timer0(100,9);
5MHz/25=1MHz => 10cuentas => 9 uSeg

//Pre-escaler 25 => 2

+ 1uSeg del DAC

*flag_pts=CIEN_PTS;
break;
}
case DIEZ_MIL_HZ:
{
*flag_pts=CINCUENTA_PTS;
LPC_TIM0->TCR=0; // Paro el Timer0

break;
}
case CIEN_MIL_HZ:
{
*flag_pts=DIEZ_PTS;
LPC_TIM0->TCR=0; // Paro el Timer0
break;
}
}
}
Se puede ver:
- La creacin de 3 vectores de 100 elementos c/u que contendrn las
distintas seales.
- La funcin "generar_senial" solo utilizada en 100 y 1kHz (ya explicar el
porque).
- La funcin "configurar_timer_por_frecuencia" que se encargar de modificar
la base de tiempo segn la frecuencia, esta base de tiempo es solo utilizada
hasta seales de 1kHz.
main.c
Cdigo PHP:

#include <LPC17xx.H>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag_e
xt1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,flag
_rtc=0,flag_adc=0;
//------- Variables globales para las interrupciones ------------//

#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_de_senial.c"
int main()
{
u8 indice_senial=0,flag_pts=CIEN_PTS;
int tipo_senial=SENIAL_SENOIDAL,frecuencia=MIL_HZ;
u8 estado_anti_reb_ext1=ANTI_REB_IDLE,estado_anti_reb_ext2=AN
TI_REB_IDLE;
configurar_pll(CLK_XTAL,25,2,3,
(DIVISOR_PERIFERICOS_1<<PCLK_TIMER0),0);

// Cristal de 12MHz =

> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


configurar_timer_por_frecuencia(frecuencia,&flag_pts);
configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

//Ext

erna 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);
na 2 configurada para que detecte flancos descendentes
iniciar_dac();
habilitar_interrupciones();
while(1)
{
if(frecuencia<DIEZ_MIL_HZ)
__wfi();

//Sleep-Mode

else
{
switch(tipo_senial)
{
case SENIAL_SENOIDAL:
{
LPC_DAC-

//Exter

>DACR=((vector_senoidal_100_pts[indice_senial]&0x3ff)<<VALUE_DAC)
;

//Mxima velocidad posible sin pasar por funciones!, Bias de

finido en forma implcita


break;
}
case SENIAL_TRIANGULAR:
{
LPC_DAC>DACR=((vector_triangular_100_pts[indice_senial]&0x3ff)<<VALUE_DA
C);

//Mxima velocidad posible sin pasar por funciones!, Bias

definido en forma implcita


break;
}
case SENIAL_DIENTE_DE_SIERRA:
{
LPC_DAC>DACR=((vector_diente_sierra_100_pts[indice_senial]&0x3ff)<<VALUE
_DAC);

//Mxima velocidad posible sin pasar por funciones!, Bi

as definido en forma implcita


break;
}
}
if(flag_pts==DIEZ_PTS)
{
indice_senial+=10;
if(indice_senial>99)
indice_senial=0;
}
else
{
indice_senial+=2;

if(indice_senial>99)
indice_senial=0;
}
}//El DAC funciona a toda velocidad --> no necesito timer

if(flag_timer0)
{
flag_timer0=0;
generar_senial(&indice_senial,tipo_senial,flag_pts);
}
if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
frecuencia--;
if(frecuencia<0)
{
frecuencia=CIEN_MIL_HZ;
tipo_senial--;
if(tipo_senial<0)
tipo_senial=SENIAL_DIENTE_DE_SIERRA;
}
configurar_timer_por_frecuencia(frecuencia,&flag_
pts);
}
}
if(flag_ext2)
{

if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
{
frecuencia++;
if(frecuencia>CIEN_MIL_HZ)
{
frecuencia=CIEN_HZ;
tipo_senial++;
if(tipo_senial>SENIAL_DIENTE_DE_SIERRA)
tipo_senial=SENIAL_SENOIDAL;
}
configurar_timer_por_frecuencia(frecuencia,&flag_
pts);
}
}
}
}
Lo ms destacado:
- Para las frecuencias altas, trat de no llamar varias funciones y actuar
directamente sobre el
registro. En ests frecuencias el DAC est continuamente convirtiendo.
- Para las frecuencias bajas, us la funcin "generar_senial" y durante el
tiempo muerto el uC est en modo sleep.
Resultados obtenidos:
Senoidal de 100Hz

Me Gusta

A electroconico le gusta esto.

Mensaje inapropiado?

01/06/201
3

cosmefu
lanito04

Citar
#13

Octava Parte - PWM

Diagrama en bloques:

Fecha de
Ingreso:
octubre2009
Ubicacin

Me Gusta

: Bs. As.
Mensajes:
2.508

A electroconico le gusta esto.

Mensaje inapropiado?

Citar

08/06/2
013

cosme
fulanit

#14
Novena Parte - SSP (como SPI)

Aclaracin:

o04

Debido a que el kit de desarrollo tiene los puertos SSP0 conectados a la


memoria SD (o MMC), explicar este bus. La diferencia con el SPI, es que
este bus permite distintas configuraciones de diversos fabricantes como ya
veremos, entre ellas el SPI, otra gran diferencia es que su interrupcin si o
si est pensada para trabajar en DMA, por lo tanto en el ejemplo que
Fecha
de
Ingreso
:

subir lamentablemente tendremos que trabajar con un polling

Caractersticas principales:

octubre
-2009

- Compatibilidad con mtiples buses (SPI, 4-Wire, TI SSI, National

Ubicaci

Semiconductor Microwire).

n: Bs.

- Comunicacin serie sincrnica.

As.
Mensaj
es:
2.508

- Trabajar como maestro o esclavo.


- Frames de datos de 4 a 16 bits.
- Transferencia mediante DMA.
Procedimiento de inicializacin:
1- Habilitar alimentacin del SSPx (registro PCONP).
2- Configurar el Pre-escaler del SSPx (registro PCLKSEL0/1)
3- Configurar los pines de salida como SSPx (registros PINSELx) y su
modo de funcionamiento (registros PINMODEx).
4- Configurar el tipo de bus y su modo de trabajo (registros CR0/1).

5- Configurar su Pre-escaler interno (registro CPSR).


6- Configurar las interrupciones [Opcional - solo til en modo DMA].
7- Configurar DMA [Opcional].
Registros a utilizar:
- CR0/1 configurarn el modo de funcionamiento del bus.
- SR registro de estado, utilizado para realizar polling.
- DR se escribirn/leern los datos.
Velocidad del clock:

No voy a entrar en detalles de como funciona una comunicacin SPI, por lo


tanto les recomiendo que repasen el tema antes de meterse con el cdigo.
Para ms informacin de como funciona el SSP, dirigirse a la pgina 412 de
la hoja de datos.
Ejercicio propuesto:
A partir del ejercicio que utilizaba el DAC, se desea implementar la lectura
y escritura de una memoria SD mediante el uso de un bus SPI. Al enviar el
caracter:
- 'l': se leern los primeros 512 bytes de la memoria.
- 'e': se escribir el string "Escribe", llenando el resto de la memoria con
0's hasta llegar a los 512 bytes.
- 'v': se escribir el string "Prueba", llenando el resto de la memoria con
0's hasta llegar a los 512 bytes.
Antes de empezar:
- Utilizaremos una librera obtenida de AVR-Freaks para realizar una
lectura/escritura tipo RAW (a secas, sin formato FAT, ni nada) que funciona
muy bien. Lamentablemente no recuerdo el autor del cdigo
- No implementaremos ningn formato de archivos.

- Necesitaremos usar las funciones malloc/calloc y free para poder usar


buffers de datos extensos, por ese motivo es muy importante avisarle a
Keil que vamos a necesitar que nos asigne memoria RAM (heap),
eso lo hacemos en el archivo de Start-up que nos brinda keil.
define.c (queda igual que en el ejercicio del DAC)
configuracion_PLL.c => no hay modificaciones.
perifericos.c (se agrega a lo anterior)
Cdigo PHP:

//Todo lo anterior
//--------------------------------------- SSP0 ---------------------------------------//
#define PCSSP0

21

#define PCSSP1

10

#define SSP_MODO_MAESTRO

#define SSP_MODO_ESCLAVO

#define SSP0_SCK0

#define SSP0_SSEL0

10

#define SSP0_MISO0

14

#define SSP0_MOSI0

16

#define SSP0_GPIO_SSEL0

//P1.20
//P1.21
//P1.23
//P1.24

21

#define DSS_4BIT

#define DSS_5BIT

#define DSS_6BIT

#define DSS_7BIT

#define DSS_8BIT

#define FRF_SPI
#define FRF_TI
#define FRF_MICROWIRE

0
(1<<4)
(2<<4)

#define

SSP0_CPOL0

#define

SSP0_CPOL1

(1<<6)

#define

SSP0_CPHA0

#define

SSP0_CPHA1

(1<<7)

#define LBM_OFF

#define LBM_ON

#define SSE_OFF

#define SSE_ON

(1<<1)

#define MS_MASTER

#define MS_SLAVE

(1<<2)

#define SLAVE_OUT_DISABLE
#define

(1<<3)

RTIM

(1<<1)

#define RXIM

(1<<2)

#define RNE

(1<<2)

#define RFF

(1<<3)

void iniciar_ssp0(u8 tipo_transferencia,u8 modo,u8 contador_ssp


,u8 cpha,u8 cpol)

//8bit de datos

{
LPC_SC->PCONP|=(1<<PCSSP0);
//Habilito el SSP0 en el control de consumo de los
perifricos
LPC_SSP0->CR1=SSE_OFF;
//SSP deshabilitado
LPC_PINCON->PINSEL3&=~((3<<SSP0_SCK0));
LPC_PINCON->PINSEL3|=(3<<SSP0_SCK0);
//Pin 1.20 funcionando como SCK0 del SSP0
LPC_PINCON->PINMODE3&=~((3<<SSP0_SCK0));
//Pull-up
LPC_PINCON->PINSEL3&=~((3<<SSP0_SSEL0));
//OJO!! usado en modo GPIO
LPC_PINCON->PINMODE3&=~((3<<SSP0_SSEL0));
//Pull-up

LPC_GPIO1->FIODIR|=(1<<SSP0_GPIO_SSEL0);
//Como salida
LPC_GPIO1->FIOSET|=(1<<SSP0_GPIO_SSEL0);

/Estado Idle => "1"


LPC_PINCON->PINSEL3&=~((3<<SSP0_MISO0));
LPC_PINCON->PINSEL3|=(3<<SSP0_MISO0);
//Pin 1.23 funcionando como MISO0 del SSP0
LPC_PINCON->PINMODE3&=~((3<<SSP0_MISO0));
//Pull-up
LPC_PINCON->PINSEL3&=~((3<<SSP0_MOSI0));
LPC_PINCON->PINSEL3|=(3<<SSP0_MOSI0);
//Pin 1.24 funcionando como MOSI0 del SSP0
LPC_PINCON->PINMODE3&=~((3<<SSP0_MOSI0));
//Pull-up
LPC_SSP0->CR0=cpol|cpha|tipo_transferencia|DSS_8BIT;//8 Bit
de datos, modo de transferencia a eleccin
if(modo==SSP_MODO_MAESTRO)
{
LPC_SSP0->CR1=MS_MASTER|SSE_ON|LBM_OFF;
//Sin loop back, SSP habilitado, modo maestro
LPC_SSP0->CPSR=contador_ssp;
//Fijo el contador del clock => 100MHz/4=25MHz => 2
5MHz/contador=Velocidad SSP
}//Modo Maestro
else
{
LPC_SSP0->CR1=MS_SLAVE|SSE_ON|LBM_OFF;
//Sin loop back, SSP habilitado, modo maestro
}//Modo Esclavo

int spiByteOut(u8 dato)


{
LPC_SSP0->DR=dato;
configura_timer3(25000,33);

//Pre-escaler 250000=> 1mSeg

y 3cuentas => 33mSeg


flag_timer3=0;
while(!((LPC_SSP0->SR&0x1C)&(RFF|RNE))&&!flag_timer3); //Es
pera a tener un dato Rx con un time-out de 32mSeg
LPC_TIM3->TCR=0;

//Paro el contador

if(!flag_timer3)
return LPC_SSP0->DR;
else
return -1;
}

//--------------------------------------- SSP0 ---------------------------------------//


Lo ms destacable:
- Funcin de inicio que permite elegir el tipo de bus segn el fabricante,
modo maestro/esclavo, pre-escaler interno, CPHA y CPOL. Es interesante
ver que el puerto de seleccin, lo uso como GPIO, esto es importante y
debo manipularlo c/vez que se realiza una transferencia de datos, yo para
simplificar, dejo habilitado este puerto durante toooodo el tiempo, no es lo
ideal.
- Funcin de envo/recepcin (full-duplex

), usar un polling con un

time-out. Nota: si se usara el modo DMA, adems de poder usar las


interrupciones, podramos usar un time-out que ya tiene incorporado este
bus.
interrupciones.c => no hay modificaciones, solo habilito los perifricos

que usar.
memoria_SD.c (librera obtenida en AVR-Freaks)
Cdigo PHP:

//------------------------ Data Token -------------------//


#define SD_DAT_TOKEN_READ

0xFE

#define SD_DAT_TOKEN_WRITE

0xFC

#define SD_DAT_TOKEN_STOP

0xFD

//------------------------ Data Token -------------------//


#define MMC_GO_IDLE_STATE

#define MMC_SEND_OP_COND

#define MMC_SEND_CSD

#define MMC_SEND_CID

10

#define MMC_SEND_STATUS

13

#define MMC_SET_BLOCKLEN

16

#define MMC_READ_SINGLE_BLOCK

17

#define MMC_WRITE_BLOCK

24

#define MMC_PROGRAM_CSD

27

#define MMC_SET_WRITE_PROT

28

#define MMC_CLR_WRITE_PROT

29

#define MMC_SEND_WRITE_PROT

30

#define MMC_TAG_SECTOR_START

32

#define MMC_TAG_SECTOR_END

33

#define MMC_UNTAG_SECTOR

34

#define MMC_TAG_ERASE_GROUP_START

35

#define MMC_TAG_ERARE_GROUP_END

36

#define MMC_UNTAG_ERASE_GROUP

37

#define MMC_ERASE

38

#define MMC_CRC_ON_OFF

59

/*##############################################
#

mmcCommand

# Send a 48 bit long command to the mmc. First #


# parameter (byte) is the

command, the second #

# is the 32 bit long parameter.

##############################################*/

u8 mmcCommand(u8 cmd,u32 arg)


{
spiByteOut(0xFF);

// Send a le

ading 0xFF
spiByteOut(cmd | 0x40);

// Send the

6 bit command plus start & transmittion flag


spiByteOut(arg>>24);

// Send the

last byte of the parameter


spiByteOut(arg>>16);

// Send the

3rd byte of the parameter


spiByteOut(arg>>8);

// Send the

2nd byte of the parameter


spiByteOut(arg);

// Send the

lowest byte of the parameter


spiByteOut(0x95);

// Send the

7 bit CRC and end bit


spiByteOut(0xFF);
return spiByteOut(0xFF);
}
/*##############################################
#

initMMC

# Initialises the SPI & MMC. You should always #


# call this function before attempting to call #
# any other mmc function.

##############################################*/
u8 initMMC (void)
{
u8 i,result;
iniciar_ssp0(FRF_SPI,SSP_MODO_MAESTRO,254,SSP0_CPHA0,SSP0_CP
OL0); //8bit de datos, SPP clock=25MHz/254=98,4kHz ==> El prees
caler solo es numero PAR!
LPC_GPIO1->FIOSET|=(1<<SSP0_GPIO_SSEL0);
//Estado Idle => "1"

for (i = 0; i < 10; i++) {

// Wait fo

r 80 SPI clockcycles
spiByteOut(0xFF);

// for the

SPI to initialise
}
LPC_GPIO1->FIOCLR|=(1<<SSP0_GPIO_SSEL0);
//Estado Idle => "0"
result = mmcCommand(MMC_GO_IDLE_STATE, 0);

// make mmc go

to SPI mode
if (result != 1) {

// if the

command returns an error value...


return result;

// stop in

ialising and return the error value


}
while (mmcCommand(MMC_SEND_OP_COND, 0) != 0);

// Wait unt

il the mmc is ready to move on.


return 0;

// return t

hat all is well


}
/*##############################################
#

mmcReadBlock

# Reads a 512 byte block from the mmc. The max #


# capacity of the mmc is 32MB. If you need any #
# more,

just change

the 'unsigned

int' into #

# 'unsigned long' and you're ready to go.

# The first parameter is the block number, the #


# second is a pointer to a 512 byte array.

##############################################*/
u8 mmcReadBlock (u16 adress, u8 *databuffer) {
// Send the read command
, and the start adress as param
u8 result = mmcCommand(MMC_READ_SINGLE_BLOCK, (unsigned long
)(adress<<9));
u16 i;

if (result != 0) {

// If the

command returns an error value...


return result;

// stop read

ing and return the error


}

// If all is

well though...
while (spiByteOut(0xFF) != (u8)0xFE);

// Wait for th

e start transmittion flag


for (i = 0; i != 512; i++) {

// And loop

512 times to recieve all the data.


*databuffer = spiByteOut(0xFF);

// Set an ele

ment from the array to the recieved byte


databuffer++;

// And increa

se the pointer
}
spiByteOut(0xFF);

// Recieve a

nd ignore the checksum bytes.


spiByteOut(0xFF);
return 0;

// Return al

l is well.
}
/*##############################################
#

mmcWriteBlock

# Writes a 512 byte block to the mmc. The rest #


# is the same as mmcReadBlock.

##############################################*/
u8 mmcWriteBlock (u16 adress, u8 *databuffer) {
//Send the write command
, and the start adress as param
u8 result = mmcCommand(MMC_WRITE_BLOCK, (u32)(adress<<9));
u16 i;
if (result != 0) {
command returns an error code...

// If the

return result;
}

// return it
// If everyt

hings OK
spiByteOut(0xFF);

// Send a du

mmy checksum
spiByteOut(0xFF);
spiByteOut(0xFE);

// Send the

start transmittion flag


for (i = 0; i < 512; i++) {

// Send all

512 bytes.
spiByteOut(databuffer[i]);

// Send the

byte
}
spiByteOut(0xFF);

// Recieve d

ummy checksum
spiByteOut(0xFF);
result = spiByteOut(0xFF) & 0x1F;

// Read the

data response token


if (result != 0x05) {

// If someth

ing bad happened...


return result;

// Return th

e error
}

// If all is

well...
while (!spiByteOut(0xFF));

// Wait unti

l the mmc isn't busy anymore


return 0;

// And retur

n succes!
}
Lo nico que me encargo yo, es de inicializar el bus SSP0 a una baja
velocidad durante la inicializacin de la memoria (100kHz). Luego de la
inicializacin, se podra llevar la velocidad hasta 25MHz.
Cdigo PHP:

iniciar_ssp0(FRF_SPI,SSP_MODO_MAESTRO,254,SSP0_CPHA0,SSP0_CPOL0
); //8bit de datos, SPP clock=25MHz/254=98,4kHz ==> El preescal

er solo es numero PAR!


LPC_GPIO1->FIOSET|=(1<<SSP0_GPIO_SSEL0);

//E

stado Idle => "1"


main.c
Cdigo PHP:

#include <LPC17xx.H>
#include <stdlib.h>
#include "defines.c"
//------- Variables globales para las interrupciones ------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag
_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,
flag_rtc=0,flag_adc=0;
//------- Variables globales para las interrupciones ------------//
#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_uart.c"
#include "memoria_SD.c"
int main()
{
u16 cont=TIEMPO_TOGGLE_INICIAL,valor_adc;
u8 tiempo_inicial_variable=1,estado_anti_reb_ext1=ANTI_REB_
IDLE,estado_anti_reb_ext2=ANTI_REB_IDLE,flag_ascendente=0,flag_
mostrar=MOSTRAR_CONTADOR;
u8 *buffer_datos;
u16 indice_buffer;
//-------------- Vectores RTC -----------------//
u16 vector_hora_inicial_rtc[RTC_TAMANIO]={0,0,0,1,0,1,1,201
3};

u16 vector_alarma_rtc[RTC_TAMANIO]={0,5,0,1,0,1,1,2013};
//-------------- Vectores RTC -----------------//
configurar_pll(CLK_XTAL,25,2,3,0,0);

// Cristal de 12MHz

=> M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz


LPC_PINCON->PINSEL7=0;
//Declaro al puerto 3 como GPIO
LPC_GPIO3->FIODIR|=LED_2|LED_1;
//Defino al puerto como salida
LPC_GPIO3->FIOSET=LED_1;

//Pongo en 1 el puerto => led a

pagado
LPC_GPIO3->FIOCLR=LED_2;

//Pongo en 0 el puerto => led e

ncendido
configurar_hora_rtc(vector_hora_inicial_rtc);
configurar_alarmas_rtc(vector_alarma_rtc);

//Al minuto 5

se espera una alarma.


habilitar_interrupciones_rtc(0,0);

//Habilito las mscar

as interrupcin por Alarma => en caso que la cuenta sea igual a


la alarma seteada.
configura_timer0(25000,1000);

//Pre-escaler 250000 => 25

MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg

configurar_externa_1(EXT1_MODO_FLANCO,EXT1_POL_NEG);

//E

xterna 1 configurada para que detecte flancos descendentes


configurar_externa_2(EXT2_MODO_FLANCO,EXT2_POL_NEG);

//Ext

erna 2 configurada para que detecte flancos descendentes


configurar_uart0(UART_9600); //9600bps
iniciar_adc(0,0);

//Inicio el ADC en el canal 0 sin offs

et
iniciar_dac();
asignar_valor_dac(620,MIN_UPDATE_BIAS);
i quiero 2Volts => 2V/3,3V*1024=620,6 cuentas

//Vref=3,3V => S

habilitar_interrupciones();
while(1)
{
if(!flag_ascendente)
{
if(cont==1)
{
cont=tiempo_inicial_variable*TIEMPO_TOGGLE_INIC
IAL;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2; asignar_valor_dac(620,MIN_UPDATE_BIAS); /*Vref=
3,3V => Si quiero 2Volts => 2V/3,3V*1024=620,6 cuentas*/}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2; asignar_valor_dac(310,MIN_UPDATE_BIAS); /*Vref=
3,3V => Si quiero 1Volt => 1V/3,3V*1024=310,3 cuentas*/}
}
}
else
{
if(cont==(tiempo_inicial_variable*TIEMPO_TOGGLE_INI
CIAL)-1)
{
cont=0;
if(LPC_GPIO3->FIOPIN&LED_1)
{LPC_GPIO3->FIOCLR|=LED_1; LPC_GPIO3>FIOSET|=LED_2;}
else
{LPC_GPIO3->FIOSET|=LED_1; LPC_GPIO3>FIOCLR|=LED_2;}
}
}

__wfi();

//Sleep-Mode

if(flag_timer0)
{
flag_timer0=0;
if(!flag_ascendente)
cont--;
else
cont++;
switch(flag_mostrar)
{
case MOSTRAR_CONTADOR:
{
enviar_string_uart0((u8 *)("Contador= \
r\n"));
envia_u16_string_uart0(cont);
enviar_string_uart0((u8 *)("\r\n"));
break;
}
case MOSTRAR_HORA_RTC:{enviar_hora_rtc_uart0();
break;}
case MOSTRAR_CONVERSION_ADC:
{
if(convertir_adc(0,250,&valor_adc)>0)
//Convierto en el canal 0, con un pre-escaler=250
{
enviar_string_uart0((u8 *)(" Conver
sion= \r\n"));
envia_u16_string_uart0(valor_adc);
}
}
}
}

if(flag_ext1)
{
if(anti_rebote_ext1(&estado_anti_reb_ext1)>0)
{
if(tiempo_inicial_variable>1)
tiempo_inicial_variable--;
}
}
if(flag_ext2)
{
if(anti_rebote_ext2(&estado_anti_reb_ext2)>0)
tiempo_inicial_variable++;
}
if(flag_uart0_rx)
{
flag_uart0_rx=0;
if(dato_uart0=='a')
flag_ascendente=1;

//Cuenta Ascendente

if(dato_uart0=='d')
flag_ascendente=0;

//Cuenta descendente

if(dato_uart0=='r')
flag_mostrar= MOSTRAR_HORA_RTC;
//Muestra el valor del RTC c/1Seg.
if(dato_uart0=='c')
flag_mostrar=MOSTRAR_CONTADOR;
//Muestra el valor de la cuenta c/1Seg.
if(dato_uart0=='q')
flag_mostrar=MOSTRAR_CONVERSION_ADC;
iza una conversin ADC y muestra su valor c/1Seg.
if(dato_uart0=='l')

//Real

{
buffer_datos=calloc(512,sizeof(u8));

//5

12 bytes
if(!buffer_datos)
{enviar_string_uart0((u8 *)("Memoria RA
M insuficiente.\r\n"));}
else
{
if(initMMC()!=0)
{enviar_string_uart0((u8 *)("Error
de inicio Memoria.\r\n"));}
else
{
if(mmcReadBlock (0,buffer_datos)==0
)
{
for(indice_buffer=0;indice_buff
er<511;indice_buffer++)
{
envia_dato_uart0(*(buff
er_datos+indice_buffer));
}
}
else
{enviar_string_uart0((u8 *)
("Error de lectura de Memoria.\r\n"));}
}
}
free(buffer_datos);
}
if(dato_uart0=='e')
{
buffer_datos=calloc(512,sizeof(u8));
12 bytes

//5

if(!buffer_datos)
{enviar_string_uart0((u8 *)("Memoria RA
M insuficiente.\r\n"));}
else
{
*buffer_datos='E';
*(buffer_datos+1)='s';
*(buffer_datos+2)='c';
*(buffer_datos+3)='r';
*(buffer_datos+4)='i';
*(buffer_datos+5)='b';
*(buffer_datos+6)='e';
*(buffer_datos+7)='\r';
*(buffer_datos+8)='\n';
if(initMMC()!=0)
{enviar_string_uart0((u8 *)("Error
de inicio Memoria.\r\n"));}
else
{
if(mmcWriteBlock (0,buffer_datos)!
=0)
{enviar_string_uart0((u8 *)
("Error de escritura de Memoria.\r\n"));}
}
}
free(buffer_datos);
}
if(dato_uart0=='v')
{
buffer_datos=calloc(512,sizeof(u8));
12 bytes
if(!buffer_datos)

//5

{enviar_string_uart0((u8 *)("Memoria RA
M insuficiente.\r\n"));}
else
{
*buffer_datos='P';
*(buffer_datos+1)='r';
*(buffer_datos+2)='u';
*(buffer_datos+3)='e';
*(buffer_datos+4)='b';
*(buffer_datos+5)='a';
*(buffer_datos+6)='\r';
*(buffer_datos+7)='\n';
if(initMMC()!=0)
{enviar_string_uart0((u8 *)("Error
de inicio Memoria.\r\n"));}
else
if(mmcWriteBlock (0,buffer_datos)!
=0)
{enviar_string_uart0((u8 *)
("Error de escritura de Memoria.\r\n"));}
}
free(buffer_datos);
}
}
if(flag_rtc)
{
flag_rtc=0;
enviar_string_uart0((u8 *)(" Alarma del RTC!!! - Pa
saron 5 minutos desde el ultimo reseteo. \r\n"));
}
}
}
Lo ms destacado:

- El uso de un buffer de 512 bytes (direcciones 0 a 511), obtenido


mediante el uso de la funcin calloc y liberado con free, para lo cual
requiere el uso de las librera "stdlib.h".
- La inicializacin de la memoria c/vez que realizar un cambio. Si no
realizaba esta inicializacin todo el tiempo, aparecan errores, es posible
que el problema est en el bit SSEL que lo dejo habilitado toooodo el
tiempo.
Fotos del terminal con los datos obtenidos de la memoria:
- Antes de escribir algo, se puede ver la cabecera tipo FAT16:

- Luego de escribir el string "Escribe" seguido de los 0's, la lectura arrojo:

"Contador=" es del string que mando durante la cuenta realizada en


ejercicios anteriores.
- Luego de escribir el string "Prueba" seguido de los 0's, la lectura arrojo:

"Contador=" es del string que mando durante la cuenta realizada en


ejercicios anteriores.
Con este mensaje se completan todos los temas que mencione,
obviamente todava falta lo ms jugoso que es USB y Ethernet, pero de
momento no tuve tiempo de verlos. Cuando aprenda a usarlos, continuar
con el tutorial.
Archivos Adjuntos
SSP0 - Parte 9.zip (277,1 KB (Kilobytes), 15 visitas)
Me Gusta

Mensaje inapropiado?

14/08/2013

cosmefulanito04

Citar
#15

alexv8 dijo:

QUE LABURO HERMANO!!


muchsimas gracias por el aporte, muy bueno!

Gracias.
Fecha de Ingreso:
octubre-2009
Ubicacin: Bs. As.

Aprovecho para dejar un link del tutorial de FreeRTOS para esta


familia de uC.

Mensajes: 2.508

[Tutorial] FreeRTOS
Me Gusta

Mensaje inapropiado?

14/08/2013

Hellmut1956

Citar
#16

Impresionante tu tutorial, en especial porque se dedica a cosas


que usualmente dejo desatendidas. No es crtica, sino
pregunta. ARM exige de todos aquellos que toman una licencia
para el desarollo de una componente del tipo ARM Cortex Mx,
que estas empresas deben obligatoriamente poner a dispocicin
del usuario una API para cada funcin periferica de su

Fecha de Ingreso:
diciembre-2008
Ubicacin:
Mammendorf,
Alemania
Mensajes: 250

componente, donde la APi es comun para todas las


componentes de todos los que licencian de ARM un Cortex Mx.
Esto tiene la gran ventaja para el que usa un ARM Cortex Mx,
que si programa usa estos API definidos por ARM, el esfuerzo
para portar un programa de un controlador a otro controlador,
sea del mismo proveedor, en nuestro caso los ARM Cortex M de
NXP, o sea de otro proveedor, siempre que tenga las periferias
que el programa requiere, ser sencillamente portable.
Claro, el camino que tu escoges, por un lado muestra que
vienes de los ARM no de los tipo Cortex Mx, pero por otro lado
permite aprender en detalle como funcionan y se usan ciertas
periferias.
Creo que sera util el incluir la referencia a este API en tu
tutorial y de aprender a usarlo. Aprendiendo desde el principio
a usar esta API educa a escribir programas que sern
facilmente portables.
Repito, no es crtica y mas que se entiende basando en tus
experiencias previas con los ARM, es solo una sugerencia. Yo
estoy aprendiendo para portar un programa para el control de
motor de paso de un ARM Cortex M0 de Samsung al LPC1769.
Tambin quiero indicar que NXP, por ejemplo ofrece los
LPCXpresso que cuestan algo menos que el kit que presentan e
incluyen una interfaz JTAG, adicional a la interfaz USB, lo que
es de gran utilidad para observar un programa como es
ejecutado en el sistema destino. Permite prcticamente todas
las funciones de Debug que en otras arquitecturas requieren
usar un simulador.
Estas placas LPCXpresso existen para todas las familias de
Cortex M de NXP y siempre usan el mas potente y grande de
cada familia. Esas placas son tan baratas y desconectando la
interfaz JTAG tan pequeas, que para mi no es posible ni
justificable hacer placas propias para estos controladores, sino
solo tarjetas madre a las que monto la placa LPCXpresso en un

zcalo, como lo hara con una componente en empaque DIP!


No trabajo para NXP, ni tengo algn beneficio de fomentar
estas placas, fuera de compartir mis experiencias con otros
interesados.
Me Gusta

Mensaje inapropiado?

Citar

14/08/2013

cosmefulanito0
4

#17
Hellmut1956 dijo:

ARM exige de todos aquellos que toman una licencia para el desarollo de
una componente del tipo ARM Cortex Mx, que estas empresas deben
obligatoriamente poner a dispocicin del usuario una API para cada
funcin periferica de su componente, donde la APi es comun para todas
las componentes de todos los que licencian de ARM un Cortex Mx. Esto
tiene la gran ventaja para el que usa un ARM Cortex Mx, que si programa
usa estos API definidos por ARM, el esfuerzo para portar un programa de

Fecha de Ingreso:
octubre-2009
Ubicacin: Bs. As.
Mensajes: 2.508

un controlador a otro controlador, sea del mismo proveedor, en nuestro


caso los ARM Cortex M de NXP, o sea de otro proveedor, siempre que
tenga las periferias que el programa requiere, ser sencillamente
portable.

Mir yo en ese sentido tengo una forma de pensar bastante crtica


hacia el uso de libreras de terceros si puedo evitarlo.
Si el perifrico a configurar es relativamente sencillo como un
Timer, una Uart o un SPI, por qu entregar el control a una
librera que no sabes lo que hace?, ya de por si con C estoy
entregando bastante control, para que seguir haciendolo?. Otra
desventaja que le veo a ese tipo de libreras, es que estn
pensadas para funcionar en forma muy genrica, ms codigo y
posiblemente ms procesamiento.
Est bien lo que decs y es un punto a favor eso de estandarizar
las libreras y que otros puedan entender tu cdigo con mayor
facilidad. Incluso en perifricos ms complejos como el Usb o
implementar un TCP Stack para el Ethernet no lo veo mal eso, lo
mismo podramos decir del uso de FreeRTOS, creo resultara una
prdida de tiempo tratar de implementar mi propio kernel (cosa

bastaaaante compleja) teniendo kernels respaldados por la


comunidad.
Resumiendo, si lo puedo hacerlo yo y no resulta difcil
(simplemente leer hoja de datos y entender el funcionamiento de
los registros de dicho perifrico), prefiero implementar m cdigo,
se que si a futuro se presenta algn comportamiento errtico, no
voy a tener que tratar de entender una librera hecha por otro.
Hellmut1956 dijo:

Creo que sera util el incluir la referencia a este API en tu tutorial y de


aprender a usarlo. Aprendiendo desde el principio a usar esta API educa a
escribir programas que sern facilmente portables.

No me parece una mala idea, si vos pods agregar al tutorial el


uso de esas libreras, ser bienvenido, incluso a m me pueden
llegar a ser tiles en un futuro.
Hellmut1956 dijo:

Tambin quiero indicar que NXP, por ejemplo ofrece los LPCXpresso que
cuestan algo menos que el kit que presentan e incluyen una interfaz
JTAG, adicional a la interfaz USB, lo que es de gran utilidad para observar
un programa como es ejecutado en el sistema destino. Permite
prcticamente todas las funciones de Debug que en otras arquitecturas
requieren usar un simulador.

Eso es algo que me est haciendo falta ahora en mi kit, poder


hacer el Debug sin usar un puerto LPT y la verdad que en este tipo
de uC debuggear es de vital importancia.
Me Gusta

Mensaje inapropiado?

Citar

16/08/2013

Hellmut1956

#18

cosme..: Soy perezoso, por eso me permito acortar tu nombre.


Pero vayamos a tu respuestas. En general comparto tu opinin de
escribir mir propias libreras, adems que ese ejercicio es muy util
para aprender en detalle el funcionamiento de periferias.
ARM, me refiero a la empresa que desarrolla los diversos tipos de

Fecha de

controladores ARM, pero que no hace componentes fsicas ella

Ingreso:

misma, sino que licencia estos controladores a productores de

diciembre-2008
Ubicacin:
Mammendorf,
Alemania
Mensajes: 250

componentes, llamadas compaas de "semiconductores". De


estas existen aquellas con su propia fbrica para producir sus
componentes y las tales "fabless semiconductor" compaas, que
no tienen fbricas propias, sino que dejan producir estas
componentes. ARM no es lo uno ni lo otro, ellos solo por decirlo as
definen los productos y venden la licencia para usar esas
definiciones. Bueno, aqu entra el papel y la importancia de estos
API. Los que adquieren licencias tienen como parte de ese
contrato de licencia con ARM, cuando se trata de controladores
ARM Cortex M0, M0+, M3, M4 y otros, tienen la obligacin a
implementar para todas las componentes fsicas que realicen,
implementar APIs para todas las funciones perifricas que
implementen alrededor del "puro controlador ARM Cortex Mx" y
estas APIs deben ser idnticas a la especificacin para estos APIs
por ARM.
La situacin competitiva en este mercado de controladores ARM
Cortex Mx a razn de lo escrito es muy diferente de la situacin
competitiva en otros mercados de controladores para sistemas
embebidos. Todo proveedor de un controlador ARM Cortex Mx est
ofreciendo un producto idntico al de todos sus competidores, la
nica diferencia son las funciones perifricas y la combinacin de
estas funciones perifricas en un producto especfico. Cada
proveedor de controladores ARM Cortex Mx busca diferenciarse de
sus competidores por implementar este con periferias especficas
para su objetivo en cierto mercado de productos que usarn estos
controladores. De all resulta que todos los proveedores buscan
facilitar al mximo el uso de sus implementaciones de
controladores ARM Cortex Mx. Un resultado es que el mercado
ofrece placas muy, pero muy econmicas resultando en que
nosotros podemos tener acceso a placas muy econmicas. pero
volvamos a los API.
Siendo tan parecidos todos los controladores ARM Cortex Mx de
todos los proveedores, una implementacin inefectiva de las
libreras que implementan en "software" el API definido por ARM

para todas las periferias, resultara en que este proveedor


"vendera" su controlador por debajo de precio! Quin usara para
su software y sus productos los productos de un proveedor, si a
razn de una mala implementacin de las libreras que
implementan el API el producto final no es competitivo. Siendo
ARM el "nuevo Intel", significa, siendo ARM la arquitectura de
controladores, tanto embebidos como para otros mercados, que
est por dominar, todo proveedor, hasta AMD lo ha licenciado,
tiene que competir ofreciendo mayor eficiencia para realizar
productos de sus clientes.
Siendo la situacin as, creo que es correcto asumir:
1. Las libreras que componen la API definida por ARM son la
implementacin mas efectiva posible, pues cada proveedor esta
obligado ofrecerla para sus productos.
2. El papel de esta API en el mercado de los ARM Cortex Mx es
permitir a los clientes de aquellos que licenciaron de ARM y as
disean, construyen y venden sus implementaciones, poder
cambiar de proveedor o usar varios proveedores sin temer tener
grandes problemas cambiando de proveedor! La software va a
funcionar, siempre y cuando el producto de otro proveedor tenga
las periferias que la software del cliente requiera!
3. Por lo dicho en los puntos "1" y "2" es de primordial importancia
aprender y usar estos APIs cuando se usan controladores del tipo
ARM Cortex Mx.
Repito cosme..., escribo esto, no por criticarte! Como tu escribes,
vienes de usar controladores ARM 7 por ejemplo, significa no del
tipo ARM Cortex Mx, y es factible que esto te sea desconocido. En
los controladores ARM de los tipos que no son ARM Cortex Mx, lo
que escribo no vale, solo en los tipos ARM Cortex Mx!
Esto tiene sus ramificacin hasta a los IDE! NXP acab por
comprar "Code Red", proveedor de los IDE para las placas
LPCXpresso! La razn es que si la IDE no ofrece el uso ptimo para

los productos ARM Cortex Mx de NXP, las inversiones de NXP en lo


fsico de sus productos ARM Cortex Mx y en la calidad de sus
libreras que implementan la API definida por ARM, entonces los
programas que resultan como resultado de la compilacin y la
configuracin sera menos que ptima y as pondra en peligro la
competitividad de NXP con sus productos ARM Cortex Mx. La IDE
es crtica y estratgica para NXP y comprando Code Red NXP
puede garantizar que este elemento estratgico siga conforme a
sus necesidades. Otra explicacin porque el uso de las API en
controladores del tipo ARM Cortex Mx tiene otra justificacin
diferente de lo que rige en general y donde estoy completamente
de acuerdo con cosme...!
Como novato, no por el tiempo que conozco C, sino por la cantidad
de experiencia que tengo usando C para escribir programas, mi
decisin de ir por productos de la empresa NXP resulta de
beneficios adicionales que ofrece la combinacin de la IDE de
"Code Red" y de las placas LPCXpresso de NXP! La IDE de Code
Red "conoce" todas las placas LPCXpresso para las diversas
familias de controladores ARM Cortex Mx de NXP. As la IDE
automticamente se configura para generar cdigo para estas
placas. eso es lo que odiaba de las "tool chaines" de otros
proveedores. veo que ahora otros proveedores estn realizando
esta ventaja e implementando las IDEs de acuerdo a esto.
La segunda razn de mi decisin para los productos ARM Cortex
Mx de NXP, y no de TI, el competidor ms prximo en mi seleccin
del proveedor de controladores, son las dimensiones fsicas de las
placas LPCXpresso!
Aqu NXP coopera con "Embedded Artists", que disea y produce
las placas LPCXpresso. Vayan al sitio de estedistribuidor en
Europa, el ms econmico sumando costos de flete y del producto,
all compr mi LPCXpresso 1769 por una suma total de 37,80
Euros. Para lugares como latinoamrica tendrn que investigar y
encontrar un distribuidor adecuado. Aqu el enlace al LPCXpresso
1769 que compr y que cuesta 23,80 Euros!

La placa LPCXpresso consiste de 2 partes que se pueden separar


fsicamente. A la izquierda, ven que la parte tiene un conector
para la interfaz USB, a la derecha la placa con el controlador
LPC1769. ven que donde las 2 partes se encuentran la placa tiene
un puente que conecta la una a la otra. Separando la placa all y
construyndose un cable para permitir la comunicacin entre
ambas partes de la placa LPCXpresso1769, es posible usar la placa
como una componente con 2 filas de pines, vemos este como fila
de "huecos" a lo largo de esta parte y a ambos lados de esta. me
atrevo a decir. es imposible para un usuario particular o
profesional hacer esta placa uno mismo, comprando las
componentes requeridas y armandola y ponerla en funcin por
solo 23,80 Euros! Ni hablar de la calidad de la placa y ni hablar de
implementarla de forma tan compacta! este tipo de placa
LPCXpresso existe para todas las familias de controladores ARM
Cortex Mx y viene siempre usando la variante mas grande y
potente de cada familia! por estas razones y por las periferias con
las que viene voy a usar un buen nmero de estas placas en mi
modelo de un velero. es excesivo, pero dado el bajo precio de
estas placas y dado las funcionalidades ofrecidas y dado el
carcter gratuito de la IDE hasta un cdigo de 128 kbytes y las
mdicas sumas para ampliar el lmite del tamao del cdigo a
usar, no veo alternativa mas eficiente.

Pero es tan importante para escribir programas en sistemas


embebidos y normalmente el impacto econmico hace su uso
prohibitivo, es que aquella parte con la interfaz USB, tambin
implementa la interfaz JTAG, que se usa para el encontrar errores
en los programas, y esto ejecutando el programa a analizar no en
una simulacin del controlador, sino fsicamente el el controlador
LPC1769 en mi caso. Puedo hacer que el programe pare a nivel de
las instrucciones del assembler, el nivel mas prximo a la
hardware, o al nivel del cdigo "C". Al para el programa la interfaz
JTAG me permite ver el valor de variables, de registros en el
controlador, en las diversas memorias del controlador, Flash SRAM,
etctera y de cambiar los valores y despus por ejemplo seguir
con la ejecucin del programa paso a paso de instruccin a
instruccin. pero tambin en caso de eventos, como Interrupt o
eventos en uno de los pines del controlador. Esta funcin se llama
"debug". esto viene gratuito con cada placa LPCXpresso y forma
parte de la funcionalidad de la IDE! Recuerden, la IDE conoce
todas las placas LPCXpresso y por lo tanto el "debugger" conoce la
placa LPCXpresso y sabe encontrar todos los registros pues se
autoconfigura! Como NXP compro Code Red el futuro ser brillante
y excitante!
Me Gusta

ltima edicin por Hellmut1956; 16/08/2013 a las 09:50

Mensaje inapropiado?

31/08/2013

marcos0007

Fecha de Ingreso:

Citar
#19

Cosmefulanito,
Excelente tu post, te felicito. Te comento que compre la misma

agosto-2013

placa con un TFT2,8, del mismo fabricante . Estoy haciendo un

Ubicacin: san isidro

proyecto para la universidad y estoy teniendo problemas para

Mensajes: 32

conectarme con la placa.


Instale los drivers del CP210x para usb to uart, el windows me

lo detecta como com 2 , perfectamente.


El tema es como comunicarme con la placa, por lo que estuve
leyendo la mayoria utiliza JTAG, con la tarjeta ULink2 para
cortex M3.
Por otra parte, estuve leyendo que se puede programar y
comunicarme por el USB-com y atacarlo con el FlashMagic. Es
esto posible
En tu experiencia cual es la forma mas sencilla y segura de
trabajar con esta placa
Muchas gracias por todo
Saludos ...
Me Gusta

Mensaje inapropiado?

Citar

01/09/2013

cosmefulanito04

#20

Con el driver ya instalado, solo te queda usar el flash magic


para poder programarlo, los pasos son:
- Elegs el uC -> 1768.
- El puerto COM "virtual" que usa el adaptador USB.

Fecha de Ingreso:
octubre-2009
Ubicacin: Bs. As.

- La velocidad, prob con 9600.


- Interfaz -> None (ISP)
- Oscilador (MHz) -> 12

Mensajes: 2.508

Por el lado del uC, antes tens que entrar en modo de


programacin:
1- Pulsas el botn ISP (y lo mantens).
2- Pulsas el botn Reset.
Listo con eso ya ests en condiciones de leer o programar el
uC.

La desventaja de esta placa es que si quers usar el Jtag, tens


que usar el puerto paralelo

o conseguir cable usb de keil

que sale como u$d 20.


Te dejo el esquemtico para armar el Jtag usando el puerto
paralelo, es bastante simple, el problema es el puerto
paralelo

Archivos Adjuntos
NGX_PARALLEL_PORT_JTAG.pdf (44,0 KB (Kilobytes), 5 visitas)

Potrebbero piacerti anche