Skip to content

Olduino Web Server Low Level Code

February 25, 2014

This post will go through the code that actually sends something out over ethernet/the internet.

The routine that you see most often in the high level code is send0s(<whatever>) where whatever is a string that we want to include in the outgoing web page.  Send0s just calculates the length of the string using strlen() and sends it along to send0 as follows(the reason for the 0 in the routine names is that we are addressing socket 0):

int send0s(char* what){
	return send0((unsigned char *)what,strlen(what));

send0 is the most complex routine that you’ll see here. After checking to make sure there is something to send(buflen>0) it reads the register in the wiznet chip that says how much room is available and loops until there is enough(with a timeout limit). Then it reads a pointer from the wiznet to where in the buffer the free space starts. It calls SPI_WriteN to transfer the data to the chip. Finally it updates the write pointer and sends a command to the wiznet to send the data and waits for it co complete.

unsigned int send0(unsigned char *buf,unsigned int buflen){
    unsigned int ptr,offaddr,realaddr,txsize,timeout;
    if (buflen &amp;lt;= 0) return 0;
    // Make sure the TX Free Size Register is available
    txsize=(((txsize &amp;amp; 0x00FF) &amp;lt;&amp;lt; 8 ) + SPI_Read(SO_TX_FSR + 1));
    while (txsize &amp;lt; buflen) {
     txsize=(((txsize &amp;amp; 0x00FF) &amp;lt;&amp;lt; 8 ) + SPI_Read(SO_TX_FSR + 1));
     // Timeout for approx 1000 ms
     if (timeout++ &amp;gt; 1000) {
       printf(&amp;quot;TX Free Size Error!\n&amp;quot;);
       // Disconnect the connection
       return 0;

   // Read the Tx Write Pointer
   ptr = SPI_Read(S0_TX_WR);
   offaddr = (((ptr &amp;amp; 0x00FF) &amp;lt;&amp;lt; 8 ) + SPI_Read(S0_TX_WR + 1));

    // Increase the S0_TX_WR value, so it point to the next transmit
    SPI_Write(S0_TX_WR,(offaddr &amp;amp; 0xFF00) &amp;gt;&amp;gt; 8 );
    SPI_Write(S0_TX_WR + 1,(offaddr &amp;amp; 0x00FF));

    // Now Send the SEND command
    // Wait for Sending Process

    return 1;

SPI_WriteN starts the process of actually sending data to the wiznet chip over SPI. Each packet to go to the wiznet is 4 bytes with a command, a two byte address, and a byte of data. There is a C language structure SPINpayload that packages the opcode, address, and data for transmission. SPI_WriteN starts by setting the opcode, then for each byte, it sets the address to be written and the data to go there. The “realaddr=TXBUFADDR+(offaddr&TX_BUF_MASK);” mumbo jumbo is done because the buffer can wrap around as we increase offaddr. The enablewiz()/disablewiz() lines are inline assebly routines that flip bit 7 on OUT 4 which controls whether or not the wiznet chip should pay attention to the SPI bus. You see them picking up a value from the global variable _PIN4 which always stores the state of OUT 4 so you can flip individual bits. I could have used digitalWrite(7,x) to do the same thing but this is quicker – 7 instructions instead of almost 50. If you look at the last couple of lines in the code block below you can see the more or less recognizable 1802 assembler statements separated by ‘\n’ for newline.

void SPI_WriteN(unsigned int offaddr,unsigned char *data, unsigned int N){
  SPINpayload.opcode=WIZNET_WRITE_OPCODE; //prep for sequence of writes
  while(N&amp;gt;0){//for all the bytes to be transferred
  	enablewiz();   			// Activate the CS pin
  	disablewiz();				// make CS pin not active
struct SPIsequence{
	unsigned char opcode;
	unsigned int realaddr;
	unsigned char data;
} SPINpayload;

//high level on 7(SS) disables the w5100 from the spi bus
#define disablewiz()  asm(&amp;quot;	ldad memaddr,_PIN4\n	ldn memaddr\n	ori 0x80\n	str memaddr\n&amp;quot; \
  			&amp;quot;	sex memaddr\n	out 4\n	sex 2\n&amp;quot;); 

The code for spiSendN is done in the same style but it’s laid out a bit better below. It’s really just a matter of writing the 4 bytes out to OUT 6 which addressse the 4021 Master Out Shift Register (MOSR) and tells the AVR to generate 8 clock pulses. Also below is the more basic routine spixfer which writes a single byte to MOSR then reads the response from MISR – the 74595 input shift register.

		asm(&amp;quot;	align 16\n&amp;quot; //make sure jumps stay on page
			&amp;quot;	sex 12\n&amp;quot; //point X to the buffer
			&amp;quot;$$spiSendLoop:\n&amp;quot; //we will do this N times
			&amp;quot;	out 6\n&amp;quot;  //this sends out a byte
			&amp;quot;	dec 13\n&amp;quot; //decrement the byte count
			&amp;quot;	glo 13\n&amp;quot; //check bottom byte of counter
			&amp;quot;	bnz $$spiSendLoop\n&amp;quot; //back for more if needed - 4 inst. per byte
			&amp;quot;	ghi 13\n&amp;quot; //check high byte of counter if necessary
			&amp;quot;	bnz $$spiSendLoop\n&amp;quot;
			&amp;quot;	sex 2\n&amp;quot;  //reset X register
			&amp;quot;	cretn\n&amp;quot; //return to caller

unsigned char spixfer(unsigned char value){
	//this code depends on the argument being in Reg 12 and returns its value in reg 15
	asm(&amp;quot;	glo 12\n&amp;quot; //get the char to be sent
		&amp;quot;	dec 2\n&amp;quot;  //make a work area
		&amp;quot;	str 2\n&amp;quot;  //save the character
		&amp;quot;	out 6\n&amp;quot;  //this loads the shift register and starts the outboard clock
		&amp;quot;	dec 2\n&amp;quot;
		&amp;quot;	sex 2\n&amp;quot;  //delay to allow outbound shift to complete
		&amp;quot;	inp 6\n&amp;quot;  //read the shift register
		&amp;quot;	plo 15\n&amp;quot; //leave it in 15
		&amp;quot;	inc 2\n&amp;quot;  //restore the stack
		&amp;quot;	cretn\n&amp;quot;);


From → web server

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: