Forum: Mikrocontroller und Digitale Elektronik Ethernet beim LPC2468


von Tobias P. (hubertus)


Lesenswert?

Hallo,
ich möchte schon lange was mit Ethernet auf meinem LPC2468 machen, doch 
habe ich nach anfänglichen Versuchen mit dem lwip aufgegeben und mich 
anderen Dingen zugewandt (wie z.B. RTOS). Nachdem ich nun uC/OS am 
laufen habe, dachte ich, es wäre wieder einmal an der Zeit, das mit dem 
Ethernet nochmal in Angriff zu nehmen.
Dazu musste ich natürlich meinen Ethernet-Driver so zurechtbiegen, dass 
er mit uC/OS zurechtkommt. Herausgekommen ist folgender Code:
1
#include "mac.h"
2
3
#include "lpc2468.h"
4
5
#include "ucos_ii.h"
6
#include "os_cpu.h"
7
8
SDRAM OS_EVENT *macsem;
9
SDRAM OS_EVENT *macmbox;
10
11
void MAC_fInitialize(OS_EVENT* m)
12
{
13
  tDWord dwID;
14
  macsem = OSSemCreate(0);
15
  macmbox = m;
16
  tByte err;
17
18
  PCONP |= BIT_30; /* power up the mac */
19
  PINSEL2 |= (BIT_00 | BIT_02 | BIT_08 | BIT_16 | /* select the rmii pins */
20
    BIT_18 | BIT_20 | BIT_28 | BIT_30);
21
  PINSEL3 |= (BIT_00 | BIT_02); /* select the mdio and clk pins */
22
  MAC1 =  (BIT_08 | BIT_09 | BIT_10 | BIT_11 | BIT_14 | BIT_15); /* reset */
23
  Command = (BIT_03 | BIT_04 | BIT_05); /* reset the mac */
24
  MAC1 = 0; /* deassert the resets */
25
  MAC2 = 0; /* set the mac into the default state */
26
  IPGR = IPGR_DEFAULT; /* set the back-to-back inter packet gap */
27
  CLRT = CLRT_DEFAULT; /* set the collision window and retransmission count */
28
  MAXF = MAX_FRAME; /* maximum frame length */
29
30
  MCFG = (BIT_02 | BIT_03 | BIT_04 | BIT_15); /* divide clk by 20, reset mii */
31
  MCFG ^= BIT_15; /* deassert the mii reset */
32
  MCMD = 0; /* clear the command register */
33
  Command = (BIT_09 | BIT_06 /*| BIT_07*/); /* enable the rmii */
34
  SUPP = BIT_08; /* enable 100MBit mode */
35
  MAC_fWritePHY(PHY_BCR, BIT_15); /* reset the phy */
36
  while(MAC_fReadPHY(PHY_BCR) & BIT_15); /* wait until reset is done */
37
  dwID = (MAC_fReadPHY(PHY_ID1) << 16); /* get PHY manufacturer and revision */
38
  dwID |= (MAC_fReadPHY(PHY_ID2) & 0xFFF0);
39
  if(dwID != KSZ8001_ID) /* check if this is a KSZ8001L PHY */
40
  {
41
    return; /* abort */
42
  }
43
  MAC_fWritePHY(PHY_ICS, BIT_08 | BIT_10); /* enable link interrupt */
44
  MAC_fWritePHY(PHY_CTL, BIT_14); /* enable the link and act LEDs */
45
  MAC_fWritePHY(PHY_BCR, BIT_12 | BIT_09); /* enable & start autonegotiation */
46
47
  PINSEL4 |= BIT_22; /* enable EINT1 interrupt */
48
  VICVectPriority15 = 0; /* set up the phy interrupt */
49
  VICVectAddr15 = (tDWord)MAC_fPHYInterrupt;
50
  VICIntEnable |= BIT_15; /* enable the link up/down interrupt */
51
52
  OSSemPend(macsem, 0, &err); /* wait until link established */
53
54
  SA0 = (MAC_ADDRESS1 << 8) | MAC_ADDRESS2; /* set up the mac address */
55
  SA1 = (MAC_ADDRESS3 << 8) | MAC_ADDRESS4;
56
  SA2 = (MAC_ADDRESS5 << 8) | MAC_ADDRESS6;
57
  MAC_fInitRxDescriptors(); /* initialize rx descriptors */
58
  MAC_fInitTxDescriptors(); /* initialize tx descriptors */
59
60
  RxFilterCtrl = (BIT_01 | BIT_02 | BIT_05); /* uni-, multi- and broadcast */
61
62
  Command |= (BIT_00 | BIT_01); /* enable receiver and transmitter */
63
  MAC1 |= BIT_00; /* enable the receiver */
64
65
  VICVectPriority21 = 0; /* set up the mac interrupt */
66
  VICVectAddr21 = (tDWord)MAC_fMACInterrupt;
67
  VICIntEnable |= BIT_21; /* enable the mac interrupt */
68
  IntClear = 0xFFFFFFFF;
69
  IntEnable = BIT_03;
70
  return;
71
}
72
73
static void MAC_fPHYInterrupt(void)
74
{
75
  tWord wInterrupt = MAC_fReadPHY(PHY_ICS); /* get the interrupt source */
76
  if(wInterrupt & BIT_00)
77
  {
78
    tWord wMode = (MAC_fReadPHY(PHY_100BTX) & (BIT_04 | BIT_03 | BIT_02));
79
    switch(wMode)
80
    {
81
      case MODE_10M_HALF_DUPLEX:
82
      {
83
        MAC2 = (BIT_04 | BIT_05 | BIT_07); /* crc, padding, half duplex */
84
        SUPP = 0; /* 10MBit mode */
85
        Command &= ~BIT_10; /* issue half duplex command */
86
        IPGT = 0x12; /* default setting for 10 MBits, half duplex */
87
        break;
88
      }
89
      case MODE_10M_FULL_DUPLEX:
90
      {
91
        MAC2 = (BIT_00 | BIT_04 | BIT_05 | BIT_07); /* crc, padding, full duplex */
92
        SUPP = 0; /* 10MBit mode */
93
        Command |= BIT_10; /* issue full-duplex command */
94
        IPGT = 0x15; /* default setting for 10 MBits, full duplex */
95
        break;
96
      }
97
      case MODE_100M_HALF_DUPLEX:
98
      {
99
        MAC2 = (BIT_04 | BIT_05 | BIT_07); /* crc, padding, half duplex */
100
        SUPP = 0; /* enable 100MBit mode */
101
        Command &= ~BIT_10; /* issue half-duplex command */
102
        IPGT = 0x12; /* default setting for 100 MBits, half duplex */
103
        break;
104
      }
105
      case MODE_100M_FULL_DUPLEX:
106
      {
107
        MAC2 = (BIT_00 | BIT_04 | BIT_05 | BIT_07); /* crc, padding, full duplex */
108
        SUPP = BIT_08; /* enable 100MBit mode */
109
        Command |= BIT_10; /* issue full-duplex command */
110
        IPGT = 0x15; /* default setting for 100 MBits, full duplex */
111
        break;
112
      }
113
    }
114
    OSSemPost(macsem);
115
  }
116
  else if(wInterrupt & BIT_02)
117
  {
118
  }
119
  EXTINT |= BIT_01;
120
}
121
122
static void MAC_fMACInterrupt(void)
123
{
124
  tDWord dwIntStatus = IntStatus; /* get the interrupt source */
125
  if(dwIntStatus & BIT_00) /* rx overrun */
126
  {
127
    IntClear = BIT_00; /* acknowledge interrupt */
128
  }
129
  if(dwIntStatus & BIT_01) /* rx error */
130
  {
131
    IntClear = BIT_01; /* acknowledge interrupt */
132
  }
133
  if(dwIntStatus & BIT_02) /* rx finished */
134
  {
135
    IntClear = BIT_02; /* acknowledge interrupt */
136
  }
137
  if(dwIntStatus & BIT_03) /* rx done */
138
  {
139
    tWord wIdx = RxConsumeIndex; /* get the index of the next received frame */
140
    OSMboxPost(macmbox, (void*)dwRxDescriptors[wIdx * 2]);
141
    wIdx++;
142
    if(wIdx == MAC_RX_FRAGMENTS)
143
      wIdx = 0;
144
    RxConsumeIndex = wIdx;
145
    IntClear = BIT_03;
146
  }
147
  if(dwIntStatus & BIT_04) /* tx underrun */
148
  {
149
    IntClear = BIT_04; /* acknowledge interrupt */
150
  }
151
  if(dwIntStatus & BIT_05) /* tx error */
152
  {
153
    IntClear = BIT_05; /* acknowledge interrupt */
154
  }
155
  if(dwIntStatus & BIT_06) /* tx finished */
156
  {
157
    IntClear = BIT_06; /* acknowledge interrupt */
158
  }
159
  if(dwIntStatus & BIT_07) /* tx done */
160
  {
161
    IntClear = BIT_07; /* acknowledge interrupt */
162
  }
163
  if(dwIntStatus & BIT_12) /* software interrupt */
164
  {
165
    IntClear = BIT_12; /* acknowledge interrupt */
166
  }
167
  if(dwIntStatus & BIT_13) /* wakeup */
168
  {
169
    IntClear = BIT_13; /* acknowledge interrupt */
170
  }
171
}

Dazu muss ich noch sagen, dass der Interrupt-Pin des Micrel KSZ8001 Phy 
mit dem External Interrupt 1 des LPC verbunden ist, sodass der 
MAC_fPHYInterrupt ausgeführt wird, wenn man ein Netzwerkkabel ein- oder 
aussteckt.
Der MAC selber hingegen hat den MAC_fMACInterrupt zugeordnet.
Der Driver funktioniert im Grossen und Ganzen, jedoch habe ich 
festgestellt, dass irgendwo der Wurm drin ist. Denn sporadisch - ich 
kann das Verhalten weder nachvollziehen noch irgendwie reproduzieren - 
wird im MAC_fInterrupt der "RX Error" interrupt ausgeführt. Das sollte 
ja eigentlich nicht passieren?! :o ist an meiner Initialisierung was 
faul, oder wodurch kann dieser RX error verursacht werden? Die 
Initialisierung habe ich mehr oder weniger von einem Sample von Keil 
entnommen. Kann natürlich schon sein, dass da was nicht ganz passt, da 
diese für ein anderes Board war - mein Board ist ja selbst gemacht.
Wenn natürlich da irgendwelche RX Errors auftauchen, muss ich mich nun 
natürlich nicht wundern, dass der lwip nie richtig gelaufen ist. 
Irgendwas bereitet der Hardware da Probleme, und ich weiss nicht was.
Könnt ihr mir helfen?

Ach ja, falls es von Belang ist:
MAC_fInitialize kriegt vom aufrufenden Task einen Pointer auf eine 
Mailbox mit. Dieser Pointer wird dann in einer internen Variable 
abgelegt (macmbox). Trifft ein Ethernet-Frame ein, wird der 
MAC-Interrupt aufgerufen, welcher die Adresse des Memory, wo das 
empfangene Paket liegt, ermittelt und diese als Message in diese Mailbox 
postet. Der Task, welcher für das Ethernet zuständig ist, wartet auf 
diese Message. Nach erhalt wird er vom OS ausgeführt, da er eine hohe 
Priorität hat. Der Task kennt dann die Adresse, wo ein Paket im Memory 
liegt, und kann dieses verarbeiten.

Ich hoffe, mir kann jemand einen Tipp geben!

Vielen Dank schon im Voraus.

Gruss Tobias

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.