PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem: ATTiny13 mit RC-Servosignal/WS2803 LED-Treiber



Poettie
25.11.2012, 18:53
Hallo,

nachdem ich nun selber nicht mehr weiter komme, versuche ich nun einen barmherzigen Bascom-Helfer zu finden, der mir vielleicht weiter helfen kann.

Ich habe ein ATMega8-Programm welches mir abhängig von einem durch einen Interrupt ausgelöstes Event (Servo-Signal aus einem RC-Empfänger), Lichtspielereien an einem WS2803 LED-Treiber ermöglicht.

Soweit funktioniert auch mit dem Mega8 alles wie ich es möchte. Allerdings wollte ich aufgrund von Platzersparnis nun einen ATTiny13 verwenden. Dieser hat allerdings nur einen 8-bit-Timer. Aber ich glaube mein portiertes Programm hat schon mit dem Interrupt Probleme. Hier der Quellcode in Bascom:




$regfile = "ATtiny13.dat" 'es handelt sich um einen ATTiny13
$crystal = 9600000 'der eingebauter RC-Oszillator läuft mit 9,6 Mhz
$hwstack = 32
$swstack = 10
$framesize = 10


Config Portb = Output 'Alle IO-Pins als Ausgang
Ddrb.1 = 0 'B1 = Interrupt also Eingang

Dim I As Byte
Dim J As Byte
Dim K As Byte

Dim Reading As Bit
Dim Timer_wert As Byte
Dim Error As Bit

Clk1 Alias Portb.0
Data1 Alias Portb.2

'Timer1 konfigurieren
Config Timer0 = Timer , Prescale = 1024

'Interrupt Int0 auf wechselndes Signal konfigurieren
Config Int0 = Change


'================================================= =====
'Initialisierungen
'================================================= =====

'Zuweisung der Interrupt-Service-Routinen
On Int0 On_int0
On Timer0 On_timer0

'Timer-Freigabe
Enable Timer0
Stop Timer0

'Freigabe der Interrupt-Routinen
Enable Int0
Enable Interrupts

'================================================= =====
'Hauptprogramm - Lauflicht mit 5 LEDs an einem WS2803
'================================================= =====

Do
If Timer_wert < 200 Then

K = 1
Do

Gosub Reset_ws2803

For J = 1 To 18
For I = 1 To 8
If J = K Then Data1 = 1 Else Data1 = 0
Gosub Clk_ws2803
Next I
Next J
Waitms 7

K = K + 1

Loop Until K > 5

End If

Loop
End


'================================================= =====
'Unterprogramme
'================================================= =====

On_int0: 'Interrupt
'Den Timer starten mit steigender Flanke
If Reading = 0 Then
Start Timer0
Reading = 1
'Den Timer stoppen mit fallender Flanke
Else
Stop Timer0
Timer_wert = Timer0
Timer0 = 0
Reading = 0
End If
'Error-Bit rücksetzen
Error = 0
Return


On_timer0: 'Timer
'Error-Bit stzen
Error = 1
Reading = 0
Stop Timer0
Timer_wert = 0
Return


'WS2803 LED-Treiber resetten
Reset_ws2803:
Clk1 = 0
Waitus 600
Return

'WS2803 alle LEDs aus
Clear_ws2803:
For J = 1 To 18
For I = 1 To 8
Data1 = 0
Gosub Clk_ws2803
Next I
Next J
Return

'WS2803 Clocksignal
Clk_ws2803:
Clk1 = 0
Waitus 1
Clk1 = 1
Waitus 1
Return


Sobald ich die Fernsteuerung einschalte, blinken die LEDs undefiniert. Ansonsten laufen sie anständig durch. Ich habe den Timer auch zum Test mal herausgenommen und das Timer-Unterprogramm "On_timer0" komplett geleert aber immer der gleiche Müll. Ich denke der Interrupt vermurkst mir das Timing für den WS2803 aber auf dem Mega8 lief es ja einwandfrei. Ich weiss ehrlich keinen Rat mehr. Hat von euch vielleicht jemand eine Idee?

P.S.:
R/C-Signal ist mit 50 Hz PWM moduliert und variiert je nach Reglerstellung zwischen 1-2 ms. Der Interrupt-Pin ist mit diesem Signal verbunden. Bei steigender Flanke wird der Timer gestartet und mit fallender Flanke wieder gestoppt.

Der WS2803 wird seriell mit Graustufen für 18 LEDs befüllt. Ich beschränke mich auf "volle Helligkeit". D.h. ich schiebe 18 x 8bit für die Graustufen in den Chip, hier 18 x den Binärwert für 256. Die Ansteuerung funktioniert wie gesagt soweit, bis ich eben den R/C-Sender einschalte und der Interrupt-Pin dann seine Signalwechsel bekommt.

for_ro
25.11.2012, 20:17
Hallo Poettie,
ich glaube nicht, dass du 18 mal den Wert 256 rausschickst, maximal gehen 255 in 8 Bit. Tatsächlich schickt deine Schleife aufgrund der Abfrage J=K wahrscheinlich das hier raus:
255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 255 0 0 0 0 0 0 0 0 0 0 0 0 0

Wieso der Int dazwischenfunken soll, sehe ich noch nicht.
Jedenfalls würde ich die Synchronisierung uber das Bit Reading anders machen. Wenn dies einmal aus dem Tritt kommt, passt das nicht mehr. Daher würde ich dir empfehlen, den Wert des Eingangs abzufragen.
If PinB.1 = 1 Then 'dann war es eine steigende Flanke
Zeige doch mal dein funktionierendes Programm für den M8.

Poettie
25.11.2012, 21:04
Hallo for_ro, danke für deine Antwort. Ja, 255 meinte ich ... ;-)

Hier der komplette Code für den Mega8





$regfile = "M8adef.dat" 'es handelt sich um einen ATmega8a
$crystal = 8000000 'der eingebauter RC-Oszillator läuft mit 8 Mhz
$hwstack = 100 'im Speicher werden für den Hardware-Stack 100 Byte reserviert
$swstack = 100 'im Speicher werden für den Software-Stack 100 Byte reserviert
$framesize = 100 'im Speicher werden für den Frame 100 Byte reserviert
'PC0 ist Clockausgang, PC1 ist Datenausgang. Rest Eingänge
Config Portd = Input
Config Portc = Output
Config Portb = Output

Dim I As Byte
Dim J As Byte
Dim K As Byte
Dim Kk As Byte
Dim L As Byte
Dim Ll As Byte
Dim Flashen As Byte
Dim Ogrenzetimer As Integer

Dim Greyscale As Byte


Dim Reading As Bit
Dim Timer_wert As Word
Dim Error As Bit

Clk1 Alias Portc.0
Data1 Alias Portc.1


'Timer1 konfigurieren
Config Timer1 = Timer , Prescale = 1

'Interrupt Int0 auf wechselndes Signal konfigurieren
Config Int0 = Change

Enable Timer1
Stop Timer1

Enable Int0
Enable Interrupts





'================================================= =====
'Initialisierungen
'================================================= =====

'Zuweisung der Interrupt-Service-Routinen
On Int0 On_int0
On Timer1 On_timer1

'Timer-Freigabe
Enable Timer1
Stop Timer1

'Freigabe der Interrupt-Routinen
Enable Int0
Enable Interrupts

Ogrenzetimer = 15350


'================================================= =====
'Hauptprogramm
'================================================= =====

Do

'Alles ausschalten
If Timer_wert > 1 And Timer_wert < 11749 Or Timer_wert > Ogrenzetimer Then

Gosub Reset_ws2803

Gosub Clear_ws2803

Portb.0 = 0
Portb.1 = 0
Portb.2 = 0
Portb.3 = 0
Portb.4 = 0
Flashen = 0
End If


If Timer_wert > 11750 And Timer_wert < 12151 Then
Gosub Reset_ws2803
Gosub Clear_ws2803

Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
'Portb.4 = 1
Portb.4 = 0
End If


If Timer_wert > 12150 And Timer_wert < 12551 Then
Gosub Reset_ws2803
Gosub Clear_ws2803
Do
Portb.0 = 0
Portb.1 = 0
Portb.2 = 0
Portb.3 = 0
Portb.4 = 0

Waitms 4

Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
'Portb.4 = 1

Waitms 1

Loop Until Timer_wert < 12551 Or Timer_wert > 12950
End If


If Timer_wert > 12550 And Timer_wert < 12951 Then
Gosub Reset_ws2803
Gosub Clear_ws2803
Do
Portb.0 = 0
Portb.1 = 0
Portb.2 = 0
Portb.3 = 0
Portb.4 = 0

Waitms 8

Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
'Portb.4 = 1

Waitms 1

Loop Until Timer_wert < 12551 Or Timer_wert > 12950
End If

If Timer_wert > 12950 And Timer_wert < 13351 Then
Gosub Reset_ws2803
Gosub Clear_ws2803
Do
Portb.0 = 0
Portb.1 = 0
Portb.2 = 0
Portb.3 = 0
Portb.4 = 0

Waitms 16

Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
'Portb.4 = 1

Waitms 1

Loop Until Timer_wert < 12951 Or Timer_wert > 13350
End If

If Timer_wert > 13350 And Timer_wert < 13751 Then
Gosub Reset_ws2803
Gosub Clear_ws2803
Do
Portb.0 = 0
Portb.1 = 0
Portb.2 = 0
Portb.3 = 0
Portb.4 = 0

Waitus 320

Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
'Portb.4 = 1

Waitus 10

Loop Until Timer_wert < 13351 Or Timer_wert > 13750
End If


If Timer_wert > 13750 And Timer_wert < 14151 Then

Gosub Reset_ws2803

Gosub Clear_ws2803

Portb.0 = 0
Portb.1 = 0
Portb.2 = 0
Portb.3 = 0
Portb.4 = 0
Flashen = 0

Waitms 50

Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
Portb.4 = 1

Gosub Reset_ws2803
For J = 1 To 36
For I = 1 To 8
Data1 = 1
Gosub Clk_ws2803
Next I
Next J

Waitms 10

End If


If Timer_wert > 14150 And Timer_wert < 14551 Then

Gosub Reset_ws2803

Gosub Clear_ws2803

Portb.0 = 0
Portb.1 = 0
Portb.2 = 0
Portb.3 = 0
Portb.4 = 0
Flashen = 0

Waitms 500

Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
Portb.4 = 1

Gosub Reset_ws2803
For J = 1 To 36
For I = 1 To 8
Data1 = 1
Gosub Clk_ws2803
Next I
Next J

Waitms 50

End If



'Lauflicht entgegen gesetzt
If Timer_wert > 14550 And Timer_wert < 14951 Then

Portb.0 = 0
Portb.1 = 0
Portb.2 = 1
Portb.3 = 1

Do

K = 1 : L = 36
Do

Gosub Reset_ws2803
For I = 1 To 18
For J = 1 To 8
If I = K Then Data1 = 1 Else Data1 = 0
Gosub Clk_ws2803
Next J
Next I
K = K + 1

For I = 19 To 36
For J = 1 To 8
If I = L Then Data1 = 1 Else Data1 = 0
Gosub Clk_ws2803
Next J
Next I
L = L - 1
Waitms 15
Portb.4 = 0

Loop Until L < 18 Or Timer_wert < 14550 Or Timer_wert > 14951

K = 18 : L = 19
Do
Gosub Reset_ws2803
For I = 1 To 18
For J = 1 To 8
If I = K Then Data1 = 1 Else Data1 = 0
Gosub Clk_ws2803
Next J
Next I
K = K - 1

For I = 19 To 36
For J = 1 To 8
If I = L Then Data1 = 1 Else Data1 = 0
Gosub Clk_ws2803
Next J
Next I
L = L + 1
Waitms 15
Portb.4 = 0

Loop Until L > 36 Or Timer_wert < 14550 Or Timer_wert > 14951

Loop Until Timer_wert < 14550 Or Timer_wert > 14949

End If


'Dimmen aller LEDs
If Timer_wert > 14950 And Timer_wert < Ogrenzetimer Then
Portb.2 = 1
Portb.3 = 1
Greyscale = 250

Do

Gosub Reset_ws2803

For J = 1 To 36
For I = 1 To 8
L = 8 - I
If J > 0 And J < 37 Then Data1 = Greyscale.l Else Data1 = 0
Gosub Clk_ws2803
Next I
Next J
If Greyscale < 125 Then Waitms 1
If Greyscale < 64 Then Waitms 1
If Greyscale < 32 Then Waitms 1
If Greyscale < 16 Then Waitms 1
Greyscale = Greyscale - 2
Portb.4 = 0
Loop Until Greyscale < 2 Or Timer_wert < 14950 Or Timer_wert > Ogrenzetimer

Greyscale = 1

Do

Gosub Reset_ws2803

For J = 1 To 36
For I = 1 To 8
L = 8 - I
If J > 0 And J < 37 Then Data1 = Greyscale.l Else Data1 = 0
Gosub Clk_ws2803
Next I
Next J
If Greyscale < 125 Then Waitms 1
If Greyscale < 64 Then Waitms 1
If Greyscale < 32 Then Waitms 1
If Greyscale < 16 Then Waitms 1
Greyscale = Greyscale + 2
Portb.4 = 0
Loop Until Greyscale > 254 Or Timer_wert < 14950 Or Timer_wert > Ogrenzetimer
End If


Loop
End


'================================================= =====
'Unterprogramme
'================================================= =====



On_int0: 'Interrupt
'Den Timer starten mit steigender Flanke
If Reading = 0 Then
Start Timer1
Reading = 1
If Flashen = 25 Then Portb.4 = 1
If Flashen < 26 Then Flashen = Flashen + 1 Else Flashen = 0
'Den Timer stoppen mit fallender Flanke
Else
Stop Timer1
Timer_wert = Timer1
Timer1 = 0
Reading = 0
End If
'Error-Bit rücksetzen
Error = 0
Return

On_timer1: 'Timer
'Error-Bit setzen
Error = 1
Reading = 0
Stop Timer1
Timer_wert = 0
Return

Reset_ws2803:
Clk1 = 0
Waitus 600
Return

Clear_ws2803:
For J = 1 To 36
For I = 1 To 8
Data1 = 0
Gosub Clk_ws2803
Next I
Next J
Return


Clk_ws2803:
Clk1 = 0
Waitus 1
Clk1 = 1
Waitus 1
Return

Zur Erläuterung:
Ich habe ein Modellflugzeug mit 36 LEDs in den Tragflächen. Diese werden über zwei WS2803 LED-Treiber angesteuert. Die Treiber sind in Reihe geschaltet.
Des Weiteren habe ich fünf 1 - 3 Watt LEDs, die ich über einen Transistor, gesteuert durch den Mega8, ansteuere.
Über meine R/C-Anlage schicke ich über einen freien Kanal ein PWM-Signal wie oben beschrieben an den Interrupt-Pin des Mega8. Dieser Startet bei steigender Flanke des PWM Signals den 16-bit Timer und stoppt ihn bei fallender Flanke.
Über den Timer Wert wird das "Lichtprogramm" bestimmt, welches gewählt wird. D.h. in Abhängigkeit von der Position des Stellers auf meiner R/C-Anlage variiert das PWM-Signal. Dies nutze ich für die Programmwahl.

Portc.0 = Clock-Eingang der WS2803 (zwei in Reihe)
Portc.1 = Dateneingang der WS2803 (zwei in Reihe)
Portb.0-Portb.4=Ausgang für 1-3W LED-Treiber (Transistorschaltung)

Das funktioniert auch alles Prima. Allerdings läuft das Ganze auf dem Tiny13 nicht so wie gewollt. Ich habe erstmal nur das Lauflicht mit 5 LEDs programmiert. Das hat auf dem Tiny funktioniert.

- - - Aktualisiert - - -

Achso, If PinB.1 = 1 abzufragen geht leider nicht. Damit würde ich meine Lichtshow unterbrechen ;-) Daher muss es schon über den Interrupt sein. Du siehst ja wie verwurschtelt das Hauptprogramm ist. Das sind einige Lichtprogramme die abhängig vom Timerwert durchlaufen werden. Wenn ich jetzt die PinB.1 = 1 IF-Abfrage da reinbauen würde, dann würde das ganze Timing nicht mehr passen. Und mit dem Interrupt beim Mega8 funktioniert es ja bestens. Ich verstehe halt nicht warum der Tiny13 da jetzt so rumzickt -.-

for_ro
25.11.2012, 22:17
Achso, If PinB.1 = 1 abzufragen geht leider nicht. Damit würde ich meine Lichtshow unterbrechen ;-) Daher muss es schon über den Interrupt sein. Du siehst ja wie verwurschtelt das Hauptprogramm ist. Das sind einige Lichtprogramme die abhängig vom Timerwert durchlaufen werden. Wenn ich jetzt die PinB.1 = 1 IF-Abfrage da reinbauen würde, dann würde das ganze Timing nicht mehr passen. Und mit dem Interrupt beim Mega8 funktioniert es ja bestens. Ich verstehe halt nicht warum der Tiny13 da jetzt so rumzickt -.-
Ich meinte dies hier:

On_int0: 'Interrupt
'Den Timer starten mit steigender Flanke
If Reading = 0 Then

Ich verstehe das so, dass du dir über die Variable Reading merkst, welche Flanke du gerade hast. Wenn du eine Flanke verpasst, ist die Synchronisation futsch.
Daher würde ich dir raten, es so zu machen

On_int0: 'Interrupt
'Den Timer starten mit steigender Flanke
If PinB.1 = 1 Then

Das wird immer stimmen. Mit Timing und so hat das nichts zu tun.

Edit: Wenn du den Timer genau so konfigurieren möchtest, wie beim M8 dann musst du den Prescale auf 256 setzen.

Poettie
27.11.2012, 18:35
Hallo for_ro,

danke für deinen Tipp. Ich habe das so getestet aber sobald der Interrupt auslöst, habe ich das gleiche unkontrollierte Verhalten.

Ich habe die Interruptroutine testweise auch mal leer gelassen also:



On_int0: 'Interrupt
Return


Sobald ich am Interruptpin meinen RC-Empfänger anschließe, habe ich willkürliches blinken meiner LEDs. Sobald ich den Stöpsel ziehe, habe ich wieder mein Lauflicht ordentlich am Laufen.

Eigentlich darf das nicht sein. Wenn ich alles richtig verstanden habe, dann sollte der Tiny doch bei steigender und fallender Flanke am PINB.1 in die Interruptroutine "On_Int0" hüpfen und dann gleich zum letzten Programmschritt zurück springen, oder?

021aet04
28.11.2012, 08:58
Ich kenne mich zwar nicht so genau mit Empfängern aus, aber könnte es sein das du noch einen Pullupwiderstand brauchst? Versuche einfach einmal einen Pullup von ca. 10k einbauen.

MfG Hannes

Poettie
28.11.2012, 16:53
Leider hilft weder ein Pull-Up noch ein Pull-Down. Der Empfänger ist ein Graupner GR-12 Hott.

Poettie
29.11.2012, 15:19
Ok, Fehler gefunden. Ich habe mir ein Soundkartenoszilloskop gebastelt und heraus gefunden das der Empfänger an dem Kanal kein vernünftiges Signal ausgibt -.-


An einem anderen Kanal lief das Programm dann auch. Trotzdem danke für eure Antworten.