ATMega128 / ENC28J60 / Procyon AVRLIB

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

Hello,

I'll start playing with the ENC28J60 module from ETT shortly, and I have some questions.
I plan to use Pascal Stang's libs (as my project is completely built with this one) and I have already compiled the complete thing. This was the first step.

Now comes the "stupid" question from a novice AVR user. The module is designed for SPI communication. Good. Does that mean it has to be wired to the same pins as the ones used for ISP (PB0..PB3) ? Is it possible to use other pins easily (doing soft SPI I guess ?) Right now I use JTAG for programming/debugging so this is not an issue.

Any hints/ good or bad experience on playing with this chip (with erratas) ? Maybe another good TCP/IP stack suited for Mega128, while not interfering with avrlib ?

Here is my plan:
1- being able to ping the device
2- being to broadcast UDP to the network
3- port SNMP agent from microchip (http://ww1.microchip.com/downloa...)
4- sleep

Bye !

V.

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

On a mega128 different pins are used for ISP and SPI (it's almost unique amongst AVRs in this respect, most others use SPI for ISP too)

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

Thanks.
So SPI is PB0..PB3 on Mega128. I use those for ISP as well. Which others can be used for ISP ? I can't figure it from the datasheet and the alternate ports functions.

V.

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

Quote:

Which others can be used for ISP ?

Look in the datasheet section entitled "Serial Programming Pin Mapping".

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

hello all. i m new to enc28j60. i want to interface Atmega 88 with enc28j60. i want to kno the diff b/w enc28j60/sp and enc28j60-i/sp

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

Hello,

Still trying to get something from my ENC28J60 from ETT, using AVRLib.
I have one question, as usual:
I set-up my config file like this (it is a Mega128):

// ENC28J60 SPI port
#define ENC28J60_SPI_PORT PORTB
#define ENC28J60_SPI_DDR DDRB
#define ENC28J60_SPI_SCK 1
#define ENC28J60_SPI_MOSI 2
#define ENC28J60_SPI_MISO 3
#define ENC28J60_SPI_SS 4
// ENC28J60 control port
#define ENC28J60_CONTROL_PORT PORTB
#define ENC28J60_CONTROL_DDR DDRB
#define ENC28J60_CONTROL_CS 4

Is it a bad idea to put SPI_SS and CONTROL_CS on the same pin, like in the stock example ?

The ETT board offers the following signals on the header:

Name	Type	Function
CS	INPUT 	Signal Enable/Disable the SPI Bus Interface of ENC28J60
		CS = 0 is Enable the SPI Bus Interface of ENC28J60
		CS = 1 is Disable the SPI Bus Interface of ENC28J60
SDO	OUTPUT	Signal Serial Data Output
SDI	INPUT	Signal Serial Data Input
SCK	INPUT	Signal Serial Clock
INT	OUTPUT	Signal Interrupt Active Logic 0
RST	INPUT	Signal RESET Active Logic 0
WOL	OUTPUT	Signal Wake-up on LAN interrupt Active Logic 0
CLKO	OUTPUT	Signal Programmable clock output
LEDA	OUTPUT	Display status of Signal LINK
LEDB	OUTPUT	Display status of Signal ACT

I wired CS to PB4, SDO to PB3, SDI to PB2, SCK to PB1. Nothing else.

Any hint ? Timing issues (running with 16Mhz ext crystal, latest AVRLib)

Thanks.
V.

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

I don't think it matters as long as you don't intend for your mega88 to be a slave in SPI mode at any time during program execution.

The SS pin is only useful if your microcontroller is an SPI slave - to get its attention. Since the mega88 is probably going to be a master, you can use the SS pin as a regular GPIO pin - initialize it as an output and use it to drive the slave select pin of the enc28j60. You could also use any other GPIO pin for the same purpose if you feel uncomfortable about it.

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

Regarding your wiring info, I have a mega644 driving an enc28j60 on my workbench right now. Here's the lineup:

=enc28j60=      =Micrcontroller=
/CS       ->     any GPIO you want.  (I used PB4)
SO        ->     MOSI
SI        ->     MISO
SCK       ->     SCK

Works fine for the most part. I am troubleshooting a bug where the driver goes down after several hours of pinging - latest change seems to have fixed that but we'll see... :roll:

Also, I don't know if you're polling or not. I started with polling but then moved to an interrupt-based approach which I like a lot better. Be sure you keep reading packets until the packet length is 0 to clear the enc28j60's buffer.

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

Progressing, thanks to all of you (I had mixed MOSI and MISO.... crossover)

Network Stack Example
Initializing Periodic Timer
Initializing Network Stack
Initializing Network Device

Initializing ARP cache

Initializing Addressing

Eth Addr is: 41:51:55:41:30:31
IP Addr is: 192.168.2.31
Network Stack is up!
Starting packet receive loop

Now, I need to find out wy terminal input is not working.... but first, some sleep !!!!

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

Hello,

I found out why serial input doesn't work. The AVR seems to hang after a few while(1) loops.
Here is the code I use:

//*****************************************************************************
// File Name	: netstacktest.c
//
// Title		: example usage of network library functions
// Revision		: 1.0
// Notes		:
// Target MCU	: Atmel AVR series
// Editor Tabs	: 4
//
// Revision History:
// When			Who			Description of change
// -----------	-----------	-----------------------
// 10-Aug-2005	pstang		Created the program
//*****************************************************************************

#include 
#include 
#include 

// include avrlib basics
#include "global.h"
#include "timer128.h"
#include "uart.h"
#include "rprintf.h"
#include "vt100.h"
#include "debug.h"

// include network support
#include "net/net.h"
#include "net/nic.h"
#include "net/arp.h"
#include "net/icmp.h"
#include "net/ip.h"
#include "net/netstack.h"

// network defines
#define IPADDRESS			IPDOT(192l,168l,2l,31l)
#define NETMASK				IPDOT(255l,255l,255l,0l)
#define GATEWAY				IPDOT(192l,168l,2l,253l)

#define LOOPBACK_PORT		7		// UDP packets sent to this port will be returned to sender
#define CONTROL_PORT		4950	// UDP packets sent to this port will be used for control
#define SERIAL_PORT			4951	// UDP packets sent to this port will be printed via serial

// timer defines
#define TIMER_PRESCALE		1024
#define TIMER_INTERVAL		(F_CPU/TIMER_PRESCALE/100)	// 100ms interval

// global variables
static volatile unsigned long UptimeMs;

// functions
void processCommand(u16 len, u08* data);
void serviceLocal(void);
void systickHandler(void);

// prototypes
int main(void)
{
	struct netEthAddr myEthAddress;

	timerInit();
	timerPause(500);
	uartInit();
	uartSetBaudRate(115200);
	rprintfInit(uartSendByte);
	timerPause(200);
	//vt100ClearScreen();
	rprintf("\nNetwork Stack Example\n");

	// initialize systick timer
	rprintf("Initializing Periodic Timer\n");
	timer2SetPrescaler(TIMERRTC_CLK_DIV1024);
	timerAttach(TIMER2OVERFLOW_INT, systickHandler);

	// init network stack
	rprintf("Initializing Network Stack\n");
	netstackInit(IPADDRESS, NETMASK, GATEWAY);

	nicGetMacAddress(&myEthAddress.addr[0]);
	rprintfProgStrM("Eth Addr is: "); netPrintEthAddr(&myEthAddress);		rprintfCRLF();
	rprintfProgStrM("IP  Addr is: "); netPrintIPAddr(ipGetConfig()->ip);	rprintfCRLF();

	rprintf("Network Stack is up!\n");
	rprintf("Starting packet receive loop\n");

	while(1)
	{
		// service local stuff
		serviceLocal();
		// service the network
		//netstackService();
	}

	return 0;
}


void netstackUDPIPProcess(unsigned int len, udpip_hdr* packet)
{
	u16 payloadlen=0;
	u08* payloaddata=0;
	u16 i;

	// get UDP payload length
	payloadlen = htons(packet->udp.udplen);
	payloadlen -= 8; // subtract header
	// get UDP payload data
	payloaddata = &((unsigned char*)packet)[IP_HEADER_LEN+UDP_HEADER_LEN];

	rprintf("UDP packet, len: %d\n", len);
//	debugPrintHexTable(len, (unsigned char*)packet);

	if(packet->udp.destport == HTONS(CONTROL_PORT))
	{
		// command packet
		processCommand(payloadlen, payloaddata);
	}
	else if(packet->udp.destport == HTONS(SERIAL_PORT))
	{
		// serial output
		for(i=0; iudp.destport == HTONS(LOOPBACK_PORT))
	{
		// loopback - return packet to sender
		udpSend(htonl(packet->ip.srcipaddr), LOOPBACK_PORT, payloadlen, payloaddata);
	}
}


void netstackTCPIPProcess(unsigned int len, tcpip_hdr* packet)
{
	rprintf("Received TCP/IP Packet: len=%d\n", len);
}


void processCommand(u16 len, u08* data)
{
	rprintf("Received UDP command: CMD=0x%x  ARG0=0x%x\n", data[0], data[1]);

	// do something based on command
	switch(data[0])
	{
	case 'C':	// set PORTC
		PORTC = data[1];
		break;	// set DDRC
	case 'c':
		PORTC = data[1];
		break;
	default:
		rprintf("Unknown UDP command\n");
		break;
	}
}

void serviceLocal(void)
{
	int c;
	char buffer[100];
	//rprintf("Service\n");

	if( (c = uartGetByte()) != -1)
	{
		// echo command to terminal
		rprintfChar(c);
		// process command
		switch(c)
		{
		case 'i':
			rprintfProgStrM("\nInitializing Ethernet Controller\n");
			nicInit();
			break;
		case 'd':
			rprintfProgStrM("\nEthernet Controller State\n");
			nicRegDump();
			break;
		case 't':
			rprintfProgStrM("\nCurrent Uptime: ");
			rprintfNum(10, 9, FALSE, ' ', UptimeMs);
			rprintfProgStrM("ms\n");
			break;
		case 'c':
			rprintfProgStrM("\nCrashing System....\n");
			while(1);
			break;
		case 'u':
			rprintfProgStrM("\nSending UDP packet\n");
			strcpy(&buffer[ETH_HEADER_LEN+IP_HEADER_LEN+UDP_HEADER_LEN], "hello");
			udpSend((ipGetConfig()->ip|~ipGetConfig()->netmask), CONTROL_PORT, 6, &buffer[ETH_HEADER_LEN+IP_HEADER_LEN+UDP_HEADER_LEN]);
			break;
		case '?':
			rprintfProgStrM("\nCommands:\n");
			rprintfProgStrM("(i) Initialize Ethernet Controller\n");
			rprintfProgStrM("(d) Dump Ethernet Controller State\n");
			rprintfProgStrM("(u) Send Broadcast UDP frame\n");
			rprintfProgStrM("(t) Print current uptime\n");
			rprintfProgStrM("(?) Help\n");
			break;
		case '\r\n':
		default:
			break;
		}
		// print new prompt
		rprintfProgStrM("\ncmd>");
	}
}

void systickHandler(void)
{
	// set timer for 10ms interval
	TCNT2 = (unsigned char)-TIMER_INTERVAL;
	// count up on uptime
	UptimeMs += 10;
}


	while(1)
	{
		// service local stuff
		serviceLocal();
		// service the network
		//netstackService();
	}
	

If I comment the netstackService call, serial console works very well. Now, I have to find out why, and where the program stops. I guess JTAG will be my friend ?
I'm able to talk to the enc28j60, as I can retrieve the MAC.

One thing is strange, however: the UptimeMs variable never gets incremented. I always have 0, when sending the t command. I ported the code for mega128 (uart2, timer128). Maybe I missed something ?
Let's hope I'm able do to a "ping" shorlty.....

V.

Edit: I tracked down one problem to the enc28j60WriteOp function:

	// issue write command
	SPDR = op | (address & ADDR_MASK);
	rprintf("\t WHILE SPSR SPIF 1\n");
	while(!(SPSR & (1<<SPIF)));
	// write data
	SPDR = data;
	rprintf("\t WHILE SPSR SPIF 2\n");
	while(!(SPSR & (1<<SPIF)));

When the AVR "hangs", I don't get the WHILE SPSR SPIF 2 text:


BEGIN SETBANK

END SETBANK

BEGIN WRITEOP
	 SET CONTROL PORT
	 WHILE SPSR SPIF 1
	 WHILE SPSR SPIF 2
	 RELEASE CONTROL PORT

END WRITEOP

BEGIN SETBANK

END SETBANK

BEGIN WRITEOP
	 SET CONTROL PORT
	 WHILE SPSR SPIF 1
	 WHILE SPSR SPIF 2

I'll investigate how SPI works.... maybe also a look in errata of enc28j60 can help. Let's see !

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

Hello !

I finally made it !
I based my program on the tuxgraphics code; It works nicely.
A sa recap:
- ATMega Stamp 128 board from futurlec (ETT), 16Mhz clocked
- Ethernet mini-module from futurlec (ETT), based on enc28j60
- Tuxgraphics code

Code to update:

#define ENC28J60_CONTROL_PORT   PORTB
#define ENC28J60_CONTROL_DDR    DDRB
#define ENC28J60_CONTROL_CS     0

Init phase of SPI in enc28j60Init function:

cbi(PORTB, 1);  // set SCK low
sbi(DDRB, 1);   // set SCK as output
cbi(DDRB, 3);   // set MISO as input
sbi(DDRB, 2);   // set MOSI as output
sbi(DDRB, 0);   // SS must be output for Master mode to work
// setup SPI interface :
// master mode
sbi(SPCR, MSTR);
// clock = f/16
cbi(SPCR, SPR0);
sbi(SPCR, SPR1);
// select clock phase positive-going in middle of data
cbi(SPCR, CPOL);
// Data order MSB first
cbi(SPCR,DORD);
// enable SPI
sbi(SPCR, SPE)

And then:

F:\Projects\udpcom\win>ping 192.168.2.26

Pinging 192.168.2.26 with 32 bytes of data:

Reply from 192.168.2.26: bytes=32 time=9ms TTL=64
Reply from 192.168.2.26: bytes=32 time=9ms TTL=64
Reply from 192.168.2.26: bytes=32 time=8ms TTL=64
Reply from 192.168.2.26: bytes=32 time=8ms TTL=64

Ping statistics for 192.168.2.26:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 8ms, Maximum = 9ms, Average = 8ms

Now I can move forward on the UDP/TCP server.... this opens a wide range of great applications (coffe machine, etc etc...)

Thanks for your help !

V.

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

But there is no SNMP in this demo.
Is there SNMP demo with ATMEGA?

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

rprintfNum(10, 9, FALSE, ' ', UptimeMs);

You don't access the UptimeMs variable atomically. In this instance the net effect would be displaying the wrong value occasionally. In other instances there might be more adverse reactions.

cli(); //depends on compiler
myUptimeMs = UptimeMs;
sei(); //depends on compiler
rprintfNum(10, 9, FALSE, ' ', myUptimeMs);

I wrote a tutorial on interrupts and abcminiuser (Dean) wrote some stuff also on interrupts with specific reference to WinAVR.