www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik SPI allgemein, MAX7221


Autor: Tester (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Ich beschäftige mich gerade das erste mal mit SPI.
Ich benutze Codevision einen Mega8515 und einen MAX7221 7-Seg-Treiber.
(http://www.datasheetcatalog.com/datasheets_pdf/M/A...)

Mein einziger Erfolg bisher war, alle Segmente zum Leuchten zu brigen.
Erstens bin ich mir nämlich nicht sicher was ich bei Clock Phase und 
Clock Polarity einstellen muss, also welchen SPI-Mode.
Des weiteren ist mir noch nicht ganz klar, wie ich die 2 x 8 Bit senden 
soll.
Einfach SPI(0xff) und in der nächsten Zeile die zweite 8 bit funkt 
nicht.
Hätte mich eh gewundert.
noch was: Aus dem Datenblatt: "LOAD/CS must go high concurrently with or 
after the
16th rising clock edge, but before the next rising clock
edge or data will be lost." Wie weiß ich wann die 16te Flanke kommt. Mit 
dem Interrupt?

Wäre für ein kurzes Beispiel mit der Codevision-Funktion sehr dankbar!

Autor: Stephan Henning (stephan-)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Tester,
der SPI wird ähnlich wie die UART beschreiben.
Byte in den Puffer und raus geht es. Wenn fertig dann Int und nächstes 
rein.
Kann ich leider nicht genau sagen. Ich habe das bei meinen MAX 7221
zu fuss gemacht, da sie an einem 2051 dran sind. Der hat kein HW SPI.
zu Fuß muß man drauf achten wann die Load/CS Leitung hoch geht.
Eben frühestens mit dem Letzten Bit. Spätestens danach.
Mit der Phase/Polarity hatte ich zu fuß auch nichts zu tun.
Mache ich ja alles selber.
CLR CS, Bit anlegen, Takt, alles 16 Mal oder 32 oder 64 und SETB CS.

ich hoffe das hilft dir.

Gruß

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier meine Routinen die für einen 7219 waren, müsste also auch auf den 
7221 übertragbar sein:
void segWrite (unsigned char data_out){

  unsigned char loop, mask;

  for (loop=0,mask=0x80;loop<8;loop++, mask=mask>>1)

  {

    PORT_SCK_7219 &= ~(1<<PIN_SCK_7219);

    if (data_out & mask)

      PORT_MOSI_7219 |= (1<<PIN_MOSI_7219);

    else

      PORT_MOSI_7219 &= ~(1<<PIN_MOSI_7219);

    PORT_SCK_7219 |= (1<<PIN_SCK_7219);

  }

  PORT_SCK_7219 &= ~(1<<PIN_SCK_7219);

}



void segAusgabe (unsigned char address, unsigned char data) {

  PORT_CS_7219 |= (1<<PIN_CS_7219);    // Chip Select

  segWrite(address);

  segWrite(data);

  PORT_CS_7219 &= ~(1<<PIN_CS_7219);   // Chip Unselect

}



void segInit (void) {

  unsigned char loop;



  DDR_SCK_7219 |= (1<<PIN_SCK_7219);

  DDR_MOSI_7219 |= (1<<PIN_MOSI_7219);

  DDR_CS_7219 |= (1<<PIN_CS_7219);



  for (loop = 0; loop < 4; loop++) {  // 4 mal um Fehler zu eliminieren

    segAusgabe(0x0c, 0x01);  // Shutdown aus

    segAusgabe(0x09, 0xff);  // Decode-Mode: alles auf Code B

    segAusgabe(0x0a, 0x08);  // Intensität

    segAusgabe(0x0b, 0x05);  // 6 digits

  }



  for (loop = 1; loop <= 6; loop++)

    segAusgabe(loop, 0x0A);   // "-" auf allen Anzeigen anzeigen

}

Autor: Georg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Viel spaß damit
/*****************
Konstante Register
******************/
//Hier werden KontrollRegister definiert

#define REG_DECODE 0x09
#define REG_INTENSITY 0x0a
#define REG_SCAN_LIMIT 0x0b
#define REG_SHUTDOWN 0x0c
#define REG_DISPLAY_TEST 0x0f

/*******************
Konstanten Allgemein
********************/
//6 Routinen, um die Ausgangspins high oder low zu setzen 

#define DIN_PORT P4
#define DIN_DDR P4
#define DIN_BIT 0x04   //liegt auf Pin10 der Pfosten-Stiftleiste
#define DIN_0()        (DIN_PORT &= ~DIN_BIT)   //Pin 4.2 auf low
#define DIN_1()        (DIN_PORT |= DIN_BIT)    //Pin 4.2 auf high                      

#define CLK_PORT P4
#define CLK_DDR P4
#define CLK_BIT 0x01   //liegt auf Pin4 der Pfosten-Stiftleiste 
#define CLK_0()        (CLK_PORT &= ~CLK_BIT)    //Pin 4.0 auf low
#define CLK_1()        (CLK_PORT |= CLK_BIT)     //Pin 4.0 auf high

#define LOAD_PORT P4
#define LOAD_DDR P4
#define LOAD_BIT 0x10  //liegt auf Pin7 der Pfosten-Stiftleiste
#define LOAD_0()        (LOAD_PORT &= ~LOAD_BIT) //Pin 4.4 auf low
#define LOAD_1()        (LOAD_PORT |= LOAD_BIT)  //Pin 4.4 auf high

#define INTENSITY_MAX 0x0f      //Konstante fuer maximale Leuchtstaerke 


/*************
Unterprogramme
**************/

void MAX7219_Init (void);
void MAX7219_ShutdownStart (void);
void MAX7219_ShutdownStop (void);
void MAX7219_DisplayTestStart (void);
void MAX7219_DisplayTestStop (void);
void MAX7219_SetBrightness (char brightness);
void MAX7219_Clear (void);
void ziffern (void);

void MAX7219_Write (unsigned char reg_number, unsigned char dataout);

unsigned char z; //wird fuer den NO-OP Befehl des MAX7219 benoetigt






void MAX7219_Init (void)
   {
   // DIN, CLK & LOAD als Ausgang definieren
   DIN_DDR |= DIN_BIT; 
   CLK_DDR |= CLK_BIT;
   LOAD_DDR |= LOAD_BIT;

   MAX7219_Write(REG_DECODE, 0xFF);        //Decode Mode B einschalten fr alle Digits
   MAX7219_SetBrightness(INTENSITY_MAX);   //Helligkeit der Segmente definieren
   MAX7219_Write(REG_SCAN_LIMIT, 0x04);    //5-Digits aktivieren
   MAX7219_ShutdownStop();                 //normaler Betrieb - kein Shutdown Mode
   MAX7219_DisplayTestStart();             //Test Betrieb - alle Digits leuchten!
   }

void MAX7219_ShutdownStart (void)

/* Schaltet das Display ab - Shutdown Mode */
   {
   MAX7219_Write (REG_SHUTDOWN, 0);
   }

void MAX7219_ShutdownStop (void)

/* Bringt das Display aus dem Shutdown Mode */
   {
   MAX7219_Write (REG_SHUTDOWN, 1);
   }

void MAX7219_DisplayTestStart (void)

/* Schaltet alle Segmente ein - Test Mode*/
   {
   MAX7219_Write (REG_DISPLAY_TEST, 1);
   }

void MAX7219_DisplayTestStop (void)

/* Bringt das Display aus dem Test Mode */
   {
   MAX7219_Write (REG_DISPLAY_TEST, 0);
   }

void MAX7219_SetBrightness (char brightness)

/* Setzt die Helligkeit des Displays*/
   {
   brightness &= 0x0f;
   MAX7219_Write (REG_INTENSITY, brightness);
   }

void MAX7219_Clear (void)

/* Loescht das Display*/
   {
   char k;
   for (k=1; k<=5; k++)
      {
      MAX7219_Write (k, 0x00);
      }
   }

void MAX7219_Write (unsigned char reg_number, unsigned char dataout)

/* Hier werden die Daten in die Register der Displays geschrieben - MSB (15) zuerst*/
   {
   char i,g;
   LOAD_0();

   // Datenbits D15-D12 werden beliebig (z.b.: '0') uebertragen:
   for (i=0;i<4;i++)
      {
      DIN_0();
      CLK_1();
      CLK_0();
      }
   // Datenbits D11-D8 (Registeradresse) werden uebertragen:
   for (i=4;i>0;i--)
      {
      unsigned char mask = 1 << (i-1);
      if (reg_number & mask)
         {
         DIN_1();
         }
      else
         {
         DIN_0();
         }
      CLK_1();
      CLK_0();
      }
   // Datenbits D7-D0 (Daten) werden uebertragen:
   for (i=8; i>0; i--)
      {
      unsigned char mask = 1 << (i-1);
      if (dataout & mask)
         {
         DIN_1();
         }
      else
         {
         DIN_0();
         }
      CLK_1();
      CLK_0();
      }
   // No-op Register beschreiben
   if (z>>0)
      {
      for (g=0;g<z;g++)
         {
         for (i=0;i<4;i++ //beliebig (z.b.: '0') uebertragen
             )
            {
            DIN_0();
            CLK_1();
            CLK_0();
            }
         for (i=4;i>0;i--  //In NO-OP Register 0x00 schreiben
             )
            {
            DIN_0();

            CLK_1();
            CLK_0();
            }
         for (i=8;i>0;i-- //beliebig (z.b.: '0') uebertragen
             )
            {
            DIN_0();
            CLK_1();
            CLK_0();
            }
         }
      }
   LOAD_1();
   }

void ziffern (void)
   {
   if (v==1)
      {
      /*Hier findet die Auswertung der Timerwerte die im Feld t1
        gespeichert wurden statt. Jede Ziffer muss aus der Zahl
        mithilfe von Rechenoperationen ermittelt werden*/
 
      unsigned int aa, xx, b1,d1,f1,g1; 
      unsigned long f,ziffer;
      unsigned char fehl;
      
      fehl = 0; //Fehler Zehlvariable 0 setzen
      for (aa=1;aa<=9;aa++)
         {
         if (t1[aa] == 0)
            {
            fehl++;
            }
         }
      if (fehl == 0 //es sind alle beschrieben
         )
         {
         for (aa=1;aa<10;aa++)
            {
            z=aa-1;              
            xx=5;

            f=t1[aa];
            ziffer= f/10000;     
            MAX7219_Write (xx,(unsigned char)ziffer);

            b1=f%10000;      
            ziffer = b1/1000;                  
            xx--;                    
            MAX7219_Write (xx,(unsigned char)ziffer);

            d1=b1%1000;      
            ziffer=d1/100;            
            xx--;                          
            MAX7219_Write (xx,(unsigned char)ziffer);

            f1=d1%100;                     
            ziffer=f1/10;                
            xx--;                          
            MAX7219_Write (xx,(unsigned char)ziffer);

            g1=f1%10;                    
            ziffer=g1;
            xx--;                           
            MAX7219_Write (xx,(unsigned char)ziffer);
            MAX7219_SetBrightness(INTENSITY_MAX);
            }

  

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo.

zur SPI:
CPOL gibt an, ob die erste flanke ("leading edge") steigend oder fallend 
ist.
anders gesagt: CPOL legt den "ruhepegel" des CLK-signals fest. in deinem 
fall ist er LOW.

CPHA gibt an, mit welcher flanke die daten übernommen werden. in deinem 
fall mit der "leading edge", die hier die steigende ist (siehe "Figure 
1. Timing Diagram", seite 6 im datenblatt).

stelle also beide werde auf 0.

der rest läuft dann so:
- SPI konfigurieren (interrupt aktivieren nicht vergessen!)
- erstes datenbyte ins senderegister schreiben
- 1. SPI-Interrupt: evtl. empfangene daten ablegen, dann zweites 
datenbyte schreiben
- 2. SPI-Interrupt: evtl. empfangene daten ablegen, übertragung beendet 
-> chip select wegnehmen

der satz "LOAD/CS must go high concurrently with or after the 16th 
rising clock edge, but before the next rising clock edge or data will be 
lost."
heißt ganz einfach, daß in die LOW-phase des signals "LOAD/CS" maximal 
16 steigende flanken des clock-signals fallen dürfen, sonst werden die 
daten verworfen.

gruß

michael

Autor: Tester (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Guten Morgen ihr Nachtschwärmer!

Danke erstmal. CPOL und CPHA auf 0. Auf das bin ich nach lagem Studium 
des Datenblatts auch gekommen. Aber danke für die Bestätigung.
Die Routinen werd heut Nachmittag probieren. Ich geb dann bescheid...

thks

Autor: Tester (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Ich bringe dieses SCH**** Ding nicht zum laufen. Habe eure Routinen 
probiert.
Funktionieren ebenso wenig wie meine. Es leuchten immer alle Segmente. 
Jeman d einen Idee? Der µC steckt aufm STK500, MAX7221 auf einem 
Steckbrett. Leider bin ich kein Besitzer eines Oszis. Ich würd mir diese 
stinkenden Bits gern mal ansehen...

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tester wrote:
> Ich bringe dieses SCH**** Ding nicht zum laufen. Habe eure Routinen
> probiert.


Ich hätte auch noch eine, läuft wie dumm:

Beitrag "DCF77 Uhr in C mit ATtiny26"

Ist sogar für Anzeigen mit gemeinsamer Anode.


Aber solange Du nichts von Dir zeigst, kann auch keiner Deinen Fehler 
sehen.


Peter

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

Bewertung
0 lesenswert
nicht lesenswert
OK, anhängender code müsste meiner Meinung nach einfach nur mal eine 
Zahl am 7-Seg ausgeben. Aber bei leuchten alle Segmente. auch mit 
Stefans code.
Also dachte ich an ein Hardware-Problem. Komm aber nicht drauf.

Danke für alle Denkanstöße... (oder Lösungen ;-)

Autor: Tester (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wahnsinn, das geht ja schnell hier!

könnte trotzdem bitte mal jemand einen Blick auf den Code werfen?
Oder ists wirklich ein Hardware-Problem?
Danke

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wo ist die Funktion spi() definiert ?

Wo wird der Interrupt enabled und warum erst die Umstände mit dem 
Interrupt ?

Läuft das Programm überhaupt durch ?


Statt tonnenweise Copy&Paste, mach lieber ne Unterfunktion, dann muß man 
sich nicht den gleichen Schnulli wieder und wieder durchlesen.

D.h. der Code wird wesentlich übersichtlicher und fehlerfreier.


Peter


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

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Den Interrupt deswegen, damit er mit dem senden der zweiten 8bit wartet 
bis die ersten fertig sind. Oder ist das nicht notwendig. Ich habs halt 
so gemacht weil es anders nicht funktionierte. Das Programm läuft durch.
(PORTC=Leds vom STK500) aber am Digit steht immer nur "8." Was mache ich 
mit der nicht benutzen Dout-Leitung vom MAX7221?

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.