Forum: Mikrocontroller und Digitale Elektronik AT89C51AC3 und SPI?


von R. B. (rabis)


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:
1
//--------------------------------------------------------
2
#include <AT89C51AC3.h>
3
#include <stdio.h>
4
//--------------------------------------------------------
5
// Function: SPI
6
// Caution! Only one Master without SS -> P3.5 = SS = High
7
8
void SPI_Init (void) {
9
  SS     = ON; 
10
  DAC_CS = ON;                                 
11
  SPCON |= MSTR;                  // 0x10 Master mode 
12
  SPCON |= SPR2 | SPR1 & ~SPR0;   // 0x82 Fclk Periph/128
13
  SPCON |= SSDIS;                 // 0x20 
14
  SPCON &= ~CPOL;                 // 0x08 POL=0  transmit mode example 
15
  SPCON |= CPHA;                  // 0x04 CPHA=1 transmit mode example
16
        ESPI   = OFF;                   // disable SPI interrupt
17
  SPCON |= SPEN;                  // 0x40 run SPI
18
}  
19
//--------------------------------------------------------
20
21
void SPI_transmit(unsigned int spi_data) {
22
  unsigned char reg80,reg81;
23
  DAC_CS = OFF;
24
  _asm
25
    nop
26
    nop
27
    nop
28
  _endasm;  
29
  SPDAT = spi_data >> 8;
30
  while ((SPSCR & SPIF) == 1) ;
31
        SPDAT = spi_data;
32
        while ((SPSCR & SPIF) == 1);
33
  
34
   DAC_CS = ON;
35
   _asm
36
    nop
37
    nop
38
    nop
39
  _endasm;
40
  reg80 = SPCON; reg81 = SPSCR;
41
  printf_fast("SPI [SPCON:0x%x,SPCSR:0x%x]\r\n",reg80,reg81);
42
}
Die Registerwerte: SPCON (0xF6) und SPCSR (0x00).

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

Gruß
Rabis

von R. B. (rabis)


Lesenswert?

Hi,
ich habs, so gehts mit SDCC und ohne Atmel bugs:
1
//-------------------------------------------------------------------------
2
#include <AT89C51AC3.h>
3
//-------------------------------------------------------------------------
4
// Function: SPI test - SPI master no SS
5
//
6
unsigned char serial_data;
7
unsigned char data_example = 0x55;
8
unsigned char data_save;
9
bit           transmit_completed = 0;
10
11
//-------------------------------------------------------------------------
12
// Function: SPI_ISR
13
//-------------------------------------------------------------------------
14
//
15
void SPI_ISR (void) interrupt SPI_VECTOR using 1 { // SPI_VECTOR = 0x0053 !
16
  switch (SPSCR) {
17
    case 0x88 : serial_data = SPDAT;      // !
18
                transmit_completed = 1;
19
                break;
20
    case 0x10 : break;
21
    case 0x40 : break;
22
  }
23
}  
24
//-------------------------------------------------------------------------
25
// Function: MAIN
26
//-------------------------------------------------------------------------
27
//
28
void main(void) {
29
  SPCON |=  0x10;                               // SPI Init
30
  SPCON |=  0x82;
31
  SPCON |=  0x20;
32
  SPCON &= ~0x08;
33
  SPCON |=  0x04;
34
  IEN1  |=  0x08;                               // enable SPI interrupt !
35
  SPCON |=  0x40;
36
  EA     =  1;
37
  while(1){
38
    SPDAT  = data_example ;   
39
    while (!transmit_completed);
40
    transmit_completed = 0;
41
    SPDAT  = 0x00;
42
    while (!transmit_completed);
43
    transmit_completed = 0;
44
    data_save = serial_data;  
45
  }
46
}
47
//-------------------------------------------------------------------------
48
//-------------------------------------------------------------------------

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

von Markus B. (lordnoxx) Benutzerseite


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

von Ralf (Gast)


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

von Ralf (Gast)


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

von Markus B. (lordnoxx) Benutzerseite


Angehängte Dateien:

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.

von Markus B. (lordnoxx) Benutzerseite


Lesenswert?

Hier mal noch mein bisheriger Code. Das ganze dient der Ansteuerung des 
Displays eDip160 via SPI.
1
#include <at89c51ac3.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
char serial_data;
6
char data_example[10]={0x1B,0x44,0x4C,0x1B,0x47,0x44,0x00,0x00,0x9F,0x67};
7
char data_save;
8
bit transmit_completed= 0;
9
10
void spi_init();
11
void SPI_interrupt(void) __interrupt (9) __using (1);
12
void SPI_send_byte(char byte);
13
void SPI_send_data(char *buf, char len);
14
15
void main(void)
16
{
17
CKCON0 = 0x01; //00000000 //X2 Bit setzen
18
CKCON1 = 0x01; //00000001 SPI CLock Crytal/2;
19
serial_init();
20
spi_init();
21
22
while(1)
23
{
24
 
25
   SPI_send_data(data_example,sizeof(data_example));
26
27
   /*SPDAT=0x00;                //data is send to generate SCK signal
28
   while(!transmit_completed);//wait end of transmition 
29
   transmit_completed = 0;    // clear software transfert flag 
30
   data_save = serial_data;   // save receive data 
31
   */
32
}
33
34
}
35
36
void SPI_send_data(char *buf, char len)
37
{
38
  char i,bcc;
39
  SPI_send_byte(0x11);
40
  bcc=0x11;
41
  SPI_send_byte(len);
42
  bcc=bcc+len;
43
  
44
  for (i=0;i<len;i++)
45
  {
46
    SPI_send_byte(buf[i]);
47
    bcc=bcc+buf[i];
48
  }
49
  
50
  SPI_send_byte(bcc);
51
}
52
53
void SPI_send_byte(char byte)
54
{
55
  SPDAT=byte;                /* send an example data */
56
  while(!transmit_completed) /* wait end of transmition */
57
  {
58
     //P1_0=0;  
59
  }
60
  
61
  transmit_completed = 0;    /* clear software transfert flag */
62
}
63
64
void SPI_interrupt(void) __interrupt (0x0053) __using (1)
65
{
66
  //P1_0=0;
67
  switch  ( SPSCR )         /* read and clear spi status register */
68
  {
69
    case 0x80:
70
                     serial_data=SPDAT;   /* read receive data */
71
                     transmit_completed=1;/* set software flag */
72
     break;
73
74
    case 0x10:
75
         /* put here for mode fault tasking */  
76
    break;
77
  
78
    case 0x40:
79
         /* put here for overrun tasking */  
80
    break;
81
  }
82
}
83
84
void spi_init()
85
{
86
  SPCON |= 0x10;                /* Master mode */
87
  SPCON |= 0x82;                /* Fclk Periph/128 */
88
  SPCON |= 0x20;                /* P1.1 is available as standard I/O
89
pin */
90
  SPCON &= ~0x08;               /* CPOL=0; transmit mode example */
91
  SPCON &= ~0x04;               /* CPHA=0; transmit mode example */
92
  ESPI = 1;                     /* enable spi interrupt */
93
  SPCON |= 0x40;                /* run spi */
94
        EA=1;                         /* enable interrupts */
95
}

von Ralf (Gast)


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

von Markus B. (lordnoxx) Benutzerseite


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:
1
void SPI_send_byte(char byte)
2
{
3
  SPDAT=byte;                /* send an example data */
4
  while(!transmit_completed) /* wait end of transmition */
5
  {
6
     P1_0=0;  
7
  }
8
  P1_0=1;
9
  transmit_completed = 0;    /* clear software transfert flag */
10
}

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.

1
void SPI_interrupt(void) __interrupt (9) __using (1)
2
{
3
  P1_0=0;
4
  switch  ( SPSCR )         /* read and clear spi status register */
5
  {
6
    case 0x80:
7
                     serial_data=SPDAT;   /* read receive data */
8
                     transmit_completed=1;/* set software flag */
9
     break;
10
11
    case 0x10:
12
         /* put here for mode fault tasking */  
13
    break;
14
  
15
    case 0x40:
16
         /* put here for overrun tasking */  
17
    break;
18
  }
19
}

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.

von Bernhard S. (b_spitzer)


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...
1
/***************************************************************
2
* spi_basis.c                                  *
3
*                                            *
4
* Version: 1.0                                  *
5
*                                          *
6
* Nach ATMEL Application Note                        *
7
* http://www.atmel.com/dyn/resources/prod_documents/doc4348.pd  *
8
***************************************************************/
9
10
/* I N C L U D E S */
11
#include <at89c5131.h>
12
#include <spi_basis.h>
13
14
char spi_data;          // Globale Variable
15
bit transmit_completed= 0;
16
17
#define SS_Pin P1_1
18
19
/**
20
* FUNKTION: SPI_init()
21
* Fclk Periph/128 als Baud Rate, Slave Select Pin deaktiviert
22
* FUNCTION_INPUTS: P1.5(MISO) serial input
23
* FUNCTION_OUTPUTS: P1.7(MOSI) serial output
24
*/
25
// Bitmuster für SPI-Baudratenteiler
26
#define SP128 0x82
27
#define SP64 0x81
28
#define SP32 0x80
29
#define SP16 0x03
30
#define SP8 0x02
31
#define SP4 0x01
32
33
// SPCON:  SPI Konfiguration,  nicht bitadressierbar
34
// Bit    7     6    5    4    3    2    1    0
35
// Name  SPR2  SPEN  SSDIS  MSTR  CPOL  CPHA  SPR1  SPR0
36
// Funktionen Baudrate mit SPR2..0
37
// Baugruppe einschalten mit SPEN
38
// SS-Pin freigeben mit SSDIS
39
// Master Mode mit MSTR
40
// SPI-Mode mit CPOL:CPHA
41
void SPI_init(void)
42
{
43
  SPCON |= 0x10;   // Master Mode
44
  SPCON |= 0x20;   // SS-Pin-Umschaltung deaktiveren
45
  SS_Pin =  1;     // SS-Pin setzen (Slave deaktivieren)
46
  SPCON &= 0x74;   // Taktrate setzten. Zuerst Bits 7,1,0 löschen
47
  SPCON |= SP128;     // Bits 7,1,0 setzen (Hier Teiler 128 => 48kBit/s)
48
  
49
  SPCON &= ~0x08;   // CPOL=0; Modus 0 für SD-Karte
50
  SPCON &= ~0x04;   // CPHA=0; 
51
  IE1   |= 0x04;   // Freigabe SPI Interrupt (IEN1 ist nicht Bitadressierbar)
52
  SPCON |= 0x40;   // SPI einschalten
53
  EA    =  1;     // Globale Freigabe
54
}
55
56
/* Auf hohe Geschwindigkeit schalten */
57
void SPI_fullspeed(void)
58
{
59
  SPCON &= 0x74;   // Taktrate setzten. Zuerst Bits 7,1,0 löschen
60
  SPCON |= SP32;     // Bits 7,1,0 setzen (Hier Teiler 4 => 1,5 MBit/s)
61
}
62
63
64
/**
65
* FUNCTION: SPI_ISR
66
* FUNCTION_INPUTS: void
67
* FUNCTION_OUTPUTS: transmit_complete is software transfert flag
68
*/
69
void SPI_ISR(void) interrupt 9 /* interrupt address is 0x004B */
70
{
71
  switch( SPSTA ) /* read and clear spi status register */
72
    {
73
    case 0x80:
74
      // Zugriff auf SPDAT löscht den Interrupt
75
      spi_data = SPDAT;     /* read receive data */
76
      transmit_completed = 1;  /* set software flag */
77
      break;
78
    case 0x10:
79
      /* put here for mode fault tasking */
80
      SPCON &= 0xBF;        // SPEN löschen
81
      SPCON |= 0x40;        // SPEN setzen
82
      break;
83
    case 0x40:
84
      /* put here for overrun tasking */
85
      break;
86
    }
87
}
88
89
/**
90
* FUNCTION: SPI_Read_Write
91
* FUNCTION_INPUTS: Sendedaten
92
* FUNCTION_OUTPUTS: Empfangsdaten
93
* Slave wird hier nicht enabled!
94
*/
95
char SPI_Read_Write(char daten)
96
{
97
  SPDAT = daten;              // Daten Senden initialisieren
98
  while (!transmit_completed);    // warte auf SPI_ISR
99
  transmit_completed = 0;        // Flag löschen
100
  //spi_data = SPDAT;            // Daten abholen
101
  return spi_data;// globale Variable
102
}
103
104
/**
105
* FUNCTION: SPI_Write_Byte
106
* FUNCTION_INPUTS: Sendedaten
107
* FUNCTION_OUTPUTS: void
108
*/
109
void SPI_Write_Byte(char daten)
110
{
111
  SS_Pin = 0;                // Slave enable  
112
  SPI_Read_Write(daten);        // nur Daten senden
113
  SS_Pin = 1;                // Slave disable
114
  return;
115
}
116
117
/**
118
* FUNCTION: SPI_Read_Byte
119
* FUNCTION_INPUTS: void
120
* FUNCTION_OUTPUTS: Empfangsdaten
121
*/
122
char SPI_Read_Byte(void)
123
{  char daten;
124
  SS_Pin = 0;                // Slave enable  
125
  daten = SPI_Read_Write(0x00);    // Nulldaten => Takt erzeugen
126
  SS_Pin = 1;                // Slave disable
127
  return daten;
128
}

tschuessle
Bernhard

von Bernhard S. (b_spitzer)


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

von Markus B. (lordnoxx) Benutzerseite


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

von Robin (Gast)


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

von Markus B. (lordnoxx) Benutzerseite


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.

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.