📄 rs485.c
字号:
//**************************************************************************
//
// Created by Kingty Leng 03-09-2007
// Version 1.8
// for RS485 network
//
// for the heat pump project,use pic16f946 as the processor.
// in fact,when using this program, u can follow this direction:
// for example,we send a packet from master to seperate:
// For master:
// Rs485Initialise():init a addr u want to send a packet;
// Rs485SendPacket():send the packet,and the crc auto_created when u send a packet
// u do not have to care about how it created if u just want to
// use it.
// For seperate:
// Rs485Initialise():set the addr
// Rs485Decode( ): used in the interrupt to receive the packets
// Rs485Process() : check if the packet is for me and correct,get it when it defined for me
// and correct or else ignore it.
//
//**************************************************************************
#include<pic16f946.h>
#include"rs485.h"
#include "delay.h"
#define _485_LOC_
unsigned char cOurAddr;
unsigned char cRs485RxChar;
unsigned char cRS485State;
unsigned char cRetFlag;
unsigned char cStart,cStop;
unsigned char cNetAddr;
unsigned char cLenExpected;
unsigned char cCommand;
unsigned char cRxCrcHigh, cRxCrcLow;
unsigned char cCalcCrcHigh, cCalcCrcLow;
unsigned char cBufPtr;
bank1 unsigned char c485Buf[32];
//****************************************************************************
//
// Initialise RS485 network driver
//
//****************************************************************************
void Rs485Initialize(unsigned char cAddr) // for seperate
{
cOurAddr = cAddr;
cRS485State = PKT_WAIT_START;
OUTPUTS_ON = 0; // enable input,for master =1 at first
RCIE = 1;
// Enable Receive Interrupt
}
//****************************************************************************
//
//
//
//****************************************************************************
unsigned char Rs485Process(void) // for seperate and master,to process the message
{ // received in the main function
unsigned char cOurPkt, cPktReady;
cOurPkt = 0;
cPktReady = 0;
//GIE = 0;
if ( (cRS485State == PKT_COMPLETE)&&(cStop==PKT_STOP) )
{
if ( cNetAddr == cOurAddr )
{
cOurPkt = 1;
}
else
{
cRS485State = PKT_INVALID; // Network traffic for other nodes or something wrong
}
if(cRS485State != PKT_INVALID)
{ cRS485State = PostValidatePacket(); } // Validate packet CRC
if ( (cRS485State == PKT_INVALID)||(cRS485State == PKT_VALID) )
{
if ( cRS485State == PKT_INVALID ) // not our invalid packets
{
cRS485State = PKT_WAIT_START;
}
else if ( cRS485State == PKT_VALID ) // If packet valid
{ // and destined for this node
if ( cOurPkt ) cPktReady = 1;
cRS485State = PKT_WAIT_START;
}
}
}
//GIE = 1;
return cPktReady;
}
//****************************************************************************
//
// void Rs485Decode(void)
// used in the interrupt to receive packtes
//
//****************************************************************************
unsigned char Rs485Decode( void )
{
switch ( cRS485State )
{
case PKT_WAIT_START: cStart = cRs485RxChar;
if ( cStart == PKT_START ) // Check for the start of packet byte
{
cRS485State++;
}
break;
case PKT_WAIT_ADDR: cNetAddr = cRs485RxChar;
cRS485State++;
break;
case PKT_WAIT_LEN: cLenExpected = cRs485RxChar; // get the lenth from master
if ( cLenExpected > sizeof(c485Buf) )
{
cRS485State = PKT_INVALID;
}
else
{
cBufPtr = 0;
cRS485State++;
}
break;
case PKT_WAIT_CMD: cCommand = cRs485RxChar; //cCommand receive the command from master
if ( PacketHasPayload() ) cRS485State = PKT_WAIT_DATA;
else cRS485State = PKT_WAIT_STOP;
break;
case PKT_WAIT_DATA: c485Buf[cBufPtr] = cRs485RxChar;
cBufPtr++;
if ( cBufPtr == cLenExpected ) // If last byte of data received
{
cRS485State++; // next byet is the CRC high byte
}
break;
case PKT_WAIT_STOP: cStop = cRs485RxChar;
cRS485State ++;
break;
case PKT_WAIT_CRC_HIGH: cRxCrcHigh = cRs485RxChar;
cRS485State++;
break;
case PKT_WAIT_CRC_LOW: cRxCrcLow = cRs485RxChar;
cRS485State = PKT_COMPLETE;
break;
case PKT_COMPLETE: break;
case PKT_VALID: break;
case PKT_INVALID: break;
default: cRS485State = PKT_WAIT_START;
break;
}
return cRS485State;
}
//****************************************************************************
// void Rs485SendPacket( char cAddr, char cCmd, char cLen, char *cData )
//
// Send a packet over the RS485 link
//
//
//****************************************************************************
void Rs485SendPacket( unsigned char cCmd, unsigned char cLen,unsigned char *cData )
{
unsigned char c, d;
RCIE = 0; // Disable Receive Interrupt
OUTPUTS_ON = 1; // Enable driver
DelayMs(1); // Line turnarround time
cCalcCrcHigh = 0xff; // Clear CRC
cCalcCrcLow = 0xff;
for ( c=0; c < 2; c++ ) Rs485SendChar( 0xaa );
Rs485UpdateCrc( PKT_START );
Rs485SendChar( PKT_START ); // Send packet start character
Rs485UpdateCrc( cOurAddr );
Rs485SendChar( cOurAddr ); // Send address
Rs485UpdateCrc( cLen );
Rs485SendChar( cLen ); // Send length
Rs485UpdateCrc( cCmd );
Rs485SendChar( cCmd ); // Send command
if ( cLen != 0 ) // If payload not empty send data
{
for ( c = 0; c < cLen; c++ )
{
d = cData[c];
Rs485UpdateCrc( d );
}
for ( c = 0; c < cLen; c++ )
{
d = cData[c];
Rs485SendChar( d ); // Send data
}
}
Rs485UpdateCrc( PKT_STOP );
Rs485SendChar( PKT_STOP );
Rs485SendChar(cCalcCrcHigh);
Rs485SendChar(cCalcCrcLow);
for ( c=0; c < 2; c++ ) Rs485SendChar( 0xaa );
DelayMs(1);
OUTPUTS_ON = 0; // Disable driver
RCIE = 1; // Enable Receive Interrupt
}
//****************************************************************************
// void Rs485GetPacket( char *cCommand, char cLen, char *cData )
//
// Pass packet to main application
//
//****************************************************************************
void Rs485GetPacket(unsigned char *cCom, unsigned char *cLen,unsigned char *cData )
{
unsigned char c;
*cCom = cCommand;
*cLen = cLenExpected;
for ( c=0; c < cLenExpected;c++ ) cData[c] = c485Buf[c];
cData[cLenExpected] = 0x00;
}
/*************************************************************************
* Example Table Driven CRC16 Routine using 4-bit message chunks
*
*
*************************************************************************/
const unsigned char CRC16_LookupHigh[16] = {
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1
};
const unsigned char CRC16_LookupLow[16] = {
0x00, 0x21, 0x42, 0x63, 0x84, 0xA5, 0xC6, 0xE7,
0x08, 0x29, 0x4A, 0x6B, 0x8C, 0xAD, 0xCE, 0xEF
};
void CRC16_Init( void )
{
// Initialise the CRC to 0xFFFF for the CCITT specification
cCalcCrcHigh = 0xFF;
cCalcCrcLow = 0xFF;
}
/*
* Process 4 bits of the message to update the CRC Value.
*
* Note that the data must be in the low nibble of val.
*/
void CRC16_Update4Bits( unsigned char val )
{
unsigned char t;
// Step one, extract the Most significant 4 bits of the CRC register
t = cCalcCrcHigh >> 4;
// XOR in the Message Data into the extracted bits
t = t ^ val;
// Shift the CRC Register left 4 bits
cCalcCrcHigh = (cCalcCrcHigh << 4) | (cCalcCrcLow >> 4);
cCalcCrcLow = cCalcCrcLow << 4;
// Do the table lookups and XOR the result into the CRC Tables
cCalcCrcHigh = cCalcCrcHigh ^ CRC16_LookupHigh[t];
cCalcCrcLow = cCalcCrcLow ^ CRC16_LookupLow[t];
}
/*
* Process one Message Byte to update the current CRC Value
*/
void Rs485UpdateCrc( unsigned char cVal )
{
CRC16_Update4Bits( cVal >> 4 ); // High nibble first
CRC16_Update4Bits( cVal & 0x0F ); // Low nibble
}
//****************************************************************************
//
//
// send a char
//
//
//****************************************************************************
void Rs485SendChar(unsigned char c )
{
TXREG = c; // Load data to send
while ( !TRMT ); // Wait for TX Empty
}
//****************************************************************************
// char PostValidatePacket(void)
//
// Verify the CRC on the last packet received
//
// Check if the CRC is correct
// and return the updated state as the result
//
//****************************************************************************
unsigned char PostValidatePacket(void)
{
unsigned char c, d;
CRC16_Init();
Rs485UpdateCrc(PKT_START);
Rs485UpdateCrc(cNetAddr);
Rs485UpdateCrc(cLenExpected);
Rs485UpdateCrc(cCommand);
if ( PacketHasPayload() ) // If the packet has a payload,
{ // then include the data in the CRC.
for ( c = 0; c < cLenExpected; c++ )
{
d = c485Buf[c];
Rs485UpdateCrc( d );
}
}
Rs485UpdateCrc(PKT_STOP);
if ( (cRxCrcHigh == cCalcCrcHigh)&&(cRxCrcLow == cCalcCrcLow) )
{
cRS485State = PKT_VALID;// Check if the CRC is correct
// and return the updated state as the result
}
else
{
cRS485State = PKT_INVALID;
}
return cRS485State;
}
//****************************************************************************
//
//
// Check packet command type
//
//
//****************************************************************************
unsigned char PacketHasPayload(void)
{
if ( cCommand == SENSOR_GET_DATA ) return 1;
else return 0;
}
//**************************
//
// Setup Hardware
//
//**************************
void Setup(void)
{
INTCON = 0; // disable global interupts
TRISD4 = 0;
TRISF6 = 0;
TRISF7 = 0;
}
void ConfigureComms(void)
{
SPEN = 1; // Enable Serial port
RX9 = 0; // 8 bit receive mode
TX9 = 0 ; // 8 bit transmit mode
SPBRG = 0x19; // ( 9600 Baud at 8 Mhx clock )
BRGH = 1; // BRGH = 1 ( High speed mode )
SYNC = 0 ; // Asyncronous mode;
TXEN = 1; // Enable Transmitter
CREN = 1; // Enable continuous receive
PEIE = 1; // Enable all Peripheral Interrupts
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -