Forum: Compiler & IDEs AD-Wandler Max157 SPI


von Stefan T. (stefan90)


Lesenswert?

Hallo zusammen,

ich habe nun schon länger vergeblich den MAX157 über SPI mit meinem 
Mega8 anzusteueren. Ich weiß leider nicht ob die Receive so 
funktioniert, denn damit hatte ich noch nirgends Erfolg.

Ich hab nun in der Funktion SPI_Master_ReceiveInit das geschrieben, was 
ich auf Seite 11 im Datenblatt von Maxim finde. Ich hoffe das das so 
funktionieren sollte, leider bleibt er aber immer in der while Schleife 
von SPI_MasterReceive hängen.

Hier der Code:
1
#define MOSI   PB3
2
#define SCK  PB5
3
#define MISO   PB4
4
#define CS   PB1
5
6
void bin_ausgabe(uint8_t byte);
7
8
void SPI_Master_ReceiveInit(void)
9
{
10
  DDRB |= (1<<CS) | (1<<SCK);
11
  DDRB &= ~(1<<MISO);
12
  
13
  PORTB &= ~(1<<SCK);
14
  PORTB &= ~(1<<CS);
15
  _delay_us(3);
16
  
17
  DDRB &= ~(1<<SCK);
18
  
19
  /* Enable SPI, Master, set clock rate fck/16 */
20
  SPCR = (1<<SPE) | (1<<MSTR);
21
  SPSR = (1<<SPR0);
22
}
23
24
//empfängt 2 Bytes
25
void SPI_MasterReceive(uint8_t buffer[])
26
{
27
  PORTB &= ~(1<<CS);
28
29
  /* Wait for reception complete */
30
  while(!(SPSR & (1<<SPIF)));
31
  buffer[0] = SPDR;
32
  
33
  /* Wait for reception complete */
34
  while(!(SPSR & (1<<SPIF)));
35
  buffer[1] = SPDR;
36
  
37
  PORTB |= (1<<CS);
38
}
39
40
void bin_ausgabe(uint8_t byte)
41
{
42
  uint8_t i;
43
  
44
  for(i=0; i<8; i++)
45
  {
46
    if(byte & (1 << (7-i)))
47
      lcd_putc('1');
48
    else
49
      lcd_putc('0');
50
  }
51
}
52
53
int main (void)
54
{
55
  uint8_t bytes[2], i;
56
  
57
  lcd_init(I2C_LCD_DISP_ON);
58
59
  SPI_Master_ReceiveInit();
60
  SPI_MasterReceive(bytes);
61
  
62
  bin_ausgabe(bytes[0]);
63
  lcd_putc('\n');
64
  bin_ausgabe(bytes[1]);
65
  
66
  return 0;
67
}

Ich hoffe, dass mir jemand weiterhelfen kann

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Du musst erst etwas auf SPDR ausgeben (zum Beispiel eine 0), bevor
du etwas empfangen kannst.  SPI sendet und empfängt immer zugleich,
sodass man etwas senden muss, um etwas zu empfangen.

von Stefan T. (stefan90)


Lesenswert?

Ok danke, wie sieht das aber konkret aus?
D.h. ich sende eine null (oder nur SPDR=0 schreiben?) und kann dann 
gleich beide bytes auslesen? oder dazwischen dann nochmal was senden?

Warum aber genau ist das so? Das versteh ich noch nicht wirklich...

Gruß Stefan

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan Tschiggerl wrote:

> D.h. ich sende eine null (oder nur SPDR=0 schreiben?)

Genau so, SPDR = 0 sendet eine Null.

> und kann dann
> gleich beide bytes auslesen?

Nein, das erste.

> oder dazwischen dann nochmal was senden?

Ja, nochmal, vermutlich wieder eine Null.

> Warum aber genau ist das so? Das versteh ich noch nicht wirklich...

Weil das Ganze weiter nichts als ein serielles Schieberegister ist.
Damit du etwas aus dem Schieberegister rausschieben kannst, musst du
auch zugleich was reinschieben.  Erst das SPDR = 0 wirft nämlich die
SPI-Maschine überhaupt an, sodass sie beginnt, (acht) Taktimpulse
auszusenden.  Der Inhalt der gesendeten Daten wird in diesem Falle
wohl vom SPI-Slave einfach komplett ignoriert.

von Stefan T. (stefan90)


Lesenswert?

So das hab ich jetzt gemacht und ich kann auch Daten empfangen, leider 
hab ich nur immer 11111111 11111111, was nicht stimmen kann.

ich hab jetzt folgendes:

void SPI_MasterReceive(uint8_t buffer[])
{
  PORTB &= ~(1<<CS);
  DDRB |= (1<<MOSI);
  SPDR=0;

  /* Wait for reception complete */
  while(!(SPSR & (1<<SPIF)));
  buffer[0] = SPDR;

  SPDR=0;

  /* Wait for reception complete */
  while(!(SPSR & (1<<SPIF)));
  buffer[1] = SPDR;

  PORTB |= (1<<CS);
}

war das so gemeint? Nur funktioniert leider noch nicht...

Gruß Stefan

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Im Prinzip ist das OK.  Jetzt stolperst du vermutlich über das
,,Kleingedruckte''.

Deine Wahl, PB1 als Chipselect für den Slave zu nehmen, ist recht
ungünstig.  Wenn man ihn nicht anders benötigt, ist dafür eigentlich
immer der /SS-Anschluss (beim ATmega8 also PB2) die erste Wahl: im
Masterbetrieb muss dieser Anschluss entweder ein Ausgang sein (dann
hat er keinen Einfluss auf die SPI-Logik), oder falls er ein Eingang
ist, dient er der dynamischen Umschaltung des Masters auf Slave.  In
diesem Falle muss PB2 garantiert auf High sein (entweder durch die
externe Beschaltung oder zumindest durch den Pullup, falls keine
externe Beschaltung dran ist), damit es sich um einen SPI-Master
handelt.  Wie gesagt, die erste Wahl ist es, diesen Pin einfach als
Ausgang zu beschalten und als Chipselect für den (oder einen, falls es
mehrere davon gibt) SPI-Slave zu nutzen.

Außerdem hat das ständige Herumfummeln an der Datenrichtung des
SPI-Interfaces keinen rechten Sinn.  Die Datenrichtung ist ja durch
das SPI-Interface selbst vorgegeben, man legt sie also einmalig bei
der Initialisierung so fest, dass MOSI, CS, und SCK Ausgänge sind und
MISO auf Eingang bleibt.  Der einzige Ausgang, an dem man dann noch
manuell ,,fummeln'' muss, ist das CS selbst.

Ich hab' mir das Datenblatt nicht angesehen, wodurch wird eigentlich
die Wandlung im Slave selbst getriggert?  Führt der Wandler ständig
Wandlungen durch, deren letztes Ergebnis du zu einem beliebigen
Zeitpunkt einfach abfragen kannst, oder musst du vielleicht erst
einmal ein Kommando schicken, mit dem er das startet?

Btw., die Code-Tags für C heißen [ c ] ... [ /c ], nicht [ c ] ...
[ \c ] (natürlich ohne den Leerzeichen).

von Stefan T. (stefan90)


Lesenswert?

Ich hab nun als Chipselect PB2 genommen, trotzdem gleiche Ergebnis.

Man muss normal kein Komando schicken, denn der Wandeler hat auch kein 
DIN, nur DOUT...

Die Wandlung startet dann automatisch wenn ich CS auf low lege nach 
2.5µs.

Ich hab mich an diesen Punkten orientiert:
1) Use a general-purpose I/O line on the CPU to pull
CS/SHDN low while SCLK is low.

2) Wait for the minimum wake-up time (tWAKE) specified
before activating SCLK.

3) Activate SCLK for a minimum of 16 clock cycles. The
first falling clock edge will generate a serial datastream
of three leading ones, followed by the channel
identification, the MSB of the digitized input
signal, and two sub-bits. DOUT transitions on
SCLK’s falling edge and is available in MSB-first format.
Observe the SCLK to DOUT valid timing characteristic.
Data should be clocked into the μP on
SCLK’s rising edge.

4) Pull CS/SHDN high at or after the 16th falling clock
edge. If CS/SHDN remains low, trailing zeros will be
clocked out after the sub-bits.

Das sollte doch so mit meinem Code übereinstimmen oder?

Hat vielleicht schon jemand diesen Wandler verwendet oder einen 
funktionierenden Code?

Gruß Stefan

von Rahul, der Trollige (Gast)


Lesenswert?

>void SPI_MasterReceive(uint8_t buffer[])
>SPI_MasterReceive(bytes);

Sowas geht?
Irgendwie hab ich im Hinterkopf, dass C Parameter-Übergaben an 
Funktionsaufrufe per "call-by-value" realisiert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

1
#define F_CPU 1000000ul
2
#include <util/delay.h>
3
4
void SPI_MasterReceive(uint8_t buffer[])
5
{
6
  PORTB &= ~(1<<CS);
7
8
  _delay_us(2.5);  /* Time the ADC needs to convert. */
9
10
  SPDR=0;
11
  /* Wait for reception complete */
12
  while(!(SPSR & (1<<SPIF)));
13
  buffer[0] = SPDR;
14
15
  SPDR=0;
16
  /* Wait for reception complete */
17
  while(!(SPSR & (1<<SPIF)));
18
  buffer[1] = SPDR;
19
20
  PORTB |= (1<<CS);
21
}
DDRB für CS setzt du bitte nur bei der Initialisierung.

von Robert (Gast)


Lesenswert?

>Sowas geht?
>Irgendwie hab ich im Hinterkopf, dass C Parameter-Übergaben an
>Funktionsaufrufe per "call-by-value" realisiert.

Ja, das geht. Das nennt sich "call-by-reference". Es wird ein Zeiger auf 
ein Feld übergeben.

mdg Robert

von Rahul, der Trollige (Gast)


Lesenswert?

>Ja, das geht. Das nennt sich "call-by-reference". Es wird ein Zeiger auf
>ein Feld übergeben.

Aber nur aufgrund dessen, dass es sich um ein Array handelt, was ja 
sowieso durch Pointer beschrieben wird...

Wenn man länger drüber nachdenkt...

von Stefan T. (stefan90)


Lesenswert?

Danke Jörg für deine Hilfe!!!

Jetzt funktionierts endlich, ich habe deine Funktion genommen und in der 
ReceiveInit nur die Eingänge und Ausgänge für MISO, SCK, CS.. + 
Aktivierung SPI

Eines hab ich trotzdem noch: Manchmal - zwar sehr selten - wenn ich 
Reset am AVR drücke und eine neue Messung kommt, habe ich nur 11111111 
11111111. Und beim Einschalten manchmal nur Nullen. Warum kann das sein? 
Ist vielleicht auch das Layaout daran schuld? ich hab den Max157 am 
Steckbrett aufgebaut und mit Drahtbrücken mit meinem Experimentierbord 
verbunden. Ist das ein möglicher Fehler? Ist ein 10 bit ADC...

Nochmals danke dass das endlich funzt!

Gruß Stefan

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.