objetivo
Apreciar la utilidad del ADC, DAC y FREEMASTER en el procesamiento de señales analógicas. Generar señales por medio del DAC. Medir el valor RMS de señales analógicas. Medir la frecuencia de señales sinusoidales.
introducción
El uso del ADC y el DAC tiene una gran utilidad en el procesamiento digital de señales. Un ejemplo de aplicación puede ser el diseño de un filtro digital, o un generador de funciones. En esta práctica se implementa un generador de funciones con la capacidad de generar onda cuadrada, diente de sierra, triangular y sinusoidal. Se implementa la suma del 3er. 5to y 7mo armónico de la señal. El periférico encargado de generar las señales analógicas es el DAC0. Para medir la señal se utiliza el ADC0, se guarda en una variable que posteriormente se visualiza en FREEMASTER.
convertidor digital-analogico dac0
El DAC0 es un convertidor digital-analógico de 12 bits, tiene modos de trabajo con el DMA, puede generar eventos de interrupción. Tiene un buffer de 16 WORDS que puede ser configurado para múltiples transferencias por medio de un apuntador. La forma en la que se utiliza el DAC0 en esta práctica es en el modo simple. Para más información acerca de los detalles de la configuración del DAC y periféricos en general, se recomienda ver el documento “k64 Sub Family Reference Manual”. La figura 1 muestra el diagrama interno del DAC general de la familia K64.
FIGURA 1. “DIAAGRAMA DE BLOQUES DEL DAC0”
ARMONICOS
Los armónicos son señales distorsionadas de una señal de tensión o de corriente que se presentan a menudo en sistemas eléctricos. El orden del armónico indica el múltiplo de la frecuencia fundamental, por ejemplo en este caso se usa una frecuencia de 60Hz, el tercer armónico tiene una frecuencia de 180Hz, el quinto 300Hz y el séptimo 420Hz. De acuerdo a la amplitud del armónico una señal original se puede distorsionar.
FIGURA 2.“DISTORSION DE SEÑAL DEBIDO A LOS HARMONICOS”
MEDICION DE FRECUENCIA
La medición de frecuencia se realiza utilizando el algoritmo de cruce por cero. Este procedimiento consiste al detectar el cruce por cero, a partir de ahí se cuenta el número de muestras hasta el siguiente cruce por cero. De lo anterior se sabe que el número de muestras no es número entero por lo que se estima la fracción resultante utilizando la ecuación de la recta. La descripción matemática del método mencionado se puede consultar en la tesis “DISEÑO E IMPLEMENTACION DE UN MEDIDOR FASORIAL SINCRONO NORMALIZADO CON EL ESTANDAR IEEE C37.118” de Blanca Hernadez Gomez.
FIGURA 3.“SEÑAL SENOIDAL DISCRETA”
En la figura 4 se muestra a detalle el cruce por cero.
FIGURA 4.“DETALLE DE CRUCE POR CERO”
Se requiere calcular el intervalo de X1 a X, por medio de la ecuación de la línea recta:
y=mx+b 1)
m=(y2-y1)/(x1-x2) 2)
Donde:
y1: muestra anterior
y2: muestra actual
x1: tiempo anterior
x2: tiempo actual
De donde la fracción de la muestra anterior, se obtiene:
∆t=x=y1/(y2+y1) 3)
Con esto se estima el promedio de la señal:
T=∆t+Numero_Muestras*Tmuestreo
Donde Numero_Muestras es la cantidad de muestras antes del cambio de signo.
DESARROLLO DE LA PRÁCTICA.
Para el desarrollo de la práctica se utiliza el DAC0, el pin utilizado para el DAC0 es la terminal J26_11 DE LA TARJETA FDRM K64, la cual tiene asignada la terminal DAC0 y ADC0_SE23.
FIGURA 5. “SALIDA DE DAC0”
Se configura como se muestra en la figura 6.
FIGURA 6.“CONFIGURACIÓN DEL DAC0”
El timer PIT0 será el encargado de actualizar el valor del DAC, figura 7.
FIGURA 7.“CONFIGURACIÓN DE PIT0”
El ADC0 es disparado por el PDB, figura 9.
FIGURA 8.“CONFIGURACION DEL ADC0”
FIGURA 9.“CONFIGURACION DEL PDB”
El diagrama de bloques del programa principal se muestra en la figura 10.
FIGURA 10. “DIAGRAMA DE BLOQUES DEL PROGRAMA PRINCIPAL”
El diagrama de flujo de la rutina principal:
FIGURA 11.“DIAGRAMA DE FLUJO DE RUTINA PRINCIPAL”
El código del programa principal:
int main(void)
{
uint32_t ModuloPdb;
uint32_t FreqBus;
Choice = 0;
Choice_Ant = Choice;
Frec = 60;
Frec_Ant = Frec;
Amp = 100;
Amp_Ant = Amp;
Amp_3=0;
Amp_3_Ant=Amp_3;
Amp_5=0;
Amp_5_Ant=Amp_5;
Amp_7=0;
Amp_7_Ant=Amp_7;
Adc_Index=0;
Adc_Inst_Value=0;
Adc_Value_Rms=0;
BUFFER_ADC_FULL=0;
BUFFER_INDEX=BUFFER_ZERO;
/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
PE_low_level_init();
/*** End of Processor Expert internal initialization. ***/
FMSTR_Init();
//Inicializa PDB
ModuloPdb=CLOCK_SYS_GetBusClockFreq()/Q_PDB;
PDB_DRV_Init(FSL_PDLY1,&pdly1_InitConfig0);
PDB_DRV_SetTimerModulusValue(FSL_PDLY1, ModuloPdb);
PDB_DRV_SetAdcPreTriggerDelayValue(FSL_PDLY1,0,1, ModuloPdb>>2);
PDB_DRV_LoadValuesCmd(FSL_PDLY1);
PDB_DRV_ConfigAdcPreTrigger(FSL_PDLY1,0, &pdly1_AdcTrigInitConfig0);
PDB_DRV_SoftTriggerCmd(FSL_PDLY1);
Res= PIT_DRV_GetTimerPeriodByUs(FSL_PITTIMER1,0);
Ticks_Frec=PIT_DRV_GetTimerPeriodByCount(FSL_PITTIMER1,0);
Tipo();
//Bucle principal de programa
for(;;)
{
//si la frecuencia anterior es diferente a la actual, cambia frecuencia
if (Frec_Ant != Frec)
{
Frec = (Frec > FREC_MAX ) ? FREC_MAX : Frec;
Frec = (Frec < FREC_MIN ) ? FREC_MIN : Frec;
New_Ticks_Frec = (Ticks_Frec*Base_Frec) / Frec;
PIT_DRV_SetTimerPeriodByCount(FSL_PITTIMER1,0, New_Ticks_Frec);
Res = PIT_DRV_GetTimerPeriodByUs(FSL_PITTIMER1,0);
Frec_Ant = Frec;
}
//si la amplitud anterior es diferente a la actual cambia amplitud, cambia el tipo de señal
if(Amp != Amp_Ant ||Amp_3 != Amp_3_Ant ||Amp_5 != Amp_5_Ant ||Amp_7 != Amp_7_Ant ||Choice != Choice_Ant)
{
Choice= (Choice> CHO_MAX ) ? CHO_MAX : Choice;
Choice= (Choice< CHO_MIN ) ? CHO_MIN : Choice;
Amp = (Amp > AMP_MAX ) ? AMP_MAX : Amp;
Amp = (Amp < AMP_MIN ) ? AMP_MIN : Amp;
Amp_3 = (Amp_3 > AMP_MAX ) ? AMP_MAX : Amp_3;
Amp_3 = (Amp_3 < AMP_MIN ) ? AMP_MIN : Amp_3;
Amp_5 = (Amp_5 > AMP_MAX ) ? AMP_MAX : Amp_5;
Amp_5 = (Amp_5 < AMP_MIN ) ? AMP_MIN : Amp_5;
Amp_7 = (Amp_7 > AMP_MAX ) ? AMP_MAX : Amp_7;
Amp_7 = (Amp_7 < AMP_MIN ) ? AMP_MIN : Amp_7;
Tipo();
Amp_3_Ant=Amp_3;
Amp_5_Ant=Amp_5;
Amp_7_Ant=Amp_7;
Amp_Ant=Amp;
Choice_Ant = Choice;
}
//mide rms de la señal
gen_getrms();
FMSTR1_Poll();
}
}
Las funciones para el generador de señales:
/*FUNCTION**********************************************************************
*
* Function Name : void Tipo (void)
* Description : selecciona tipo de onda para generador de funciones
*
*END**************************************************************************/
void Tipo (void)
{
switch (Choice)
{
case 1:
gen_sen();
break;
case 2:
gen_cua();
break;
case 3:
gen_tri();
break;
case 4:
gen_sie();
break;
default:
break;
}
}
/*FUNCTION**********************************************************************
*
* Function Name : void gen_cua(void)
* Description : genera una onda cuadrada
*
*END**************************************************************************/
void gen_cua(void)
{
uint8_t i;
for(i=0; i<32; i++)
{
Signal[i]=(gen_cuad[i]*Amp+gen_seno3[i]*Amp_3+gen_seno5[i]*Amp_5+gen_seno7[i]*Amp_7)/100+2047;
if(Signal[i]>DAC_MAX)
Signal[i]=DAC_MAX;
if(Signal[i]<DAC_MIN)
Signal[i]=DAC_MIN;
}
}
/*FUNCTION**********************************************************************
*
* Function Name : void gen_tri(void)
* Description : genera una onda triangular
*
*END**************************************************************************/
void gen_tri(void)
{
uint8_t i;
for(i=0;i<32;i++)
{
Signal[i]=((gen_trian[i])*Amp+gen_seno3[i]*Amp_3+gen_seno5[i]*Amp_5+gen_seno7[i]*Amp_7)/100+2047;
if(Signal[i]>DAC_MAX)
Signal[i]=DAC_MAX;
if(Signal[i]<DAC_MIN)
Signal[i]=DAC_MIN;
}
}
/*FUNCTION**********************************************************************
*
* Function Name : void gen_sie(void)
* Description : genera una onda diente de sierra
*
*END**************************************************************************/
void gen_sie(void)
{
uint8_t i;
for(i=0;i<32;i++)
{
Signal[i]=((gen_sierra[i])*Amp+gen_seno3[i]*Amp_3+gen_seno5[i]*Amp_5+gen_seno7[i]*Amp_7)/100+2047;
if(Signal[i]>DAC_MAX)
Signal[i]=DAC_MAX;
if(Signal[i]<DAC_MIN)
Signal[i]=DAC_MIN;
}
}
/*FUNCTION**********************************************************************
*
* Function Name : void gen_sen(void)
* Description : genera una onda senoidal
*
*END**************************************************************************/
void gen_sen(void)
{
uint8_t i;
for(i=0;i<32;i++)
{
Signal[i]=((gen_seno[i]*Amp)+gen_seno3[i]*Amp_3+gen_seno5[i]*Amp_5+gen_seno7[i]*Amp_7)/100+2047;
if(Signal[i]>DAC_MAX)
Signal[i]=DAC_MAX;
if(Signal[i]<DAC_MIN)
Signal[i]=DAC_MIN;
}
}
El diagrama de flujo para calcular el valor RMS, se observa en la figura 12:
FIGURA 12.“DIAGRAMA DE FLUJO DEL CALCULO DE RMS”
El bloque de código para el valor rms:
/*FUNCTION**********************************************************************
*
* Function Name : void gen_getrms(void)
* Description : obtiene valor rms de la señal que se mide en el adc
*
*END**************************************************************************/
void gen_getrms(void)
{
uint8_t i;
if(BUFFER_ADC_FULL)
{
Adc_Value_Rms=0;
for(i=0;i<N_ADC_SAMPLES;i++)
{
if(BUFFER_CALCULAR==BUFFER_ZERO)
Adc_Value_Rms+=(Adc_Buffer_0[i]*Adc_Buffer_0[i]);
else
Adc_Value_Rms+=(Adc_Buffer_1[i]*Adc_Buffer_1[i]);
}
Value_RMS=Adc_Value_Rms/N_ADC_SAMPLES;
Value_RMS=raiz(Value_RMS);
BUFFER_ADC_FULL=0;
}
}
/*FUNCTION**********************************************************************
*
* Function Name : uint32_t raiz(uint32_t x)
* Description : obtiene la raiz de x
*
*END**************************************************************************/
uint32_t raiz(uint32_t x){
float r = 0;
r=sqrt((float)x);
return r;
}
La rutina para actualizar el DAC, se implementa por medio de la interrupción del PIT1:
void pitTimer1_IRQHandler(void)
{
/* Clear interrupt flag.*/
PIT_HAL_ClearIntFlag(g_pitBase[FSL_PITTIMER1], FSL_PITTIMER1_CHANNEL);
Mostrar = Signal[index++];
//Actualiza salida de DAC0
DAC_DRV_Output(FSL_DACONV1,Mostrar);
if(index==DAC_TEST_BUFF_SIZE)
index = 0;
if (cnt == 32)
{
cnt=0;
GPIO_DRV_TogglePinOutput(LED_BLUE);
}
else
{
cnt++;
}
}
En la rutina de interrupción del ADC, se guarda un dato en un buffer, cuando se llena se activa una bandera que indica que hay datos disponibles para calcular el RMS. Se implementa el algoritmo de cruce por cero. El diagrama de bloques para esta rutina se muestra en la figura 13.
FIGURA 13.“DIAGRAMA DE FLUJO DE INTERRUPCION DE ADC0”
El código de la interrupción del ADC0:
/*! adConv1 IRQ handler */
void ADC0_IRQHandler(void)
{
/* Write your code here ... */
Adc_Inst_Value=ADC16_DRV_GetConvValueRAW(FSL_ADCONV1,ADC16_CHN_GROUP);
if(BUFFER_INDEX==BUFFER_ZERO)
{
Adc_Buffer_0[Adc_Index]=Adc_Inst_Value-2047;
v_act=Adc_Buffer_0[Adc_Index];
}
else
{
Adc_Buffer_1[Adc_Index]=Adc_Inst_Value-2047;
v_act=Adc_Buffer_1[Adc_Index];
}
//Detecta cruce por cero
if((v_act>0)&&(v_ant<0))
{
periodo=n_muestras;
delta=(v_ant<<12)/(v_ant-v_act);
periodo=periodo+delta;
delta_ant=(1<<12)-delta;
n_muestras=delta_ant;
if(periodo>78000)
Frec_Calculada=FREC_BASE_CALCULADA/periodo;
frec_media=(Frec_Calculada+frec_media*9)/10;
}
else
n_muestras+=1<<12;
v_ant=v_act;
Adc_Index++;
if(Adc_Index>=N_ADC_SAMPLES)
{
Adc_Index=0;
BUFFER_ADC_FULL=1;
}
FMSTR1_Recorder();
}
resultados
Se muestran los resultados para formas de onda sinusoidal, cuadrada, triangular y diente de sierra a 60Hz y 100Hz respectivamente:
FIGURA 14.“SINUSOIDAL A 60HZ”
FIGURA 15.“CUADRADA A 60 HZ”
FIGURA 16.“TRIANGULAR A 60 HZ”
FIGURA 17.“DIENTE DE SIERRA A 60 HZ”
FIGURA 18.“SINISOIDAL A 100 HZ”
FIGURA 19.“CUADRADA A 100 HZ”
FIGURA 20.“TRIANGULAR A 100 HZ”
FIGURA 21.“DIENTE DE SIERRA A 100 HZ”
La señal a 60 Hz con los armonicos 3ro, 5to, 7mo se muestra en la figura 22:
FIGURA 22. “SEÑAL DE 60 HZ CON ARMONICOS”
El valor RMS y la frecuencia medida a 60Hz y 100Hz respectivamente en las figuras 23 y 24. Como se observa existe una variación en el valor RMS y la frecuencia medida:
FIGURA 23.“FRECUENCIA Y VALOR RMS DE SEÑAL DE 60 HZ”
FIGURA 24.“FRECUENCIA Y VALOR RMS DE SEÑAL DE 100 HZ”
REFERENCIAS
Manual de microcontroladores de la serie K64.
Proyecto de la práctica:
Este comentario ha sido eliminado por el autor.
ResponderEliminar