www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AT89C51AC3 und SPI?


Autor: R. B. (rabis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich versuche die SPI-Schnittstelle des AT89C51AC3 anzusprechen, es kommt 
aber nicht einmal der SPI-clock heraus. Da muss ich wohl noch etwas 
wissen, oder? Die application note von Atmel führt bei mir nicht zum 
Erfolg. Der µC läuft mit 29,4912 Mhz im X2 mode. DAC_CS ist das Chip 
enable
signal für den DAC, das geht wenigstens.

Hier mein Code für die hoffentlich Wissenden:
//--------------------------------------------------------
#include <AT89C51AC3.h>
#include <stdio.h>
//--------------------------------------------------------
// Function: SPI
// Caution! Only one Master without SS -> P3.5 = SS = High

void SPI_Init (void) {
  SS     = ON; 
  DAC_CS = ON;                                 
  SPCON |= MSTR;                  // 0x10 Master mode 
  SPCON |= SPR2 | SPR1 & ~SPR0;   // 0x82 Fclk Periph/128
  SPCON |= SSDIS;                 // 0x20 
  SPCON &= ~CPOL;                 // 0x08 POL=0  transmit mode example 
  SPCON |= CPHA;                  // 0x04 CPHA=1 transmit mode example
        ESPI   = OFF;                   // disable SPI interrupt
  SPCON |= SPEN;                  // 0x40 run SPI
}  
//--------------------------------------------------------

void SPI_transmit(unsigned int spi_data) {
  unsigned char reg80,reg81;
  DAC_CS = OFF;
  _asm
    nop
    nop
    nop
  _endasm;  
  SPDAT = spi_data >> 8;
  while ((SPSCR & SPIF) == 1) ;
        SPDAT = spi_data;
        while ((SPSCR & SPIF) == 1);
  
   DAC_CS = ON;
   _asm
    nop
    nop
    nop
  _endasm;
  reg80 = SPCON; reg81 = SPSCR;
  printf_fast("SPI [SPCON:0x%x,SPCSR:0x%x]\r\n",reg80,reg81);
}
Die Registerwerte: SPCON (0xF6) und SPCSR (0x00).

Vielleicht eine Idee? Die "nop"s sind eigentlich überflüssig.

Gruß
Rabis

Autor: R. B. (rabis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
ich habs, so gehts mit SDCC und ohne Atmel bugs:
//-------------------------------------------------------------------------
#include <AT89C51AC3.h>
//-------------------------------------------------------------------------
// Function: SPI test - SPI master no SS
//
unsigned char serial_data;
unsigned char data_example = 0x55;
unsigned char data_save;
bit           transmit_completed = 0;

//-------------------------------------------------------------------------
// Function: SPI_ISR
//-------------------------------------------------------------------------
//
void SPI_ISR (void) interrupt SPI_VECTOR using 1 { // SPI_VECTOR = 0x0053 !
  switch (SPSCR) {
    case 0x88 : serial_data = SPDAT;      // !
                transmit_completed = 1;
                break;
    case 0x10 : break;
    case 0x40 : break;
  }
}  
//-------------------------------------------------------------------------
// Function: MAIN
//-------------------------------------------------------------------------
//
void main(void) {
  SPCON |=  0x10;                               // SPI Init
  SPCON |=  0x82;
  SPCON |=  0x20;
  SPCON &= ~0x08;
  SPCON |=  0x04;
  IEN1  |=  0x08;                               // enable SPI interrupt !
  SPCON |=  0x40;
  EA     =  1;
  while(1){
    SPDAT  = data_example ;   
    while (!transmit_completed);
    transmit_completed = 0;
    SPDAT  = 0x00;
    while (!transmit_completed);
    transmit_completed = 0;
    data_save = serial_data;  
  }
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------

SS bzw. CS für den Peripheriebaustein einfach über einen Portpin auf 
low.
Richtig sind die Adressen: SPCON 0xD4, SPSCR 0xD5 und SPDAT 0xD6.
"using 1" nutzt Registerbank 1 - muss nicht sein.

Bye
Rabis

Autor: Markus B. (lordnoxx) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
R. B. schrieb:
> void SPI_ISR (void) interrupt SPI_VECTOR using 1 { // SPI_VECTOR = 0x0053 !

Hallo, ich habe gerade genau das gleiche Problem wie du, als du damals 
mit dem AT89C51AC3 gearbeitet hast. Aus dem Datenblatt ist mir nicht 
ganz klar, welche Interruptnummer der SPI-Interrupt hat. Daher würde ich 
dich gerne fragen wie du die Variable  SPI_VECTOR belegt hast.

Getestet habe ich es mal mit SPI_VECTOR=0x53 jedoch will er einfach 
nicht in die ISR rein springen.

Würde mich freuen, wenn du mir hier einen kleinen Tipp geben könntest. 
Danke!

Grüßle

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die ISR-Einsprungadresse ist lt. DB tatsächlich 0x0053, das passt. Die 
IRQ-Nummer bzw. dessen Definition, die dein nicht näher genannter 
Compiler verwendet, auf 0x0053 zu setzen ist definitiv falsch. Die 
Definition müsste eigentlich bereits im Header-File definiert sein, 
schau dort mal nach.

Ralf

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:
> Die IRQ-Nummer bzw. dessen Definition, die dein nicht näher genannter
> Compiler verwendet, auf 0x0053 zu setzen ist definitiv falsch...
... es sei denn, der Compiler erwartet dort tatsächlich die IRQ-Adresse, 
sorry. Der Keil will ne Nummer haben und berechnet die Adresse anhand 
der Nummer.

Ralf

Autor: Markus B. (lordnoxx) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Als Compiler kommt der sdcc zum Einsatz. Habe gleich mal einen Blick in 
mein Headerfile geworfen...konnte aber nichts finden. Habe das File mal 
angehängt.

Autor: Markus B. (lordnoxx) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier mal noch mein bisheriger Code. Das ganze dient der Ansteuerung des 
Displays eDip160 via SPI.
#include <at89c51ac3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char serial_data;
char data_example[10]={0x1B,0x44,0x4C,0x1B,0x47,0x44,0x00,0x00,0x9F,0x67};
char data_save;
bit transmit_completed= 0;

void spi_init();
void SPI_interrupt(void) __interrupt (9) __using (1);
void SPI_send_byte(char byte);
void SPI_send_data(char *buf, char len);

void main(void)
{
CKCON0 = 0x01; //00000000 //X2 Bit setzen
CKCON1 = 0x01; //00000001 SPI CLock Crytal/2;
serial_init();
spi_init();

while(1)
{
 
   SPI_send_data(data_example,sizeof(data_example));

   /*SPDAT=0x00;                //data is send to generate SCK signal
   while(!transmit_completed);//wait end of transmition 
   transmit_completed = 0;    // clear software transfert flag 
   data_save = serial_data;   // save receive data 
   */
}

}

void SPI_send_data(char *buf, char len)
{
  char i,bcc;
  SPI_send_byte(0x11);
  bcc=0x11;
  SPI_send_byte(len);
  bcc=bcc+len;
  
  for (i=0;i<len;i++)
  {
    SPI_send_byte(buf[i]);
    bcc=bcc+buf[i];
  }
  
  SPI_send_byte(bcc);
}

void SPI_send_byte(char byte)
{
  SPDAT=byte;                /* send an example data */
  while(!transmit_completed) /* wait end of transmition */
  {
     //P1_0=0;  
  }
  
  transmit_completed = 0;    /* clear software transfert flag */
}

void SPI_interrupt(void) __interrupt (0x0053) __using (1)
{
  //P1_0=0;
  switch  ( SPSCR )         /* read and clear spi status register */
  {
    case 0x80:
                     serial_data=SPDAT;   /* read receive data */
                     transmit_completed=1;/* set software flag */
     break;

    case 0x10:
         /* put here for mode fault tasking */  
    break;
  
    case 0x40:
         /* put here for overrun tasking */  
    break;
  }
}

void spi_init()
{
  SPCON |= 0x10;                /* Master mode */
  SPCON |= 0x82;                /* Fclk Periph/128 */
  SPCON |= 0x20;                /* P1.1 is available as standard I/O
pin */
  SPCON &= ~0x08;               /* CPOL=0; transmit mode example */
  SPCON &= ~0x04;               /* CPHA=0; transmit mode example */
  ESPI = 1;                     /* enable spi interrupt */
  SPCON |= 0x40;                /* run spi */
        EA=1;                         /* enable interrupts */
}


Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Als Compiler kommt der sdcc zum Einsatz. Habe gleich mal einen Blick in
> mein Headerfile geworfen...konnte aber nichts finden. Habe das File mal
> angehängt.
Stimmt, da ist nix drin bzgl. der ISR-Vektoren.

> Hier mal noch mein bisheriger Code.
Du hast Unterschiede zwischen ISR-Prototyp und -Funktion (braucht der 
SDCC denn einen ISR-Prototyp?).
Der Rest sieht auf einen schnellen Blick stimmig aus, wobei ich 
mindestens das transmit_completed-Flag als volatile deklarieren würde.

> jedoch will er einfach nicht in die ISR rein springen.
Wie hast du das geprüft? Kommt denn überhaupt n SPI-Clock oder so raus? 
Dann weisst du das wenigstens das SPI-Interface wirklich an ist.
Und ob der Interrupt wirklich ausgelöst werden kann, kannst du prüfen, 
indem du testweise einfach mal das SPIF-Flag setzt, dann muss er den IRQ 
ausführen.

Ralf

Autor: Markus B. (lordnoxx) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ralf schrieb:
> Du hast Unterschiede zwischen ISR-Prototyp und -Funktion (braucht der

Ja, das sehe ich gerade auch :-) muss dann wohl beim rumprobieren 
passiert sein. Aber ich kann versichern, dass ich die genannte 
Problematik auch schon hatte, als ISR-Prototyp und -Funktion den 
gleichen Kopf hatten.

>> jedoch will er einfach nicht in die ISR rein springen.
> Wie hast du das geprüft? Kommt denn überhaupt n SPI-Clock oder so raus?
> Dann weisst du das wenigstens das SPI-Interface wirklich an ist.
> Und ob der Interrupt wirklich ausgelöst werden kann, kannst du prüfen,
> indem du testweise einfach mal das SPIF-Flag setzt, dann muss er den IRQ
> ausführen.

SPI-Clock und MOSI Signal sind da. Habe ich mit Oszi geprüft.
SPI_send_byte(0x11); führt er noch aus. Konnte auf dem Oszi die Bitfolge 
00010001 also dezimal 17 erkennen. Aber danach kommt nichts mehr. Obwohl 
ja noch einige Bytes im Buffer sind.

Über das schalten einer LED am Pin P1.0 konnte herausfinden wo genau er 
hängen bleibt. Habe dazu in die obigen Funktionen das Setzen des Ports 
auf Null eingebaut:
void SPI_send_byte(char byte)
{
  SPDAT=byte;                /* send an example data */
  while(!transmit_completed) /* wait end of transmition */
  {
     P1_0=0;  
  }
  P1_0=1;
  transmit_completed = 0;    /* clear software transfert flag */
}

Hier ging die LED an P1.0 dann an und blieb an! Das heißt für mich er 
klemmt in der While-Schleife fest.
Dann habe ich die beiden Zeilen mit P1_0 wieder auskommentiert und dafür 
das nachfolgende getestet.

void SPI_interrupt(void) __interrupt (9) __using (1)
{
  P1_0=0;
  switch  ( SPSCR )         /* read and clear spi status register */
  {
    case 0x80:
                     serial_data=SPDAT;   /* read receive data */
                     transmit_completed=1;/* set software flag */
     break;

    case 0x10:
         /* put here for mode fault tasking */  
    break;
  
    case 0x40:
         /* put here for overrun tasking */  
    break;
  }
}

Der Pin wird hier ja eigentlich gleich zu Beginn der ISR gesetzt. Doch 
die LED bleibt aus. Daher kam ich zu dem Schluss, dass er nicht in die 
ISR springt.

Autor: Bernhard Spitzer (b_spitzer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Interrupt-Nummer bei 8051-Kompatiblen errechnet sich eigentlich aus
( Einsprungadresse - 3 ) / 8
Bei 0x53 müsste daher eigenlich die Interrupt-Nummer 10 sein... In 
meinem (funktionierenden Code für den AT89C5131 habe ich 
Interrupt-Nummer 9 mit Adresse 0x4B drinstehen...
/***************************************************************
* spi_basis.c                                  *
*                                            *
* Version: 1.0                                  *
*                                          *
* Nach ATMEL Application Note                        *
* http://www.atmel.com/dyn/resources/prod_documents/doc4348.pd  *
***************************************************************/

/* I N C L U D E S */
#include <at89c5131.h>
#include <spi_basis.h>

char spi_data;          // Globale Variable
bit transmit_completed= 0;

#define SS_Pin P1_1

/**
* FUNKTION: SPI_init()
* Fclk Periph/128 als Baud Rate, Slave Select Pin deaktiviert
* FUNCTION_INPUTS: P1.5(MISO) serial input
* FUNCTION_OUTPUTS: P1.7(MOSI) serial output
*/
// Bitmuster für SPI-Baudratenteiler
#define SP128 0x82
#define SP64 0x81
#define SP32 0x80
#define SP16 0x03
#define SP8 0x02
#define SP4 0x01

// SPCON:  SPI Konfiguration,  nicht bitadressierbar
// Bit    7     6    5    4    3    2    1    0
// Name  SPR2  SPEN  SSDIS  MSTR  CPOL  CPHA  SPR1  SPR0
// Funktionen Baudrate mit SPR2..0
// Baugruppe einschalten mit SPEN
// SS-Pin freigeben mit SSDIS
// Master Mode mit MSTR
// SPI-Mode mit CPOL:CPHA
void SPI_init(void)
{
  SPCON |= 0x10;   // Master Mode
  SPCON |= 0x20;   // SS-Pin-Umschaltung deaktiveren
  SS_Pin =  1;     // SS-Pin setzen (Slave deaktivieren)
  SPCON &= 0x74;   // Taktrate setzten. Zuerst Bits 7,1,0 löschen
  SPCON |= SP128;     // Bits 7,1,0 setzen (Hier Teiler 128 => 48kBit/s)
  
  SPCON &= ~0x08;   // CPOL=0; Modus 0 für SD-Karte
  SPCON &= ~0x04;   // CPHA=0; 
  IE1   |= 0x04;   // Freigabe SPI Interrupt (IEN1 ist nicht Bitadressierbar)
  SPCON |= 0x40;   // SPI einschalten
  EA    =  1;     // Globale Freigabe
}

/* Auf hohe Geschwindigkeit schalten */
void SPI_fullspeed(void)
{
  SPCON &= 0x74;   // Taktrate setzten. Zuerst Bits 7,1,0 löschen
  SPCON |= SP32;     // Bits 7,1,0 setzen (Hier Teiler 4 => 1,5 MBit/s)
}


/**
* FUNCTION: SPI_ISR
* FUNCTION_INPUTS: void
* FUNCTION_OUTPUTS: transmit_complete is software transfert flag
*/
void SPI_ISR(void) interrupt 9 /* interrupt address is 0x004B */
{
  switch( SPSTA ) /* read and clear spi status register */
    {
    case 0x80:
      // Zugriff auf SPDAT löscht den Interrupt
      spi_data = SPDAT;     /* read receive data */
      transmit_completed = 1;  /* set software flag */
      break;
    case 0x10:
      /* put here for mode fault tasking */
      SPCON &= 0xBF;        // SPEN löschen
      SPCON |= 0x40;        // SPEN setzen
      break;
    case 0x40:
      /* put here for overrun tasking */
      break;
    }
}

/**
* FUNCTION: SPI_Read_Write
* FUNCTION_INPUTS: Sendedaten
* FUNCTION_OUTPUTS: Empfangsdaten
* Slave wird hier nicht enabled!
*/
char SPI_Read_Write(char daten)
{
  SPDAT = daten;              // Daten Senden initialisieren
  while (!transmit_completed);    // warte auf SPI_ISR
  transmit_completed = 0;        // Flag löschen
  //spi_data = SPDAT;            // Daten abholen
  return spi_data;// globale Variable
}

/**
* FUNCTION: SPI_Write_Byte
* FUNCTION_INPUTS: Sendedaten
* FUNCTION_OUTPUTS: void
*/
void SPI_Write_Byte(char daten)
{
  SS_Pin = 0;                // Slave enable  
  SPI_Read_Write(daten);        // nur Daten senden
  SS_Pin = 1;                // Slave disable
  return;
}

/**
* FUNCTION: SPI_Read_Byte
* FUNCTION_INPUTS: void
* FUNCTION_OUTPUTS: Empfangsdaten
*/
char SPI_Read_Byte(void)
{  char daten;
  SS_Pin = 0;                // Slave enable  
  daten = SPI_Read_Write(0x00);    // Nulldaten => Takt erzeugen
  SS_Pin = 1;                // Slave disable
  return daten;
}


tschuessle
Bernhard

Autor: Bernhard Spitzer (b_spitzer)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Markus,

>   ESPI = 1;                     /* enable spi interrupt */
Das hier kommt kam mir gerade komisch vor. Beim 5131 ist das obere 
Freigaberegister nicht bitadressierbar, bei Deinem Typ ist es auf einer 
bitadressierbaren Adresse, sollte also in Ordnung sein.
Da hat Atmel doch einige Unterschiede zwischen den Derivaten eingebaut!


tschuessle
Bernhard

Autor: Markus B. (lordnoxx) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bernhard Spitzer schrieb:
> Die Interrupt-Nummer bei 8051-Kompatiblen errechnet sich eigentlich aus
> ( Einsprungadresse - 3 ) / 8
> Bei 0x53 müsste daher eigenlich die Interrupt-Nummer 10 sein...

Hallo Bernhard...danke dir, das war der entscheidende Hinweis. Mit 
Interrupt Nummer 10 geht es jetzt auch bei mir. :-)

Grüße

Autor: Robin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo ,

ich möchte gerne mit einem 89c51cc03 ein ediptft43 von Elektronik
Assembly mit dem spi-bus verbinden und dem display etwas senden bwz ich 
habe auf dem Display Instrumenten von denen ich den Zeiger bewegen 
möchte leider habe ich in C keinen Plan.
Über hilfe freue ich mich sehr.

Grüße Robin

Autor: Markus B. (lordnoxx) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Robin schrieb:
> Hallo ,
>
> ich möchte gerne mit einem 89c51cc03 ein ediptft43 von Elektronik
> Assembly mit dem spi-bus verbinden und dem display etwas senden bwz ich
> habe auf dem Display Instrumenten von denen ich den Zeiger bewegen
> möchte leider habe ich in C keinen Plan.
> Über hilfe freue ich mich sehr.
>
> Grüße Robin

So leid es mir tut, aber da wirst du nicht umhin kommen, dich erstmal in 
C einzuarbeiten. Es sei denn, du kannst Assembler. Aber da kann ich dir 
dann aich nicht mehr Helfen.

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.