Skyduino:~#
Articles
Corrigé, projet

[DIY] Un port série … audio

Bonjour tout le monde !

Fut un temps je cherchais un moyen de programmer un ATmega de la façon la plus tordu possible (oui j’ai que ça à foutre).
Dans le genre bien tordu j’avais trouvé ce programmateur d’AVR à base de clavier usb :
http://hackaday.com/2012/11/26/usb-keyboard-becomes-an-avr-programmer/

J’ai eu beau chercher je n’ai pas pu trouver d’idée encore plus tordue …
Du coup je suis parti sur une autre idée, émuler un port série au moyen d’une carte son !

Le montage :

audiogpio

Pour ce montage j’ai piqué le schéma ici :
http://robots-everywhere.com/re_wiki/index.php?title=Serial_on_Android_using_the_audio_port

En réalité c’est un bête montage avec un amplificateur opérationnel (en saturation), monté en inverseur.

Quand l’entrée dépasse le seuil fixé par le potentiomètre la sortie bascule.
Cela permet d’avoir un signal TTL 0v/5v "propre" à partir d’un signal audio de +/- 1v d’amplitude.

Comme une carte son possède (dans 99.9% des cas) une sortie stéréo il est possible d’avoir deux sorties.
Du coup on peut imaginer émuler autre chose qu’un port série, voire même faire des sorties TTL tout ce qui à de plus classique.

Remarque : dans mon montage physique (pas sur le schéma) j’ai inversé par erreur les entrées V+ et V-, du coup j’ai un montage non inverseur.
Ne vous faites pas avoir, il faut bien un montage inverseur, vous comprendrez par la suite pourquoi ;)

Le prototype :

P1060053

Sur la gauche les deux sorties et les alimentations +5v et GND.
Et juste à côté l’entrée audio (gauche, droite, masse).

P1060056

Pour l’amplificateur opérationnel j’utilise un LM393 qui a l’avantage de fonctionner sans problème en +5V, contrairement à un TL082 par exemple qui a besoin d’une alimentation symétrique.

P1060057

Oui je sais c’est dégueulasse …
(ne pas vous fier à mon câblage, rappel : j’ai inversé V+ et V-)

Découpage d’une trame série :

Pour pouvoir vous expliquer rapidement la composition d’une trame série j’avais besoin d’une trame.
L’image ci dessous provient donc de batsocks.co.uk et est disponible dans son contexte d’origine ici :
http://www.batsocks.co.uk/products/Other/SweeperMeter_Technical.htm

SerialFormat

Une trame série se décompose en plusieurs parties :
- un bit de start
- 8 (ou 7) bits de données
- 1 (ou 2) bit de stop

Le bit de start correspond à un niveau bas (0v), un bit de stop correspond à un niveau haut (5v).
Au repos le signal doit être à l’état haut (historiquement cela permettait de détecter une coupure sur la ligne).

Concernant la durée des temps haut / temps bas c’est très simple à calculer.
Imaginons que l’on souhaite une vitesse de communication de 9600 bauds par seconde.
9600 bauds/s = 9600 bits/s -> durée d’une période = 1 / fréquence = 1 / 9600 = 0.104 millisecondes

De même pour connaître la durée d’une trame complète il suffit de multiplier le tout par le nombre de bits par octet.
Ici avec 8 bits de données et 1 bits de stop :
1 bit de start + 8 bits de données + 1 bits de stop = 10 bits
10 bits = 10 x (1 / 9600) = 1.04 millisecondes

Un niveau haut de plus d’une fois la durée d’un octet s’appelle un "break", si un récepteur série reçoit un "break" il est obligé de se recaler sur le bit de start qui suit, cela permet de ne pas perdre la synchronisation avec le signal série.

Remarque : on peut aussi déduire la vitesse réelle de transmission en octets par seconde :
9600 bits/s / 10 bits = 960 octets par seconde

Le logiciel :

Comprendre le fonctionnement d’une communication série asynchrone c’est une chose, émuler un signal série en est une autre.
Et émuler un signal série avec une carte son d’ordinateur est encore une autre histoire.

Pour me simplifier la tache j’ai conçue un script python prenant un fichier (texte ou binaire) en entrée et générant un fichier wave en sortie.
Pour lire le fichier il suffit d’ouvrir un éditeur comme audacity par exemple ou n’importe quel lecteur de musique supportant la lecture des .wav.

Voici un exemple de fichier audio généré et ouvert sous audacity :

audacity_lorem

Note : le signal présenté ci dessus est volontairement non-inversé, il ne devrait pas être dans le même sens que sur le schéma vu plus haut.
L’amplificateur inversant le signal audio celui ci doit être lui inversé pour au final avoir un signal série avec des niveaux logiques corrects.

Vous vous demandez sûrement pourquoi inverser le signal ?
Réfléchissons une petite seconde.
Le signal série doit être à l’état haut au repos, la carte son elle ne sort aucun signal au repos.
En utilisant un montage inverseur le signal est de base au niveau haut et passe au niveau bas lorsque la carte son envoie un signal.
il faut donc inverser le signal audio pour que le montage inverse de nouveau le signal et que celui ci finisse par être dans "le bon sens".
L’avantage de cette méthode est que si l’on débranche l’entrée audio le récepteur série ne se retrouvera pas avec un signal corrompu mais avec un "break" constant.
Cela évite aussi d’avoir à générer un signal haut avec la carte son au repos.

Le code du script (avec des commentaires pour expliquer chaque sous-parties) :

# -- Imports --
# sys: for argv / argc
# wave: for wave file generation
# struct: for pack
import sys, wave, struct

# Vitesse de communication série
SERIAL_RATE = 2400

# Fréquence d'échantillonnage (min 4000 ~ max 48000)
SAMPLE_RATE = 48000

# Vérification des arguments CLI
if len(sys.argv) < 3:
    print "USAGE: data2wav.py <input file> <output file>"
    exit(1)

# Nom de fichier source / sortie
i_filename = sys.argv[1]
o_filename = sys.argv[2]

# Ajoute l'extension .wav au fichier de sortie si nécessaire
if o_filename[-4:] != '.wav':
    o_filename += '.wav'

# Ouvre le fichier source (en mode binaire)
fi = open(i_filename, 'rb')
if not fi:
    print "Cannot open input file: %s" % i_filename
    exit(1)

# Ouvre le fichier de sortie (en mode binaire)
fo = wave.open(o_filename, 'wb')
if not fo:
    print "Cannot open output file: %s" % o_filename
    exit(1)

# Configure l'entête du fichier Wave
fo.setnchannels(1) # Mono
fo.setsampwidth(1) # 8 bits
fo.setframerate(SAMPLE_RATE)

# Calcul le nombre d'échantillons audio par bit
# Plus il y a d'échantillons par bit plus la qualité du signal audio sera grande
# Une fréquence d'échantillonnage élevé donnera donc les meilleurs résultats
NB_SAMPLES_PER_BIT = SAMPLE_RATE / SERIAL_RATE

# Buffer audio
audio_output = []

# Ajoute un bit dans le buffer audio
def appendBit(bit):
    for i in range(0, NB_SAMPLES_PER_BIT):
        if bit: # Inversion du signal : 0 -> 1, 1 -> 0
            audio_output.append(struct.pack('B', 0))
        else:
            audio_output.append(struct.pack('B', 255))

# Ajoute un "break" dans le buffer audio
def serialBreak():
    for i in range(0, 20):
        appendBit(True)

# Début du processus de conversion
b = fi.read(1)
serialBreak()
serialBreak()
while b != '':

    # Converti la valeur d'un octet du fichier source en décimal
    b = ord(b)
    
    # Ajoute un bit de start au buffer audio
    appendBit(False)

    # Ajoute 8 bits de données au buffer audio
    for i in range(0, 8):
        appendBit(b & (1 << i))

    # Ajoute un bit de stop au buffer audio
    appendBit(True)

    # Lit l'octet suivant du fichier source
    b = fi.read(1)

# Écrit le contenu du buffer audio dans le fichier de sortie
fo.writeframes(''.join(audio_output))

# Fermeture du fichier source et du fichier de sortie
fi.close()
fo.close()

Test IRL :

Le montage finit :

P1060060

Sur la photo :
- une carte son usb
- un dongle usb/série
- un câble audio jack bricolé
- quelques fils
- et le montage

Il suffit ensuite de lancer la lecture du fichier wave :

terminal_lorem

Et voila, un joli "Lorem ipsum" transmit au moyen d’un "vrai faux" port série émulé par une carte son !
Peut être qu’un jour on programmera nos arduino au moyen d’un simple lecteur de musique ;)

About these ads

Discussion

6 réflexions sur “[DIY] Un port série … audio

  1. tu est vraiment un gros taré :O

    Publié par zilators | 11 février 2013, 22 h 27 min
  2. Voilà le genre de démarche intellectuelle que j’apprécie

    Publié par artouste | 11 février 2013, 23 h 44 min
  3. au début des années 70 on faisait bien de la musique avec des imprimantes à impact; il y a même eu des essais avec un récepteur à transistors approché de l’unité centrale pour capter le bruit fait par un programme dont on gérait des boucles de durée variable

    Publié par noui | 11 février 2013, 23 h 51 min
  4. Pas mal, pas mal.
    Et pourquoi ne pas rajouter des LED Infra rouge émettrices derrière les AOP pour pouvoir changer les chaines de sa TV en RC5 ! ;-)

    Publié par \Fab\ | 12 février 2013, 0 h 22 min
  5. très cool mon pot, très génial. tu n’as rien a proposé sur le hacking des souris optiques USB?

    Publié par segun | 16 février 2013, 7 h 17 min
  6. programer avec un lecteur de musique portable sa peut etre une bonne idée

    Publié par yui | 20 mars 2013, 23 h 36 min

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Archives

Wow. Dogecoin. Amaze.

Laissez un tip en Dogecoin

DMMNFk6WBVTpx2Wu1Z35GL61QXSd6r6WQx

Suivre

Recevez les nouvelles publications par mail.

Joignez-vous à 678 followers