Pages

Monday, March 25, 2013

TCP/IP Ethernet Communication Using Stellaris ARM Cortex M3


     Stellaris ARM Cortex M3 is a product  of Texas Instruments (TI). Unlike powerful DSP’s or floating point microcontrollers, this microcontroller is not suitable for Power Electronics applications control system. However, due to its communication features it can be a very good monitoring processor. The microcontroller has an Ethernet module and its evaluation kit has already mounted Ethernet connector. I am using Stellaris ARM Cortex M3 for monitoring purposes in one of my Active Power Filter project. The microcontroller has the analog inputs for necessary channels like active filter current, line voltage, dc link voltage and heatsink temperatures. These analog information is converted to digital and sent to the Human Machine Interface (HMI) of automation system via Ethernet communication. This is the duty to be conducted by this microcontroller. In this article, I will try to explain the methods to conduct this applications with basic TCP/IP protocol and explain ,in detail, my solution.
     TI provides lots of sample codes and manuals for different applications. One of these sample codes and manuals is about TCP/IP protocol. There are two modules served for this purpose which are generated by Adam Dunkels namely uIP (micro IP) and lwIP (lightweight IP). One can use these methods to communicate to any device using Ethernet communication.
     uIP is smaller in size than lwIP. It contains basic functions to establish or finish the connection and sending or receiving data. uIP is not a complex structure thus; it is better for simple applications. lwIP , on the other hand, has more complex structure than uIP but its features are a bit more developed. Actually, for basic communication purposes, one can use uIP easily for Ethernet communications.
     My solution ,on the other hand, uses the open-sourced uIP stack ,however; I add my functions for establishing, finishing connections and receiving or sending data routins. By this way, I form a code that listens any of the device that requires connection establishment and sends the data immediately after receiving data from the host. 
     The physical connection is started when the host sends a broadcast in ARP (Adress Resolution Protocol), stating its own MAC adress, IP and the destination IP adress. If our microcontroller has the same IP adress of host requires, our system sends an answer stating its own MAC address and IP in ARP. After this process, now the host knows our IP address. To establish the connection the host sends a SYN flaged packet in order to initialize the sequence number and the rules of connection (like window size, protocol number IPv4, TCP protocol and its listening port). Our system turns an answer of SYN/ACK packet to understand the rules and port numberof the host. Finally, the host send and ACK packet to show that it succesfully establish the connection and ready to transfer data with the stated rules. After the synchronisation is handled, the host sends a data whenever it wants using the PSH,ACK flaged packet. This packet contains the data to be transfered and requires an acknowledge that the data is reached correctly. Once, our system get the PSH,ACK flaged packet, we immediately send an ACK packet to say that we succesfully get the data. After we successfully receive the data we send our information to the host using PSH,ACK flaged packet. Similarly, it is expected that the host sends ACK packet when it reads our information. The data sending cycle continues until the host finishes or reset the connection. To finish the connection, host sends FIN,ACK packet for healty ending of connection or RST,ACK packet if it needs to force the connection to end.
      My code for handling all these TCP/IP communication procedure is as stated below.

//
// HANDLING THE TCP/IP AND ARP PROTOCOLS - EMRE DURNA
//
long
EthernetPacketGetDMA(unsigned long ulBase, unsigned char *pucBuf, long lBufLen)
{
    int k,freq_index;
                long len;
                int Data_len = 0;
    // Check the arguments.
    ASSERT(ulBase == ETH_BASE);
    ASSERT(pucBuf != 0);
    ASSERT(lBufLen > 0);
        len=ROM_EthernetPacketGetNonBlocking(ulBase, pucBuf, lBufLen); // get the received packet on pointer pucBuf
                // ARP packets handled here
                                if(*(pucBuf+12)==0x08 && *(pucBuf+13)==0x06 && *(pucBuf+41)==DEFAULT_IPADDR3)
                                {
                                               for(k=0;k<6;k++)
                                               {ARP_SEND[k] = *(pucBuf+k+6);  // Dest MAC
                                               ARP_SEND[k+32] = *(pucBuf+k+6);   // target mac }
                                               for(k=0;k<4;k++)
                                               {ARP_SEND[k+38]=*(pucBuf+k+28); }
                                EthernetPacketPutDMA(ETH_BASE,ARP_SEND,42);
                                }

                    // IP packets handled here
                                if(*(pucBuf+12)==0x08 && *(pucBuf+13)==0x00) // IP packets here
                {
                                for(k=0;k<6;k++)                // destination MAC
                                               TCP_SEND[k]=*(pucBuf+k+6);
                                 if(TCP_SEND[19]==0xFF)   // identification increments every send procedure
                                               {TCP_SEND[19]=0x00;
                                               if(TCP_SEND[18]==0xFF)
                                                              TCP_SEND[18]=0x00;
                                               else
                                                              TCP_SEND[18]++;
                                               }
                                               else
                                                              TCP_SEND[19]++;
                                 for(k=0;k<4;k++)
                                               TCP_SEND[k+30]=*(pucBuf+k+26);     // Destination IP
                                               TCP_SEND[36]=*(pucBuf+34);     // Destination Port
                                               TCP_SEND[37]=*(pucBuf+35);
                                               TCP_SEND[46]=0x50; // header length
                                 ///****** SYN packets here
                                if(*(pucBuf+47)==0x02)  //SYN packets
                                {    for(k=0;k<4;k++)
                                 {TCP_SEND[k+42]=*(pucBuf+38+k);  }
                                 TCP_SEND[38]=0x00;
                                 TCP_SEND[39]=0xDE;
                                 TCP_SEND[40]=0xAB;
                                 TCP_SEND[41]=0x32;
                                 TCP_SEND[45]++; // Ack num + 1
                                 TCP_SEND[47]=0x12; // SYN,ACK
                                 TCP_SEND[48]=*(pucBuf+48);  // window size
                                 TCP_SEND[49]=*(pucBuf+49);
                                 TCP_SEND[52]=0x00;
                                 TCP_SEND[53]=0x00;
                                 if(len>54)
                               {TCP_SEND[17]=*(pucBuf+17);
                               TCP_SEND[46]=*(pucBuf+46);  // header length
                               for(k=54;k<len;k++)      // options
                                               TCP_SEND[k]=*(pucBuf+k);}
                                 Add_CheckSum(len);
                               EthernetPacketPutDMA(ETH_BASE,TCP_SEND,len);
                                }// end SYN
                                
                                ////////*** ACK packets here
                                else if (*(pucBuf+47)==0x10)
                                {  // data transfer cycle is ended. Nothing is necessary to do                 
                                }// end ACK
                                
                                ////////*** PSH,ACK packets here
                                else if (*(pucBuf+47)==0x18) 
                           {Data_len = *(pucBuf+17)-0x28; // how many bytes of data are received?
                               TCP_SEND[17]=0x28;
                               for(k=0;k<4;k++)
                                 {TCP_SEND[k+38] = *(pucBuf+k+42);
                                 TCP_SEND[k+42] = *(pucBuf+38+k); }
                                TCP_SEND[45] += Data_len;
                                 if(TCP_SEND[45]<Data_len)
                                               {TCP_SEND[44]++;
                                               if(TCP_SEND[44]==0x00)
                                                              {TCP_SEND[43]++;
                                                              if(TCP_SEND[43]==0x00)
                                                                              TCP_SEND[42]++;
                                                              }
                                               }
                                 TCP_SEND[46] = 0x50;
                                 TCP_SEND[47] = 0x10; // ACK
                                 TCP_SEND[48]=*(pucBuf+48);  // window size
                                 TCP_SEND[49]=*(pucBuf+49);
                                 Add_CheckSum(54);
                                 EthernetPacketPutDMA(ETH_BASE,TCP_SEND,54);         
                                if(*(pucBuf+55) == 0xCC)
                                               ETH_COMM_CORRECT++;
                                if(*(pucBuf+55)==0xAA && Data_len == 22)
                                                               {for(k=0;k<22;k++)
                                                               receiveArray[k] = *(pucBuf+54+k); }
                                sendArray[0] = 0x02; // ID2
                                sendArray[1] = 0xBB; // ack
                                for(k=2;k<22;k++)
                                               sendArray[k] = receiveArray[k];
                                               
                                sendArray[22] = (mALARM >> 8) & 0x00FF;            // alarm high byte
                                sendArray[23] = mALARM& 0x00FF;                         //alarm low byte
                                sendArray[24] = (_IQ7int(Vdc_mean) >> 8) & 0x00FF;   //dc link voltage
                                sendArray[25] = _IQ7int(Vdc_mean) & 0x00FF;
                                GP_CURRENT = _IQ7int(Iha_mean);
                                GP_CURRENT = isqrt(GP_CURRENT);
                                sendArray[26] = (GP_CURRENT >> 8) & 0x00FF;  // active filter current
                                sendArray[27] = GP_CURRENT& 0x00FF;
                               
                                freq_index = 0;
                                for(k=28;k<108;k=k+4)  // send frequency values
                                               {
                                                               sendArray[k] = (freq[freq_index][0]>>8) & 0x00FF;
                                                               sendArray[k+1] = freq[freq_index][0] & 0x00FF;
                                                               sendArray[k+2] = (freq[freq_index][1]>>8) & 0x00FF;
                                                               sendArray[k+3] = freq[freq_index][1] & 0x00FF;
                                                               freq_index++;
                                               }
                                sendArray[108] = T1_int;   // send temperatures
                                sendArray[109] = T2_int;
                                sendArray[110] = T3_int;
                                Send_Data_PLC(sendArray,111);                                
                               }// end PSH,ACK
                   
                    ////********** FIN,ACK packets here
                   else if(*(pucBuf+47)==0x11)  
                               {TCP_SEND[17]=0x28;
                                 for(k=0;k<4;k++)
                                 {TCP_SEND[k+38] = *(pucBuf+k+42);
                                 TCP_SEND[k+42] = *(pucBuf+38+k); }
                               TCP_SEND[45] += 1;
                                if(TCP_SEND[45]==0x00)
                                 {TCP_SEND[44]++;
                                 if(TCP_SEND[44]==0x00)
                                 {TCP_SEND[43]++;
                                 if(TCP_SEND[43]==0x00)
                                 TCP_SEND[42]++; }
                                 }
                                 TCP_SEND[46] = 0x50;
                                 TCP_SEND[47] = 0x10; // ACK
                                 TCP_SEND[48]=*(pucBuf+48);  // window size
                                 TCP_SEND[49]=*(pucBuf+49);
                               Add_CheckSum(54);                       
                                EthernetPacketPutDMA(ETH_BASE,TCP_SEND,54);
                                TCP_SEND[47] = 0x11; // FIN,ACK
                               Add_CheckSum(54);
                               EthernetPacketPutDMA(ETH_BASE,TCP_SEND,54);
                               }// end FIN,ACK
 } // end else if
  
            // Clear the RX Packet event and re-enable RX Packet interrupts.
            //
            if(HWREGBITW(&g_ulFlags, FLAG_RXPKT) == 1)
            {
                HWREGBITW(&g_ulFlags, FLAG_RXPKT) = 0;
                ROM_EthernetIntEnable(ETH_BASE, ETH_INT_RX);
            }                                   
  return len; 
}// end function 


For the whole source code and detailed information please contact me.

5 comments:

  1. Emre,

    Thanks for sharing your code. Don't you use any protocol on top of TCP/IP to communicate with HMI.

    I would be happy if you could share your code with me (ekrem03@gmail.com)

    ReplyDelete
  2. Hi Emre!
    Thank you for sharing your idea.
    I'm trying to establish Ethernet connection for TI f28m36. And I have some problems with DMA functions of example project.
    Could you mail me the whole code?
    oleg.karnienko@gmail.com
    Thanks in advance!

    ReplyDelete
  3. Hi,

    Could you please email me the source code.

    my email : gsmsby@yahoo.com
    Thank you
    Novita A

    ReplyDelete
  4. Hi,

    Could you please email me the source code.

    my email : luongtien.bkan@gmail.com
    thank you!

    ReplyDelete
  5. Could you please send me as well the source code.
    My email is: didogm@gmail.com
    Thank you in advance.
    Cheers!

    ReplyDelete