Forum: Compiler & IDEs SPI Master Rx Problem


von Georg B. (Firma: Devel Solutions) (shorsh)


Lesenswert?

Hallo leute, folgendes Problem:
ich möchte von einem 16Bit ADC (AD977A) Datenempfangen. Dieser hat eine 
Art "halbes" SPI... D.h. er wird vom MCU (ATMega32-->Master) geklockt, 
und der ATMega empfängt mit MISO.

Die 16bit möchte ich auslesen in dem ich jeweils imner ein 8bit paket 
sende und direkt wieder auslese. Und das ganze 2mal hintereinander, 
wegen 16bit Wortbreite.
So bekomme ich aber für conv_data_H immer nur dasselbe heraus und höchst 
unterschiedl. Wert für die gleichen Eingangsspannungen am ADC.
Kann das so überhaupt funktionieren, was sagt ihr??

#include <avr/interrupt.h>
#define F_CPU 8000000
#include <avr/io.h>
#include <util/delay.h>

ISR(INT2_vect) //Conversion finished
{
cli();
uint8_t conv_data_H, conv_data_L;
conv_data_H = 0;
conv_data_L = 0;


PORTB |= (1<<PB7);     //Set SCK output
PORTB &= ~(1<<PB6);    //SET MISO input
PORTB |= (1<<PB5);    //SET MOSI Output(wird hier nicht  //gebraucht Pin 
hängt in der Luft)
SPCR = (1<<MSTR)|(1<<SPR0) | (1<<SPR1); /* Enable SPI, Master, set clock 
rate fck/128 */
SPCR |=(1<<CPHA);     //CPOL = 0, CPHA = 1
SPCR |= (1<<SPE);    //SPI_enable

SPDR = 0xF0;
conv_data_H = SPDR;
while(!(SPSR & (1<<SPIF)));
SPDR = 0xF0;
conv_data_L = SPDR;
sei();
}


int main()
{
cli();
GICR|=(1<<INT2); //INT2 ist Trigger für |Busy, steig. flanke
MCUCSR |= (1<<ISC2); //INT2(PB2) ein!
DDRB = 0b10100000;   //
PORTB |= ( 1<<PB0); //START_conv 1x DummyConversion
PORTB &= ~(1<<PB0);
PORTB |= ( 1<<PB0);
_delay_ms(1);
sei();               //Interupts an
PORTB &= ~(1<<PB0);
PORTB |= ( 1<<PB0);

while(1)
{
;
}

}

Ist der Ansatz Prinzipiell richtig?

von Jörg X. (Gast)


Lesenswert?

Woher kennst du denn den Inhalt der Variablen conv_data_H, ~_L ? Die 
sind doch nur lokal in der ISR... ;)

Im Datasheet gibt's die Kapitel "EXTERNAL DISCONTINUOUS CLOCK DATA READ
AFTER CONVERSION ..." und "EXTERNAL DISCONTINUOUS CLOCK DATA READ
DURING CONVERSION ..". Du willst scheinbar ersteres.

In beiden Fällen kann CS an GND angeschlossen werden und mit R\C wird 
die Wandlung gesteuert: RC muss vor dem auslesen auf high.

Das sieht soweit korrekt aus bei dir, ABER:
 - du brauchst kein cli()/sei() in der ISR
 - der AD-Wandler verträgt 15MHz SPI-takt, F_CPU/128 ist da bestimmt 
nicht notwendig/verlängert unnötig deine ISR
 - das SPIF flag ist nicht vom startweg gesetzt, sondern erst nach einer 
Übertragung, also besser grundsätzlich erst SPDR schreiben und danach 
auf das Flag warten
- warum schreibst du das DAC-Ergebnis nicht gleich in eine 16Bit 
Variable (int, int16_t)?
- Wenn du das Ergebnis in main() haben willst, musst du es dort 
hinbringen -> mit einer volatile globalen Variable

hth Jörg, der Datenblattleser ;)

ps.: wenn du noch mehr Probleme hast, poste bitte deine Schaltung (der 
ADC wird ja mit diversen Pins konfiguriert...)

von holger (Gast)


Lesenswert?

Zusätzlich zu Jörg:

>conv_data_H = 0;
>conv_data_L = 0;

Kannst du dir komplett sparen. Werden beim lesen
von SPDR sowieso wieder überschrieben.

PB4/SS wird nicht auf Ausgang geschaltet.
Da wird der Master evtl. ganz schnell wieder zum Slave.

>PORTB |= (1<<PB7);     //Set SCK output
>PORTB &= ~(1<<PB6);    //SET MISO input
>PORTB |= (1<<PB5);    //SET MOSI Output(wird hier nicht  //gebraucht Pin

Das sollte wohl DDRB statt PORTB heissen.
Hat in der ISR aber auch nichts zu suchen.

Auch SPCR setzt man besser nicht in der ISR.

von Jörg X. (Gast)


Lesenswert?

Achja, hab ich vorhin vergessen:
nimm die ganze SPI-Register- und Port-Initialisierung aus der ISR!
  Da musst du meistens gar nichts machen, es sei denn du hast mehrere 
SPi-Slaves mit unterschiedlichen Modi bzw. Geschwindigkeiten.

hth. Jörg

von Georg B. (Firma: Devel Solutions) (shorsh)


Lesenswert?

danke erstmal für die schnellen comments!
es lag v.a. am nicht gesetzen |ss
sollte nur ein test sein ob das überhaupt funzt. werde eure hints morgen 
umsetzen.

gruß georg

von Georg B. (Firma: Devel Solutions) (shorsh)


Lesenswert?

Jörg X. wrote:

> - warum schreibst du das DAC-Ergebnis nicht gleich in eine 16Bit
> Variable (int, int16_t)?

Weil SPDR nur 8bit breit ist, dachte ich! also, wenn ich das mit 
uint16_t auslese machen die werte wenig sinn, bzw. scheint so als würde 
er ein byte "abschneiden"...
wie kann ich das denn direkt auslesen, oder warum sollte das überhaupt 
direkt funzen?

mfg
g.brfd

von Jörg X. (Gast)


Lesenswert?

>wie kann ich das denn direkt auslesen,
1
uint16_t ergebnis; /* vielleicht auch besser int16_t, wenn der ADC auf
2
 2erkomplement eingestellt ist */
3
uint8_t tmp; // nicht wirklich notwendig, aber besser lesbar:
4
tmp = spi_put_byte(DUMMY);
5
ergebnis = ((uint16_t) tmp)<<8; /* bin mir hier mit den Klammern nicht
6
     ganz sicher */
7
tmp = spi_put_byte(DUMMY);
8
ergebnis |=  tmp;
9
//...
10
//wenn dich "tmp = spi_PUT_byte(DUMMY);" stoert:
11
#define spi_get_byte() spi_put_byte(DUMMY)
12
//;)

hth. Jörg

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.