Forum: Mikrocontroller und Digitale Elektronik Schieberegister, SPI und XOR


von helle (Gast)


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

von Karl H. (kbuchegg)


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)

von helle (Gast)


Lesenswert?

Super, danke für die Erklärung :) !

von helle (Gast)


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:
1
for(char i = 0; i < 16; i++)
2
    {
3
      // oberes Byte übertragen
4
                        data = (x >> 8);
5
      transmit_byte_spi(data);
6
      
7
                        // unteres Byte übertragen
8
                        data = (speed & 0xFF);
9
      transmit_byte_spi(data);
10
11
      _delay_ms(60);
12
      x <<= 1;
13
    }
14
15
int transmit_byte_spi(unsigned char data)
16
{
17
  // die nötigen I/Os zu Ausgängen machen
18
  spi_ddr |= ((1<<serial_data) | (1<<shift_clock) | ~(1<<output_sr));
19
  enable_spi;
20
  disable_spi_pwm;
21
22
  SPDR = (data ^ 0xF0);
23
24
  while( !(SPSR & (1<<SPIF)) )
25
  {asm("NOP");}
26
27
        spi_ddr |= (1<<output_sr);
28
  asm("NOP");
29
30
  PORTB &= ~(1<<output_sr);
31
  asm("NOP");
32
  PORTB |= (1<<output_sr);
33
  asm("NOP");
34
35
  disable_spi;
36
  enable_spi_pwm;
37
38
return transmit_cycles;
39
}

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

von Vlad T. (vlad_tepesch)


Lesenswert?

woz denn beide enden der led auf SRs?

häng doch das eine an masse oder plus.

von Karl H. (kbuchegg)


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.

von helle (Gast)


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-Tutorial:_Schieberegister

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.

von helle (Gast)


Angehängte Dateien:

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

von Karl H. (kbuchegg)


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.

von helle (Gast)


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

von helle (Gast)


Angehängte Dateien:

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/Porterweiterung_mit_SPI 
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.
1
#define serial_data        PB3
2
#define shift_clock        PB5
3
#define output_sr        PB2
4
#define enable_spi        (SPCR |= (1<<SPE))
5
#define disable_spi        (SPCR &= ~(1<<SPE))
6
7
void init_ports()
8
{
9
  DDRB = 0xFF;  // Output for SPI
10
11
  PORTB |= (1<<output_sr);
12
}
13
14
void init_spi()
15
{
16
  // controller is SPI-"master", communication mode as in:
17
  // http://www.mikrocontroller.net/articles/Porterweiterung_mit_SPI
18
  SPCR |= (1<<SPE);    // enable SPI
19
  SPCR |= (1<<MSTR);
20
  //SPCR |= (1<<CPOL);
21
  //SPCR |= (1<<CPHA);
22
23
  SPSR |= (1<<SPI2X);  // full speed, fcpu/2 -> 4 MHz
24
}
25
26
int main()
27
{
28
  init_ports();
29
  init_spi();
30
31
  clear_shift_registers();
32
33
  unsigned char data;
34
35
  while(1)
36
  {
37
    x = 1;
38
39
    for(char i = 0; i < 16; i++)
40
    {
41
      data = (x >> 8);
42
      transmit_byte_spi(data);
43
44
      data = (x & 0xFF);
45
      transmit_byte_spi(data);
46
47
      output_sr_latch();
48
49
      x <<= 1;
50
51
      _delay_ms(300);
52
    }
53
  }
54
}
55
56
void output_sr_latch()
57
{
58
  PORTB &= ~(1<<output_sr);
59
  asm("NOP");
60
61
  PORTB |= (1<<output_sr);
62
}
63
64
void clear_shift_registers()
65
{
66
  transmit_byte_spi(0x00);
67
  transmit_byte_spi(0x00);
68
  output_sr_latch();
69
}
70
71
void transmit_byte_spi(unsigned char data)
72
{  
73
  enable_spi;
74
75
  SPDR = data;
76
77
  while( !(SPSR & (1<<SPIF)) )
78
  {asm("NOP");}
79
80
  disable_spi;
81
}

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

Danke,
Gruß,
Helle

von helle (Gast)


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

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.