Sabtu, 06 Oktober 2012

Code Slave Modbus dengan Microcontroller AVR Atmega8535 dgn bahasa C codevision


Pada Artikel ini saya mencoba  membuat Slave  untuk protokol Modbus dengan microcontroller Atmega8535 dengan bahasa C CodeVision.  Saya berharap bagi anda yang sudah mengerti atau  pernah membuat , bisa memberikan masukannya demi kemajuan kita semua.
Untuk mengerti  tentang modbus RTU atau ASCII anda sebaiknya sudah mengerti pemrograman RS232 /  RS485 . anda bisa baca pada artikel lainya pada blog ini.
Kita akan membuat  Slave dengan function Code 03 dan 04 yaitu   fungsi untuk membaca data (register)  pada slave ,  sebagai masternya  adalah program di PC dengan C# .
Untuk versi awal ini saya tidak menggunakan timeout spt yg dipersyaratkan oleh Modbus , untuk menghindari kerumitan.
Konsep dasar  modbus RTU.
master-slave-modbus
master-slave-modbus function code 03 atau 04
Program Master Pada PC
Slave Modbus hanya bersifat menungu perintah dari master , maka  sebelumnya kita harus membuat program sbagai Master  pada PC untk mengirim query /perintah. Perintah master disebut query dan jawaban slave disebut respon.
Program pd PC akan kita buat dgn C#, program ini  akan berfungsi:
1. Mengirim Perintah (query)  ke Slave.
2. Menerima Respon dari Slave.
Berikut ini contoh kode program mengirim perintah/query dengan C# :
pembahasan contoh  master RTU lengkapnya bisa baca di artikel ini
private SerialPort serialcomm1 = new SerialPort();
private void open_com_Click()
{
                serialcomm1.PortName = "COM1";
                serialcomm1.BaudRate = 9600;
                serialcomm1.DataBits = 8;
                serialcomm1.Parity = Parity.None;
                serialcomm1.StopBits = StopBits.One;

                serialcomm1.ReadTimeout = 1000;
                serialcomm1.WriteTimeout = 1000;
                serialcomm1.Open();
}
private void KirimPerintah(ref byte[] framedata)
        {

            byte[] CRC = new byte[2];
            ushort alamat _awal = Convert.ToUInt16(txtAlamat_Awal_Register.Text)
            ushort jumlah_register = Convert.ToUInt16(txtJumlah_Register.Text);
                //Clear  buffers:
                serialcomm1.DiscardOutBuffer();
                serialcomm1.DiscardInBuffer();
                //Perintah Function Code 03 selalu  8 byte:
                byte[] framedata = new byte[8];

            framedata[0] =Convert.ToByte(txtAlamatSlave.Text);
            framedata[1] = 0x03 ; //FuctionCode
            framedata[2] = (byte)(alamat_awal >> 8);    // MSB starting address
            framedata[3] = (byte)alamat_awal;           // LSB
            framedata[4] = (byte)(jumlah_register >> 8);  //MSB
            framedata[5] = (byte)jumlah_register;         //LSB
            GetCRC(framedata, ref CRC);
            framedata[framedata.Length - 2] = CRC[0];
            framedata[framedata.Length - 1] = CRC[1];

             //kirim perintah/query  ke slave
                    serialcomm1.Write(framedata, 0, framedata.Length);

            }

//fungsi menghitung Error Check ( CRC16 )
 private void GetCRC(byte[] framedata, ref byte[] CRC)
        {

            ushort CRCFull = 0xFFFF;
            byte CRCHigh = 0xFF, CRCLow = 0xFF;
            char CRCLSB;

            for (int i = 0; i < (message.Length) - 2; i++)
            {
                CRCFull = (ushort)(CRCFull ^ message[i]);

                for (int j = 0; j < 8; j++)
                {
                    CRCLSB = (char)(CRCFull & 0x0001);
                    CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);

                    if (CRCLSB == 1)
                        CRCFull = (ushort)(CRCFull ^ 0xA001);
                }
            }
            CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
            CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
        }
Anda bisa juga membuat program mengirim data tetap buat testing saja
contoh data :
  • 01 05 00 01 00 02 CB  1D    //data ini a kan memberi memberi fuction eror
  • 01 03 00 01 00 02   CB 95  // data yg ok FC=03
data data tsb akan ditampilkan di LCD2x16 dlm bentuk desimal.
private void button2_Click(object sender, EventArgs e)       
 {

            byte[] message = new byte[9]; 

            message[0] = 0x01; //address;
            message[1] = 0x03;//FC03;

            message[2] = 0x00;//START ADRESS MSB ;
            message[3] = 0x01;  //START ADRESS LSB;

            message[4] = 0x00;  //QTY REGISTER MSB;
            message[5] = 0x02;  //QTY REGISTER LSB;

            message[6] = 0x95; //   CRC LOW             
            message[7] = 0xCB; //   CRC High

           serialcomm1.Write(message, 0, message.Length);
                    }
Berikut ini contoh program untuk Microcontroller Atmega8535 dgn C codevision:  (maaf  belum selesai)
/*
fungsi usart_rx_isr()   -> untuk menerima data dan simpan di rx_buffer
fungsi kirim_data_isr()   -> untuk mengirim data array di tx_buffer
fungsi main()    -> fungsi  utama terus menerus mengecek data rx_buffer yg datang.
fungsi  checkframedata()   -> fungsi  yg  memeriksa  alamat , fungsi 03 dan crc
fungsi baca_register()   -> fungsi untuk membaca  alamat  register yg dituju
fungi get_crc();    -> fungsi untuk menghitung nilai CRC dari frame datang atau yg akan dikirim.
/*****************************************************

Date    : 6/30/2011
Author  : PCCONTROL.WORDPRESS.COM
Company :
Comments: Slave Modbus RTU 

Chip type           : ATmega8535
Program type        : Application
Clock frequency     : 4.000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include <mega8535.h>
#include <math.h>
#include <delay.h>
// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x15 ;PORTC
#endasm
#include <lcd.h>
#include <string.h>
#include <stdio.h>

#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7

#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

unsigned char	sendBuf[32],localAddr = 0x1;
// USART Receiver buffer
#define RX_BUFFER_SIZE 30
unsigned char rx_buffer[RX_BUFFER_SIZE],rx_counter;

//char tampung3[16]; 

unsigned char CRC[2],CRCLow,CRCHigh;
unsigned char tampung1[10];
unsigned char tampung2[10];
unsigned char tampung3[10];
unsigned char data0[10];
unsigned char data1[10];
unsigned char data2[10];
unsigned char data3[10];
unsigned char data4[10];
unsigned char data5[10];
unsigned char data6[10];
unsigned char data7[10];
//unsigned char data8[10];

unsigned char r_ready; 

void GetCRC(char message[], char panjangdata);
unsigned char rx_wr_index,tx_wr_index;

void checkframedata();
void readRegisters();
int getRegisterVal(int addr,int *tempData);
int testRegister; 

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
      if(rx_wr_index <= 7)  // 0 sampai 7
      {  

      rx_buffer[rx_wr_index]=data;
      rx_wr_index++;
      }
      else
      {
       lcd_clear();
       sprintf(tampung3,"%i",rx_wr_index);
       lcd_gotoxy(15,1);
       lcd_puts(tampung3); 

       GetCRC(rx_buffer,rx_wr_index-2);

       rx_wr_index=0;
       r_ready=1;
       UCSRB.7=0;
      }

   };
}

void main(void)
{

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 9600
UCSRA=0x00;
UCSRB=0x98;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x19;

// LCD module initialization
lcd_init(16);
lcd_gotoxy(0,0);
lcd_putsf("MODBUS SLAVE FC3");
// Global enable interrupts
#asm("sei")

 r_ready=0;
 rx_wr_index=0;
 UCSRB.7=1;  

while (1)
      {
      // Place your code here

        //ambil parameter
        if(r_ready==1)  //==============
           {  

             checkframedata(); 

           } 

      }; //end of while(1)
}     //end of main

        void GetCRC(char message[], char panjangdata)
       // unsigned  int GetCRC(char message[], char panjangdata)
        {
            //Function expects a modbus message of any length as well as a 2 byte CRC array in which to
            //return the CRC values:
           unsigned char i,j;
            unsigned int  CRCFull = 0xFFFF;
         //  unsigned char CRCHigh = 0xFF, CRCLow = 0xFF;
           unsigned  char CRCLSB;

            for (i = 0; i < (panjangdata) ; i++)
            {
                CRCFull = (CRCFull ^ message[i]);

                for ( j = 0; j < 8; j++)
                {
                    CRCLSB = (CRCFull & 0x0001);   //ambil LSB
                    CRCFull = ((CRCFull >> 1) & 0x7FFF);  // geser 

                    if (CRCLSB == 1)
                        CRCFull = CRCFull ^ 0xA001;
                }
            }
            CRC[1] = CRCHigh = ((CRCFull >> 8) & 0xFF);
            CRC[0] = CRCLow = (CRCFull & 0xFF); 

           return ;//0 ; //CRCFull;
        }

  void checkframedata()
  {  

      //untuk mendebug tampilkan Data di baris pertama dan CRC di baris ke dua  LCD 2x16
        sprintf(data0,"%i",rx_buffer[0]); //01
        lcd_gotoxy(0,0);
        lcd_puts(data0); 

        sprintf(data1,"%i",rx_buffer[1]); //03
        lcd_gotoxy(3,0);
        lcd_puts(data1);

        sprintf(data2,"%i",rx_buffer[2]); //00
        lcd_gotoxy(6,0);
        lcd_puts(data2);

        sprintf(data3,"%i",rx_buffer[3]); //01
        lcd_gotoxy(9,0);
        lcd_puts(data3);

        sprintf(data4,"%i",rx_buffer[4]); //00
        lcd_gotoxy(12,0);
        lcd_puts(data4);

        sprintf(data5,"%i",rx_buffer[5]); //02
        lcd_gotoxy(14,0);
        lcd_puts(data5);
        // tampilkan nilai CRC kiriman dari PC

        sprintf(data6,"%i",rx_buffer[6]); //123
        lcd_gotoxy(0,1);
        lcd_puts(data6);

        sprintf(data6,"%i",rx_buffer[7]); //123
        lcd_gotoxy(3,1);
        lcd_puts(data6);

        //Tampilkan Nilai CRC hasil hitungan Microcontroller
        sprintf(tampung1,"%i",CRC[1]);
        lcd_gotoxy(7,1);
        lcd_puts(tampung1);  

        sprintf(tampung2,"%i",CRC[0]);
        lcd_gotoxy(10,1);
        lcd_puts(tampung2);  

	if(rx_buffer[0]==0x01)  // alamat slave ini = 01
	 {
	  if(CRC[1] == rx_buffer[7] && CRC[0]==rx_buffer[6])
	{
	  	if(rx_buffer[1] == 0x03)
	  	{
	  	 lcd_gotoxy(14,1);
	         lcd_putsf("ok");
	        readRegisters();
		}
		 else if(rx_buffer[1] != 3)
		 {
		 lcd_gotoxy(0,1);
		 lcd_putsf("Fuction Error ");
		 }
	  }
         else
          {
          lcd_gotoxy(0,1);
	  lcd_putsf("CRC error    ");
          }

         }											

   r_ready=0;
   rx_wr_index=0;
   UCSRB.7=1;
  }       

void readRegisters(void)
{
	unsigned char addr;
	unsigned char tempAddr;

	unsigned int crcData;
	unsigned char readCount;
	unsigned char byteCount;

	unsigned int i;
	unsigned int tempData = 0;	

	//addr = (receBuf[2]<<8) + receBuf[3];
	//tempAddr = addr & 0xfff;
	addr = rx_buffer[3];
	tempAddr = addr;

	//readCount = (receBuf[4]<<8) + receBuf[5];	//?????
	readCount = rx_buffer[5];

	byteCount = readCount * 2;

	for(i=0;i<byteCount;i++)
	{     tempAddr++;
		getRegisterVal(tempAddr,&tempData);
		sendBuf[2*i+3] = tempData >> 8;
		sendBuf[2*i+4] = tempData & 0xff;
	}

	sendBuf[0] = localAddr;
	sendBuf[1] = 3;
	sendBuf[2] = byteCount;
	byteCount = byteCount + 3;
	GetCRC(sendBuf,byteCount);;
	sendBuf[byteCount] = crcData >> 8;
	byteCount++;
	sendBuf[byteCount] = crcData & 0xff;

	tx_wr_index = byteCount + 1;
	//beginSend();
}//void readRegisters(void)

int getRegisterVal(int addr,int *tempData)
{
	 int result = 0;

	switch(addr & 0xff)
	{
		case 0:
		        	*tempData = testRegister;
				break;
		case 1:
				break;

		default:
				break;
	}

	return result;
}

0 komentar:

Posting Komentar

 
Copyright ELEKTRONICA MIKROKONTROLER All Rights Reserved
Powered by Alat Rekaman
ProSense theme created by Dosh Dosh and The Wrong Advices.
Blogerized by Bonard Alfin Forum Distorsi.