Talkie-walkie avancé XBee avec Arduino, DAC et amplificateurs audio
Composants nécessaires
- 1 × Goldilocks Analogue (prototype) — ou recréer avec : MCP4822 DAC, amplificateur microphone, amplificateur casque, Arduino Uno
- 1 × Amplificateur audio MAX9744
- 1 × Amplificateur microphone MAX9814
- 1 × DAC MCP4921 (alternative au MCP4822)
- 1 × Arduino UNO
- 1 × Shield sans fil Arduino (XBee)
À propos du projet
Je construis un clone Arduino avancé basé sur l'AVR ATmega1284p avec des fonctionnalités spéciales : DAC 12 bits MCP4822, amplificateur casque, 2× mémoire SPI (SRAM, EEPROM), carte SD. Comme la plateforme Arduino n'a pas de DAC intégré, il y a peu d'applications publiées pour les signaux analogiques. Un talkie-walkie est un exemple d'utilisation conjointe du numérique et de l'analogique pour un projet simple mais très utile.
La fonctionnalité talkie-walkie ne représente que quelques lignes de code, mais repose sur : entrée analogique (échantillonnage), sortie analogique sur bus SPI vers le DAC MCP4822, routines de temporisation d'échantillonnage, et plateforme radio numérique XBee. Examinons chaque couche.
Radio XBee
J'utilise des radios XBee Pro S2B configurées en communication point-à-point. Il faut un coordinateur et un routeur. Les guides de configuration sont disponibles en ligne.
Les radios sont configurées pour attendre le temps inter-caractère maximal avant d'envoyer un paquet (paquets pleins de 84 octets), maximisant le débit radio. Débit brut 250 kbit/s, mais débit utile ~32 kbit/s. Cela limite le taux d'échantillonnage et donc la qualité vocale.
Avec des échantillons 8 bits, ~3 kHz génère autant de données que peut transmettre sans compression. La compression est laissée pour un autre projet.
Les XBee sont en mode AT (tube série transparent), le moyen le plus simple de connecter deux appareils par radio numérique. Cela a permis des tests filaires avant de valider la radio.
Le tampon circulaire de réception autorise jusqu'à 255 octets (paquet XBee = 84 octets). Les échantillons à transmettre sont envoyés sur Tx à chaque période d'échantillonnage, mis en tampon par le XBee jusqu'à envoi du paquet complet.
Taux d'échantillonnage
Budget bits : 8 bits × 3000 Hz = 24 kbit/s à transmettre. Cela fonctionne bien. 4 kHz est trop proche du maximum théorique et fonctionne mal.
Traitement d'échantillon
Une broche « ping » (trace jaune) montre le temps passé dans l'interruption d'échantillonnage : très court par rapport à la période totale. De la compression pourrait être implémentée.
Dans l'interruption : sortie audio via DAC, lecture ADC pour capture échantillon et transmission vers tampon USART. C'est la fonction audioCodec_dsp appelée depuis une interruption timer.
J'utilise le timer 8 bits Timer0 de l'AVR pour générer les intervalles réguliers. Avec une fréquence MCU multiple binaire des fréquences audio standards, on obtient des taux précis avec un prédiviseur 64. Pour fréquences impaires (44,1 kHz), le Timer1 16 bits donne la précision sans prédiviseur.
L'ADC de l'ATmega1284p est en mode free-run, cadencé à 192 kHz (proche du max pour échantillons 8 bits). L'interruption prend 14 µs sur 333 µs par période → beaucoup de temps pour autres traitements (UI, synthèse audio).
Transaction SPI
Au niveau le plus fin, on voit la transaction SPI vers le DAC MCP4822.
Sur le Goldilocks Analogue Prototype 2 (bus SPI standard), la transaction est normale. Les prototypes ultérieurs utilisent le mode SPI maître sur USART1 de l'ATmega1284p (double buffer, libère le bus SPI normal pour SD/SRAM). Pour le talkie-walkie pas d'enregistrement audio, donc pas d'inconvénient à utiliser l'ancien prototype et le bus SPI standard.
Conclusion
Avec quelques outils existants et quelques lignes de code, on construit rapidement un talkie-walkie à chiffrement numérique, capable de communiquer de la voix (compréhensible mais pas haute qualité). Plus de routiers CB à écouter vos conversations familiales !
C'était un test d'entrée microphone basée sur MAX9814 pour le Goldilocks Analogue. Je réviserai le Prototype 3 en ajoutant un circuit d'amplification microphone pour applications nécessitant une entrée audio (talkie-walkie, changeur de voix, synthétiseur vocal).
L'ATmega1284p tourne à 24,576 MHz (au lieu de 20 MHz standard). Cette fréquence permet une reproduction précise d'échantillons audio de 48 kHz down to 4 kHz (voire 1,5 kHz). Les cycles MCU supplémentaires par période d'échantillonnage sont bienvenus pour la synthèse musicale.
Code sur SourceForge AVR freeRTOS. Merci à Shuyang de SeeedStudio pour son OPL et composants/PCBs.
Code principal : audioCodec_dsp
void audioCodec_dsp(uint16_t *ch_A, uint16_t *ch_B) {
int16_t xn;
uint8_t cn;
/*----- Audio Rx -----*/
if (ringBuffer_IsEmpty((ringBuffer_t*)&(xSerialPort.xRxedChars))) {
cn = 0x80 ^ 0x55; // signal A-Law nul en sortie
} else if (ringBuffer_GetCount(&(xSerialPort.xRxedChars)) > (portSERIAL_BUFFER_RX >> 1)) {
cn = ringBuffer_Pop((ringBuffer_t*)&(xSerialPort.xRxedChars));
cn = ringBuffer_Pop((ringBuffer_t*)&(xSerialPort.xRxedChars)); // dépile 2 échantillons pour rattraper
} else {
cn = ringBuffer_Pop((ringBuffer_t*)&(xSerialPort.xRxedChars));
}
alaw_expand1(&cn, &xn); // décompression A-Law
*ch_A = *ch_B = (uint16_t)(xn + 0x7fff); // signal positif, sortie canaux A & B
/*----- Audio Tx -----*/
AudioCodec_ADC(&mod7_value.u16); // échantillon 10 bits justifié gauche
xn = mod7_value.u16 - 0x7fe0; // centrer sur 0
IIRFilter(&tx_filter, &xn); // filtrage échantillon transmis
alaw_compress1(&xn, &cn); // compression A-Law
xSerialPutChar(&xSerialPort, cn); // transmission échantillon
}
Routine d'interruption timer
ISR(TIMER0_COMPA_vect) __attribute__ ((hot, flatten));
ISR(TIMER0_COMPA_vect) {
#if defined(DEBUG_PING)
PORTD |= _BV(PORTD7); // Ping ligne IO (début interruption)
#endif
DAC_out(ch_A_ptr, ch_B_ptr); // Transfert données vers MCP4822 (en premier pour régularité)
if (audioHandler != NULL)
audioHandler(ch_A_ptr, ch_B_ptr); // Gestionnaire audio global (callback)
#if defined(DEBUG_PING)
PORTD &= ~_BV(PORTD7); // Ping ligne IO (fin interruption)
#endif
}
Sortie DAC (SPI)
void DAC_out(const uint16_t *ch_A, const uint16_t *ch_B) {
DAC_command_t write;
if (ch_A != NULL) {
write.value.u16 = (*ch_A) >> 4;
write.value.u8[1] |= CH_A_OUT;
} else {
write.value.u8[1] = CH_A_OFF;
}
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // SS bas
SPDR = write.value.u8[1];
while (!(SPSR & _BV(SPIF)));
SPDR = write.value.u8[0];
if (ch_B != NULL) {
write.value.u16 = (*ch_B) >> 4;
write.value.u8[1] |= CH_B_OUT;
} else {
write.value.u8[1] = CH_B_OFF;
}
while (!(SPSR & _BV(SPIF)));
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // SS haut, verrouillage valeur
SPI_PORT_SS_DAC &= ~SPI_BIT_SS_DAC; // SS bas pour canal B
SPDR = write.value.u8[1];
while (!(SPSR & _BV(SPIF)));
SPDR = write.value.u8[0];
while (!(SPSR & _BV(SPIF)));
SPI_PORT_SS_DAC |= SPI_BIT_SS_DAC; // SS haut, verrouillage
}
Ressources
Dépôt AVR freeRTOS sur SourceForge (inclut DAC.h et fichiers de test analogique) : sourceforge.net/projects/avrfreertos/
Schéma de câblage
Le schéma Fritzing utilise un DAC MCP4725 (I2C) au lieu du MCP4822 (SPI) car le composant Adafruit n'était pas disponible. Il montre une seule direction (sauf Rx/Tx interconnectés). Les modules XBee remplacent simplement les deux fils Rx/Tx. Tout système radio supportant le débit requis fonctionne.
Schéma pour la sortie DAC et l'amplificateur casque. L'amplificateur microphone sera ajouté sur le prototype 4.
Processus de fabrication
- Considérations clés pour l'usinage Swiss à haut volume
- Guide du prototypage CNC : précision, rapidité et production rentable
- Fabrication d'arbres experte : procédés de précision et solutions sur mesure
- Passivation acier inoxydable : Améliorer la résistance à la corrosion et la performance
- Mesurer des capteurs analogiques sur Raspberry Pi avec une seule broche GPIO
- Utiliser un ADC MCP3008 pour lire des capteurs analogiques avec Raspberry Pi
- Génération de formes d'onde de haute précision avec un PCB personnalisé basé sur DAC
- Contrôleur d'irrigation intelligent avec Windows 10 IoT, Raspberry Pi et capteurs d'humidité XBee
- Pourquoi les mesures analogiques pilotent la performance usine