Added MS/TP automatic baudrate detection option into the core MS/TP state machine. (#900)
This commit is contained in:
@@ -417,7 +417,7 @@ uint16_t dlmstp_receive(
|
||||
MSTP_Slave_Node_FSM(MSTP_Port);
|
||||
} else if (
|
||||
(MSTP_Port->This_Station <= DEFAULT_MAX_MASTER) ||
|
||||
MSTP_Port->ZeroConfigEnabled) {
|
||||
MSTP_Port->ZeroConfigEnabled || MSTP_Port->CheckAutoBaud) {
|
||||
while (MSTP_Master_Node_FSM(MSTP_Port)) {
|
||||
/* do nothing while some states fast transition */
|
||||
};
|
||||
@@ -700,6 +700,39 @@ bool dlmstp_zero_config_enabled_set(bool flag)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the MSTP port AutoBaudEnabled status
|
||||
* @return true if the MSTP port has AutoBaudEnabled
|
||||
*/
|
||||
bool dlmstp_check_auto_baud(void)
|
||||
{
|
||||
if (!MSTP_Port) {
|
||||
return false;
|
||||
}
|
||||
return MSTP_Port->CheckAutoBaud;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the MSTP port AutoBaudEnabled flag
|
||||
* @param flag - true if the MSTP port has AutoBaudEnabled
|
||||
* @return true if the MSTP port AutoBaudEnabled was set
|
||||
* @note This flag is used to enable the Zero Configuration state machine
|
||||
* for the MSTP port. The Zero Configuration state machine is used to
|
||||
* automatically assign a MAC address to the MSTP port.
|
||||
*/
|
||||
bool dlmstp_check_auto_baud_set(bool flag)
|
||||
{
|
||||
if (!MSTP_Port) {
|
||||
return false;
|
||||
}
|
||||
MSTP_Port->CheckAutoBaud = flag;
|
||||
if (flag) {
|
||||
MSTP_Port->Auto_Baud_State = MSTP_AUTO_BAUD_STATE_INIT;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the MSTP port MAC address that this node prefers to use.
|
||||
* @return ZeroConfigStation value, or an out-of-range value if invalid
|
||||
@@ -961,6 +994,46 @@ uint32_t dlmstp_silence_milliseconds(void *arg)
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the valid frame time in milliseconds
|
||||
* @param arg - pointer to MSTP port structure
|
||||
* @return valid frame time in milliseconds
|
||||
*/
|
||||
uint32_t dlmstp_valid_frame_milliseconds(void *arg)
|
||||
{
|
||||
uint32_t milliseconds = 0, now = 0;
|
||||
struct mstp_port_struct_t *port = arg;
|
||||
struct dlmstp_user_data_t *user = NULL;
|
||||
|
||||
if (port) {
|
||||
user = port->UserData;
|
||||
}
|
||||
if (user) {
|
||||
now = mstimer_now();
|
||||
milliseconds = now - user->Valid_Frame_Milliseconds;
|
||||
}
|
||||
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the valid frame timer
|
||||
* @param arg - pointer to MSTP port structure
|
||||
* @return valid frame time in milliseconds
|
||||
*/
|
||||
void dlmstp_valid_frame_milliseconds_reset(void *arg)
|
||||
{
|
||||
struct mstp_port_struct_t *port = arg;
|
||||
struct dlmstp_user_data_t *user = NULL;
|
||||
|
||||
if (port) {
|
||||
user = port->UserData;
|
||||
}
|
||||
if (user) {
|
||||
user->Valid_Frame_Milliseconds = mstimer_now();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the RS-485 silence time to zero
|
||||
* @param arg - pointer to MSTP port structure
|
||||
@@ -994,6 +1067,10 @@ bool dlmstp_init(char *ifname)
|
||||
if (MSTP_Port) {
|
||||
MSTP_Port->SilenceTimer = dlmstp_silence_milliseconds;
|
||||
MSTP_Port->SilenceTimerReset = dlmstp_silence_reset;
|
||||
MSTP_Port->ValidFrameTimer = dlmstp_valid_frame_milliseconds;
|
||||
MSTP_Port->ValidFrameTimerReset = dlmstp_valid_frame_milliseconds_reset;
|
||||
MSTP_Port->BaudRate = dlmstp_baud_rate;
|
||||
MSTP_Port->BaudRateSet = dlmstp_set_baud_rate;
|
||||
user = (struct dlmstp_user_data_t *)MSTP_Port->UserData;
|
||||
if (user && !user->Initialized) {
|
||||
Ringbuf_Initialize(
|
||||
|
||||
@@ -102,6 +102,7 @@ struct dlmstp_user_data_t {
|
||||
dlmstp_hook_frame_rx_start_cb Preamble_Callback;
|
||||
dlmstp_hook_frame_rx_complete_cb Valid_Frame_Rx_Callback;
|
||||
dlmstp_hook_frame_rx_complete_cb Invalid_Frame_Rx_Callback;
|
||||
uint32_t Valid_Frame_Milliseconds;
|
||||
/* the PDU Queue is made of Nmax_info_frames x dlmstp_packet's */
|
||||
RING_BUFFER PDU_Queue;
|
||||
struct dlmstp_packet PDU_Buffer[DLMSTP_MAX_INFO_FRAMES];
|
||||
@@ -192,6 +193,10 @@ bool dlmstp_zero_config_enabled(void);
|
||||
BACNET_STACK_EXPORT
|
||||
bool dlmstp_zero_config_enabled_set(bool flag);
|
||||
BACNET_STACK_EXPORT
|
||||
bool dlmstp_check_auto_baud(void);
|
||||
BACNET_STACK_EXPORT
|
||||
bool dlmstp_check_auto_baud_set(bool flag);
|
||||
BACNET_STACK_EXPORT
|
||||
uint8_t dlmstp_zero_config_preferred_station(void);
|
||||
BACNET_STACK_EXPORT
|
||||
bool dlmstp_zero_config_preferred_station_set(uint8_t station);
|
||||
@@ -211,6 +216,11 @@ uint32_t dlmstp_silence_milliseconds(void *arg);
|
||||
BACNET_STACK_EXPORT
|
||||
void dlmstp_silence_reset(void *arg);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t dlmstp_valid_frame_milliseconds(void *arg);
|
||||
BACNET_STACK_EXPORT
|
||||
void dlmstp_valid_frame_milliseconds_reset(void *arg);
|
||||
|
||||
/* Set the callback function to be called on every valid received frame */
|
||||
/* This is not necessary for normal usage, but is helpful if the caller */
|
||||
/* needs to monitor traffic on the MS/TP bus */
|
||||
|
||||
+109
-7
@@ -648,7 +648,9 @@ bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port)
|
||||
}
|
||||
switch (mstp_port->master_state) {
|
||||
case MSTP_MASTER_STATE_INITIALIZE:
|
||||
if (mstp_port->ZeroConfigEnabled) {
|
||||
if (mstp_port->CheckAutoBaud) {
|
||||
MSTP_Auto_Baud_FSM(mstp_port);
|
||||
} else if (mstp_port->ZeroConfigEnabled) {
|
||||
MSTP_Zero_Config_FSM(mstp_port);
|
||||
if (mstp_port->This_Station != 255) {
|
||||
/* indicate that the next station is unknown */
|
||||
@@ -1430,8 +1432,8 @@ static void MSTP_Zero_Config_State_Idle(struct mstp_port_struct_t *mstp_port)
|
||||
return;
|
||||
}
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
/* IdleValidFrame */
|
||||
/* 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) {
|
||||
@@ -1482,7 +1484,7 @@ static void MSTP_Zero_Config_State_Lurk(struct mstp_port_struct_t *mstp_port)
|
||||
}
|
||||
}
|
||||
if (src == mstp_port->Zero_Config_Station) {
|
||||
/* AddressInUse */
|
||||
/* LurkAddressInUse */
|
||||
/* monitor PFM from the next address */
|
||||
mstp_port->Zero_Config_Station = MSTP_Zero_Config_Station_Increment(
|
||||
mstp_port->Zero_Config_Station);
|
||||
@@ -1493,23 +1495,23 @@ static void MSTP_Zero_Config_State_Lurk(struct mstp_port_struct_t *mstp_port)
|
||||
/* calculate this node poll count priority number */
|
||||
count = Nmin_poll + mstp_port->Npoll_slot;
|
||||
if (mstp_port->Poll_Count == count) {
|
||||
/* PollResponse */
|
||||
/* LurkPollResponse */
|
||||
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 {
|
||||
/* CountFrame */
|
||||
/* LurkCountFrame */
|
||||
mstp_port->Poll_Count++;
|
||||
}
|
||||
}
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
/* InvalidFrame */
|
||||
/* LurkInvalidFrame */
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
} else if (mstp_port->Zero_Config_Silence > 0) {
|
||||
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
||||
mstp_port->Zero_Config_Silence) {
|
||||
/* LurkingTimeout */
|
||||
/* LurkTimeout */
|
||||
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
@@ -1660,6 +1662,106 @@ void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the baud rate for auto-baud at a given index
|
||||
* @param baud_rate_index the index of the baud rate
|
||||
* @return the baud rate at the index
|
||||
* @note A modulo operation keeps the index within the bounds of the array of
|
||||
* baud rates.
|
||||
*/
|
||||
uint32_t MSTP_Auto_Baud_Rate(unsigned baud_rate_index)
|
||||
{
|
||||
const uint32_t TestBaudrates[6] = {
|
||||
115200, 76800, 57600, 38400, 19200, 9600
|
||||
};
|
||||
unsigned index;
|
||||
|
||||
index = baud_rate_index % ARRAY_SIZE(TestBaudrates);
|
||||
|
||||
return TestBaudrates[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The MSTP_AUTO_BAUD_STATE_INIT state is entered when
|
||||
* CheckAutoBaud is TRUE
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
static void MSTP_Auto_Baud_State_Init(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint32_t baud;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
mstp_port->ValidFrames = 0;
|
||||
mstp_port->BaudRateIndex = 0;
|
||||
mstp_port->ValidFrameTimerReset((void *)mstp_port);
|
||||
baud = MSTP_Auto_Baud_Rate(mstp_port->BaudRateIndex);
|
||||
mstp_port->BaudRateSet(baud);
|
||||
mstp_port->Auto_Baud_State = MSTP_AUTO_BAUD_STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The MSTP_AUTO_BAUD_STATE_IDLE state is entered when
|
||||
* CheckAutoBaud is TRUE and waits for good frames or timeout
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
static void MSTP_Auto_Baud_State_Idle(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint32_t baud;
|
||||
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
/* IdleValidFrame */
|
||||
mstp_port->ValidFrames++;
|
||||
if (mstp_port->ValidFrames >= 4) {
|
||||
/* GoodBaudRate */
|
||||
mstp_port->CheckAutoBaud = false;
|
||||
mstp_port->Auto_Baud_State = MSTP_AUTO_BAUD_STATE_USE;
|
||||
}
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
/* IdleInvalidFrame */
|
||||
mstp_port->ValidFrames = 0;
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
} else if (mstp_port->ValidFrameTimer((void *)mstp_port) >= 5000UL) {
|
||||
/* IdleTimeout */
|
||||
mstp_port->BaudRateIndex++;
|
||||
baud = MSTP_Auto_Baud_Rate(mstp_port->BaudRateIndex);
|
||||
mstp_port->BaudRateSet(baud);
|
||||
mstp_port->ValidFrames = 0;
|
||||
mstp_port->ValidFrameTimerReset((void *)mstp_port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finite State Machine for the Auto Baud Rate process
|
||||
* @param mstp_port the context of the MSTP port
|
||||
*/
|
||||
void MSTP_Auto_Baud_FSM(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (!mstp_port) {
|
||||
return;
|
||||
}
|
||||
if (!mstp_port->CheckAutoBaud) {
|
||||
return;
|
||||
}
|
||||
switch (mstp_port->Auto_Baud_State) {
|
||||
case MSTP_AUTO_BAUD_STATE_INIT:
|
||||
MSTP_Auto_Baud_State_Init(mstp_port);
|
||||
break;
|
||||
case MSTP_AUTO_BAUD_STATE_IDLE:
|
||||
MSTP_Auto_Baud_State_Idle(mstp_port);
|
||||
break;
|
||||
case MSTP_AUTO_BAUD_STATE_USE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* note: This_Station assumed to be set with the MAC address */
|
||||
/* note: Nmax_info_frames assumed to be set (default=1) */
|
||||
/* note: Nmax_master assumed to be set (default=127) */
|
||||
|
||||
@@ -213,6 +213,24 @@ struct mstp_port_struct_t {
|
||||
turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */
|
||||
uint8_t Tturnaround_timeout;
|
||||
|
||||
/* orderly transition tracking for auto-baud node startup */
|
||||
MSTP_AUTO_BAUD_STATE Auto_Baud_State;
|
||||
/* A Boolean flag set to TRUE if this node is checking frames for
|
||||
automatic baud rate detection */
|
||||
unsigned CheckAutoBaud : 1;
|
||||
/* The number of elapsed milliseconds since the last received valid frame */
|
||||
uint32_t (*ValidFrameTimer)(void *pArg);
|
||||
void (*ValidFrameTimerReset)(void *pArg);
|
||||
/* The number of header frames received with good CRC since
|
||||
initialization at the current trial baudrate. */
|
||||
uint8_t ValidFrames;
|
||||
/** Get the current baud rate */
|
||||
uint32_t (*BaudRate)(void);
|
||||
/** Set the current baud rate */
|
||||
void (*BaudRateSet)(uint32_t baud);
|
||||
/* The zero-based index in TestBaudrates of the next baudrate to try. */
|
||||
unsigned BaudRateIndex;
|
||||
|
||||
/*Platform-specific port data */
|
||||
void *UserData;
|
||||
};
|
||||
@@ -265,6 +283,12 @@ unsigned MSTP_Zero_Config_Station_Increment(unsigned station);
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t MSTP_Auto_Baud_Rate(unsigned baud_rate_index);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void MSTP_Auto_Baud_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 */
|
||||
|
||||
|
||||
@@ -90,6 +90,13 @@ typedef enum MSTP_Zero_Config_State {
|
||||
MSTP_ZERO_CONFIG_STATE_USE = 5
|
||||
} MSTP_ZERO_CONFIG_STATE;
|
||||
|
||||
/* MSTP auto-baud FSM states */
|
||||
typedef enum MSTP_Auto_Baud_State {
|
||||
MSTP_AUTO_BAUD_STATE_INIT = 0,
|
||||
MSTP_AUTO_BAUD_STATE_IDLE = 1,
|
||||
MSTP_AUTO_BAUD_STATE_USE = 2
|
||||
} MSTP_AUTO_BAUD_STATE;
|
||||
|
||||
/* The time without a DataAvailable or ReceiveError event before declaration */
|
||||
/* of loss of token: 500 milliseconds. */
|
||||
#define Tno_token 500
|
||||
|
||||
Reference in New Issue
Block a user