AVR SPI Master Kommunikation mit Sensor

#3434448
Lesenswert?

Moin,

ich versuche gerade einen MS5803-14BA Drucksensor mit SPI anzusteuern.

Mein SPI - Code sieht wie folgt aus:
1
#include "spi.h"
2
#include <avr/io.h>
3
#include <stdio.h>
4
#include "uart.h"
5

6
#define SPI_PORT      PORTB
7
#define SPI_DDR        DDRB
8

9
#define  SPI_CS          2  //SS, CSB
10
#define SPI_MOSI        3  //DO,MOSI
11
#define SPI_MISO        4  //DI,MISO
12
#define SPI_SCK          5  //Clock
13

14
#define SPI_ENABLE()    SPI_PORT &= ~(1<<SPI_CS);
15
#define SPI_DISABLE()    SPI_PORT |=  (1<<SPI_CS);
16
#define SPI_WAIT()      while(!(SPSR & (1<<SPIF)));
17

18
void spi_init(uint8_t mode, bool interrupt) {
19

20
  SPI_DDR &= ~(1 << SPI_MISO);
21
  SPI_DDR |= (1 << SPI_SCK);
22
  SPI_DDR |= (1 << SPI_MOSI);
23
  SPI_DDR |= (1 << SPI_CS);
24

25
  SPCR = ((interrupt ? 1 : 0) << SPIE) | (1 << SPE) | (1 << MSTR)
26
      | (((mode & 0x02) == 2) << CPOL)  //Clock Polarity
27
      | (((mode & 0x01) == 1) << CPHA); //Clock Phase
28

29
  //SPSR = (1<<SPI2X); //Double the SPI Speed.
30

31

32
}
33

34
void spi_writeByte(uint8_t Byte) {
35
  SPDR = Byte;
36
  SPI_WAIT();
37
}
38

39
uint8_t spi_rcvByte() {
40
    uint8_t Byte = 0;
41
  SPDR = 0x00;
42
  SPI_WAIT()  ;
43
  Byte = SPDR;
44

45
    //debug
46
    uart_putc(Byte);
47
    uart_puts("\n");
48
    //
49
  return Byte;
50
}
51

52
uint16_t spi_rcvWord() {
53
  uint16_t Word = 0;
54
  Word |= (uint16_t) spi_rcvByte() << 8;
55
  Word |= spi_rcvByte();
56
  return Word;
57
}
58

59
uint32_t spi_rcvDWord() {
60
  uint32_t DWord = 0;
61
  DWord |= (uint32_t) spi_rcvByte() << 24;
62
  DWord |= (uint32_t) spi_rcvByte() << 16;
63
  DWord |= (uint16_t) spi_rcvByte() << 8;
64
  DWord |= spi_rcvByte();
65
  return DWord;
66
}

Ich bekomme aber immer nur 0xFF zurück, wenn ich versuche etwas per SPI 
zu lesen und dann (erstmal zum Testen) per UART weiter an den PC zu 
senden.

Ist mein Code falsch ? Ich nutze einen Atmega168V@3V3,8MHz und für UART 
die Library von Peter Fleury.
Moderator Persönliche Seite #3434613
Lesenswert?

Was du uns allerdings verschweigst ist, welchen Modus du bei spi_init()
nun eigentlich wählst.

Ehrlich: ich würde den ganzen Firlefanz da weglassen:
1
static void spi_init(void) {
2

3
  SPI_DDR &= ~(1 << SPI_MISO);
4
  SPI_DDR |= (1 << SPI_SCK);
5
  SPI_DDR |= (1 << SPI_MOSI);
6
  SPI_DDR |= (1 << SPI_CS);
7

8
  SPCR = _BV(SPE) | _BV(MSTR);
9

10
  //SPSR = (1<<SPI2X); //Double the SPI Speed.
11
}

Laut Datenblatt arbeitet das Teil (wie die meisten SPI-Geräte) im Modus
0, da brauchst das ganze CPHA- und CPOL-Gewusel nicht.  SPI mit
Interrupt braucht man auch nur bei schweinelangsamen Slaves.  Bei all
denen, die mit ein paar MHz SPI-Takt arbeiten (und du hast ja SPI2X
sogar in Erwägung gezogen), ist der Betrieb mit Interrupt langsamer
als das Polling.
Moderator Persönliche Seite #3434938
Lesenswert?

Ich habe das Datenblatt noch nicht bis zu Ende gelesen (hatte mir nur
den SPI-Teil angeguckt).  Typischerweise brauchen derartige Sensoren
nach dem Start der Datenkonvertierung relativ lange, bis man die Daten
auch abrufen kann.  Bist du dir sicher, dass du das Timing eingehalten
hast?

(Aus den paar Codefetzen da oben geht praktisch von den weiteren
Zusammenhängen nichts hervor.  => Netiquette)
#3434953
Lesenswert?

Ich habe mal den Rest Code hochgeladen (abgesehen vom UART, das ist wie 
gesagt der von Fleury).

Laut Datenblatt dauert eine ADC Conversion max. 8.22ms, ich warte 10ms. 
Das sollte ja eigentlich reichen ?

EDIT:
Habe nun mal Mode/Interrupt rausgeschmissen, so wie bei dir oben.
Angehängte Dateien:
#3435280
Lesenswert?

Jan H. schrieb:
> Da neben dem
> AVR nur der genannte Sensor am Bus ist, mache ich in der Initialisierung
> einmal SPI_ENABLE und lasse dann CS dauerhaft low.

Deinen Code habe ich mir nicht angesehen, aber im Beispielcode [1] kommt 
zwischen 'send command' und 'read result' das hier:
1
csb_hi(); // pull CSB high to finish the conversion
2
csb_lo(); // pull CSB low to start new command

[1] http://www.amsys.de/sheets/amsys.de.an520_e.pdf
Moderator Persönliche Seite #3435354
Lesenswert?

Jan H. schrieb:
> und lasse dann CS dauerhaft low.

Ich kenne keinen SPI-Slave, bei dem man das machen darf.

/CS = low ist üblicherweise das Signal, die state machine für den
internen SPI-Slave synchronisiert anzuwerfen.  Am Ende einer
SPI-Transaktion (was auch immer das für den jeweiligen Slave sein mag)
wird dann durch /CS = high die state machine wieder in die Ruhelage
versetzt.  Erst danach ist sie bereit für die nächste SPI-Transaktion.

Ansonsten könnte man das /CS ja gleich hart auf GND verdrahten, "ich
hab' doch nur einen Slave am Bus". :-)
Moderator Persönliche Seite #3435752
Lesenswert?

Jan H. schrieb:
> Wollte nur vorher sicherstellen, dass ich keinen üblen Patzer im Code
> habe.

Der sieht erstmal OK aus.  Die auf eine neue Zeile gezogenen Semikolons
muten etwas eigenwillig an, aber das ist reine Kosmetik.

Ich würde wahrscheinlich das /CS schon vor dem delay wieder zurück
nehmen, aber da du ja schon Huddeleien beim Lesen der PROM-Konstanten
hast, muss das Problem woanders sein.
#3440433
Lesenswert?

So. Das Problem wurde nun endlich behoben. Derjenige von dem ich die 
Platine habe, hat für die Chip Select Leitung nicht /SS Pin benutzt, 
sondern einen willkürlichen.

Im Code musste jedoch zusätzlich zu der gewählten Leitung auch der 
unbeschaltete /SS Pin des ATmegas als Ausgang gesetzt werden.

Danke an Alle die hier geholfen haben !

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren