Cómo decodificar eficientemente una señal serial no estándar

Soy un miembro de pregrado de un equipo de investigación que trabaja en un proyecto que involucra un ASIC transmisor de RF y su receptor inalámbrico que finalmente debería enviar datos a una PC.

El receptor emite una señal serie rápida , continua, asíncrona y no estándar (es decir, no SPI, I2C, UART, etc.), por lo que mi trabajo es escribir el software del microcontrolador para conectar el receptor a la computadora. Actualmente, mi enfoque es usar interrupciones activadas por borde para colocar los datos en un búfer circular y hacer todo el proceso de decodificación bit a bit en el ciclo principal. El microcontrolador debe enviar simultáneamente estos datos mediante USB (puerto de comunicación virtual) a la computadora.

Aquí hay un problema que tengo, y uno que estoy anticipando:

  1. No puedo procesar los datos almacenados en búfer lo suficientemente rápido, incluso con mi potente procesador ARM Cortex M3 de 72 MHz. El bitrate es de 400 Kbps (2,5 us/bit). Como referencia, eso deja solo 180 ciclos por bit (incluida la decodificación Y el ISR, que tiene ~ 30 ciclos de sobrecarga, ¡ay!). La MCU también tiene que manejar muchas otras tareas que sondea en el ciclo principal.

  2. El controlador del puerto USB virtual com también se basa en interrupciones. Esto me da casi la certeza de que el controlador finalmente interrumpirá el procesador durante tanto tiempo que perderá la ventana de 2,5 microsegundos (180 ciclos) en la que se puede transmitir un bit. No estoy seguro de cómo se resuelven normalmente los conflictos/carreras de interrupción como esta.

Entonces, la pregunta es simplemente, ¿qué se podría hacer para resolver estos problemas o no es este el enfoque correcto? También estoy dispuesto a considerar enfoques menos centrados en el software. Por ejemplo, usar un chip USB dedicado con algún tipo de máquina de estado de hardware para la decodificación, pero este es un territorio desconocido.

Debo decir que es raro que vea que muchas sugerencias que me gustan respondieron rápidamente, responde bien a su pregunta. Me interesaría saber más sobre las ráfagas de datos. ¿Son ráfagas, repentinamente a toda velocidad y luego períodos de datos bajos o es plausible que pase un período prolongado con datos continuos?
Siempre que el ASIC tenga energía, envía un flujo continuo de datos. No estalló en absoluto. Es una aplicación de detección médica en tiempo real con una lectura de computadora. ¿Alguna vez has visto un electrocardiograma?
Tantas respuestas geniales aquí. Vi una clara división entre las soluciones que involucran cambios en las interrupciones y las soluciones que involucran lógica digital/hardware dedicado. Estoy familiarizado con cosas como FPGA y Verilog, pero aún no tengo experiencia, por lo que esto significa que deben guardarse a largo plazo. En el método @rocketmagnets a corto plazo, menos interrumpido, está bien. Me gusta la elegancia de dedicar tareas menores a la lógica digital y guardar el ARM para la verdadera computación. En el futuro, la potencia del ARM se utilizará para el análisis y filtrado de los datos seriales inalámbricos.
¿La señal es síncrona o asíncrona?
Asincrónico. 4 bits de inicio, 10 bits de datos, 2 bits de parada. Debido a la naturaleza del ASIC que transmite, los tiempos HI y LO varían mucho de un chip a otro. Ya he escrito un algoritmo para deducir la tasa de baudios.
@JayKeegan, Ohh, médico. Hay chips completos por ahí dedicados a poder funcionar a velocidades ridículas para mantenerse al día con los médicos. Se supone que no debe usar compresión en las imágenes, no confían en la compresión sin pérdidas. Cuando tomamos rayos X, las pequeñas imágenes que obtenemos son de varios megabytes cada una y el CT que tenemos que comunicar es estúpidamente grande. Creo que encontrará que una interfaz digital funciona realmente bien y elegir un HDL no es difícil, pero si fuera mi proyecto, lo haría funcionar y volvería a esa mejora más tarde, buena decisión.

Respuestas (6)

Otra respuesta: deja de usar interrupciones.

La gente salta a usar interrupciones con demasiada facilidad. Personalmente, rara vez los uso porque en realidad pierden mucho tiempo, como está descubriendo.

A menudo es posible escribir un bucle principal que sondee todo tan rápidamente que su latencia esté dentro de las especificaciones y se desperdicie muy poco tiempo.

loop
{
    if (serial_bit_ready)
    {
        // shift serial bit into a byte
    }

    if (serial_byte_ready)
    {
        // decode serial data
    }

    if (enough_serial_bytes_available)
    {
        // more decoding
    }        

    if (usb_queue_not_empty)
    {
        // handle USB data
    }        
}

Puede haber algunas cosas en el bucle que sucedan con mucha más frecuencia que otras. Tal vez los bits entrantes, por ejemplo, en cuyo caso, agregue más de esas pruebas, para que una mayor parte del procesador se dedique a esa tarea.

loop
{
    if (serial_bit_ready)
    {
        // shift serial bit into a byte
    }

    if (serial_byte_ready)
    {
        // decode serial data
    }

    if (serial_bit_ready)
    {
        // shift serial bit into a byte
    }

    if (enough_serial_bytes_available)
    {
        // more decoding
    }        

    if (serial_bit_ready)
    {
        // shift serial bit into a byte
    }

    if (usb_queue_not_empty)
    {
        // handle USB data
    }        
}

Puede haber algunos eventos para los que la latencia de este enfoque sea demasiado alta. Por ejemplo, es posible que necesite un evento cronometrado con mucha precisión. En cuyo caso, tenga ese evento en interrupción y tenga todo lo demás en el ciclo.

Me gusta tu respuesta más que la respuesta de otra persona de Rocketmagnet. En lugar de más hardware, hardware más rápido, más de otra cosa, usted Rocketmagnet, sugiera: haga menos, mejor, más simple.
De acuerdo, he visto muchos casos en los que las interrupciones hacen que una solución sea mucho mejor. Hacen grandes cosas, permiten código bien estructurado, baja latencia y muchas otras ventajas, pero tengo que estar de acuerdo contigo aquí. Parece que el proceso es tan intenso que es posible que el controlador deba dedicar toda su atención al manejo de la transmisión en serie. El front-end digital suena ideal para mí, pero muchas veces tienes algunos micros y no hay FPGA cuando se trata de un proyecto escolar, probablemente dedicaría un micro para manejarlo primero y trataría de encajar en un FPGA más tarde para reemplazarlo por costo.
Esta es probablemente la solución que elegiré a corto plazo. Tenía la esperanza de evitar esto porque implica volver a escribir un poco de los controladores seriales existentes, pero es una solución elegante que está dentro de mis capacidades en un corto período de tiempo.
@JayKeegan: sí, probablemente sea la ruta más rápida hacia una solución. PSoC y FPGA podrían ser el enfoque para el próximo proyecto.

Fácil: utilice un microcontrolador PSoC5 .

PSoC

Tiene toda la facilidad de uso de un microcontrolador, además de que contiene un CPLD, por lo que puede escribir sus propios periféricos de hardware en Verilog. Simplemente escriba su decodificador de datos en serie en verilog y use DMA para transmitirlo al puerto USB.

Mientras tanto, el poderoso núcleo ARM de 32 bits puede estar jugando con sus instrucciones Thumb.

La página de descripción general no enumera las frecuencias del reloj, lo que despertó mis sospechas. La hoja de datos dice 40MHz (también noté 6mA a 6MHz). Eso es la mitad de lo que OP tiene ahora. "La MCU también tiene que manejar muchas otras tareas", por lo que puede depender de cuáles sean si es una buena idea o no.
Van hasta 67MHz. Por lo tanto, es casi tan rápido como el procesador actual del OP, excepto que la mayor parte del trabajo se realizará en el hardware, dejando a la CPU con mucho más tiempo libre.
No miré todas las hojas de datos. El primero que elegí dijo 40MHz.
@stevenvh: tienen diferentes grados de velocidad. El tercer número en el PN es el grado de velocidad. (4=48 MHz, 6=67 MHz).
No es bueno. demasiado complejo Mira lo que otros Rocketmagnet respondieron a continuación. los estoy viendo a los dos
@RocketSurgeon: ambos soy yo. No sé si alguna vez has usado un PSoC5, pero son sorprendentemente fáciles de usar. Por supuesto, hacer su propio periférico es más difícil, pero solo un poco más que implementar un periférico en el software.
Una vez encontré TI SRM (Manual de referencia de software) para AM35xx SoC. Y son >5000 páginas. No es divertido.
Me gusta esta alternativa también, grandes ideas. Probablemente este no sea mi favorito, preferiría instalar un FPGA frontal digital a bajo costo y poner el micro existente detrás, pero esto tiene muchas ventajas, especialmente para un proyecto escolar donde esto les permitirá lidiar con las necesidades cambiantes del proyecto rápidamente. .
Esta también es una solución fantástica a largo plazo, muy parecida a la idea de FPGA. Nunca había oído hablar de este tipo de chip, pero reúne gran parte de la funcionalidad del resto de mi placa en un solo chip. En el futuro, esto podría significar que todo el receptor cabe en algo del tamaño de una memoria USB, que es la visión de mi líder de proyecto. Aprenderé Verilog el próximo semestre.
@JayKeegan: también puedo decir que es un placer usarlos, y es muy simple configurar los periféricos. Una vez que se acostumbre al hecho de que puede tener todo tipo de combinaciones inusuales de periféricos que ninguna otra MCU ofrece, nunca mirará hacia atrás. Echa un vistazo a PSoC controlando 4 motores sin escobillas completamente sin la CPU .
Nunca los he usado, pero parecen muy buenos. Sin embargo, buscándolos en Digikey, tienen un costo alto, por lo que debe hacer los cálculos para justificar su uso (es decir, si uno de estos reemplaza otros cinco chips, entonces probablemente valga la pena)
@vicatcu - Sí, eso es cierto. Aunque creo que es porque no hacen ninguno de gama baja. Probablemente se comparen favorablemente con otros microcontroladores con niveles similares de funcionalidad.
@jayKeegan: no tiene que aprender verilog, puede usar el diseño esquemático.
He realizado un diseño extenso con PSoC4 y PSoC5LP; los UDB son apenas útiles. Son seductores exactamente para este tipo de aplicación, pero la entrada de esquemas es tediosa y su generación de código es deficiente. Sin embargo, es posible que puedas encontrar algo que funcione; algo que se encarga del cambio y luego DMA a la memoria donde puede procesar cosas en un contexto menos sensible al tiempo.

Posiblemente podría usar un FPGA en lugar de un microcontrolador para decodificar y almacenar en búfer el flujo de datos inalámbrico. Luego use el procesador ARM para vaciar los búferes de FPGA (por ejemplo, usando una interfaz SPI) y envíe el contenido por el puerto USB Comm. Funciona, pero un FPGA debería poder mantenerse al día fácilmente siempre que pueda repararlo con la frecuencia suficiente para garantizar que sus búferes de hardware no se desborden (o si puede manejar datos perdidos en un nivel más alto del protocolo ).

Esta podría ser una excelente solución a largo plazo. ¡Esperaba recibir muchas soluciones de hardware/lógica digital además de soluciones de software porque ahora tengo una excusa para aprender sobre estas cosas! Desafortunadamente, todavía no tengo experiencia con FPGA.

Creo que tienes que tomar una decisión clásica de ingeniería: rápido, barato, funciona: elige dos.

La solución de @vicatcu es ciertamente buena, pero si no puede o no quiere agregarle más hardware (y esto incluye un procesador más rápido), entonces debe elegir. Si este enlace serial es el más importante, debe sentarse en el ISR hasta que se hayan recopilado todos los bits. 180 instrucciones por bit en realidad no está nada mal, pero no intentes hacerlo todo. Cuando detecte el inicio de una transferencia, gire hasta que se complete la transferencia. Rellene el resultado en un FIFO y luego reanude el procesamiento normal.

No dice cuánto dura cada transmisión, pero si son cortas y con ráfagas, esta sería una solución viable. Estoy dispuesto a apostar que la implementación de su puerto COM virtual también tiene algo de almacenamiento en búfer de hardware, por lo que un servicio de interrupción "retrasado" no debería presentar demasiados problemas. En cuanto al resto de lo que debe hacer el MCU... tiene que tomar algunas decisiones de diseño.

Esta solución complementa el enfoque de software de Rocketman al reducir la cantidad de controladores basados ​​en interrupciones. Puedo mantener el controlador serie principal que mencioné como basado en interrupciones. También intentaré girar hasta que se lea todo el cuadro como mencionas.

En primer lugar, ya me gustan algunas de las respuestas aquí, y algunas han obtenido mi voto positivo.

Pero solo para agregar otra posible solución: dadas las limitaciones de su proyecto, ¿sería malo agregar un segundo microcontrolador (eso implicaría otra ejecución de la placa)? Tal vez un simple microcontrolador de 8 bits que se conecte a su Cortex-M3 a través de un periférico rápido como SPI. El controlador de 8 bits de su elección buscará bits y formará bytes como en la respuesta seleccionada, pero cuando tenga un byte, podría descargarlo en el registro de datos SPI para transferirlo.

El lado de la corteza-M3 simplemente interrumpiría los datos SPI recibidos. Eso reduce su anterior interrupción externa activada por borde de 400 KHz a 50 KHz.

Las dos razones por las que sugiero esto es porque algunos de los otros métodos (PSoC o FPGA agregado) son un poco costosos (aunque esto probablemente no importe para un proyecto académico de bajo volumen) y porque puede permitirle preservar algunos de la estructura de su código actual.

Aparte de eso, creo que la idea de PSoC es increíble con su propio periférico personalizado transfiriendo DMA a USB.

Este es en realidad el plan que tenía en mente al momento de publicar esto. Si no puedo optimizar el software reduciendo la dependencia de las interrupciones (respuesta seleccionada), seguramente esto es lo que haré. Pero sí, requerirá otra ejecución de tablero, probablemente dos porque apesto para hacer mis diseños bien la primera vez.
@JayKeegan, jaja ¡bienvenido al club!

Si su formato de datos es similar al de un UART, pero a una tasa de baudios impredecible pero consistente, mi inclinación sería usar un CPLD para convertir cada palabra de los datos entrantes en formato SPI o asíncrono estándar. No creo que haya ninguna necesidad de avanzar hasta el final en el ámbito de los CPLD. En realidad, incluso la lógica discreta podría ser casi viable. Si pudiera generar un reloj que fuera un poco más de 5 veces su velocidad de datos deseada, podría usar un contador de dividir por cinco y dividir por 16 con algunas puertas. Organice el contador de división por cinco de modo que se mantenga en reinicio siempre que la entrada esté inactiva y el contador de división por 16 esté en cero. De lo contrario, genere un pulso de reloj SPI y golpee el contador de dividir por 16 cada vez que el contador de dividir por cinco llegue a 2.

Dado el reloj 5x, uno podría generar el reloj SPI usando un 16V8 (el dispositivo lógico programable más pequeño y más barato disponible actualmente). Se podría usar un segundo 16V8 o 22V10 como divisor de frecuencia fraccionaria para generar el reloj 5x, o se podría usar un chip un poco más grande (CPLD) y hacer todo en uno.

Editar/Anexo

Luego de una consideración adicional, si uno va a usar un CPLD, uno puede agregar fácilmente algunas mejoras adicionales al circuito. Por ejemplo, se puede agregar lógica con bastante facilidad para que el circuito se bloquee hasta que reciba al menos 1,5 veces el bit de parada, seguido de 3,5 veces el bit de inicio; si recibe un bit de inicio demasiado corto, debe volver a buscar el bit de parada. Además, si uno está usando SPI, podría usar la señal /CS para asegurarse de que el dispositivo receptor vea los datos enmarcados correctamente. Si el dispositivo que recibe los datos SPI puede manejar tramas de 10 bits, se podrían enviar dichas tramas directamente. De lo contrario, cada cuadro de diez bits podría enviarse como un cuadro de 8 bits con el LSB activado (7 bits de datos) y un cuadro con todos los LSB despejados (3 bits de datos); el reloj SPI se aceleraría durante los bits de parada para que se enviaran todos los datos.

Algunos microcontroladores tienen módulos de generación de PWM bastante versátiles que incluyen cosas como la capacidad de ser reiniciados por una señal externa y sincronizar su sincronización con la liberación de dicha señal. Si su microcontrolador puede hacer eso, dependiendo de sus características exactas, eso podría simplificar considerablemente el CPLD o el circuito de generación de tiempo.

Otro enfoque que Rocketmagnet mencionó un poco sería tener un micro pequeño cuyo único propósito es decodificar los datos en serie y convertirlos a un formato utilizable por el micro principal. Su velocidad de datos de 400 KHz es bastante rápida para la decodificación de software, pero algo como un PIC podría manejarlo si no tuviera que hacer nada más al mismo tiempo. Dependiendo de los dispositivos con los que esté familiarizado, esto podría ser más fácil o más difícil que usar un CPLD.

Todo esto será muy valioso al diseñar la lógica digital para la decodificación. De hecho, emitiré como SPI. Por ahora, solo estoy haciendo la decodificación usando una MCU independiente (restricciones de tiempo). ¡Gracias!