mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Schieberegister, SPI und XOR


Autor: helle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen!

Ich habe eine ganz kurze Frage, bei der ich mir nicht ganz sicher bin 
und mir darum einfach ein paar weitere Meinungen einholen will.

Vorweg: ich habe vor zwei Schieberegister (HC595)am SPI von einem 
Atmega8 zu betreiben. An jedem Ausgang der Schieberegister hängt eine 
LED (15 mA). Jetzt hab ich das so verschaltet, daß die Ausgänge 0-3 an 
den Anoden von vier LEDs hängen und die Ausgänge 4-7 an den Kathoden der 
anderen LEDs (wegen max. 70 mA durch Vcc bzw. GND).
Wenn ich jetzt ein Byte angezeigt haben möchte und das MSB zuerst 
übertragen wird muß ich ja die obersten vier Bit invertieren. Und genau 
DA bin ich mir nicht sicher, wie das geht.
Hab das jetzt so implementiert:

SPDR = (data ^ 0xF0);
Ist das richtig? Mit den Logikbefehlen komm ich immer etwas 
durcheinander...

Und nochwas:
im Tutorium wird ja so invertiert: PORTB ^= (1<<PB3);
müßte das nicht auch so gehen: PORTB |= !(1<<PB3);

Danke schonmal!

helle :)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
helle schrieb:

> Hab das jetzt so implementiert:
>
> SPDR = (data ^ 0xF0);
> Ist das richtig? Mit den Logikbefehlen komm ich immer etwas
> durcheinander...

Perfekt

> Und nochwas:
> im Tutorium wird ja so invertiert: PORTB ^= (1<<PB3);
> müßte das nicht auch so gehen: PORTB |= !(1<<PB3);

Nein.
Wenn schon, dann

   PORTB |= ~(1<<PB3);

Aber auch das toggelt nicht, sondern setzt alle Bits in PORTB ausser PB3 
(bei PB3 hängt es davon ab, welchen Zustand PB3 vor der Operation hatte. 
Dieser wird nicht verändert). Einfach mal mit irgendeinem Byte 
durchspielen und du wirst sehen, was da passiert.


! ist die logische Negierung.
Alles was nicht 0 ist wird zu 1. Alles was 0 ist wird zu 1
Das geht aber auf ein Byte als Ganzes und nicht auf einzelne Bits.

Du musst unterscheiden zwischen den logischen Operationen und den 
Bitoperationen.
Die logischen Operationen sehen ein Byte als eine Einheit an, die 
entweder 0 oder nicht 0 ist. zb 5. 5 ist perfekt um als TRUE zu gelten, 
da es nicht 0 ist. !5 ergibt aber 0. (weil 5 ja nicht 0 war. Daher ist 
die Negierung dazu die 0)
Die Bitoperationen arbeiten auf den einzelnen Bits eines Bytes.

log. Operationen   && || !      (und oder nicht)
Bitoperationen     &  |  ^  ~   (und oder xor nicht)

Autor: helle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Super, danke für die Erklärung :) !

Autor: helle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh oh, jetzt hab ich doch noch ein Problem!

Hab jetzt alles soweit richtig verdrahtet und ein Testprogramm 
(Lauflicht, bietet sich ja an) auf den Controller gezogen.

Aber: bei dem "Übergang" vom 1. zum 2. Register klappt das nicht so, wie 
ich mir das vorgestellt hab. Und zwar hat Pin 1 vom zweiten SR immer den 
gleichen Zustand wie der letzte Pin vom ersten SR. Ich hab im Tutorium 
und im Datenblatt zwar gelesen, daß das eben so ist, aber wie soll das 
kaskadieren wie im Tutorium dann funktionieren?

Ich will 16 Bit per SPI übertragen. Dazu ruf ich die Funktionen so auf:
for(char i = 0; i < 16; i++)
    {
      // oberes Byte übertragen
                        data = (x >> 8);
      transmit_byte_spi(data);
      
                        // unteres Byte übertragen
                        data = (speed & 0xFF);
      transmit_byte_spi(data);

      _delay_ms(60);
      x <<= 1;
    }

int transmit_byte_spi(unsigned char data)
{
  // die nötigen I/Os zu Ausgängen machen
  spi_ddr |= ((1<<serial_data) | (1<<shift_clock) | ~(1<<output_sr));
  enable_spi;
  disable_spi_pwm;

  SPDR = (data ^ 0xF0);

  while( !(SPSR & (1<<SPIF)) )
  {asm("NOP");}

        spi_ddr |= (1<<output_sr);
  asm("NOP");

  PORTB &= ~(1<<output_sr);
  asm("NOP");
  PORTB |= (1<<output_sr);
  asm("NOP");

  disable_spi;
  enable_spi_pwm;

return transmit_cycles;
}

Die Ganzen Macros sind mit #defines hinterlegt. Der Ablauf ist ja 
eigentlich recht klar:
oberes Byte übertragen
warten
Ausgabe (Puls an RCK)
unteres Byte übertragen
warten
Ausgabe (Puls an RCK)

Hab auch schon versucht die Ausgabe erst nach Übertragung der vollen 16 
Bit zu machen, aber das hat nix geändert. Der Übergang vom 1. zum 2. SR 
passt nicht richtig... Wäre echt toll, wenn mir da einer einen Tipp 
geben könnt!

Danke,
helle

Autor: Vlad Tepesch (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
woz denn beide enden der led auf SRs?

häng doch das eine an masse oder plus.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hier

  spi_ddr |= ((1<<serial_data) | (1<<shift_clock) | ~(1<<output_sr));


macht nicht das was du denkst.
Man kann ein Bit nicht mit einem | löschen.


Tu dir selbst einen Gefallen:
Konfiguriere die DDRx einmal ganz am Programmanfang und lass sie dann in 
Ruhe! Du 'künstelst' im Moment zuviel noch ehe die Funktinoalität 
grundsätzlich steht.

Autor: helle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi!
Sorry, ich weiß grad nicht, was du meinst. Wie die LEDS angeklemmt sind 
steht in dem ersten Post. 4 pro SR activ-high, und vier sind active-low. 
Das ist die Schaltung, wie unter
http://www.mikrocontroller.net/articles/AVR-Tutori...

Nur 1) genau umgekehrt, also die Pins 0-3 active-high und die Pins 4-7 
active-low,
und 2) um ein weiteres SR erweitert.

Autor: helle (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
@Karl Heinz:

ja, danke für den Hinweis. Das war nicht so gemeint. Eigentlich sollte 
der Pin output_sr (s.o.) auch gesetzt werden. Ich fummel immer an den 
DDRx-Registern rum, damit sie auch garantiert an der richtigen Stelle im 
Programm die richtige Richtung haben.
Hab es korrigiert, aber daran lag es nicht.
Hab davon mal ein Video gemacht. So richtig erklären kann ich es mir 
noch nicht...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
helle schrieb:

> ja, danke für den Hinweis. Das war nicht so gemeint. Eigentlich sollte
> der Pin output_sr (s.o.) auch gesetzt werden. Ich fummel immer an den
> DDRx-Registern rum, damit sie auch garantiert an der richtigen Stelle im
> Programm die richtige Richtung haben.

Wie gesagt: Setzte sie einmal am Programmanfang.
Sonst landest du nämlich ganz schnell in der Situation, dass das DDR 
falsch steht und du massenhaft Code durchforsten musst, um rauszufinden, 
wo du dich vertan hast.
Setzt du sie aber nur am Anfang, dann brauchst du auch nur an einer 
Stelle kontrollieren.

(Natürlich gibt es auch Ausnahmen. Wenn ein Pin zwischendurch kurz 
umgeschaltet werden muss, wie zb in einem TWI Interface)

> Hab es korrigiert, aber daran lag es nicht.

Ist mir beim drüberlesen nur aufgefallen.

Autor: helle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe irgendwie so das Gefühl, daß der logische Ablauf bei mir noch 
nicht ganz richtig ist. Aber ich komm einfach nicht drauf, wo das sei 
könnte. Mache ich noch einen Fehler bei der Ausgabe der SR-Werte? Mir 
kommt das eigentlich richtig vor. Das durchschieben übernimmt ja das 
CLK-Signal vom SPI und wenn das fertig übertragen hat (also immer nach 
dem 8. bit) geb ich das aus. Oder?
In dem Video entsprechen die LEDs von links nach rechts den Bits 0-15 
von der int-Variablen, die Ausgegeben werden soll.
Im Moment steh ich aufm Schlauch...

VG,
helle

Autor: helle (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, ich muß nochmal nerven, weil ich im Moment echt nicht so richtig 
weiter komme. Es geht immernoch um das Ansteuern von zwei kaskadierten 
Schieberegistern via SPI. Der Übergang vom 1. zum 2. Schieberegister 
will einfach nicht so richtig funktionieren, obwohl ich schon mit den 
CPOL- und CPHA-Bits gespielt habe, wie unter 
http://www.mikrocontroller.net/articles/Porterweit... 
beschrieben.

Habe nochmal meinen vollständigen Quellcode und zwei Videos beigefügt. 
Das erste Video ist mit CPOL=CPHA=0, das zweite mit CPOL=CPHA=1.
#define serial_data        PB3
#define shift_clock        PB5
#define output_sr        PB2
#define enable_spi        (SPCR |= (1<<SPE))
#define disable_spi        (SPCR &= ~(1<<SPE))

void init_ports()
{
  DDRB = 0xFF;  // Output for SPI

  PORTB |= (1<<output_sr);
}

void init_spi()
{
  // controller is SPI-"master", communication mode as in:
  // http://www.mikrocontroller.net/articles/Porterweiterung_mit_SPI
  SPCR |= (1<<SPE);    // enable SPI
  SPCR |= (1<<MSTR);
  //SPCR |= (1<<CPOL);
  //SPCR |= (1<<CPHA);

  SPSR |= (1<<SPI2X);  // full speed, fcpu/2 -> 4 MHz
}

int main()
{
  init_ports();
  init_spi();

  clear_shift_registers();

  unsigned char data;

  while(1)
  {
    x = 1;

    for(char i = 0; i < 16; i++)
    {
      data = (x >> 8);
      transmit_byte_spi(data);

      data = (x & 0xFF);
      transmit_byte_spi(data);

      output_sr_latch();

      x <<= 1;

      _delay_ms(300);
    }
  }
}

void output_sr_latch()
{
  PORTB &= ~(1<<output_sr);
  asm("NOP");

  PORTB |= (1<<output_sr);
}

void clear_shift_registers()
{
  transmit_byte_spi(0x00);
  transmit_byte_spi(0x00);
  output_sr_latch();
}

void transmit_byte_spi(unsigned char data)
{  
  enable_spi;

  SPDR = data;

  while( !(SPSR & (1<<SPIF)) )
  {asm("NOP");}

  disable_spi;
}

Hoffe nochmal, daß jemand Mitleid hat und da drüberguckt...

Danke,
Gruß,
Helle

Autor: helle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, ich finds ein bißchen schade, daß keiner Lust hat mir zu helfen. 
Obwohl ich versucht habe alle Infaormationen, die dafür wichtig sein 
könnten, anzugeben. Wenn noch was fehlen sollte mach ich natürlich gern 
noch mehr Angaben. Aber z.Z. ist bei mir einfach Ende. Und wenn man 
allein nicht weiterkommt, ist doof, kennt ihr ja bestimmt auch...
Naja, ok, ich probier noch etwas rum...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.