Forum: Mikrocontroller und Digitale Elektronik Problem mit SPI gesteuerter Soft-PWM


von Flo K. (Gast)


Angehängte Dateien:

Lesenswert?

Guten Tag µC-Profis,

folgendes Problem quält mich:

Ich habe ein System aus zwei ATmega8515. Einer fungiert als SPI-Master, 
der andere als Slave. Der Master sendet ständig via SPI 8-Bit 
Helligkeitswerte an den Slave, welcher diese in Soft-PWM umwandeln 
sollte.

Die Kommunikation über SPI funktioniert wunderbar. Als Soft-PWM Vorlage 
habe ich diesen Code benutzt 
(http://www.mikrocontroller.net/articles/Soft-PWM) welcher eigenständig 
auch super läuft. Habe den ein bisschen geändert.

Wenn ich nun die beiden Controller verbinde sieht man (STK500), dass was 
passiert, jedoch nicht das, was ich erwarte.

Die angesprochene LED blinkt/leuchtet nicht in der Helligkeit, welche 
übertragen wird.

Ich selbst vermute, dass das ein Interrupt-Problem ist. Ich bin aber zu 
unerfahren, um das Problem sofort zu erkennen. Im Anhang findet Ihr den 
Code für den Slave.

Vielleicht findet von Euch jemand den Fehler. Der Slave sollte lediglich 
den via SPI übertragenen Wert in ein PWM-Signal übersetzen.

Vielen Dank

Gruß

Flo

von Falk B. (falk)


Lesenswert?

@Flo K. (Gast)

>Die angesprochene LED blinkt/leuchtet nicht in der Helligkeit, welche
>übertragen wird.

Kann sie auch schlecht, wen du die Daten nicht ins Array kopierst.
Dein Array t1 wird bei jedem Schleifendurchlauf neu intitialisiert. Und 
gewöhn dir diesen Mist mit Variablendefinition mitten im Programm ab! 
Das ist schlicht unnötig. Variablendefinitionen gehören an den 
Funktionsanfang.
Probier mal das.

1
  while(1)
2
  {    
3
      uint8_t port_tmp;
4
      
5
      SPI_SlaveReceive();
6
7
      port_tmp = SPDR;
8
      t1[0] = port_tmp;
9
10
      memcpy(pwm_setting, t1, 8);
11
      pwm_update();
12
  }

MFG
Falk

von chester (Gast)


Lesenswert?

> Und gewöhn dir diesen Mist mit Variablendefinition mitten im Programm ab!
> Das ist schlicht unnötig. Variablendefinitionen gehören an den Funktionsanfang.

Das kann ich so nicht stehenlassen. Ich denke schon, dass es
Sinn macht Variablen so "spät" als möglich zu deklarieren.

von Falk B. (falk)


Lesenswert?

@ chester (Gast)

>Das kann ich so nicht stehenlassen. Ich denke schon, dass es
>Sinn macht Variablen so "spät" als möglich zu deklarieren.

Sehe ich nicht so. Das bringt nur Kauderwelsch rein, z.B. die IMO 
unsägliche Definition der Laufvariable in for Schleifen. Igitt!

Wenn man die Variablen kompakt am Funktionsanfang definiert hat man 
einen guten Überblick und es gibt keine Doppeldefinitionen, mit denen 
man sich in Blöcken etc. ins Knie schiessen kann.

MFG
Falk

von Flo K. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Falk & Co.,

danke für die Antwort. War durchaus brauchbar.

Ich habe nun den Code etwas geändert:

while(1)
{
   uint8_t port_tmp;
   uint8_t t1[8];
   SPI_SlaveReceive();

   port_tmp = SPDR;
   t1[0] = port_tmp;

   memcpy(pwm_setting, t1, 8);
   pwm_update();
}


Noch einige Infos zu meinen Versuchen: Ich bin dran, einen DMX-Empfänger 
dazu zu bringen, einzelne Dantenbytes via SPI an Slaves zu senden. Der 
DMX-Empfang und die PWM arbeiten tadellos. Nur wenn ich einzelne Werte 
via SPI an den Slave sende, gibts Probleme.

Wenn ich nämlich ständig den gleichen DMX-Wert empfange und diesen auch 
sende, ist das Ergebnis am PWM-Ausgang ein zyklisch zappelnder 
PWM-Pegel, der sich nicht nachvollziehen lässt.

Ist das ein Timing-Problem?

von Michael Wilhelm (Gast)


Lesenswert?

> while(!(SPSR & (1<<SPIF)));

Das würde ich aus der Senderoutine rausnehmen und das Flag im 
Hauptprogramm abfragen. Wenn die Hardware bereit ist und sich der 
DMX-Wert geändert hat, würde ich den neuen Wert senden.

MW

von Flo K. (Gast)


Lesenswert?

Uiuiui

Vielen Dank Michael.

Bitte Entschuldige meine Unwissenheit, aber könntest Du mir das genauer 
erklären. So quasi Schritt für Schritt was da vor sich geht?

Danke

von Michael Wilhelm (Gast)


Lesenswert?

In der Senderoutine wartest du, bis das Byte rausgeshiftet wurde. 
Während dieser Wartezeit können andere Aktionen, außer 
Interruptfunktionen, nicht ausgeführt werden. Also frag das SPIF im 
Hauptprogramm ab, ob das Byte schon geschrieben wurde.

Weiterhin ist es nur µC Belastung, gleiche Werte wiederholt zu senden. 
Das ist nicht notwendig. Wenn du mal mehr Sklaven ansprechen willst, du 
einen Datenstrom von 512 Byte mit der Geschwindigkeit von 44 
Wiederholungen in der Sekunde hast, wirst du froh sein um jede 
vermiedene Wartezeit.

MW

von Flo K. (Gast)


Lesenswert?

Wenn ich Deinem Vorschlag nachgehe, sieht doch mein Programm 
folgendermaßen aus:

********************************************************************
int main(void)
{
  cli();
  DDRA = 0xff;  //Ausgänge
  DMX_initialisieren();
  SPI_MasterInit();

  sei();

  //Arbeitsschleife
  while(1)
  {

    DIPs_auslesen();

    SPI_MasterTransmit(DMX_WERT[1]);

    while(!(SPSR & (1<<SPIF)));

  }
}


//Funktionen
void SPI_MasterInit(void)
{
  /* Set MOSI and SCK output, all others input */
  DDRB = (1<<DDB5)|(1<<DDB4)|(1<<DDB7);
  /* Enable SPI, Master, set clock rate fck/16 */
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<DORD)|(1<<SPI2X)|(1<<SPR0);
}


void SPI_MasterTransmit(char cData)
{
  /* Start transmission */
  SPDR = cData;
  /* Wait for transmission complete */
}

********************************************************************


Das ist doch aber nichts anderes als vorher, oder?

Liegt das Problem nicht vielmehr darin, dass das Hauptprogramm ständig 
von der Interruptroutine des DMX-Empfangs unterbrochen wird? Womöglich 
an ungünstigen Stellen und dadurch das Durcheinander entsteht? Oder 
verstehe ich das falsch.

Ich müsste es irgendwie schaffen, dass ein DMX-Wert empfangen wird, 
dieser übergeben und gesendet und dann erst wieder ein Wert empfangen 
werden darf. Zeitlich sehe ich da nicht so sehr das Problem, weil der 
Slave nur maximalst 20 Kanäle über SPI empfangen soll.

Vorschläge? Meinungen?

von Michael Wilhelm (Gast)


Lesenswert?

Was ich meinte, ist die Wartezeit ganz rauszunehmen. In etwa in main 
schreiben:

if (SPSR & (1 << SPIF)) und dann erst senden, wenn der aktuelle DMX-Wert 
anders ist als der zuletzt gesendete.

MW

von Flo K. (Gast)


Lesenswert?

OK!

Das entspricht aber leider nicht meiner Anwendung.

Im Idealfall sollten DMX-Werte empfangen werden und sofort über SPI an 
den Slave gesendet werden. Und das ganze ständig.

D. h. ein Vektor von z. B. DMX-Kanal 1 bis 10 soll ständig empfangen und 
sofort gesendet werden. Egal, ob sich Werte ändern oder nicht.

Dazu eine Idee?

von Michael Wilhelm (Gast)


Lesenswert?

Du sendest mit Clock/8. Das bedeutet, bei Quarz von 16 MHz sendet du mit 
2 MBaud. Das ist wesentlich schneller als DMX. Dann fütter das SPDR 
direkt in der Empfangsroutine. Bis der nächste Uart-Int kommt, ist die 
Hardware mit dem Senden fertig. Finde ich aber unnötig, wiederholt die 
gleichen Daten zu senden.

MW

von chester (Gast)


Lesenswert?

@ Falk
> Wenn man die Variablen kompakt am Funktionsanfang definiert hat man
> einen guten Überblick und es gibt keine Doppeldefinitionen, mit denen
> man sich in Blöcken etc. ins Knie schiessen kann.

Der Nachteil liegt auf der Hand. Je länger die Lebensdauer einer 
Variable ist, umso mehr Speicher (Stack) wird statistisch benötigt.
Es wird eigentlich in jedem guten Buch (zB: Scott Meyers) darauf 
hingewiesen, lokale Variablen so spät als möglich anzulegen.

Nach deiner Argumentation wäre es dann eigentlich noch besser,
ausschließlich globale Variablen zu verwenden.

chester

von Flo K. (Gast)


Lesenswert?

Hallo Michael,
hallo µC Gemeinde,

ich habe nun den Master so weit, dass er das mach, was ich will. D. h. 
er empfängt acht DMX-Kanäle, sendet diese sofort via SPI zum Slave und 
dann wieder von Vorne.

Nun stehe ich vor dem nächsten Problem:
Wie bringe ich den Slave dazu, diese acht Werte zu isolieren und 
jeweiligen Variablen zuzuweisen, die dann die PWM-Werte darstellen oder 
anderweitig verwendet werden?

Der verwendete Code ist dem ersten Artikel dieses Threads zu entnehmen. 
Es wird dabei wahrscheinlich so sein, dass es Konflikte zwischen der 
Empfangsroutine und dem Interrupt des Timers gibt.

Kann mir jemand Tips geben, wie ich das Ganze umbauen muss?

Vor allem die Arbeitsschleife?

while(1)
{
uint8_t port_tmp;
uint8_t t1[8];
SPI_SlaveReceive();

port_tmp = SPDR;
t1[0] = port_tmp;

memcpy(pwm_setting, t1, 8);
pwm_update();
}


Ich bin für jeden Tip sehr dankbar.

Gruß

Flo

von Falk B. (falk)


Lesenswert?

@ chester (Gast)

>Der Nachteil liegt auf der Hand. Je länger die Lebensdauer einer
>Variable ist, umso mehr Speicher (Stack) wird statistisch benötigt.

Ich behaupte mal ganz kess, dass ein gescheiter Compiler das allein 
richtig macht und optimiert, EGAL wo die Variablen in der Funktion 
definiert werden.

>Nach deiner Argumentation wäre es dann eigentlich noch besser,
>ausschließlich globale Variablen zu verwenden.

Käse. Hat kein Mensch behauptet.

von chester (Gast)


Lesenswert?

@ falk

> Ich behaupte mal ganz kess, dass ein gescheiter Compiler das allein
> richtig macht und optimiert, EGAL wo die Variablen in der Funktion
> definiert werden.

DAS kann ein Compiler nicht. Der Linker vergibt den Speicher...

aber ich seh schon, das bringt nichts. nichts für ungut.

von Falk B. (falk)


Lesenswert?

@  chester (Gast)

>> Ich behaupte mal ganz kess, dass ein gescheiter Compiler das allein
>> richtig macht und optimiert, EGAL wo die Variablen in der Funktion
>> definiert werden.

>DAS kann ein Compiler nicht. Der Linker vergibt den Speicher...

Das meine ich nicht. Es geht NICHT um globale/lokale Variablen, sondern 
den Definitionspunkt INNERHALB einer Funktion, welche EINEN Scope 
darstellt. Also auch keine Blöcke innerhalb einer Funktion.

>aber ich seh schon, das bringt nichts. nichts für ungut.

Ahhhh, der Meister ist sich zu fein, um mit dem Fussvolk zu sprechen. 
Ich bin unwürdig . . .

MFG
Falk

P S Wer sich selbst erhöht soll erniedrigt, und wer sich selbst 
erniedrigt soll erhöht werden.

von hans (Gast)


Lesenswert?

@Falk

Ich bin auf deiner Seite und damit auch unwürdig.
Wenn ich im Code eine Variable sehe schaue ich nach oben, zum
Kopf der Funktion, der Main oder des Programmes. Vorne (!) steht
wie die Variable definiert ist. Keine Suche um zu wissen, ob
z.B. v1&=0x1234 geht oder v1 ein char ist.

Aber wer Zeit zum Suchen hat !

gruß hans

von Simon K. (simon) Benutzerseite


Lesenswert?

chester wrote:
> @ Falk
>> Wenn man die Variablen kompakt am Funktionsanfang definiert hat man
>> einen guten Überblick und es gibt keine Doppeldefinitionen, mit denen
>> man sich in Blöcken etc. ins Knie schiessen kann.
>
> Der Nachteil liegt auf der Hand. Je länger die Lebensdauer einer
> Variable ist, umso mehr Speicher (Stack) wird statistisch benötigt.
> Es wird eigentlich in jedem guten Buch (zB: Scott Meyers) darauf
> hingewiesen, lokale Variablen so spät als möglich anzulegen.

Willkommen in der Neuzeit, wo Compiler so gut optimieren, wie noch nie 
zu vor.

Wie schon gesagt, wird der Compiler die Zeit, in der die Variable 
effektiv genutzt wird, erkennen und danach den Speicher einteilen.
Übrigens werden (zumindest bei Architekturen mit so vielen Registern wie 
dem AVR) lokale Variablen kaum auf dem Stack angelegt (erst, wenn die 
Arbeitsregister ausgehen).

Übrigens: Wenn du Recht haben würdest, warum empfiehlt man dann, 
möglichst eine lokale Variable für einen Zweck anzulegen und keine 
Sammelvariablen ala "temp" zu benutzen um da ganz verschiedene Typen von 
Ergebnissen drin zu speichern?

von chester (Gast)


Lesenswert?

Es kann kein Compiler wissen, ob zur Laufzeit der
else-Zweig einer if-Anweisung genommen wird oder nicht.

Ist die Variable erst im else-Zweig deklariert, dann
ist sie eben nur dann existent, wenn reingehüpft wird.

Hast du viele solche Konstellationen, dann denk ich,
dass es Vorteile bringt, mehr nicht.

Ich habe mich nur gegen die generelle Aussage gewehrt:

> gewöhn dir diesen Mist mit Variablendefinition mitten im Programm ab!

Dass sich jemand dadurch emotional angegriffen fühlt,
oder sich gar unwürdig fühlt, war von mir nicht gewollt.

chester

von Simon K. (simon) Benutzerseite


Lesenswert?

chester wrote:
> Es kann kein Compiler wissen, ob zur Laufzeit der
> else-Zweig einer if-Anweisung genommen wird oder nicht.
Hä?

Was ist denn die Alternative zu
1
void func(int Param1)
2
{
3
    int Bla;
4
5
    if(Param1)
6
        Bla = 8;
7
    else
8
        Bla = -4;
9
}

> Ist die Variable erst im else-Zweig deklariert, dann
> ist sie eben nur dann existent, wenn reingehüpft wird.
Wenn du die Variable erst im else-Zweig deklarierst, kann der if-Zweig 
nicht darauf zugreifen! Sprich: Du brauchst die Variable nur im 
else-Zweig, was den Compiler wieder die Speicheroptimierung erhöht.
Du hast dir gerade selbst ins Bein geschossen.

> Hast du viele solche Konstellationen, dann denk ich,
> dass es Vorteile bringt, mehr nicht.
Welche Konstellationen? Gib mal ein wenig Beispielcode.

> Ich habe mich nur gegen die generelle Aussage gewehrt:
>
>> gewöhn dir diesen Mist mit Variablendefinition mitten im Programm ab!
Schon klar. Trotzdem bin (auch) ich noch der Meinung, dass diese 
generelle Aussage Sinn macht.

von chester (Gast)


Lesenswert?

z.B:
1
if (alles_gut)
2
{
3
  led = gruen;
4
}
5
else
6
{
7
  failure_struct f1;
8
  f1 . a =
9
  f1 . b = 
10
  :
11
  .
12
  f1 . z = 
13
  print_failure(f1);
14
}

von Flo K. (Gast)


Lesenswert?

Guten Morgen zusammen,

es fällt mir immer wieder auf, dass es in diesem Forum anscheinend gang 
und gäbe ist, dass so mancher seine Minderwertigkeitskomplexe auf der 
Tastatur ausleben muss.

Ich habe nicht diesen Thread eröffnet, um zu erfahren wo und wann welche 
scheiß Variable wie definiert ist.

Bitte kauft Euch Bücher in denen das Thema behandelt wird aber langweilt 
doch bitte nicht mit diesem Dünnschiss!!!

Ich wollte mit diesem Thread von Profis Lösungen für mein Problem 
bekommen. Habt Ihr denn gar nichts anderes zu tun?

Hier mein letzter Eintrag der eigentlich ein wenig zielführender sein 
sollte:

************************************************************************ 
**
Hallo Michael,
hallo µC Gemeinde,

ich habe nun den Master so weit, dass er das mach, was ich will. D. h.
er empfängt acht DMX-Kanäle, sendet diese sofort via SPI zum Slave und
dann wieder von Vorne.

Nun stehe ich vor dem nächsten Problem:
Wie bringe ich den Slave dazu, diese acht Werte zu isolieren und
jeweiligen Variablen zuzuweisen, die dann die PWM-Werte darstellen oder
anderweitig verwendet werden?

Der verwendete Code ist dem ersten Artikel dieses Threads zu entnehmen.
Es wird dabei wahrscheinlich so sein, dass es Konflikte zwischen der
Empfangsroutine und dem Interrupt des Timers gibt.

Kann mir jemand Tips geben, wie ich das Ganze umbauen muss?

Vor allem die Arbeitsschleife?

while(1)
{
uint8_t port_tmp;
uint8_t t1[8];
SPI_SlaveReceive();

port_tmp = SPDR;
t1[0] = port_tmp;

memcpy(pwm_setting, t1, 8);
pwm_update();
}


Ich bin für jeden Tip sehr dankbar.

Gruß

Flo
************************************************************************ 
**

Und ich bitte inständig darum, dass Themenbezogene Artikel eingestellt 
werden.

Vielen Dank

Flo

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.