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
voidspi_init(uint8_tmode,boolinterrupt){
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
voidspi_writeByte(uint8_tByte){
35
SPDR=Byte;
36
SPI_WAIT();
37
}
38
39
uint8_tspi_rcvByte(){
40
uint8_tByte=0;
41
SPDR=0x00;
42
SPI_WAIT();
43
Byte=SPDR;
44
45
//debug
46
uart_putc(Byte);
47
uart_puts("\n");
48
//
49
returnByte;
50
}
51
52
uint16_tspi_rcvWord(){
53
uint16_tWord=0;
54
Word|=(uint16_t)spi_rcvByte()<<8;
55
Word|=spi_rcvByte();
56
returnWord;
57
}
58
59
uint32_tspi_rcvDWord(){
60
uint32_tDWord=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
returnDWord;
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.
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
staticvoidspi_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.
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)
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.
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.
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.
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
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". :-)
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.
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.
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.
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 !