Added MS/TP automatic baudrate detection option into the core MS/TP state machine. (#900)

This commit is contained in:
Steve Karg
2025-02-03 15:10:31 -06:00
committed by GitHub
parent c5b129e9ab
commit 19ef7f74cd
12 changed files with 471 additions and 11 deletions
+78 -1
View File
@@ -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(
+10
View File
@@ -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
View File
@@ -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) */
+24
View File
@@ -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 */
+7
View File
@@ -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