[Aporte] Tutorial ARM Cortex-M3 - LPC1768

muy buen tutorial cosme ! algun dia voy a continuar el de ARM7 lo abande un poco por falta de tiempo! :(
 
muy buen tutorial cosme ! algun dia voy a continuar el de ARM7 lo abande un poco por falta de tiempo! :(

Gracias.

Este tutorial te puede ser muy útil para ARM7, solo cambian pequeñas cosas como el nombre de los registros, pero básicamente es muy similar.

A vos tu código te funciona perfectamente?

Si, por ej. colocando una batería entre masa y ADC0 (P0.23), usando el código "Sexta Parte - ADC", obtengo una conversión entre 1971 y 1977, con varias muestras (1,587v a 1,592v, menos de 4mV!).

¿Configuraste bien el PLL? ¿configuraste bien el pre-escaler del ADC?

En el ejemplo que subí, el PLL está configurado para trabjar con un CCLK=100MHz, PCLK=25MHz, pre-escaler del ADC 250 dando como resultado una frecuencia de muestreo de 100kHz para tener 12 bits de resolución.
 
Cosmefulanito, yo probé el código tal cual lo bajé de tu ejemplo. Las cosas que fui probando fue

PHP:
    configura_timer0(25000,1000);    //Pre-escaler 250000 => 25MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg 

    iniciar_adc(0,0);    //Inicio el ADC en el canal 0 sin offset

cambiarle el 1000 por 500 para que haga la conversión más rápido, otra cosa que cabié fué probar iniciar el canal 2 en lugar del 0. Nada más

En realidad no necesito 12 bit de resolución con 8 alcanzaría. pero varía demasiado
GRacias
 
Cosmefulanito, yo probé el código tal cual lo bajé de tu ejemplo. Las cosas que fui probando fue

PHP:
    configura_timer0(25000,1000);    //Pre-escaler 250000 => 25MHz/250000=1000Hz => 1000cuentas => 1000Hz/1000cuentas=1Hz=1Seg 

    iniciar_adc(0,0);    //Inicio el ADC en el canal 0 sin offset

cambiarle el 1000 por 500 para que haga la conversión más rápido, otra cosa que cabié fué probar iniciar el canal 2 en lugar del 0. Nada más

Cambiar el timer0 no hace ni más rápida ni más lenta la conversión del ADC, sino que generás interrupciones de timer en menor tiempo, es decir c/500mS.

La velocidad de conversión se fija cuando realizas una usando la función "convertir_adc", en mi ejemplo usé:

PHP:
if(convertir_adc(0,250,&valor_adc)>0)    //Convierto en el canal 0, con un pre-escaler=250 
{ 
  ....
}

En esa condición el pre-escaler está en 250 => PCLK=25MHz => Fsampling=100kHz.

¿Estás usando bien la variable que pasas por referencia? (en este caso "valor_adc")

En realidad no necesito 12 bit de resolución con 8 alcanzaría. pero varía demasiado

Insisto, no es solo un tema de tener mayor o menor resolución, a mayor velocidad de conversión, mayor será el ruido que te entre en la lectura, por eso como primera medida te recomendé bajar la velocidad.
 
Aprovecho que veo que estáis con DAC para una duda sobre un trabajo que tenía (adjunto el pdf), y es en la actividad 7.2, que he de utilizar la función SYSTICK para que cada 1ms me saque las 64 muestras, y bueno, en general no se por donde empezar... ¿Alguna idea de como realizar el código?
 

Adjuntos

  • Practica_7_Conversor_Digital_Analogico.pdf
    270.4 KB · Visitas: 29
Se me ocurre que podrías usar un timer c/15us para que vaya muestreando el vector y lo saques por el DAC hasta llegar al índice 65, luego c/1mS sincronizas de nuevo y reinicias el índice.

Para eso, yo configuraría el PCLK del timer en 100MHz, de esa forma aumentas la cuenta en el timer y tenés menor error de cuantización, tal como expliqué en el ejemplo "Segunda Parte - PLL y timers (código)":

cosmefulanito04 dijo:
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 cuantización de 1/25=0,04 cuentas. Para mejorar ese inconveniente, se podría aumentar el Pclk a 100MHz, logrando así que se necesiten 100 cuentas para obtener 1uS y bajando el error de cuantización a 1/100=0,01 cuentas.
 
Última edición:
Me exigen emplear únicamente el SYSTICK (lo siento estoy un poco verde en estos temas, ¿pero entiendo que PCLK es un periférico, no?) y no se que valor he de dar al registro TENMS de SysTick para conseguir 1ms en lugar de los 10ms que están predeterminados, le pregunté al profesor y me respondió escuetamente que debía emplear una frecuencia de 64KHz pero no entendí bien como emplearla ni como declararla en la función.
 
Mucho sentido no le veo usar el systemtick a frecuencia tan alta... pero si te piden eso, hace eso :D

La cosa es así, vos tenés 64 muestras que debes muestrear a 1mSeg (1kHz), por lo tanto el tiempo de c/muestra deberá ser de 1mSeg/64, eso te dá casi 15uSeg (los que mencioné arriba, que son esos 64kHz que menciona tu pofesor).

Resumiendo, configurá el System Tick para que lance una interrupción c/15uSeg y en la rutina de interrupción y envia por Dac la muestra (y no voy a subirte el código porque es tu tarea).

Si lo querés hacer perfecto, también deberías tener en cuenta el tiempo de conversión del Dac.
 
Última edición:
¡Vale, genial! Ya veo lo que quería decirme el profesor con esa frecuencia jaja, una última cosilla, ¿Cómo calculo el valor del registro de SYSTICK que realiza la cuenta atrás (TENMS) para que en lugar de los 10ms sea de 15us? ¿He de tantearlo o hay alguna forma de calcularlo? Gracias!

Entiendo que es lo que me piden y me gusta resolverlo yo, no trato de que me resuelvas el ejercicio aunque entiendo que pudiese parecerlo, pero no nos explican casi nada en clase y luego de leerme el manual de LPC17xx sigo con bastantes dudas... Si te sirve como prueba subo el ejercicio por el que te pregunte el otro día y como lo resolví yo:)

PHP:
/*----------------------------------------------------------------------------
 * Name:    SYSTICK_interrupt.c
 *----------------------------------------------------------------------------*/

#include "LPC17xx.H"
uint32_t pulsador;  
uint32_t SystemFrequency=100000000;

volatile uint32_t msTicks=1;  // Contadores de Ticks de x ms
uint32_t LED1_18=0;
uint32_t LED1_29=0;
uint32_t contador=0;
uint32_t k=1;


void SysTick_Handler (void)			// Rutina (ISR) de atención...
                                //... a la excepción 15 -del SYSTICK-
{
	if (pulsador==0){		// pulsador pulsado
		if(msTicks==1){
			LED1_29=1;		// enciendo LED P1.29
			LED1_18=0;		// apago LED P1.18
		}
		if(msTicks>=100 && msTicks<200){
			LED1_29=0;					// apago LED P1.29
		  	LED1_18=1;					// enciendo LED P1.18
		  	}
		if(msTicks>=200){
		  	LED1_18=0;					// apago LED P1.18
			msTicks=0;
			}
		}
    else{					// pulsador sin pulsar
		if(msTicks==1){
			LED1_29=1;		// enciendo LED P1.29
			LED1_18=0;		// apago LED P1.18
		}
       	if(msTicks>=200 && msTicks<400){
        	LED1_29=0;					// apago LED P1.29
		  	LED1_18=1;					// enciendo LED P1.18
          	}	
		if(msTicks>=400){
	  		LED1_18=0;					// apago el LED P1.18
			msTicks=0;
			}
    	}
	if(LED1_18==1){
		LPC_GPIO1->FIOPIN &= ~(1<<18);		// enciendo LED P1.18
		LPC_GPIO1->FIOPIN &= ~(1<<29);		// apago LED P1.29
		}
	if(LED1_18==0){
		LPC_GPIO1->FIOPIN |= (1<<18);	// apago LED P1.18
		LPC_GPIO1->FIOPIN |= (1<<29);	// enciendo LED P1.29
		}
	if(msTicks==k*100){
		contador++;
		k++;						// Indicador de que ha llegado a 1 segundo
	}
	msTicks++;	                   		// Incrementar contadores cada 10ms  y cada 1s
}

static __INLINE uint32_t SysTick_Configuracion(uint32_t prio_SysTick)
{
	SysTick->CTRL |= (1<<2);						//Selecciono la fuente de reloj interna
	SysTick->CTRL |= (1<<0);						//Habilito la cuenta de SysTick
	SysTick->CTRL |= (1<<1);						//Habilito su posibilidad de generar interrupción
	SysTick->LOAD = SysTick->CALIB;	//Genera una interrupción de 10ms, los cuales los coge del registro de Calibración
	SysTick->VAL = 0;
	NVIC_SetPriority(SysTick_IRQn, prio_SysTick);	//Establece la prioridad que le llega como parametro

 return(0);
}


int main (void)
{
  LPC_GPIO1->FIODIR |= (1<<18);	  // P1.18 definido como salida
  LPC_GPIO1->FIODIR |= (1<<29);	  // P1.29 definido como salida  
  LPC_GPIO2->FIODIR &= ~(1<<12);  // P2.12 definido como entrada
  SysTick_Configuracion((1<<__NVIC_PRIO_BITS) - 1); 
  
	
  // SystemInit ();                // Inicializar relojes " clocks"
  //SysTick_Config (SystemFrequency/100);    // Configurar  SYSTICK

  while (1) pulsador=((LPC_GPIO2->FIOPIN & (1<<12))>>12);
}
 
markisrodrigo dijo:
¡Vale, genial! Ya veo lo que quería decirme el profesor con esa frecuencia jaja, una última cosilla, ¿Cómo calculo el valor del registro de SYSTICK que realiza la cuenta atrás (TENMS) para que en lugar de los 10ms sea de 15us? ¿He de tantearlo o hay alguna forma de calcularlo? Gracias!

Fijate que en el mensaje #56 que te deje el método para calcular la cuenta y que registro tenés que modificar, más detalles fijate en la hoja de datos.

Como evidentemente tu tiempo no será en mSeg, sino que en uSeg, en vez de 1000, deberías usar 1000000 en la división:

[LATEX]Cuenta=\frac{TuSeg}{1000000}.Clock-1[/LATEX]

Ejemplo, 100uS con clock de 100MHz:

[LATEX]Cuenta=\frac{100}{1000000}.100000000-1=9999[/LATEX]

markisrodrigo dijo:
...pero no nos explican casi nada en clase y luego de leerme el manual de LPC17xx sigo con bastantes dudas...

Entiendo, te dan mucha teoría y poca práctica.
 
Última edición:
Cosmefulanito,
te hago una consulta tengo que convertir un uint32_t en uint8_t, como primer medida tomé tu función obviamente no me funcionó

Código:
void convertir_digitos_u32_serie(uint8_t digitos[],uint32_t value)
{	  
	  uint8_t aux;
    
    for(aux=4;aux>0;aux--)
        {
        digitos[aux]=(value%10)+0x30;
        value=(int)(value/10);
        }
        
    digitos[0]=value+0x30;

}

Después probe directamente así:
Código:
UINT32 value;
UINT8 result[4];

result[0] = (value & 0x000000ff);
result[1] = (value & 0x0000ff00) >> 8;
result[2] = (value & 0x00ff0000) >> 16;
result[3] = (value & 0xff000000) >> 24;

tampoco funcionó, es para presentar el dato en pantalla anteriormente ya había tenido que pasar de uint16 a uint8, pero eso funcionó correctamente.

Gracias como siempre
 
La función no pasa de u32 a u8 :no: .

La función pasa de u32 a String, es decir un vector u8 (o char según corresponda). Ahora, no recuerdo pasar u32, si u16, por lo tanto de esa función hay que corregir la cantidad de dígitos:

PHP:
void convertir_digitos_u32_serie(uint8_t digitos[],uint32_t value)
{	  
	  uint8_t aux;
    
    for(aux=9;aux>0;aux--)
        {
        digitos[aux]=(value%10)+0x30;
        value=(int)(value/10);
        }
        
    digitos[0]=value+0x30;

}

Entonces ahora el vector "digitos" deberá ser de 10 elementos.

Ejemplo:

PHP:
u8 vector_digitos[10];

convertir_digitos_u32_serie(vector_digitos,32000);

for(cont=10;cont>0;cont--)
  envia_dato_uart0(vector_digitos[cont-1]);

Luego de convertir el u32 a string, por el puerto serie saldrán los siguientes caracteres:

Salida uart dijo:
'0'-'0'-'0'-'0'-'0'-'3'-'2'-'0'-'0'-'0'

Alternativa Ansi-C => sprintf.
 
Cosmefulanito, consulta, vos pudiste probar multiples canales de ADC en simultáneo?
Estoy armando un código que trabaje en modo DMA y otro en modo Burst, para ver si puedo eliminar la variación que tengo en la medida. pero cuando compilo acusa este error
ERROR: L6218E: UNDEFINED SYMBOL. Antes ya me pasó y era un tema de librerías pero ahora no lo puedo solucionar.

Gracias
 
No probé, solo usé un canal en las pruebas.

Tené en cuenta que al trabajar con un multiplexo analógico, solo podes convertir un canal a la vez e incluso en muchos uC c/vez que cambias de canal, necesitas hacer una conversión de descarte.
 
Estuve leyendo que en el modo Burst, convierte todos los canales a la vez y así podes obtener las mediciones de las entradas a medir, sin tener que multiplexar. Lo estoy probando, ni bien salga, comento.
 
Cosmefulanito una consultoa, etuve volviendo a tu ejemplo ADC-Parte 6, modifiqué la parte de conversión porque quería mostrar un promedio del dato para que varíe menos. Esto me quedó

Código:
case MOSTRAR_CONVERSION_ADC:
					{
						if(convertir_adc(0,125,&valor_adc)>0)	//Convierto en el canal 0, con un pre-escaler=125
						{	valor_adc=valor_adc*0.805860; 
							contador=contador + 1;
						    valoradc=(valor_adc + valoradc)/contador;
							
							if (contador==8)
								{ enviar_string_uart0(" Conversion= \n");
								 envia_u16_string_uart0(valoradc);
								 contador=0;
								}
							valor_adc=0;
						}
					}

El valo 0.805860 es para que cuando le conecte 3300mv me muestre 3300 en lugar de 4095. el tema está en que ahora que le hice el promediado, el valor queda casi constante con una pila de 1.5v. Pero el valor que me muestra es 235. No entiendo porque jaja. No se si se está perdiendo la cuenta o si estoy calculando mientras convierte. Está bien lo que estoy planteando?

gracias
 
gracias capo!!

Estoy "debbugueando" para ver porque me muestre ese valor tan errado.

Si no me equivoco le erras en dos cosas:

  • La variable contador no la limitas, esto implicará desborde en algún punto.
  • El promedio siempre lo haces con dos valores, el actual y la sumatoria ya promediada con los valores viejos, por lo tanto en vez de dividir por la variable contador, deberías dividir por 2.

Lo que deberías hacer es un promedio móvil, ejemplo:

PHP:
#define MUESTRAS_MAX 16

int main()
{
  ...
  //--------- Vectores de muestreo --------------//
  u16 muestra_adc[MUESTRAS_MAX];
  u8 indice_muestra_adc=0;
  
  u32 promedio_movil=0;
  //--------- Vectores de muestreo --------------//
  ....
  iniciar_adc(0,0);    //Inicio el ADC en el canal 0 sin offset
  // Inicializo timers, puerto serie, etc
  ....
 
  while(1)
    {
      
      if(flag_timer0) //Configuro el timer0 (o cualquier timer disponible), para que c/cierto tiempo tome una muestra del ADC.
        {
           if(convertir_adc(0,250,&valor_adc)>0)    //Convierto en el canal 0, con un pre-escaler=250 
            { 
               muestra_adc[indice_muestra_adc]=valor_adc;
               indice_muestra_adc++;
               if(indice_muestra_adc==MUESTRAS_MAX)
                 indice_muestra_adc=0;
            } 
         }
        
         if(flag_timer1) //Configuro el timer1 (o cualquier timer disponible), para que c/cierto tiempo envíe el promedio móvil por uart (ej. c/1 seg).
           {
              promediar_datos(muestra_adc,&promedio_movil); //Funcion que promedia las muestras
              enviar_string_uart0(" Conversion promediada= \n"); 
              envia_u16_string_uart0((u16)(promedio_movil));
           }
        
    }
}

La función promedio móvil es simplemento un for:

PHP:
void promediar_datos(u16 muestras[],unsigned long int *promedio_movil)
{
	u8 cont;
	
	*promedio_movil=0;
	for(cont=0;cont<MUESTRAS_MAX;cont++)
		*promedio_movil+=muestras[cont];
									
	*promedio_movil=(u16)(*promedio_movil/MUESTRAS_MAX);
}
 
Atrás
Arriba