Feature/mstp zero config option (#564)
* Added a MS/TP zero-config (automatically choose an unused MAC address) using an algorithm that starts with MAC=64 and waits for a random number of PFM (minimum of 8 plus modulo 64) before attempting to choose a MAC sequentially from 64..127. The confirmation uses a 128-bit UUID with the MSTP Test Request frame. The modifications are in src/bacnet/datalink/mstp.c and src/bacnet/datalink/dlmstp.c modules enabling any device to use zero-config if enabled. A working demonstration is in the ports/stm32f4xx for the NUCLEO board. Complete unit testing is included. Options include lurking forever (wait for a router or another master node before joining) or lurking for a minimum time (enables self forming automatic MAC addressing device nodes).
This commit is contained in:
@@ -396,3 +396,7 @@ clean: ports-clean
|
||||
test:
|
||||
$(MAKE) -s -C test clean
|
||||
$(MAKE) -s -j -C test all
|
||||
|
||||
.PHONY: retest
|
||||
retest:
|
||||
$(MAKE) -s -j -C test retest
|
||||
|
||||
+13
-24
@@ -68,7 +68,7 @@
|
||||
#define MSTP_HEADER_MAX (2 + 1 + 1 + 1 + 2 + 1)
|
||||
|
||||
/* local port data - shared with RS-485 */
|
||||
static volatile struct mstp_port_struct_t MSTP_Port;
|
||||
static struct mstp_port_struct_t MSTP_Port;
|
||||
/* track the receive state to know when there is a broken packet */
|
||||
static MSTP_RECEIVE_STATE MSTP_Receive_State = MSTP_RECEIVE_STATE_IDLE;
|
||||
/* buffers needed by mstp port struct */
|
||||
@@ -185,7 +185,7 @@ static void mstp_monitor_i_am(uint8_t mac, uint8_t *pdu, uint16_t pdu_len)
|
||||
}
|
||||
|
||||
static void packet_statistics(
|
||||
struct timeval *tv, volatile struct mstp_port_struct_t *mstp_port)
|
||||
struct timeval *tv, struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
static struct timeval old_tv = { 0 };
|
||||
static uint8_t old_frame = 255;
|
||||
@@ -286,8 +286,7 @@ static void packet_statistics(
|
||||
MSTP_Statistics[src].der_reply = delta;
|
||||
}
|
||||
}
|
||||
if ((mstp_port->ReceivedValidFrame) ||
|
||||
(mstp_port->ReceivedValidFrameNotForUs)) {
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
if ((mstp_port->DataLength <= mstp_port->InputBufferSize) &&
|
||||
(mstp_port->DataLength > 0)) {
|
||||
mstp_monitor_i_am(
|
||||
@@ -413,7 +412,7 @@ static void Timer_Silence_Reset(void *pArg)
|
||||
}
|
||||
|
||||
/* functions used by the MS/TP state machine to put or get data */
|
||||
uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
(void)mstp_port;
|
||||
|
||||
@@ -423,7 +422,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
(void)mstp_port;
|
||||
(void)timeout;
|
||||
@@ -437,7 +436,7 @@ uint16_t MSTP_Get_Send(
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
@@ -447,7 +446,7 @@ void MSTP_Send_Frame(
|
||||
}
|
||||
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
(void)mstp_port;
|
||||
(void)timeout;
|
||||
@@ -623,7 +622,7 @@ static void write_global_header(void)
|
||||
}
|
||||
|
||||
static void write_received_packet(
|
||||
volatile struct mstp_port_struct_t *mstp_port, size_t header_len)
|
||||
struct mstp_port_struct_t *mstp_port, size_t header_len)
|
||||
{
|
||||
uint32_t ts_sec = 0; /* timestamp seconds */
|
||||
uint32_t ts_usec = 0; /* timestamp microseconds */
|
||||
@@ -637,8 +636,7 @@ static void write_received_packet(
|
||||
gettimeofday(&tv, NULL);
|
||||
ts_sec = tv.tv_sec;
|
||||
ts_usec = tv.tv_usec;
|
||||
if ((mstp_port->ReceivedValidFrame) ||
|
||||
(mstp_port->ReceivedValidFrameNotForUs)) {
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
packet_statistics(&tv, mstp_port);
|
||||
}
|
||||
(void)data_write(&ts_sec, sizeof(ts_sec), 1);
|
||||
@@ -765,7 +763,7 @@ static bool test_global_header(const char *filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_received_packet(volatile struct mstp_port_struct_t *mstp_port)
|
||||
static bool read_received_packet(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint32_t ts_sec = 0; /* timestamp seconds */
|
||||
uint32_t ts_usec = 0; /* timestamp microseconds */
|
||||
@@ -824,7 +822,6 @@ static bool read_received_packet(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
if (mstp_port->DataLength == 0) {
|
||||
mstp_port->ReceivedValidFrame = true;
|
||||
mstp_port->ReceivedValidFrameNotForUs = true;
|
||||
}
|
||||
} else {
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
@@ -864,19 +861,16 @@ static bool read_received_packet(volatile struct mstp_port_struct_t *mstp_port)
|
||||
if (mstp_port->DataCRC == 0xF0B8) {
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
mstp_port->ReceivedValidFrame = true;
|
||||
mstp_port->ReceivedValidFrameNotForUs = true;
|
||||
} else {
|
||||
mstp_port->ReceivedInvalidFrame = true;
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
mstp_port->ReceivedValidFrameNotForUs = false;
|
||||
}
|
||||
} else {
|
||||
mstp_port->DataLength = 0;
|
||||
}
|
||||
if (mstp_port->ReceivedInvalidFrame) {
|
||||
Invalid_Frame_Count++;
|
||||
} else if ((mstp_port->ReceivedValidFrame) ||
|
||||
(mstp_port->ReceivedValidFrameNotForUs)) {
|
||||
} else if (mstp_port->ReceivedValidFrame) {
|
||||
packet_statistics(&tv, mstp_port);
|
||||
}
|
||||
} else {
|
||||
@@ -984,7 +978,7 @@ static void print_help(char *filename)
|
||||
}
|
||||
|
||||
/* initialize some of the variables in the MS/TP Receive structure */
|
||||
static void mstp_structure_init(volatile struct mstp_port_struct_t *mstp_port)
|
||||
static void mstp_structure_init(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (mstp_port) {
|
||||
mstp_port->FrameType = FRAME_TYPE_PROPRIETARY_MAX;
|
||||
@@ -996,7 +990,6 @@ static void mstp_structure_init(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->EventCount = 0;
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
mstp_port->ReceivedValidFrameNotForUs = false;
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
@@ -1004,7 +997,7 @@ static void mstp_structure_init(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* simple test to packetize the data and print it */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
volatile struct mstp_port_struct_t *mstp_port;
|
||||
struct mstp_port_struct_t *mstp_port;
|
||||
long my_baud = 38400;
|
||||
uint32_t packet_count = 0;
|
||||
uint32_t header_len = 0;
|
||||
@@ -1177,10 +1170,6 @@ int main(int argc, char *argv[])
|
||||
write_received_packet(mstp_port, MSTP_HEADER_MAX);
|
||||
mstp_structure_init(mstp_port);
|
||||
packet_count++;
|
||||
} else if (mstp_port->ReceivedValidFrameNotForUs) {
|
||||
write_received_packet(mstp_port, MSTP_HEADER_MAX);
|
||||
mstp_structure_init(mstp_port);
|
||||
packet_count++;
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
if (MSTP_Receive_State == MSTP_RECEIVE_STATE_HEADER) {
|
||||
mstp_port->Index = 0;
|
||||
|
||||
@@ -50,9 +50,7 @@ void *dl_mstp_thread(void *pArgs)
|
||||
uint16_t pdu_len;
|
||||
uint8_t shutdown = 0;
|
||||
|
||||
shared_port_data.Treply_timeout = 260;
|
||||
shared_port_data.MSTP_Packets = 0;
|
||||
shared_port_data.Tusage_timeout = 30;
|
||||
shared_port_data.RS485_Handle = -1;
|
||||
shared_port_data.RS485_Baud = B38400;
|
||||
shared_port_data.RS485MOD = 0;
|
||||
@@ -95,6 +93,8 @@ void *dl_mstp_thread(void *pArgs)
|
||||
if (!dlmstp_init(&mstp_port, port->iface)) {
|
||||
printf("MSTP %s init failed. Stop.\n", port->iface);
|
||||
}
|
||||
mstp_port.Treply_timeout = 260;
|
||||
mstp_port.Tusage_timeout = 30;
|
||||
|
||||
port->port_id = create_msgbox();
|
||||
if (port->port_id == INVALID_MSGBOX_ID) {
|
||||
|
||||
@@ -75,7 +75,7 @@ static bool run_thread;
|
||||
|
||||
/*RT_TASK Receive_Task, Fsm_Task;*/
|
||||
/* local MS/TP port data - shared with RS-485 */
|
||||
static volatile struct mstp_port_struct_t MSTP_Port;
|
||||
static struct mstp_port_struct_t MSTP_Port;
|
||||
/* buffers needed by mstp port struct */
|
||||
static uint8_t TxBuffer[DLMSTP_MPDU_MAX];
|
||||
static uint8_t RxBuffer[DLMSTP_MPDU_MAX];
|
||||
@@ -287,7 +287,7 @@ static void *dlmstp_master_fsm_task(void *pArg)
|
||||
if (MSTP_Port.ReceivedValidFrame || MSTP_Port.ReceivedInvalidFrame) {
|
||||
run_master = true;
|
||||
} else {
|
||||
silence = MSTP_Port.SilenceTimer(NULL);
|
||||
silence = MSTP_Port.SilenceTimer(&MSTP_Port);
|
||||
switch (MSTP_Port.master_state) {
|
||||
case MSTP_MASTER_STATE_IDLE:
|
||||
if (silence >= Tno_token)
|
||||
@@ -353,7 +353,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
}
|
||||
|
||||
/* for the MS/TP state machine to use for putting received data */
|
||||
uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
|
||||
@@ -384,8 +384,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
uint16_t MSTP_Get_Send(struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0;
|
||||
uint8_t frame_type = 0;
|
||||
@@ -421,7 +420,7 @@ uint16_t MSTP_Get_Send(
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
@@ -584,8 +583,7 @@ static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
}
|
||||
|
||||
/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
uint16_t MSTP_Get_Reply(struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
bool matched = false;
|
||||
|
||||
@@ -228,7 +228,7 @@ void *dlmstp_receive_fsm_task(void *pArg)
|
||||
do {
|
||||
RS485_Check_UART_Data(mstp_port);
|
||||
MSTP_Receive_Frame_FSM(
|
||||
(volatile struct mstp_port_struct_t *)pArg);
|
||||
(struct mstp_port_struct_t *)pArg);
|
||||
received_frame = mstp_port->ReceivedValidFrame ||
|
||||
mstp_port->ReceivedInvalidFrame;
|
||||
if (received_frame) {
|
||||
@@ -274,11 +274,11 @@ void *dlmstp_master_fsm_task(void *pArg)
|
||||
run_master = true;
|
||||
break;
|
||||
case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
|
||||
if (silence >= poSharedData->Treply_timeout)
|
||||
if (silence >= mstp_port->Treply_timeout)
|
||||
run_master = true;
|
||||
break;
|
||||
case MSTP_MASTER_STATE_POLL_FOR_MASTER:
|
||||
if (silence >= poSharedData->Tusage_timeout)
|
||||
if (silence >= mstp_port->Tusage_timeout)
|
||||
run_master = true;
|
||||
break;
|
||||
default:
|
||||
@@ -324,7 +324,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
}
|
||||
|
||||
/* for the MS/TP state machine to use for putting received data */
|
||||
uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *)mstp_port->UserData;
|
||||
@@ -353,7 +353,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0;
|
||||
uint8_t frame_type = 0;
|
||||
@@ -391,7 +391,7 @@ uint16_t MSTP_Get_Send(
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
@@ -555,7 +555,7 @@ bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
|
||||
/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
bool matched = false;
|
||||
|
||||
@@ -87,16 +87,6 @@ typedef struct shared_mstp_data {
|
||||
/* buffers needed by mstp port struct */
|
||||
uint8_t TxBuffer[DLMSTP_MPDU_MAX];
|
||||
uint8_t RxBuffer[DLMSTP_MPDU_MAX];
|
||||
/* 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.) */
|
||||
uint16_t Treply_timeout;
|
||||
/* 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.) */
|
||||
uint8_t Tusage_timeout;
|
||||
/* Timer that indicates line silence - and functions */
|
||||
uint16_t SilenceTime;
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
#endif
|
||||
|
||||
/* local port data - shared with RS-485 */
|
||||
static volatile struct mstp_port_struct_t MSTP_Port;
|
||||
static struct mstp_port_struct_t MSTP_Port;
|
||||
/* buffers needed by mstp port struct */
|
||||
static uint8_t RxBuffer[DLMSTP_MPDU_MAX];
|
||||
static uint8_t TxBuffer[DLMSTP_MPDU_MAX];
|
||||
@@ -83,7 +83,7 @@ static void Timer_Silence_Reset(void *pArg)
|
||||
}
|
||||
|
||||
/* functions used by the MS/TP state machine to put or get data */
|
||||
uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
(void)mstp_port;
|
||||
|
||||
@@ -93,7 +93,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
(void)mstp_port;
|
||||
(void)timeout;
|
||||
@@ -107,7 +107,7 @@ uint16_t MSTP_Get_Send(
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
@@ -117,7 +117,7 @@ void MSTP_Send_Frame(
|
||||
}
|
||||
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
(void)mstp_port;
|
||||
(void)timeout;
|
||||
@@ -165,7 +165,7 @@ static int network_init(const char *name, int protocol)
|
||||
}
|
||||
|
||||
static void snap_received_packet(
|
||||
volatile struct mstp_port_struct_t *mstp_port, int sockfd)
|
||||
struct mstp_port_struct_t *mstp_port, int sockfd)
|
||||
{
|
||||
uint16_t mtu_len = 0; /* number of octets of packet saved in file */
|
||||
unsigned i = 0; /* counter */
|
||||
@@ -241,7 +241,7 @@ void signal_init(void)
|
||||
/* simple test to packetize the data and print it */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
volatile struct mstp_port_struct_t *mstp_port;
|
||||
struct mstp_port_struct_t *mstp_port;
|
||||
long my_baud = 38400;
|
||||
uint32_t packet_count = 0;
|
||||
int sockfd = -1;
|
||||
|
||||
+4
-4
@@ -217,7 +217,7 @@ uint32_t RS485_Get_Baud_Rate(void)
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
uint32_t RS485_Get_Port_Baud_Rate(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint32_t RS485_Get_Port_Baud_Rate(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint32_t baud = 0;
|
||||
SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *)mstp_port->UserData;
|
||||
@@ -382,7 +382,7 @@ bool RS485_Set_Baud_Rate(uint32_t baud)
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
void RS485_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
uint8_t *buffer, /* frame to send (up to 501 bytes of data) */
|
||||
uint16_t nbytes)
|
||||
{ /* number of bytes of data (up to 501) */
|
||||
@@ -458,7 +458,7 @@ void RS485_Send_Frame(
|
||||
* ALGORITHM: none
|
||||
* NOTES: none
|
||||
*****************************************************************************/
|
||||
void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port)
|
||||
void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
fd_set input;
|
||||
struct timeval waiter;
|
||||
@@ -694,7 +694,7 @@ void RS485_Print_Ports(void)
|
||||
#include <string.h>
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
volatile struct mstp_port_struct_t mstp_port = { 0 };
|
||||
struct mstp_port_struct_t mstp_port = { 0 };
|
||||
uint8_t token_buf[8] = { 0x55, 0xFF, 0x00, 0x7E, 0x07, 0x00, 0x00, 0xFD };
|
||||
uint8_t pfm_buf[8] = { 0x55, 0xFF, 0x01, 0x67, 0x07, 0x00, 0x00, 0x3E };
|
||||
long baud = 38400;
|
||||
|
||||
+3
-3
@@ -57,16 +57,16 @@ extern "C" {
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void RS485_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
|
||||
uint16_t nbytes); /* number of bytes of data (up to 501) */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void RS485_Check_UART_Data(
|
||||
volatile struct mstp_port_struct_t *mstp_port); /* port specific data */
|
||||
struct mstp_port_struct_t *mstp_port); /* port specific data */
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t RS485_Get_Port_Baud_Rate(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
struct mstp_port_struct_t *mstp_port);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t RS485_Get_Baud_Rate(
|
||||
void);
|
||||
|
||||
Vendored
+4
@@ -17,6 +17,10 @@
|
||||
"searchDir": [],
|
||||
"svdFile":"${workspaceRoot}/stm32f429.svd",
|
||||
"runToEntryPoint": "main",
|
||||
"liveWatch": {
|
||||
"enabled": true,
|
||||
"samplesPerSecond": 1
|
||||
},
|
||||
"swoConfig": {
|
||||
"enabled": true,
|
||||
"cpuFrequency": 8000000,
|
||||
|
||||
@@ -77,7 +77,7 @@ static struct my_object_functions {
|
||||
properties that are writable or that may change.
|
||||
The properties that are constant can be hard coded
|
||||
into the read-property encoding. */
|
||||
static uint32_t Object_Instance_Number = 103;
|
||||
static uint32_t Object_Instance_Number = BACNET_MAX_INSTANCE;
|
||||
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
|
||||
static BACNET_CHARACTER_STRING My_Object_Name;
|
||||
static const char *Device_Name_Default = "stm32f4xx";
|
||||
@@ -1131,9 +1131,8 @@ void Device_Init(object_functions_t *object_table)
|
||||
pObject++;
|
||||
}
|
||||
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
|
||||
if (Object_Instance_Number >= BACNET_MAX_INSTANCE) {
|
||||
Object_Instance_Number = 103;
|
||||
srand(Object_Instance_Number);
|
||||
if (Object_Instance_Number > BACNET_MAX_INSTANCE) {
|
||||
Object_Instance_Number = BACNET_MAX_INSTANCE;
|
||||
}
|
||||
characterstring_init_ansi(&My_Object_Name, Device_Name_Default);
|
||||
}
|
||||
|
||||
+27
-2
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -30,7 +30,9 @@
|
||||
#include "stm32f4xx.h"
|
||||
#include "stm32f4xx_pwr.h"
|
||||
#include "stm32f4xx_rcc.h"
|
||||
#include "stm32f4xx_rng.h"
|
||||
#include "system_stm32f4xx.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
#include "bacnet/basic/sys/ringbuf.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
@@ -96,13 +98,36 @@ int main(void)
|
||||
MSTP_Port.OutputBuffer = Output_Buffer;
|
||||
MSTP_Port.OutputBufferSize = sizeof(Output_Buffer);
|
||||
/* user data */
|
||||
MSTP_Port.ZeroConfigEnabled = true;
|
||||
MSTP_Port.SlaveNodeEnabled = false;
|
||||
MSTP_User_Data.RS485_Driver = &RS485_Driver;
|
||||
MSTP_Port.UserData = &MSTP_User_Data;
|
||||
dlmstp_init((char *)&MSTP_Port);
|
||||
dlmstp_set_mac_address(2);
|
||||
if (MSTP_Port.ZeroConfigEnabled) {
|
||||
dlmstp_set_mac_address(255);
|
||||
} else {
|
||||
/* FIXME: get the address from hardware DIP or from EEPROM */
|
||||
dlmstp_set_mac_address(1);
|
||||
}
|
||||
/* FIXME: get the baud rate from hardware DIP or from EEPROM */
|
||||
dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT);
|
||||
/* initialize application layer*/
|
||||
bacnet_init();
|
||||
/* FIXME: get the device ID from EEPROM */
|
||||
Device_Set_Object_Instance_Number(103);
|
||||
/* seed stdlib rand() with device-id to get pweudo consisten
|
||||
zero-config poll slot, or use hardware RNG to get a more random slot */
|
||||
#ifdef BACNET_ZERO_CONFIG_RNG_HARDWARE
|
||||
/* enable the random number generator hardware */
|
||||
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
|
||||
RNG_Cmd(ENABLE);
|
||||
while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET) {
|
||||
/* wait for 32-bit random number to generate */
|
||||
}
|
||||
srand(RNG_GetRandomNumber());
|
||||
#else
|
||||
srand(Device_Object_Instance_Number());
|
||||
#endif
|
||||
for (;;) {
|
||||
if (mstimer_expired(&Blink_Timer)) {
|
||||
mstimer_reset(&Blink_Timer);
|
||||
|
||||
@@ -52,7 +52,7 @@ static HANDLE Receive_Packet_Flag;
|
||||
HANDLE Received_Frame_Flag;
|
||||
static DLMSTP_PACKET Transmit_Packet;
|
||||
/* local MS/TP port data - shared with RS-485 */
|
||||
volatile struct mstp_port_struct_t MSTP_Port;
|
||||
static struct mstp_port_struct_t MSTP_Port;
|
||||
/* buffers needed by mstp port struct */
|
||||
static uint8_t TxBuffer[DLMSTP_MPDU_MAX];
|
||||
static uint8_t RxBuffer[DLMSTP_MPDU_MAX];
|
||||
@@ -62,11 +62,12 @@ static uint32_t TimeBeginPeriod;
|
||||
/* 1-millisecond target resolution */
|
||||
#define TARGET_RESOLUTION 1
|
||||
|
||||
static uint16_t Timer_Silence(void)
|
||||
static uint16_t Timer_Silence(void *arg)
|
||||
{
|
||||
uint32_t now = timeGetTime();
|
||||
uint32_t delta_time = 0;
|
||||
|
||||
(void)arg;
|
||||
if (SilenceStartTime < now) {
|
||||
delta_time = now - SilenceStartTime;
|
||||
} else {
|
||||
@@ -79,8 +80,9 @@ static uint16_t Timer_Silence(void)
|
||||
return (uint16_t)delta_time;
|
||||
}
|
||||
|
||||
static void Timer_Silence_Reset(void)
|
||||
static void Timer_Silence_Reset(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
SilenceStartTime = timeGetTime();
|
||||
}
|
||||
|
||||
@@ -241,7 +243,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
}
|
||||
|
||||
/* for the MS/TP state machine to use for putting received data */
|
||||
uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
BOOL rc;
|
||||
@@ -266,7 +268,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0;
|
||||
uint8_t destination = 0; /* destination address */
|
||||
@@ -303,7 +305,7 @@ uint16_t MSTP_Get_Send(
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
@@ -420,7 +422,7 @@ bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
|
||||
/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
uint8_t destination = 0; /* destination address */
|
||||
|
||||
@@ -51,7 +51,7 @@ static HANDLE Receive_Packet_Flag;
|
||||
HANDLE Received_Frame_Flag;
|
||||
static DLMSTP_PACKET Transmit_Packet;
|
||||
/* local MS/TP port data - shared with RS-485 */
|
||||
volatile struct mstp_port_struct_t MSTP_Port;
|
||||
struct mstp_port_struct_t MSTP_Port;
|
||||
/* buffers needed by mstp port struct */
|
||||
static uint8_t TxBuffer[DLMSTP_MPDU_MAX];
|
||||
static uint8_t RxBuffer[DLMSTP_MPDU_MAX];
|
||||
@@ -71,11 +71,13 @@ static struct mstimer Silence_Timer;
|
||||
/* Timer that indicates line silence - and functions */
|
||||
static uint32_t Timer_Silence(void *pArg)
|
||||
{
|
||||
(void)pArg;
|
||||
return mstimer_elapsed(&Silence_Timer);
|
||||
}
|
||||
|
||||
static void Timer_Silence_Reset(void *pArg)
|
||||
{
|
||||
(void)pArg;
|
||||
mstimer_set(&Silence_Timer, 0);
|
||||
}
|
||||
|
||||
@@ -226,7 +228,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
}
|
||||
|
||||
/* for the MS/TP state machine to use for putting received data */
|
||||
uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
BOOL rc;
|
||||
@@ -252,7 +254,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0;
|
||||
uint8_t destination = 0; /* destination address */
|
||||
@@ -288,7 +290,7 @@ uint16_t MSTP_Get_Send(
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
@@ -405,7 +407,7 @@ static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu,
|
||||
|
||||
/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
uint8_t destination = 0; /* destination address */
|
||||
|
||||
+2
-2
@@ -421,7 +421,7 @@ bool RS485_Set_Baud_Rate(uint32_t baud)
|
||||
|
||||
/* Transmits a Frame on the wire */
|
||||
void RS485_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
uint8_t *buffer, /* frame to send (up to 501 bytes of data) */
|
||||
uint16_t nbytes)
|
||||
{ /* number of bytes of data (up to 501) */
|
||||
@@ -453,7 +453,7 @@ void RS485_Send_Frame(
|
||||
}
|
||||
|
||||
/* called by timer, interrupt(?) or other thread */
|
||||
void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port)
|
||||
void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
char lpBuf[1];
|
||||
DWORD dwRead = 0;
|
||||
|
||||
+2
-2
@@ -58,13 +58,13 @@ extern "C" {
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void RS485_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
struct mstp_port_struct_t *mstp_port, /* port specific data */
|
||||
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
|
||||
uint16_t nbytes); /* number of bytes of data (up to 501) */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void RS485_Check_UART_Data(
|
||||
volatile struct mstp_port_struct_t *mstp_port); /* port specific data */
|
||||
struct mstp_port_struct_t *mstp_port); /* port specific data */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t RS485_Get_Baud_Rate(
|
||||
|
||||
@@ -2631,6 +2631,7 @@ bool Network_Port_Read_Range(
|
||||
(void)pInfo;
|
||||
pRequest->error_class = ERROR_CLASS_PROPERTY;
|
||||
pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
(void)pInfo;
|
||||
#endif
|
||||
break;
|
||||
case PROP_BBMD_FOREIGN_DEVICE_TABLE:
|
||||
@@ -2642,6 +2643,7 @@ bool Network_Port_Read_Range(
|
||||
(void)pInfo;
|
||||
pRequest->error_class = ERROR_CLASS_PROPERTY;
|
||||
pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
(void)pInfo;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
|
||||
+87
-112
@@ -41,7 +41,7 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest,
|
||||
{
|
||||
int bytes_sent = 0;
|
||||
unsigned i = 0; /* loop counter */
|
||||
struct dlmstp_user_data_t *port = NULL;
|
||||
struct dlmstp_user_data_t *user = NULL;
|
||||
struct dlmstp_packet *pkt;
|
||||
|
||||
if (!MSTP_Port) {
|
||||
@@ -50,8 +50,8 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest,
|
||||
if (!MSTP_Port->UserData) {
|
||||
return 0;
|
||||
}
|
||||
port = MSTP_Port->UserData;
|
||||
pkt = (struct dlmstp_packet *)(void *)Ringbuf_Data_Peek(&port->PDU_Queue);
|
||||
user = MSTP_Port->UserData;
|
||||
pkt = (struct dlmstp_packet *)(void *)Ringbuf_Data_Peek(&user->PDU_Queue);
|
||||
if (pkt && (pdu_len <= DLMSTP_MPDU_MAX)) {
|
||||
if (npdu_data->data_expecting_reply) {
|
||||
pkt->frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
|
||||
@@ -71,7 +71,7 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest,
|
||||
pkt->address.mac[0] = MSTP_BROADCAST_ADDRESS;
|
||||
pkt->address.len = 0;
|
||||
}
|
||||
if (Ringbuf_Data_Put(&port->PDU_Queue, (uint8_t *)pkt)) {
|
||||
if (Ringbuf_Data_Put(&user->PDU_Queue, (uint8_t *)pkt)) {
|
||||
bytes_sent = pdu_len;
|
||||
}
|
||||
}
|
||||
@@ -86,30 +86,30 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest,
|
||||
* @return amount of PDU data
|
||||
*/
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
struct dlmstp_packet *pkt;
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
|
||||
if (!mstp_port) {
|
||||
return 0;
|
||||
}
|
||||
port = (struct dlmstp_user_data_t *)mstp_port->UserData;
|
||||
if (!port) {
|
||||
user = (struct dlmstp_user_data_t *)mstp_port->UserData;
|
||||
if (!user) {
|
||||
return 0;
|
||||
}
|
||||
if (Ringbuf_Empty(&port->PDU_Queue)) {
|
||||
if (Ringbuf_Empty(&user->PDU_Queue)) {
|
||||
return 0;
|
||||
}
|
||||
/* look at next PDU in queue without removing it */
|
||||
pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&port->PDU_Queue);
|
||||
pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&user->PDU_Queue);
|
||||
/* convert the PDU into the MSTP Frame */
|
||||
pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0],
|
||||
mstp_port->OutputBufferSize, pkt->frame_type, pkt->address.mac[0],
|
||||
mstp_port->This_Station, &pkt->pdu[0], pkt->pdu_len);
|
||||
port->Statistics.transmit_pdu_counter++;
|
||||
(void)Ringbuf_Pop(&port->PDU_Queue, NULL);
|
||||
user->Statistics.transmit_pdu_counter++;
|
||||
(void)Ringbuf_Pop(&user->PDU_Queue, NULL);
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
@@ -148,15 +148,12 @@ static bool MSTP_Compare_Data_Expecting_Reply(
|
||||
request_pdu = &mstp_port->InputBuffer[0];
|
||||
request_pdu_len = mstp_port->DataLength;
|
||||
src_address = mstp_port->SourceAddress;
|
||||
|
||||
/* unused parameters */
|
||||
request_pdu_len = request_pdu_len;
|
||||
reply_pdu_len = reply_pdu_len;
|
||||
/* decode the request data */
|
||||
request.address.mac[0] = src_address;
|
||||
request.address.mac_len = 1;
|
||||
offset = (uint16_t)npdu_decode(
|
||||
&request_pdu[0], NULL, &request.address, &request.npdu_data);
|
||||
offset = bacnet_npdu_decode(
|
||||
&request_pdu[0], request_pdu_len, NULL, &request.address,
|
||||
&request.npdu_data);
|
||||
if (request.npdu_data.network_layer_message) {
|
||||
return false;
|
||||
}
|
||||
@@ -172,8 +169,9 @@ static bool MSTP_Compare_Data_Expecting_Reply(
|
||||
request.service_choice = request_pdu[offset + 3];
|
||||
/* decode the reply data */
|
||||
bacnet_address_copy(&reply.address, dest_address);
|
||||
offset = (uint16_t)npdu_decode(
|
||||
&reply_pdu[0], &reply.address, NULL, &reply.npdu_data);
|
||||
offset = bacnet_npdu_decode(
|
||||
&reply_pdu[0], reply_pdu_len, &reply.address, NULL,
|
||||
&reply.npdu_data);
|
||||
if (reply.npdu_data.network_layer_message) {
|
||||
return false;
|
||||
}
|
||||
@@ -244,26 +242,26 @@ static bool MSTP_Compare_Data_Expecting_Reply(
|
||||
* @return number of bytes, or 0 if no reply is available
|
||||
*/
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
struct mstp_port_struct_t *mstp_port, unsigned timeout)
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
bool matched = false;
|
||||
struct dlmstp_packet packet = { 0 };
|
||||
struct dlmstp_user_data_t *port = NULL;
|
||||
struct dlmstp_user_data_t *user = NULL;
|
||||
struct dlmstp_packet *pkt;
|
||||
|
||||
if (!mstp_port) {
|
||||
return 0;
|
||||
}
|
||||
port = mstp_port->UserData;
|
||||
if (!port) {
|
||||
user = mstp_port->UserData;
|
||||
if (!user) {
|
||||
return 0;
|
||||
}
|
||||
if (Ringbuf_Empty(&port->PDU_Queue)) {
|
||||
if (Ringbuf_Empty(&user->PDU_Queue)) {
|
||||
return 0;
|
||||
}
|
||||
/* look at next PDU in queue without removing it */
|
||||
pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&port->PDU_Queue);
|
||||
pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&user->PDU_Queue);
|
||||
/* is this the reply to the DER? */
|
||||
matched = MSTP_Compare_Data_Expecting_Reply(
|
||||
mstp_port, pkt->pdu, pkt->pdu_len, &pkt->address);
|
||||
@@ -274,8 +272,8 @@ uint16_t MSTP_Get_Reply(
|
||||
pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0],
|
||||
mstp_port->OutputBufferSize, pkt->frame_type, packet.address.mac[0],
|
||||
mstp_port->This_Station, &pkt->pdu[0], pkt->pdu_len);
|
||||
port->Statistics.transmit_pdu_counter++;
|
||||
(void)Ringbuf_Pop(&port->PDU_Queue, NULL);
|
||||
user->Statistics.transmit_pdu_counter++;
|
||||
(void)Ringbuf_Pop(&user->PDU_Queue, NULL);
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
@@ -286,70 +284,48 @@ uint16_t MSTP_Get_Reply(
|
||||
* @param buffer - buffer to send
|
||||
* @param nbytes - number of bytes of data to send
|
||||
*/
|
||||
void MSTP_Send_Frame(volatile struct mstp_port_struct_t *mstp_port,
|
||||
void MSTP_Send_Frame(struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t *buffer,
|
||||
uint16_t nbytes)
|
||||
{
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
struct dlmstp_rs485_driver *driver;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
port = mstp_port->UserData;
|
||||
if (!port) {
|
||||
user = mstp_port->UserData;
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
driver = port->RS485_Driver;
|
||||
driver = user->RS485_Driver;
|
||||
if (!driver) {
|
||||
return;
|
||||
}
|
||||
driver->send(buffer, nbytes);
|
||||
port->Statistics.transmit_frame_counter++;
|
||||
user->Statistics.transmit_frame_counter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MS/TP state machine received a frame
|
||||
* @return number of bytes queued, or 0 if unable to be queued
|
||||
*/
|
||||
uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port)
|
||||
uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
struct dlmstp_user_data_t *port = NULL;
|
||||
struct dlmstp_user_data_t *user = NULL;
|
||||
|
||||
if (!mstp_port) {
|
||||
return 0;
|
||||
}
|
||||
port = mstp_port->UserData;
|
||||
if (!port) {
|
||||
user = mstp_port->UserData;
|
||||
if (!user) {
|
||||
return 0;
|
||||
}
|
||||
port->ReceivePacketPending = true;
|
||||
user->ReceivePacketPending = true;
|
||||
|
||||
return mstp_port->DataLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Baud rate determines turnaround time.
|
||||
* The minimum time after the end of the stop bit of the final octet of a
|
||||
* received frame before a node may enable its EIA-485 driver: 40 bit times.
|
||||
* At 9600 baud, 40 bit times would be about 4.166 milliseconds
|
||||
* At 19200 baud, 40 bit times would be about 2.083 milliseconds
|
||||
* At 38400 baud, 40 bit times would be about 1.041 milliseconds
|
||||
* At 57600 baud, 40 bit times would be about 0.694 milliseconds
|
||||
* At 76800 baud, 40 bit times would be about 0.520 milliseconds
|
||||
* At 115200 baud, 40 bit times would be about 0.347 milliseconds
|
||||
* 40 bits is 4 octets including a start and stop bit with each octet
|
||||
* @param baud_rate - baud rate in bits per second
|
||||
* @return: amount of whole milliseconds to wait 1..5
|
||||
*/
|
||||
static uint32_t dlmstp_receive_turnaround_time(uint32_t baud_rate)
|
||||
{
|
||||
if (baud_rate == 0) {
|
||||
baud_rate = 9600;
|
||||
}
|
||||
return (1 + ((Tturnaround * 1000) / baud_rate));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Run the MS/TP state machines, and get packet if available
|
||||
* @param pdu - place to put PDU data for the caller
|
||||
@@ -363,11 +339,10 @@ uint16_t dlmstp_receive(
|
||||
{
|
||||
uint16_t pdu_len = 0;
|
||||
uint8_t data_register = 0;
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
struct dlmstp_rs485_driver *driver;
|
||||
uint16_t i;
|
||||
uint32_t milliseconds;
|
||||
uint32_t turnaround_milliseconds;
|
||||
|
||||
if (!MSTP_Port) {
|
||||
return 0;
|
||||
@@ -375,11 +350,11 @@ uint16_t dlmstp_receive(
|
||||
if (!MSTP_Port->UserData) {
|
||||
return 0;
|
||||
}
|
||||
port = MSTP_Port->UserData;
|
||||
if (!port) {
|
||||
user = MSTP_Port->UserData;
|
||||
if (!user) {
|
||||
return 0;
|
||||
}
|
||||
driver = port->RS485_Driver;
|
||||
driver = user->RS485_Driver;
|
||||
if (!driver) {
|
||||
return 0;
|
||||
}
|
||||
@@ -392,7 +367,6 @@ uint16_t dlmstp_receive(
|
||||
}
|
||||
/* only do receive state machine while we don't have a frame */
|
||||
while ((MSTP_Port->ReceivedValidFrame == false) &&
|
||||
(MSTP_Port->ReceivedValidFrameNotForUs == false) &&
|
||||
(MSTP_Port->ReceivedInvalidFrame == false)) {
|
||||
MSTP_Port->DataAvailable = driver->read(&data_register);
|
||||
if (MSTP_Port->DataAvailable) {
|
||||
@@ -404,39 +378,35 @@ uint16_t dlmstp_receive(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (MSTP_Port->ReceivedValidFrameNotForUs ||
|
||||
MSTP_Port->ReceivedValidFrame || MSTP_Port->ReceivedInvalidFrame) {
|
||||
if (MSTP_Port->ReceivedValidFrame || MSTP_Port->ReceivedInvalidFrame) {
|
||||
/* delay after reception before transmitting - per MS/TP spec */
|
||||
turnaround_milliseconds =
|
||||
dlmstp_receive_turnaround_time(driver->baud_rate());
|
||||
milliseconds = MSTP_Port->SilenceTimer(MSTP_Port);
|
||||
if (milliseconds < turnaround_milliseconds) {
|
||||
if (milliseconds < MSTP_Port->Tturnaround_timeout) {
|
||||
/* we're waiting; do nothing else */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (MSTP_Port->ReceivedValidFrameNotForUs) {
|
||||
MSTP_Port->ReceivedValidFrameNotForUs = false;
|
||||
port->Statistics.receive_valid_frame_counter++;
|
||||
}
|
||||
if (MSTP_Port->ReceivedValidFrame) {
|
||||
port->Statistics.receive_valid_frame_counter++;
|
||||
user->Statistics.receive_valid_frame_counter++;
|
||||
}
|
||||
if (MSTP_Port->ReceivedInvalidFrame) {
|
||||
port->Statistics.receive_invalid_frame_counter++;
|
||||
user->Statistics.receive_invalid_frame_counter++;
|
||||
}
|
||||
if (MSTP_Port->receive_state == MSTP_RECEIVE_STATE_IDLE) {
|
||||
/* only do master state machine while rx is idle */
|
||||
if (MSTP_Port->This_Station <= DEFAULT_MAX_MASTER) {
|
||||
/* only node state machines while rx is idle */
|
||||
if (MSTP_Port->SlaveNodeEnabled) {
|
||||
MSTP_Slave_Node_FSM(MSTP_Port);
|
||||
} else if ((MSTP_Port->This_Station <= DEFAULT_MAX_MASTER) ||
|
||||
MSTP_Port->ZeroConfigEnabled) {
|
||||
while (MSTP_Master_Node_FSM(MSTP_Port)) {
|
||||
/* do nothing while some states fast transition */
|
||||
};
|
||||
}
|
||||
}
|
||||
/* see if there is a packet available */
|
||||
if (port->ReceivePacketPending) {
|
||||
port->ReceivePacketPending = false;
|
||||
port->Statistics.receive_pdu_counter++;
|
||||
if (user->ReceivePacketPending) {
|
||||
user->ReceivePacketPending = false;
|
||||
user->Statistics.receive_pdu_counter++;
|
||||
pdu_len = MSTP_Port->DataLength;
|
||||
if (pdu_len > max_pdu) {
|
||||
/* PDU is too large */
|
||||
@@ -498,12 +468,9 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
*/
|
||||
void dlmstp_set_mac_address(uint8_t mac_address)
|
||||
{
|
||||
/* Master Nodes can only have address 0-127 */
|
||||
if (mac_address <= 127) {
|
||||
if (MSTP_Port) {
|
||||
MSTP_Port->This_Station = mac_address;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -648,12 +615,12 @@ void dlmstp_get_broadcast_address(BACNET_ADDRESS *dest)
|
||||
bool dlmstp_send_pdu_queue_empty(void)
|
||||
{
|
||||
bool status = false;
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
|
||||
if (MSTP_Port) {
|
||||
port = MSTP_Port->UserData;
|
||||
if (port) {
|
||||
status = Ringbuf_Empty(&port->PDU_Queue);
|
||||
user = MSTP_Port->UserData;
|
||||
if (user) {
|
||||
status = Ringbuf_Empty(&user->PDU_Queue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,12 +634,12 @@ bool dlmstp_send_pdu_queue_empty(void)
|
||||
bool dlmstp_send_pdu_queue_full(void)
|
||||
{
|
||||
bool status = false;
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
|
||||
if (MSTP_Port) {
|
||||
port = MSTP_Port->UserData;
|
||||
if (port) {
|
||||
status = Ringbuf_Full(&port->PDU_Queue);
|
||||
user = MSTP_Port->UserData;
|
||||
if (user) {
|
||||
status = Ringbuf_Full(&user->PDU_Queue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,7 +653,7 @@ bool dlmstp_send_pdu_queue_full(void)
|
||||
*/
|
||||
void dlmstp_set_baud_rate(uint32_t baud)
|
||||
{
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
struct dlmstp_rs485_driver *driver;
|
||||
|
||||
if (!MSTP_Port) {
|
||||
@@ -695,12 +662,20 @@ void dlmstp_set_baud_rate(uint32_t baud)
|
||||
if (!MSTP_Port->UserData) {
|
||||
return;
|
||||
}
|
||||
port = MSTP_Port->UserData;
|
||||
driver = port->RS485_Driver;
|
||||
user = MSTP_Port->UserData;
|
||||
driver = user->RS485_Driver;
|
||||
if (!driver) {
|
||||
return;
|
||||
}
|
||||
driver->baud_rate_set(baud);
|
||||
if (driver->baud_rate_set(baud)) {
|
||||
/* Tframe_abort=60 bit times, not to exceed 100 milliseconds.*/
|
||||
if (MSTP_Port->Tframe_abort <= 7) {
|
||||
/* within baud range, so auto-calculate range based on baud */
|
||||
MSTP_Port->Tframe_abort = 1+((60*1000UL)/baud);
|
||||
}
|
||||
/* Tturnaround=40 bit times */
|
||||
MSTP_Port->Tturnaround_timeout = 1 + ((Tturnaround * 1000) / baud);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -709,7 +684,7 @@ void dlmstp_set_baud_rate(uint32_t baud)
|
||||
*/
|
||||
uint32_t dlmstp_baud_rate(void)
|
||||
{
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
struct dlmstp_rs485_driver *driver;
|
||||
|
||||
if (!MSTP_Port) {
|
||||
@@ -718,8 +693,8 @@ uint32_t dlmstp_baud_rate(void)
|
||||
if (!MSTP_Port->UserData) {
|
||||
return 0;
|
||||
}
|
||||
port = MSTP_Port->UserData;
|
||||
driver = port->RS485_Driver;
|
||||
user = MSTP_Port->UserData;
|
||||
driver = user->RS485_Driver;
|
||||
if (!driver) {
|
||||
return 0;
|
||||
}
|
||||
@@ -733,7 +708,7 @@ uint32_t dlmstp_baud_rate(void)
|
||||
*/
|
||||
void dlmstp_fill_statistics(struct dlmstp_statistics *statistics)
|
||||
{
|
||||
struct dlmstp_user_data_t *port;
|
||||
struct dlmstp_user_data_t *user;
|
||||
|
||||
if (!MSTP_Port) {
|
||||
return;
|
||||
@@ -741,12 +716,12 @@ void dlmstp_fill_statistics(struct dlmstp_statistics *statistics)
|
||||
if (!MSTP_Port->UserData) {
|
||||
return;
|
||||
}
|
||||
port = MSTP_Port->UserData;
|
||||
if (!port) {
|
||||
user = MSTP_Port->UserData;
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
if (statistics) {
|
||||
*statistics = port->Statistics;
|
||||
*statistics = user->Statistics;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,18 +796,18 @@ void dlmstp_silence_reset(void *arg)
|
||||
*/
|
||||
bool dlmstp_init(char *ifname)
|
||||
{
|
||||
struct dlmstp_user_data_t *user_data;
|
||||
struct dlmstp_user_data_t *user;
|
||||
MSTP_Port = (struct mstp_port_struct_t *)ifname;
|
||||
if (MSTP_Port) {
|
||||
MSTP_Port->SilenceTimer = dlmstp_silence_milliseconds;
|
||||
MSTP_Port->SilenceTimerReset = dlmstp_silence_reset;
|
||||
user_data = (struct dlmstp_user_data_t *)MSTP_Port->UserData;
|
||||
if (user_data && !user_data->Initialized) {
|
||||
Ringbuf_Init(&user_data->PDU_Queue,
|
||||
(volatile uint8_t *)user_data->PDU_Buffer,
|
||||
sizeof(user_data->PDU_Buffer), DLMSTP_MAX_INFO_FRAMES);
|
||||
user = (struct dlmstp_user_data_t *)MSTP_Port->UserData;
|
||||
if (user && !user->Initialized) {
|
||||
Ringbuf_Init(&user->PDU_Queue,
|
||||
(volatile uint8_t *)user->PDU_Buffer,
|
||||
sizeof(user->PDU_Buffer), DLMSTP_MAX_INFO_FRAMES);
|
||||
MSTP_Init(MSTP_Port);
|
||||
user_data->Initialized = true;
|
||||
user->Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+461
-148
@@ -48,12 +48,15 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include "bacnet/datalink/cobs.h"
|
||||
#include "bacnet/datalink/crc.h"
|
||||
#include "bacnet/datalink/mstp.h"
|
||||
#include "bacnet/datalink/crc.h"
|
||||
#include "bacnet/datalink/mstptext.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
@@ -119,50 +122,6 @@ static inline void printf_master(const char *format, ...)
|
||||
/* least significant octet first */
|
||||
/* (pad): (optional) at most one octet of padding: X'FF' */
|
||||
|
||||
/* 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); */
|
||||
#ifndef Tframe_abort
|
||||
#define Tframe_abort 95
|
||||
#endif
|
||||
|
||||
/* 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. */
|
||||
#ifndef Treply_delay
|
||||
#define Treply_delay 250
|
||||
#endif
|
||||
|
||||
/* 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. */
|
||||
#ifndef Troff
|
||||
#define Troff 30
|
||||
#endif
|
||||
|
||||
/* 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.) */
|
||||
#ifndef Treply_timeout
|
||||
#define Treply_timeout 295
|
||||
#endif
|
||||
|
||||
/* The 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 35 milliseconds.) */
|
||||
#ifndef Tusage_timeout
|
||||
#define Tusage_timeout 30
|
||||
#endif
|
||||
|
||||
/* we need to be able to increment without rolling over */
|
||||
#define INCREMENT_AND_LIMIT_UINT8(x) \
|
||||
{ \
|
||||
@@ -170,8 +129,12 @@ static inline void printf_master(const char *format, ...)
|
||||
x++; \
|
||||
}
|
||||
|
||||
bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port)
|
||||
bool MSTP_Line_Active(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (!mstp_port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (mstp_port->EventCount > Nmin_octets);
|
||||
}
|
||||
|
||||
@@ -179,6 +142,9 @@ void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (!src) {
|
||||
return;
|
||||
}
|
||||
if (mstp_address == MSTP_BROADCAST_ADDRESS) {
|
||||
/* mac_len = 0 if broadcast address */
|
||||
src->mac_len = 0;
|
||||
@@ -219,7 +185,7 @@ void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
||||
* @param destination - destination address
|
||||
* @param source - source address
|
||||
* @param data - any data to be sent - may be null
|
||||
* @param data_len - number of bytes of data (up to 501)
|
||||
* @param data_len - number of bytes of data
|
||||
* @return number of bytes encoded, or 0 on error
|
||||
*/
|
||||
uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
@@ -237,9 +203,7 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
bool cobs_bacnet_frame = false; /* true for COBS BACnet frames */
|
||||
|
||||
/* not enough to do a header */
|
||||
if (buffer_size < 8) {
|
||||
return 0;
|
||||
}
|
||||
if (buffer && (buffer_size >= 8)) {
|
||||
buffer[0] = 0x55;
|
||||
buffer[1] = 0xFF;
|
||||
buffer[2] = frame_type;
|
||||
@@ -253,10 +217,11 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
buffer[6] = data_len & 0xFF;
|
||||
crc8 = CRC_Calc_Header(buffer[6], crc8);
|
||||
buffer[7] = ~crc8;
|
||||
}
|
||||
index = 8;
|
||||
|
||||
if ((data_len > 501) || ((frame_type >= Nmin_COBS_type) &&
|
||||
(frame_type <= Nmax_COBS_type))) {
|
||||
if ((data_len > MSTP_FRAME_NPDU_MAX) ||
|
||||
((frame_type >= Nmin_COBS_type) && (frame_type <= Nmax_COBS_type))) {
|
||||
/* COBS encoded frame */
|
||||
if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) {
|
||||
frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY;
|
||||
@@ -291,8 +256,10 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
index = index + cobs_len - 2;
|
||||
} else if (data_len > 0) {
|
||||
while (data_len && data && (index < buffer_size)) {
|
||||
if (buffer) {
|
||||
buffer[index] = *data;
|
||||
crc16 = CRC_Calc_Data(buffer[index], crc16);
|
||||
}
|
||||
data++;
|
||||
index++;
|
||||
data_len--;
|
||||
@@ -301,9 +268,13 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
return 0;
|
||||
}
|
||||
crc16 = ~crc16;
|
||||
if (buffer) {
|
||||
buffer[index] = crc16 & 0xFF; /* LSB first */
|
||||
}
|
||||
index++;
|
||||
if (buffer) {
|
||||
buffer[index] = crc16 >> 8;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
@@ -317,10 +288,9 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
* @param destination - destination address
|
||||
* @param source - source address
|
||||
* @param data - any data to be sent - may be null
|
||||
* @param data_len - number of bytes of data (up to 501)
|
||||
* @param data_len - number of bytes of data
|
||||
*/
|
||||
void MSTP_Create_And_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
void MSTP_Create_And_Send_Frame(struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t frame_type,
|
||||
uint8_t destination,
|
||||
uint8_t source,
|
||||
@@ -329,17 +299,22 @@ void MSTP_Create_And_Send_Frame(
|
||||
{
|
||||
uint16_t len = 0; /* number of bytes to send */
|
||||
|
||||
len = MSTP_Create_Frame((uint8_t *)&mstp_port->OutputBuffer[0],
|
||||
mstp_port->OutputBufferSize, frame_type, destination, source, data,
|
||||
data_len);
|
||||
len =
|
||||
MSTP_Create_Frame(mstp_port->OutputBuffer, mstp_port->OutputBufferSize,
|
||||
frame_type, destination, source, data, data_len);
|
||||
|
||||
MSTP_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[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)
|
||||
/**
|
||||
* @brief Finite State Machine for receiving an MSTP frame
|
||||
* @param mstp_port MSTP port context data
|
||||
*/
|
||||
void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
MSTP_RECEIVE_STATE receive_state = mstp_port->receive_state;
|
||||
|
||||
printf_receive(
|
||||
"MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u "
|
||||
"Silence=%u\n",
|
||||
@@ -348,24 +323,23 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->EventCount, mstp_port->DataLength,
|
||||
mstp_port->SilenceTimer((void *)mstp_port));
|
||||
switch (mstp_port->receive_state) {
|
||||
/* In the IDLE state, the node waits for the beginning of a frame.
|
||||
*/
|
||||
case MSTP_RECEIVE_STATE_IDLE:
|
||||
/* In the IDLE state, the node waits for
|
||||
the beginning of a frame. */
|
||||
/* EatAnError */
|
||||
if (mstp_port->ReceiveError == true) {
|
||||
mstp_port->ReceiveError = false;
|
||||
mstp_port->SilenceTimerReset((void *)mstp_port);
|
||||
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
||||
/* wait for the start of a frame. */
|
||||
} else if (mstp_port->DataAvailable == true) {
|
||||
/* wait for the start of a frame. */
|
||||
printf_receive_data("MSTP Rx: %02X ", mstp_port->DataRegister);
|
||||
/* Preamble1 */
|
||||
if (mstp_port->DataRegister == 0x55) {
|
||||
/* Preamble1 */
|
||||
/* receive the remainder of the frame. */
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE;
|
||||
}
|
||||
} else {
|
||||
/* EatAnOctet */
|
||||
else {
|
||||
printf_receive_data("\n");
|
||||
/* wait for the start of a frame. */
|
||||
}
|
||||
@@ -374,17 +348,17 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
||||
}
|
||||
break;
|
||||
/* In the PREAMBLE state, the node waits for the second octet of the
|
||||
* preamble. */
|
||||
case MSTP_RECEIVE_STATE_PREAMBLE:
|
||||
/* In the PREAMBLE state, the node waits for
|
||||
the second octet of the preamble. */
|
||||
/* Timeout */
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) > Tframe_abort) {
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
mstp_port->Tframe_abort) {
|
||||
/* a correct preamble has not been received */
|
||||
/* wait for the start of a frame. */
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
}
|
||||
} else if (mstp_port->ReceiveError == true) {
|
||||
/* Error */
|
||||
else if (mstp_port->ReceiveError == true) {
|
||||
mstp_port->ReceiveError = false;
|
||||
mstp_port->SilenceTimerReset((void *)mstp_port);
|
||||
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
||||
@@ -392,19 +366,17 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (mstp_port->DataAvailable == true) {
|
||||
printf_receive_data("%02X ", mstp_port->DataRegister);
|
||||
/* Preamble2 */
|
||||
if (mstp_port->DataRegister == 0xFF) {
|
||||
/* Preamble2 */
|
||||
mstp_port->Index = 0;
|
||||
mstp_port->HeaderCRC = 0xFF;
|
||||
/* receive the remainder of the frame. */
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
|
||||
}
|
||||
} else if (mstp_port->DataRegister == 0x55) {
|
||||
/* ignore RepeatedPreamble1 */
|
||||
else if (mstp_port->DataRegister == 0x55) {
|
||||
/* wait for the second preamble octet. */
|
||||
}
|
||||
} else {
|
||||
/* NotPreamble */
|
||||
else {
|
||||
/* wait for the start of a frame. */
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
}
|
||||
@@ -413,11 +385,12 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
||||
}
|
||||
break;
|
||||
/* In the HEADER state, the node waits for the fixed message header.
|
||||
*/
|
||||
case MSTP_RECEIVE_STATE_HEADER:
|
||||
/* In the HEADER state, the node waits for
|
||||
the fixed message header. */
|
||||
/* Timeout */
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) > Tframe_abort) {
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
mstp_port->Tframe_abort) {
|
||||
/* indicate that an error has occurred during the reception of a
|
||||
* frame */
|
||||
mstp_port->ReceivedInvalidFrame = true;
|
||||
@@ -425,10 +398,9 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
printf_receive_error("MSTP: Rx Header: SilenceTimer %u > %d\n",
|
||||
(unsigned)mstp_port->SilenceTimer((void *)mstp_port),
|
||||
Tframe_abort);
|
||||
}
|
||||
mstp_port->Tframe_abort);
|
||||
} else if (mstp_port->ReceiveError == true) {
|
||||
/* Error */
|
||||
else if (mstp_port->ReceiveError == true) {
|
||||
mstp_port->ReceiveError = false;
|
||||
mstp_port->SilenceTimerReset((void *)mstp_port);
|
||||
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
||||
@@ -440,43 +412,38 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else if (mstp_port->DataAvailable == true) {
|
||||
printf_receive_data("%02X ", mstp_port->DataRegister);
|
||||
/* FrameType */
|
||||
if (mstp_port->Index == 0) {
|
||||
/* FrameType */
|
||||
mstp_port->HeaderCRC = CRC_Calc_Header(
|
||||
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
||||
mstp_port->FrameType = mstp_port->DataRegister;
|
||||
mstp_port->Index = 1;
|
||||
}
|
||||
} else if (mstp_port->Index == 1) {
|
||||
/* Destination */
|
||||
else if (mstp_port->Index == 1) {
|
||||
mstp_port->HeaderCRC = CRC_Calc_Header(
|
||||
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
||||
mstp_port->DestinationAddress = mstp_port->DataRegister;
|
||||
mstp_port->Index = 2;
|
||||
}
|
||||
} else if (mstp_port->Index == 2) {
|
||||
/* Source */
|
||||
else if (mstp_port->Index == 2) {
|
||||
mstp_port->HeaderCRC = CRC_Calc_Header(
|
||||
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
||||
mstp_port->SourceAddress = mstp_port->DataRegister;
|
||||
mstp_port->Index = 3;
|
||||
}
|
||||
} else if (mstp_port->Index == 3) {
|
||||
/* Length1 */
|
||||
else if (mstp_port->Index == 3) {
|
||||
mstp_port->HeaderCRC = CRC_Calc_Header(
|
||||
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
||||
mstp_port->DataLength = mstp_port->DataRegister * 256;
|
||||
mstp_port->Index = 4;
|
||||
}
|
||||
} else if (mstp_port->Index == 4) {
|
||||
/* Length2 */
|
||||
else if (mstp_port->Index == 4) {
|
||||
mstp_port->HeaderCRC = CRC_Calc_Header(
|
||||
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
||||
mstp_port->DataLength += mstp_port->DataRegister;
|
||||
mstp_port->Index = 5;
|
||||
}
|
||||
} else if (mstp_port->Index == 5) {
|
||||
/* HeaderCRC */
|
||||
else if (mstp_port->Index == 5) {
|
||||
mstp_port->HeaderCRC = CRC_Calc_Header(
|
||||
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
||||
mstp_port->HeaderCRCActual = mstp_port->DataRegister;
|
||||
@@ -496,18 +463,9 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
printf_receive_data("%s",
|
||||
mstptext_frame_type(
|
||||
(unsigned)mstp_port->FrameType));
|
||||
if ((mstp_port->DestinationAddress ==
|
||||
mstp_port->This_Station) ||
|
||||
(mstp_port->DestinationAddress ==
|
||||
MSTP_BROADCAST_ADDRESS)) {
|
||||
/* ForUs */
|
||||
/* indicate that a frame with no data has been
|
||||
* received */
|
||||
mstp_port->ReceivedValidFrame = true;
|
||||
} else {
|
||||
/* NotForUs */
|
||||
mstp_port->ReceivedValidFrameNotForUs = true;
|
||||
}
|
||||
/* wait for the start of the next frame. */
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
} else {
|
||||
@@ -538,9 +496,8 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->DataCRC = 0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* not per MS/TP standard, but it is a case not covered */
|
||||
else {
|
||||
mstp_port->ReceiveError = false;
|
||||
/* indicate that an error has occurred during */
|
||||
/* the reception of a frame */
|
||||
@@ -555,24 +512,24 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->DataAvailable = false;
|
||||
}
|
||||
break;
|
||||
/* In the DATA state, the node waits for the data portion of a
|
||||
* frame. */
|
||||
case MSTP_RECEIVE_STATE_DATA:
|
||||
case MSTP_RECEIVE_STATE_SKIP_DATA:
|
||||
/* In the DATA and SKIP DATA states, the node waits for the
|
||||
data portion of a frame. */
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
mstp_port->Tframe_abort) {
|
||||
/* Timeout */
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) > Tframe_abort) {
|
||||
/* indicate that an error has occurred during the reception of a
|
||||
* frame */
|
||||
mstp_port->ReceivedInvalidFrame = true;
|
||||
printf_receive_error(
|
||||
"MSTP: Rx Data: SilenceTimer %ums > %dms\n",
|
||||
(unsigned)mstp_port->SilenceTimer((void *)mstp_port),
|
||||
Tframe_abort);
|
||||
mstp_port->Tframe_abort);
|
||||
/* wait for the start of the next frame. */
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
}
|
||||
} else if (mstp_port->ReceiveError == true) {
|
||||
/* Error */
|
||||
else if (mstp_port->ReceiveError == true) {
|
||||
mstp_port->ReceiveError = false;
|
||||
mstp_port->SilenceTimerReset((void *)mstp_port);
|
||||
/* indicate that an error has occurred during the reception of a
|
||||
@@ -622,29 +579,16 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
&mstp_port->InputBuffer[mstp_port->Index + 1],
|
||||
mstp_port->InputBufferSize,
|
||||
mstp_port->InputBuffer, mstp_port->Index + 1)) {
|
||||
if (mstp_port->receive_state ==
|
||||
MSTP_RECEIVE_STATE_DATA) {
|
||||
/* ForUs */
|
||||
mstp_port->ReceivedValidFrame = true;
|
||||
} else {
|
||||
/* NotForUs */
|
||||
mstp_port->ReceivedValidFrameNotForUs = true;
|
||||
}
|
||||
} else {
|
||||
mstp_port->ReceivedInvalidFrame = true;
|
||||
}
|
||||
} else {
|
||||
/* STATE DATA CRC - no need for new state */
|
||||
/* indicate the complete reception of a valid frame */
|
||||
if (mstp_port->DataCRC == 0xF0B8) {
|
||||
if (mstp_port->receive_state ==
|
||||
MSTP_RECEIVE_STATE_DATA) {
|
||||
/* ForUs */
|
||||
/* indicate the complete reception of a
|
||||
valid frame */
|
||||
mstp_port->ReceivedValidFrame = true;
|
||||
} else {
|
||||
/* NotForUs */
|
||||
mstp_port->ReceivedValidFrameNotForUs = true;
|
||||
}
|
||||
} else {
|
||||
mstp_port->ReceivedInvalidFrame = true;
|
||||
printf_receive_error(
|
||||
@@ -674,8 +618,12 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
return;
|
||||
}
|
||||
|
||||
/* returns true if we need to transition immediately */
|
||||
bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/**
|
||||
* @brief Finite State Machine for receiving an MSTP frame
|
||||
* @param mstp_port MSTP port context data
|
||||
* @return true if we need to transition immediately
|
||||
*/
|
||||
bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
unsigned length = 0;
|
||||
uint8_t next_poll_station = 0;
|
||||
@@ -695,6 +643,23 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
(mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1);
|
||||
switch (mstp_port->master_state) {
|
||||
case MSTP_MASTER_STATE_INITIALIZE:
|
||||
if (mstp_port->ZeroConfigEnabled) {
|
||||
MSTP_Zero_Config_FSM(mstp_port);
|
||||
if (mstp_port->This_Station != 255) {
|
||||
/* indicate that the next station is unknown */
|
||||
mstp_port->Next_Station = mstp_port->This_Station;
|
||||
mstp_port->Poll_Station = (mstp_port->Next_Station + 1) %
|
||||
(mstp_port->Zero_Config_Max_Master + 1);
|
||||
mstp_port->TokenCount = Npoll;
|
||||
mstp_port->EventCount = 0;
|
||||
mstp_port->SoleMaster = true;
|
||||
MSTP_Create_And_Send_Frame(mstp_port,
|
||||
FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station,
|
||||
mstp_port->This_Station, NULL, 0);
|
||||
mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
||||
transition_now = true;
|
||||
}
|
||||
} else {
|
||||
/* DoneInitializing */
|
||||
/* indicate that the next station is unknown */
|
||||
mstp_port->Next_Station = mstp_port->This_Station;
|
||||
@@ -705,6 +670,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->SoleMaster = false;
|
||||
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
||||
transition_now = true;
|
||||
}
|
||||
break;
|
||||
case MSTP_MASTER_STATE_IDLE:
|
||||
/* In the IDLE state, the node waits for a frame. */
|
||||
@@ -721,7 +687,16 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->DataLength, mstp_port->FrameCount,
|
||||
mstp_port->SilenceTimer((void *)mstp_port),
|
||||
mstptext_frame_type((unsigned)mstp_port->FrameType));
|
||||
if ((mstp_port->DestinationAddress ==
|
||||
if (mstp_port->SourceAddress == mstp_port->This_Station) {
|
||||
/* DuplicateNode */
|
||||
if (mstp_port->ZeroConfigEnabled) {
|
||||
/* we are a zero config node - start over */
|
||||
mstp_port->Zero_Config_State =
|
||||
MSTP_ZERO_CONFIG_STATE_INIT;
|
||||
mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE;
|
||||
}
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
} else if ((mstp_port->DestinationAddress ==
|
||||
mstp_port->This_Station) ||
|
||||
(mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS)) {
|
||||
/* destined for me! */
|
||||
@@ -857,7 +832,8 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
|
||||
/* In the WAIT_FOR_REPLY state, the node waits for */
|
||||
/* a reply from another node. */
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >= Treply_timeout) {
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >=
|
||||
mstp_port->Treply_timeout) {
|
||||
/* ReplyTimeout */
|
||||
/* assume that the request has failed */
|
||||
mstp_port->FrameCount = mstp_port->Nmax_info_frames;
|
||||
@@ -1011,7 +987,8 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
case MSTP_MASTER_STATE_PASS_TOKEN:
|
||||
/* The PASS_TOKEN state listens for a successor to begin using */
|
||||
/* the token that this node has just attempted to pass. */
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) <= Tusage_timeout) {
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) <=
|
||||
mstp_port->Tusage_timeout) {
|
||||
if (mstp_port->EventCount > Nmin_octets) {
|
||||
/* SawTokenUser */
|
||||
/* Assume that a frame has been sent by the new token user.
|
||||
@@ -1138,7 +1115,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
}
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
} else if ((mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
Tusage_timeout) ||
|
||||
mstp_port->Tusage_timeout) ||
|
||||
(mstp_port->ReceivedInvalidFrame == true)) {
|
||||
if (mstp_port->SoleMaster == true) {
|
||||
/* SoleMaster */
|
||||
@@ -1209,7 +1186,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* clear our flag we were holding for comparison */
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
} else if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
Treply_delay) {
|
||||
mstp_port->Treply_delay) {
|
||||
/* DeferredReply */
|
||||
/* If no reply will be available from the higher layers */
|
||||
/* within Treply_delay after the reception of the */
|
||||
@@ -1247,7 +1224,11 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
return transition_now;
|
||||
}
|
||||
|
||||
void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/**
|
||||
* @brief Finite State Machine for the Slave Node process
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
void MSTP_Slave_Node_FSM(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
unsigned length = 0;
|
||||
|
||||
@@ -1257,9 +1238,27 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* invalid frame was received */
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
} else if (mstp_port->ReceivedValidFrame) {
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
switch (mstp_port->FrameType) {
|
||||
case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
|
||||
if (mstp_port->DestinationAddress != MSTP_BROADCAST_ADDRESS) {
|
||||
/* indicate successful reception to the higher layers */
|
||||
(void)MSTP_Put_Receive(mstp_port);
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_TEST_REQUEST:
|
||||
MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TEST_RESPONSE,
|
||||
mstp_port->SourceAddress, mstp_port->This_Station,
|
||||
&mstp_port->InputBuffer[0], mstp_port->DataLength);
|
||||
break;
|
||||
case FRAME_TYPE_TOKEN:
|
||||
case FRAME_TYPE_POLL_FOR_MASTER:
|
||||
case FRAME_TYPE_TEST_RESPONSE:
|
||||
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* The ANSWER_DATA_REQUEST state is entered when a */
|
||||
/* BACnet Data Expecting Reply, a Test_Request, or */
|
||||
/* a proprietary frame that expects a reply is received. */
|
||||
@@ -1281,7 +1280,7 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* clear our flag we were holding for comparison */
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
} else if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
Treply_delay) {
|
||||
mstp_port->Treply_delay) {
|
||||
/* If no reply will be available from the higher layers
|
||||
within Treply_delay after the reception of the final
|
||||
octet of the requesting frame (the mechanism used to
|
||||
@@ -1290,26 +1289,321 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* clear our flag we were holding for comparison */
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize a UUID for storing the unique identifier of this node
|
||||
* which is used to send and validate a test request and test response
|
||||
* @note A Universally Unique IDentifier (UUID) - also called a
|
||||
* Global Unique IDentifier (GUID) - is a 128-bit value.
|
||||
*
|
||||
* 4.4. Algorithms for Creating a UUID from Truly Random or
|
||||
* Pseudo-Random Numbers
|
||||
*
|
||||
* The version 4 UUID is meant for generating UUIDs from truly-random or
|
||||
* pseudo-random numbers.
|
||||
*
|
||||
* The algorithm is as follows:
|
||||
*
|
||||
* o Set the two most significant bits (bits 6 and 7) of the
|
||||
* clock_seq_hi_and_reserved to zero and one, respectively.
|
||||
*
|
||||
* o Set the four most significant bits (bits 12 through 15) of the
|
||||
* time_hi_and_version field to the 4-bit version number from
|
||||
* Section 4.1.3.
|
||||
*
|
||||
* o Set all the other bits to randomly (or pseudo-randomly) chosen
|
||||
* values.
|
||||
*
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
void MSTP_Zero_Config_UUID_Init(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
/* 1. Generate 16 random bytes = 128 bits */
|
||||
for (i = 0; i < MSTP_UUID_SIZE; i++) {
|
||||
mstp_port->UUID[i] = rand() % 255;
|
||||
}
|
||||
/* 2. Adjust certain bits according to RFC 4122 section 4.4.
|
||||
This just means do the following
|
||||
(a) set the high nibble of the 7th byte equal to 4 and
|
||||
(b) set the two most significant bits of the 9th byte to 10'B,
|
||||
so the high nibble will be one of {8,9,A,B}.
|
||||
From http://www.cryptosys.net/pki/Uuid.c.html */
|
||||
mstp_port->UUID[6] = 0x40 | (mstp_port->UUID[6] & 0x0f);
|
||||
mstp_port->UUID[8] = 0x80 | (mstp_port->UUID[8] & 0x3f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ZERO_CONFIGURATION_INIT state is entered when
|
||||
* ZeroConfigurationMode is TRUE
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
static void MSTP_Zero_Config_State_Init(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint32_t slots;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
mstp_port->Poll_Count = 0;
|
||||
mstp_port->Zero_Config_Station = Nmin_poll_station;
|
||||
mstp_port->Npoll_slot = 1 + (rand() % Nmax_poll_slot);
|
||||
/* basic silence timeout is the dropped token time plus
|
||||
one Tslot after the last master node. Add one Tslot of
|
||||
silence timeout per zero config priority slot */
|
||||
slots = 128 + mstp_port->Npoll_slot;
|
||||
mstp_port->Zero_Config_Silence = Tno_token + Tslot * slots;
|
||||
MSTP_Zero_Config_UUID_Init(mstp_port);
|
||||
mstp_port->Zero_Config_Max_Master = 0;
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ZERO_CONFIGURATION_IDLE state is entered when
|
||||
* ZeroConfigurationMode is TRUE, and a node is
|
||||
* is waiting for any frame or waiting to timeout.
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
static void MSTP_Zero_Config_State_Idle(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
/* next state will clear the frame flags */
|
||||
/* MonitorPFM */
|
||||
mstp_port->Poll_Count = 0;
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
/* InvalidFrame */
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
} else if (mstp_port->Zero_Config_Silence > 0) {
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
mstp_port->Zero_Config_Silence) {
|
||||
/* ClaimAddress */
|
||||
/* long silence indicates we are alone or
|
||||
with other silent devices */
|
||||
/* claim the token at the current zero-config address */
|
||||
/* configure max master at maximum */
|
||||
/* confirm this station with a quick test */
|
||||
mstp_port->Zero_Config_Max_Master = DEFAULT_MAX_MASTER;
|
||||
MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TEST_REQUEST,
|
||||
mstp_port->Zero_Config_Station, mstp_port->Zero_Config_Station,
|
||||
mstp_port->UUID, sizeof(mstp_port->UUID));
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CONFIRM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ZERO_CONFIGURATION_LURK state is entered when
|
||||
* ZeroConfigurationMode is TRUE, and a node is
|
||||
* counting a Poll For Master frames to Zero_Config_Station address
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
static void MSTP_Zero_Config_State_Lurk(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint8_t count, frame, src, dst;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
dst = mstp_port->DestinationAddress;
|
||||
src = mstp_port->SourceAddress;
|
||||
frame = mstp_port->FrameType;
|
||||
if (frame == FRAME_TYPE_POLL_FOR_MASTER) {
|
||||
if ((dst > mstp_port->Zero_Config_Max_Master) &&
|
||||
(dst <= DEFAULT_MAX_MASTER)) {
|
||||
/* LearnMaxMaster */
|
||||
mstp_port->Zero_Config_Max_Master = dst;
|
||||
}
|
||||
}
|
||||
if (src == mstp_port->Zero_Config_Station) {
|
||||
/* AddressInUse */
|
||||
/* monitor PFM from the next address */
|
||||
mstp_port->Zero_Config_Station++;
|
||||
if (mstp_port->Zero_Config_Station > Nmax_poll_station) {
|
||||
/* start again from first */
|
||||
mstp_port->Zero_Config_Station = Nmin_poll_station;
|
||||
}
|
||||
mstp_port->Poll_Count = 0;
|
||||
} else if ((frame == FRAME_TYPE_POLL_FOR_MASTER) &&
|
||||
(dst == mstp_port->Zero_Config_Station)) {
|
||||
mstp_port->Poll_Count++;
|
||||
/* calculate this node poll count priority */
|
||||
count = Nmin_poll + mstp_port->Npoll_slot;
|
||||
if (mstp_port->Poll_Count == count) {
|
||||
/* ClaimAddress */
|
||||
MSTP_Create_And_Send_Frame(mstp_port,
|
||||
FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, src,
|
||||
mstp_port->Zero_Config_Station, NULL, 0);
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CLAIM;
|
||||
}
|
||||
}
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
/* InvalidFrame */
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
} else if (mstp_port->Zero_Config_Silence > 0) {
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
mstp_port->Zero_Config_Silence) {
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ZERO_CONFIGURATION_CLAIM state is entered when a node
|
||||
* is waiting for a Token frame from the master to which it
|
||||
* previously sent a Reply To Poll For Master frame, and
|
||||
* ZeroConfigurationMode is TRUE.
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
static void MSTP_Zero_Config_State_Claim(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint8_t frame, src, dst;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
/* */
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
dst = mstp_port->DestinationAddress;
|
||||
src = mstp_port->SourceAddress;
|
||||
frame = mstp_port->FrameType;
|
||||
if (src == mstp_port->Zero_Config_Station) {
|
||||
/* ClaimAddressInUse */
|
||||
/* monitor PFM from the next address */
|
||||
mstp_port->Zero_Config_Station++;
|
||||
if (mstp_port->Zero_Config_Station > Nmax_poll_station) {
|
||||
/* start again from first */
|
||||
mstp_port->Zero_Config_Station = Nmin_poll_station;
|
||||
}
|
||||
mstp_port->Poll_Count = 0;
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
|
||||
} else if (frame == FRAME_TYPE_TOKEN) {
|
||||
if (dst == mstp_port->Zero_Config_Station) {
|
||||
/* ClaimTokenForUs */
|
||||
MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TEST_REQUEST,
|
||||
src, mstp_port->Zero_Config_Station, mstp_port->UUID,
|
||||
MSTP_UUID_SIZE);
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CONFIRM;
|
||||
}
|
||||
}
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
/* ClaimInvalidFrame */
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
} else if (mstp_port->Zero_Config_Silence > 0) {
|
||||
/* ClaimLostToken */
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
mstp_port->Zero_Config_Silence) {
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ZERO_CONFIGURATION_CONFIRM state is entered when
|
||||
* a node is waiting for a Test Response frame and
|
||||
* ZeroConfigurationMode is TRUE.
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
static void MSTP_Zero_Config_State_Confirm(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
bool match = false;
|
||||
uint8_t frame, src, dst;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
dst = mstp_port->DestinationAddress;
|
||||
src = mstp_port->SourceAddress;
|
||||
frame = mstp_port->FrameType;
|
||||
/* note: test frame could be from us. Check frame type first. */
|
||||
if (frame == FRAME_TYPE_TEST_RESPONSE) {
|
||||
if (dst == mstp_port->Zero_Config_Station) {
|
||||
match = true;
|
||||
}
|
||||
if (match & (mstp_port->DataLength < MSTP_UUID_SIZE)) {
|
||||
match = false;
|
||||
}
|
||||
if (match &&
|
||||
(memcmp(mstp_port->InputBuffer, mstp_port->UUID,
|
||||
MSTP_UUID_SIZE) != 0)) {
|
||||
match = false;
|
||||
}
|
||||
if (match) {
|
||||
/* ConfirmationSuccessful */
|
||||
mstp_port->This_Station = mstp_port->Zero_Config_Station;
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_USE;
|
||||
} else {
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
/* ConfirmationFailed */
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
||||
}
|
||||
} else if (src == mstp_port->Zero_Config_Station) {
|
||||
/* ConfirmationAddressInUse */
|
||||
/* monitor PFM from the next address */
|
||||
mstp_port->Zero_Config_Station++;
|
||||
if (mstp_port->Zero_Config_Station > Nmax_poll_station) {
|
||||
/* start again from first */
|
||||
mstp_port->Zero_Config_Station = Nmin_poll_station;
|
||||
}
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
|
||||
}
|
||||
} else if (mstp_port->SilenceTimer((void *)mstp_port) >=
|
||||
mstp_port->Treply_timeout) {
|
||||
/* ConfirmationTimeout */
|
||||
/* In case validating device doesn't support Test Request */
|
||||
/* no response and no collision */
|
||||
mstp_port->This_Station = mstp_port->Zero_Config_Station;
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_USE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finite State Machine for the Zero Configuration process
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
if (!mstp_port->ZeroConfigEnabled) {
|
||||
return;
|
||||
}
|
||||
switch (mstp_port->Zero_Config_State) {
|
||||
case MSTP_ZERO_CONFIG_STATE_INIT:
|
||||
MSTP_Zero_Config_State_Init(mstp_port);
|
||||
break;
|
||||
case FRAME_TYPE_TEST_REQUEST:
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
MSTP_Create_And_Send_Frame(mstp_port, FRAME_TYPE_TEST_RESPONSE,
|
||||
mstp_port->SourceAddress, mstp_port->This_Station,
|
||||
&mstp_port->InputBuffer[0], mstp_port->DataLength);
|
||||
case MSTP_ZERO_CONFIG_STATE_IDLE:
|
||||
MSTP_Zero_Config_State_Idle(mstp_port);
|
||||
break;
|
||||
case MSTP_ZERO_CONFIG_STATE_LURK:
|
||||
MSTP_Zero_Config_State_Lurk(mstp_port);
|
||||
break;
|
||||
case MSTP_ZERO_CONFIG_STATE_CLAIM:
|
||||
MSTP_Zero_Config_State_Claim(mstp_port);
|
||||
break;
|
||||
case MSTP_ZERO_CONFIG_STATE_CONFIRM:
|
||||
MSTP_Zero_Config_State_Confirm(mstp_port);
|
||||
break;
|
||||
case MSTP_ZERO_CONFIG_STATE_USE:
|
||||
break;
|
||||
case FRAME_TYPE_TOKEN:
|
||||
case FRAME_TYPE_POLL_FOR_MASTER:
|
||||
case FRAME_TYPE_TEST_RESPONSE:
|
||||
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
||||
default:
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* note: This_Station assumed to be set with the MAC address */
|
||||
/* note: Nmax_info_frames assumed to be set (default=1) */
|
||||
@@ -1317,7 +1611,7 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* note: InputBuffer and InputBufferSize assumed to be set */
|
||||
/* note: OutputBuffer and OutputBufferSize assumed to be set */
|
||||
/* note: SilenceTimer and SilenceTimerReset assumed to be set */
|
||||
void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port)
|
||||
void MSTP_Init(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (mstp_port) {
|
||||
#if 0
|
||||
@@ -1330,10 +1624,28 @@ void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port)
|
||||
/* FIXME: these are adjustable, so you must set these in dlmstp */
|
||||
mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES;
|
||||
mstp_port->Nmax_master = DEFAULT_MAX_MASTER;
|
||||
mstp_port->Tframe_abort = DEFAULT_Tframe_abort;
|
||||
mstp_port->Treply_delay = DEFAULT_Treply_delay;
|
||||
mstp_port->Treply_timeout = DEFAULT_Treply_timeout;
|
||||
mstp_port->Tusage_timeout = DEFAULT_Tusage_timeout;
|
||||
/* FIXME: point to functions */
|
||||
mstp_port->SilenceTimer = Timer_Silence;
|
||||
mstp_port = >SilenceTimerReset = Timer_Silence_Reset;
|
||||
mstp_port->SilenceTimerReset = Timer_Silence_Reset;
|
||||
#endif
|
||||
if ((mstp_port->Tframe_abort < 6) || (mstp_port->Tframe_abort > 100)) {
|
||||
mstp_port->Tframe_abort = DEFAULT_Tframe_abort;
|
||||
}
|
||||
if (mstp_port->Treply_delay > 250) {
|
||||
mstp_port->Treply_delay = DEFAULT_Treply_delay;
|
||||
}
|
||||
if ((mstp_port->Treply_timeout < 20) ||
|
||||
(mstp_port->Treply_timeout > 300)) {
|
||||
mstp_port->Treply_timeout = DEFAULT_Treply_timeout;
|
||||
}
|
||||
if ((mstp_port->Tusage_timeout < 20) ||
|
||||
(mstp_port->Tusage_timeout > 35)) {
|
||||
mstp_port->Tusage_timeout = DEFAULT_Tusage_timeout;
|
||||
}
|
||||
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
||||
mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE;
|
||||
mstp_port->ReceiveError = false;
|
||||
@@ -1351,11 +1663,12 @@ void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port)
|
||||
mstp_port->Poll_Station = mstp_port->This_Station;
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
mstp_port->ReceivedValidFrameNotForUs = false;
|
||||
mstp_port->RetryCount = 0;
|
||||
mstp_port->SilenceTimerReset((void *)mstp_port);
|
||||
mstp_port->SilenceTimerReset(mstp_port);
|
||||
mstp_port->SoleMaster = false;
|
||||
mstp_port->SourceAddress = 0;
|
||||
mstp_port->TokenCount = 0;
|
||||
/* zero config */
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
+156
-103
@@ -30,6 +30,17 @@
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/datalink/mstpdef.h"
|
||||
#include "bacnet/config.h"
|
||||
|
||||
/* 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. */
|
||||
#ifndef Troff
|
||||
#define Troff 30
|
||||
#endif
|
||||
|
||||
/* size of the buffer used to send and validate a unique test request */
|
||||
#define MSTP_UUID_SIZE 16
|
||||
|
||||
struct mstp_port_struct_t {
|
||||
MSTP_RECEIVE_STATE receive_state;
|
||||
@@ -47,13 +58,13 @@ struct mstp_port_struct_t {
|
||||
/* if a valid frame is received. */
|
||||
/* Set to FALSE by the Master or Slave Node state machine. */
|
||||
unsigned ReceivedValidFrame : 1;
|
||||
/* A Boolean flag set to TRUE by the Receive State Machine */
|
||||
/* if a valid frame is received but it is not addressed to us. */
|
||||
/* Set to FALSE by the Master or Slave Node state machine. */
|
||||
unsigned ReceivedValidFrameNotForUs:1;
|
||||
/* A Boolean flag set to TRUE by the master machine if this node is the */
|
||||
/* only known master node. */
|
||||
/* A Boolean flag set to TRUE by the master machine if this node is the
|
||||
only known master node. */
|
||||
unsigned SoleMaster : 1;
|
||||
/* A Boolean flag set to TRUE if this node is a slave node */
|
||||
unsigned SlaveNodeEnabled : 1;
|
||||
/* A Boolean flag set to TRUE if this node is using a ZeroConfig address */
|
||||
unsigned ZeroConfigEnabled : 1;
|
||||
/* stores the latest received data */
|
||||
uint8_t DataRegister;
|
||||
/* Used to accumulate the CRC on the data field of a frame. */
|
||||
@@ -65,109 +76,157 @@ struct mstp_port_struct_t {
|
||||
uint16_t DataLength;
|
||||
/* Used to store the destination address of a received frame. */
|
||||
uint8_t DestinationAddress;
|
||||
/* Used to count the number of received octets or errors. */
|
||||
/* This is used in the detection of link activity. */
|
||||
/* Compared to Nmin_octets */
|
||||
/* Used to count the number of received octets or errors.
|
||||
This is used in the detection of link activity.
|
||||
Compared to Nmin_octets */
|
||||
uint8_t EventCount;
|
||||
/* Used to store the frame type of a received frame. */
|
||||
uint8_t FrameType;
|
||||
/* The number of frames sent by this node during a single token hold. */
|
||||
/* When this counter reaches the value Nmax_info_frames, the node must */
|
||||
/* The number of frames sent by this node during a single token hold.
|
||||
When this counter reaches the value Nmax_info_frames, the node must */
|
||||
/* pass the token. */
|
||||
uint8_t FrameCount;
|
||||
/* Used to accumulate the CRC on the header of a frame. */
|
||||
uint8_t HeaderCRC;
|
||||
/* Used to store the actual CRC from the header. */
|
||||
uint8_t HeaderCRCActual;
|
||||
/* Used as an index by the Receive State Machine, up to a maximum value of */
|
||||
/* InputBufferSize. */
|
||||
/* Used as an index by the Receive State Machine,
|
||||
up to a maximum value of InputBufferSize. */
|
||||
uint32_t Index;
|
||||
/* An array of octets, used to store octets as they are received. */
|
||||
/* InputBuffer is indexed from 0 to InputBufferSize-1. */
|
||||
/* The maximum size of a frame is 501 octets. */
|
||||
/* FIXME: assign this to an actual array of bytes! */
|
||||
/* An array of octets, used to store octets as they are received.
|
||||
InputBuffer is indexed from 0 to InputBufferSize-1. */
|
||||
/* Note: assign this to an actual array of bytes! */
|
||||
/* Note: the buffer is designed as a pointer since some compilers
|
||||
and microcontroller architectures have limits as to places to
|
||||
hold contiguous memory. */
|
||||
uint8_t *InputBuffer;
|
||||
uint16_t InputBufferSize;
|
||||
/* "Next Station," the MAC address of the node to which This Station passes */
|
||||
/* the token. If the Next_Station is unknown, Next_Station shall be equal to */
|
||||
/* "Next Station," the MAC address of the node to which
|
||||
This Station passes */
|
||||
/* the token. If the Next_Station is unknown, Next_Station
|
||||
shall be equal to */
|
||||
/* This_Station. */
|
||||
uint8_t Next_Station;
|
||||
/* "Poll Station," the MAC address of the node to which This Station last */
|
||||
/* sent a Poll For Master. This is used during token maintenance. */
|
||||
/* "Poll Station," the MAC address of the node to which This Station last
|
||||
sent a Poll For Master. This is used during token maintenance. */
|
||||
uint8_t Poll_Station;
|
||||
/* A counter of transmission retries used for Token and Poll For Master */
|
||||
/* transmission. */
|
||||
/* A counter of transmission retries used for Token and Poll For Master
|
||||
transmission. */
|
||||
unsigned RetryCount;
|
||||
/* A timer with nominal 5 millisecond resolution used to measure and */
|
||||
/* generate silence on the medium between octets. It is incremented by a */
|
||||
/* timer process and is cleared by the Receive State Machine when activity */
|
||||
/* is detected and by the SendFrame procedure as each octet is transmitted. */
|
||||
/* Since the timer resolution is limited and the timer is not necessarily */
|
||||
/* synchronized to other machine events, a timer value of N will actually */
|
||||
/* denote intervals between N-1 and N */
|
||||
/* A timer with nominal 5 millisecond resolution used to measure
|
||||
and generate silence on the medium between octets. It is
|
||||
incremented by a timer process and is cleared by the Receive
|
||||
State Machine when activity is detected and by the SendFrame
|
||||
procedure as each octet is transmitted. */
|
||||
/* Since the timer resolution is limited and the timer is not necessarily
|
||||
synchronized to other machine events, a timer value of N will actually
|
||||
denote intervals between N-1 and N */
|
||||
/* Note: done here as functions - put into timer task or ISR
|
||||
so that you can be atomic on 8 bit microcontrollers */
|
||||
uint32_t(
|
||||
*SilenceTimer) (
|
||||
void *pArg);
|
||||
void (
|
||||
*SilenceTimerReset) (
|
||||
void *pArg);
|
||||
uint32_t (*SilenceTimer)(void *pArg);
|
||||
void (*SilenceTimerReset)(void *pArg);
|
||||
|
||||
/* A timer used to measure and generate Reply Postponed frames. It is */
|
||||
/* incremented by a timer process and is cleared by the Master Node State */
|
||||
/* Machine when a Data Expecting Reply Answer activity is completed. */
|
||||
/* note: we always send a reply postponed since a message other than
|
||||
/* A timer used to measure and generate Reply Postponed frames. It is
|
||||
incremented by a timer process and is cleared by the Master Node State
|
||||
Machine when a Data Expecting Reply Answer activity is completed.
|
||||
note: we always send a reply postponed since a message other than
|
||||
the reply may be in the transmit queue */
|
||||
/* uint16_t ReplyPostponedTimer; */
|
||||
|
||||
/* Used to store the Source Address of a received frame. */
|
||||
uint8_t SourceAddress;
|
||||
|
||||
/* The number of tokens received by this node. When this counter reaches the */
|
||||
/* value Npoll, the node polls the address range between TS and NS for */
|
||||
/* additional master nodes. TokenCount is set to zero at the end of the */
|
||||
/* polling process. */
|
||||
/* The number of tokens received by this node. When this counter
|
||||
reaches the value Npoll, the node polls the address range between
|
||||
TS and NS for additional master nodes. TokenCount is set to zero
|
||||
at the end of the polling process. */
|
||||
unsigned TokenCount;
|
||||
|
||||
/* "This Station," the MAC address of this node. TS is generally read from a */
|
||||
/* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */
|
||||
/* 0 to 254. The value 255 is used to denote broadcast when used as a */
|
||||
/* destination address but is not allowed as a value for TS. */
|
||||
/* "This Station," the MAC address of this node. TS is generally read from a
|
||||
hardware DIP switch, or from nonvolatile memory. Valid values for TS are
|
||||
0 to 254. The value 255 is used to denote broadcast when used as a
|
||||
destination address but is not allowed as a value for TS. */
|
||||
uint8_t This_Station;
|
||||
|
||||
/* This parameter represents the value of the Max_Info_Frames property of */
|
||||
/* the node's Device object. The value of Max_Info_Frames specifies the */
|
||||
/* maximum number of information frames the node may send before it must */
|
||||
/* pass the token. Max_Info_Frames may have different values on different */
|
||||
/* nodes. This may be used to allocate more or less of the available link */
|
||||
/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */
|
||||
/* node, its value shall be 1. */
|
||||
/* This parameter represents the value of the Max_Info_Frames property of
|
||||
the node's Device object. The value of Max_Info_Frames specifies the
|
||||
maximum number of information frames the node may send before it must
|
||||
pass the token. Max_Info_Frames may have different values on different
|
||||
nodes. This may be used to allocate more or less of the available link
|
||||
bandwidth to particular nodes. If Max_Info_Frames is not writable in a
|
||||
node, its value shall be 1.*/
|
||||
uint8_t Nmax_info_frames;
|
||||
|
||||
/* This parameter represents the value of the Max_Master property of the */
|
||||
/* node's Device object. The value of Max_Master specifies the highest */
|
||||
/* allowable address for master nodes. The value of Max_Master shall be */
|
||||
/* less than or equal to 127. If Max_Master is not writable in a node, */
|
||||
/* its value shall be 127. */
|
||||
/* This parameter represents the value of the Max_Master property of the
|
||||
node's Device object. The value of Max_Master specifies the highest
|
||||
allowable address for master nodes. The value of Max_Master shall be
|
||||
less than or equal to 127. If Max_Master is not writable in a node,
|
||||
its value shall be 127. */
|
||||
uint8_t Nmax_master;
|
||||
|
||||
/* An array of octets, used to store octets for transmitting */
|
||||
/* OutputBuffer is indexed from 0 to OutputBufferSize-1. */
|
||||
/* The maximum size of a frame is 501 octets. */
|
||||
/* FIXME: assign this to an actual array of bytes! */
|
||||
/* Note: the buffer is designed as a pointer since some compilers
|
||||
/* An array of octets, used to store octets for transmitting
|
||||
OutputBuffer is indexed from 0 to OutputBufferSize-1.
|
||||
FIXME: assign this to an actual array of bytes!
|
||||
Note: the buffer is designed as a pointer since some compilers
|
||||
and microcontroller architectures have limits as to places to
|
||||
hold contiguous memory. */
|
||||
uint8_t *OutputBuffer;
|
||||
uint16_t OutputBufferSize;
|
||||
|
||||
/* orderly transition tracking for zero-configuration node startup */
|
||||
MSTP_ZERO_CONFIG_STATE Zero_Config_State;
|
||||
/* the MAC address that this node is testing for MAC addresses
|
||||
that are not in-use.*/
|
||||
uint8_t Zero_Config_Station;
|
||||
/* Used to count the number of received poll-for-master frames
|
||||
This is used in the detection of addresses not in-use. */
|
||||
uint8_t Poll_Count;
|
||||
/* This parameter is random value 1..64, used to choose the poll slot */
|
||||
uint8_t Npoll_slot;
|
||||
/* UUID for storing the unique identifier for this node
|
||||
used to send and validate a unique test request and response */
|
||||
uint8_t UUID[MSTP_UUID_SIZE];
|
||||
/* amount of silence time to wait, in milliseconds */
|
||||
uint32_t Zero_Config_Silence;
|
||||
/* This parameter tracks the highest polled station address.
|
||||
The value of this parameter shall be less than or equal to 127.
|
||||
In the absence of other fixed address nodes, this value shall be 127. */
|
||||
uint8_t Zero_Config_Max_Master;
|
||||
|
||||
/* 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.
|
||||
Tframe_abort = 1 + ((60*1000UL)/RS485_Baud); */
|
||||
uint8_t Tframe_abort;
|
||||
|
||||
/* 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. */
|
||||
uint8_t Treply_delay;
|
||||
|
||||
/* 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.) */
|
||||
uint16_t Treply_timeout;
|
||||
|
||||
/* 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 35 milliseconds.) */
|
||||
uint8_t Tusage_timeout;
|
||||
|
||||
/* The minimum time after the end of the stop bit of the final
|
||||
octet of a received frame before a node may enable its
|
||||
EIA-485 driver: 40 bit times.
|
||||
40 bits is 4 octets including a start and stop bit with each octet.
|
||||
turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */
|
||||
uint8_t Tturnaround_timeout;
|
||||
|
||||
/*Platform-specific port data */
|
||||
void *UserData;
|
||||
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -175,73 +234,67 @@ extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Init(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
void MSTP_Init(struct mstp_port_struct_t *mstp_port);
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Receive_Frame_FSM(
|
||||
volatile struct mstp_port_struct_t
|
||||
*mstp_port);
|
||||
void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port);
|
||||
BACNET_STACK_EXPORT
|
||||
bool MSTP_Master_Node_FSM(
|
||||
volatile struct mstp_port_struct_t
|
||||
*mstp_port);
|
||||
bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port);
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Slave_Node_FSM(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
void MSTP_Slave_Node_FSM(struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
/* returns true if line is active */
|
||||
BACNET_STACK_EXPORT
|
||||
bool MSTP_Line_Active(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
bool MSTP_Line_Active(struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint16_t MSTP_Create_Frame(
|
||||
uint8_t * buffer, /* where frame is loaded */
|
||||
uint16_t 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 */
|
||||
uint16_t data_len); /* number of bytes of data (up to 501) */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
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 */
|
||||
uint16_t MSTP_Create_Frame(uint8_t *buffer,
|
||||
uint16_t buffer_len,
|
||||
uint8_t frame_type,
|
||||
uint8_t destination,
|
||||
uint8_t source,
|
||||
uint8_t *data,
|
||||
uint16_t data_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Fill_BACnet_Address(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t mstp_address);
|
||||
void MSTP_Create_And_Send_Frame(
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t frame_type,
|
||||
uint8_t destination,
|
||||
uint8_t source,
|
||||
uint8_t *data,
|
||||
uint16_t data_len);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Zero_Config_UUID_Init(struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
/* functions used by the MS/TP state machine to put or get data */
|
||||
/* FIXME: developer must implement these in their DLMSTP module */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint16_t MSTP_Put_Receive(
|
||||
volatile struct mstp_port_struct_t *mstp_port);
|
||||
struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
BACNET_STACK_EXPORT
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint16_t MSTP_Get_Send(struct mstp_port_struct_t *mstp_port,
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
/* for the MS/TP state machine to use for getting the reply for
|
||||
Data-Expecting-Reply Frame */
|
||||
/* Return: amount of PDU data */
|
||||
BACNET_STACK_EXPORT
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
uint16_t MSTP_Get_Reply(struct mstp_port_struct_t *mstp_port,
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Send_Frame(
|
||||
volatile struct mstp_port_struct_t *mstp_port,
|
||||
struct mstp_port_struct_t *mstp_port,
|
||||
uint8_t * buffer,
|
||||
uint16_t nbytes);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
/* The value 255 is used to denote broadcast when used as a */
|
||||
/* destination address but is not allowed as a value for a station. */
|
||||
/* Station addresses for master nodes can be 0-127. */
|
||||
/* Station addresses for slave nodes can be 127-254. */
|
||||
/* Station addresses for slave nodes can be 0-254. */
|
||||
#define MSTP_BROADCAST_ADDRESS 255
|
||||
|
||||
/* MS/TP Frame Type */
|
||||
@@ -52,15 +52,23 @@
|
||||
/* These frames are available to vendors as proprietary (non-BACnet) frames. */
|
||||
/* The first two octets of the Data field shall specify the unique vendor */
|
||||
/* identification code, most significant octet first, for the type of */
|
||||
/* vendor-proprietary frame to be conveyed. The length of the data portion */
|
||||
/* of a Proprietary frame shall be in the range of 2 to 501 octets. */
|
||||
/* vendor-proprietary frame to be conveyed. */
|
||||
#define FRAME_TYPE_PROPRIETARY_MIN 128
|
||||
#define FRAME_TYPE_PROPRIETARY_MAX 255
|
||||
/* The initial CRC16 checksum value */
|
||||
#define CRC16_INITIAL_VALUE (0xFFFF)
|
||||
#define CRC32K_INITIAL_VALUE (0xFFFFFFFF)
|
||||
#define CRC32K_RESIDUE (0x0843323B)
|
||||
/* frame specific data */
|
||||
#define MSTP_PREAMBLE_X55 (0x55)
|
||||
/* The length of the data portion of a Test_Request, Test_Response,
|
||||
BACnet Data Expecting Reply, or BACnet Data Not Expecting Reply frame
|
||||
may range from 0 to 501 octets.
|
||||
The length of the data portion of a proprietary frame shall
|
||||
be in the range of 2 to 501 octets.*/
|
||||
#define MSTP_FRAME_NPDU_MAX 501
|
||||
/* COBS-encoded frames data parameter length is between
|
||||
502 and 1497 octets, inclusive */
|
||||
#define MSTP_EXTENDED_FRAME_NPDU_MAX 1497
|
||||
|
||||
/* receive FSM states */
|
||||
@@ -85,6 +93,16 @@ typedef enum {
|
||||
MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8
|
||||
} MSTP_MASTER_STATE;
|
||||
|
||||
/* MSTP zero config FSM states */
|
||||
typedef enum MSTP_Zero_Config_State {
|
||||
MSTP_ZERO_CONFIG_STATE_INIT = 0,
|
||||
MSTP_ZERO_CONFIG_STATE_IDLE = 1,
|
||||
MSTP_ZERO_CONFIG_STATE_LURK = 2,
|
||||
MSTP_ZERO_CONFIG_STATE_CLAIM = 3,
|
||||
MSTP_ZERO_CONFIG_STATE_CONFIRM = 4,
|
||||
MSTP_ZERO_CONFIG_STATE_USE = 5
|
||||
} MSTP_ZERO_CONFIG_STATE;
|
||||
|
||||
/* The time without a DataAvailable or ReceiveError event before declaration */
|
||||
/* of loss of token: 500 milliseconds. */
|
||||
#define Tno_token 500
|
||||
@@ -105,6 +123,22 @@ typedef enum {
|
||||
/* is executed: 50. */
|
||||
#define Npoll 50
|
||||
|
||||
/* The minimum number of polls received before a zero-config address */
|
||||
/* is claimed: 8. */
|
||||
#define Nmin_poll 8
|
||||
|
||||
/* The first zero-config address: 64 */
|
||||
#define Nmin_poll_station 64
|
||||
|
||||
/* The last zero-config address: 127 */
|
||||
#define Nmax_poll_station 127
|
||||
|
||||
/* The number of zero-config station poll slots: 64 */
|
||||
#define Nmax_poll_slot 64
|
||||
|
||||
/* The last master node address: 127 */
|
||||
#define Nmax_master_station 127
|
||||
|
||||
/* The number of retries on sending Token: 1. */
|
||||
#define Nretry_token 1
|
||||
|
||||
@@ -126,6 +160,15 @@ typedef enum {
|
||||
/* 15 milliseconds. */
|
||||
#define Tusage_delay 15
|
||||
|
||||
/* 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
|
||||
|
||||
#define DEFAULT_Tframe_abort 95
|
||||
#define DEFAULT_Treply_delay 245
|
||||
#define DEFAULT_Treply_timeout 250
|
||||
#define DEFAULT_Tusage_timeout 35
|
||||
|
||||
#define DEFAULT_MAX_INFO_FRAMES 1
|
||||
#define DEFAULT_MAX_MASTER 127
|
||||
#define DEFAULT_MAC_ADDRESS 127
|
||||
@@ -134,7 +177,6 @@ typedef enum {
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -87,3 +87,18 @@ const char *mstptext_frame_type(unsigned index)
|
||||
return indtext_by_index_split_default(mstp_frame_type_text, index,
|
||||
FRAME_TYPE_PROPRIETARY_MIN, "UNKNOWN", "PROPRIETARY");
|
||||
}
|
||||
|
||||
static INDTEXT_DATA mstp_zero_config_state_text[] = {
|
||||
{ MSTP_ZERO_CONFIG_STATE_INIT, "INIT" },
|
||||
{ MSTP_ZERO_CONFIG_STATE_IDLE, "IDLE" },
|
||||
{ MSTP_ZERO_CONFIG_STATE_LURK, "LURK" },
|
||||
{ MSTP_ZERO_CONFIG_STATE_CLAIM, "CLAIM" },
|
||||
{ MSTP_ZERO_CONFIG_STATE_CONFIRM, "CONFIRM" },
|
||||
{ MSTP_ZERO_CONFIG_STATE_USE, "USE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *mstptext_zero_config_state(unsigned index)
|
||||
{
|
||||
return indtext_by_index_default(mstp_zero_config_state_text, index, "unknown");
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ extern "C" {
|
||||
BACNET_STACK_EXPORT
|
||||
const char *mstptext_frame_type(
|
||||
unsigned index);
|
||||
BACNET_STACK_EXPORT
|
||||
const char *mstptext_zero_config_state(
|
||||
unsigned index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ list(APPEND testdirs
|
||||
bacnet/datalink/cobs
|
||||
bacnet/datalink/crc
|
||||
bacnet/datalink/bvlc
|
||||
bacnet/datalink/mstp
|
||||
)
|
||||
|
||||
enable_testing()
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
|
||||
|
||||
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(test_${basename}
|
||||
VERSION 1.0.0
|
||||
LANGUAGES C)
|
||||
|
||||
|
||||
string(REGEX REPLACE
|
||||
"/test/bacnet/[a-zA-Z_/-]*$"
|
||||
"/src"
|
||||
SRC_DIR
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
string(REGEX REPLACE
|
||||
"/test/bacnet/[a-zA-Z_/-]*$"
|
||||
"/test"
|
||||
TST_DIR
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(ZTST_DIR "${TST_DIR}/ztest/src")
|
||||
|
||||
add_compile_definitions(
|
||||
BIG_ENDIAN=0
|
||||
CONFIG_ZTEST=1
|
||||
BACDL_MSTP=1
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${SRC_DIR}
|
||||
${TST_DIR}/ztest/include
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
# File(s) under test
|
||||
${SRC_DIR}/bacnet/datalink/mstp.c
|
||||
${SRC_DIR}/bacnet/datalink/mstptext.c
|
||||
${SRC_DIR}/bacnet/datalink/crc.c
|
||||
${SRC_DIR}/bacnet/datalink/cobs.c
|
||||
${SRC_DIR}/bacnet/basic/sys/fifo.c
|
||||
${SRC_DIR}/bacnet/indtext.c
|
||||
# core files needed
|
||||
${SRC_DIR}/bacnet/bacdcode.c
|
||||
${SRC_DIR}/bacnet/bacint.c
|
||||
${SRC_DIR}/bacnet/bacstr.c
|
||||
${SRC_DIR}/bacnet/bacreal.c
|
||||
${SRC_DIR}/bacnet/npdu.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
# Support files and stubs (pathname alphabetical)
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
${ZTST_DIR}/ztest.c
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user