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:
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.
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.
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.
Fácil: utilice un microcontrolador PSoC5 .
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.
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 ).
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.
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.
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.
Kortuk
jay keegan
jay keegan
marcajes
jay keegan
Kortuk