www.mikrocontroller.net

Forum: Compiler & IDEs AD-Wandler Max157 SPI


Autor: Stefan Tschiggerl (stefan90)
Datum:

Bewertung
0 lesenswert
nicht 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:
#define MOSI   PB3
#define SCK  PB5
#define MISO   PB4
#define CS   PB1

void bin_ausgabe(uint8_t byte);

void SPI_Master_ReceiveInit(void)
{
  DDRB |= (1<<CS) | (1<<SCK);
  DDRB &= ~(1<<MISO);
  
  PORTB &= ~(1<<SCK);
  PORTB &= ~(1<<CS);
  _delay_us(3);
  
  DDRB &= ~(1<<SCK);
  
  /* Enable SPI, Master, set clock rate fck/16 */
  SPCR = (1<<SPE) | (1<<MSTR);
  SPSR = (1<<SPR0);
}

//empfängt 2 Bytes
void SPI_MasterReceive(uint8_t buffer[])
{
  PORTB &= ~(1<<CS);

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

void bin_ausgabe(uint8_t byte)
{
  uint8_t i;
  
  for(i=0; i<8; i++)
  {
    if(byte & (1 << (7-i)))
      lcd_putc('1');
    else
      lcd_putc('0');
  }
}

int main (void)
{
  uint8_t bytes[2], i;
  
  lcd_init(I2C_LCD_DISP_ON);

  SPI_Master_ReceiveInit();
  SPI_MasterReceive(bytes);
  
  bin_ausgabe(bytes[0]);
  lcd_putc('\n');
  bin_ausgabe(bytes[1]);
  
  return 0;
}

Ich hoffe, dass mir jemand weiterhelfen kann

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Tschiggerl (stefan90)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Tschiggerl (stefan90)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Stefan Tschiggerl (stefan90)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rahul, der Trollige (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#define F_CPU 1000000ul
#include <util/delay.h>

void SPI_MasterReceive(uint8_t buffer[])
{
  PORTB &= ~(1<<CS);

  _delay_us(2.5);  /* Time the ADC needs to convert. */

  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);
}
DDRB für CS setzt du bitte nur bei der Initialisierung.

Autor: Robert (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rahul, der Trollige (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Stefan Tschiggerl (stefan90)
Datum:

Bewertung
0 lesenswert
nicht 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

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.