Uart Transmite cada 1µs Interrupción StM32 NucleoF401-Re

Estoy usando las bibliotecas STM32 Nucleo F401-RE y HAL. Me gustaría enviar valor a través de UART a la consola de la PC cada interrupción de 1 µs. En realidad, quiero que cada interrupción de 1 µs incremente un contador para que actúe como una marca de tiempo, luego envíe este valor cada 1 µs, de modo que cuando haya una interrupción cada 1 µs -> Incremento de un contador (++ recuento) y envíe el valor de el contador.

Mi código que no funciona como quiero que sea:

Mi reloj: 84Mhz

Utilicé la calculadora de temporizador MicroElektronika para calcular el período y el Prescaler para una interrupción de 1 µs. Configuración del temporizador:

__TIM4_CLK_ENABLE()
;
TIM_Handle.Init.Prescaler = 0;
TIM_Handle.Init.ClockDivision = 0;
TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM_Handle.Init.Period = 83;
TIM_Handle.Init.RepetitionCounter = 0;
TIM_Handle.Instance = TIM4;   //Same timer whose clocks we enabled
HAL_TIM_Base_Init(&TIM_Handle);     // Init timer
HAL_TIM_Base_Start_IT(&TIM_Handle); // start timer interrupts
HAL_NVIC_SetPriority(TIM4_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(TIM4_IRQn);

Y el controlador TIM4 IRQ:

uint32_t count=0;

void TIM4_IRQHandler(void) {
if (__HAL_TIM_GET_FLAG(&TIM_Handle, TIM_FLAG_UPDATE) != RESET) 
        {
    if (__HAL_TIM_GET_ITSTATUS(&TIM_Handle, TIM_IT_UPDATE) != RESET) {

        __HAL_TIM_CLEAR_FLAG(&TIM_Handle, TIM_FLAG_UPDATE);
        ++count;
        printf("%ld\r\n,count");

    }
}

}

Los UART generalmente no funcionan tan rápido. ¡Para enviar una cantidad de 32 bits cada 1 us, su UART debería estar funcionando a una velocidad mínima de 40 Mbit/seg!
Entonces, ¿cuál es la mejor tarifa? puedo al menos cada 10 µs?
La mejor tasa está limitada en parte por el receptor de datos. Supongo que 4Mbits podría ser sostenible, pero eso es solo una suposición. A esa velocidad, un protocolo de 5 bytes de longitud fija tardaría 10 µs en completar la transferencia. Así que 10µs suena como un objetivo plausible. He agregado algunos detalles más en mi respuesta.
Necesito que mi aplicación tenga una resolución de al menos 10 µs para mi reloj, por eso necesito un temporizador con interrutpino de 10 µs.
elimino mi comentario mira este ejemplo: carminenoviello.com/2015/03/02/… @YoussefKamoun
bien, este no usa printf, usa Hal_uart_Transmit pero el mismo problema 115200 velocidad de transmisión que es solo 14,400 bytes / segundo. Un recuento de 1,000,000,000 tomará 30 bits, o al menos 4 bytes. Por lo tanto, el número máximo de conteos que se pueden transferir es de 3600/segundo, es decir, una marca de tiempo cada 277 µs. lo que dijo @gbulmer y, por lo tanto, no es una marca de tiempo de 10 µs
Explique el problema que está tratando de resolver, incluido el receptor, y es posible que podamos ayudarlo. Actualmente parece estar lejos de ser factible con el hardware que está utilizando. Inicie una nueva pregunta para eso. Puede incluir un enlace a esta pregunta en su nueva pregunta.

Respuestas (3)

La línea printf("%ld\r\n,count");siempre enviará "\r\n,count", que es de 8 bytes, y probablemente no sea lo que desea.

Probablemente quisiste escribir:

printf("%ld\r\n",count);

Sin embargo, como explicó Dave Tweed, es poco probable que el UART funcione lo suficientemente rápido.

Después del primer milisegundo, el conteo será de 4 dígitos, por lo que printfintentará imprimir al menos 6 bytes, lo que requeriría 60 Mbit/segundo.

Peor aún, TIM4_IRQHandler(void)se bloqueará esperando que se printfcomplete, por lo que se bloquearán todas las interrupciones de menor prioridad. El Nucleo F401-RE se bloqueará dentro del controlador de IRQ.

Turbo J señala que, incluso si el UART pudiera manejar la velocidad de datos, el código printfconsumirá más ciclos de instrucción que los disponibles antes de que ocurra la siguiente interrupción.

Por cierto, ¿qué está recibiendo los personajes de la UART? ¿Qué tasa de datos es capaz de recibir? Una gran cantidad de convertidores USB a UART ordinarios tendrán dificultades para superar mucho más de 1 MByte/segundo.

Sería más eficiente un formato de longitud fija o un formato que solo use un bit para señalar el final del 'paquete'. Una buena elección también depende de las capacidades de los receptores. Escriba directamente en el búfer UART, evite usar printf. No hará nada útil para un protocolo binario (a menos que use DMA internamente).

Posibles enfoques: 'paquete' fijo de 4 bytes.

Edit3:
suponiendo que una sendByte(byte)función esté disponible para rellenar bytes directamente en el búfer UART:

sendbyte((count>>24)&0xff);
sendbyte((count>>16)&0xff);
sendbyte((count>>8)&0xff);
sendbyte((count)&0xff);

Enviaría los 4 bytes de la cuenta. Sin embargo, el receptor podría perder la sincronización. Esto no sería un gran problema durante los primeros segundos, ya que el byte superior siempre sería cero. Además, el receptor siempre puede ver qué byte está cambiando más rápidamente. Sin embargo, si perder bytes es un problema, esto podría no ser suficiente.

Para ayudar a evitar perder la sincronización, reserve el bit superior de cada byte para marcar el inicio o el final del paquete. Entonces, use un conteo máximo de 28 bits y establezca el bit superior del primer byte del paquete de 4 bytes en '1' y el bit superior de los siguientes tres bytes en '0'.

Use caracteres imprimibles, para simplificar use 6 bits, con el bit superior del primer byte establecido (por lo que está por encima de ASCII) pero el resto usando ASCII. Esto perdería otro bit de cada byte, por lo que es posible que desee transmitir 30 bits en un paquete de 5 bytes. Estos mensajes serían imprimibles, lo que podría ayudarlo a depurarlo.

Si mantuvo la velocidad de datos a una velocidad que el receptor pudiera manejar (¿quizás 4 Mbit/s o menos?) y envió lo suficientemente lento para dar una cantidad razonable de tiempo de procesamiento al resto de la aplicación, debería estar bien.

Eso sugeriría un byte cada 2 µs, y para el protocolo de 5 bytes, una marca de tiempo cada 10 µs.

Puede usar DMA para enviar los bytes. El temporizador IRQ configuraría los bytes para la transferencia, inicializaría el DMA y todo sucedería en segundo plano. Sin embargo, esto es mucho más complejo de configurar, así que haga que la transferencia funcione antes de intentar usar DMA.

Editar:
a una velocidad de transmisión de 115200, eso es solo 14,400 bytes / segundo.
Un conteo de 1,000,000,000 tomará 30 bits, o al menos 4 bytes.
Por lo tanto, el número máximo de conteos que se pueden transferir es de 3600/segundo, es decir, una marca de tiempo cada 277 µs.

Le recomiendo encarecidamente que explique el problema real que está tratando de resolver, tal vez en una nueva pregunta, ya que actualmente parece estar lejos de ser factible.

Edit2: no pude encontrarlos, pero creo que FTDI publicó algunos números de referencia para su USB a UART usando transferencias USB de 12 Mbits hace unos años. Creo que, en condiciones de referencia (que ellos diseñaron), obtuvieron poco menos de 700 kBytes/segundo (aproximadamente 670 kB/s, creo). Sin más detalle, lo consideraría inalcanzable en situaciones generales, y un máximo absoluto.

Hay un conjunto algo interesante de puntos de referencia de 'lectura' de USB de PJRC que compara varios microcontroladores equipados con USB . Los resultados para Maple (un STM32F103) fueron 234.114 Bytes/segundo directamente sobre USB de 12 Mbit/s. Teensy 3.0 de PJRC gestionó 683.689 con las mismas limitaciones. Paul Stoffregen parece tener mucho talento, por lo que lo usaría como un rendimiento muy bueno para lectura . También está lo suficientemente cerca de mi recuerdo del punto de referencia FTDI (pensé en 670k) que lo reduciría en un 20%, digamos 500kB/s, y lo usaría como mi máximo.

s/mostly/alwaysEl printf()solo es más largo que los ciclos de reloj disponibles que quedan.
Sí, quise escribir eso, entonces, ¿cuál es la mejor tarifa? ¿puedo enviar al menos cada 10 µs? o tal vez en formato binario, ¿será más rápido? o Uart_transmit_IT?
La tasa de baudios es 115200. En cuanto a lo que sugirió, ¿cómo puedo lograrlo? Es un bif confuso para mí. en realidad, la longitud de mi valor podría ser como 1000000000 µs porque actuará como un reloj para la marca de tiempo.
Sí, entonces una marca de tiempo cada 277 μs, ¿cómo lograrlo para cada 10 μs? ¿Puedes mostrarme el código real? es mas facil de entender para mi :)
No es factible enviar una marca de tiempo, de ninguna forma, más rápido que el baudrate/8. Entonces, si la tasa de baudios es 115200, lo mejor posible es 14,400 bytes/segundo. Necesitará una velocidad en baudios mucho más alta para enviar una marca de tiempo cada 10us.
@gbulmer aquí está el nuevo tema: electronics.stackexchange.com/questions/230431/…

Esta es, en mi humilde opinión, una aplicación poco realista.

En primer lugar; printf es lento. Como muy lento. Su CPU funciona a 84 MHz y desea interrumpirla a 1 MHz. Eso ya está cerca de la tasa de interrupción máxima solo por la sobrecarga de cambio de contexto.

Además, si pudiera dedicar los 84 ciclos de la CPU a la impresión, simplemente printf() es demasiado lento. Si el valor del contador sube a 8, 9 o 10 dígitos, la CPU solo tiene ~10 ciclos para procesar 1 carácter. No creo que sea tan eficiente, o mágico.

Eso sugiere un formato binario. En ese caso, puede imprimir los bytes de datos sin procesar. Pero incluso entonces, la tasa de salida de 1 MHz es muy alta. Si solo imprimiera datos sin procesar de 4 bytes (sin encabezado, sin sincronización, sin CRC, ¡nada extra!) a 1 MHz, tiene una velocidad de datos de 4 MByte/s o 32 Mbit/s. El UART solo puede subir hasta 10,5 Mbit/s, SPI hasta 42 Mbit/s.

En un intervalo de 10 us, el UART debería ser capaz de alcanzar esa velocidad; pero requerirá 3,2 Mbit/s o una tasa de baudios bastante superior (como 4 o 6 Mbaud). ¿Qué receptor vas a conectar (y procesar) a eso? Por ejemplo, la mayoría de los cables seriales USB alcanzarán un máximo de 1-3 Mbaudios (teniendo en cuenta que los convertidores seriales no pueden operar con ninguna tasa de baudios arbitraria a este nivel).

Además, es posible que desee considerar que la mayoría de las funciones de Uart tx están bloqueadas, por lo que la CPU pasará casi todos sus ciclos esperando que el hardware se complete o cargue bytes en el periférico. Eso significa que es posible que desee ver DMA y descubrir alguna forma de empaquetar los datos correctamente en caso de que el extremo receptor pierda la sincronización.

Quería intentar usar Hal_Uart_Transmit pero no me funciona y el ejemplo oficial de ST lo está redirigiendo a Printf. mi tasa de baudios es 115200, en cuanto a valores binarios, ¿cómo lograrlo?

No ha contradicho a nadie que sugiriera que está utilizando una interfaz serie USB y, de hecho, eso es lo que está presente en la placa Nulceo que menciona. Un convertidor serie USB típico va a tener una fluctuación de muestreo del orden de un milisegundo cuando informa los datos recibidos al host, por lo que, completamente independiente de la velocidad de transmisión en serie insuficiente, todo este enfoque no puede producir los resultados que desea.

Si necesita un tiempo más ajustado que eso, probablemente necesitará una luz estroboscópica de hardware con una interfaz más directa a la CPU host (no es fácil en una PC moderna, más razonable en algunas placas integradas basadas en ARM), y luego usar los mensajes seriales anteriores o posteriores para indicar la marca de tiempo que está siendo estroboscópica. Probablemente será más fácil si hace que el intervalo de luz estroboscópica sea mucho menos frecuente para que tenga tiempo de informar el conteo en el medio y ejecutar un reloj local a través de los espacios. Pero incluso haciendo eso no obtendrá una precisión superior a milisegundos hasta que deje de depender del USB de velocidad completa.