Getting started with ENC28J60 SPI-to-Ethernet Controller

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I've finally built a PCB with the AP7002 processor. The AP7002 is working nicely, and the next step is to start on the Linux software to work with the hardware.

Here is my previous post which I asked about the possibility of using this particular chip:

https://www.avrfreaks.net/index.p...

Now that I've successfully produced the hardware, I need to "wire up" the ENC28J60 in software. In my setup.c file, I have the following code:


static struct spi_board_info ethernet_ctrl_info[] __initdata = {
	{
		.modalias 		= "enc28j60",
		.controller_data	= (void*)GPIO_PIN_PB(3),
		.mode 			= SPI_MODE_0,
		.irq			= (GPIO_PIN_PB(25) + GPIO_IRQ_BASE),	
		.max_speed_hz		= 20000000,
		.bus_num		= 1,	
	},
};



/*Placed in the init function*/
at32_add_device_spi(1, ethernet_ctrl_info,
ARRAY_SIZE(ethernet_ctrl_info));

To load the module into the Linux kernel (linux-2.6.27.6.atmel.1), I use:

modprobe enc28j60

After running modprobe, there are no error messages printed at the console.

However, after running

ifconfig -a

All that I get is the loopback interface!

So I am wondering:

(1) How do I "map" the enc28j60 to the eth0 or eth1 interface?

(2) How do I set the ethernet address of the embedded system and the host? Is this any different for the enc28j60?

Note that I am using a "red" loopback Ethernet cable to connect to the chip. The cable plugs into the Ethernet connector on my custom PCB. After plug-in, the ENC28J60 starts blinking the Ethernet LEDs.

Probing the chip with an oscilloscope, I realized that the oscillators are working. The voltage levels are also the requisite 3.3V.

Last Edited: Sun. Feb 22, 2009 - 09:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's another question:

The datasheet mentions that the mode used for the ENC28J60 is SPI mode 0,0. Does this coincide with

 .mode          = SPI_MODE_0, 

used in my board setup code?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Moreover, the IRQ is normally HIGH, and then drops LOW to signal an interrupt.

GPIO_PIN_PB(25) is external interrupt 0 on the AP7002. Does the external interrupt require special handling in the board setup code?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

Could you route your board with 4 layers ?

With freq you are running ?

thks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually, msr2001, I routed the board with 8 layers, since I have the AP7002 processor. I think that it may be possible to use six layers with this particular processor in a BGA package, but I am not certain. I had to use 8 layers to be able to make connections between all of the components!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would like to report that I have been successful in getting started with the ENC28J60 processor. The problems that I document above are purely software setup problems. Here is a step-by-step list of what I did to start up the ENC28J60 chip. The chip is working nicely on my system, and I am able to run an FTP server and ping the system.

(1) Wire up the Ethernet connector LEDs so that the LEDB and LEDA pins are connected to the anode of each LED. A current-limiting resistor is on the cathode of each LED. Refer to the ENC28J60 datasheet for additional LED configuration information. Apparently, the manner in which the LEDs are configured will affect the operation of the ENC28J60. The datasheet gives more information on this. I am not certain whether this is the optimal way in which the LEDs are wired up, but this is the way which allows the controller to work on my board.

(2) The interrupt pin MUST be connected to a GPIO. On my custom board, I have the interrupt pin connected to IRQ-EXTINT0. This is the External Interrupt 0 pin, as well as general purpose pin PB25.

(2) In your board setup file (setup.c), add the ENC28J60 to the spi_board_info struct:

static struct spi_board_info spi1_board_info[] __initdata = {
	/*SPI Atmel dataflash*/
	{
		.modalias	= "mtd_dataflash",
		.max_speed_hz	= 8000000,
		.chip_select	= 0,
	},

	/*Ethernet controller*/
	{
		.modalias 		= "enc28j60",
		.controller_data	= (void*)GPIO_PIN_PB(3),
		.mode 			= SPI_MODE_0,
		.max_speed_hz		= 12000000,
		.chip_select		= 1,	
	},
};

Note that the speed of the SPI bus is 12 MHz, and that SPI MODE 0 is being used. You will have to update the controller_data according to the chip-select of the pin connected to the ENC28J60.

In your init function, you can add:


at32_select_gpio(GPIO_PIN_PB(25), 0);
spi1_board_info[1].irq = gpio_to_irq(GPIO_PIN_PB(25));
 at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));  

Apparently, hardware interrupts do not seem to work with the ENC28J60.

In the kernel xconfig, compile ENC28J60 as a module. To load the module at run-time:

modprobe enc28j60
ifconfig -a

To set the IP address and to bring the ethernet interface up:

ifconfig eth0 [IP_ADDRESS] up

In the above code, [IP_ADDRESS] is the IP ADDRESS that you wish to set on your embedded system. Do not include the brackets when you type the address.

At this point, you should have a functioning Ethernet interface. Using a cross-wired cable, you should be able to ping the system.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nkinar wrote:
I would like to report that I have been successful in getting started with the ENC28J60 processor. The problems that I document above are purely software setup problems. Here is a step-by-step list of what I did to start up the ENC28J60 chip. The chip is working nicely on my system, and I am able to run an FTP server and ping the system.
Awesome! But:
Quote:

	/*Ethernet controller*/
	{
		.modalias 		= "enc28j60",
		.controller_data	= (void*)GPIO_PIN_PB(3),
		.mode 			= SPI_MODE_0,
		.max_speed_hz		= 12000000,
		.chip_select		= 1,	
	},
};

Why do you have to set the controller data there? I thought controller_data for the atmel_spi controller just corresponded to the chip select pin. In fact, at32_add_device_spi() will override whatever you put in that field anyway (calculated from the chip_select number you put in)
Quote:
Apparently, hardware interrupts do not seem to work with the ENC28J60.
By hardware interrupts you mean interrupts through the EIC? There is a note in the enc28j60 driver:
Quote:
Board setup must set the relevant edge trigger type; level triggers won't currently work.
When you were using the external interrupt pin did you make sure to add a call setting the interrupt trigger mode to the appropriate edge? I suspect that the only reason the gpio one is working where the EIC one wasn't is that the gpio interrupt can only be edge triggered but the EIC will default to a level interrupt (ISTR)
Quote:
In the kernel xconfig, compile ENC28J60 as a module.
I'm guessing that it all works as expected if the driver is built in as well?

Anyway, well done, awesome!

-S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for your reply, S!

Quote:

Why do you have to set the controller data there? I thought controller_data for the atmel_spi controller just corresponded to the chip select pin. In fact, at32_add_device_spi() will override whatever you put in that field anyway (calculated from the chip_select number you put in)

Actually, I started to play around with different chip select pins (in preparation for communication with other SPI devices on the SPI bus), and I read the Atmel app note AVR32401. On page 4 of that app note, the controller_data is being set up as (*void)GPIO_PIN_PB(2). I agree that it would be possible to simply use ".chip_select = 1", but if I wanted to select another GPIO, I think that I could simply set it up in the controller_data.

Quote:

When you were using the external interrupt pin did you make sure to add a call setting the interrupt trigger mode to the appropriate edge? I suspect that the only reason the gpio one is working where the EIC one wasn't is that the gpio interrupt can only be edge triggered but the EIC will default to a level interrupt (ISTR)

Ah, perhaps this would work as well. Could I see an example of how to do this?

Quote:

I'm guessing that it all works as expected if the driver is built in as well?

Yes, the driver should work, but as personal preference, I like to compile as a module. Then I can simply modprobe and rmmod the module.

Quote:

Anyway, well done, awesome!

Thank you so much for your kind words, S. I had to do a lot of playing around to get the SPI-to-Ethernet bridge working on the AVR32 architecture.

Regarding the speed of the SPI bus, I was actually tipped off by one of the ENC28J60 driver writers (Claudio Lanconelli) about the suggested 12 MHz speed limit.

I've tried many different speeds, but I am not certain as to why this one works. In the ENC28J60 datasheet, 20 MHz is the maximum. For some strange reason, the ENC28J60 is very picky about the speed of the SPI bus. So I can say that 12 MHz does indeed work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nkinar wrote:
Actually, I started to play around with different chip select pins (in preparation for communication with other SPI devices on the SPI bus), and I read the Atmel app note AVR32401. On page 4 of that app note, the controller_data is being set up as (*void)GPIO_PIN_PB(2). I agree that it would be possible to simply use ".chip_select = 1", but if I wanted to select another GPIO, I think that I could simply set it up in the controller_data.
Ah yeah, indeed controller_data is only set if it isn't already. Coolies.
Quote:
Ah, perhaps this would work as well. Could I see an example of how to do this?

 set_irq_type(IRQ_NUMBER, type); 

where "type" is a bitwise-or of the different available modes:

IRQ_TYPE_EDGE_RISING
IRQ_TYPE_EDGE_FALLING
IRQ_TYPE_LEVEL_HIGH
IRQ_TYPE_LEVEL_LOW

-S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, Squidgit! I'll give the function a try in the morning (it's past midnight here in Canada)...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I modified the code to set up the external interrupt. Here is what I did in the spi_board_info struct:

static struct spi_board_info spi1_board_info[] __initdata = {
	/*SPI Atmel dataflash*/
	{
		.modalias	= "mtd_dataflash",
		.max_speed_hz	= 8000000,
		.chip_select	= 0,
	},

	/*Ethernet controller*/
	{
		.modalias 		= "enc28j60",
		.mode 			= SPI_MODE_0,
		.irq			= AT32_EXTINT(0),
		.max_speed_hz		= 12000000,
		.chip_select		= 1,	
	},
};

In the static int __init atngw100_init(void) function, I have:

	at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));

In the static int __init atngw100_arch_init(void) function
of the board setup code, I placed the function:


set_irq_type(AT32_EXTINT(0), IRQ_TYPE_EDGE_FALLING );

This works quite well! I am able to load the ENC28J60 driver and ping the system. So thank you so much for your suggestion, Squidgit!

I've also noticed an increase in speed when I perform FTP transfers. When using the software interrupt, I've noticed that some passive FTP transfers would "lock-up" the kernel. For example, when transferring a new uImage to the /boot/ directory, the kernel would become unresponsive when transferring ASCII mode data.

Now that I have a hardware interrupt, these particular FTP transfers do not "lock up" the kernel.

So thank you so much for your help, S!

:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excellent, you're very welcome nkinar.

-S.