Forum: Compiler & IDEs Atmega8 mit MAX7221 über SPI


von Nook (Gast)


Lesenswert?

Hi,

ist bin ein totaler Anfänger im Bereich Microprozessoren.
Ich will mit einem Atmega8 und einem Max7221 eine LED-Lichtleichte
realisieren. Von der Hardware ist alles Fertig.
Die Verdrahtung:

Atmega8             MAX7221
PIN17 PB3(MOSI) -> PIN1 DIN
PIN19 PB5 (CLK) -> PIN13 CLK
PIN16 PB2 (SS)  -> PIN12 LOAD(CS)

Strom und der Rest ist alles Klar.
So nun habe ich verschiedene Varianten hier gefunden wie man Daten
senden kann aber irgendwie kommt nichts an an dem MAX7221 der Atmel
sendet nix. Wenn ich den Atmel mit PonyProg beschreibe kann ich mit
meinem Messgerät Spannungsschankungen feststellen am MOSI aber der
Atmel selbt sendet nichts.

Das ist mein derzeitiger Code:
_______________________________________________________
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

int main()
{
 DDRB = (1<<PB3)|(1<<PB5); // MOSI,SCK als Output, MISO als Input
 SPCR = (1<<SPE)|(1<<MSTR); // SPI enable, Master Mode

 for(;;)
 {
  SPDR = Fxff; // 10101010
  while(!(SPSR & (1<<SPIF))); // warte bis Byte gesendet
 }
};
_______________________________________________________

Ich hoffe ihr könnt mir helfen. Hab echt keinen plan was ich genau
machen soll.

von Nook (Gast)


Lesenswert?

Kann mir keiner helfen?

von Stefan K. (_sk_)


Lesenswert?

Hi,

Du musst bei der SPI-Schnittstelle den SS-Pin per Hand bedienen. Also:

* vor der Übertragung SS auf Low
* Daten in SPDR schreiben
* warten, bis SPI fertig ist
* SS wieder auf High

Damit überträgst Du EIN Byte an den 7221. Kann sein, dass der 7221
mehrere Bytes hintereinander braucht (->Datenblatt), dann muss das SS
während der ganzen Zeit auf High bleiben.

Was soll bitte diese Zeile:
  SPDR = Fxff; // 10101010
Hast Du Fxff irgendwo definiert - und wie?
Oder hast Du Dich verschrieben und meinst 0xFF? Warum übersetzt dann
gcc korrekt? Wenn Du 0xff ausgibst, dann ändert sich (natürlich) nichts
am MOSI-Pin, der bleibt dann die ganze Zeit High. Bei anderen Werten
wird Messgerät wohl eine konstante Gleichspg anzeigen.

Gruß, Stefan

von Nook (Gast)


Lesenswert?

Danke. das ist mein code zurzeit es scheint sogar was zu passieren =)
Aber die Anzeige zeigt noch nicht das an was ich will.

________________________________________________________
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

void spi_senden(unsigned);

int main()
{
 DDRB = (1<<PB3)|(1<<PB5)|(1<<PB2); // MOSI,SCK als Output
 PORTB =(1<<PB2); //SS auf High
 SPCR = (1<<SPE)|(1<<MSTR); // SPI enable, Master Mode

  spi_senden(0x0F00); // Displaytest ausschalten , Tabelle 9 / 2
  spi_senden(0x0B07); // Anzahl Digits (Segmetbausteine ) Tabelle 7
  spi_senden(0x0C01); // Display auf Normal Operation , Tabelle 3
  spi_senden(0x0900); // Decode-Mode (BCD-CODE für die mitleren 4)
Tabelle 4

 for(;;)
 {
  spi_senden(0x0100);
 }
};


void spi_senden(unsigned daten)
{
  PORTB =(1<<PB2);
  SPDR = daten;
  while(!(SPSR & (1<<SPIF))); // warte bis Byte gesendet
  PORTB =(0<<PB2);
}

von Stefan K. (_sk_)


Lesenswert?

> Aber die Anzeige zeigt noch nicht das an was ich will.

Hmm - daraus ergeben sich gleich 2 Fragen:
* was willst Du?
* was zeigt sie tatsächlich an?
-> Fehler- und Unfallmeldungen immer möglichst präzise ...

Nachdem ich mal im Datenblatt geschaut habe:

der 7221 erwartet 16 Bit SPI-Daten. D.h. Du musst:
* vor der Übertragung SS auf Low
* Daten(high) in SPDR schreiben
* warten, bis SPI fertig ist
* Daten(low) in SPDR schreiben
* warten, bis SPI fertig ist
* SS wieder auf High

Entweder Du übergibst Deinem spi_senden also 2 8-Bit-Werte (High und
Low), was einfacher zu programmieren ist, einen 16-Bit-Wert und trennst
diesen in spi_senden in High- und Low auf.


Etwas ganz anderes:

> PORTB =(1<<PB2);

Im Moment ist es wohl (noch) nicht Dein Problem, aber es kann später zu
einem werden:
Diese Zeile setzt den GESAMTEN Port B, und zwar auf 0000 0100 binär.
Sobald Du die anderen Pins auch benutzst, hast Du ein Problem.
Deshalb:
* zu Setzen eines Portpins verODERn:
  PORTB |= (1<<PB2);
* zum Löschen eines Portpins verUNDen:
  PORTB &= ~(1<<PB2);

Beispiel 1: Portpin setzen
angenommen, PORTB ist 1111 0000 binär.
1 << PB2  entspricht 0000 0100 binär (nämlich 1 um 2 Binärstellen links
geschoben).
PORTB |= (1<<PB2) entspricht:      1111 0000 (Annahme von oben)
                              ODER 0000 0100 (1 << PB2)
                                 = 1111 0100 (neuer Portzustand)

Beispiel 2: Portpin löschen
angenommen, PORTB ist 1111 0100 binär.
(1 << PB2)  entspricht 0000 0100 binär (nämlich 1 um 2 Binärstellen
links geschoben).
~(1 << PB2) entspricht diesem Wert negiert, also 1111 1011 binär.

PORTB &= ~(1<<PB2) entspricht:     1111 0100 (Annahme von oben)
                               UND 1111 1011 ~(1 << PB2)
                                 = 1111 0000 (neuer Portzustand)

Gruß, Stefan

von Nook (Gast)


Lesenswert?

vielen Dank für deine Hilfe!

>der 7221 erwartet 16 Bit SPI-Daten. D.h. Du musst:
>* vor der Übertragung SS auf Low
>* Daten(high) in SPDR schreiben
>* warten, bis SPI fertig ist
>* Daten(low) in SPDR schreiben
>* warten, bis SPI fertig ist
>* SS wieder auf High

>Entweder Du übergibst Deinem spi_senden also 2 8-Bit-Werte (High und
Low), was einfacher zu programmieren ist, einen 16-Bit-Wert und
trennst
>diesen in spi_senden in High- und Low auf.
________________________________________
Mein Code:
void spi_senden(unsigned daten)
{
  PORTB &= ~(1<<PB2);
  SPDR = daten;
  while(!(SPSR & (1<<SPIF))); // warte bis Byte gesendet
  PORTB |=(1<<PB2);
}
________________________________________

ich weiss noch nicht genau was du meinst, dass der einmal high daten
braucht und dann low.?!?
meinst du das so?
________________________________________
Mein Code:
void spi_senden(unsigned daten, unsigned daten1)
{
  PORTB &= ~(1<<PB2);
  SPDR = daten;
  while(!(SPSR & (1<<SPIF))); // warte bis Byte gesendet
  SPDR = daten1;
  while(!(SPSR & (0<<SPIF))); // warte bis Byte gesendet
  PORTB |=(1<<PB2);
}
________________________________________

von Stefan K. (_sk_)


Lesenswert?

PORTB &= ~(1<<PB2);
  SPDR = daten;
  while(!(SPSR & (1<<SPIF))); // warte bis Byte gesendet
  SPDR = daten1;
  while(!(SPSR & (0<<SPIF))); // warte bis Byte gesendet
  PORTB |=(1<<PB2);

Ja, so kannst Du das machen. Sollte eigendlich funktionieren?

Gruß, Stefan

von Nook (Gast)


Lesenswert?

Hi,

wo hast du das eigentlich gefunden im datenblatt mit dem einmal 8Bit
high und dann 8Bit low senden???

irgendwie klappt da nix.

von peter dannegger (Gast)


Angehängte Dateien:

Lesenswert?

Ich benutze den MAX7219 mit Anzeigen mit gemeinsamer Anode.

Anbei ein Beispielcode.


Peter

von Nook (Gast)


Lesenswert?

Hi,

@Peter
Der MAX7219 hat doch kein SPI oder?

Irgendwie raf ich dein code auch net haste keine kurze version?

von peter dannegger (Gast)


Lesenswert?

@Nook,

der MAX7219 ist der Vorgänger des MAX7221.

Die eigentliche Ausgaberoutine ist diese hier:
1
void display_write( u16 dc )
2
{
3
  u8 i;
4
5
  MAX7219_DDR |= 1<<MAX7219_DO;
6
  MAX7219_DDR |= 1<<MAX7219_SCK;
7
  MAX7219_DDR |= 1<<MAX7219_STB;
8
  MAX7219_PORT &= ~(1<<MAX7219_STB);
9
10
  for( i = 12; i; i-- ){
11
    MAX7219_PORT &= ~(1<<MAX7219_SCK);
12
    MAX7219_PORT |= 1<<MAX7219_DO;
13
    if( (dc & 0x0800) == 0 )
14
      MAX7219_PORT &= ~(1<<MAX7219_DO);
15
    dc <<= 1;
16
    MAX7219_PORT |= 1<<MAX7219_SCK;
17
  }
18
19
  MAX7219_PORT |= 1<<MAX7219_STB;
20
}


Der Rest ist nur Initialisierung und 7-Segment Wandlung.


Peter

von Marian (Gast)


Lesenswert?

@Stefan Kleinwort:

PORTB &= ~(1<<PB2);
  SPDR = daten;
  while(!(SPSR & (1<<SPIF))); // warte bis Byte gesendet
  SPDR = daten1;
>>  while(!(SPSR & (0<<SPIF))); // warte bis Byte gesendet
  PORTB |=(1<<PB2);

Muss in der 2. Whileschleife nicht eine 1 anstatt der 0 stehen? Man
wartet auch hier bis das SPI_flag gesetzt ist oder nicht?

von Stefan K. (_sk_)


Lesenswert?

@Marian:

ooops .. natürlich muss da eine 1 stehen, sorry.
Keine Ahnung, wie da die 0 hinkam ...

Viele Grüße, Stefan

von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo an alle

ICh bin ebenfalls daran beschäftigt einen Max 7221 mit 6 LED
Segmentanzeigen zum laufen zu bringen Leider funktioniert es nicht. Es
werden immer alle Leds der Segmentanzeigen eingeschaltet. Und ich weiß
nicht warum :(

Hier der Code:

void spi_init (void)
{
  DDRB |= ((1 << PB3) | (1 << PB5) | (1 << PB2)); //MOSI, SCK, SS
Output, alle anderen Input
  SPCR |= ((1 << SPE) | (1 << MSTR));
}

void spi_senden (uint8_t adress, uint8_t data)
{
  PORTB &= ~(1 << PB2); //SS auf Low
  SPDR = adress;
  while (!(SPSR & (1 << SPIF))); //warten bis gesendet
  SPDR = data;
  while (!(SPSR & (1 << SPIF))); //warten bis gesendet
  PORTB |= (1 << PB2); //SS auf high
}

void segments_init(void)
{
  spi_senden(0x0B, 0x05); //6 Digits 0-5
  spi_senden(0x0F, 0x00); //Display Test ausschaltren
  spi_senden(0x0C, 0x01); //Normal Operation
  spi_senden(0x09, 0xFF); //BCD Code für alle Digits
}
void show_temp(void)
{
  spi_senden(0x01, 0x01);
  spi_senden(0x02, 0x02);
  spi_senden(0x03, 0x03);
  spi_senden(0x04, 0x04);
  spi_senden(0x05, 0x05);
  spi_senden(0x06, 0x06);
}

int main (void)
{
  spi_init();
  i2c_init();
  segments_init();

  spi_senden(0x0A, 0x0C); //Helligkeit
  show_temp();

  while(1)
  {
  }
  return 0;
}

Vielleicht kann mir wer helfen

Danke im Voraus

Gruß Robert

von Nook (Gast)


Lesenswert?

Ich hab auch immer noch das selbe problem wie du auch.

von Didi (Gast)


Lesenswert?

Hi Leute! Hab genau dasselbe Problem. Bei mir werden trotz richtiger 
Initialisierung auch immer alle LEDs angezeigt. Übertragung findet auch 
über SPI statt. Habt ihr schon ne Lösung?
Grüße,
Didi

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.