Forum: Haus & Smart Home SPI-Probleme und ENC28J60


von Philipp H. (magier)


Lesenswert?

Hallo zusammen

schon länger bewegt mich die Idee mir meine eigene netzwerkfähige 
Steckdosenleiste zu bauen und habe mir daher den Pollin Bausatzes 
AVR-NET-IO gekauft. Nach etwas hin und her läuft das Ding auch mit der 
originalen Pollin Firmware, die mich aber nicht wirklich überzeugt. Habe 
daher beschlossen selber etwas zusammen zu stellen, Projekte dieser Art 
gibt es ja zur genüge.
(siehe http://www.lochraster.org/etherrape/ o.ä. 
http://www.mikrocontroller.net/articles/AVR_Net-IO_Bausatz_von_Pollin )

Dabei habe ich festgestellt das die ENC28J60.c und  ENC28J60.c bei allen 
Projekten die ich gefunden habe ziemlich tief mit anderen Teilen des 
Projektes verzahnt ist und ein entsprechender Umbau fast genauso viel 
Arbeit wäre wie sie gleich neu zu schreiben. (natürlich in Anlehnung, 
niemand erfindet das Rad neu)
Jetzt hänge ich aber schon bei den ersten Grundschritten, dem Ansprechen 
per SPI.

Meine spi.c sieht folgendermaßen aus.
1
void spi_init(void)
2
{
3
  unsigned char tmp;
4
    /* configure MOSI, SCK, CS lines as outputs */
5
    DDRB = _BV(PB5) | _BV(PB7) | _BV(PB4);    
6
7
    /* set all CS high (output) */
8
    PORTB = _BV(PB4);    
9
10
    /* enable spi, set master and clock modes (f/2) */
11
    SPCR = _BV(SPE) | _BV(MSTR);
12
    SPCR = _BV(SPI2X);
13
 
14
  tmp = SPSR;
15
  tmp = SPDR;
16
}
17
18
void spi_send(uint8_t data)
19
{    
20
  UART_send_str("Sende data:",11);  //NUR FÜR DEBUG ZWECKE
21
  SPDR = data;
22
  UART_send_str("Gesende data:",13); //NUR FÜR DEBUG ZWECKE  
23
  while (!(SPSR & _BV(SPIF)))
24
  {
25
    UART_send_str("Warte:",6);  //NUR FÜR DEBUG ZWECKE
26
    UART_send_U8(SPSR);  //NUR FÜR DEBUG ZWECKE
27
    UART_send_U8(SPIF);  //NUR FÜR DEBUG ZWECKE
28
  }
29
  UART_send_str("Ende:",5);
30
  data = SPDR;  
31
}
32
33
uint8_t spi_send_read(uint8_t data)
34
{
35
  uint8_t redata;  
36
  UART_send_str("Sende data:",11);  //NUR FÜR DEBUG ZWECKE
37
  SPDR = data;
38
  UART_send_str("Gesende data:",13);  //NUR FÜR DEBUG ZWECKE
39
  while (!(SPSR & _BV(SPIF)))
40
  {
41
    UART_send_str("Warte:",6);  //NUR FÜR DEBUG ZWECKE
42
  }
43
  redata = SPDR;  
44
  return redata;
45
}
46
47
void spi_cs_on(void)
48
{
49
  SPCR &= ~_BV(PB4);
50
}
51
52
void spi_cs_off(void)
53
{
54
  SPCR |= _BV(PB4);
55
}



und wird so benutzt.

1
int main(void)
2
{
3
  uint8_t u_data;
4
  UART_init();
5
  spi_init();
6
  UART_send_str("----Beginne----", 15);  //NUR FÜR DEBUG ZWECKE
7
  
8
  //Read ECON1
9
  spi_cs_on();
10
  UART_send_str("Sende: 0b00011111", 17);  //NUR FÜR DEBUG ZWECKE
11
  spi_send(0b00011111);
12
  UART_send_str("Empfange: 0b00011111", 20);  //NUR FÜR DEBUG ZWECKE
13
  u_data = spi_send_read(0b00011111);
14
  UART_send_U8(u_data);  //NUR FÜR DEBUG ZWECKE
15
....
16
  spi_cs_off();
17
  UART_send_str("-ENDE", 5);  //NUR FÜR DEBUG ZWECKE
18
  return 0;
19
}


Die UART geschichte habe ich zur Zeit nur für einen Debug eingebaut.

Das Problem hier bei ist ich komme nie über die erste
1
while (!(SPSR & _BV(SPIF)));
Schleife hinaus. SPIF weigert sich und bleibt beharrlich auf  0b00000111 
stehen.

Die ganze Console sieht so aus.....
1
----Beginne----
2
Sende: 0b00011111
3
Sende data:
4
Gesende data:
5
Warte:
6
00000000
7
00000111
8
Warte:
9
00000000
10
00000111
11
Warte:
12
00000000
13
00000111
14
Warte:
15
00000000
16
00000111
17
...
18
..
19
.
Wieso??????? Was mache ich falsch?????

Schon mal Danke
Magier

von Alexander N. (fd0)


Lesenswert?

Hallo Philipp,

Schoen, das dir mein Code als Inspiration dient :)

Du hast leider an einer Stelle etwas verschlimmbessert, in spi_init() 
setzt du erst ein paar Bits in SPCR, und anschliessend moechtest du 
SPI2X im Register SPSR setzen, schreibst den Wert aber in SPCR:

Dein Code:
1
    /* enable spi, set master and clock modes (f/2) */
2
    SPCR = _BV(SPE) | _BV(MSTR);
3
    SPCR = _BV(SPI2X);

Besser ist (achte genau auf die letzte Zeile):
1
    /* enable spi, set master and clock modes (f/2) */
2
    SPCR = _BV(SPE) | _BV(MSTR);
3
    SPSR = _BV(SPI2X);

Dein Code fuehrt dazu, dass du die SPI-Schnittstelle effektiv direkt 
wieder abschaltest ;)

Kennst du eigentlich das ethersex-Projekt? -> http://www.ethersex.de

Gruss,
- Alexander

von Philipp H. (magier)


Lesenswert?

Hallo Alexander

Ganz herzlichen Danke für deine Fehler-"verschlimmbessert"-Korrektur... 
:-) "lustiges Wort"!
Habe das natürlich sofort umgeschrieben, nur das Ergebnis ist nicht so 
ganz das was ich erhofft habe.
SPSR wird jetzt 1 aber SPIF bleibt bei 7.
Die Console sagt also folgendes:
1
----Beginne----
2
Sende: 0b00011111
3
Sende data:
4
Gesende data:
5
Warte:
6
00000001
7
00000111
8
Warte:
9
00000001
10
00000111
11
Warte:
12
00000001
13
00000111
14
Warte:
15
...
16
..
17
.

Bist du der Alexander der die ENC28J60 API aus dem Etherrape-Projekt 
geschrieben hat?
Muss ja echt mal sagen. RESPEKT... finde ich mega cool, das ist echt nen 
Traum.
Und hätte da gleich mal ne Frage direkt zum ENC28J60.
Wo liegt der genau Unterschied zwischen "Write Control" Register 
Kommando und dem "Bit Field Set" und "Bit Field Clear" Kommando? Machen 
die nicht im Prinzip das selbe?
Mit "Wirte Control" überschreibt man doch ein Byte, wo liegt jetzt der 
ganz tiefer Sinn noch ein Kommando für eine OR und NOTAND Anweisung zu 
haben? Eigentlich könnte man doch gleich das ganze Byte neu schreiben 
oder?

Bedankt und Gruß
Philipp

P.S. Habe ich ganz vergessen. Ja sicher kenne ich das Ethersex Projekt, 
habe ich hier auf der Platte, nur hatte ich es auf der Suche nach einer 
simplen ENC28J60 Api gelesen und aussortiert, da sie mir nicht 
grundlegend anderes als die des Etherrape-Projekt schien.

von Alexander N. (fd0)


Lesenswert?

Hi,

Philipp H. schrieb:
> Habe das natürlich sofort umgeschrieben, nur das Ergebnis ist nicht so
> ganz das was ich erhofft habe.
> SPSR wird jetzt 1 aber SPIF bleibt bei 7.

Uhm, das ist in soweit korrekt, als dass "SPIF" eine Konstante mit dem 
Wert 7 ist, da wird sich nichts dran aendern.  Diese Konstante (in der 
avr-libc definiert) nutzt du ja auch nur, um auf das richtige Bit in 
SPSR zuzugreifen.

Welcher Controller ist das eigentlich? ATmega32?  Bist du sicher, das du 
da den SS-Pin als Ausgang (bzw. mindestens als Eingang mit aktiviertem 
Pullup) konfiguriert hast?  Wenn nicht, kanns passieren, das der 
Controller sich ploetzlich als SPI-Slave angesprochen fuehlt (weil ein 
voellig in der Luft haengender CMOS-Input-Pin manchmal Null, manchmal 
Eins liest...), und dann in den SPI-Slave-Mode umkippt (wo dann SPIF 
garnicht gesetzt wird, hat mich mal zwei Tage gekostet, das zu 
debuggen...).

> Die Console sagt also folgendes:
> ----Beginne----
> Sende: 0b00011111
> Sende data:
> Gesende data:
> Warte:
> 00000001
> 00000111
> Warte:
> 00000001
> 00000111
> Warte:
> 00000001
> 00000111
> Warte:

Find ich grad was merkwuerdig, kann ich so nicht nachvollziehen (ausser 
der SPIF-Konstante ;)

> Bist du der Alexander der die ENC28J60 API aus dem Etherrape-Projekt
> geschrieben hat?

Ja, bin ich.

> Muss ja echt mal sagen. RESPEKT... finde ich mega cool, das ist echt nen
> Traum.

Uhm, was meinst du?  Du findest die Api gut durchdacht, oder wie? Aber 
danke fuer die Lorbeeren ;)

> Und hätte da gleich mal ne Frage direkt zum ENC28J60.
> Wo liegt der genau Unterschied zwischen "Write Control" Register
> Kommando und dem "Bit Field Set" und "Bit Field Clear" Kommando? Machen
> die nicht im Prinzip das selbe?
> Mit "Wirte Control" überschreibt man doch ein Byte, wo liegt jetzt der
> ganz tiefer Sinn noch ein Kommando für eine OR und NOTAND Anweisung zu
> haben? Eigentlich könnte man doch gleich das ganze Byte neu schreiben
> oder?

Klar koennte man das.  Der Unterschied ist, dass man mit 
write_control_register einen Wert in einem Register nur veraendern kann, 
wenn man das in drei Schritten (read-modify-write) macht.  Bei manchen 
Registern (zb. fuer die Interrupt-Flags) koennen sich dazwischen Bits 
veraendern, die der enc28j60 dann selber setzt, zb. bei einem gerade 
aufgetretenen Interrupt.  Tritt das NACH dem Lesen, aber VOR dem 
Schreiben des Registerwerts ein, geht der neue Wert verloren.

Deshalb gibts, speziell zum Setzen und Ruecksetzen von einzelnen Flags 
(zb. Interrupt-Flags) das bit_field_set() und bit_field_clear(), die 
aendern atomisch (also in einem Zug) nur die angegebenen Bits, und alles 
andere bleibt unangetastet.

> P.S. Habe ich ganz vergessen. Ja sicher kenne ich das Ethersex Projekt,
> habe ich hier auf der Platte, nur hatte ich es auf der Suche nach einer
> simplen ENC28J60 Api gelesen und aussortiert, da sie mir nicht
> grundlegend anderes als die des Etherrape-Projekt schien.

Stimmt.  Ethersex ist ein (sehr viel weiter fortgeschrittener) Fork 
meiner originalen Firmware fuer das etherrape.  GPL ist toll :)

Gruss,
- Alexander

von Philipp H. (magier)


Lesenswert?

Hallo

Ja ich benutze einen ATmega32, allerdings nicht den mitgelieferten von 
Pollin, der hatte nämlich nen Schuss weg und fehlerhafte Flashzellen. 
Also so weit ich das begreife sollte der SS-Pin als Ausgang geschaltet 
sein..
1
void spi_init(void)
2
{
3
    unsigned char tmp;
4
    /* configure MOSI, SCK, CS lines as outputs */
5
    DDRB = _BV(PB5) | _BV(PB7) | _BV(PB4);    
6
7
    /* set all CS high (output) */
8
    PORTB = _BV(PB4);

Und nach meinem Datasheet sollte PB4 SS oder CS sein. Das SPIF ne 
Konstante ist war mir nicht bewusst... betreibe das Projekt um mir 
selber was beizubringen und habe das Datenblatt des ATmega nicht 
wirklich Zeile für Zeile gelesen... werde ich aber nachholen, sonst 
laufe ich dauernd in solche Fallen.

Naja die Lorbeeren sind für das ganze Projekt, das aus einer Idee mal so 
was werden kann finde ich gut und erstrebenswert. Deine ENC28J60 API ist 
bestimmt ganz toll, auf sehr wenig Speicherplatz getrimmt, als nicht so 
geschulter C Benutzer fehlt mir da etwas das Wissen um die Details zu 
verstehen.
Ich für mein Projekt brauche aber sehr sehr vieles von dem was im 
Etherrape oder Ethersex Projekt mit eingebaut ist aber nicht. Meine 
Kiste soll in ein eigenes Sub-Netz und nur mit einem zentralen Server 
reden und sonst auch mit keinem, Http ist schon viel zu hoch gegriffen. 
UDP ist mein erstes Ziel das ich ansteuere. Der zweite Grund ist das 
mich das Interesse antreibt genau zu verstehen wie "es" geht und nicht 
nur mal den Quell-Code gelesen zu haben.

Darf ich mal fragen wie lange es so in etwa gedauert hat vom ersten 
lesen des ENC28J60 Datasheets bis du die erste stabile Version eines 
Treibers hattest?
Habe dich übrigens auf meiner erste Recherche im Netz in einem Video von 
23rd Chaos Communication Congress gesehen, nachdem ich mir gedacht habe. 
"Hey das klingt gut, so was versuchst du auch mal."

Danke für deine Hilfe
Philipp

von Philipp H. (magier)


Lesenswert?

HA-------HABE-ES--------------!!!!!!!!!!!!!!!!!!!!!

Habe was rumprobiert und siehe da der Fehler lag wirklich im CS!!!!!!!
Habe das ein und ausschalten des CS Pins verwechselt.
1
void spi_cs_off(void)
2
{
3
  SPCR &= ~_BV(PB4);
4
}
5
6
void spi_cs_on(void)
7
{
8
  SPCR |= _BV(PB4);
9
}

Ist die richtige Version!!!!! ARGGGGGG...... (da sieht man den Wald vor 
lauter Bäumen nicht)!!!!!!!!!!

Trotzdem DANKE, jetzt läuft mein Testprogramm ganz durch!!!!!!!!

Grüße Philipp

von Stefan E. (sternst)


Lesenswert?

Und so soll das jetzt funktionieren? Das bezweifle ich doch ganz stark. 
Statt des SPCR gehört da PORTB hin. Und die High/Low-Logik war in der 
ersten Version richtig, und ist jetzt falsch.

von Alexander N. (fd0)


Lesenswert?

Hi,

Philipp H. schrieb:
> Darf ich mal fragen wie lange es so in etwa gedauert hat vom ersten
> lesen des ENC28J60 Datasheets bis du die erste stabile Version eines
> Treibers hattest?

Puh, die erste Version eines Treibers bereits nach ein paar Tagen, da 
hatten anderen auch schon vorgearbeitet, ich hab das nur nochmal 
"sauber" implementiert.  Bis das stabil war, dauerte aber noch etwas 
laenger (der enc28j60 hat auch ein paar sehr nervige Bugs, auf die man 
dann im Treiber eingehen muss, schau mal in die Errata...)

Stefan Ernst schrieb:
> Und so soll das jetzt funktionieren? Das bezweifle ich doch ganz stark.
> Statt des SPCR gehört da PORTB hin. Und die High/Low-Logik war in der
> ersten Version richtig, und ist jetzt falsch.

Korrekt.  Philipp, waer besser wenn du das auch korrigierst ;)

Grus,
- Alexander

von Philipp H. (magier)


Lesenswert?

Stefan Ernst schrieb:
> Und so soll das jetzt funktionieren? Das bezweifle ich doch ganz stark.
> Statt des SPCR gehört da PORTB hin. Und die High/Low-Logik war in der
> ersten Version richtig, und ist jetzt falsch.

Ja du hast recht......

Danke

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.