Forum: Mikrocontroller und Digitale Elektronik Atmega32 Bitmuster senden


von T. Baumbach (Gast)


Lesenswert?

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

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Jim M. (turboj)


Lesenswert?

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.

von Jim M. (turboj)


Lesenswert?

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).

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

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.

: Bearbeitet durch User
von T. Baumbach (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Stefan E. (sternst)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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:
1
L x H L x H L x H
2
|               |
3
Startbit        Stopbit

: Bearbeitet durch User
von T. Baumbach (Gast)


Lesenswert?

Hi,

kann man da evtl. mit ASM-Code innerhalb des C-Codes was machen?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von T. Baumbach (Gast)


Lesenswert?

Hallo,

ich werde es dann wohl mal mit den delays versuchen.

Danke an alle!

von Route_66 H. (route_66)


Lesenswert?

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.

: Bearbeitet durch User
von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von uwe (Gast)


Lesenswert?

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

von peterfido (Gast)


Lesenswert?

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.

von Thomas H. (Firma: CIA) (apostel13)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.