Comment décoder efficacement un signal série non standard

11

Je suis un membre de premier cycle d'une équipe de recherche travaillant sur un projet impliquant un ASIC transmettant des RF et son récepteur sans fil qui devrait finalement envoyer des données à un PC.

Le récepteur émet un signal série rapide , continu, asynchrone et non standard (c'est-à-dire pas SPI, I2C, UART, etc.) donc mon travail consiste à écrire un logiciel de microcontrôleur pour interfacer le récepteur à l'ordinateur. Actuellement, mon approche consiste à utiliser des interruptions déclenchées par le front pour placer les données dans un tampon circulaire et effectuer tout le processus de décodage bit par bit dans la boucle principale. Le microcontrôleur doit sortir simultanément ces données via USB (port de communication virtuel) vers l'ordinateur.

Voici un problème que je rencontre et que j'anticipe:

  1. Je ne peux pas traiter les données tamponnées assez rapidement même avec mon processeur ARM Cortex M3 72 MHz assez puissant. Le débit est de 400 Kbps (2,5 us / bit). Pour référence cela ne laisse que 180 cycles par bit (y compris le décodage ET l'ISR, qui a ~ 30 cycles de ouch overhead!). Le MCU doit également gérer de nombreuses autres tâches qu'il interroge dans la boucle principale.

  2. Le pilote du port de communication virtuel USB est également basé sur les interruptions. Cela me rend presque certain que le pilote finira par avoir le processeur interrompu pendant si longtemps qu'il manque la fenêtre de 2,5 microsecondes (180 cycles) dans laquelle un bit peut être transmis. Je ne sais pas comment les conflits d'interruption / races comme celui-ci sont normalement résolus.

La question est donc simplement: que peut-on faire pour résoudre ces problèmes ou n'est-ce pas la bonne approche? Je suis également prêt à envisager des approches moins centrées sur les logiciels. Par exemple, utiliser une puce USB dédiée avec une sorte de machine d'état matérielle pour le décodage, mais ce n'est pas un territoire inconnu.

Keegan Jay
la source
Je dois dire qu'il est rare que je constate que de nombreuses suggestions auxquelles j'aime avoir répondu aussi rapidement répondent bien à votre question. Je serais intéressé d'en savoir plus sur les données Burts. Sont-ils brusques, soudainement à pleine vitesse, puis des périodes de données faibles ou est-il plausible que vous alliez une période prolongée avec des données continues?
Kortuk
Tant que l'ASIC est alimenté, il envoie un flux continu de données. Pas du tout éclaté. Il s'agit d'une application de détection médicale en temps réel avec lecture sur ordinateur. Avez-vous déjà vu un électrocardiogramme?
Keegan Jay
Tant de bonnes réponses ici. J'ai vu une nette fracture entre les solutions impliquant des modifications des interruptions et les solutions impliquant une logique matérielle / numérique dédiée. Des choses comme les FPGA et Verilog que je connais, mais qui ne sont pas encore expérimentées, cela signifie donc qu'elles doivent être enregistrées à long terme. À court terme, @rocketmagnets, une méthode moins lourde d'interruption est très bien. J'aime l'élégance de consacrer des tâches subalternes à la logique numérique et de sauvegarder l'ARM pour un vrai calcul. À l'avenir, la puissance de l'ARM sera utilisée pour l'analyse et le filtrage des données série sans fil.
Keegan Jay
Le signal est-il synchrone ou asynchrone?
markrages
Asynchrone. 4 bits de départ, 10 bits de données, 2 bits d'arrêt. En raison de la nature de l'ASIC qui transmet, les temps HI et LO varient considérablement d'une puce à l'autre. J'ai déjà écrit un algorithme pour déduire le débit en bauds.
Keegan Jay

Réponses:

5

Autre réponse: arrêtez d'utiliser les interruptions.

Les gens sautent trop facilement pour utiliser les interruptions. Personnellement, je les utilise rarement car ils perdent beaucoup de temps, comme vous le découvrez.

Il est souvent possible d'écrire une boucle principale qui interroge tout si rapidement que sa latence est conforme aux spécifications, et très peu de temps est perdu.

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
    }        
}

Il peut y avoir certaines choses dans la boucle qui se produisent beaucoup plus souvent que d'autres. Peut-être que les bits entrants par exemple, dans ce cas, ajoutent plus de ces tests, de sorte qu'une plus grande partie du processeur est dédiée à cette tâche.

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
    }        
}

Il peut y avoir certains événements pour lesquels la latence de cette approche est trop élevée. Par exemple, vous pourriez avoir besoin d'un événement chronométré de façon très précise. Dans ce cas, ayez cet événement sur interruption et ayez tout le reste dans la boucle.

Rocketmagnet
la source
J'aime votre réponse plus que toute autre réponse Rocketmagnet. Au lieu de plus de hadrware, de matériel plus rapide, plus d'autre chose, vous Rocketmagnet, proposez: faites moins, mieux, plus simple.
D'accord, j'ai vu de nombreux cas où les interruptions rendaient la solution bien meilleure. Ils font de grandes choses, permettent un code bien structuré, une faible latence et de nombreux autres avantages, mais je suis d'accord avec vous ici. Il semble que le processus soit si intense 1 que le contrôleur devra peut-être consacrer chaque instant de son attention à la gestion du flux série. La partie frontale numérique me semble idéale, mais plusieurs fois vous avez des micros et pas de FPGA autour quand c'est un projet scolaire, je consacrerais probablement un micro à la manipulation pour moi d'abord et j'essaierais de rentrer dans un FPGA plus tard pour le remplacer pour Coût.
Kortuk
C'est probablement la solution avec laquelle j'irai à court terme. J'espérais éviter cela car cela implique de réécrire une bonne partie des pilotes série existants, mais c'est une solution élégante qui est à ma portée sur une courte période de temps.
Keegan Jay
1
@JayKeegan - Oui, c'est probablement le chemin le plus rapide vers une solution. PSoC et FPGA pourraient être l'approche pour le prochain projet.
Rocketmagnet
6

Vous pouvez éventuellement utiliser un FPGA au lieu d'un microcontrôleur pour décoder et mettre en mémoire tampon le flux de données sans fil. Utilisez ensuite le processeur ARM pour vider les tampons FPGA (par exemple en utilisant une interface SPI) et expédiez le contenu via le port USB Comm. C'est du travail, mais un FPGA devrait pouvoir suivre facilement tant que vous êtes en mesure de le réparer assez souvent pour garantir que ses tampons matériels ne dépassent pas (ou si vous pouvez traiter les données perdues à un niveau supérieur du protocole). ).

vicatcu
la source
Cela pourrait être une excellente solution à long terme. J'espérais avoir reçu beaucoup de solutions logiques / matérielles numériques en plus des solutions logicielles car maintenant j'ai une excuse pour en savoir plus sur ces choses! Je n'ai malheureusement pas encore d'expérience avec les FPGA.
Keegan Jay
6

Facile: utilisez un microcontrôleur PSoC5 .

PSoC

Vous avez toute la facilité d'utilisation d'un microcontrôleur, en plus il contient un CPLD, vous pouvez donc écrire vos propres périphériques matériels dans Verilog. Écrivez simplement votre décodeur de données série dans verilog et utilisez DMA pour le diffuser sur le port USB.

Pendant ce temps, le puissant cœur ARM 32 bits peut déformer ses instructions Thumb.

Rocketmagnet
la source
La page d'aperçu ne répertorie pas les fréquences d'horloge, ce qui a soulevé mes soupçons. La fiche technique indique 40 MHz (j'ai également noté 6 mA à 6 MHz). C'est la moitié de ce que OP a maintenant. "Le MCU doit également gérer beaucoup d'autres tâches", cela peut donc dépendre de ce que ce soit, que ce soit une bonne idée ou non.
stevenvh
Ils vont jusqu'à 67 MHz. Il est donc presque aussi rapide que le processeur actuel de l'OP, sauf que la plupart du travail sera effectué sur le matériel, laissant au CPU beaucoup plus de temps libre.
Rocketmagnet
1
Je n'ai pas regardé toutes les fiches techniques. Le premier que j'ai choisi dit 40 MHz.
stevenvh
@stevenvh - Ils ont des degrés de vitesse différents. Le troisième chiffre du PN est la classe de vitesse. (4 = 48 MHz, 6 = 67 MHz).
Rocketmagnet
1
C'est également une solution fantastique à long terme, un peu comme l'idée FPGA. Je n'ai jamais entendu parler de ce type de puce, mais il apporte une grande partie des fonctionnalités du reste de ma carte en une seule puce. À l'avenir, cela pourrait signifier que le récepteur entier s'adapte à quelque chose de la taille d'une clé USB, ce qui est la vision de mon chef de projet. J'apprendrai Verilog au prochain semestre.
Keegan Jay
4

Je pense que vous avez un choix d'ingénierie classique à faire: rapide, bon marché, fonctionne: choisissez-en deux.

La solution de @ vicatcu est certainement une bonne solution, mais si vous ne pouvez pas ou ne voulez pas y ajouter plus de matériel (et cela inclut un processeur plus rapide), alors vous devez faire un choix. Si cette liaison série est la plus importante, vous devez vous asseoir dans l'ISR jusqu'à ce que tous les bits aient été collectés. 180 instructions par bit n'est pas mal du tout, mais n'essayez pas de tout faire. Lorsque vous détectez le début d'un transfert, tournez jusqu'à ce que le transfert soit terminé. Remplissez le résultat dans un FIFO, puis reprenez le traitement normal.

Vous ne dites pas combien de temps chaque transmission est mais si elles sont courtes et éclatantes, ce serait une solution viable. Je suis prêt à parier que votre implémentation de port COM virtuel a également un tampon matériel, donc un service d'interruption "retardé" ne devrait pas poser trop de problèmes. En ce qui concerne le reste de ce que le MCU doit faire ... vous avez des décisions de conception à prendre.

akohlsmith
la source
Cette solution complète en quelque sorte l'approche logicielle de rocketman en réduisant le nombre de pilotes basés sur les interruptions. Je peux garder le pilote série principal que j'ai mentionné comme basé sur les interruptions. J'essaierai également de tourner jusqu'à ce que tout le cadre soit lu, comme vous le mentionnez.
Keegan Jay
3

Tout d'abord, j'aime déjà certaines réponses ici, et certaines ont obtenu mon vote.

Mais juste pour ajouter une autre solution possible: étant donné les contraintes de votre projet, l'ajout d'un deuxième microcontrôleur serait-il mauvais (cela impliquerait-il une autre exécution de la carte)? Peut-être un simple microcontrôleur 8 bits qui se connecte à votre Cortex-M3 via un périphérique rapide comme SPI. Le contrôleur 8 bits de votre choix interrogerait les bits et formerait les octets comme dans la réponse sélectionnée, mais lorsqu'il a un octet, il pourrait le vider dans le registre de données SPI pour le transfert.

Le côté cortex-M3 interromprait simplement les données SPI reçues. Cela réduit votre précédente interruption déclenchée par les bords externes à 400 KHz à 50 KHz.

Les deux raisons pour lesquelles je suggère cela sont parce que certaines des autres méthodes (PSoC ou FPGA ajouté) sont un peu chères (bien que cela n'ait probablement pas d'importance pour un projet académique à faible volume) et parce qu'elles peuvent vous permettre de préserver certaines la structure de votre code actuel.

En dehors de cela, je pense que l'idée PSoC est géniale avec votre propre périphérique personnalisé transférant via DMA vers USB.

Jon L
la source
C'est en fait le plan que j'avais en tête en publiant ceci. Si je ne peux pas rationaliser le logiciel en réduisant la dépendance aux interruptions (réponse sélectionnée), c'est sûrement ce que je ferai. Mais oui, cela nécessitera une autre course de planche, probablement deux car je crains de bien faire mes designs la première fois.
Keegan Jay
@JayKeegan, haha ​​bienvenue au club!
Jon L
2

Si votre format de données est similaire à celui d'un UART, mais à un débit en bauds imprévisible mais cohérent, mon inclinaison serait d'utiliser un CPLD pour convertir chaque mot de données entrantes en format SPI ou asynchrone standard. Je ne pense pas qu'il soit nécessaire de pousser jusqu'au bout dans le domaine des CPLD. En fait, même une logique discrète pourrait presque fonctionner. Si vous pouviez générer une horloge qui était un smidgin plus de 5 fois votre débit de données souhaité, vous pourriez utiliser un compteur diviser par cinq et diviser par 16 avec quelques portes. Disposez le compteur de division par cinq de sorte qu'il soit maintenu à zéro chaque fois que l'entrée est inactive et que le compteur de division par 16 est à zéro. Sinon, générez une impulsion d'horloge SPI et heurtez le compteur de division par 16 chaque fois que le compteur de division par cinq atteint 2.

Compte tenu de l'horloge 5x, on pourrait générer l'horloge SPI en utilisant un 16V8 (le dispositif logique programmable le plus petit et le moins cher actuellement disponible). Un deuxième 16V8 ou 22V10 pourrait être utilisé comme diviseur de taux fractionnaire pour générer l'horloge 5x, ou on pourrait utiliser une puce légèrement plus grande (CPLD) et tout faire en un.

Modifier / Addendum

Après un examen plus approfondi, si l'on veut utiliser un CPLD, on peut facilement ajouter quelques améliorations supplémentaires au circuit. Par exemple, on peut assez facilement ajouter une logique pour que le circuit se bloque jusqu'à ce qu'il reçoive au moins 1,5 bit de temps d'arrêt, suivi de 3,5 bits de bit de démarrage; s'il reçoit un bit de démarrage trop court, il devrait recommencer à rechercher le bit d'arrêt. De plus, si l'on utilise SPI, on pourrait utiliser le signal / CS pour s'assurer que le périphérique récepteur verra les données correctement cadrées. Si l'appareil recevant les données SPI peut gérer des trames 10 bits, on pourrait envoyer ces trames directement. Sinon, chaque trame de dix bits pourrait être envoyée en tant que trame de 8 bits avec l'ensemble LSB (7 bits de données), et une trame avec tous les LSB effacés (3 bits de données); l'horloge SPI serait accélérée pendant les bits d'arrêt afin que toutes les données soient envoyées.

Certains microcontrôleurs ont des modules de génération PWM assez polyvalents qui incluent des choses comme la possibilité d'être maintenus en réinitialisation par un signal externe, et de synchroniser leur synchronisation avec la libération d'un tel signal. Si votre microcontrôleur peut le faire, selon ses caractéristiques exactes, cela pourrait considérablement simplifier le circuit CPLD ou de génération de synchronisation.

Une autre approche que Rocketmagnet a quelque peu abordée, serait d'avoir un petit micro dont le seul but est de décoder les données série et de les convertir dans un format utilisable par le micro principal. Votre débit de données de 400 kHz est assez rapide pour le décodage logiciel, mais quelque chose comme un PIC pourrait le gérer s'il n'avait rien d'autre à faire en même temps. Selon les appareils que vous connaissez, cela pourrait être plus facile ou plus difficile que d'utiliser un CPLD.

supercat
la source
Tout cela sera très précieux lors de la conception de la logique numérique pour le décodage. Je vais en effet sortir en SPI. Pour l'instant, je fais juste le décodage à l'aide d'un MCU autonome (contraintes de temps). Je vous remercie!
Keegan Jay