📄 mstp.c
字号:
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2003 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307
USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */
/* protocol, which provides the same services to the network layer as */
/* ISO 8802-2 Logical Link Control. It uses services provided by the */
/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */
/* included in this standard by reference. The following hardware is assumed: */
/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */
/* transmitting and receiving eight data bits with one stop bit */
/* and no parity. */
/* (b) An EIA-485 transceiver whose driver may be disabled. */
/* (c) A timer with a resolution of five milliseconds or less */
#include <stddef.h>
#include <stdint.h>
#if PRINT_ENABLED
#include <stdio.h>
#endif
#include "mstp.h"
#include "bytes.h"
#include "crc.h"
#include "rs485.h"
#if PRINT_ENABLED
#include "mstptext.h"
#endif
/* debug print statements */
#if PRINT_ENABLED
#define PRINT_ENABLED_RECEIVE 0
#define PRINT_ENABLED_RECEIVE_DATA 1
#define PRINT_ENABLED_MASTER 0
#else
#define PRINT_ENABLED_RECEIVE 0
#define PRINT_ENABLED_RECEIVE_DATA 0
#define PRINT_ENABLED_MASTER 0
#endif
/* MS/TP Frame Format */
/* All frames are of the following format: */
/* */
/* Preamble: two octet preamble: X`55', X`FF' */
/* Frame Type: one octet */
/* Destination Address: one octet address */
/* Source Address: one octet address */
/* Length: two octets, most significant octet first, of the Data field */
/* Header CRC: one octet */
/* Data: (present only if Length is non-zero) */
/* Data CRC: (present only if Length is non-zero) two octets, */
/* least significant octet first */
/* (pad): (optional) at most one octet of padding: X'FF' */
/* The number of tokens received or used before a Poll For Master cycle */
/* is executed: 50. */
#define Npoll 50
/* The number of retries on sending Token: 1. */
#define Nretry_token 1
/* The minimum number of DataAvailable or ReceiveError events that must be */
/* seen by a receiving node in order to declare the line "active": 4. */
#define Nmin_octets 4
/* The minimum time without a DataAvailable or ReceiveError event within */
/* a frame before a receiving node may discard the frame: 60 bit times. */
/* (Implementations may use larger values for this timeout, */
/* not to exceed 100 milliseconds.) */
/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */
/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */
#define Tframe_abort 30
/* The maximum idle time a sending node may allow to elapse between octets */
/* of a frame the node is transmitting: 20 bit times. */
#define Tframe_gap 20
/* The time without a DataAvailable or ReceiveError event before declaration */
/* of loss of token: 500 milliseconds. */
#define Tno_token 500
/* The maximum time after the end of the stop bit of the final */
/* octet of a transmitted frame before a node must disable its */
/* EIA-485 driver: 15 bit times. */
#define Tpostdrive 15
/* The maximum time a node may wait after reception of a frame that expects */
/* a reply before sending the first octet of a reply or Reply Postponed */
/* frame: 250 milliseconds. */
/* note: we always send a reply postponed since a message other than
the reply may be in the transmit queue */
#define Treply_delay 10
/* The minimum time without a DataAvailable or ReceiveError event */
/* that a node must wait for a station to begin replying to a */
/* confirmed request: 255 milliseconds. (Implementations may use */
/* larger values for this timeout, not to exceed 300 milliseconds.) */
#define Treply_timeout 255
/* Repeater turnoff delay. The duration of a continuous logical one state */
/* at the active input port of an MS/TP repeater after which the repeater */
/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */
#define Troff 30
/* The width of the time slot within which a node may generate a token: */
/* 10 milliseconds. */
#define Tslot 10
/* The maximum time a node may wait after reception of the token or */
/* a Poll For Master frame before sending the first octet of a frame: */
/* 15 milliseconds. */
#define Tusage_delay 15
/* The minimum time without a DataAvailable or ReceiveError event that a */
/* node must wait for a remote node to begin using a token or replying to */
/* a Poll For Master frame: 20 milliseconds. (Implementations may use */
/* larger values for this timeout, not to exceed 100 milliseconds.) */
#define Tusage_timeout 20
/* we need to be able to increment without rolling over */
#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;}
bool MSTP_Line_Active(
volatile struct mstp_port_struct_t *mstp_port)
{
return (mstp_port->EventCount > Nmin_octets);
}
unsigned MSTP_Create_Frame(
uint8_t * buffer, /* where frame is loaded */
unsigned buffer_len, /* amount of space available */
uint8_t frame_type, /* type of frame to send - see defines */
uint8_t destination, /* destination address */
uint8_t source, /* source address */
uint8_t * data, /* any data to be sent - may be null */
unsigned data_len)
{ /* number of bytes of data (up to 501) */
uint8_t crc8 = 0xFF; /* used to calculate the crc value */
uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */
unsigned index = 0; /* used to load the data portion of the frame */
/* not enough to do a header */
if (buffer_len < 8)
return 0;
buffer[0] = 0x55;
buffer[1] = 0xFF;
buffer[2] = frame_type;
crc8 = CRC_Calc_Header(buffer[2], crc8);
buffer[3] = destination;
crc8 = CRC_Calc_Header(buffer[3], crc8);
buffer[4] = source;
crc8 = CRC_Calc_Header(buffer[4], crc8);
buffer[5] = data_len / 256;
crc8 = CRC_Calc_Header(buffer[5], crc8);
buffer[6] = data_len % 256;
crc8 = CRC_Calc_Header(buffer[6], crc8);
buffer[7] = ~crc8;
index = 8;
while (data_len && data && (index < buffer_len)) {
buffer[index] = *data;
crc16 = CRC_Calc_Data(buffer[index], crc16);
data++;
index++;
data_len--;
}
/* append the data CRC if necessary */
if (index > 8) {
if ((index + 2) <= buffer_len) {
crc16 = ~crc16;
buffer[index] = LO_BYTE(crc16);
index++;
buffer[index] = HI_BYTE(crc16);
index++;
} else
return 0;
}
return index; /* returns the frame length */
}
void MSTP_Create_And_Send_Frame(
volatile struct mstp_port_struct_t *mstp_port, /* port to send from */
uint8_t frame_type, /* type of frame to send - see defines */
uint8_t destination, /* destination address */
uint8_t source, /* source address */
uint8_t * data, /* any data to be sent - may be null */
unsigned data_len)
{ /* number of bytes of data (up to 501) */
uint8_t buffer[MAX_MPDU] = { 0 }; /* buffer for sending */
uint16_t len = 0; /* number of bytes to send */
len = (uint16_t) MSTP_Create_Frame(&buffer[0], /* where frame is loaded */
sizeof(buffer), /* amount of space available */
frame_type, /* type of frame to send - see defines */
destination, /* destination address */
source, /* source address */
data, /* any data to be sent - may be null */
data_len); /* number of bytes of data (up to 501) */
RS485_Send_Frame(mstp_port, &buffer[0], len);
/* FIXME: be sure to reset SilenceTimer after each octet is sent! */
}
void MSTP_Receive_Frame_FSM(
volatile struct mstp_port_struct_t *mstp_port)
{
#if PRINT_ENABLED_RECEIVE_DATA
static MSTP_RECEIVE_STATE receive_state = MSTP_RECEIVE_STATE_IDLE;
#endif
#if PRINT_ENABLED_RECEIVE
fprintf(stderr,
"MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n",
mstptext_receive_state(mstp_port->receive_state),
mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index,
mstp_port->EventCount, mstp_port->DataLength, mstp_port->SilenceTimer);
#endif
switch (mstp_port->receive_state) {
/* In the IDLE state, the node waits for the beginning of a frame. */
case MSTP_RECEIVE_STATE_IDLE:
/* EatAnError */
if (mstp_port->ReceiveError == true) {
mstp_port->ReceiveError = false;
mstp_port->SilenceTimer = 0;
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
/* wait for the start of a frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
} else if (mstp_port->DataAvailable == true) {
#if PRINT_ENABLED_RECEIVE_DATA
fprintf(stderr, "MSTP Rx: %02X ", mstp_port->DataRegister);
#endif
/* Preamble1 */
if (mstp_port->DataRegister == 0x55) {
mstp_port->DataAvailable = false;
mstp_port->SilenceTimer = 0;
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
/* receive the remainder of the frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE;
}
/* EatAnOctet */
else {
#if PRINT_ENABLED_RECEIVE_DATA
fprintf(stderr, "\n");
#endif
mstp_port->DataAvailable = false;
mstp_port->SilenceTimer = 0;
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
/* wait for the start of a frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
}
}
break;
/* In the PREAMBLE state, the node waits for the second octet of the preamble. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -