Hallo,
ich möchte Bitmuster z.B. 01101100 über einen GPIO des Atmega32 senden.
Der Atmega ist mit 16MHz getaktet.
Für eine logische Null möchte ich 3uS low dann 6us high an den GPIO Pin
legen:
___------
Für eine logische Eins möchte ich 6uS low dann 3us high an den GPIO Pin
legen:
______---
Wie bekomme ich das Timing hin?
Setze ich den Pin einfach auf high/low und arbeite dann mit delay_us()?
Oder nehem ich da besser die PWM, aber wie sende ich damit dann
Bitfolgen?
Weil ja die PWM eigentlich ein immergleiches high/low Verhältnis sendet.
Danke für Eure Hilfe...
T. Baumbach schrieb:> Setze ich den Pin einfach auf high/low und arbeite dann mit delay_us()?
Wenn der µC nichts anderes tun soll als diese Bitfolge auszugeben, dann
spricht nichts dagegen.
Sonst: Timer aufsetzen und in der ISR bis 3 oder 6 zählen, dann Pegel
ändern.
Man könnte auch überlegen, ob man das Timing mit SPI hinbekommt, aber
ohne DMA sendet SPI IMO nicht back-to-back auf AVR.
Die delay_us Methode hat großen Jitter wenn Interrupts aktiv sind, denn
die Delay Zeit verlängert sich um die Zeit, die der µC währenddessen im
Interrupt braucht.
Die PWM müsste man nach jedem Zyklus neu mit dem nächsten Wert laden,
das könnte knapp passen.
Frank M. schrieb:> Sonst: Timer aufsetzen und in der ISR bis 3 oder 6 zählen, dann Pegel> ändern.
Timer Interrupt mit 1 µs Zykluszeit ist bei 16 MHz nicht mehr möglich,
da für den Interrupt Aufruf zuviele Zyklen verwendet werden (Register
sichern etc).
T. Baumbach schrieb:> Wie bekomme ich das Timing hin?
Die erste Frage lautet: wie genau muss es sein?
Je nach Anforderung und je nachdem, was im Programm sonst noch so läuft,
nimmt man eine andere Methode.
Jim M. schrieb:> Frank M. schrieb:>> Sonst: Timer aufsetzen und in der ISR bis 3 oder 6 zählen, dann Pegel>> ändern.>> Timer Interrupt mit 1 µs Zykluszeit ist bei 16 MHz nicht mehr möglich,> da für den Interrupt Aufruf zuviele Zyklen verwendet werden (Register> sichern etc).
3µs könnten sich allerdings ausgehen. Bei 48 Takten hat man noch ein
bischen Reserve. Nicht viel, aber immerhin.
T. Baumbach schrieb:> Wie bekomme ich das Timing hin?
Wie bereits gefragt, soll der Controller nur seine Bitfolge senden, oder
was anderes nebenher machen? Für eine Sequenz deines Bits zu senden
brauchst du ca. 72 µs. Das ist relativ unkritisch und wesentlich
schneller als in der Regel eine Ausgabe auf ein LCD Display. Kommt
jetzt darauf an wie oft das gesendet werden muss und wie die Toleranzen
der Timings sind. Man kann, aber man muß es nicht mittels Timer und ISR
machen. Ich würde es zumindest in C embeded Assembler (Externe
Ass-Datei) umsetzen. Aber das ist natürlich Geschmacksache.
Hi,
danke schonmal, die Genauigkeit sollte so bei +/- 150ns liegen.
Die Bitfolgen bestehen aus ca. 100-300 Bytes.
Dann ist erstmal Pause. Neue Bitfolgen sollen dann gesendet werden, wenn
bestimmte Sensoren neue Daten liefern. Das kann mal Sekunden, mal
Minuten dauern. Die Sensoren werden über Interrupts abgefragt.
T. Baumbach schrieb:> Hi,>> danke schonmal, die Genauigkeit sollte so bei +/- 150ns liegen.
Das wird eng.
Da kommt es auf jeden einzelnen Takt an.
> Die Bitfolgen bestehen aus ca. 100-300 Bytes.
Dann wird es noch enger. 1 Byte ausgeben wird schon knapp, aber dann
muss ja auch noch der Nachschub rollen.
> Dann ist erstmal Pause. Neue Bitfolgen sollen dann gesendet werden, wenn> bestimmte Sensoren neue Daten liefern. Das kann mal Sekunden, mal> Minuten dauern.
reine Neugier:
Was hängt denn da am anderen Ende, dass du mit derart fiesen Timings
arbeiten musst?
das ganze erinnert mich ans Bundesheer: 3/4 der Zeit hängt man rum und
tut nichts, aber der Mülleimer muss in Lichtgeschwindigkeit ausgeleert
werden.
Karl H. schrieb:> T. Baumbach schrieb:>> Hi,>>>> danke schonmal, die Genauigkeit sollte so bei +/- 150ns liegen.>> Das wird eng.> Da kommt es auf jeden einzelnen Takt an.
Im Moment seh ich nicht viele andere Möglichkeiten als tatsächlich einen
PWM Modus zu benutzen. Zusätzlich noch einen Interrupt auf den Compare
Match, wenn der Pin vom Timer von 1 auf 0 umgeschaltet wird. Im
Interrupt wird dann das OCR Register jeweils nachgeladen.
Eng wird es auf jeden Fall, denn mehr als 3µs hast du im schelchtesten
Fall nicht, um dieses Nachladen zu bewerkstelligen. Und die 300 bytes
wollen ja auch aus dem Speicher rangekarrt werden.
Karl H. schrieb:> Eng wird es auf jeden Fall, denn mehr als 3µs hast du im schelchtesten> Fall nicht, um dieses Nachladen zu bewerkstelligen. Und die 300 bytes> wollen ja auch aus dem Speicher rangekarrt werden.
Was natürlich schon geht.
Interrupts aus und dann mit Warteschleifen die Zeit absitzen. Erfordert
allerdings genau Abstimmung in Assembler über alles. Jeder Taktzyklus
zählt.
UART auf 7N1 einstellen, UBRR = 2.
So passen 3 Daten-Bits in ein "UART-Byte". Diese "UART-Bytes" vorher
vorbereiten, dann ist das ganze Timing ziemlich unkritisch.
Karl H. schrieb im Beitrag #4312284:
> Das Problem sind die Stoppbits.
Warum?
0 und 1 enden beide mit 3µs High. Sie unterscheiden sich nur im
"Mittelteil".
Drei Daten-Bits hintereinander sehen also so aus:
T. Baumbach schrieb:> Hi,>> kann man da evtl. mit ASM-Code innerhalb des C-Codes was machen?
Solange Du parallel Interrupts laufen lässt, ist ASM auch keine Lösung.
Du hast eigentlich 2 Möglichkeiten. Beide wurden oben schon angerissen.
1. Während des Transfers sämtliche Interrupts auschalten
und mit Delays arbeiten, damit Dir keiner dazwischenfunkt.
Dann bleibt jedoch noch ein kleiner Overhead, um die 300
Bytes in einer Schleife rauszupusten. Diesen musst Du evtl. in
die Delays mit einbeziehen.
2. Du benutzt den UART und füllst diesen, wie Stefan es beschrieben
hat. Hier könntest Du Interrupts dann zulassen, wenn diese
so kurz sind, dass das Füllen des UARTs nicht abreisst, der
UART also ununterbrochen feuert.
Ich weiß nicht, wie genau die Signale sein müssen. Hier handelt es sich
wohl um einen asymmetrischen Biphase-Code. Wenn der Empfänger sich auf
die Flankenwechsel neu "einschießt", also den Takt aus den Flanken
gewinnt, kann es durchaus sein, dass größere Abweichungen (z.B. 20%)
keine Rolle spielen.
T. Baumbach schrieb:> kann man da evtl. mit ASM-Code innerhalb des C-Codes was machen?
Nein. Bei Deinen Timing-Anforderungen mußt Du das in ASM machen.
Der Rest kann dann C sein.
T. Baumbach schrieb:> Hi,>> kann man da evtl. mit ASM-Code innerhalb des C-Codes was machen?
Klar kann man, aber Das ist meiner Meinung nach nur etwas für
Masochisten. Ich lagere den *.s Code aus und kann so Ass programmieren
ohne mir das Hirn zu verbiegen. Es wäre ein Versuch wert eine Funktion
zum senden deiner Bitblöcke in Asseembler zu programmieren und diese am
Stück zusenden. Das unterbricht dein Hauptprogramm circa um 22ms, wenn
ich mich nicht verrechnet habe. Die zusenden und Bytes sollten in einem
Block hintereinander im Speicher liegen, dann sollte eigentlich auch das
Nachladen kein großes Problem sein. Etwas Zeit unkrietischer wäre es
natürlich wenn du deinen Chip auf 20 MHz takten könntest.
Stefan E. schrieb:> Karl H. schrieb im Beitrag #4312284:>> Das Problem sind die Stoppbits.>> Warum?
Ich habs nach 30 Sekunden zurückgezogen. hatte die 7 in 7N1 übersehen.
Sind zusammen mit dem Startbit 9 Bits und damit ein vielfaches von 3
> Drei Daten-Bits hintereinander sehen also so aus:>
1
> L x H L x H L x H
2
> | |
3
> Startbit Stopbit
4
>
Ja.
Da hab ich zu langsam geschaltet und zu schnell getippt.
Sorry.
Thomas H. schrieb:> Etwas Zeit unkrietischer wäre es> natürlich wenn du deinen Chip auf 20 MHz takten könntest.
Ebenso könnte man einen DMA-fähigen µC verwenden. Dann sind das Peanuts.
1. fast PWM:
Du hast 9µs = 144 Zyklen Zeit, das nächste Bit nachzuladen. Das geht in
C ohne Probleme.
2. UART mit 7Bit bei 333kBaud:
Damit kannst Du je UART-Byte 3 Bit senden.
3. ATmega324 nehmen und UART als gepufferten SPI-Master.
Man kann ja im Assembler versuchen kein Register zu benutzen mit SBI und
CBI, dann braucht man auch keine Register zu sichern. Bei 16MHz dann
halt
Low: 48 x "CBI PORTA,7" und 96 "SBI PORTA,7"
High:96 x "CBI PORTA,7" und 48 "SBI PORTA,7"
Oder halt ne schleife mit einem Arbeitsregister das dann noch gepusht
und gepopt werden muß.
Ansosnten halt Timer benutzen: mit CTC Bit gesetzt und Interupt on
Compare match und Interupthandler selber in Assembler schreiben nur
Statusregister sichern und ein Arbeitsregister sichern und zum laden des
Comparewertes benutzen
Sieht für mich fast nach seriellen LEDs (WS2812B) aus.
Die haben ein kritisches Timing. Das Senden macht man dann per ASM und
sperrt davor die Interrupts. Bei 4 MHz kritisch, bei 16 MHz locker
machbar.
Frank M. schrieb:> Ebenso könnte man einen DMA-fähigen µC verwenden. Dann sind das Peanuts.
Klar und ohne Frage. Es gäbe noch unzählige Lösungen. Sind aber alle
unnötig inclusive 20 Mhz, wenn er seine ISR's stoppen könnte für die
25ms, wozu er sich aber noch nicht geäußert hat. Den Assemblercode für
das Rausschieben von ein paar Bytes zu erstellen ist unter der
Zuhilfenahme des Simulators (um das Timing zu kontrollieren) zudem eine
Anfängeraufgabe.
uwe schrieb:> Interupthandler selber in Assembler schreiben
Bei 144 Zyklen Interruptrate ist Assembler völlig unnötig.
Da muß man sich schon sehr dumm anstellen, um in C über 100 Zyklen zu
verbrauchen.
Die Daten sollten aber schon gepuffert vorliegen, d.h. der Interrupt hat
einen Zähler 0..7 für das Rotieren der 8 Bits und einen Pointer auf das
zu sendende Datenbyte.
T. Baumbach schrieb:> ich möchte Bitmuster z.B. 01101100 über einen GPIO des Atmega32 senden.> Der Atmega ist mit 16MHz getaktet.> Für eine logische Null möchte ich 3uS low dann 6us high an den GPIO Pin> legen:>> ___------>>> Für eine logische Eins möchte ich 6uS low dann 3us high an den GPIO Pin> legen:>> ______--->>> Wie bekomme ich das Timing hin?
Z.B. mit den Dingern, die sich schon rein vom Namen her für solche
Sachen anbieten, mit Timern.
> Oder nehem ich da besser die PWM, aber wie sende ich damit dann> Bitfolgen?
Indem du in der ISR den OCR-Wert für das nächste Bit einstellst.
> Weil ja die PWM eigentlich ein immergleiches high/low Verhältnis sendet.
Nicht, wenn du den OCR-Wert änderst...
Das müßtest du hier alle 9µs tun, was bei 16MHz Takt noch kein
nennenswertes Problem ist, schließlich kann der AVR in 9µs bei 16MHz
genau 144 Takte lang Code abarbeiten. Natürlich hast du etwas
Interrupt-Overhead, aber in einer gediegenen Programmiersprache bleiben
trotzdem reichlich Takte in der ISR über, um den Job zu erledigen, den
sie erledigen muß.
Allerdings gibt es eine noch effizientere Lösung, wenn man eine
SPI-masterfähige USART zur Verfügung hat. Dein Problem kann man dann
nämlich auch damit lösen und dadurch die Interrupt-Rate auf fast ein
Drittel senken (in jedem gesendeten Byte stecken 2 2/3 encodierte
Nutzbits. Leider ist aber die Komplexität des Nutzcodes selber dann
deutlich höher, im Vergleich von hochoptimierten Asm-Implementierungen
der beiden Ansätze ergibt sich dadurch dann nur noch ein relativ kleiner
Vorteil für die USART-SPI-Lösung.
Aber in Stümper-C mit seinem hoch-ineffizienten Interrupthandling wiegt
die Ersparnis bei der ISR-Rate deutlich schwerer, hier lohnt der Ansatz
durchaus.
Noch besser ist übrigens im konkreten Fall eine USART in ihrer ganz
normalen Betriebsart, allerdings im 7N1-Modus. Da bekommt man dann
nämlich
volle drei Nutzbits in einem Sende-"Byte" (nur 7 Bit lang) encodiert,
drittelt also die nötige ISR-Rate, da zufällig die Level von LL-RS232-
Start- und Stopbits in's gewünschte Encodier-Schema passen. Das
vereinfacht dann den Nutzcode in der ISR so weit, dass es auch in Asm
wieder sehr attraktiv wird. (In C sowieso, die Gülle profitiert hier
gleich doppelt, sowohl vom trivialeren ISR-Code als auch von der
weiteren Absenkung der ISR-Rate)