Forum: Mikrocontroller und Digitale Elektronik SPI Winkelsensor Novotechnik RFC4800 will nicht


von Jan (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich will mit einem Olimex AT90CAN128 Board 16-Bit Werte eines 
Winkelsensors RFC 4800 von Novotechnik über CAN-Bus senden. Der Bus 
funktioniert und läuft mit der CAN-Lib von Marco Glietsch 
Beitrag "CAN-Bibliothek für den at90CAN128 und das AVRStudio".
Angeschlossen ist der Sensor wie in der angehängten 
Applikation-Note(Anhang) des Herstellers beschrieben. Als FET habe ich 
den BS170 verwendet(Anhang).
MISO --> Drain
MOSI --> Gate
GNG  --> Source
Die Wiederstände habe ich wie in Zeile 4 (Atmel AVR) der Tabelle 
beschrieben gewählt. Sicherheitshalber hab ich die Schaltung nochmal 
durch gemessen. Soweit ok.

Nun habe ich meine Quellcode auf Basis verschiedener Beispiele u.A. aus 
dem Datenblatt vom Controller und AVR151(Setup And Use of The SPI) 
aufgesetzt. So weit ich es verstanden habe muss der Sensor in SPI Mode 1 
betrieben werden. Also CPHA=1 und CPOL=0. Laut Support des Herstellers 
soll ich die SCK-Frequenz auf 50kHz stellen.
Dazu habe ich einmal den IO-Clock Prescaler (CLKPR) auf 4  und dann noch 
den SPI-Clock Prescaler auf 128 gesetzt um von 16MHz CPU auf 62,5kHz 
SCK-Frequenz zu kommen.

Nach langem rumgefummel habe ich es hin bekommen, dass der Anfrage-Frame 
gesendet wird (0x55, 0x00...[Laut Hersteller soll ich die invertierten 
Signale von dem was in der Appikationnote steht senden]). Dazu ziehe ich 
den SS-Pin erst auf Low und warte 1,5ms damit sich Master und Slave 
synchronsieren. Dann SS-Pin wieder hoch für Mastermode und 
Master-Enable, weil durch SS:low Master disable (MSTR in SPCR). Dann 
senden nach dem in Beispielen üblichen Schema.

Anschließend wechsel ich durch SS-Pin auf Low in den Slavemode um Daten 
zu empfangen. Mit der ebenfalls nach üblichem Schema aufgebauten 
receive-Methode empfange ich dann zwei Bytes, aber beim dritten 
Durchlauf der Methode wird while(!(SPSR & (1<<SPIF))) nicht mehr 
verlassen.

Nun verstehe ich nicht warum das passiert. Darf ich den SS-Pin nicht auf 
Low ziehen? Ist die Frequenz ok? Nächst kleinere wäre 31,25kHz. Was ja 
viel zu kurz wäre. Muss ich das DDR-Register im Slavemode umstellen? 
Also "MISO output, all others input". Z.z sieht es so aus:
1
SPI_DDR = (1<<SPI_DDR_MOSI)|(1<<SPI_DDR_SCK);

Anbei der SPI-Quellcode, in dem ich zur Kontrolle Ausgaben auf ein LCD 
mache. Anbei auch ein Bild des LCD, damit man sich das besser vorstellen 
kann. Weitere Ausgaben in der Main...
1
#include "PORT.h"
2
#include "SPI.h"
3
#include "LCD.h"
4
5
int8_t i=0;
6
7
void SPI_init(void)
8
{
9
  //Enable SPI
10
  //Mastermode
11
  //SPI Mode 1 (CPOL=0 | CPHA=1)
12
  /*
13
  SPI2X|SPR1|SPR0|SCK Frequency
14
  0   |0   |0   |fclkio/4
15
  0    |0   |1   |fclkio/16
16
  0    |1   |0   |fclkio/64
17
  0    |1   |1   |fclkio/128
18
  1    |0   |0   |fclkio/2
19
  1    |0   |1   |fclkio/8
20
  1    |1   |0   |fclkio/32
21
  1    |1   |1   |fclkio/64  
22
  F_SCK = F_CLK/PR_SPI = F_CPU/PR_CLK/PR_SPI = 
23
  16000kHz/4(CLKPR)/128(SPCR) = 62,5kHz*/
24
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPHA)|(1<<SPR1)|(1<<SPR0);
25
  
26
  //Wait for RFC 4800 startup
27
  _delay_ms(16); 
28
}
29
30
void SPI_select_mastermode(void)
31
{
32
  /* To synchronize communication, the master deactivates /SS high for at least 
33
  t5 (1,5ms). In this case, the slave will be ready to receive a new frame.*/
34
  SPI_PORT &= ~(1<<SPI_PIN_SS);
35
  _delay_us(1500);
36
  
37
  /*In master mode, the SS pin must be held high to ensure master SPI operation*/
38
  SPI_PORT |= (1<<SPI_PIN_SS);
39
  
40
  /*Once the MSTR bit has been cleared by a low level on the SS line, 
41
  it must beset by the application to re-enable SPI master mode.*/
42
  SPCR |= (1<<MSTR);
43
}
44
45
void SPI_select_slavemode(void)
46
{
47
  //Chipselect Active Low
48
  SPI_PORT &= ~(1<<SPI_PIN_SS);
49
  _delay_us(1500);
50
}
51
52
void SPI_master_transmit(char data)
53
{
54
  //write param to SPI data register
55
  SPDR = data;
56
57
  //Wait for transmission to complete
58
  while(!(SPSR & (1<<SPIF)));
59
}
60
61
char SPI_slave_receive(void)
62
{
63
  LCD_displayInt8(3,4,i);
64
  //Wait for transmission to complete
65
  while(!(SPSR & (1<<SPIF)));
66
  LCD_displayInt8(3,6,i);
67
  i++;
68
  return(SPDR);
69
}

Und hier die Main. Die Delays nach den LCD-Methoden sind nur wegen 
Timing-Problemen des Displays die ich auch noch fixen muss.
1
#define F_CPU 16000000
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <avr/interrupt.h>
6
7
#include "PORT.h"
8
#include "LCD.h"
9
#include "CAN.h"
10
#include "ADC.h"
11
#include "SPI.h"
12
13
uint16_t adc_var=0;
14
uint16_t adc_var_high=0;
15
16
uint8_t angle_var_lsb=0;
17
uint8_t angle_var_msb=0;
18
19
int main(void)
20
{
21
  PORT_init();
22
  ADC_init();
23
  LCD_init();
24
        LCD_clear();
25
  SPI_init();
26
  
27
  
28
  /*SET CLOCK PRESCALER
29
  CLKPS3|CLKPS2|CLKPS1|CLKPS0|Clock Division Factor
30
  0    |0   |0    |0     |1
31
  0    |0   |0   |1      |2
32
  0    |0   |1   |0      |4
33
  0    |0   |1   |1      |8
34
  0    |1   |0   |0      |16
35
  0    |1   |0   |1      |32
36
  0    |1   |1   |0      |64
37
  0    |1   |1   |1      |128
38
  1    |0   |0   |0      |256  
39
  
40
  F_CLK = F_CPU/PR_CLK =
41
  16MHz/2 = 8MHz*/
42
  CLKPR |= (1<<CLKPCE)|(1<<CLKPS0);
43
  
44
  CAN_init(500,RX);//(500kBaud, Interrupt für Empfang)
45
  
46
  // Definition von CAN message structs
47
  CAN_message    msg;
48
  // ID-Maske festlegen -> alle IDs akzeptiert
49
  msg.idm = 0x00000000;   
50
  //CAN MOB aktivieren
51
  CAN_enableMOB(0,TRANSMIT_DATA,msg);  
52
  CAN_enableMOB(1,TRANSMIT_DATA,msg);  
53
  
54
    while(1)
55
    {
56
    //Get ADC Voltage value
57
    adc_var = ADC_readMeanValueOf(5);
58
    adc_var_high = (adc_var >> 8);
59
    
60
    CAN_senden(0,0x100,8,adc_var_high,adc_var,0x00,0x00,0x00,0x00,0x00,0x00);
61
    
62
    //Send SPI data request frame
63
    SPI_select_mastermode();
64
    SPI_master_transmit(0x55);
65
    
66
    LCD_displayInt8(3,0,1);
67
    _delay_ms(1);
68
    
69
    for(int i=0; i<9;i++)
70
    {
71
      SPI_master_transmit(0x00);
72
      LCD_displayInt8(3,2,i+1);
73
      _delay_ms(1);
74
    }
75
    
76
    //Read SPI data frame
77
    SPI_select_slavemode();
78
    
79
    LCD_displayInt8(0,0,SPI_slave_receive());
80
    _delay_ms(1);
81
    LCD_displayInt8(0,2,SPI_slave_receive());
82
    _delay_ms(1);
83
    angle_var_msb = SPI_slave_receive();
84
    LCD_displayInt8(0,4,angle_var_msb);
85
    _delay_ms(1);
86
    angle_var_msb = SPI_slave_receive();
87
    LCD_displayInt8(0,6,angle_var_lsb);
88
    _delay_ms(1);
89
    LCD_displayInt8(1,0,SPI_slave_receive());
90
    _delay_ms(1);
91
    LCD_displayInt8(1,2,SPI_slave_receive());
92
    _delay_ms(1);
93
    LCD_displayInt8(1,4,SPI_slave_receive());
94
    _delay_ms(1);
95
    LCD_displayInt8(1,6,SPI_slave_receive());
96
    _delay_ms(1);
97
    LCD_displayInt8(2,0,SPI_slave_receive());
98
    _delay_ms(1);
99
    LCD_displayInt8(2,2,SPI_slave_receive());
100
    _delay_ms(1);
101
    
102
    CAN_senden(0,0x200,8,0xFF,0xFF,angle_var_msb,angle_var_lsb,0x00,0x00,0x00,0x00);
103
    _delay_ms(1000);
104
    }
105
}

Und noch die Mail vom Support:
> sie verwenden sicherlich den im Datenprotokoll angegebenen FET, da
> wir die 3-Leitertechnik verwenden. Der eingesetzte chip ist ein
> Melexis 90316 und bietet dieses 3-Leiter Version an.
> Um den eingesetzten Transistor zu sperren um Daten zu empfangen senden
> Sie  anstatt (AA,FF,FF,..) bitte die invertierten Signale (55, 00,00,..)
> um den Transistor zu sperren.
> Dann müssten Sie die Daten empfangen können. Bitte achten Sie darauf,
> dass die timings eingehalten werden.
> Bei einer clock rate von 50kHz müsste dies automatisch der Fall sein.

So ich hoffe ich habe alles geschrieben was wichtig sein könnte. Danke 
schon mal für die Hilfe und dafür dass du bist ganz unten gelesen hast 
:)

von holger (Gast)


Lesenswert?

>Dazu ziehe ich
>den SS-Pin erst auf Low und warte 1,5ms damit sich Master und Slave
>synchronsieren. Dann SS-Pin wieder hoch für Mastermode und
>Master-Enable, weil durch SS:low Master disable (MSTR in SPCR).

So ein Unsinn. SS schaltet den Master nur auf Slave um
wenn es ein EINGANG ist. SS als Ausgang kann man
nutzen um das SlaveSelect des Slaves zu steuern. Dazu legt
man SS auf low wenn man den Slave ansprechen möchte.
Beim lesen UND beim schreiben.

Den Master hier auf Slave umschalten zu wollen macht gar
keinen Sinn. Wer soll denn dann den SPI Takt liefern?
Dein Slave macht das jedenfalls nicht. Der Master empfängt
indem er DummyBytes sendet. Nur so wird der SPI Takt erzeugt.

von Jan (Gast)


Lesenswert?

Also bleibt der SS-Pin die ganze Zeit auf Low in meinem Fall, weil nur 
ein Master und ein Slave? So war ich drauf gekommen: 
http://www.atmel.com/images/doc2585.pdf
Seite 3 SS-Pin Funcionality

von holger (Gast)


Lesenswert?

>Also bleibt der SS-Pin die ganze Zeit auf Low in meinem Fall,

Nein, SS legt man nur low wenn etwas gelesen, geschrieben
werden soll. Wenn man damit fertig ist zieht man SS auf high.
So bleibt das SPI immer schön synchron.

von Jan (Gast)


Lesenswert?

holger schrieb:
> Der Master empfängt
> indem er DummyBytes sendet. Nur so wird der SPI Takt erzeugt.

Also so:
1
char SPI_receive(void)
2
{
3
  LCD_displayInt8(3,4,i);
4
5
  SPDR = 0x00;
6
  
7
  //Wait for transmission to complete
8
  while(!(SPSR & (1<<SPIF)));
9
  LCD_displayInt8(3,6,i);
10
  i++;
11
  return(SPDR);
12
}

holger schrieb:
> Nein, SS legt man nur low wenn etwas gelesen, geschrieben
> werden soll. Wenn man damit fertig ist zieht man SS auf high.
> So bleibt das SPI immer schön synchron.
1
void SPI_SS_high(void)
2
{  
3
  SPI_PORT |= (1<<SPI_PIN_SS);
4
  
5
  SPCR |= (1<<MSTR);
6
}
7
8
void SPI_SS_low(void)
9
{
10
  SPI_PORT &= ~(1<<SPI_PIN_SS);
11
  
12
  _delay_us(1500);
13
}

Muss MSTR in SPCR nochmal gesetzt werden?

Jan schrieb:
> http://www.atmel.com/images/doc2585.pdf
> Seite 3 SS-Pin Funcionality

Dort steht:
A low level will switch the SPI into slave mode and the hardware of the 
SPI will perform the following actions:
1.    The master bit (MSTR) in the SPI Control Register (SPCR) is 
cleared

In der Main sieht es jetzt so aus:
1
int main(void)
2
{
3
  PORT_init();
4
  LCD_init();
5
  SPI_init();
6
  LCD_clear();
7
8
  CLKPR |= (1<<CLKPCE)|(1<<CLKPS0);
9
10
  
11
    while(1)
12
    {
13
    SPI_SS_low();
14
    
15
    for(int i=0; i<10;i++)
16
    {
17
      if(i)
18
        SPI__transmit(0x00);
19
      else
20
        SPI__transmit(0x55);
21
        
22
      LCD_displayInt8(3,2,i);
23
      _delay_ms(1);
24
    }
25
    
26
    for(ini i=0; i<3; i++)
27
    {
28
      for(int ii=0; ii<8; ii+=2)
29
      {
30
        LCD_displayInt8(i,ii,SPI_receive());
31
        _delay_ms(1);
32
      }
33
    }
34
35
    SPI_SS_high();
36
    
37
    _delay_ms(100);
38
    }
39
}

von Jan (Gast)


Lesenswert?

Hmm nach dem ersten Trasmit bleibts wieder hängen. Seltsamerweise blinkt 
auch in der zweiten Zeile, jedesmal wenn ich das Netzteil ein- und 
ausschalte eine 666 auf. Das Anweisung dafür ist längst gelöscht, weil 
ich nur was testen wollte. Oo
Extra in AtmelStudio auch ganz sicher nochmal die Datei vorm flashen 
ausgewählt. Seltsam oder?

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.