Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Modbus Master communication using PIC16f876a

Status
Not open for further replies.

nishal

Advanced Member level 4
Joined
May 4, 2007
Messages
108
Helped
10
Reputation
20
Reaction score
5
Trophy points
1,298
Activity points
1,936
Hi all,
I am trying to interface PIC16f876a with Frequency meter having Modbus RTU protocol (9600, 8, E, 1). I could able to view the frequency data from PC using Modbus Poll software. But it fails to respond when I send it from pic with the same hex codes that is sent by the modbus software. I am using Proton Pic basic and it is a modified to work as a modbus master
The original code is designed to work as a modbus slave, downloaded from Modbus in embedded systems

I appreciate your help in advance!

-Nishal


Code:
/**
*
* Modbus RTU Slave
* Copyright (c), Andrzej Sokulski, 
* andrzej@modbus.pl - www.modbus.pl
* All rights reserved.
* 
* 
*  This software is free you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This software is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Lesser General Public License for more details. 
*  In no event shall the regents or contributors be liable for any direct,
*  indirect, incidential, special, exemplary, or consequential damages. 
*  Redistributions of source code should retain the above copyright notice,
*  If you want to remove the copyright notice, 
*  please donate me (www.modbus.pl/donate.html).
*
*/
'Modbus slave based on PIC 16F876 and  I2C Flash memory 24LC128
'You can read and write up to 32 register in single frame
'This slave supports modbus function 3 (read holding registers) and 16 (preset multiple registers)
'You can address up to 8000 registers (16 bit) with I2C Flash memory 24LC128 (!! if you get different Flash the register nr will be different too)
'The RS232 is set to baudrate - 9600, data bits - 8, stop bits 1, parity - none 
'The RS232 16F876 pins are RC6/TX - Transmit, RC7/RX - Read
'The I2C 16F876 pins are RC4/SDA - Data, RC3/SCI -Clock
'The Timer 2 is used for detecting end of modbus frame

Device 16F876A
Xtal 20                   ' Define clock freq. 20 MHz

@CONFIG_REQ
@__CONFIG XT_OSC & WDT_OFF & PWRTE_ON & BODEN_OFF & CP_OFF 

Include "modedefs.bas"          ' Include serial modes

Symbol  RS485_CTRL  PORTC.4


    ' Initialize USART
    TRISC = %10001111       ' Set TX (RC.6) to out, rest in
    RCSTA = %10010000       ' Enable serial port and continuous receive
    TXSTA = %00100000       ' Enable transmit and asynchronous mode
parity bit
   	SPBRG = 32              ' Baudrate =  9600 at 20 MHz
	INTCON = %11000000		' Enable interrupts
	PIE1.5 = 1				' Enable interrupt on USART
    PIE1.1 = 1          	' Enable interrupt on TMR2
    T2CON.1 = 1 			' TMR1 prescaler 1:16
    TMR2 = 0                'TMR2 Holding register = 0
	On Interrupt GoTo ReadMB	' Declare interrupt handler routine

Dim SlaveAddress    As Byte
SlaveAddress = 1                ' Modbus slave adress (1..255) 

Dim buffer[10]  As Byte             'Modbus frame buffer - change if want to get more than 32 registers in single frame
Dim CRC16[2]    As Byte 

Dim B0          As Byte            'Variables definition  
Dim B1          As Byte
Dim W0          As Word
Dim MBFrame     As Byte
Dim NewFrame    As Bit 
Dim Length      As Byte
Dim TMR1Ticks   As Byte
Dim i           As Byte
Dim counter1    As Word
Dim counter2    As Word
Dim CountVal    As Word
Dim Generator   As Word
Dim Temp        As Word
Dim CRC         As Word
Dim j           As Byte
Dim BitVal      As Bit
Dim OutByte     As Byte
Dim ParityResult    As Byte

TMR1Ticks = 0       
Length = 0
TRISB = 0

GoTo Loop


'----------------------------------------------------------------
CharOut: 									' USART send single byte 
    If PIR1.4 = 0 Then CharOut     	        ' Wait for transmit register empty
        TXREG = B1              			' Send character to transmit register
    Return 

'----------------------------------------------------------------
CRC_16:									    ' Function to calculate CRC16 checksum
    CRC = 65535
    Generator = 40961
    For i = 0 To Length
        Temp = buffer[i]
        CRC = CRC ^ Temp
        For j = 1 To 8
            BitVal = CRC.0
            CRC = CRC >> 1
            If BitVal = 1 Then
               CRC = CRC ^ Generator
            EndIf
         Next j
    Next i
    CRC16[0] = (CRC // 256)					' CRC16 low byte
    CRC16[1] = (CRC / 256)					' CRC16 high byte
Return

'----------------------------------------------------------------
CheckMB:                                    'Check the modbus frame
    If buffer[1] <> SlaveAddress Then 		'Wrong Slave address
    	MBFrame = 0
    Else
    	If buffer[2] <> 3 And buffer[2] <> 16 Then 'Wrong Modbus function
    		MBFrame = $81
    	Else
    		If buffer[5]>0 Or buffer[6] > 32 Then 'Too many registers
    			MBFrame = $83
    		Else
    			If Length > 74 Then  'Too many bytes in frame       
    				MBFrame = $82
    			Else
    				If Length <9 Then 'Frame too short
    					MBFrame = $81
    				Else
    					If Length >74 Then 'Frame too long
    						MBFrame = $82
    					Else
    						If buffer[3] * 256 + buffer[4] + buffer[5]*256 +buffer[6] > 7999 Then 'Address data > 8000 (set for your hardware configuration)
    							MBFrame = $82
    						Else
    							Length = Length - 3
    							GoSub CRC_16
    							If buffer[Length+1] <> CRC16[0] Or buffer[Length+2] <> CRC16[1] Then 'Bad CRC16 checksum
    								MBFrame = 0
    							Else
    								MBFrame = buffer[2]                                                   ' Frame OK !
    							EndIf
    						EndIf
    					EndIf
    				EndIf
    			EndIf
    		EndIf
    	EndIf
    EndIf
Return

'----------------------------------------------------------------
ReadRegResponse:  							' Response for modbus function 3 (Read Holding Registers)
    W0 = buffer[3]*255 + buffer[4]
    W0 = W0 * 2
    buffer[3] = buffer[6]*2
    For B0=1 To buffer[3]
        I2CIN PORTC.4 ,PORTC.3 ,$A1,W0,[B1]  ' Read data from I2C flash memory
    	buffer[B0+3] = B1
        W0 = W0 + 1
    Next B0	
    Length = buffer[3] + 3
    GoSub CRC_16
    buffer[length+1] = CRC16[0]					' Calculate the CRC16 for response frame
    buffer[length+2] = CRC16[1]
    Length = Length + 2
    For B0 = 1 To Length						' Send the response to Master
    	B1 = buffer[B0]
    	GoSub CharOut
    Next B0
Return

'----------------------------------------------------------------
WriteRegResponse:							' Response for modbus 16 function (Preset Multiple Registers)
    W0 = buffer[3]*255 + buffer[4]
    W0 = W0 * 2
    For B0 = 0 To buffer[7]
    	B1 = buffer[B0 + 8]
    	I2COUT PORTC.4 ,PORTC.3 ,$A0,W0,[B1] ' Write data to I2C flash memory
    	W0 = W0 +1
    	DelayMS 10
    Next B0
    Length = 6
    GoSub CRC_16
    buffer[length+1] = CRC16[0]					' Calculate the CRC16 for response frame
    buffer[length+2] = CRC16[1]
    Length = Length + 2
    For B0 = 1 To Length						' Send the response to Master
    	B1 = buffer[B0]
    	GoSub CharOut
    Next B0
Return

'----------------------------------------------------------------
WriteBadRequest: 							' Response for error in modbus poll
    If MBFrame <> 0 Then						' If MBFrame = 0 then no response
    	buffer[2]=buffer[1]+ $80				' Set the error code in modbus frame
    	buffer[3]=MBFrame - $80					' Set the error nr
    	Length = 3
    	GoSub CRC_16
    	buffer[length+1] = CRC16[0]				' Calculate the CRC16 for response frame
    	buffer[length+2] = CRC16[1]
    	Length = Length + 2
    	For B0 = 1 To Length					' Send the response to Master
    		B1 = buffer[B0]
    		GoSub CharOut
    	Next B0
    EndIf
Return

'----------------------------------------------------------------
Disable
ReadMB:	
    If RCSTA.1 Or RCSTA.2 Then               	' If USART error then clear the error flag
		RCSTA.4 = 0
    EndIf										
    
    If PIR1.5 = 1 Then                    	' USART Interrupt 
        TMR1Ticks = 0
         If NewFrame = 1 Or Length = 0 Then      'New modbus frame start
            NewFrame = 0
        	T2CON.2 = 1 							' Enable Timer2
        	Length = 0					
         EndIf   
         While PIR1.5 = 1      					'Write modbus frame to buffer
            buffer[Length] = RCREG
            Length = Length + 1
            If Length = 75 Then 
                Length = 0
            EndIf
         Wend 
    EndIf
    
    If PIR1.1 = 1 Then                        ' Timer2 interrupt
         PIR1.1 = 0
         T2CON.2 = 0                                ' Timer2 disable
         TMR1Ticks = TMR1Ticks + 1
         If TMR1Ticks > 120 Then                     ' ~ 10 ms without new char  
            NewFrame = 1                               'There was no new char on USART => end of modbus frame 
         Else
            T2CON.2 = 1                                'Enable timer
         EndIf 
    EndIf
Resume
Enable


'----------------------------------------------------------------
Loop: 
'    For counter2 = 0 To 5  ' The main program
'        For counter1 = 0 To 30000  ' Write your code here
'        Next counter1
'    Next counter2
'    Toggle 1

'' Modbus protocol function
'	If NewFrame = 1 Then 					 ' Check if new modbus frame is in buffer
'        NewFrame = 0	
'		GoSub CheckMB						 ' Check the modbus frame	
'		If MBFrame = 3 Then
'			GoSub ReadRegResponse			 ' Response for function 3
'		Else
'			If MBFrame = 16 Then
'				GoSub WriteRegResponse		 ' Response for function 16	
'			Else
'				GoSub WriteBadRequest 		 ' Response for error
'			EndIf
'		EndIf
'      Length = 0
'	EndIf


    buffer[0] = 0x01
    buffer[1] = 0x03
    buffer[2] = 0x00
    buffer[3] = 0x9C
    buffer[4] = 0x00
    buffer[5] = 0x02
    buffer[6] = 0x04
    buffer[7] = 0x25

    
    Length = 7
    
    
    High RS485_CTRL
    DelayMS 2
    For B0 = 0 To Length						' Send the response to Master
    	B1 = buffer[B0]
    	GoSub CharOut
    Next B0
    DelayMS 2

    Low RS485_CTRL

    DelayMS 1000

GoTo Loop

End
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top