nishal
Advanced Member level 4
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
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