diff --git a/ports/stm32f10x/main.c b/ports/stm32f10x/main.c index 6d0e5445..96aa9fb3 100644 --- a/ports/stm32f10x/main.c +++ b/ports/stm32f10x/main.c @@ -129,6 +129,7 @@ static void mstp_configure(void) /* user data */ MSTP_Port.ZeroConfigEnabled = true; MSTP_Port.SlaveNodeEnabled = false; + MSTP_Port.CheckAutoBaud = false; MSTP_Zero_Config_UUID_Init(&MSTP_Port); MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_Port.UserData = &MSTP_User_Data; diff --git a/ports/stm32f4xx/main.c b/ports/stm32f4xx/main.c index 14e670af..2a670196 100644 --- a/ports/stm32f4xx/main.c +++ b/ports/stm32f4xx/main.c @@ -102,6 +102,7 @@ int main(void) MSTP_Port.ZeroConfigEnabled = true; MSTP_Port.Zero_Config_Preferred_Station = 0; MSTP_Port.SlaveNodeEnabled = false; + MSTP_Port.CheckAutoBaud = false; /* user data */ MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_Port.UserData = &MSTP_User_Data; @@ -113,8 +114,10 @@ int main(void) /* 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); + if (!MSTP_Port.CheckAutoBaud) { + /* FIXME: get the baud rate from hardware DIP or from EEPROM */ + dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT); + } /* initialize application layer*/ bacnet_init(); for (;;) { diff --git a/ports/xplained/main.c b/ports/xplained/main.c index 2cdf260a..fe94862d 100644 --- a/ports/xplained/main.c +++ b/ports/xplained/main.c @@ -91,6 +91,11 @@ static void dlmstp_configure(void) } else { MSTP_Port.SlaveNodeEnabled = true; } + MSTP_Port.CheckAutoBaud = false; + if (!MSTP_Port.CheckAutoBaud) { + /* FIXME: get the baud rate from hardware DIP or from EEPROM */ + dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT); + } MSTP_Zero_Config_UUID_Init(&MSTP_Port); MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_Port.UserData = &MSTP_User_Data; diff --git a/src/bacnet/datalink/dlmstp.c b/src/bacnet/datalink/dlmstp.c index 9a5b58e9..0fa00106 100644 --- a/src/bacnet/datalink/dlmstp.c +++ b/src/bacnet/datalink/dlmstp.c @@ -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( diff --git a/src/bacnet/datalink/dlmstp.h b/src/bacnet/datalink/dlmstp.h index 2cf1c744..9459b8d2 100644 --- a/src/bacnet/datalink/dlmstp.h +++ b/src/bacnet/datalink/dlmstp.h @@ -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 */ diff --git a/src/bacnet/datalink/mstp.c b/src/bacnet/datalink/mstp.c index 42274f5e..c9dbdc5d 100644 --- a/src/bacnet/datalink/mstp.c +++ b/src/bacnet/datalink/mstp.c @@ -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) */ diff --git a/src/bacnet/datalink/mstp.h b/src/bacnet/datalink/mstp.h index 8bec38c0..9aeb812f 100644 --- a/src/bacnet/datalink/mstp.h +++ b/src/bacnet/datalink/mstp.h @@ -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 */ diff --git a/src/bacnet/datalink/mstpdef.h b/src/bacnet/datalink/mstpdef.h index d07e402f..e773328e 100644 --- a/src/bacnet/datalink/mstpdef.h +++ b/src/bacnet/datalink/mstpdef.h @@ -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 diff --git a/test/bacnet/basic/sys/test/mstimer_mock.c b/test/bacnet/basic/sys/test/mstimer_mock.c new file mode 100644 index 00000000..228506f8 --- /dev/null +++ b/test/bacnet/basic/sys/test/mstimer_mock.c @@ -0,0 +1,20 @@ +/** + * @file + * @brief Stub functions for unit test of a BACnet object + * @author Steve Karg + * @date January 2025 + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include "bacnet/basic/sys/mstimer.h" + +/** + * @brief Get the current time in milliseconds + * @return milliseconds + */ +unsigned long mstimer_now(void) +{ + return ztest_get_return_value(); +} diff --git a/test/bacnet/datalink/dlmstp/CMakeLists.txt b/test/bacnet/datalink/dlmstp/CMakeLists.txt index 5c196455..90e2a62e 100644 --- a/test/bacnet/datalink/dlmstp/CMakeLists.txt +++ b/test/bacnet/datalink/dlmstp/CMakeLists.txt @@ -47,6 +47,7 @@ add_executable(${PROJECT_NAME} ./src/main.c ${TST_DIR}/bacnet/datalink/test/mstp-mock.c ${TST_DIR}/bacnet/datalink/test/mstp-rs485.c + ${TST_DIR}/bacnet/basic/sys/test/mstimer_mock.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/datalink/mstp/src/main.c b/test/bacnet/datalink/mstp/src/main.c index db9398e2..bbf3d9ed 100644 --- a/test/bacnet/datalink/mstp/src/main.c +++ b/test/bacnet/datalink/mstp/src/main.c @@ -149,6 +149,52 @@ static void Timer_Silence_Reset(void *pArg) SilenceTime = 0; } +/* track the last good header time in milliseconds */ +uint32_t Good_Header_Time = 0; +/** + * @brief MS/TP state machine calls this to get the good header time + * @param pArg pointer to the port specific context data + * @return amount of time in milliseconds + */ +static uint32_t Good_Header_Timer(void *pArg) +{ + (void)pArg; + return Good_Header_Time; +} + +/** + * @brief MS/TP state machine calls this to reset the good header time + * @param pArg pointer to the port specific context data + */ +void Good_Header_Timer_Reset(void *pArg) +{ + (void)pArg; + Good_Header_Time = 0; +} + +/* track the internal baud rate */ +static uint32_t Baud_Rate_Internal = 0; +/** + * @brief Get the current baud rate + * @param pArg pointer to the port specific context data + * @return The current baud rate + */ +uint32_t Baud_Rate(void) +{ + return Baud_Rate_Internal; +} + +/** + * @brief Set the current baud rate + * @param pArg pointer to the port specific context data + * @param baud the new baud rate + * @return true if the baud rate was set + */ +void Baud_Rate_Set(uint32_t baud) +{ + Baud_Rate_Internal = baud; +} + /** * @brief MS/TP state machine calls this to send a frame * @param mstp_port port specific context data @@ -648,6 +694,9 @@ static void testZeroConfigNode_Init(struct mstp_port_struct_t *mstp_port) mstp_port->SilenceTimer = Timer_Silence; mstp_port->SilenceTimerReset = Timer_Silence_Reset; + mstp_port->CheckAutoBaud = false; + mstp_port->SlaveNodeEnabled = false; + /* configure for Zero Config */ mstp_port->ZeroConfigEnabled = true; mstp_port->This_Station = 255; @@ -1126,6 +1175,161 @@ static void testZeroConfigNodeFSM(void) next_station); } +static void testAutoBaudNode_Init(struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + + mstp_port->InputBuffer = &RxBuffer[0]; + mstp_port->InputBufferSize = sizeof(RxBuffer); + mstp_port->OutputBuffer = &TxBuffer[0]; + mstp_port->OutputBufferSize = sizeof(TxBuffer); + + mstp_port->Nmax_info_frames = 1; + mstp_port->Nmax_master = 127; + + 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; + + mstp_port->SilenceTimer = Timer_Silence; + mstp_port->SilenceTimerReset = Timer_Silence_Reset; + + mstp_port->CheckAutoBaud = true; + mstp_port->SlaveNodeEnabled = false; + mstp_port->ZeroConfigEnabled = false; + mstp_port->This_Station = 255; + + mstp_port->Auto_Baud_State = MSTP_AUTO_BAUD_STATE_INIT; + mstp_port->BaudRate = Baud_Rate; + mstp_port->BaudRateSet = Baud_Rate_Set; + mstp_port->ValidFrameTimer = Good_Header_Timer; + mstp_port->ValidFrameTimerReset = Good_Header_Timer_Reset; + + MSTP_Init(mstp_port); + zassert_true(mstp_port->master_state == MSTP_MASTER_STATE_INITIALIZE, NULL); + zassert_true(mstp_port->Auto_Baud_State == MSTP_AUTO_BAUD_STATE_INIT, NULL); + zassert_true(mstp_port->Tframe_abort == DEFAULT_Tframe_abort, NULL); + zassert_true(mstp_port->Treply_delay == DEFAULT_Treply_delay, NULL); + zassert_true(mstp_port->Treply_timeout == DEFAULT_Treply_timeout, NULL); + zassert_true(mstp_port->Tusage_timeout == DEFAULT_Tusage_timeout, NULL); + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->Auto_Baud_State == MSTP_AUTO_BAUD_STATE_IDLE, NULL); + zassert_true(mstp_port->ValidFrames == 0, NULL); + zassert_true(mstp_port->BaudRateIndex == 0, NULL); + zassert_true(mstp_port->BaudRate() != 0, NULL); + zassert_true(mstp_port->ValidFrameTimer(NULL) == 0, NULL); +} + +static void +testAutoBaudNode_Idle_ValidFrame(struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + + if (!mstp_port->CheckAutoBaud) { + return; + } + Good_Header_Time = 0; + SilenceTime = 0; + mstp_port->SourceAddress = 0; + mstp_port->DestinationAddress = 1; + mstp_port->ReceivedValidFrame = true; + mstp_port->ReceivedInvalidFrame = false; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_true(mstp_port->ReceivedInvalidFrame == false, NULL); + zassert_true(mstp_port->BaudRateIndex == 0, NULL); + zassert_true(mstp_port->ValidFrames > 0, NULL); + if (mstp_port->ValidFrames >= 4) { + zassert_true(mstp_port->CheckAutoBaud == 0, NULL); + zassert_true( + mstp_port->Auto_Baud_State == MSTP_AUTO_BAUD_STATE_USE, NULL); + } else { + zassert_true( + mstp_port->Auto_Baud_State == MSTP_AUTO_BAUD_STATE_IDLE, NULL); + } +} + +static void +testAutoBaudNode_Idle_InvalidFrame(struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + + if (!mstp_port->CheckAutoBaud) { + return; + } + Good_Header_Time = 0; + SilenceTime = 0; + mstp_port->SourceAddress = 0; + mstp_port->DestinationAddress = 1; + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_true(mstp_port->ReceivedInvalidFrame == false, NULL); + zassert_true(mstp_port->BaudRateIndex == 0, NULL); + zassert_true(mstp_port->ValidFrames == 0, NULL); + zassert_true(mstp_port->CheckAutoBaud, NULL); + zassert_true(mstp_port->Auto_Baud_State == MSTP_AUTO_BAUD_STATE_IDLE, NULL); +} + +static void testAutoBaudNode_Idle_Timeout(struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + uint32_t baud; + + if (!mstp_port->CheckAutoBaud) { + return; + } + Good_Header_Time = 5000UL; + zassert_true(mstp_port->ValidFrameTimer(NULL) == 5000UL, NULL); + SilenceTime = 0; + mstp_port->SourceAddress = 0; + mstp_port->DestinationAddress = 1; + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = false; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_true(mstp_port->ReceivedInvalidFrame == false, NULL); + baud = MSTP_Auto_Baud_Rate(mstp_port->BaudRateIndex); + zassert_true(mstp_port->BaudRate() == baud, NULL); + zassert_true(mstp_port->ValidFrames == 0, NULL); + zassert_true(mstp_port->CheckAutoBaud, NULL); + zassert_true(mstp_port->Auto_Baud_State == MSTP_AUTO_BAUD_STATE_IDLE, NULL); + zassert_true(mstp_port->ValidFrameTimer(NULL) == 0, NULL); +} + +static void testAutoBaudNodeFSM(void) +{ + struct mstp_port_struct_t MSTP_Port = { 0 }; /* port data */ + + /* test case: got at least valid frames, use the baud rate */ + testAutoBaudNode_Init(&MSTP_Port); + testAutoBaudNode_Idle_ValidFrame(&MSTP_Port); + testAutoBaudNode_Idle_ValidFrame(&MSTP_Port); + testAutoBaudNode_Idle_ValidFrame(&MSTP_Port); + testAutoBaudNode_Idle_ValidFrame(&MSTP_Port); + testAutoBaudNode_Idle_ValidFrame(&MSTP_Port); + testAutoBaudNode_Idle_ValidFrame(&MSTP_Port); + + /* test case: got an invalid frame */ + testAutoBaudNode_Init(&MSTP_Port); + testAutoBaudNode_Idle_InvalidFrame(&MSTP_Port); + testAutoBaudNode_Idle_InvalidFrame(&MSTP_Port); + + /* test case: timeout */ + testAutoBaudNode_Init(&MSTP_Port); + testAutoBaudNode_Idle_Timeout(&MSTP_Port); + testAutoBaudNode_Idle_Timeout(&MSTP_Port); + testAutoBaudNode_Idle_Timeout(&MSTP_Port); + testAutoBaudNode_Idle_Timeout(&MSTP_Port); + testAutoBaudNode_Idle_Timeout(&MSTP_Port); +} + /** * @} */ @@ -1135,7 +1339,8 @@ void test_main(void) ztest_test_suite( crc_tests, ztest_unit_test(testReceiveNodeFSM), ztest_unit_test(testMasterNodeFSM), ztest_unit_test(testSlaveNodeFSM), - ztest_unit_test(testZeroConfigNodeFSM)); + ztest_unit_test(testZeroConfigNodeFSM), + ztest_unit_test(testAutoBaudNodeFSM)); ztest_run_test_suite(crc_tests); } diff --git a/test/bacnet/datalink/test/mstp-mock.c b/test/bacnet/datalink/test/mstp-mock.c index 0fe6745e..ebe67c53 100644 --- a/test/bacnet/datalink/test/mstp-mock.c +++ b/test/bacnet/datalink/test/mstp-mock.c @@ -91,3 +91,8 @@ void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port) { ztest_check_expected_value(mstp_port); } + +void MSTP_Auto_Baud_FSM(struct mstp_port_struct_t *mstp_port) +{ + ztest_check_expected_value(mstp_port); +}