Buenos días a todos!!
Me presento, soy Pablo y trabajo como investigador en el laboratorio de telecomunicaciones marinas de la Universidad de Cádiz (España).
Estoy trabajando desde hace un tiempo con el dsPIC33FJ256GP710-I/PF-ND, ya que después de investigar llegué a la conclusión de que para temas de telecomunicaciones, estos son bastante eficientes. Os presento mi problema actual (mucho texto incoming):
Tengo a uno de los ADCs del dsPIC configurado para que muestree una señal (en principio es una CC, luego será una senoidal), a una velocidad de muestreo de 256KHz (según datasheet, este en concreto puede muestrear hasta a 1.1 Msps, así que la que estoy utilizando no debería ser ningún problema). La configuración del ADC quedaría así (os voy adjuntando el código según vaya hablando de él, y finalmente os adjunto el fichero completo):
void ADCInit() {
AD1PCFGLbits.PCFG10 = 0; // Se configura el ADPCFG para que el único pin
// usado analógicamente sea el AN10
AD1CON1bits.SSRC = 0b111;
AD1CON1bits.ASAM = 1;
AD1CON1bits.AD12B = 0; //10 bits resolución
AD1CHS0bits.CH0SA = 0b1010; // Entrada positiva AN10 para la entrada de la muestra
AD1CHS0bits.CH0NA = 0; // Entrada negativa se usa el VR-
AD1CON2bits.VCFG = 0b000; // Referencias: AVDD y AVSS,
AD1CON2bits.BUFM = 0;
AD1CON3bits.ADCS = 12;
}
Esa función sería para la configuración, y posteriormente tengo esta otra que lo único que hace es encender el ADC:
void ADCStart() {
AD1CON1bits.ADON = 1; // Enciende el ADC
}
Una de las posibles preguntas que os haréis es: ¿Ese valor para el ADCS? La fórmula para sacar este valor viene en el datasheet y es:
Tcy · (ADCS<7:0> +1) = Tad
Teniendo en cuenta que estoy utilizando un cristal de 20MHz para, obtener utilizando PLLs, una Fcy de 40MHz, me sale un resultado tal que:
(1/40x10^6) · (12 + 1 ) = 3.25x10^-7s = Tad.
Hasta ahí creo que sin problema. En el datasheet también se indica que para que el proceso de captura y conversión se realice sin errores, es necesario un tiempo total (para una resoluciónde 10bits, que es lo que estoy utilizando) un tiempo total de 12·Tad. Por lo tanto:
12 · Tad = 12 · 3.25x10^-7 = 3.9x10^-6 s =
3.9 us.Como os he comentado, la frecuencia de muestreo que quiero es de 256KHz, lo que equivale a:
1/256x10^3 = 3.90625x10^-6 s =
3.9 us.Según entiendo, con las configuraciones que os acabo de exponer, el ADC debería trabajar sin problemas a una frecuencia de 256KHz.
Pues bien, una vez configurado el módulo ADC, en el que CREO que está todo correcto, paso a configurar el módulo UART, ya que la transmisión de los datos al PC lo hago a través del puerto serie. La configuración de este registro quedaría tal que así:
void Configurar_UART() {//uart 2
U2MODEbits.BRGH = 1;
U2MODEbits.PDSEL = 0b00; // No Parity, 8 data bits
U2MODEbits.STSEL = 0; // 1 Stop bit
U2MODEbits.UEN = 0b00;
U2MODEbits.ABAUD = 0; // Auto-Baud Disabled
U2BRG = 9; //BAUD Rate Setting for 1.000.000
U2STAbits.URXISEL = 0; // Interrupt after one RX character is received
IFS1bits.U2RXIF = 0; // Clear the Recieve Interrupt Flag
IEC1bits.U2RXIE = 1; // Enable Recieve Interrupts
U2MODEbits.UARTEN = 1; // Enable UART
U2STAbits.UTXEN = 1; // Enable UART TX
}
De aquí lo más conflictivo creo que es la selección del BAUD Rate, el cual lo he fijado en 1.000.000, obteniendo un valor de 9 para U2BRG. Este valor se obtiene, de nuevo gracias a datasheet, a través de la siguiente fórmula:
U2BRG = (Fcy / (4 · BAUDRATE )) -1 = (40x10^6 / (4 · 1.000.000)) -1 = 9
Finalmente, utilizo los timers 2 y 3 concatenados (timer 32bits) con un preescaler 1:256 para poder ver que distancia temporal hay entre la recepción de una muestra y otra. Os dejo aquí la configuración de sus registros:
void Configurar_Timer32() {
T3CONbits.TON = 0;
T2CONbits.TON = 0;
T2CONbits.T32 = 1;
T2CONbits.TCS = 0;
T2CONbits.TGATE = 0;
T2CONbits.TCKPS = 0b11;
TMR3 = 0;
TMR2 = 0;
PR3 = 0x0017;
PR2 = 0xD784;
IPC2bits.T3IP = 1;
IFS0bits.T3IF = 0;
IEC0bits.T3IE = 1;
}
Como veis, utilizo una interrupción para que cuando este timer llegue al valor establecido (en este caso 10 segundos), deje de enviar los datos por el puerto serie. Os dejo aquí su interrupción:
void __attribute__((__interrupt__, __auto_psv__)) _T3Interrupt(void) {
AD1CON1bits.ADON = 0;
T1CONbits.TON = 0;
T2CONbits.TON = 0;
while (1) {
}
return 0;
_T3IF = 0; /* Ponemos a 0 el Flag de la Int del Timer 3 */
}
En el main, lo único que hago es capturar el valor del ADC y enviarlo por el puerto serie junto con el valor actual del TMR3 y el TMR2, para poder observar la distancia temporal que hay entre cada recepción, obteniendo algo tal que así:
Siendo los valores:
Voltaje--TMR3:TMR2 (En esa prueba no estoy inyectando ninguna señal, por eso todos los valores a cero)
Bien, si nos quedamos con los dos últimos valores de TMR2 tenemos una diferencia de ciclos de 55119 - 55065 = 54 ciclos de reloj.
Como la Fcy= 40MHz, esto querría decir que con un preescaler 1:1, tendríamos 40.000.000 de ciclos en un segundo, pero al tener el preescaler 1:256 en el timer, tenemos que:
40MHz / 256 = 156250 ciclos/s.
Por lo tanto, si tengo una diferencia de 54 ciclos entre una muestra y otra, eso quiere decir que hay una diferencia temporal de (haciendo una simple regla de tres):
54/156250 =
3.456x10^-4 s .
Y ahí mi problema, que debería tener una diferencia temporal entre una muestra y otra de 3.9 us aproximadamente, pero como podéis ver está bastante lejos. No sé si el cuello de botella está en el USB, ya que debería de estar recibiendo 256000 muestras por segundo, y no sé si el monitor serie de Arduino es capaz de mostrar semejante cantidad de datos, o si estoy haciendo algo mal en la configuración del dsPIC.
Muchas gracias a todos de antemano y perdonad por el "mucho texto", pero quería explicarlo todo para que no queden dudas.