Forum: Compiler & IDEs AVR SPI Master Kommunikation mit Sensor


von Jan H. (jhnet)


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.

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


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.

von Jan H. (jhnet)


Lesenswert?

Ja stimmt, habe ich nicht erwähnt, ich nutze Mode 0 und keine 
Interrupts.

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


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)

von Jan H. (jhnet)


Angehängte Dateien:

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Was hast du eigentlich mit dem Chip-Select gemacht?

In deinem Code ganz oben sieht es so aus, als ob der verdrahtet wäre. 
Immerhin gibt es ein Makro SPI_ENABLE. Allerdings wird es im ganzen Code 
nirgends verwendet.

von Jan H. (jhnet)


Lesenswert?

Ups, ist wohl irgendwie beim umkopieren verloren gegangen. 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.

von Leo C. (rapid)


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

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


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". :-)

von Jan H. (jhnet)


Angehängte Dateien:

Lesenswert?

Ich habe nun noch mal alle eure Anregungen geprüft und auch den
geposteten Beispielcode (danke dafür!) angeschaut.

Auch mein überarbeiteter Code gibt mir aber für jeden Koeffizienten
C1-C6 immer nur 65535 aus.

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


Lesenswert?

Jan H. schrieb:
> Auch mein überarbeiteter Code gibt mir aber für jeden Koeffizienten
> C1-C6 immer nur 65535 aus.

Dann wäre es wohl an der Zeit, die Hardware nochmal kritisch unter
die Lupe zu nehmen.  Vielleicht auch mal einen Oszi dranhängen.

von Jan H. (jhnet)


Lesenswert?

Jo das wäre auch mein nächster Schritt gewesen. Wollte nur vorher 
sicherstellen, dass ich keinen üblen Patzer im Code habe. Danke !

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


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.

von Jan H. (jhnet)


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 !

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.