• Willkommen im Geoclub - dem größten deutschsprachigen Geocaching-Forum. Registriere dich kostenlos, um alle Inhalte zu sehen und neue Beiträge zu erstellen.

Reaktives Audio mit Atmel AVR

eigengott

Geowizard
Angeregt durch den Reaktivlicht-Thread habe ich angefangen, an einem Reaktivaudiomodul basierend auf einem ATtiny zu basteln. Das Projekt ist noch nicht fertig, aber ich habe es immerhin schon geschafft, ein 30s langes Soundsample abzuspielen.

Zur Technik: Ich verwende einen ATtiny 44V. Der hat genügend Port-Pins, hardwareunterstütztes SPI, einen ADC, genügend RAM, Watchdog und extreme Stromparmodi. Die Samples speichere ich auf einer gewöhnlichen SD-Karte, die via SPI an den ATtiny angeschlossen wird. LDR zur Aktivierung wie beim Reaktivlicht. Audio momentan per direkt angeschlossenem Piezo-Element.

Da der ATtiny keinen DAC besitzt und ich auch keinen externen anschliessen möchte (von R2R-Frickeleien ganz abgesehen), benutze ich Fast-PWM via Timer0 zur Ausgabe. Die Samples werden als Comparematch-Wert genutzt, an dem der PWM-Ausgang umschaltet. Das Timing um das nächste Sample zu laden bekommt man praktischerweise auch gleich von Timer0 - es wird einfach der Overflow-Interrupt dafür genutzt.

Macht bei 8 MHz Takt, 256 Schritten pro PWM-Zyklus eine Samplerate von 31250 Hz mit 8 Bit breiten Samples. Kein HiFi aber auch kein extremes Gekrächze. :)

Schon etwas frickeliger ist das Anbinden der SD-Karte. Die will ordentlich Initialisiert werden und danach muss man blockweise lesen. Da der ATtiny 44 genügend RAM hat (256 Byte), werden jeweils Blöcke zu 64 Byte gelesen und in dem insgesamt 128 Byte grossen Sample-Buffer gespeichert. Und zwar jeweils in die Hälfte, in der der "Audioplayer" (also die Timer0-Overflow-Routine) gerade nicht liest.

Auf FAT usw. habe ich komplett verzichtet. Die SD-Karte wird frisch formatiert und dann die PCM-Datei darufgeschrieben. Die liegt damit unfragmentiert auf der Karte. Mit einem Hex-Editor lässt sich der erste Datenblock bestimmen und dann wird einfach sukzessive eine vorgegebene Anzahl Blöcke gelesen und als Audio ausgegeben.

Momentan fehlt noch die Reaktiv-Funktion (trivial, da Code im Prinzip vorhanden), PowerManagement für die Karte (sprich: abschalten wenn sie nicht benötigt wird), Audio-Verstärkung und -Filterung.
 
OP
eigengott

eigengott

Geowizard
Windi schrieb:
Ich tippe mal eher auf Assembler.

So ist es. Einige Sachen sind durchaus zeitkritisch. Und ehrlich gesagt sind die kleinen Atmels ein willkommener Vorwand mal wieder in Assembler zu programmieren. Der Befehlssatz ist ja durchaus brauchbar und Schweinereien wie bei MIPS gibt es zum Glück auch nicht.

Kann Bascom mit Interrupts umgehen?
 
OP
eigengott

eigengott

Geowizard
Windi schrieb:
Kannst Du mal die D-A-Wandelung (Fast PWM) genauer erläutern?

PWM = Pulsweitenmodulation. Gemeint ist damit ein Rechtecksignal, bei dem man einstellen kann, wie lange das Signal pro Schwingung "an" und wie lange es "aus" ist:

om_pwm.gif


Integriert man jetzt die Kurven, sieht man das die Strommengen je nach Pulsweite unterschiedlich sind. D.h. über einen etwas längeren Zeitraum betrachtet & integriert, kann man über die Pulsweite verschieden hohe Spannungen ausgeben - nichts anderes macht ein D/A-Wandler, nur daß es dort nicht zerhackt sondern kontinuierlich ist.

Für Audio lässt man den zerhackenden Takt (PWM) deutlich schneller laufen als das Audio selbst. Mit einem Lowpass-Filter kann man dann die PWM-Hochfrequenz unterdrücken. Im konkreten Fall läuft die PWM 256x schneller als die Audiosamplefrequenz.

Das lässt sich alle relativ simpel mit den 8bit Timern der Atmels erledigen. Im Fast PWM-Modus zählen die stur von 0 (Bottom) bis 255 (Top) durch und fangen dann wieder von vorne an.

Weiterhin gibt es ein CompareMatch-Register. In das kann man einen Wert zwischen 0 und 255 schreiben, der dann immer mit dem Timer verglichen wird. bei Gleichheit wird dann der zugehörige Ausgang auf Low gesetzt. Sobald der Timer wieder bei 0 (Bottom) ist, wird der Ausgang automatisch auf High gesetzt.

Damit hat man dann ein PWM-Signal am Ausgang, dessen "an"/"aus"-Verhältnis vom jeweiligen Wert im CompareMatch-Register abhängt. Schreibt man dort z.B. eine 0 hinein, geht der Ausgang nach Bottom->High direkt wieder auf Low - also praktisch keine Spannung am Ausgang. Schreibt man 128 in das Register, bekommt man 50% von Vcc, schreibt man 255 hinein, bekommt man 100% Vcc.

Will man nun Audio spielen, kann man sich bei jedem Übergang des Timers von 255 auf 0 einen Overflow-Interrupt auslösen lassen. In der Interrupt-Routine schreibt man dann das nächste Sample in das Compare-Match Register.

Hier ein einfacher Sinus auf einem ATtiny 13, hörbar auf PB0 mit einem Piezo-Element. Fuses: 9.8 MHz RC-Oszillator, keine Teilung durch 8.

Alternativ kann man auch den 128 kHz Oszillator und Teilung durch 8 einstellen und eine LED and PB0 anschliessen - die dimmt dann schön hoch und runter im Sinustakt.

Code:
.nolist
.include "tn13def.inc"
.list

.def	mp = R16
.def	sample = R17

;
; Interrupt vectors
;
	.cseg
	rjmp main		; Reset handler

	.org OVF0addr
	rjmp ovf0_handler

;
; Main program
;
main:
	cli
;
; Setup stack pointer
;
	ldi mp, low(RAMEND)
	out SPL, mp

; Setup idle sleep mode
	ldi mp, (1<<SE)
	out MCUCR, mp
	
;
; Setup PORTB0 as output
;
	ldi mp, (1<<PORTB0)
	out DDRB, mp
	ldi mp, 0
	out PORTB, mp

;
; Setup timer 0 in fast PWM mode
;
	ldi sample, 0x80
	out OCR0A, sample
	ldi mp, (1<<COM0A1) | (0<<COM0A0) | (1<<WGM01) | (1<<WGM00)
	out TCCR0A, mp
	ldi mp, (0<<CS02) | (0<<CS01) | (1<<CS00) | (0<<WGM02)
	out TCCR0B, mp

;
; Setup timer 0 overflow interrupt
;
	ldi mp, (1<<TOIE0)
	out TIMSK0, mp
	clr mp		; force data update on first interrupt
	sei

;
; Main loop - empty, all the work is done in the interrupt
; service routine
;
loop:
	sleep
	rjmp loop

;
; Timer0 overflow interrupt service
; Write sample and fetch next one
;
ovf0_handler:
	out OCR0A, sample
	dec mp
	brpl no_update
	ldi mp, 63
no_update:
	out EEARL, mp
	sbi EECR, EERE
	in sample, EEDR
	reti

;
; A small sine table as output
;
	.eseg
sintab:
	.db 128, 140, 152, 165, 176, 188, 198, 208
	.db 218, 226, 234, 240, 245, 250, 253, 254
	.db 255, 254, 253, 250, 245, 240, 234, 226
	.db 218, 208, 198, 188, 176, 165, 152, 140
	.db 128, 115, 103, 90, 79, 67, 57, 47
	.db 37, 29, 21, 15, 10, 5, 2, 1
	.db 0, 1, 2, 5, 10, 15, 21, 29
	.db 37, 47, 57, 67, 79, 90, 103, 115
 

waste1

Geocacher
Ein Soundrecorder mit AVR ist in der Application Note AVR335 von Atmel beschrieben. Da ist auch sehr gut die DA-Wandlung mittels PWM erklärt. Die AppNote gibt es hier:
http://www.atmel.com/dyn/resources/prod_documents/doc1456.pdf

Mein Sound-Modul ist im Wesentlichen nach dieser AppNote aufgebaut, habe aber auf den Mikrofoneingang verzichtet. Die PCM-Daten (sind ja .wav Dateien) werden auf dem PC erzeugt und über eine serielle Schnittstelle in den Flashspeicher geschoben. Mit einem Flashspeicher AT45DB041B (hat 4MBit) kann man bei PCM 8kHz und 8Bit bis zu 1 Minute Sound speichern. Beim AT45DB161D (hat 16MBit) bis zu 4 Minuten.

@eigengott
Hast Du mal einen Link von dem Datenblatt deiner SD-Karte, will mir das mal anschauen.
 
OP
eigengott

eigengott

Geowizard
waste1 schrieb:
@eigengott
Hast Du mal einen Link von dem Datenblatt deiner SD-Karte, will mir das mal anschauen.

Sehr hilfreich (und mit lustigem Englisch 8) ):
http://elm-chan.org/docs/mmc/mmc_e.html

Die genaue Spezifikation gibt es da:
http://www.cs.ucr.edu/~amitra/sdcard/ProdManualSDCardv1.9.pdf
 
OP
eigengott

eigengott

Geowizard
eigengott schrieb:
Einige Sachen sind durchaus zeitkritisch.

:roll: Ich hatte anfangs nur mit einer uralten 16 MB Karte experimentiert, die ich mal als nutzlose Dreingabe mit einer Digitalkamera bekommen habe. Damit lief es auch...

Gestern habe ich dann mal mit verschiedenen anderen, moderneren Karten (64 MB TransFlash vom Garmin 60Csx, 512 MB TransFlash, 1 GByte SD) probiert. Nix, nada, bestenfalls schlimmes Rauschen. Heute habe ich dann den Fehler gefunden: Die SPI-Routine war *zu schnell* für die neueren Karten. :evil: Mit einem NOP zwischen den Bits klappt es jetzt aber auch damit.

Als Fassung für die SD-Karten während der Entwicklung hat sich übrigens ein uraltes Floppykabel bewährt, an dem noch ein Anschluss nach alter Norm dran war. Der passt perfekt auf die Kontakte.

Siehe auch da: http://uanr.com/sdfloppy/
 

waste1

Geocacher
Danke für die Links zur Spezifikation. Was mir auf die Schnelle aufgefallen ist, die Stromaufnahme der SD-Karte im Sleepmode ist mit 250µA angegeben. Ist das nur ein worst case Wert oder ist der wirklich so hoch. Mir wäre das zu viel.
 
OP
eigengott

eigengott

Geowizard
waste1 schrieb:
Ist das nur ein worst case Wert oder ist der wirklich so hoch. Mir wäre das zu viel.

Das dürfte ein worst case Wert sein. Ich habe gerade mal mit einer SanDisk 1GB Karte gemessen: 115 uA im Leerlauf. Während des Betriebs 20 mA (oder auch mehr, ich habe nur ein simples Multimeter und kann daher schnelle Spitzen nicht messen).

Für die Anwendung hier wird man der Karte aber ohnehin den Saft via Transistor komplett abdrehen, wenn kein Sound gespielt wird.

Viel ätzender bei der SD-Karte ist die relativ komplexe Ansprache - da sind Atmels serielle Flashs *viel* einfacher zu handhaben. Dafür sind sie aber nicht so leicht zu bekommen, vergleichsweise teuer und in der Kapazität begrenzt. Eine 1 GB SD-Karte gibt's bei Reichelt z.B. für weniger als 5 Euro. Da passen dann 8 Stunden 31250hz-8bit-pcm-Sound 'drauf...
 

waste1

Geocacher
eigengott schrieb:
Für die Anwendung hier wird man der Karte aber ohnehin den Saft via Transistor komplett abdrehen, wenn kein Sound gespielt wird.
Würde ich auch, denn 115µA ist immer noch zu viel.

Viel ätzender bei der SD-Karte ist die relativ komplexe Ansprache - da sind Atmels serielle Flashs *viel* einfacher zu handhaben. Dafür sind sie aber nicht so leicht zu bekommen, vergleichsweise teuer und in der Kapazität begrenzt. Eine 1 GB SD-Karte gibt's bei Reichelt z.B. für weniger als 5 Euro.
Ich habe meine bei Kessler-electronic gekauft. Den AT45DB161D-SU für 2,44 EUR.

Da passen dann 8 Stunden 31250hz-8bit-pcm-Sound 'drauf...
Das wird hart, 8 Stunden an einer Station auszuharren. :lol:
 

Windi

Geoguru
waste1 schrieb:
Da passen dann 8 Stunden 31250hz-8bit-pcm-Sound 'drauf...
Das wird hart, 8 Stunden an einer Station auszuharren. :lol:
Ich hätte da schon eine Idee.
Am Startpunkt nimmt der Geocacher ein entsprechendes Gerät mit.
An den einzelnen Stationen werden dann (wie auch immer) verschiedene Samples aktiviert und abgespielt.
Damit wird dann Stück für Stück eine Geschichte erzählt.
Das wird bestimmt sehr interessante Caches geben.
 

Portitzer

Geocacher
OP
eigengott

eigengott

Geowizard
waste1 schrieb:
Ich habe meine bei Kessler-electronic gekauft. Den AT45DB161D-SU für 2,44 EUR.

Den Laden kannte ich nochgar nicht, bisher hatte ich die nur beim Apotheker (Segor) gefunden. Das ist ja wirklich günstig.

Hmm... Durch den Deep Power Down-Modus entfiele die Notwendigkeit, den Chip hardwaremässig auszuschalten. Und dann müsste das Ganze sogar mit einem ATtiny13 machbar sein, wenn man z.B. CLK out und LDR in auf den gleichen Pin legt (werden ja nicht gleichzeitig benötigt). Das Timing könnte etwas knapp sein, da der ATtiny 13 keine USI Einheit besitzt und man die Bits von Hand klöppeln muss - pro Byte hat man dafür ca. 240 Takte Zeit, der Rest geht für den Timer Interrupt 'drauf.

Und noch ein Problem: Wie genau hast du den Flash mit Daten gefüllt? Hardware, Software?
 

waste1

Geocacher
eigengott schrieb:
Und dann müsste das Ganze sogar mit einem ATtiny13 machbar sein, wenn man z.B. CLK out und LDR in auf den gleichen Pin legt (werden ja nicht gleichzeitig benötigt). Das Timing könnte etwas knapp sein, da der ATtiny 13 keine USI Einheit besitzt und man die Bits von Hand klöppeln muss - pro Byte hat man dafür ca. 240 Takte Zeit, der Rest geht für den Timer Interrupt 'drauf.
ATtiny45 wäre noch eine Alternative, der hat USI.
Ich habe auch den ATtiny44 vorgesehen, weil ich auch noch andere Pins brauche. Aber ist alles noch nicht so festgelegt.

Und noch ein Problem: Wie genau hast du den Flash mit Daten gefüllt? Hardware, Software?
Im Moment nehme ich dafür den AVR-Butterfly, der hat selbst einen Flashbaustein AT45DB041B drauf und deshalb auch die Routinen zum Beschreiben schon integriert. Zusätzlich ist auf dem Butterfly auch eine serielle Schnittstelle, so dass man vom PC den Flashbaustein beschreiben kann. Ich habe jetzt einfach die SPI-Schnittstelle zum Flashbaustein auf ein vierpoliges Kabel gelegt und den ChipSelect umprogrammiert, so dass ich jetzt auch externe Flashbausteine beschreiben kann. Die neuen Flashbausteine werden also an das vierpolige Kabel (SPI + Masse) angeklemmt und programmiert. Dabei muss ich im Moment noch den ATtiny44 auf der Soundrecorderplatine ziehen, damit der nicht stört. Ist nur ein Provisorium, aber da der Flash wahrscheinlich nur einmal beschrieben wird, bleibt es vielleicht auch dabei.
 
OP
eigengott

eigengott

Geowizard
waste1 schrieb:
ATtiny45 wäre noch eine Alternative, der hat USI.

Der ist aber teurer als der ATtiny 24 und der Platzbedarf für die sechs Pins mehr dürfte angesichts der restlichen Peripherie (Audioverstärker, Stromversorgung) nicht weiter stören.

Der ATtiny 24 genügt bereits für die SD-Karten-Variante, ich habe mit zwei 32 Byte grossen Lesepuffern erfolgreich getestet. Der Code ist deutlich unter 1 kB gross (momentan 648 Byte, inklusive LDR-Funktion).

Das mit dem DataFlash laden klingt ja genauso kompliziert wie befürchtet. :) Das war für mich mit ein Grund, erstmal mit der SD-Karte zu arbeiten, da hat man dieses Problem nicht.
 
Oben