Quiero muestrear una señal de 4kHz producida por un generador de señales.
Leo 2000 muestras seguidas y luego las envío al ordenador a través del serial virtual que proporciona el USB del STM32F103C8T6. He configurado el temporizador 3 con el fin de activar el ADC y luego me puse un búfer de longitud 2000 para DMA.
Cuando trazo la señal en el ordenador los datos no son continuos y hay un problema entre la señal.
También detengo el ADC al principio del PeriodElapsedCallback y lo inicio de nuevo al final del mismo.
Aquí está la imagen de mi señal:
El reloj del ADC es de 12MHz. Los ciclos están ajustados a 28,5 y utilizo un ADC de 12 bits. Mi reloj temporizador es de 48MHZ y cuenta 30 relojes para disparar el ADC. El DMA está configurado en modo circular. No espero estas interrupciones en mi señal porque estoy tomando 2000 muestras sin ninguna parada.
EDITAR: Este es mi código. Como mencioné antes, detengo el DMA al principio de la interrupción y lo vuelvo a iniciar al final.
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
HAL_ADC_Stop_DMA(&hadc1);
HAL_TIM_Base_Stop(&htim3);
usb_put_arr_int(arr,ARRAY_LENGTH);
HAL_GPIO_TogglePin(GPIOC, 1<<13);
HAL_TIM_Base_Start(&htim3);
HAL_ADC_Start_DMA(&hadc1, arr , ARRAY_LENGTH);
}
También cambié el periodo del temporizador de 30 a 400 y obtuve un mejor resultado pero el problema sigue ocurriendo. Sospecho de la velocidad DMA.
Según mis cálculos, el temporizador activa la interrupción cada 400/48000000 = 8.33us y la velocidad de mi ADC es de (28.5+12.5+2.5)/12000000 = 3.62us
Así que la velocidad del ADC es más rápida que la del temporizador. Por lo tanto, espero que el temporizador no pueda disparar el ADC antes de que el último muestreo se haya completado.
Cambié la función CDC según lo que dijo @JiríMaier (no cambié mi código de interrupción).
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
uint8_t result = USBD_OK;
/* USER CODE BEGIN 7 */
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
if (hcdc->TxState != 0){
return USBD_BUSY;
}
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
for (uint32_t usbTimeout = 100000; usbTimeout > 0; usbTimeout--) {
if (hcdc->TxState == 0)
break;
}
/* USER CODE END 7 */
return result;
}
También cambié la estructura de mi código y cambié mi código de interrupción según los consejos de @JiríMaier y obtuve el siguiente resultado. El problema es que si no abro el plotter serie en el ordenador al principio del arranque del microcontrolador, éste se queda atascado si abro el plotter serie después.
Cuando cambio el tamaño del buffer de 2000 a 1000 todo va bien pero no sé por qué tiene problema con el tamaño de 2000.
Para completar la documentación, esto es put_arr_int
función:
void usb_put_arr_int(uint16_t * number,int len)
{
#define batch_size 50
for (int j=0; j < (len/batch_size) ; j++)
{
count += j*batch_size;
char f[batch_size*7];
sprintf(f,"%hu\n",number[0 + j*batch_size]);
for(int i=1;i<(batch_size);i++)
{
sprintf(f,"%s%hu\n",f,number[i + j*batch_size]);
count+=i;
}
CDC_Transmit_FS(f,strlen(f));
}
len
es siempre mayor que batch size
.
0 votos
Tendrás que enviar el código para obtener una respuesta. Sospecho fuertemente que tienes un problema de longitud/manejo del buffer DMA pero es imposible decirlo sin ver el código. Si tienes la RAM, configura dos buffers en memoria y en el manejador de interrupciones al final de las conversiones sólo cambia al otro buffer y establece una bandera. En un bucle de sondeo principal, comprueba la bandera y procesa el buffer completo fuera del manejador de interrupciones.
0 votos
Algo más está funcionando a 1/200 de la frecuencia de muestreo. Desactiva cualquier interrupción con manejadores lentos mientras se realiza el muestreo; vuelve a activarlos después.
0 votos
@DeanFranks, he actualizado la pregunta.
0 votos
@user_1818839, he actualizado la pregunta
0 votos
¿Cómo se implementa el usb_put_arr_int? La función CDC_Transmit_FS((uint8_t*) buf, len) (que supongo que utilizas para transmitir el buffer) no se bloquea hasta que la transmisión haya terminado. Sólo inicia la transferencia y el código continúa inmediatamente. Así que probablemente está sobrescribiendo el buffer por una nueva medida mientras se transmite.
0 votos
@JiríMaier pero como ves he parado el DMA y el timer para que no transmita nuevos datos en el DMA. ¿Quieres decir que CDC_Transmit_FS((uint8_t*) buf, len) no bloquea el código? Pero la interrupción no está habilitada para la transmisión.
0 votos
Pero si usas (sin modificar) CDC_Transmit_FS dentro de usb_put_arr_int(arr,ARRAY_LENGTH), la función retorna inmediatamente después de que la transferencia se inicie (y aún no haya terminado). Así que se inicia el temporizador y el DMA antes de que el USB haya terminado de todos modos.
0 votos
@JiríMaier. Ese es un punto sutil. Pero, ¿cómo transmite los datos si no utiliza la interrupción?