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.
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 :
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