lunes, 10 de diciembre de 2012

Lectura analógica en TMS320f28027

Como ya comenté en entradas anteriores, compré hace un par de meses una placa de desarrollo de Texas Instruments la cual lleva un DSP (Procesador digital de señal), de la familia c2000, en concreto lleva un f28027. No es uno DSP de los más potentes pero no está nada mal, ya que lleva en su interior un procesador de 32 bits funcionando a 60 MIPS. El punto más fuerte de este tipo de procesadores es la parte del conversor analógico-digital, ya que este DSP, sin ser de gama alta ni mucho menos lleva un CAD de 12 bits que nos permite conversiones en 6 ciclos de reloj, lo que nos da una velocidad de 10Msps, recordemos que los PIC más potentes nos dan 1,3Msps para conversiones de 10 bits, mientras que si queremos conversiones a 12 bits la velocidad baja hasta los 500ksps.
El módulo ADC de los DSP, a parte de la alta velocidad y la gran resolución nos permiten hacer hasta 2 muestras diferentes, aunque solo cuente con un CAD. Esto es posible ya que cuenta con dos circuitos de muestreo y retención, o sample and hold. Esto es muy útil por ejemplo para calcular potencias instantáneas, ya que podemos, en un momento dado medir la corriente y la tensión sin que haya diferencia de tiempo entre ambas.
Otra de las características del CAD de los DSP de texas, es que pueden hacer lecturas de forma secuencial. Esto es, existe una pila en la que nosotros ponemos por orden los canales que vamos a leer, por ejemplo, en primer lugar leeremos el canal 5, después el canal 7, después el canal 4…, cada uno de estos puede tener una señal de disparo diferente, o todos la misma, si por ejemplo los canales 5 y 4 tienen la misma señal de disparo, o trigger, el conversor leerá el canal 5 y después el 4, saltándose el 7. Esto ocurre cuando no hemos seleccionado la lectura simultánea, si está seleccionada esta, las conversiones van siempre en parejas simultáneas, es decir, el que hemos puesto en la posición 1 de la pila, con el que hemos puesto en la 2, el de la 3 con la 4 y así sucesivamente. Para poder realizar lecturas simultáneas, hay que tener en cuenta que los canales que hayamos elegido deben pertenecer a circuitos sample and hold diferentes, esto es por ejemplo que en la posición 1 de la pila esté el canal A1, y en la posición 2, el canal B4, donde A y B son cada uno de los circuitos de sample and hold de nuestro DSP.
He hablado antes de las señales de trigger, en estos DSP, como sucede en los DSPic y PIC32, la señal que inicia la conversión del conversor puede ser bien el módulo PWM, una señal de cualquier temporizador, el seteo manual de un registro, etc, y cuando esta señal se active, el conversor iniciará la conversión para aquellos canales cuyo trigger se haya activado.
El último punto que queda por configurar en el CAD de los DSP, es la ventana de muestreo que queremos para nuestra aplicación. Como he dicho, el conversor de 12 bits necesita al menos 6 ciclos de reloj para realizar la conversión, pero no siempre será suficiente con 6 ciclos, ya que habrá sensores, que por su alta impedáncia de salida, hagan que nuestro conversor con tan solo 6 ciclos no haya podido muestrear correctamente la tensión, por lo que este tiempo es configurable desde 6 ciclos hasta 64 ciclos. Dicho todo este, solo queda empezar con el código.
Para centrarnos solo en la parte que nos interesa, la parte de la inicialización del DSP la he realizado mediante las librerias que ofrece texas, por eso podeis ver en el código una instrucción que es DeviceInit(), la cual inicializa oscilador y PLL para trabajar a una frecuencia de 60MHz, así como los 4 leds y el pulsador de la placa. En cuanto a la  inicialización del módulo CAD es la siguiente:
void InitADC(){

 EALLOW;
 SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1;    // ADC
 AdcRegs.ADCCTL1.bit.TEMPCONV = 1;
 EDIS;
 ADC_SOC_CNF(ChSel, TrigSel, ACQPS, 1, 0);

}
Lo primero que hacemos es llevar el reloj al modulo DAC mediante el registro ADCENCLK, después, y solo para probar que todo funciona bien, hemos conectado por software el sensor de temperatura interno del DSP con el canal A5, seteando el registro TEMPCONV. Una vez hecho esto nos queda configurar la adquisición del conversor, para ello he utilizado una instrucción que nos da texas, en la que le tenemos que meter, en primer lugarel parámetro ChSel, el cual le dice el orden de canales que queremos leer, el segundo es TrigSel, mediante el cual, para cada canal seleccionamos la señal de trigger que queremos, siendo 0 para seteo por software, 1 para el temporizador 0, 2 para el temporizador 1… como está descrito en el datasheet, el siguiente parametro es la ventana de muestreo, tambien para cada canal por separado, el siguiente parametro es el nivel de interrupción, es decir, cuantos canales queremos leer antes de que salte la interrupción, y por ultimo, el modo, en el cual podemos elegir entre lectura continua (mode = 0), lectura start – stop (mode = 1) en la cual leemos cuando se dispara el trigger. Los vectores quedarían de la siguiente manera.
// ADC configuration variables
int16 ChSel[16]={5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int16 TrigSel[16]={1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int16 ACQPS[16]={10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10};
Donde vemos que se va a convertir el canal A5, que tendrá un trigger mediante T0, los demás, tendrán un trigger manual, por lo que si no lo activamos, nunca se leerán. La configuración del temporizador 0 es la que sigue.
void InitT0(){

 asm(" eallow"); //permiso escritura en memoria

 SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // llevamos el reloj al timer0

 CpuTimer0Regs.PRD.all = periodo; //Cargamos el valor del periodo para 100us
 CpuTimer0Regs.TCR.bit.TRB = 1; //cuando el timer llega a 0, se precarga el valor de TPR
 CpuTimer0Regs.TCR.bit.TIF = 1; //borramos el flag del timer.

 asm(" edis");

}
La variable periodo está en principio en 6000, y los preescalers están ambos en 1, nos da un periodo de 100us.
Una vez inicializados los dos módulos solo queda escribir el main, el cual es muy sencillo.
//main
void main()
{

// CONFIGURACIÓN INICIAL.
 DeviceInit();
 InitT0();
 InitADC();
  //loop
 while(1) {

  if(CpuTimer0Regs.TCR.bit.TIF == 1) { //si el temporizador ha llegado a 0...
   sampleTemp = AdcResult.ADCRESULT0;
   CpuTimer0Regs.TCR.bit.TIF = 1;
   CpuTimer0Regs.PRD.all = periodo;
  } //if
  GpioDataRegs.GPADAT.bit.GPIO1 = led;
 }// while
}//main
Con este tendremos nuestro DSP muestrando la señal analógica proveniente del sensor de temperatura interno cada 100us, la cual leemos desde el registro AdcResult.ADCRESULT0, y almacenamos en sampleTemp.

1 comentario:

  1. Hola Pablo, podrías explicar un poco la parte en que dices que el periodo esta a 6000 y los prescaladores en 1 ¿Como consigues los 100 us?. Muchas Gracias

    ResponderEliminar