diff --git a/ports/bdk-atxx4-mstp/external/bootloader/main.c b/ports/bdk-atxx4-mstp/external/bootloader/main.c index ea1d311c..6db7238b 100644 --- a/ports/bdk-atxx4-mstp/external/bootloader/main.c +++ b/ports/bdk-atxx4-mstp/external/bootloader/main.c @@ -36,7 +36,7 @@ /*#define REMOVE_FLASH_BYTE_SUPPORT */ /* - * GCC doesn't optimize long int arithmetics very clever. As the + * GCC doesn't optimize long int arithmetic very cleverly. As the * address only needs to be larger than 16 bits for the ATmega128 and * above (where flash consumptions isn't much of an issue as the * entire boot loader will still fit even into the smallest possible diff --git a/ports/stm32f4xx/.clang-format b/ports/stm32f4xx/.clang-format index 579a192a..57317e25 100644 --- a/ports/stm32f4xx/.clang-format +++ b/ports/stm32f4xx/.clang-format @@ -1,7 +1,27 @@ --- -# Disable formatting for now as there is external code. We should move external -# code to a separate directory and enable formatting for our code. +BasedOnStyle: WebKit +AlignAfterOpenBracket: AlwaysBreak +BinPackArguments: true +BinPackParameters: false +PenaltyBreakBeforeFirstCallParameter: 1 +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlignEscapedNewlines: Left +AlignArrayOfStructures: None +PointerAlignment: Right +InsertBraces: true +BreakBeforeBraces: Linux +BreakBeforeBinaryOperators: None +KeepEmptyLinesAtTheStartOfBlocks: false +IndentCaseLabels: true +IndentWidth: 4 +UseTab: Never +SortIncludes: false +ColumnLimit: 80 +# Const always to left side. +QualifierAlignment: Left +--- +Language: JavaScript DisableFormat: true - -# DisableFormat will not disable include sorting with some versions. -SortIncludes: Never +... diff --git a/ports/stm32f4xx/.vscode/launch.json b/ports/stm32f4xx/.vscode/launch.json index 864ea158..df89d38a 100644 --- a/ports/stm32f4xx/.vscode/launch.json +++ b/ports/stm32f4xx/.vscode/launch.json @@ -1,49 +1,53 @@ { - "version": "0.2.0", - "configurations": [ - { - "type": "cortex-debug", - "request": "launch", - "name": "Debug with OpenOCD", - "cwd": "${workspaceRoot}", - "executable": "${workspaceRoot}/build/bacnet-mstp.elf", - "servertype": "openocd", - "device": "STM32F429ZI", - "serialNumber": "", - "configFiles": [ - "interface/stlink.cfg", - "target/stm32f4x.cfg" - ], - "searchDir": [], - "svdFile":"${workspaceRoot}/stm32f429.svd", - "runToEntryPoint": "main", - "liveWatch": { - "enabled": true, - "samplesPerSecond": 1 - }, - "swoConfig": { - "enabled": true, - "cpuFrequency": 8000000, - "swoFrequency": 2000000, - "source": "probe", - "decoders": [ - { "type": "console", "label": "ITM", "port": 0 } - ] - }, - "preLaunchCommands": [ - // guarantee the halt at soon as possible after reset - "monitor reset", - "monitor sleep 2000", - "monitor reset halt", - // synchronize GDB to the state of the target after reset - "monitor gdb_sync", - "stepi" - ], - "postLaunchCommands": [ - "monitor reset init", - "monitor sleep 200" - ], - "showDevDebugOutput": "raw" - } - ] + "version": "0.2.0", + "configurations": [ + { + "type": "cortex-debug", + "request": "launch", + "name": "Debug with OpenOCD", + "cwd": "${workspaceRoot}", + "executable": "${workspaceRoot}/build/bacnet-mstp.elf", + "servertype": "openocd", + "device": "STM32F429ZI", + "serialNumber": "", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "searchDir": [], + "svdFile": "${workspaceRoot}/stm32f429.svd", + "runToEntryPoint": "main", + "liveWatch": { + "enabled": true, + "samplesPerSecond": 1 + }, + "swoConfig": { + "enabled": true, + "cpuFrequency": 8000000, + "swoFrequency": 2000000, + "source": "probe", + "decoders": [ + { + "type": "console", + "label": "ITM", + "port": 0 + } + ] + }, + "preLaunchCommands": [ + // guarantee the halt at soon as possible after reset + "monitor reset", + "monitor sleep 2000", + "monitor reset halt", + // synchronize GDB to the state of the target after reset + "monitor gdb_sync", + "stepi" + ], + "postLaunchCommands": [ + "monitor reset init", + "monitor sleep 200" + ], + "showDevDebugOutput": "raw" + } + ] } diff --git a/ports/stm32f4xx/CMakeLists.txt b/ports/stm32f4xx/CMakeLists.txt index 3e5aca44..0b76804a 100644 --- a/ports/stm32f4xx/CMakeLists.txt +++ b/ports/stm32f4xx/CMakeLists.txt @@ -151,6 +151,9 @@ set(BACNET_PROJECT_SOURCE ${CMAKE_SOURCE_DIR}/mstimer-init.c ${CMAKE_SOURCE_DIR}/rs485.c + ${CMAKE_SOURCE_DIR}/program-ubasic.c + ${CMAKE_SOURCE_DIR}/ubasic-port.c + ${CMAKE_SOURCE_DIR}/device.c ${CMAKE_SOURCE_DIR}/netport.c @@ -163,6 +166,9 @@ set(BACNET_PROJECT_SOURCE ${LIBRARY_BACNET_BASIC}/object/ms-input.c ${LIBRARY_BACNET_BASIC}/object/mso.c ${LIBRARY_BACNET_BASIC}/object/msv.c + ${LIBRARY_BACNET_BASIC}/object/program.c + ${LIBRARY_BACNET_BASIC}/program/ubasic/ubasic.c + ${LIBRARY_BACNET_BASIC}/program/ubasic/tokenizer.c ${LIBRARY_BACNET_BASIC}/service/h_dcc.c ${LIBRARY_BACNET_BASIC}/service/h_apdu.c diff --git a/ports/stm32f4xx/Makefile b/ports/stm32f4xx/Makefile index df81b18e..a1df0099 100644 --- a/ports/stm32f4xx/Makefile +++ b/ports/stm32f4xx/Makefile @@ -29,6 +29,8 @@ PLATFORM_SRC = \ $(PLATFORM_DIR)/netport.c \ $(PLATFORM_DIR)/rs485.c \ $(PLATFORM_DIR)/mstimer-init.c \ + $(PLATFORM_DIR)/program-ubasic.c \ + $(PLATFORM_DIR)/ubasic-port.c \ $(PLATFORM_DIR)/stm32f4xx_it.c \ $(PLATFORM_DIR)/system_stm32f4xx.c @@ -43,6 +45,9 @@ BASIC_SRC = \ $(BACNET_BASIC)/object/ms-input.c \ $(BACNET_BASIC)/object/mso.c \ $(BACNET_BASIC)/object/msv.c \ + $(BACNET_BASIC)/object/program.c \ + $(BACNET_BASIC)/program/ubasic/ubasic.c \ + $(BACNET_BASIC)/program/ubasic/tokenizer.c \ $(BACNET_BASIC)/service/h_apdu.c \ $(BACNET_BASIC)/service/h_dcc.c \ $(BACNET_BASIC)/service/h_rd.c \ @@ -173,6 +178,7 @@ BACNET_FLAGS += -DMAX_TSM_TRANSACTIONS=0 BACNET_FLAGS += -DMAX_CHARACTER_STRING_BYTES=64 BACNET_FLAGS += -DMAX_OCTET_STRING_BYTES=64 BACNET_FLAGS += -DBACAPP_MINIMAL +BACNET_FLAGS += -DBACNET_STACK_DEPRECATED_DISABLE # if called from root Makefile, PRINT was already defined BACNET_FLAGS += -UPRINT_ENABLED BACNET_FLAGS += -DPRINT_ENABLED=0 diff --git a/ports/stm32f4xx/bacnet.c b/ports/stm32f4xx/bacnet.c index 4196b13a..7dd7e0ba 100644 --- a/ports/stm32f4xx/bacnet.c +++ b/ports/stm32f4xx/bacnet.c @@ -1,15 +1,16 @@ -/************************************************************************** - * - * Copyright (C) 2011 Steve Karg - * - * SPDX-License-Identifier: MIT - * - *********************************************************************/ +/** + * @file + * @brief BACnet stack initialization and task processing + * @author Steve Karg + * @date 2011 + * @copyright SPDX-License-Identifier: MIT + */ #include #include /* hardware layer includes */ #include "bacnet/basic/sys/mstimer.h" #include "rs485.h" +#include "program-ubasic.h" /* BACnet Stack includes */ #include "bacnet/datalink/datalink.h" #include "bacnet/npdu.h" @@ -20,15 +21,8 @@ #include "bacnet/iam.h" /* BACnet objects */ #include "bacnet/basic/object/device.h" -#include "bacnet/basic/object/ai.h" -#include "bacnet/basic/object/ao.h" -#include "bacnet/basic/object/av.h" -#include "bacnet/basic/object/bi.h" -#include "bacnet/basic/object/bo.h" -#include "bacnet/basic/object/bv.h" -#include "bacnet/basic/object/ms-input.h" -#include "bacnet/basic/object/mso.h" -#include "bacnet/basic/object/msv.h" +#include "bacnet/basic/object/program.h" +#include "bacnet/basic/program/ubasic/ubasic.h" /* me */ #include "bacnet.h" @@ -38,69 +32,14 @@ static struct mstimer DCC_Timer; /* Device ID to track changes */ static uint32_t Device_ID = 0xFFFFFFFF; -#ifndef BACNET_ANALOG_INPUTS_MAX -#define BACNET_ANALOG_INPUTS_MAX 12 -#endif -#ifndef BACNET_ANALOG_OUTPUTS_MAX -#define BACNET_ANALOG_OUTPUTS_MAX 12 -#endif -#ifndef BACNET_ANALOG_VALUES_MAX -#define BACNET_ANALOG_VALUES_MAX 12 -#endif -#ifndef BACNET_BINARY_INPUTS_MAX -#define BACNET_BINARY_INPUTS_MAX 12 -#endif -#ifndef BACNET_BINARY_OUTPUTS_MAX -#define BACNET_BINARY_OUTPUTS_MAX 12 -#endif -#ifndef BACNET_BINARY_VALUES_MAX -#define BACNET_BINARY_VALUES_MAX 12 -#endif -#ifndef BACNET_MULTISTATE_INPUTS_MAX -#define BACNET_MULTISTATE_INPUTS_MAX 12 -#endif -#ifndef BACNET_MULTISTATE_OUTPUTS_MAX -#define BACNET_MULTISTATE_OUTPUTS_MAX 12 -#endif -#ifndef BACNET_MULTISTATE_VALUES_MAX -#define BACNET_MULTISTATE_VALUES_MAX 12 -#endif - /** * @brief Initialize the BACnet device object, the service handlers, and timers */ void bacnet_init(void) { - uint32_t instance; /* initialize objects */ Device_Init(NULL); - for (instance = 1; instance <= BACNET_ANALOG_INPUTS_MAX; instance++) { - Analog_Input_Create(instance); - } - for (instance = 1; instance <= BACNET_ANALOG_OUTPUTS_MAX; instance++) { - Analog_Output_Create(instance); - } - for (instance = 1; instance <= BACNET_ANALOG_VALUES_MAX; instance++) { - Analog_Value_Create(instance); - } - for (instance = 1; instance <= BACNET_BINARY_INPUTS_MAX; instance++) { - Binary_Input_Create(instance); - } - for (instance = 1; instance <= BACNET_BINARY_OUTPUTS_MAX; instance++) { - Binary_Output_Create(instance); - } - for (instance = 1; instance <= BACNET_BINARY_VALUES_MAX; instance++) { - Binary_Value_Create(instance); - } - for (instance = 1; instance <= BACNET_MULTISTATE_INPUTS_MAX; instance++) { - Multistate_Input_Create(instance); - } - for (instance = 1; instance <= BACNET_MULTISTATE_OUTPUTS_MAX; instance++) { - Multistate_Output_Create(instance); - } - for (instance = 1; instance <= BACNET_MULTISTATE_VALUES_MAX; instance++) { - Multistate_Value_Create(instance); - } + Program_UBASIC_Init(BACNET_MAX_INSTANCE); /* set up our confirmed service unrecognized service handler - required! */ apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service); /* we need to handle who-is to support dynamic device binding */ @@ -118,12 +57,12 @@ void bacnet_init(void) SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property); /* local time and date */ apdu_set_unconfirmed_handler( - SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, - handler_timesync); + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync); handler_timesync_set_callback_set(datetime_timesync); datetime_init(); /* handle communication so we can shutup when asked */ - apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, handler_device_communication_control); /* start the cyclic 1 second timer for DCC */ mstimer_set(&DCC_Timer, DCC_CYCLE_SECONDS * 1000); @@ -154,6 +93,7 @@ void bacnet_task(void) mstimer_reset(&DCC_Timer); dcc_timer_seconds(DCC_CYCLE_SECONDS); } + Program_UBASIC_Task(); /* handle the messaging */ pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); if (pdu_len) { diff --git a/ports/stm32f4xx/bacnet.ewp b/ports/stm32f4xx/bacnet.ewp index 9426774a..30a78b31 100644 --- a/ports/stm32f4xx/bacnet.ewp +++ b/ports/stm32f4xx/bacnet.ewp @@ -1235,6 +1235,12 @@ BACnet Basic System + + $PROJ_DIR$\..\..\src\bacnet\basic\program\ubasic\ubasic.c + + + $PROJ_DIR$\..\..\src\bacnet\basic\program\ubasic\tokenizer.c + $PROJ_DIR$\..\..\src\bacnet\basic\sys\bigend.c @@ -1277,6 +1283,9 @@ $PROJ_DIR$\system_stm32f4xx.c + + $PROJ_DIR$\ubasic-port.c + NUCLEO-BACnet diff --git a/ports/stm32f4xx/bacnet.h b/ports/stm32f4xx/bacnet.h index 32b65f85..d469493e 100644 --- a/ports/stm32f4xx/bacnet.h +++ b/ports/stm32f4xx/bacnet.h @@ -1,10 +1,10 @@ -/************************************************************************** - * - * Copyright (C) 2010 Steve Karg - * - * SPDX-License-Identifier: MIT - * - *********************************************************************/ +/** + * @file + * @brief BACnet stack initialization and task processing + * @author Steve Karg + * @date 2021 + * @copyright SPDX-License-Identifier: MIT + */ #ifndef BACNET_H #define BACNET_H diff --git a/ports/stm32f4xx/device.c b/ports/stm32f4xx/device.c index 60b2e5c3..71490c8f 100644 --- a/ports/stm32f4xx/device.c +++ b/ports/stm32f4xx/device.c @@ -1,14 +1,11 @@ -/************************************************************************** - * - * Copyright (C) 2011 Steve Karg - * - * SPDX-License-Identifier: MIT - * - *********************************************************************/ - -/** @file device.c Base "class" for handling all BACnet objects belonging - * to a BACnet device, as well as Device-specific properties. */ - +/** + * @file + * @brief Base "class" for handling all BACnet objects belonging + * to a BACnet device, as well as Device-specific properties. + * @author Steve Karg + * @date 2021 + * @copyright SPDX-License-Identifier: MIT + */ #include #include #include @@ -33,12 +30,12 @@ #include "bacnet/basic/object/ms-input.h" #include "bacnet/basic/object/mso.h" #include "bacnet/basic/object/msv.h" +#include "bacnet/basic/object/program.h" #if (BACNET_PROTOCOL_REVISION >= 17) #include "bacnet/basic/object/netport.h" #endif #include "bacnet/basic/object/device.h" - /* clang-format off */ static struct my_object_functions { BACNET_OBJECT_TYPE Object_Type; @@ -93,6 +90,10 @@ static struct my_object_functions { Multistate_Value_Index_To_Instance, Multistate_Value_Valid_Instance, Multistate_Value_Object_Name, Multistate_Value_Read_Property, Multistate_Value_Write_Property, Multistate_Value_Property_Lists}, + { OBJECT_PROGRAM, Program_Init, Program_Count, + Program_Index_To_Instance, Program_Valid_Instance, + Program_Object_Name, Program_Read_Property, + Program_Write_Property, Program_Property_Lists}, #if (BACNET_PROTOCOL_REVISION >= 17) { OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count, Network_Port_Index_To_Instance, Network_Port_Valid_Instance, @@ -121,14 +122,27 @@ static uint8_t Device_UUID[16]; static const int Device_Properties_Required[] = { /* required properties for this object */ PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_SYSTEM_STATUS, PROP_VENDOR_NAME, - PROP_VENDOR_IDENTIFIER, PROP_MODEL_NAME, PROP_FIRMWARE_REVISION, - PROP_APPLICATION_SOFTWARE_VERSION, PROP_PROTOCOL_VERSION, - PROP_PROTOCOL_REVISION, PROP_PROTOCOL_SERVICES_SUPPORTED, - PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, PROP_OBJECT_LIST, - PROP_MAX_APDU_LENGTH_ACCEPTED, PROP_SEGMENTATION_SUPPORTED, - PROP_APDU_TIMEOUT, PROP_NUMBER_OF_APDU_RETRIES, PROP_DEVICE_ADDRESS_BINDING, - PROP_DATABASE_REVISION, -1 }; + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_REVISION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_OBJECT_LIST, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_DEVICE_ADDRESS_BINDING, + PROP_DATABASE_REVISION, + -1 +}; static const int Device_Properties_Optional[] = { /* optional properties for this object */ @@ -141,7 +155,8 @@ static const int Device_Properties_Optional[] = { PROP_LOCAL_TIME, PROP_UTC_OFFSET, PROP_DAYLIGHT_SAVINGS_STATUS, - -1 }; + -1 +}; static const int Device_Properties_Proprietary[] = { -1 }; @@ -153,8 +168,8 @@ static const int Device_Properties_Proprietary[] = { -1 }; * @return Pointer to the group of object helper functions that implement this * type of Object. */ -static struct my_object_functions *Device_Objects_Find_Functions( - BACNET_OBJECT_TYPE Object_Type) +static struct my_object_functions * +Device_Objects_Find_Functions(BACNET_OBJECT_TYPE Object_Type) { struct my_object_functions *pObject = NULL; @@ -182,7 +197,8 @@ static struct my_object_functions *Device_Objects_Find_Functions( * list, separately, the Required, Optional, and Proprietary object * properties with their counts. */ -void Device_Objects_Property_List(BACNET_OBJECT_TYPE object_type, +void Device_Objects_Property_List( + BACNET_OBJECT_TYPE object_type, uint32_t object_instance, struct special_property_list_t *pPropertyList) { @@ -200,8 +216,9 @@ void Device_Objects_Property_List(BACNET_OBJECT_TYPE object_type, pObject = Device_Objects_Find_Functions(object_type); if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { - pObject->Object_RPM_List(&pPropertyList->Required.pList, - &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); + pObject->Object_RPM_List( + &pPropertyList->Required.pList, &pPropertyList->Optional.pList, + &pPropertyList->Proprietary.pList); } /* Fetch the counts if available otherwise zero them */ @@ -430,8 +447,9 @@ bool Device_Set_Object_Instance_Number(uint32_t object_id) if (object_id <= BACNET_MAX_INSTANCE) { Object_Instance_Number = object_id; - } else + } else { status = false; + } return status; } @@ -656,7 +674,8 @@ int Device_Object_List_Element_Encode( * Object. * @return True on success or else False if not found. */ -bool Device_Valid_Object_Name(const BACNET_CHARACTER_STRING *object_name1, +bool Device_Valid_Object_Name( + const BACNET_CHARACTER_STRING *object_name1, BACNET_OBJECT_TYPE *object_type, uint32_t *object_instance) { @@ -675,7 +694,7 @@ bool Device_Valid_Object_Name(const BACNET_CHARACTER_STRING *object_name1, pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)type); if ((pObject != NULL) && (pObject->Object_Name != NULL) && (pObject->Object_Name(instance, &object_name2) && - characterstring_same(object_name1, &object_name2))) { + characterstring_same(object_name1, &object_name2))) { found = true; if (object_type) { *object_type = type; @@ -716,7 +735,8 @@ bool Device_Valid_Object_Id( * @param object_name [out] The Object Name found for this child Object. * @return True on success or else False if not found. */ -bool Device_Object_Name_Copy(BACNET_OBJECT_TYPE object_type, +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) { @@ -811,7 +831,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata) bitstring_init(&bit_string); for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { /* automatic lookup based on handlers set */ - bitstring_set_bit(&bit_string, (uint8_t)i, + bitstring_set_bit( + &bit_string, (uint8_t)i, apdu_service_supported((BACNET_SERVICES_SUPPORTED)i)); } apdu_len = encode_application_bitstring(&apdu[0], &bit_string); @@ -836,9 +857,9 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata) break; case PROP_OBJECT_LIST: count = Device_Object_List_Count(); - apdu_len = bacnet_array_encode(rpdata->object_instance, - rpdata->array_index, Device_Object_List_Element_Encode, count, - apdu, apdu_max); + apdu_len = bacnet_array_encode( + rpdata->object_instance, rpdata->array_index, + Device_Object_List_Element_Encode, count, apdu, apdu_max); if (apdu_len == BACNET_STATUS_ABORT) { rpdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; @@ -893,8 +914,7 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata) break; case PROP_DAYLIGHT_SAVINGS_STATUS: datetime_local(&bdate, &btime, &utc_offset_minutes, &dst_active); - apdu_len = - encode_application_boolean(&apdu[0], dst_active); + apdu_len = encode_application_boolean(&apdu[0], dst_active); break; default: rpdata->error_class = ERROR_CLASS_PROPERTY; @@ -949,16 +969,16 @@ static int Read_Property_Common( encode_application_character_string(&apdu[0], &char_string); break; case PROP_OBJECT_TYPE: - apdu_len = encode_application_enumerated( - &apdu[0], rpdata->object_type); + apdu_len = + encode_application_enumerated(&apdu[0], rpdata->object_type); break; #if (BACNET_PROTOCOL_REVISION >= 14) case PROP_PROPERTY_LIST: Device_Objects_Property_List( rpdata->object_type, rpdata->object_instance, &property_list); - apdu_len = property_list_encode(rpdata, - property_list.Required.pList, property_list.Optional.pList, - property_list.Proprietary.pList); + apdu_len = property_list_encode( + rpdata, property_list.Required.pList, + property_list.Optional.pList, property_list.Proprietary.pList); break; #endif default: diff --git a/ports/stm32f4xx/led.c b/ports/stm32f4xx/led.c index a9ed34d0..b8bf2486 100644 --- a/ports/stm32f4xx/led.c +++ b/ports/stm32f4xx/led.c @@ -1,10 +1,10 @@ -/************************************************************************** - * - * Copyright (C) 2011 Steve Karg - * - * SPDX-License-Identifier: MIT - * - *********************************************************************/ +/** + * @file + * @brief LED control for the STM32F4xx + * @author Steve Karg + * @date 2021 + * @copyright SPDX-License-Identifier: MIT + */ #include #include "stm32f4xx.h" #include "bacnet/basic/sys/mstimer.h" diff --git a/ports/stm32f4xx/led.h b/ports/stm32f4xx/led.h index fdc92f7a..be7e30c1 100644 --- a/ports/stm32f4xx/led.h +++ b/ports/stm32f4xx/led.h @@ -1,10 +1,10 @@ -/************************************************************************** - * - * Copyright (C) 2009 Steve Karg - * - * SPDX-License-Identifier: MIT - * - *********************************************************************/ +/** + * @file + * @brief LED control for the STM32F4xx + * @author Steve Karg + * @date 2021 + * @copyright SPDX-License-Identifier: MIT + */ #ifndef LED_H #define LED_H diff --git a/ports/stm32f4xx/main.c b/ports/stm32f4xx/main.c index 2a670196..b046e3ad 100644 --- a/ports/stm32f4xx/main.c +++ b/ports/stm32f4xx/main.c @@ -1,10 +1,10 @@ -/************************************************************************ - * - * Copyright (C) 2011 Steve Karg - * - * SPDX-License-Identifier: MIT - * - *************************************************************************/ +/** + * @file + * @brief Main function for the STM32F4xx NUCLEO board + * @author Steve Karg + * @date 2021 + * @copyright SPDX-License-Identifier: MIT + */ #include #include #include @@ -102,7 +102,7 @@ int main(void) MSTP_Port.ZeroConfigEnabled = true; MSTP_Port.Zero_Config_Preferred_Station = 0; MSTP_Port.SlaveNodeEnabled = false; - MSTP_Port.CheckAutoBaud = false; + MSTP_Port.CheckAutoBaud = true; /* user data */ MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_Port.UserData = &MSTP_User_Data; diff --git a/ports/stm32f4xx/mstimer-init.c b/ports/stm32f4xx/mstimer-init.c index 660927b0..fd9533ab 100644 --- a/ports/stm32f4xx/mstimer-init.c +++ b/ports/stm32f4xx/mstimer-init.c @@ -1,13 +1,10 @@ -/************************************************************************** - * - * Copyright (C) 2011 Steve Karg - * - * SPDX-License-Identifier: MIT - * - * Module Description: - * Generate a periodic timer tick for use by generic timers in the code. - * - *************************************************************************/ +/** + * @file + * @brief Generate a periodic timer tick for use by generic timers in the code. + * @author Steve Karg + * @date 2011 + * @copyright SPDX-License-Identifier: MIT + */ #include #include #include "bacnet/basic/sys/mstimer.h" diff --git a/ports/stm32f4xx/netport.c b/ports/stm32f4xx/netport.c index 523154c9..5f1c45d4 100644 --- a/ports/stm32f4xx/netport.c +++ b/ports/stm32f4xx/netport.c @@ -1,17 +1,10 @@ /** * @file + * @brief The Network Port object provides access to the configuration + * and properties of network ports of a device. * @author Steve Karg * @date 2016 - * @brief Network port objects, customize for your use - * - * @section DESCRIPTION - * - * The Network Port object provides access to the configuration - * and properties of network ports of a device. - * - * @section LICENSE - * - * SPDX-License-Identifier: MIT + * @copyright SPDX-License-Identifier: MIT */ #include #include @@ -55,9 +48,15 @@ struct object_data Object_List[BACNET_NETWORK_PORTS_MAX]; static uint32_t Link_Speeds[] = { 9600, 19200, 38400, 57600, 76800, 115200 }; /* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Network_Port_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_STATUS_FLAGS, PROP_RELIABILITY, - PROP_OUT_OF_SERVICE, PROP_NETWORK_TYPE, PROP_PROTOCOL_LEVEL, +static const int Network_Port_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_STATUS_FLAGS, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_NETWORK_TYPE, + PROP_PROTOCOL_LEVEL, PROP_CHANGES_PENDING, #if (BACNET_PROTOCOL_REVISION < 24) PROP_APDU_LENGTH, @@ -65,17 +64,22 @@ static const int Network_Port_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, PROP_NETWORK_NUMBER_QUALITY, PROP_LINK_SPEED, #endif - -1 }; + -1 +}; -static const int Network_Port_Properties_Optional[] = { PROP_MAC_ADDRESS, - PROP_MAX_MASTER, PROP_MAX_INFO_FRAMES, PROP_LINK_SPEEDS, +static const int Network_Port_Properties_Optional[] = { + PROP_MAC_ADDRESS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_LINK_SPEEDS, #if (BACNET_PROTOCOL_REVISION >= 24) PROP_APDU_LENGTH, PROP_NETWORK_NUMBER, PROP_NETWORK_NUMBER_QUALITY, PROP_LINK_SPEED, #endif - -1 }; + -1 +}; static const int Network_Port_Properties_Proprietary[] = { -1 }; @@ -91,7 +95,8 @@ static const int Network_Port_Properties_Proprietary[] = { -1 }; * @param pProprietary - pointer to list of int terminated by -1, of * BACnet proprietary properties for this object. */ -void Network_Port_Property_List(uint32_t object_instance, +void Network_Port_Property_List( + uint32_t object_instance, const int **pRequired, const int **pOptional, const int **pProprietary) @@ -438,8 +443,9 @@ bool Network_Port_Changes_Pending_Set(uint32_t instance, bool flag) index = Network_Port_Instance_To_Index(instance); if (index < BACNET_NETWORK_PORTS_MAX) { Object_List[index].Changes_Pending = flag; - } else + } else { return false; + } return true; } @@ -523,8 +529,8 @@ bool Network_Port_MSTP_Max_Info_Frames_Set( * @param object_property - object-property to be checked * @return true if the property is a member of this object instance */ -static bool Network_Port_Property_List_Member( - uint32_t object_instance, int object_property) +static bool +Network_Port_Property_List_Member(uint32_t object_instance, int object_property) { bool found = false; const int *pRequired = NULL; @@ -640,9 +646,9 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) break; case PROP_LINK_SPEEDS: count = Network_Port_Link_Speeds_Count(rpdata->object_instance); - apdu_len = bacnet_array_encode(rpdata->object_instance, - rpdata->array_index, Network_Port_Link_Speeds_Encode, count, - apdu, apdu_max); + apdu_len = bacnet_array_encode( + rpdata->object_instance, rpdata->array_index, + Network_Port_Link_Speeds_Encode, count, apdu, apdu_max); if (apdu_len == BACNET_STATUS_ABORT) { rpdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; @@ -652,7 +658,8 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } break; case PROP_CHANGES_PENDING: - apdu_len = encode_application_boolean(&apdu[0], + apdu_len = encode_application_boolean( + &apdu[0], Network_Port_Changes_Pending(rpdata->object_instance)); break; case PROP_APDU_LENGTH: @@ -660,11 +667,13 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) &apdu[0], Network_Port_APDU_Length(rpdata->object_instance)); break; case PROP_MAX_MASTER: - apdu_len = encode_application_unsigned(&apdu[0], + apdu_len = encode_application_unsigned( + &apdu[0], Network_Port_MSTP_Max_Master(rpdata->object_instance)); break; case PROP_MAX_INFO_FRAMES: - apdu_len = encode_application_unsigned(&apdu[0], + apdu_len = encode_application_unsigned( + &apdu[0], Network_Port_MSTP_Max_Info_Frames(rpdata->object_instance)); break; default: @@ -760,8 +769,8 @@ bool Network_Port_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) break; case PROP_MAC_ADDRESS: if (value.tag == BACNET_APPLICATION_TAG_OCTET_STRING) { - if (!Network_Port_MAC_Address_Set(wp_data->object_instance, - value.type.Octet_String.value, + if (!Network_Port_MAC_Address_Set( + wp_data->object_instance, value.type.Octet_String.value, value.type.Octet_String.length)) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; diff --git a/ports/stm32f4xx/program-ubasic.c b/ports/stm32f4xx/program-ubasic.c new file mode 100644 index 00000000..70a1ea9e --- /dev/null +++ b/ports/stm32f4xx/program-ubasic.c @@ -0,0 +1,147 @@ +/** + * @file + * @brief uBASIC-Plus program object for BACnet + * @author Steve Karg + * @date 2025 + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include "bacnet/basic/object/program.h" +#include "bacnet/basic/program/ubasic/ubasic.h" +#include "bacnet/basic/sys/mstimer.h" + +/* uBASIC-Plus program object */ +static struct ubasic_data UBASIC_DATA = { 0 }; +static struct mstimer UBASIC_Timer; +static uint32_t UBASIC_Instance = 0; +static const char *UBASIC_Program = + /* program listing with either \0, \n, or ';' at the end of each line. + note: indentation is not required */ + "println 'Demo - BACnet & GPIO';" + "bac_create(0, 1, 'ADC-1');" + "bac_create(0, 2, 'ADC-2');" + "bac_create(4, 1, 'LED-1');" + "bac_create(4, 2, 'LED-2');" + ":startover;" + " a = aread(1);" + " c = avgw(a, c, 10);" + " bac_write(0, 1, 85, c);" + " b = aread(2);" + " d = avgw(b, d, 10);" + " bac_write(0, 2, 85, d);" + " h = bac_read(4, 1, 85);" + " dwrite(1, (h % 2));" + " i = bac_read(4, 2, 85);" + " dwrite(2, (i % 2));" + " sleep (0.2);" + "goto startover;" + "end;"; + +/** + * @brief Load the program into the uBASIC interpreter + * @param context Pointer to the uBASIC data structure + * @return 0 on success + */ +static int Program_Load(void *context) +{ + struct ubasic_data *data = (struct ubasic_data *)context; + + ubasic_load_program(data, UBASIC_Program); + return 0; +} + +/** + * @brief Run the program in the uBASIC interpreter + * @param context Pointer to the uBASIC data structure + * @return 0 while the programm is running, non-zero when finished + * or an error occurred + */ +static int Program_Run(void *context) +{ + struct ubasic_data *data = (struct ubasic_data *)context; + int result = 0; + + result = ubasic_run_program(data); + if (result <= 0) { + return -1; + } + + return 0; +} + +/** + * @brief Halt the program in the uBASIC interpreter + * @param context Pointer to the uBASIC data structure + * @return 0 on success, non-zero on error + */ +static int Program_Halt(void *context) +{ + struct ubasic_data *data = (struct ubasic_data *)context; + + data->status.bit.isRunning = 0; + + return 0; +} + +/** + * @brief Restart the program in the uBASIC interpreter + * @param context Pointer to the uBASIC data structure + * @return 0 on success, non-zero on error + */ +static int Program_Restart(void *context) +{ + struct ubasic_data *data = (struct ubasic_data *)context; + + ubasic_clear_variables(data); + ubasic_load_program(data, UBASIC_Program); + + return 0; +} + +/** + * @brief Unload the program in the uBASIC interpreter + * @param context Pointer to the uBASIC data structure + * @return 0 on success, non-zero on error + */ +static int Program_Unload(void *context) +{ + struct ubasic_data *data = (struct ubasic_data *)context; + + ubasic_clear_variables(data); + return 0; +} + +/** + * @brief Timer task for the uBASIC program object + */ +void Program_UBASIC_Task(void) +{ + if (mstimer_expired(&UBASIC_Timer)) { + mstimer_reset(&UBASIC_Timer); + Program_Timer(UBASIC_Instance, mstimer_interval(&UBASIC_Timer)); + } +} + +/** + * @brief Initialize the uBASIC program object + * @param instance Instance number of the program object + * @return 0 on success, non-zero on error + */ +void Program_UBASIC_Init(uint32_t instance) +{ + ubasic_port_init(&UBASIC_DATA); + UBASIC_Instance = Program_Create(instance); + Program_Context_Set(UBASIC_Instance, &UBASIC_DATA); + Program_Load_Set(UBASIC_Instance, Program_Load); + Program_Run_Set(UBASIC_Instance, Program_Run); + Program_Halt_Set(UBASIC_Instance, Program_Halt); + Program_Restart_Set(UBASIC_Instance, Program_Restart); + Program_Unload_Set(UBASIC_Instance, Program_Unload); + /* auto-run the program */ + Program_Change_Set(UBASIC_Instance, PROGRAM_REQUEST_RUN); + /* start the cyclic 10ms run timer for the program object */ + mstimer_set(&UBASIC_Timer, 10); +} diff --git a/ports/stm32f4xx/program-ubasic.h b/ports/stm32f4xx/program-ubasic.h new file mode 100644 index 00000000..f095202f --- /dev/null +++ b/ports/stm32f4xx/program-ubasic.h @@ -0,0 +1,23 @@ +/** + * @file + * @brief uBASIC-Plus program object for BACnet + * @author Steve Karg + * @date 2025 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef PROGRAM_UBASIC_H +#define PROGRAM_UBASIC_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void Program_UBASIC_Task(void); +void Program_UBASIC_Init(uint32_t instance); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/stm32f4xx/rs485.c b/ports/stm32f4xx/rs485.c index 3e55b346..7c59e52c 100644 --- a/ports/stm32f4xx/rs485.c +++ b/ports/stm32f4xx/rs485.c @@ -24,36 +24,36 @@ #include "rs485.h" #ifndef RS485_LINKSPRITE_ENABLED - #ifndef RS485_DFR0259_ENABLED - #define RS485_DFR0259_ENABLED 1 - #endif +#ifndef RS485_DFR0259_ENABLED +#define RS485_DFR0259_ENABLED 1 +#endif #endif #if defined(RS485_DFR0259_ENABLED) || defined(RS485_LINKSPRITE_ENABLED) - /* DFR0259 RS485 Shield - TXD=PG14, RXD=PG9, USART6 */ - #define RS485_USARTx USART6 - #define RS485_NVIC_IRQ USART6_IRQn - #define RS485_USARTx_ISR USART6_IRQHandler - #define RS485_USARTx_RCC RCC_APB2Periph_USART6 - #define RS485_GPIO_RCC RCC_AHB1Periph_GPIOG - #define RS485_GPIO_PINS GPIO_Pin_9 | GPIO_Pin_14 - #define RS485_GPIO GPIOG - /* alternate function (AF) */ - #define RS485_AF_PINSOURCE_RX GPIO_PinSource9 - #define RS485_AF_PINSOURCE_TX GPIO_PinSource14 - #define RS485_AF_FUNCTION GPIO_AF_USART6 +/* DFR0259 RS485 Shield - TXD=PG14, RXD=PG9, USART6 */ +#define RS485_USARTx USART6 +#define RS485_NVIC_IRQ USART6_IRQn +#define RS485_USARTx_ISR USART6_IRQHandler +#define RS485_USARTx_RCC RCC_APB2Periph_USART6 +#define RS485_GPIO_RCC RCC_AHB1Periph_GPIOG +#define RS485_GPIO_PINS GPIO_Pin_9 | GPIO_Pin_14 +#define RS485_GPIO GPIOG +/* alternate function (AF) */ +#define RS485_AF_PINSOURCE_RX GPIO_PinSource9 +#define RS485_AF_PINSOURCE_TX GPIO_PinSource14 +#define RS485_AF_FUNCTION GPIO_AF_USART6 #endif #if defined(RS485_DFR0259_ENABLED) - /* DFR0259 RS485 Shield - CE=PF15 */ - #define RS485_RTS_RCC RCC_AHB1Periph_GPIOF - #define RS485_RTS_PIN GPIO_Pin_15 - #define RS485_RTS_GPIO GPIOF +/* DFR0259 RS485 Shield - CE=PF15 */ +#define RS485_RTS_RCC RCC_AHB1Periph_GPIOF +#define RS485_RTS_PIN GPIO_Pin_15 +#define RS485_RTS_GPIO GPIOF #endif #if defined(RS485_LINKSPRITE_ENABLED) - /* LINKSPRITE RS485 Shield - CE=PD15 */ - #define RS485_RTS_RCC RCC_AHB1Periph_GPIOD - #define RS485_RTS_PIN GPIO_Pin_15 - #define RS485_RTS_GPIO GPIOD +/* LINKSPRITE RS485 Shield - CE=PD15 */ +#define RS485_RTS_RCC RCC_AHB1Periph_GPIOD +#define RS485_RTS_PIN GPIO_Pin_15 +#define RS485_RTS_GPIO GPIOD #endif /* buffer for storing received bytes - size must be power of two */ @@ -305,9 +305,11 @@ void rs485_init(void) NVIC_InitTypeDef NVIC_InitStructure; /* initialize the Rx and Tx byte queues */ - FIFO_Init(&Receive_Queue, &Receive_Queue_Data[0], + FIFO_Init( + &Receive_Queue, &Receive_Queue_Data[0], (unsigned)sizeof(Receive_Queue_Data)); - FIFO_Init(&Transmit_Queue, &Transmit_Queue_Data[0], + FIFO_Init( + &Transmit_Queue, &Transmit_Queue_Data[0], (unsigned)sizeof(Transmit_Queue_Data)); /* Enable GPIOx clock */ diff --git a/ports/stm32f4xx/stm32f4xx_conf.h b/ports/stm32f4xx/stm32f4xx_conf.h index 3ae8d1c4..4d77b7dc 100644 --- a/ports/stm32f4xx/stm32f4xx_conf.h +++ b/ports/stm32f4xx/stm32f4xx_conf.h @@ -1,23 +1,23 @@ /** - ****************************************************************************** - * @file stm32f4xx_conf.h - * @author MCD Application Team - * @version V1.0.0 - * @date 19-September-2011 - * @brief Library configuration file. - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2011 STMicroelectronics

- ****************************************************************************** - */ + ****************************************************************************** + * @file stm32f4xx_conf.h + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief Library configuration file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __STM32F4xx_CONF_H @@ -25,53 +25,62 @@ /* ########################## HSE/HSI Values adaptation ##################### */ /** - * @brief Adjust the value of External High Speed oscillator (HSE) used in your application. - * This value is used by the RCC HAL module to compute the system frequency - * (when HSE is used as system clock source, directly or through the PLL). - */ -#if !defined (HSE_VALUE) - #define HSE_VALUE ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */ + * @brief Adjust the value of External High Speed oscillator (HSE) used in your + * application. This value is used by the RCC HAL module to compute the system + * frequency (when HSE is used as system clock source, directly or through the + * PLL). + */ +#if !defined(HSE_VALUE) +#define HSE_VALUE \ + ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */ #endif /* HSE_VALUE */ -#if !defined (HSE_STARTUP_TIMEOUT) - #define HSE_STARTUP_TIMEOUT ((uint32_t)100U) /*!< Time out for HSE start up, in ms */ +#if !defined(HSE_STARTUP_TIMEOUT) +#define HSE_STARTUP_TIMEOUT \ + ((uint32_t)100U) /*!< Time out for HSE start up, in ms */ #endif /* HSE_STARTUP_TIMEOUT */ /** - * @brief Internal High Speed oscillator (HSI) value. - * This value is used by the RCC HAL module to compute the system frequency - * (when HSI is used as system clock source, directly or through the PLL). - */ -#if !defined (HSI_VALUE) - #define HSI_VALUE ((uint32_t)16000000U) /*!< Value of the Internal oscillator in Hz*/ + * @brief Internal High Speed oscillator (HSI) value. + * This value is used by the RCC HAL module to compute the system + * frequency (when HSI is used as system clock source, directly or through the + * PLL). + */ +#if !defined(HSI_VALUE) +#define HSI_VALUE \ + ((uint32_t)16000000U) /*!< Value of the Internal oscillator in Hz*/ #endif /* HSI_VALUE */ /** - * @brief Internal Low Speed oscillator (LSI) value. - */ -#if !defined (LSI_VALUE) - #define LSI_VALUE ((uint32_t)32000U) /*!< LSI Typical Value in Hz*/ -#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz - The real value may vary depending on the variations - in voltage and temperature.*/ + * @brief Internal Low Speed oscillator (LSI) value. + */ +#if !defined(LSI_VALUE) +#define LSI_VALUE ((uint32_t)32000U) /*!< LSI Typical Value in Hz*/ +#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz \ + The real value may vary depending on the variations \ + in voltage and temperature.*/ /** - * @brief External Low Speed oscillator (LSE) value. - */ -#if !defined (LSE_VALUE) - #define LSE_VALUE ((uint32_t)32768U) /*!< Value of the External Low Speed oscillator in Hz */ + * @brief External Low Speed oscillator (LSE) value. + */ +#if !defined(LSE_VALUE) +#define LSE_VALUE \ + ((uint32_t)32768U) /*!< Value of the External Low Speed oscillator in Hz \ + */ #endif /* LSE_VALUE */ -#if !defined (LSE_STARTUP_TIMEOUT) - #define LSE_STARTUP_TIMEOUT ((uint32_t)5000U) /*!< Time out for LSE start up, in ms */ +#if !defined(LSE_STARTUP_TIMEOUT) +#define LSE_STARTUP_TIMEOUT \ + ((uint32_t)5000U) /*!< Time out for LSE start up, in ms */ #endif /* LSE_STARTUP_TIMEOUT */ /** - * @brief External clock source for I2S peripheral - * This value is used by the I2S HAL module to compute the I2S clock source - * frequency, this source is inserted directly through I2S_CKIN pad. - */ -#if !defined (EXTERNAL_CLOCK_VALUE) - #define EXTERNAL_CLOCK_VALUE ((uint32_t)12288000U) /*!< Value of the External audio frequency in Hz*/ + * @brief External clock source for I2S peripheral + * This value is used by the I2S HAL module to compute the I2S clock + * source frequency, this source is inserted directly through I2S_CKIN pad. + */ +#if !defined(EXTERNAL_CLOCK_VALUE) +#define EXTERNAL_CLOCK_VALUE \ + ((uint32_t)12288000U) /*!< Value of the External audio frequency in Hz*/ #endif /* EXTERNAL_CLOCK_VALUE */ /* Tip: To avoid modifying this file each time you need to use different HSE, @@ -79,14 +88,14 @@ /* ########################### System Configuration ######################### */ /** - * @brief This is the HAL system configuration section - */ -#define VDD_VALUE ((uint32_t)3300U) /*!< Value of VDD in mv */ -#define TICK_INT_PRIORITY ((uint32_t)0U) /*!< tick interrupt priority */ -#define USE_RTOS 0U -#define PREFETCH_ENABLE 1U -#define INSTRUCTION_CACHE_ENABLE 1U -#define DATA_CACHE_ENABLE 1U + * @brief This is the HAL system configuration section + */ +#define VDD_VALUE ((uint32_t)3300U) /*!< Value of VDD in mv */ +#define TICK_INT_PRIORITY ((uint32_t)0U) /*!< tick interrupt priority */ +#define USE_RTOS 0U +#define PREFETCH_ENABLE 1U +#define INSTRUCTION_CACHE_ENABLE 1U +#define DATA_CACHE_ENABLE 1U /* Includes ------------------------------------------------------------------*/ @@ -123,29 +132,30 @@ /* If an external clock source is used, then the value of the following define should be set to the value of the external clock source, else, if no external clock is used, keep this define commented */ -/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in Hz */ - +/*#define I2S_EXTERNAL_CLOCK_VAL 12288000 */ /* Value of the external clock in + Hz */ /* Uncomment the line below to expanse the "assert_param" macro in the Standard Peripheral Library drivers code */ /* #define USE_FULL_ASSERT 1 */ /* Exported macro ------------------------------------------------------------*/ -#ifdef USE_FULL_ASSERT +#ifdef USE_FULL_ASSERT /** - * @brief The assert_param macro is used for function's parameters check. - * @param expr: If expr is false, it calls assert_failed function - * which reports the name of the source file and the source - * line number of the call that failed. - * If expr is true, it returns no value. - * @retval None - */ - #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ +#define assert_param(expr) \ + ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) /* Exported functions ------------------------------------------------------- */ - void assert_failed(uint8_t* file, uint32_t line); +void assert_failed(uint8_t *file, uint32_t line); #else - #define assert_param(expr) ((void)0) +#define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */ #endif /* __STM32F4xx_CONF_H */ diff --git a/ports/stm32f4xx/stm32f4xx_it.c b/ports/stm32f4xx/stm32f4xx_it.c index aff22ab8..73f71cb0 100644 --- a/ports/stm32f4xx/stm32f4xx_it.c +++ b/ports/stm32f4xx/stm32f4xx_it.c @@ -1,37 +1,37 @@ /** - ****************************************************************************** - * @file IO_Toggle/stm32f4xx_it.c - * @author MCD Application Team - * @version V1.0.0 - * @date 19-September-2011 - * @brief Main Interrupt Service Routines. - * This file provides template for all exceptions handler and - * peripherals interrupt service routine. - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2011 STMicroelectronics

- ****************************************************************************** - */ + ****************************************************************************** + * @file IO_Toggle/stm32f4xx_it.c + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief Main Interrupt Service Routines. + * This file provides template for all exceptions handler and + * peripherals interrupt service routine. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ /* Includes ------------------------------------------------------------------*/ #include "stm32f4xx_it.h" #include "stm32f4xx.h" /** @addtogroup STM32F4_Discovery_Peripheral_Examples - * @{ - */ + * @{ + */ /** @addtogroup IO_Toggle - * @{ - */ + * @{ + */ /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ @@ -45,89 +45,81 @@ /******************************************************************************/ /** - * @brief This function handles NMI exception. - * @param None - * @retval None - */ + * @brief This function handles NMI exception. + * @param None + * @retval None + */ void NMI_Handler(void) { } /** - * @brief This function handles Hard Fault exception. - * @param None - * @retval None - */ + * @brief This function handles Hard Fault exception. + * @param None + * @retval None + */ void HardFault_Handler(void) { - /* Go to infinite loop when Hard Fault exception occurs */ - while (1) - { - } + /* Go to infinite loop when Hard Fault exception occurs */ + while (1) { } } /** - * @brief This function handles Memory Manage exception. - * @param None - * @retval None - */ + * @brief This function handles Memory Manage exception. + * @param None + * @retval None + */ void MemManage_Handler(void) { - /* Go to infinite loop when Memory Manage exception occurs */ - while (1) - { - } + /* Go to infinite loop when Memory Manage exception occurs */ + while (1) { } } /** - * @brief This function handles Bus Fault exception. - * @param None - * @retval None - */ + * @brief This function handles Bus Fault exception. + * @param None + * @retval None + */ void BusFault_Handler(void) { - /* Go to infinite loop when Bus Fault exception occurs */ - while (1) - { - } + /* Go to infinite loop when Bus Fault exception occurs */ + while (1) { } } /** - * @brief This function handles Usage Fault exception. - * @param None - * @retval None - */ + * @brief This function handles Usage Fault exception. + * @param None + * @retval None + */ void UsageFault_Handler(void) { - /* Go to infinite loop when Usage Fault exception occurs */ - while (1) - { - } + /* Go to infinite loop when Usage Fault exception occurs */ + while (1) { } } /** - * @brief This function handles SVCall exception. - * @param None - * @retval None - */ + * @brief This function handles SVCall exception. + * @param None + * @retval None + */ void SVC_Handler(void) { } /** - * @brief This function handles Debug Monitor exception. - * @param None - * @retval None - */ + * @brief This function handles Debug Monitor exception. + * @param None + * @retval None + */ void DebugMon_Handler(void) { } /** - * @brief This function handles PendSVC exception. - * @param None - * @retval None - */ + * @brief This function handles PendSVC exception. + * @param None + * @retval None + */ void PendSV_Handler(void) { } @@ -140,17 +132,16 @@ void PendSV_Handler(void) /******************************************************************************/ /** - * @brief This function handles PPP interrupt request. - * @param None - * @retval None - */ + * @brief This function handles PPP interrupt request. + * @param None + * @retval None + */ /*void PPP_IRQHandler(void) { }*/ - /** - * @} - */ + * @} + */ /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f4xx/stm32f4xx_it.h b/ports/stm32f4xx/stm32f4xx_it.h index dd000126..31c48323 100644 --- a/ports/stm32f4xx/stm32f4xx_it.h +++ b/ports/stm32f4xx/stm32f4xx_it.h @@ -1,30 +1,30 @@ /** - ****************************************************************************** - * @file GPIO/IOToggle/stm32f4xx_it.h - * @author MCD Application Team - * @version V1.0.0 - * @date 19-September-2011 - * @brief This file contains the headers of the interrupt handlers. - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2011 STMicroelectronics

- ****************************************************************************** - */ + ****************************************************************************** + * @file GPIO/IOToggle/stm32f4xx_it.h + * @author MCD Application Team + * @version V1.0.0 + * @date 19-September-2011 + * @brief This file contains the headers of the interrupt handlers. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __STM32F4xx_IT_H #define __STM32F4xx_IT_H #ifdef __cplusplus - extern "C" { +extern "C" { #endif /* Includes ------------------------------------------------------------------*/ diff --git a/ports/stm32f4xx/system_stm32f4xx.c b/ports/stm32f4xx/system_stm32f4xx.c index a1c9362a..aea0666c 100644 --- a/ports/stm32f4xx/system_stm32f4xx.c +++ b/ports/stm32f4xx/system_stm32f4xx.c @@ -1,315 +1,325 @@ /** - ****************************************************************************** - * @file system_stm32f4xx.c - * @author MCD Application Team - * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. - * - * This file provides two functions and one global variable to be called from - * user application: - * - SystemInit(): This function is called at startup just after reset and - * before branch to main program. This call is made inside - * the "startup_stm32f4xx.s" file. - * - * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used - * by the user application to setup the SysTick - * timer or configure other parameters. - * - * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must - * be called whenever the core clock is changed - * during program execution. - * - * - ****************************************************************************** - * @attention - * - *

© COPYRIGHT 2017 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ + ****************************************************************************** + * @file system_stm32f4xx.c + * @author MCD Application Team + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * + * This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f4xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be + *used by the user application to setup the SysTick timer or configure other + *parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * + ****************************************************************************** + * @attention + * + *

© COPYRIGHT 2017 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without + *modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *notice, this list of conditions and the following disclaimer in the + *documentation and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + *ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + *LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + *CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + *SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + *INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + *CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + *ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + *POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ /** @addtogroup CMSIS - * @{ - */ + * @{ + */ /** @addtogroup stm32f4xx_system - * @{ - */ + * @{ + */ /** @addtogroup STM32F4xx_System_Private_Includes - * @{ - */ - + * @{ + */ #include "stm32f4xx.h" -#if !defined (HSE_VALUE) - #define HSE_VALUE ((uint32_t)25000000) /*!< Default value of the External oscillator in Hz */ +#if !defined(HSE_VALUE) +#define HSE_VALUE \ + ((uint32_t)25000000) /*!< Default value of the External oscillator in Hz \ + */ #endif /* HSE_VALUE */ -#if !defined (HSI_VALUE) - #define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/ +#if !defined(HSI_VALUE) +#define HSI_VALUE \ + ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/ #endif /* HSI_VALUE */ /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Private_TypesDefinitions - * @{ - */ + * @{ + */ /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Private_Defines - * @{ - */ + * @{ + */ /************************* Miscellaneous Configuration ************************/ -/*!< Uncomment the following line if you need to use external SRAM or SDRAM as data memory */ -#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\ - || defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ - || defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) +/*!< Uncomment the following line if you need to use external SRAM or SDRAM as + * data memory */ +#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || \ + defined(STM32F417xx) || defined(STM32F427xx) || defined(STM32F437xx) || \ + defined(STM32F429xx) || defined(STM32F439xx) || defined(STM32F469xx) || \ + defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) /* #define DATA_IN_ExtSRAM */ -#endif /* STM32F40xxx || STM32F41xxx || STM32F42xxx || STM32F43xxx || STM32F469xx || STM32F479xx ||\ - STM32F412Zx || STM32F412Vx */ +#endif /* STM32F40xxx || STM32F41xxx || STM32F42xxx || STM32F43xxx || \ + STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx */ -#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ - || defined(STM32F446xx) || defined(STM32F469xx) || defined(STM32F479xx) +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || \ + defined(STM32F439xx) || defined(STM32F446xx) || defined(STM32F469xx) || \ + defined(STM32F479xx) /* #define DATA_IN_ExtSDRAM */ -#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F446xx || STM32F469xx ||\ - STM32F479xx */ +#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || \ + STM32F446xx || STM32F469xx || STM32F479xx */ /*!< Uncomment the following line if you need to relocate your vector Table in Internal SRAM. */ /* #define VECT_TAB_SRAM */ -#define VECT_TAB_OFFSET 0x00 /*!< Vector Table base offset field. - This value must be a multiple of 0x200. */ +#define VECT_TAB_OFFSET \ + 0x00 /*!< Vector Table base offset field. \ + This value must be a multiple of 0x200. */ /******************************************************************************/ /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Private_Macros - * @{ - */ -#ifdef USE_FULL_ASSERT + * @{ + */ +#ifdef USE_FULL_ASSERT /** - * @brief Reports the name of the source file and the source line number - * where the assert_param error has occurred. - * @param file: pointer to the source file name - * @param line: assert_param error line source number - * @retval None - */ -void assert_failed( - uint8_t * file, - uint32_t line) + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(uint8_t *file, uint32_t line) { - /* User can add his own implementation to report the file name and line number, - ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ + /* User can add his own implementation to report the file name and line + number, ex: printf("Wrong parameters value: file %s on line %d\r\n", + file, line) */ /* Infinite loop */ - while (1) { - } + while (1) { } } #endif /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Private_Variables - * @{ - */ - /* This variable is updated in three ways: - 1) by calling CMSIS function SystemCoreClockUpdate() - 2) by calling HAL API function HAL_RCC_GetHCLKFreq() - 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency - Note: If you use this function to configure the system clock; then there - is no need to call the 2 first functions listed above, since SystemCoreClock - variable is updated automatically. - */ + * @{ + */ +/* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetHCLKFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock + frequency Note: If you use this function to configure the system clock; then + there is no need to call the 2 first functions listed above, since + SystemCoreClock variable is updated automatically. +*/ uint32_t SystemCoreClock = 16000000; -const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; -const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4}; +const uint8_t AHBPrescTable[16] = { 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 6, 7, 8, 9 }; +const uint8_t APBPrescTable[8] = { 0, 0, 0, 0, 1, 2, 3, 4 }; /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Private_FunctionPrototypes - * @{ - */ + * @{ + */ -#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM) - static void SystemInit_ExtMemCtl(void); +#if defined(DATA_IN_ExtSRAM) || defined(DATA_IN_ExtSDRAM) +static void SystemInit_ExtMemCtl(void); #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */ /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Private_Functions - * @{ - */ + * @{ + */ /** - * @brief Setup the microcontroller system - * Initialize the FPU setting, vector table location and External memory - * configuration. - * @param None - * @retval None - */ + * @brief Setup the microcontroller system + * Initialize the FPU setting, vector table location and External memory + * configuration. + * @param None + * @retval None + */ void SystemInit(void) { - /* FPU settings ------------------------------------------------------------*/ - #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) - SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ - #endif +/* FPU settings ------------------------------------------------------------*/ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= + ((3UL << 10 * 2) | (3UL << 11 * 2)); /* set CP10 and CP11 Full Access */ +#endif -#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM) - SystemInit_ExtMemCtl(); +#if defined(DATA_IN_ExtSRAM) || defined(DATA_IN_ExtSDRAM) + SystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */ - /* Configure the Vector Table location add offset address ------------------*/ + /* Configure the Vector Table location add offset address + * ------------------*/ #ifdef VECT_TAB_SRAM - SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ + SCB->VTOR = SRAM_BASE | + VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ #else - SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ + SCB->VTOR = FLASH_BASE | + VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ #endif } /** - * @brief Update SystemCoreClock variable according to Clock Register Values. - * The SystemCoreClock variable contains the core clock (HCLK), it can - * be used by the user application to setup the SysTick timer or configure - * other parameters. - * - * @note Each time the core clock (HCLK) changes, this function must be called - * to update SystemCoreClock variable value. Otherwise, any configuration - * based on this variable will be incorrect. - * - * @note - The system frequency computed by this function is not the real - * frequency in the chip. It is calculated based on the predefined - * constant and the selected clock source: - * - * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) - * - * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) - * - * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) - * or HSI_VALUE(*) multiplied/divided by the PLL factors. - * - * (*) HSI_VALUE is a constant defined in stm32f4xx_hal_conf.h file (default value - * 16 MHz) but the real value may vary depending on the variations - * in voltage and temperature. - * - * (**) HSE_VALUE is a constant defined in stm32f4xx_hal_conf.h file (its value - * depends on the application requirements), user has to ensure that HSE_VALUE - * is same as the real frequency of the crystal used. Otherwise, this function - * may have wrong result. - * - * - The result of this function could be not correct when using fractional - * value for HSE crystal. - * - * @param None - * @retval None - */ + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or + * configure other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any + * configuration based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the + * HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the + * HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the + * HSE_VALUE(**) or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f4xx_hal_conf.h file + * (default value 16 MHz) but the real value may vary depending on the + * variations in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f4xx_hal_conf.h file + * (its value depends on the application requirements), user has to ensure that + * HSE_VALUE is same as the real frequency of the crystal used. Otherwise, this + * function may have wrong result. + * + * - The result of this function could be not correct when using + * fractional value for HSE crystal. + * + * @param None + * @retval None + */ void SystemCoreClockUpdate(void) { - uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; + uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; - /* Get SYSCLK source -------------------------------------------------------*/ - tmp = RCC->CFGR & RCC_CFGR_SWS; + /* Get SYSCLK source + * -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; - switch (tmp) - { - case 0x00: /* HSI used as system clock source */ - SystemCoreClock = HSI_VALUE; - break; - case 0x04: /* HSE used as system clock source */ - SystemCoreClock = HSE_VALUE; - break; - case 0x08: /* PLL used as system clock source */ + switch (tmp) { + case 0x00: /* HSI used as system clock source */ + SystemCoreClock = HSI_VALUE; + break; + case 0x04: /* HSE used as system clock source */ + SystemCoreClock = HSE_VALUE; + break; + case 0x08: /* PLL used as system clock source */ - /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N - SYSCLK = PLL_VCO / PLL_P - */ - pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; - pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; + /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N + SYSCLK = PLL_VCO / PLL_P + */ + pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; + pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; - if (pllsource != 0) - { - /* HSE used as PLL clock source */ - pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); - } - else - { - /* HSI used as PLL clock source */ - pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); - } + if (pllsource != 0) { + /* HSE used as PLL clock source */ + pllvco = (HSE_VALUE / pllm) * + ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } else { + /* HSI used as PLL clock source */ + pllvco = (HSI_VALUE / pllm) * + ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); + } - pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; - SystemCoreClock = pllvco/pllp; - break; - default: - SystemCoreClock = HSI_VALUE; - break; - } - /* Compute HCLK frequency --------------------------------------------------*/ - /* Get HCLK prescaler */ - tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; - /* HCLK frequency */ - SystemCoreClock >>= tmp; + pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >> 16) + 1) * 2; + SystemCoreClock = pllvco / pllp; + break; + default: + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK frequency + * --------------------------------------------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK frequency */ + SystemCoreClock >>= tmp; } /* Private define ------------------------------------------------------------*/ -#define LSE_FAIL_FLAG 0x80 -#define LSE_PASS_FLAG 0x100 +#define LSE_FAIL_FLAG 0x80 +#define LSE_PASS_FLAG 0x100 /** - * @brief This function provides delay (in milliseconds) based on CPU cycles method. - * @param mdelay specifies the delay time length, in milliseconds. - * @retval None - */ + * @brief This function provides delay (in milliseconds) based on CPU cycles + * method. + * @param mdelay specifies the delay time length, in milliseconds. + * @retval None + */ static void System_Delay_Milliseconds(uint32_t mdelay) { - __IO uint32_t Delay = mdelay * (SystemCoreClock / 8U / 1000U); - do - { - __NOP(); - } - while (Delay --); + __IO uint32_t Delay = mdelay * (SystemCoreClock / 8U / 1000U); + do { + __NOP(); + } while (Delay--); } /** - * @brief This function configures the LSE - */ -void System_LSE_Init( - void) + * @brief This function configures the LSE + */ +void System_LSE_Init(void) { uint32_t LSE_Delay = 0; @@ -346,478 +356,488 @@ void System_LSE_Init( } } -#if defined (DATA_IN_ExtSRAM) && defined (DATA_IN_ExtSDRAM) -#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ - || defined(STM32F469xx) || defined(STM32F479xx) +#if defined(DATA_IN_ExtSRAM) && defined(DATA_IN_ExtSDRAM) +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || \ + defined(STM32F439xx) || defined(STM32F469xx) || defined(STM32F479xx) /** - * @brief Setup the external memory controller. - * Called in startup_stm32f4xx.s before jump to main. - * This function configures the external memories (SRAM/SDRAM) - * This SRAM/SDRAM will be used as program data memory (including heap and stack). - * @param None - * @retval None - */ + * @brief Setup the external memory controller. + * Called in startup_stm32f4xx.s before jump to main. + * This function configures the external memories (SRAM/SDRAM) + * This SRAM/SDRAM will be used as program data memory (including heap + * and stack). + * @param None + * @retval None + */ void SystemInit_ExtMemCtl(void) { - __IO uint32_t tmp = 0x00; + __IO uint32_t tmp = 0x00; - register uint32_t tmpreg = 0, timeout = 0xFFFF; - register __IO uint32_t index; + register uint32_t tmpreg = 0, timeout = 0xFFFF; + register __IO uint32_t index; - /* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface clock */ - RCC->AHB1ENR |= 0x000001F8; + /* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface clock + */ + RCC->AHB1ENR |= 0x000001F8; - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN); + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN); - /* Connect PDx pins to FMC Alternate function */ - GPIOD->AFR[0] = 0x00CCC0CC; - GPIOD->AFR[1] = 0xCCCCCCCC; - /* Configure PDx pins in Alternate function mode */ - GPIOD->MODER = 0xAAAA0A8A; - /* Configure PDx pins speed to 100 MHz */ - GPIOD->OSPEEDR = 0xFFFF0FCF; - /* Configure PDx pins Output type to push-pull */ - GPIOD->OTYPER = 0x00000000; - /* No pull-up, pull-down for PDx pins */ - GPIOD->PUPDR = 0x00000000; + /* Connect PDx pins to FMC Alternate function */ + GPIOD->AFR[0] = 0x00CCC0CC; + GPIOD->AFR[1] = 0xCCCCCCCC; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xAAAA0A8A; + /* Configure PDx pins speed to 100 MHz */ + GPIOD->OSPEEDR = 0xFFFF0FCF; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; - /* Connect PEx pins to FMC Alternate function */ - GPIOE->AFR[0] = 0xC00CC0CC; - GPIOE->AFR[1] = 0xCCCCCCCC; - /* Configure PEx pins in Alternate function mode */ - GPIOE->MODER = 0xAAAA828A; - /* Configure PEx pins speed to 100 MHz */ - GPIOE->OSPEEDR = 0xFFFFC3CF; - /* Configure PEx pins Output type to push-pull */ - GPIOE->OTYPER = 0x00000000; - /* No pull-up, pull-down for PEx pins */ - GPIOE->PUPDR = 0x00000000; + /* Connect PEx pins to FMC Alternate function */ + GPIOE->AFR[0] = 0xC00CC0CC; + GPIOE->AFR[1] = 0xCCCCCCCC; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xAAAA828A; + /* Configure PEx pins speed to 100 MHz */ + GPIOE->OSPEEDR = 0xFFFFC3CF; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; - /* Connect PFx pins to FMC Alternate function */ - GPIOF->AFR[0] = 0xCCCCCCCC; - GPIOF->AFR[1] = 0xCCCCCCCC; - /* Configure PFx pins in Alternate function mode */ - GPIOF->MODER = 0xAA800AAA; - /* Configure PFx pins speed to 50 MHz */ - GPIOF->OSPEEDR = 0xAA800AAA; - /* Configure PFx pins Output type to push-pull */ - GPIOF->OTYPER = 0x00000000; - /* No pull-up, pull-down for PFx pins */ - GPIOF->PUPDR = 0x00000000; + /* Connect PFx pins to FMC Alternate function */ + GPIOF->AFR[0] = 0xCCCCCCCC; + GPIOF->AFR[1] = 0xCCCCCCCC; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xAA800AAA; + /* Configure PFx pins speed to 50 MHz */ + GPIOF->OSPEEDR = 0xAA800AAA; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; - /* Connect PGx pins to FMC Alternate function */ - GPIOG->AFR[0] = 0xCCCCCCCC; - GPIOG->AFR[1] = 0xCCCCCCCC; - /* Configure PGx pins in Alternate function mode */ - GPIOG->MODER = 0xAAAAAAAA; - /* Configure PGx pins speed to 50 MHz */ - GPIOG->OSPEEDR = 0xAAAAAAAA; - /* Configure PGx pins Output type to push-pull */ - GPIOG->OTYPER = 0x00000000; - /* No pull-up, pull-down for PGx pins */ - GPIOG->PUPDR = 0x00000000; + /* Connect PGx pins to FMC Alternate function */ + GPIOG->AFR[0] = 0xCCCCCCCC; + GPIOG->AFR[1] = 0xCCCCCCCC; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0xAAAAAAAA; + /* Configure PGx pins speed to 50 MHz */ + GPIOG->OSPEEDR = 0xAAAAAAAA; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; - /* Connect PHx pins to FMC Alternate function */ - GPIOH->AFR[0] = 0x00C0CC00; - GPIOH->AFR[1] = 0xCCCCCCCC; - /* Configure PHx pins in Alternate function mode */ - GPIOH->MODER = 0xAAAA08A0; - /* Configure PHx pins speed to 50 MHz */ - GPIOH->OSPEEDR = 0xAAAA08A0; - /* Configure PHx pins Output type to push-pull */ - GPIOH->OTYPER = 0x00000000; - /* No pull-up, pull-down for PHx pins */ - GPIOH->PUPDR = 0x00000000; + /* Connect PHx pins to FMC Alternate function */ + GPIOH->AFR[0] = 0x00C0CC00; + GPIOH->AFR[1] = 0xCCCCCCCC; + /* Configure PHx pins in Alternate function mode */ + GPIOH->MODER = 0xAAAA08A0; + /* Configure PHx pins speed to 50 MHz */ + GPIOH->OSPEEDR = 0xAAAA08A0; + /* Configure PHx pins Output type to push-pull */ + GPIOH->OTYPER = 0x00000000; + /* No pull-up, pull-down for PHx pins */ + GPIOH->PUPDR = 0x00000000; - /* Connect PIx pins to FMC Alternate function */ - GPIOI->AFR[0] = 0xCCCCCCCC; - GPIOI->AFR[1] = 0x00000CC0; - /* Configure PIx pins in Alternate function mode */ - GPIOI->MODER = 0x0028AAAA; - /* Configure PIx pins speed to 50 MHz */ - GPIOI->OSPEEDR = 0x0028AAAA; - /* Configure PIx pins Output type to push-pull */ - GPIOI->OTYPER = 0x00000000; - /* No pull-up, pull-down for PIx pins */ - GPIOI->PUPDR = 0x00000000; + /* Connect PIx pins to FMC Alternate function */ + GPIOI->AFR[0] = 0xCCCCCCCC; + GPIOI->AFR[1] = 0x00000CC0; + /* Configure PIx pins in Alternate function mode */ + GPIOI->MODER = 0x0028AAAA; + /* Configure PIx pins speed to 50 MHz */ + GPIOI->OSPEEDR = 0x0028AAAA; + /* Configure PIx pins Output type to push-pull */ + GPIOI->OTYPER = 0x00000000; + /* No pull-up, pull-down for PIx pins */ + GPIOI->PUPDR = 0x00000000; -/*-- FMC Configuration -------------------------------------------------------*/ - /* Enable the FMC interface clock */ - RCC->AHB3ENR |= 0x00000001; - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); + /*-- FMC Configuration + * -------------------------------------------------------*/ + /* Enable the FMC interface clock */ + RCC->AHB3ENR |= 0x00000001; + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); - FMC_Bank5_6->SDCR[0] = 0x000019E4; - FMC_Bank5_6->SDTR[0] = 0x01115351; + FMC_Bank5_6->SDCR[0] = 0x000019E4; + FMC_Bank5_6->SDTR[0] = 0x01115351; - /* SDRAM initialization sequence */ - /* Clock enable command */ - FMC_Bank5_6->SDCMR = 0x00000011; - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - while((tmpreg != 0) && (timeout-- > 0)) - { + /* SDRAM initialization sequence */ + /* Clock enable command */ + FMC_Bank5_6->SDCMR = 0x00000011; tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* Delay */ - for (index = 0; index<1000; index++); + /* Delay */ + for (index = 0; index < 1000; index++) + ; - /* PALL command */ - FMC_Bank5_6->SDCMR = 0x00000012; - timeout = 0xFFFF; - while((tmpreg != 0) && (timeout-- > 0)) - { - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + /* PALL command */ + FMC_Bank5_6->SDCMR = 0x00000012; + timeout = 0xFFFF; + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* Auto refresh command */ - FMC_Bank5_6->SDCMR = 0x00000073; - timeout = 0xFFFF; - while((tmpreg != 0) && (timeout-- > 0)) - { - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + /* Auto refresh command */ + FMC_Bank5_6->SDCMR = 0x00000073; + timeout = 0xFFFF; + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* MRD register program */ - FMC_Bank5_6->SDCMR = 0x00046014; - timeout = 0xFFFF; - while((tmpreg != 0) && (timeout-- > 0)) - { - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + /* MRD register program */ + FMC_Bank5_6->SDCMR = 0x00046014; + timeout = 0xFFFF; + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* Set refresh count */ - tmpreg = FMC_Bank5_6->SDRTR; - FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C<<1)); + /* Set refresh count */ + tmpreg = FMC_Bank5_6->SDRTR; + FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C << 1)); - /* Disable write protection */ - tmpreg = FMC_Bank5_6->SDCR[0]; - FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF); + /* Disable write protection */ + tmpreg = FMC_Bank5_6->SDCR[0]; + FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF); -#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx) - /* Configure and enable Bank1_SRAM2 */ - FMC_Bank1->BTCR[2] = 0x00001011; - FMC_Bank1->BTCR[3] = 0x00000201; - FMC_Bank1E->BWTR[2] = 0x0fffffff; +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || \ + defined(STM32F439xx) + /* Configure and enable Bank1_SRAM2 */ + FMC_Bank1->BTCR[2] = 0x00001011; + FMC_Bank1->BTCR[3] = 0x00000201; + FMC_Bank1E->BWTR[2] = 0x0fffffff; #endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx */ #if defined(STM32F469xx) || defined(STM32F479xx) - /* Configure and enable Bank1_SRAM2 */ - FMC_Bank1->BTCR[2] = 0x00001091; - FMC_Bank1->BTCR[3] = 0x00110212; - FMC_Bank1E->BWTR[2] = 0x0fffffff; + /* Configure and enable Bank1_SRAM2 */ + FMC_Bank1->BTCR[2] = 0x00001091; + FMC_Bank1->BTCR[3] = 0x00110212; + FMC_Bank1E->BWTR[2] = 0x0fffffff; #endif /* STM32F469xx || STM32F479xx */ - (void)(tmp); + (void)(tmp); } -#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */ -#elif defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM) +#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || \ + STM32F469xx || STM32F479xx */ +#elif defined(DATA_IN_ExtSRAM) || defined(DATA_IN_ExtSDRAM) /** - * @brief Setup the external memory controller. - * Called in startup_stm32f4xx.s before jump to main. - * This function configures the external memories (SRAM/SDRAM) - * This SRAM/SDRAM will be used as program data memory (including heap and stack). - * @param None - * @retval None - */ + * @brief Setup the external memory controller. + * Called in startup_stm32f4xx.s before jump to main. + * This function configures the external memories (SRAM/SDRAM) + * This SRAM/SDRAM will be used as program data memory (including heap + * and stack). + * @param None + * @retval None + */ void SystemInit_ExtMemCtl(void) { - __IO uint32_t tmp = 0x00; -#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ - || defined(STM32F446xx) || defined(STM32F469xx) || defined(STM32F479xx) -#if defined (DATA_IN_ExtSDRAM) - register uint32_t tmpreg = 0, timeout = 0xFFFF; - register __IO uint32_t index; + __IO uint32_t tmp = 0x00; +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || \ + defined(STM32F439xx) || defined(STM32F446xx) || defined(STM32F469xx) || \ + defined(STM32F479xx) +#if defined(DATA_IN_ExtSDRAM) + register uint32_t tmpreg = 0, timeout = 0xFFFF; + register __IO uint32_t index; #if defined(STM32F446xx) - /* Enable GPIOA, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG interface - clock */ - RCC->AHB1ENR |= 0x0000007D; + /* Enable GPIOA, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG interface + clock */ + RCC->AHB1ENR |= 0x0000007D; #else - /* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface - clock */ - RCC->AHB1ENR |= 0x000001F8; + /* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface + clock */ + RCC->AHB1ENR |= 0x000001F8; #endif /* STM32F446xx */ - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN); + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN); #if defined(STM32F446xx) - /* Connect PAx pins to FMC Alternate function */ - GPIOA->AFR[0] |= 0xC0000000; - GPIOA->AFR[1] |= 0x00000000; - /* Configure PDx pins in Alternate function mode */ - GPIOA->MODER |= 0x00008000; - /* Configure PDx pins speed to 50 MHz */ - GPIOA->OSPEEDR |= 0x00008000; - /* Configure PDx pins Output type to push-pull */ - GPIOA->OTYPER |= 0x00000000; - /* No pull-up, pull-down for PDx pins */ - GPIOA->PUPDR |= 0x00000000; + /* Connect PAx pins to FMC Alternate function */ + GPIOA->AFR[0] |= 0xC0000000; + GPIOA->AFR[1] |= 0x00000000; + /* Configure PDx pins in Alternate function mode */ + GPIOA->MODER |= 0x00008000; + /* Configure PDx pins speed to 50 MHz */ + GPIOA->OSPEEDR |= 0x00008000; + /* Configure PDx pins Output type to push-pull */ + GPIOA->OTYPER |= 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOA->PUPDR |= 0x00000000; - /* Connect PCx pins to FMC Alternate function */ - GPIOC->AFR[0] |= 0x00CC0000; - GPIOC->AFR[1] |= 0x00000000; - /* Configure PDx pins in Alternate function mode */ - GPIOC->MODER |= 0x00000A00; - /* Configure PDx pins speed to 50 MHz */ - GPIOC->OSPEEDR |= 0x00000A00; - /* Configure PDx pins Output type to push-pull */ - GPIOC->OTYPER |= 0x00000000; - /* No pull-up, pull-down for PDx pins */ - GPIOC->PUPDR |= 0x00000000; + /* Connect PCx pins to FMC Alternate function */ + GPIOC->AFR[0] |= 0x00CC0000; + GPIOC->AFR[1] |= 0x00000000; + /* Configure PDx pins in Alternate function mode */ + GPIOC->MODER |= 0x00000A00; + /* Configure PDx pins speed to 50 MHz */ + GPIOC->OSPEEDR |= 0x00000A00; + /* Configure PDx pins Output type to push-pull */ + GPIOC->OTYPER |= 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOC->PUPDR |= 0x00000000; #endif /* STM32F446xx */ - /* Connect PDx pins to FMC Alternate function */ - GPIOD->AFR[0] = 0x000000CC; - GPIOD->AFR[1] = 0xCC000CCC; - /* Configure PDx pins in Alternate function mode */ - GPIOD->MODER = 0xA02A000A; - /* Configure PDx pins speed to 50 MHz */ - GPIOD->OSPEEDR = 0xA02A000A; - /* Configure PDx pins Output type to push-pull */ - GPIOD->OTYPER = 0x00000000; - /* No pull-up, pull-down for PDx pins */ - GPIOD->PUPDR = 0x00000000; + /* Connect PDx pins to FMC Alternate function */ + GPIOD->AFR[0] = 0x000000CC; + GPIOD->AFR[1] = 0xCC000CCC; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xA02A000A; + /* Configure PDx pins speed to 50 MHz */ + GPIOD->OSPEEDR = 0xA02A000A; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; - /* Connect PEx pins to FMC Alternate function */ - GPIOE->AFR[0] = 0xC00000CC; - GPIOE->AFR[1] = 0xCCCCCCCC; - /* Configure PEx pins in Alternate function mode */ - GPIOE->MODER = 0xAAAA800A; - /* Configure PEx pins speed to 50 MHz */ - GPIOE->OSPEEDR = 0xAAAA800A; - /* Configure PEx pins Output type to push-pull */ - GPIOE->OTYPER = 0x00000000; - /* No pull-up, pull-down for PEx pins */ - GPIOE->PUPDR = 0x00000000; + /* Connect PEx pins to FMC Alternate function */ + GPIOE->AFR[0] = 0xC00000CC; + GPIOE->AFR[1] = 0xCCCCCCCC; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xAAAA800A; + /* Configure PEx pins speed to 50 MHz */ + GPIOE->OSPEEDR = 0xAAAA800A; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; - /* Connect PFx pins to FMC Alternate function */ - GPIOF->AFR[0] = 0xCCCCCCCC; - GPIOF->AFR[1] = 0xCCCCCCCC; - /* Configure PFx pins in Alternate function mode */ - GPIOF->MODER = 0xAA800AAA; - /* Configure PFx pins speed to 50 MHz */ - GPIOF->OSPEEDR = 0xAA800AAA; - /* Configure PFx pins Output type to push-pull */ - GPIOF->OTYPER = 0x00000000; - /* No pull-up, pull-down for PFx pins */ - GPIOF->PUPDR = 0x00000000; + /* Connect PFx pins to FMC Alternate function */ + GPIOF->AFR[0] = 0xCCCCCCCC; + GPIOF->AFR[1] = 0xCCCCCCCC; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xAA800AAA; + /* Configure PFx pins speed to 50 MHz */ + GPIOF->OSPEEDR = 0xAA800AAA; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; - /* Connect PGx pins to FMC Alternate function */ - GPIOG->AFR[0] = 0xCCCCCCCC; - GPIOG->AFR[1] = 0xCCCCCCCC; - /* Configure PGx pins in Alternate function mode */ - GPIOG->MODER = 0xAAAAAAAA; - /* Configure PGx pins speed to 50 MHz */ - GPIOG->OSPEEDR = 0xAAAAAAAA; - /* Configure PGx pins Output type to push-pull */ - GPIOG->OTYPER = 0x00000000; - /* No pull-up, pull-down for PGx pins */ - GPIOG->PUPDR = 0x00000000; + /* Connect PGx pins to FMC Alternate function */ + GPIOG->AFR[0] = 0xCCCCCCCC; + GPIOG->AFR[1] = 0xCCCCCCCC; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0xAAAAAAAA; + /* Configure PGx pins speed to 50 MHz */ + GPIOG->OSPEEDR = 0xAAAAAAAA; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; -#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ - || defined(STM32F469xx) || defined(STM32F479xx) - /* Connect PHx pins to FMC Alternate function */ - GPIOH->AFR[0] = 0x00C0CC00; - GPIOH->AFR[1] = 0xCCCCCCCC; - /* Configure PHx pins in Alternate function mode */ - GPIOH->MODER = 0xAAAA08A0; - /* Configure PHx pins speed to 50 MHz */ - GPIOH->OSPEEDR = 0xAAAA08A0; - /* Configure PHx pins Output type to push-pull */ - GPIOH->OTYPER = 0x00000000; - /* No pull-up, pull-down for PHx pins */ - GPIOH->PUPDR = 0x00000000; +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || \ + defined(STM32F439xx) || defined(STM32F469xx) || defined(STM32F479xx) + /* Connect PHx pins to FMC Alternate function */ + GPIOH->AFR[0] = 0x00C0CC00; + GPIOH->AFR[1] = 0xCCCCCCCC; + /* Configure PHx pins in Alternate function mode */ + GPIOH->MODER = 0xAAAA08A0; + /* Configure PHx pins speed to 50 MHz */ + GPIOH->OSPEEDR = 0xAAAA08A0; + /* Configure PHx pins Output type to push-pull */ + GPIOH->OTYPER = 0x00000000; + /* No pull-up, pull-down for PHx pins */ + GPIOH->PUPDR = 0x00000000; - /* Connect PIx pins to FMC Alternate function */ - GPIOI->AFR[0] = 0xCCCCCCCC; - GPIOI->AFR[1] = 0x00000CC0; - /* Configure PIx pins in Alternate function mode */ - GPIOI->MODER = 0x0028AAAA; - /* Configure PIx pins speed to 50 MHz */ - GPIOI->OSPEEDR = 0x0028AAAA; - /* Configure PIx pins Output type to push-pull */ - GPIOI->OTYPER = 0x00000000; - /* No pull-up, pull-down for PIx pins */ - GPIOI->PUPDR = 0x00000000; -#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */ + /* Connect PIx pins to FMC Alternate function */ + GPIOI->AFR[0] = 0xCCCCCCCC; + GPIOI->AFR[1] = 0x00000CC0; + /* Configure PIx pins in Alternate function mode */ + GPIOI->MODER = 0x0028AAAA; + /* Configure PIx pins speed to 50 MHz */ + GPIOI->OSPEEDR = 0x0028AAAA; + /* Configure PIx pins Output type to push-pull */ + GPIOI->OTYPER = 0x00000000; + /* No pull-up, pull-down for PIx pins */ + GPIOI->PUPDR = 0x00000000; +#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || \ + STM32F469xx || STM32F479xx */ -/*-- FMC Configuration -------------------------------------------------------*/ - /* Enable the FMC interface clock */ - RCC->AHB3ENR |= 0x00000001; - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); + /*-- FMC Configuration + * -------------------------------------------------------*/ + /* Enable the FMC interface clock */ + RCC->AHB3ENR |= 0x00000001; + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); - /* Configure and enable SDRAM bank1 */ + /* Configure and enable SDRAM bank1 */ #if defined(STM32F446xx) - FMC_Bank5_6->SDCR[0] = 0x00001954; + FMC_Bank5_6->SDCR[0] = 0x00001954; #else - FMC_Bank5_6->SDCR[0] = 0x000019E4; + FMC_Bank5_6->SDCR[0] = 0x000019E4; #endif /* STM32F446xx */ - FMC_Bank5_6->SDTR[0] = 0x01115351; + FMC_Bank5_6->SDTR[0] = 0x01115351; - /* SDRAM initialization sequence */ - /* Clock enable command */ - FMC_Bank5_6->SDCMR = 0x00000011; - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - while((tmpreg != 0) && (timeout-- > 0)) - { + /* SDRAM initialization sequence */ + /* Clock enable command */ + FMC_Bank5_6->SDCMR = 0x00000011; tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* Delay */ - for (index = 0; index<1000; index++); + /* Delay */ + for (index = 0; index < 1000; index++) + ; - /* PALL command */ - FMC_Bank5_6->SDCMR = 0x00000012; - timeout = 0xFFFF; - while((tmpreg != 0) && (timeout-- > 0)) - { - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + /* PALL command */ + FMC_Bank5_6->SDCMR = 0x00000012; + timeout = 0xFFFF; + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* Auto refresh command */ + /* Auto refresh command */ #if defined(STM32F446xx) - FMC_Bank5_6->SDCMR = 0x000000F3; + FMC_Bank5_6->SDCMR = 0x000000F3; #else - FMC_Bank5_6->SDCMR = 0x00000073; + FMC_Bank5_6->SDCMR = 0x00000073; #endif /* STM32F446xx */ - timeout = 0xFFFF; - while((tmpreg != 0) && (timeout-- > 0)) - { - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + timeout = 0xFFFF; + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* MRD register program */ + /* MRD register program */ #if defined(STM32F446xx) - FMC_Bank5_6->SDCMR = 0x00044014; + FMC_Bank5_6->SDCMR = 0x00044014; #else - FMC_Bank5_6->SDCMR = 0x00046014; + FMC_Bank5_6->SDCMR = 0x00046014; #endif /* STM32F446xx */ - timeout = 0xFFFF; - while((tmpreg != 0) && (timeout-- > 0)) - { - tmpreg = FMC_Bank5_6->SDSR & 0x00000020; - } + timeout = 0xFFFF; + while ((tmpreg != 0) && (timeout-- > 0)) { + tmpreg = FMC_Bank5_6->SDSR & 0x00000020; + } - /* Set refresh count */ - tmpreg = FMC_Bank5_6->SDRTR; + /* Set refresh count */ + tmpreg = FMC_Bank5_6->SDRTR; #if defined(STM32F446xx) - FMC_Bank5_6->SDRTR = (tmpreg | (0x0000050C<<1)); + FMC_Bank5_6->SDRTR = (tmpreg | (0x0000050C << 1)); #else - FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C<<1)); + FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C << 1)); #endif /* STM32F446xx */ - /* Disable write protection */ - tmpreg = FMC_Bank5_6->SDCR[0]; - FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF); + /* Disable write protection */ + tmpreg = FMC_Bank5_6->SDCR[0]; + FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF); #endif /* DATA_IN_ExtSDRAM */ -#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F446xx || STM32F469xx || STM32F479xx */ +#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || \ + STM32F446xx || STM32F469xx || STM32F479xx */ -#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\ - || defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\ - || defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) +#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || \ + defined(STM32F417xx) || defined(STM32F427xx) || defined(STM32F437xx) || \ + defined(STM32F429xx) || defined(STM32F439xx) || defined(STM32F469xx) || \ + defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) #if defined(DATA_IN_ExtSRAM) -/*-- GPIOs Configuration -----------------------------------------------------*/ - /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ - RCC->AHB1ENR |= 0x00000078; - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN); + /*-- GPIOs Configuration + * -----------------------------------------------------*/ + /* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */ + RCC->AHB1ENR |= 0x00000078; + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN); - /* Connect PDx pins to FMC Alternate function */ - GPIOD->AFR[0] = 0x00CCC0CC; - GPIOD->AFR[1] = 0xCCCCCCCC; - /* Configure PDx pins in Alternate function mode */ - GPIOD->MODER = 0xAAAA0A8A; - /* Configure PDx pins speed to 100 MHz */ - GPIOD->OSPEEDR = 0xFFFF0FCF; - /* Configure PDx pins Output type to push-pull */ - GPIOD->OTYPER = 0x00000000; - /* No pull-up, pull-down for PDx pins */ - GPIOD->PUPDR = 0x00000000; + /* Connect PDx pins to FMC Alternate function */ + GPIOD->AFR[0] = 0x00CCC0CC; + GPIOD->AFR[1] = 0xCCCCCCCC; + /* Configure PDx pins in Alternate function mode */ + GPIOD->MODER = 0xAAAA0A8A; + /* Configure PDx pins speed to 100 MHz */ + GPIOD->OSPEEDR = 0xFFFF0FCF; + /* Configure PDx pins Output type to push-pull */ + GPIOD->OTYPER = 0x00000000; + /* No pull-up, pull-down for PDx pins */ + GPIOD->PUPDR = 0x00000000; - /* Connect PEx pins to FMC Alternate function */ - GPIOE->AFR[0] = 0xC00CC0CC; - GPIOE->AFR[1] = 0xCCCCCCCC; - /* Configure PEx pins in Alternate function mode */ - GPIOE->MODER = 0xAAAA828A; - /* Configure PEx pins speed to 100 MHz */ - GPIOE->OSPEEDR = 0xFFFFC3CF; - /* Configure PEx pins Output type to push-pull */ - GPIOE->OTYPER = 0x00000000; - /* No pull-up, pull-down for PEx pins */ - GPIOE->PUPDR = 0x00000000; + /* Connect PEx pins to FMC Alternate function */ + GPIOE->AFR[0] = 0xC00CC0CC; + GPIOE->AFR[1] = 0xCCCCCCCC; + /* Configure PEx pins in Alternate function mode */ + GPIOE->MODER = 0xAAAA828A; + /* Configure PEx pins speed to 100 MHz */ + GPIOE->OSPEEDR = 0xFFFFC3CF; + /* Configure PEx pins Output type to push-pull */ + GPIOE->OTYPER = 0x00000000; + /* No pull-up, pull-down for PEx pins */ + GPIOE->PUPDR = 0x00000000; - /* Connect PFx pins to FMC Alternate function */ - GPIOF->AFR[0] = 0x00CCCCCC; - GPIOF->AFR[1] = 0xCCCC0000; - /* Configure PFx pins in Alternate function mode */ - GPIOF->MODER = 0xAA000AAA; - /* Configure PFx pins speed to 100 MHz */ - GPIOF->OSPEEDR = 0xFF000FFF; - /* Configure PFx pins Output type to push-pull */ - GPIOF->OTYPER = 0x00000000; - /* No pull-up, pull-down for PFx pins */ - GPIOF->PUPDR = 0x00000000; + /* Connect PFx pins to FMC Alternate function */ + GPIOF->AFR[0] = 0x00CCCCCC; + GPIOF->AFR[1] = 0xCCCC0000; + /* Configure PFx pins in Alternate function mode */ + GPIOF->MODER = 0xAA000AAA; + /* Configure PFx pins speed to 100 MHz */ + GPIOF->OSPEEDR = 0xFF000FFF; + /* Configure PFx pins Output type to push-pull */ + GPIOF->OTYPER = 0x00000000; + /* No pull-up, pull-down for PFx pins */ + GPIOF->PUPDR = 0x00000000; - /* Connect PGx pins to FMC Alternate function */ - GPIOG->AFR[0] = 0x00CCCCCC; - GPIOG->AFR[1] = 0x000000C0; - /* Configure PGx pins in Alternate function mode */ - GPIOG->MODER = 0x00085AAA; - /* Configure PGx pins speed to 100 MHz */ - GPIOG->OSPEEDR = 0x000CAFFF; - /* Configure PGx pins Output type to push-pull */ - GPIOG->OTYPER = 0x00000000; - /* No pull-up, pull-down for PGx pins */ - GPIOG->PUPDR = 0x00000000; + /* Connect PGx pins to FMC Alternate function */ + GPIOG->AFR[0] = 0x00CCCCCC; + GPIOG->AFR[1] = 0x000000C0; + /* Configure PGx pins in Alternate function mode */ + GPIOG->MODER = 0x00085AAA; + /* Configure PGx pins speed to 100 MHz */ + GPIOG->OSPEEDR = 0x000CAFFF; + /* Configure PGx pins Output type to push-pull */ + GPIOG->OTYPER = 0x00000000; + /* No pull-up, pull-down for PGx pins */ + GPIOG->PUPDR = 0x00000000; -/*-- FMC/FSMC Configuration --------------------------------------------------*/ - /* Enable the FMC/FSMC interface clock */ - RCC->AHB3ENR |= 0x00000001; + /*-- FMC/FSMC Configuration + * --------------------------------------------------*/ + /* Enable the FMC/FSMC interface clock */ + RCC->AHB3ENR |= 0x00000001; -#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx) - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); - /* Configure and enable Bank1_SRAM2 */ - FMC_Bank1->BTCR[2] = 0x00001011; - FMC_Bank1->BTCR[3] = 0x00000201; - FMC_Bank1E->BWTR[2] = 0x0fffffff; +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || \ + defined(STM32F439xx) + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); + /* Configure and enable Bank1_SRAM2 */ + FMC_Bank1->BTCR[2] = 0x00001011; + FMC_Bank1->BTCR[3] = 0x00000201; + FMC_Bank1E->BWTR[2] = 0x0fffffff; #endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx */ #if defined(STM32F469xx) || defined(STM32F479xx) - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); - /* Configure and enable Bank1_SRAM2 */ - FMC_Bank1->BTCR[2] = 0x00001091; - FMC_Bank1->BTCR[3] = 0x00110212; - FMC_Bank1E->BWTR[2] = 0x0fffffff; + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN); + /* Configure and enable Bank1_SRAM2 */ + FMC_Bank1->BTCR[2] = 0x00001091; + FMC_Bank1->BTCR[3] = 0x00110212; + FMC_Bank1E->BWTR[2] = 0x0fffffff; #endif /* STM32F469xx || STM32F479xx */ -#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx)|| defined(STM32F417xx)\ - || defined(STM32F412Zx) || defined(STM32F412Vx) - /* Delay after an RCC peripheral clock enabling */ - tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN); - /* Configure and enable Bank1_SRAM2 */ - FSMC_Bank1->BTCR[2] = 0x00001011; - FSMC_Bank1->BTCR[3] = 0x00000201; - FSMC_Bank1E->BWTR[2] = 0x0FFFFFFF; -#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || STM32F412Zx || STM32F412Vx */ +#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || \ + defined(STM32F417xx) || defined(STM32F412Zx) || defined(STM32F412Vx) + /* Delay after an RCC peripheral clock enabling */ + tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN); + /* Configure and enable Bank1_SRAM2 */ + FSMC_Bank1->BTCR[2] = 0x00001011; + FSMC_Bank1->BTCR[3] = 0x00000201; + FSMC_Bank1E->BWTR[2] = 0x0FFFFFFF; +#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || \ + STM32F412Zx || STM32F412Vx */ #endif /* DATA_IN_ExtSRAM */ -#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || STM32F427xx || STM32F437xx ||\ - STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx */ - (void)(tmp); +#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || \ + STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || \ + STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx */ + (void)(tmp); } #endif /* DATA_IN_ExtSRAM && DATA_IN_ExtSDRAM */ /** - * @} - */ + * @} + */ /** - * @} - */ + * @} + */ /** - * @} - */ + * @} + */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f4xx/system_stm32f4xx.h b/ports/stm32f4xx/system_stm32f4xx.h index bc937b5e..b853c687 100644 --- a/ports/stm32f4xx/system_stm32f4xx.h +++ b/ports/stm32f4xx/system_stm32f4xx.h @@ -1,89 +1,87 @@ /** - ****************************************************************************** - * @file system_stm32f4xx.h - * @author MCD Application Team - * @version V1.0.0 - * @date 30-September-2011 - * @brief CMSIS Cortex-M4 Device System Source File for STM32F4xx devices. - ****************************************************************************** - * @attention - * - * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - * - *

© COPYRIGHT 2011 STMicroelectronics

- ****************************************************************************** - */ + ****************************************************************************** + * @file system_stm32f4xx.h + * @author MCD Application Team + * @version V1.0.0 + * @date 30-September-2011 + * @brief CMSIS Cortex-M4 Device System Source File for STM32F4xx devices. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ /** @addtogroup CMSIS - * @{ - */ + * @{ + */ /** @addtogroup stm32f4xx_system - * @{ - */ + * @{ + */ /** - * @brief Define to prevent recursive inclusion - */ + * @brief Define to prevent recursive inclusion + */ #ifndef __SYSTEM_STM32F4XX_H #define __SYSTEM_STM32F4XX_H #ifdef __cplusplus - extern "C" { +extern "C" { #endif /** @addtogroup STM32F4xx_System_Includes - * @{ - */ + * @{ + */ /** - * @} - */ - + * @} + */ /** @addtogroup STM32F4xx_System_Exported_types - * @{ - */ - -extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + * @{ + */ +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Exported_Constants - * @{ - */ + * @{ + */ /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Exported_Macros - * @{ - */ + * @{ + */ /** - * @} - */ + * @} + */ /** @addtogroup STM32F4xx_System_Exported_Functions - * @{ - */ + * @{ + */ extern void SystemInit(void); extern void SystemCoreClockUpdate(void); extern void System_LSE_Init(void); /** - * @} - */ + * @} + */ #ifdef __cplusplus } @@ -92,10 +90,10 @@ extern void System_LSE_Init(void); #endif /*__SYSTEM_STM32F4XX_H */ /** - * @} - */ + * @} + */ /** - * @} - */ + * @} + */ /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32f4xx/ubasic-port.c b/ports/stm32f4xx/ubasic-port.c new file mode 100644 index 00000000..11b9315b --- /dev/null +++ b/ports/stm32f4xx/ubasic-port.c @@ -0,0 +1,644 @@ +/** + * @file + * @brief Example uBASIC-Plus porting layer for the uBASIC-Plus interpreter + * for STM32F4xx NUCLIO board + * @author Steve Karg + * @date 2025 + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "bacnet/basic/object/ai.h" +#include "bacnet/basic/object/ao.h" +#include "bacnet/basic/object/av.h" +#include "bacnet/basic/object/bi.h" +#include "bacnet/basic/object/bo.h" +#include "bacnet/basic/object/bv.h" +#include "bacnet/basic/object/device.h" +#include "bacnet/basic/object/ms-input.h" +#include "bacnet/basic/object/mso.h" +#include "bacnet/basic/object/msv.h" +#include "bacnet/basic/program/ubasic/ubasic.h" +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/wp.h" +#include "led.h" + +#if defined(UBASIC_SCRIPT_PRINT_TO_SERIAL) +/** + * @brief Write a buffer to the serial port + * @param msg Pointer to the buffer to write + * @param n Number of bytes to write + */ +static void serial_write(const char *msg, uint16_t n) +{ + (void)msg; + (void)n; +} + +/** + * @brief Gather key presses until new-line is recieved or buffer is full + * @return return the next byte from the input stream, or EOF(-1) if no byte is + * available + */ +static int serial_getc(void) +{ + int ch = -1; + + return ch; +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) +static uint32_t Event_Mask; + +/** + * @brief Hardware event status bit + * @param bit Event bit + * @return 1 if the event is set, 0 otherwise + */ +static int8_t hw_event(uint8_t bit) +{ + if (bit < 32) { + if (Event_Mask & (1UL << bit)) { + return 1; // Event is set + } + } + + return 0; // Event is not set +} + +/** + * @brief Clear a hardware event state bit + * @param bit Event bit + */ +static void hw_event_clear(uint8_t bit) +{ + if (bit < 32) { + Event_Mask &= ~(1UL << bit); + } +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) +static uint8_t EEPROM_Buffer[256]; + +/** + * @brief Read some data from the EEPROM + * @param start_address EEPROM starting memory address + * @param buffer data to store + * @param length number of bytes of data to read + */ +static size_t +eepromRead(uint16_t start_address, uint8_t *buffer, uint16_t length) +{ + size_t bytes_read = 0; + uint16_t i = 0; + + for (i = 0; i < length; i++) { + if (start_address + i < sizeof(EEPROM_Buffer)) { + buffer[i] = EEPROM_Buffer[start_address + i]; + bytes_read++; + } else { + break; + } + } + + return bytes_read; +} + +/** + * @brief Write some data to the EEPROM + * @param start_address EEPROM starting memory address + * @param buffer data to send + * @param length number of bytes of data + */ +static size_t +eepromWrite(uint16_t start_address, uint8_t *buffer, uint16_t length) +{ + size_t bytes_written = 0; + uint16_t i = 0; + + for (i = 0; i < length; i++) { + if (start_address + i < sizeof(EEPROM_Buffer)) { + EEPROM_Buffer[start_address + i] = buffer[i]; + bytes_written++; + } else { + break; + } + } + + return bytes_written; +} + +#define UBASIC_FLASH_PAGE_SIZE 256 + +/** + * @brief Write a variable to the EEPROM + * @param Name Variable name + * @param Vartype Variable type + * @param datalen_bytes Data length in bytes + * @param dataptr Pointer to the data + */ +static void variable_write( + uint8_t Name, uint8_t vartype, uint8_t datalen_bytes, uint8_t *dataptr) +{ + // Calculate the starting address based on variable name + uint16_t start_address = Name * UBASIC_FLASH_PAGE_SIZE; + uint8_t buffer[UBASIC_FLASH_PAGE_SIZE]; + + // Prepare the buffer with the variable type and data length + buffer[0] = vartype; // First byte is the variable type + buffer[1] = datalen_bytes; // Second byte is the data length + for (uint8_t i = 0; i < datalen_bytes; i++) { + buffer[i + 2] = dataptr[i]; // Copy the actual data into the buffer + } + + // Write the buffer to EEPROM + eepromWrite(start_address, buffer, datalen_bytes + 2); +} + +/** + * @brief Read a variable from the EEPROM + * @param Name Variable name + * @param Vartype Variable type + * @param dataptr Pointer to store the data + * @param datalen Pointer to store the data length + */ +static void +variable_read(uint8_t Name, uint8_t vartype, uint8_t *dataptr, uint8_t *datalen) +{ + // Calculate the starting address based on variable name + uint8_t buffer[UBASIC_FLASH_PAGE_SIZE] = { 0 }; + uint16_t start_address = Name * UBASIC_FLASH_PAGE_SIZE; + + // Read the data from EEPROM + eepromRead(start_address, buffer, UBASIC_FLASH_PAGE_SIZE); + // Check if the variable type matches + if (buffer[0] == vartype) { + *datalen = buffer[1]; // Get the data length + for (uint8_t i = 0; i < *datalen; i++) { + dataptr[i] = + buffer[i + 2]; // Copy the actual data into the provided pointer + } + } else { + *datalen = 0; // If type does not match, set length to 0 + } +} +#endif + +#if ( \ + defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) || \ + defined(UBASIC_SCRIPT_HAVE_SLEEP) || \ + defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL)) +/* defined in mstimer.h */ +#endif + +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) +/** + * @brief Generate a random number + * @param size Size of the random number in bits + * @return Random number size-bits wide + */ +static uint32_t random_uint32(uint8_t size) +{ + uint32_t value = 0, grains = 0; + uint8_t k, i; + static bool initialized = false; + + if (!initialized) { + srand(0); + initialized = true; + } + for (k = 0; k < 4; k++) { + grains = 0; + for (i = 0; i < (size >> 1); i++) { + /* Two LS bits are most likely most random */ + grains |= (rand() & 0x00000003) << (2 * i); + } + value ^= grains; + } + + return value; +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_PWM_CHANNELS) +static int32_t dutycycle_pwm_ch[UBASIC_SCRIPT_HAVE_PWM_CHANNELS]; + +/** + * @brief Configure the PWM + * @param psc Prescaler + * @param per Period + */ +static void pwm_config(uint16_t psc, uint16_t per) +{ + (void)psc; + (void)per; +} + +/** + * @brief Write a value to the PWM + * @param ch Channel + * @param dutycycle Duty cycle + */ +static void pwm_write(uint8_t ch, int32_t dutycycle) +{ + if (ch < UBASIC_SCRIPT_HAVE_PWM_CHANNELS) { + dutycycle_pwm_ch[ch] = dutycycle; + } +} + +/** + * @brief Read a value from the PWM + * @param ch Channel + * @return Duty cycle + */ +static int32_t pwm_read(uint8_t ch) +{ + if (ch < UBASIC_SCRIPT_HAVE_PWM_CHANNELS) { + return dutycycle_pwm_ch[ch]; + } + return 0; +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) +/** + * @brief Configure the ADC + * @param sampletime Sample time + * @param nreads Number of reads + */ +static void adc_config(uint8_t sampletime, uint8_t nreads) +{ + (void)sampletime; + (void)nreads; +} + +/** + * @brief Read a value from the ADC + * @param channel Channel + * @return ADC value + */ +static int32_t adc_read(uint8_t channel) +{ + return (int32_t)random_uint32(12); +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) +/** + * @brief Configure the GPIO + * @param ch Channel + * @param mode Mode + * @param freq Frequency + */ +static void gpio_config(uint8_t ch, int8_t mode, uint8_t freq) +{ + (void)ch; + (void)mode; + (void)freq; +} + +/** + * @brief Write a value to the GPIO + * @param ch Channel + * @param pin_state Pin state + */ +static void gpio_write(uint8_t ch, uint8_t pin_state) +{ + switch (ch) { + case 1: + if (pin_state) { + led_on(LED_LD1); + } else { + led_off(LED_LD1); + } + break; + case 2: + if (pin_state) { + led_on(LED_LD2); + } else { + led_off(LED_LD2); + } + break; + default: + break; + } +} + +/** + * @brief Read a value from the GPIO + * @param ch Channel + * @return GPIO value + */ +static int32_t gpio_read(uint8_t ch) +{ + return 0; +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_BACNET) +/** + * @brief Create a BACnet object + * @param object_type Object type + * @param instance Object instance + * @param object_name Object name + */ +static void +bacnet_create_object(uint16_t object_type, uint32_t instance, char *object_name) +{ + uint32_t r; + + switch (object_type) { + case OBJECT_ANALOG_INPUT: + if (!Analog_Input_Valid_Instance(instance)) { + r = Analog_Input_Create(instance); + if (r == instance) { + Analog_Input_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_ANALOG_OUTPUT: + if (!Analog_Output_Valid_Instance(instance)) { + r = Analog_Output_Create(instance); + if (r == instance) { + Analog_Output_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_ANALOG_VALUE: + if (!Analog_Value_Valid_Instance(instance)) { + r = Analog_Value_Create(instance); + if (r == instance) { + Analog_Value_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_BINARY_INPUT: + if (!Binary_Input_Valid_Instance(instance)) { + r = Binary_Input_Create(instance); + if (r == instance) { + Binary_Input_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_BINARY_OUTPUT: + if (!Binary_Output_Valid_Instance(instance)) { + r = Binary_Output_Create(instance); + if (r == instance) { + Binary_Output_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_BINARY_VALUE: + if (!Binary_Value_Valid_Instance(instance)) { + r = Binary_Value_Create(instance); + if (r == instance) { + Binary_Value_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_MULTI_STATE_INPUT: + if (!Multistate_Input_Valid_Instance(instance)) { + r = Multistate_Input_Create(instance); + if (r == instance) { + Multistate_Input_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_MULTI_STATE_OUTPUT: + if (!Multistate_Output_Valid_Instance(instance)) { + r = Multistate_Output_Create(instance); + if (r == instance) { + Multistate_Output_Name_Set(instance, strdup(object_name)); + } + } + break; + case OBJECT_MULTI_STATE_VALUE: + if (!Multistate_Value_Valid_Instance(instance)) { + r = Multistate_Value_Create(instance); + if (r == instance) { + Multistate_Value_Name_Set(instance, strdup(object_name)); + } + } + break; + default: + break; + } +} + +/** + * @brief Write a property to a BACnet object + * @param object_type Object type + * @param instance Object instance + * @param property_id Property ID + * @param value Property value + */ +static void bacnet_write_property( + uint16_t object_type, + uint32_t instance, + uint32_t property_id, + VARIABLE_TYPE value) +{ + BACNET_BINARY_PV value_binary = BINARY_INACTIVE; + + switch (object_type) { + case OBJECT_ANALOG_INPUT: + if (property_id == PROP_PRESENT_VALUE) { + Analog_Input_Present_Value_Set( + instance, fixedpt_tofloat(value)); + } + break; + case OBJECT_ANALOG_OUTPUT: + if (property_id == PROP_PRESENT_VALUE) { + Analog_Output_Present_Value_Set( + instance, fixedpt_tofloat(value), BACNET_MAX_PRIORITY); + } + break; + case OBJECT_ANALOG_VALUE: + if (property_id == PROP_PRESENT_VALUE) { + Analog_Value_Present_Value_Set( + instance, fixedpt_tofloat(value), BACNET_MAX_PRIORITY); + } + break; + case OBJECT_BINARY_INPUT: + if (property_id == PROP_PRESENT_VALUE) { + if (fixedpt_toint(value) != 0) { + value_binary = BINARY_ACTIVE; + } + Binary_Input_Present_Value_Set(instance, value_binary); + } + break; + case OBJECT_BINARY_OUTPUT: + if (property_id == PROP_PRESENT_VALUE) { + if (fixedpt_toint(value) != 0) { + value_binary = BINARY_ACTIVE; + } + Binary_Output_Present_Value_Set( + instance, value_binary, BACNET_MAX_PRIORITY); + } + break; + case OBJECT_BINARY_VALUE: + if (property_id == PROP_PRESENT_VALUE) { + if (fixedpt_toint(value) != 0) { + value_binary = BINARY_ACTIVE; + } + Binary_Value_Present_Value_Set(instance, value_binary); + } + break; + case OBJECT_MULTI_STATE_INPUT: + if (property_id == PROP_PRESENT_VALUE) { + Multistate_Input_Present_Value_Set( + instance, fixedpt_toint(value)); + } + break; + case OBJECT_MULTI_STATE_OUTPUT: + if (property_id == PROP_PRESENT_VALUE) { + Multistate_Output_Present_Value_Set( + instance, fixedpt_toint(value), BACNET_MAX_PRIORITY); + } + break; + case OBJECT_MULTI_STATE_VALUE: + if (property_id == PROP_PRESENT_VALUE) { + Multistate_Value_Present_Value_Set( + instance, fixedpt_toint(value)); + } + break; + default: + break; + } +} + +/** + * @brief Read a property from a BACnet object + * @param object_type Object type + * @param instance Object instance + * @param property_id Property ID + * @return Property value + */ +static VARIABLE_TYPE bacnet_read_property( + uint16_t object_type, uint32_t instance, uint32_t property_id) +{ + VARIABLE_TYPE value = 0; + float value_float = 0.0; + BACNET_BINARY_PV value_binary = BINARY_INACTIVE; + + switch (object_type) { + case OBJECT_ANALOG_INPUT: + if (property_id == PROP_PRESENT_VALUE) { + value_float = Analog_Input_Present_Value(instance); + value = fixedpt_fromfloat(value_float); + } + break; + case OBJECT_ANALOG_OUTPUT: + if (property_id == PROP_PRESENT_VALUE) { + value_float = Analog_Output_Present_Value(instance); + value = fixedpt_fromfloat(value_float); + } + break; + case OBJECT_ANALOG_VALUE: + if (property_id == PROP_PRESENT_VALUE) { + value_float = Analog_Value_Present_Value(instance); + value = fixedpt_fromfloat(value_float); + } + break; + case OBJECT_BINARY_INPUT: + if (property_id == PROP_PRESENT_VALUE) { + value_binary = Binary_Input_Present_Value(instance); + value = + fixedpt_fromint((value_binary == BINARY_ACTIVE) ? 1 : 0); + } + break; + case OBJECT_BINARY_OUTPUT: + if (property_id == PROP_PRESENT_VALUE) { + value_binary = Binary_Output_Present_Value(instance); + value = + fixedpt_fromint((value_binary == BINARY_ACTIVE) ? 1 : 0); + } + break; + case OBJECT_BINARY_VALUE: + if (property_id == PROP_PRESENT_VALUE) { + value_binary = Binary_Value_Present_Value(instance); + value = + fixedpt_fromint((value_binary == BINARY_ACTIVE) ? 1 : 0); + } + break; + case OBJECT_MULTI_STATE_INPUT: + if (property_id == PROP_PRESENT_VALUE) { + value = + fixedpt_fromint(Multistate_Input_Present_Value(instance)); + } + break; + case OBJECT_MULTI_STATE_OUTPUT: + if (property_id == PROP_PRESENT_VALUE) { + value = + fixedpt_fromint(Multistate_Output_Present_Value(instance)); + } + break; + case OBJECT_MULTI_STATE_VALUE: + if (property_id == PROP_PRESENT_VALUE) { + value = + fixedpt_fromint(Multistate_Value_Present_Value(instance)); + } + break; + default: + break; + } + + return value; +} +#endif + +/** + * @brief Initialize the hardware drivers + * @param data Pointer to the ubasic data structure + */ +void ubasic_port_init(struct ubasic_data *data) +{ +#if ( \ + defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) || \ + defined(UBASIC_SCRIPT_HAVE_SLEEP) || \ + defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL)) + data->mstimer_now = mstimer_now; +#endif + data->variable_write = variable_write; + data->variable_read = variable_read; +#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) + data->hw_event = hw_event; + data->hw_event_clear = hw_event_clear; +#endif +#if defined(UBASIC_SCRIPT_HAVE_PWM_CHANNELS) + data->pwm_config = pwm_config; + data->pwm_write = pwm_write; + data->pwm_read = pwm_read; +#endif +#if defined(UBASIC_SCRIPT_HAVE_PWM_CHANNELS) + data->adc_config = adc_config; + data->adc_read = adc_read; +#endif +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) + data->gpio_config = gpio_config; + data->gpio_write = gpio_write; + data->gpio_read = gpio_read; +#endif +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + data->random_uint32 = random_uint32; +#endif +#if defined(UBASIC_SCRIPT_PRINT_TO_SERIAL) + data->serial_write = serial_write; +#endif +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + data->ubasic_getc = serial_getc; +#endif +#if defined(UBASIC_SCRIPT_HAVE_BACNET) + data->bacnet_create_object = bacnet_create_object; + data->bacnet_write_property = bacnet_write_property; + data->bacnet_read_property = bacnet_read_property; +#endif +} diff --git a/src/bacnet/basic/object/program.c b/src/bacnet/basic/object/program.c index eb3e7206..312f762c 100644 --- a/src/bacnet/basic/object/program.c +++ b/src/bacnet/basic/object/program.c @@ -48,7 +48,7 @@ struct object_data { bool Changed : 1; void *Context; /* return 0 for success, negative on error */ - int (*Load)(void *context, const char *location); + int (*Load)(void *context); int (*Run)(void *context); int (*Halt)(void *context); int (*Restart)(void *context); @@ -960,9 +960,9 @@ void Program_Context_Set(uint32_t object_instance, void *context) * @brief Set the Load function for the object * @param object_instance [in] BACnet object instance number * @param load [in] pointer to the Load function + * @note function should return 0 for success, negative on error */ -void Program_Load_Set( - uint32_t object_instance, int (*load)(void *context, const char *location)) +void Program_Load_Set(uint32_t object_instance, int (*load)(void *context)) { struct object_data *pObject = Object_Data(object_instance); @@ -975,6 +975,7 @@ void Program_Load_Set( * @brief Set the Run function for the object * @param object_instance [in] BACnet object instance number * @param run [in] pointer to the Run function + * @note function should return 0 for success, negative on error */ void Program_Run_Set(uint32_t object_instance, int (*run)(void *context)) { @@ -989,6 +990,7 @@ void Program_Run_Set(uint32_t object_instance, int (*run)(void *context)) * @brief Set the Halt function for the object * @param object_instance [in] BACnet object instance number * @param halt [in] pointer to the Halt function + * @note function should return 0 for success, negative on error */ void Program_Halt_Set(uint32_t object_instance, int (*halt)(void *context)) { @@ -1003,6 +1005,7 @@ void Program_Halt_Set(uint32_t object_instance, int (*halt)(void *context)) * @brief Set the Restart function for the object * @param object_instance [in] BACnet object instance number * @param restart [in] pointer to the Restart function + * @note function should return 0 for success, negative on error */ void Program_Restart_Set( uint32_t object_instance, int (*restart)(void *context)) @@ -1018,6 +1021,7 @@ void Program_Restart_Set( * @brief Set the Unload function for the object * @param object_instance [in] BACnet object instance number * @param unload [in] pointer to the Unload function + * @note function should return 0 for success, negative on error */ void Program_Unload_Set(uint32_t object_instance, int (*unload)(void *context)) { @@ -1028,13 +1032,17 @@ void Program_Unload_Set(uint32_t object_instance, int (*unload)(void *context)) } } +/** + * @brief Handle the IDLE state of the program + * @param pObject [in] pointer to the object data + */ static void Program_State_Idle_Handler(struct object_data *pObject) { int err; if (pObject->Program_Change == PROGRAM_REQUEST_LOAD) { if (pObject->Load) { - err = pObject->Load(pObject->Context, pObject->Program_Location); + err = pObject->Load(pObject->Context); if (err == 0) { pObject->Program_State = PROGRAM_STATE_LOADING; pObject->Reason_For_Halt = PROGRAM_ERROR_NORMAL; @@ -1047,7 +1055,7 @@ static void Program_State_Idle_Handler(struct object_data *pObject) } } else if (pObject->Program_Change == PROGRAM_REQUEST_RUN) { if (pObject->Load) { - err = pObject->Load(pObject->Context, pObject->Program_Location); + err = pObject->Load(pObject->Context); if (err == 0) { pObject->Program_State = PROGRAM_STATE_RUNNING; pObject->Reason_For_Halt = PROGRAM_ERROR_NORMAL; @@ -1074,6 +1082,10 @@ static void Program_State_Idle_Handler(struct object_data *pObject) } } +/** + * @brief Handle the HALTED state of the program + * @param pObject [in] pointer to the object data + */ static void Program_State_Halted_Handler(struct object_data *pObject) { int err; @@ -1093,7 +1105,7 @@ static void Program_State_Halted_Handler(struct object_data *pObject) } } else if (pObject->Program_Change == PROGRAM_REQUEST_LOAD) { if (pObject->Load) { - err = pObject->Load(pObject->Context, pObject->Program_Location); + err = pObject->Load(pObject->Context); if (err == 0) { pObject->Reason_For_Halt = PROGRAM_ERROR_NORMAL; pObject->Program_State = PROGRAM_STATE_LOADING; @@ -1123,6 +1135,10 @@ static void Program_State_Halted_Handler(struct object_data *pObject) } } +/** + * @brief Handle the RUNNING state of the program + * @param pObject [in] pointer to the object data + */ static void Program_State_Running_Handler(struct object_data *pObject) { int err; @@ -1142,7 +1158,7 @@ static void Program_State_Running_Handler(struct object_data *pObject) } } else if (pObject->Program_Change == PROGRAM_REQUEST_LOAD) { if (pObject->Load) { - err = pObject->Load(pObject->Context, pObject->Program_Location); + err = pObject->Load(pObject->Context); if (err == 0) { pObject->Reason_For_Halt = PROGRAM_ERROR_NORMAL; pObject->Program_State = PROGRAM_STATE_LOADING; diff --git a/src/bacnet/basic/object/program.h b/src/bacnet/basic/object/program.h index d8d474d1..8fd3b826 100644 --- a/src/bacnet/basic/object/program.h +++ b/src/bacnet/basic/object/program.h @@ -110,12 +110,13 @@ void Program_Cleanup(void); BACNET_STACK_EXPORT void Program_Init(void); -/* API for the program requests */ +/* API for the program requests + note: return value is 0 for success, non-zero for failure +*/ BACNET_STACK_EXPORT void Program_Context_Set(uint32_t object_instance, void *context); BACNET_STACK_EXPORT -void Program_Load_Set( - uint32_t object_instance, int (*load)(void *context, const char *location)); +void Program_Load_Set(uint32_t object_instance, int (*load)(void *context)); BACNET_STACK_EXPORT void Program_Run_Set(uint32_t object_instance, int (*run)(void *context)); BACNET_STACK_EXPORT diff --git a/src/bacnet/basic/program/ubasic/CMakeLists.txt b/src/bacnet/basic/program/ubasic/CMakeLists.txt new file mode 100644 index 00000000..5a2900ba --- /dev/null +++ b/src/bacnet/basic/program/ubasic/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) + +add_library(ubasic STATIC + ubasic.c + tokenizer.c +) diff --git a/src/bacnet/basic/program/ubasic/config.h b/src/bacnet/basic/program/ubasic/config.h new file mode 100644 index 00000000..4da5e214 --- /dev/null +++ b/src/bacnet/basic/program/ubasic/config.h @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 2017-18, Marijan Kostrun + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include +#include +#include +#include +#include + +/* Undef it all */ + +/* Storage and arithmetic */ +#undef VARIABLE_STORAGE_INT16 +#undef VARIABLE_STORAGE_INT32 +#undef VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8 +#undef VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10 +#undef VARIABLE_TYPE_STRING +#undef VARIABLE_TYPE_ARRAY +#undef UBASIC_SCRIPT_HAVE_DEMO_SCRIPTS + +/* Microcontroller related functionality */ +#undef UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR +#undef UBASIC_SCRIPT_HAVE_PWM_CHANNELS +#undef UBASIC_SCRIPT_HAVE_GPIO +#undef UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS +#undef UBASIC_SCRIPT_HAVE_SLEEP +#undef UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS +#undef UBASIC_SCRIPT_PRINT_TO_SERIAL +#undef UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL +#undef UBASIC_SCRIPT_HAVE_ANALOG_READ +#undef UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH +#undef UBASIC_SCRIPT_HAVE_BACNET + +/** + * + * UBASIC-PLUS: Start + * + */ +/* default storage for all numeric values */ +#define VARIABLE_STORAGE_INT32 + +/* defines the representation of floating point numbers as fixed points: + this is to allow UBASIC to run on Cortex M0 processors which do not + support Floating Point Arithmetic in hardware (they emulate it which + consumes lots of memory) */ +#define VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8 + +/* This many one-letter variables UBASIC supports */ +#define MAX_VARNUM 26 + +/* have numeric arrays and set their storage to this many VARIABLE_TYPE entries + */ +#define VARIABLE_TYPE_ARRAY 64 + +/* have strings and related functions */ +#define VARIABLE_TYPE_STRING + +/* can go to sleep: leave UBASIC for other stuff while waiting for timer to + * expire */ +#define UBASIC_SCRIPT_HAVE_SLEEP + +/* have microcontroller support for PWM: specify how many channels */ +#define UBASIC_SCRIPT_HAVE_PWM_CHANNELS (4) + +/* have internal timer channels available through rlab-like toc(ch) functions */ +#define UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS (8) + +/* support for random number generator by micro-controller */ +#define UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR + +/* support for direct access to pin inputs and outputs */ +#define UBASIC_SCRIPT_HAVE_GPIO_CHANNELS + +/* support flags in BASIC that change on hardware events: + for STM32F0XX nucleo and discovery boards + source of the events is push-button +*/ +#define UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS + +/* have a standard print to serial console function */ +#define UBASIC_SCRIPT_PRINT_TO_SERIAL + +/* how is input function supported ? */ +#define UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL + +/* support for analog inputs */ +#define UBASIC_SCRIPT_HAVE_ANALOG_READ + +/* support for BACnet objects and ReadProperty and WriteProperty (internal) */ +#define UBASIC_SCRIPT_HAVE_BACNET + +/* Demo scripts are huge. Do we need them? */ +#define UBASIC_SCRIPT_HAVE_DEMO_SCRIPTS + +/* support for storing/recalling variables in/from flash memory */ +#define UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH +/** + * + * UBASIC-PLUS: End + * + */ + +/* + * Selectively load header files based on the ocnfiguration above. + * Remember MC Hammer: + * CAN'T TOUCH THIS! + * + */ +#if defined(VARIABLE_STORAGE_INT32) + +#define VARIABLE_TYPE int32_t + +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + +#define FIXEDPT_BITS 32 + +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) +#define FIXEDPT_WBITS 24 +#elif defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) +#define FIXEDPT_WBITS 22 +#else +#error "Only 24.8 and 22.10 floats are currently supported" +#endif + +#include "fixedptc.h" + +#endif + +#elif defined(VARIABLE_STORAGE_INT16) + +#define VARIABLE_TYPE int16_t +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) +#error "Fixed Point Floats are Supported for 32bit Storage Only!" +#endif + +#else + +#error "Only INT32 and INT16 variable types are supported." + +#endif + +#define UBASIC_STATEMENT_SIZE (64) + +#define MAX_STRINGLEN 40 +#define MAX_LABEL_LEN 10 + +#if defined(VARIABLE_TYPE_STRING) +#define MAX_STRINGVARLEN 64 +#define MAX_BUFFERLEN 256 +#define MAX_SVARNUM 26 +#endif + +#endif /* #ifndef _CONFIG_H_ */ diff --git a/src/bacnet/basic/program/ubasic/fixedptc.h b/src/bacnet/basic/program/ubasic/fixedptc.h new file mode 100644 index 00000000..ec30dc8e --- /dev/null +++ b/src/bacnet/basic/program/ubasic/fixedptc.h @@ -0,0 +1,672 @@ +#ifndef _FIXEDPTC_H_ +#define _FIXEDPTC_H_ + +/* + * fixedptc.h is a 32-bit or 64-bit fixed point numeric library. + * + * The symbol FIXEDPT_BITS, if defined before this library header file + * is included, governs the number of bits in the data type (its "width"). + * The default width is 32-bit (FIXEDPT_BITS=32) and it can be used + * on any recent C99 compiler. The 64-bit precision (FIXEDPT_BITS=64) is + * available on compilers which implement 128-bit "long long" types. This + * precision has been tested on GCC 4.2+. + * + * Since the precision in both cases is relatively low, many complex + * functions (more complex than div & mul) take a large hit on the precision + * of the end result because errors in precision accumulate. + * This loss of precision can be lessened by increasing the number of + * bits dedicated to the fraction part, but at the loss of range. + * + * Adventurous users might utilize this library to build two data types: + * one which has the range, and one which has the precision, and carefully + * convert between them (including adding two number of each type to produce + * a simulated type with a larger range and precision). + * + * The ideas and algorithms have been cherry-picked from a large number + * of previous implementations available on the Internet. + * Tim Hartrick has contributed cleanup and 64-bit support patches. + * + * == Special notes for the 32-bit precision == + * Signed 32-bit fixed point numeric library for the 24.8 format. + * The specific limits are -8388608.999... to 8388607.999... and the + * most precise number is 0.00390625. In practice, you should not count + * on working with numbers larger than a million or to the precision + * of more than 2 decimal places. Make peace with the fact that PI + * is 3.14 here. :) + */ + +/*- + * Copyright (c) 2010-2012 Ivan Voras + * Copyright (c) 2012, Tim Hartrick + * Copyright (c) 2018, Marijan Kostrun + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Marijan Kostrun, added function str_fixedpt(char*,int, int) + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#ifndef FIXEDPT_BITS +#define FIXEDPT_BITS 32 +#endif + +#if FIXEDPT_BITS == 32 +typedef int32_t fixedpt; +typedef int64_t fixedptd; +typedef uint32_t fixedptu; +typedef uint64_t fixedptud; +#elif FIXEDPT_BITS == 64 +typedef int64_t fixedpt; +typedef __int128_t fixedptd; +typedef uint64_t fixedptu; +typedef __uint128_t fixedptud; +#else +#error "FIXEDPT_BITS must be equal to 32 or 64" +#endif + +#ifndef FIXEDPT_WBITS +#define FIXEDPT_WBITS 24 +#endif + +#if FIXEDPT_WBITS >= FIXEDPT_BITS +#error "FIXEDPT_WBITS must be less than or equal to FIXEDPT_BITS" +#endif + +#define FIXEDPT_FBITS (FIXEDPT_BITS - FIXEDPT_WBITS) +#define FIXEDPT_FMASK (((fixedpt)1 << FIXEDPT_FBITS) - 1) + +#define fixedpt_rconst(R) \ + ((fixedpt)((R) * \ + (((fixedptd)1 << FIXEDPT_FBITS) + ((R) >= 0 ? 0.5 : -0.5)))) +#define fixedpt_fromint(I) ((fixedptd)(I) << FIXEDPT_FBITS) +#define fixedpt_toint(F) ((F) >> FIXEDPT_FBITS) +#define fixedpt_add(A, B) ((A) + (B)) +#define fixedpt_sub(A, B) ((A) - (B)) +#define fixedpt_xmul(A, B) \ + ((fixedpt)(((fixedptd)(A) * (fixedptd)(B)) >> FIXEDPT_FBITS)) +#define fixedpt_xdiv(A, B) \ + ((fixedpt)(((fixedptd)(A) << FIXEDPT_FBITS) / (fixedptd)(B))) +#define fixedpt_fracpart(A) ((fixedpt)(A) & FIXEDPT_FMASK) + +#define FIXEDPT_ONE ((fixedpt)((fixedpt)1 << FIXEDPT_FBITS)) +#define FIXEDPT_ONE_HALF (FIXEDPT_ONE >> 1) +#define FIXEDPT_TWO (FIXEDPT_ONE + FIXEDPT_ONE) +#define FIXEDPT_PI fixedpt_rconst(3.14159265358979323846) +#define FIXEDPT_TWO_PI fixedpt_rconst(2 * 3.14159265358979323846) +#define FIXEDPT_HALF_PI fixedpt_rconst(3.14159265358979323846 / 2) +#define FIXEDPT_E fixedpt_rconst(2.7182818284590452354) + +#define fixedpt_abs(A) ((A) < 0 ? -(A) : (A)) + +/* Multiplies two fixedpt numbers, returns the result. */ +static inline fixedpt fixedpt_mul(fixedpt A, fixedpt B) +{ + return (((fixedptd)A * (fixedptd)B) >> FIXEDPT_FBITS); +} + +/* Divides two fixedpt numbers, returns the result. */ +static inline fixedpt fixedpt_div(fixedpt A, fixedpt B) +{ + return (((fixedptd)A << FIXEDPT_FBITS) / (fixedptd)B); +} + +/* + * Note: adding and subtracting fixedpt numbers can be done by using + * the regular integer operators + and -. + */ + +/** + * Convert decimal string to a fixedpt number up to specified + * number of decimal places. + * + */ +#include +static inline fixedpt +str_fixedpt(const char *p, uint8_t plen, uint8_t decimal_places) +{ + uint8_t i_minus = *p == '-' ? 1 : 0; + + fixedpt rval = fixedpt_fromint(atoi(p)); + + /* find '.': the number is float because it has at least one */ + /* digit past decimal point */ + const char *s = p; + while ((*s != '.') && ((s - p) < plen)) { + s++; + } + s++; + + /* are there any digits left past decimal point */ + if ((s - p) < plen) { + /* pick up not more then 'decimal_places': */ + uint16_t f = 0, fpow10 = 1; + uint8_t idec = 0; + while (((s - p) < plen) && isdigit(*s) && (idec < decimal_places)) { + f = 10 * f + ((*s) - '0'); + s++; + fpow10 *= 10; + idec++; + } + if (i_minus) { + rval -= (f << FIXEDPT_FBITS) / fpow10; + } else { + rval += (f << FIXEDPT_FBITS) / fpow10; + } + } + + return rval; +} + +/** + * Convert the given fixedpt number to a decimal string. + * The max_dec argument specifies how many decimal digits to the right + * of the decimal point to generate. If set to -1, the "default" number + * of decimal digits will be used (2 for 32-bit fixedpt width, 10 for + * 64-bit fixedpt width); If set to -2, "all" of the digits will + * be returned, meaning there will be invalid, bogus digits outside the + * specified precisions. + */ +static inline void fixedpt_str(fixedpt A, char *str, int max_dec) +{ + int32_t ndec = 0, slen = 0; + char tmp[12] = { 0 }; + fixedptud fr, ip; + const fixedptud one = (fixedptud)1 << FIXEDPT_BITS; + const fixedptud mask = one - 1; + + if (max_dec == -1) +#if FIXEDPT_BITS == 32 + max_dec = 2; +#elif FIXEDPT_BITS == 64 + max_dec = 10; +#else +#error Invalid width +#endif + else if (max_dec == -2) { + max_dec = 15; + } + + if (A < 0) { + str[slen++] = '-'; + A *= -1; + } + + ip = fixedpt_toint(A); + do { + tmp[ndec++] = '0' + ip % 10; + ip /= 10; + } while (ip != 0); + + while (ndec > 0) { + str[slen++] = tmp[--ndec]; + } + str[slen++] = '.'; + + fr = (fixedpt_fracpart(A) << FIXEDPT_WBITS) & mask; + do { + fr = (fr & mask) * 10; + str[slen++] = '0' + (fr >> FIXEDPT_BITS) % 10; + ndec++; + } while (fr != 0 && ndec < max_dec); + + if (ndec > 0 && str[slen - 1] == '0') { + str[slen - 1] = '\0'; /* cut off trailing 0 */ + if (str[slen - 2] == '.') { + str[slen - 2] = '\0'; /* cut off trailing .*/ + } + } else { + str[slen] = '\0'; + } +} + +/* Converts the given fixedpt number into a string, using a static + * (non-threadsafe) string buffer */ +static inline char *fixedpt_cstr(const fixedpt A, const int max_dec) +{ + static char str[25]; + + fixedpt_str(A, str, max_dec); + return (str); +} + +/* Returns the square root of the given number, or -1 in case of error */ +static inline fixedpt fixedpt_sqrt(fixedpt A) +{ + int invert = 0; + int iter = FIXEDPT_FBITS; + int l, i; + + if (A < 0) { + return (-1); + } + if (A == 0 || A == FIXEDPT_ONE) { + return (A); + } + if (A < FIXEDPT_ONE && A > 6) { + invert = 1; + A = fixedpt_div(FIXEDPT_ONE, A); + } + if (A > FIXEDPT_ONE) { + int s = A; + + iter = 0; + while (s > 0) { + s >>= 2; + iter++; + } + } + + /* Newton's iterations */ + l = (A >> 1) + 1; + for (i = 0; i < iter; i++) { + l = (l + fixedpt_div(A, l)) >> 1; + } + if (invert) { + return (fixedpt_div(FIXEDPT_ONE, l)); + } + return (l); +} + +/* Returns the sine of the given fixedpt number. + * Note: the loss of precision is extraordinary! */ +static inline fixedpt fixedpt_sin(fixedpt fp) +{ + int sign = 1; + fixedpt sqr, result; + const fixedpt SK[2] = { fixedpt_rconst(7.61e-03), + fixedpt_rconst(1.6605e-01) }; + + fp %= 2 * FIXEDPT_PI; + if (fp < 0) { + fp = FIXEDPT_PI * 2 + fp; + } + if ((fp > FIXEDPT_HALF_PI) && (fp <= FIXEDPT_PI)) { + fp = FIXEDPT_PI - fp; + } else if ((fp > FIXEDPT_PI) && (fp <= (FIXEDPT_PI + FIXEDPT_HALF_PI))) { + fp = fp - FIXEDPT_PI; + sign = -1; + } else if (fp > (FIXEDPT_PI + FIXEDPT_HALF_PI)) { + fp = (FIXEDPT_PI << 1) - fp; + sign = -1; + } + sqr = fixedpt_mul(fp, fp); + result = SK[0]; + result = fixedpt_mul(result, sqr); + result -= SK[1]; + result = fixedpt_mul(result, sqr); + result += FIXEDPT_ONE; + result = fixedpt_mul(result, fp); + return sign * result; +} + +/* Returns the cosine of the given fixedpt number */ +static inline fixedpt fixedpt_cos(fixedpt A) +{ + return (fixedpt_sin(FIXEDPT_HALF_PI - A)); +} + +/* Returns the tangens of the given fixedpt number. + tan(A) = sin(A) / cos(A) */ +static inline fixedpt fixedpt_tan(fixedpt A) +{ + return fixedpt_div(fixedpt_sin(A), fixedpt_cos(A)); +} + +/* Returns the value exp(x), i.e. e^x of the given fixedpt number. */ +static inline fixedpt fixedpt_exp(fixedpt fp) +{ + fixedpt xabs, k, z, R, xp; + const fixedpt LN2 = fixedpt_rconst(0.69314718055994530942); + const fixedpt LN2_INV = fixedpt_rconst(1.4426950408889634074); + const fixedpt EXP_P[5] = { + fixedpt_rconst(1.66666666666666019037e-01), + fixedpt_rconst(-2.77777777770155933842e-03), + fixedpt_rconst(6.61375632143793436117e-05), + fixedpt_rconst(-1.65339022054652515390e-06), + fixedpt_rconst(4.13813679705723846039e-08), + }; + + if (fp == 0) { + return (FIXEDPT_ONE); + } + xabs = fixedpt_abs(fp); + k = fixedpt_mul(xabs, LN2_INV); + k += FIXEDPT_ONE_HALF; + k &= ~FIXEDPT_FMASK; + if (fp < 0) { + k = -k; + } + fp -= fixedpt_mul(k, LN2); + z = fixedpt_mul(fp, fp); + /* Taylor */ + R = FIXEDPT_TWO + + fixedpt_mul( + z, + EXP_P[0] + + fixedpt_mul( + z, + EXP_P[1] + + fixedpt_mul( + z, + EXP_P[2] + + fixedpt_mul( + z, EXP_P[3] + fixedpt_mul(z, EXP_P[4]))))); + xp = FIXEDPT_ONE + fixedpt_div(fixedpt_mul(fp, FIXEDPT_TWO), R - fp); + if (k < 0) { + k = FIXEDPT_ONE >> (-k >> FIXEDPT_FBITS); + } else { + k = FIXEDPT_ONE << (k >> FIXEDPT_FBITS); + } + return (fixedpt_mul(k, xp)); +} + +/** + * @brief Returns the natural logarithm of the given fixedpt number. + * @param x The number. + * @return the natural logarithm of the given fixedpt number. + */ +static inline fixedpt fixedpt_ln(fixedpt x) +{ + fixedpt log2, xi; + fixedpt f, s, z, w, R; + const fixedpt LN2 = fixedpt_rconst(0.69314718055994530942); + const fixedpt LG[7] = { fixedpt_rconst(6.666666666666735130e-01), + fixedpt_rconst(3.999999999940941908e-01), + fixedpt_rconst(2.857142874366239149e-01), + fixedpt_rconst(2.222219843214978396e-01), + fixedpt_rconst(1.818357216161805012e-01), + fixedpt_rconst(1.531383769920937332e-01), + fixedpt_rconst(1.479819860511658591e-01) }; + + if (x < 0) { + return (0); + } + if (x == 0) { + return 0xffffffff; + } + + log2 = 0; + xi = x; + while (xi > FIXEDPT_TWO) { + xi >>= 1; + log2++; + } + f = xi - FIXEDPT_ONE; + s = fixedpt_div(f, FIXEDPT_TWO + f); + z = fixedpt_mul(s, s); + w = fixedpt_mul(z, z); + R = fixedpt_mul(w, LG[1] + fixedpt_mul(w, LG[3] + fixedpt_mul(w, LG[5]))) + + fixedpt_mul( + z, + LG[0] + + fixedpt_mul( + w, LG[2] + fixedpt_mul(w, LG[4] + fixedpt_mul(w, LG[6])))); + return ( + fixedpt_mul(LN2, (log2 << FIXEDPT_FBITS)) + f - fixedpt_mul(s, f - R)); +} + +/** + * @brief Returns the logarithm of the given base of the given fixedpt number + * @param x The number. + * @param base The base. + * @return the logarithm of the given base of the given fixedpt number + */ +static inline fixedpt fixedpt_log(fixedpt x, fixedpt base) +{ + return (fixedpt_div(fixedpt_ln(x), fixedpt_ln(base))); +} + +/** + * @brief Return the power value (n^exp) of the given fixedpt numbers + * @param n The base. + * @param exp The exponent. + * @return The power value. + */ +static inline fixedpt fixedpt_pow(fixedpt n, fixedpt exp) +{ + if (exp == 0) { + return (FIXEDPT_ONE); + } + + if (n < 0) { + return 0; + } + + return (fixedpt_exp(fixedpt_mul(fixedpt_ln(n), exp))); +} + +/** + * @brief Return a weighted moving average. + * @param latest_reading The latest reading. + * @param previous_average The previous average. + * @param nsamples The number of samples. + * @return The weighted moving average. + * @note The formula used is: + * AN+1 = (XN+1 + N * AN)/(N+1) + * where XN+1 is the latest reading, AN is the previous average, and N is the + * number of samples. + */ +static inline fixedpt fixedpt_averagew( + fixedpt latest_reading, fixedpt previous_average, fixedpt nsamples) +{ + if (nsamples <= 0) { + return latest_reading; + } + + return (fixedpt_div( + fixedpt_add(latest_reading, fixedpt_mul(nsamples, previous_average)), + fixedpt_add(nsamples, FIXEDPT_ONE))); +} + +/** + * @brief Extracts the fractional part of a fixedpt number and + * rounds it to max_dec decimal places. + * + * @param A The fixedpt value to process. + * @param max_dec The number of decimal places to round to. + * @return The rounded fractional part as a fixedpt number. + */ +static inline fixedpt fixedpt_fracpart_round(fixedpt A, int max_dec) +{ + /* Extract the fractional part */ + fixedpt frac = fixedpt_fracpart(A); + + /* allow -1 or other negative to default to a fixed number of places */ + if (max_dec < 0) { +#if FIXEDPT_BITS == 32 + max_dec = 2; +#elif FIXEDPT_BITS == 64 + max_dec = 10; +#else + max_dec = 15; +#endif + } + + /* Scale the fractional part to the desired decimal places */ + fixedpt scale = fixedpt_fromint(1); + for (int i = 0; i < max_dec; i++) { + scale = fixedpt_mul(scale, fixedpt_fromint(10)); + } + fixedpt scaled_frac = fixedpt_mul(frac, scale); + + return scaled_frac; +} + +/** + * @brief Extracts the fractional part of a fixedpt number and + * rounds it up to max_dec decimal places, returning it as an integer. + * @param A The fixedpt value to process. + * @param max_dec The number of decimal places to round to. + * @return returns the smallest integer that is not less than the given number. + */ +static inline int fixedpt_fracpart_ceil_toint(fixedpt A, int max_dec) +{ + fixedpt scaled_frac = fixedpt_fracpart_round(A, max_dec); + + /* Add 0.5 (scaled) for rounding */ + scaled_frac = fixedpt_add(scaled_frac, FIXEDPT_ONE_HALF); + + return fixedpt_toint(scaled_frac); +} + +/** + * @brief Extracts the fractional part of a fixedpt number and + * rounds it down to max_dec decimal places, returning it as an integer. + * @param A The fixedpt value to process. + * @param max_dec The number of decimal places to round to. + * @return the largest integer that is not greater than the given number. + */ +static inline int fixedpt_fracpart_floor_toint(fixedpt A, int max_dec) +{ + fixedpt scaled_frac = fixedpt_fracpart_round(A, max_dec); + + return fixedpt_toint(scaled_frac); +} + +/** + * @brief rounds a fixedpt number to the nearest integer. + * @param A The fixedpt value to process. + * @return the nearest fixedpt integer + */ +static inline fixedpt fixedpt_round(fixedpt A) +{ + uint32_t f = (A & FIXEDPT_FMASK); + + if (A >= 0) { + A = A & (~FIXEDPT_FMASK); + if (f >= FIXEDPT_ONE_HALF) { + A += FIXEDPT_ONE; + } + } else { + A = A & (~FIXEDPT_FMASK); + if (f <= FIXEDPT_ONE_HALF) { + A -= FIXEDPT_ONE; + } + } + + return A; +} + +/** + * @brief rounds a fixedpt number to the nearest integer. + * @param A The fixedpt value to process. + * @return the nearest integer + */ +static inline int fixedpt_round_toint(fixedpt A) +{ + return fixedpt_toint(fixedpt_round(A)); +} + +/** + * @brief rounds a number up to the nearest fixedpt integer. + * @param A The fixedpt value to process. + * @return the smallest fixedpt integer that is not less than the given number. + */ +static inline fixedpt fixedpt_ceil(fixedpt A) +{ + if (A >= 0) { + uint32_t f = (A & FIXEDPT_FMASK); + A = A & (~FIXEDPT_FMASK); + if (f > 0) { + A += FIXEDPT_ONE; + } + } else { + A = A & (~FIXEDPT_FMASK); + } + + return A; +} + +/** + * @brief rounds a number up to the nearest integer. + * @param A The fixedpt value to process. + * @return the smallest integer that is not less than the given number. + */ +static inline int fixedpt_ceil_toint(fixedpt A) +{ + return fixedpt_toint(fixedpt_ceil(A)); +} + +/** + * @brief This function rounds a number down to the nearest fixedpt integer. + * @param A The fixedpt value to process. + * @return the largest fixedpt integer that is not greater than the given + * number. + */ +static inline fixedpt fixedpt_floor(fixedpt A) +{ + if (A >= 0) { + A = A & (~FIXEDPT_FMASK); + } else { + uint32_t f = (A & FIXEDPT_FMASK); + A = A & (~FIXEDPT_FMASK); + if (f > 0) { + A -= FIXEDPT_ONE; + } + } + + return A; +} + +/** + * @brief This function rounds a number down to the nearest integer. + * @param A The fixedpt value to process. + * @return the largest integer that is not greater than the given number. + */ +static inline int fixedpt_floor_toint(fixedpt A) +{ + return fixedpt_toint(fixedpt_floor(A)); +} + +/** + * @brief Converts a fixedpt value to a float. + * + * @param A The fixedpt value to convert. + * @return The float representation of the fixedpt value. + */ +static inline float fixedpt_tofloat(fixedpt A) +{ + return (float)A / (1 << FIXEDPT_FBITS); +} + +/** + * @brief Converts a fixedpt value to a float. + * + * @param A The fixedpt value to convert. + * @return The float representation of the fixedpt value. + */ +static inline fixedpt fixedpt_fromfloat(float F) +{ + return fixedpt_rconst(F); +} + +/** + * @brief Converts a fixedpt value to a double. + * + * @param A The fixedpt value to convert. + * @return The double representation of the fixedpt value. + */ +static inline double fixedpt_todouble(fixedpt A) +{ + return (double)A / (1 << FIXEDPT_FBITS); +} +#endif diff --git a/src/bacnet/basic/program/ubasic/platform.h b/src/bacnet/basic/program/ubasic/platform.h new file mode 100644 index 00000000..d5224a0e --- /dev/null +++ b/src/bacnet/basic/program/ubasic/platform.h @@ -0,0 +1,46 @@ +/** + * @file + * @author Steve Karg + * @date 2025 + * @brief Platform libc and compiler abstraction layer + * @details This libc and compiler abstraction layer assists with differences + * between compiler and libc versions, capabilities, and C standards. + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef UBASIC_PLATFORM_H +#define UBASIC_PLATFORM_H +#include +#include +#include +#include + +#ifndef INT_MAX +#define INT_MAX (~0U >> 1U) +#endif + +#ifndef NOMINMAX +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#endif + +#ifndef islessgreater +#define islessgreater(x, y) ((x) < (y) || (x) > (y)) +#endif + +#ifndef isgreaterequal +#define isgreaterequal(x, y) ((x) > (y) || !islessgreater((x), (y))) +#endif + +#ifndef islessequal +#define islessequal(x, y) ((x) < (y) || !islessgreater((x), (y))) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) ((size_t)(sizeof(array) / sizeof((array)[0]))) +#endif + +#endif diff --git a/src/bacnet/basic/program/ubasic/tokenizer.c b/src/bacnet/basic/program/ubasic/tokenizer.c new file mode 100644 index 00000000..17163589 --- /dev/null +++ b/src/bacnet/basic/program/ubasic/tokenizer.c @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2006, Adam Dunkels + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified to support simple string variables and functions by David Mitchell + * November 2008. + * Changes and additions are marked 'string additions' throughout + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "config.h" +#include "tokenizer.h" + +#define MAX_NUMLEN 8 + +struct keyword_token { + const char *keyword; + uint8_t token; +}; + +static const struct keyword_token keywords[] = { +#if defined(VARIABLE_TYPE_STRING) + /* new string-related statements and functions */ + { "left$", TOKENIZER_LEFT_STR }, + { "right$", TOKENIZER_RIGHT_STR }, + { "mid$", TOKENIZER_MID_STR }, + { "str$", TOKENIZER_STR_STR }, + { "chr$", TOKENIZER_CHR_STR }, + { "val", TOKENIZER_VAL }, + { "len", TOKENIZER_LEN }, + { "instr", TOKENIZER_INSTR }, + { "asc", TOKENIZER_ASC }, +#endif + /* end of string additions */ + { "let ", TOKENIZER_LET }, + { "println ", TOKENIZER_PRINTLN }, + { "print ", TOKENIZER_PRINT }, + { "if", TOKENIZER_IF }, + { "then", TOKENIZER_THEN }, + { "else", TOKENIZER_ELSE }, + { "endif", TOKENIZER_ENDIF }, +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) + { "toc", TOKENIZER_TOC }, +#endif +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + { "input", TOKENIZER_INPUT }, +#endif + { "for ", TOKENIZER_FOR }, + { "to ", TOKENIZER_TO }, + { "next ", TOKENIZER_NEXT }, + { "step ", TOKENIZER_STEP }, + { "while", TOKENIZER_WHILE }, + { "endwhile", TOKENIZER_ENDWHILE }, + { "goto ", TOKENIZER_GOTO }, + { "gosub ", TOKENIZER_GOSUB }, + { "return", TOKENIZER_RETURN }, + { "end", TOKENIZER_END }, +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) + { "sleep", TOKENIZER_SLEEP }, +#endif +#if defined(VARIABLE_TYPE_ARRAY) + { "dim ", TOKENIZER_DIM }, +#endif +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) + { "tic", TOKENIZER_TIC }, +#endif +#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) + { "flag", TOKENIZER_HWE }, +#endif +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + { "ran", TOKENIZER_RAN }, +#endif +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + { "sqrt", TOKENIZER_SQRT }, + { "sin", TOKENIZER_SIN }, + { "cos", TOKENIZER_COS }, + { "tan", TOKENIZER_TAN }, + { "exp", TOKENIZER_EXP }, + { "ln", TOKENIZER_LN }, +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + { "uniform", TOKENIZER_UNIFORM }, +#endif + { "abs", TOKENIZER_ABS }, + { "floor", TOKENIZER_FLOOR }, + { "ceil", TOKENIZER_CEIL }, + { "round", TOKENIZER_ROUND }, + { "pow", TOKENIZER_POWER }, + { "avgw", TOKENIZER_AVERAGEW }, +#endif +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) + { "pinmode", TOKENIZER_PINMODE }, + { "dread", TOKENIZER_DREAD }, + { "dwrite", TOKENIZER_DWRITE }, +#endif +#ifdef UBASIC_SCRIPT_HAVE_PWM_CHANNELS + { "awrite_conf", TOKENIZER_PWMCONF }, + { "awrite", TOKENIZER_PWM }, +#endif +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) + { "aread_conf", TOKENIZER_AREADCONF }, + { "aread", TOKENIZER_AREAD }, +#endif + { "hex ", TOKENIZER_PRINT_HEX }, + { "dec ", TOKENIZER_PRINT_DEC }, + { ":", TOKENIZER_COLON }, +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) + { "store", TOKENIZER_STORE }, + { "recall", TOKENIZER_RECALL }, +#endif +#if defined(UBASIC_SCRIPT_HAVE_BACNET) + { "bac_create", TOKENIZER_BACNET_CREATE_OBJECT }, + { "bac_read", TOKENIZER_BACNET_READ_PROPERTY }, + { "bac_write", TOKENIZER_BACNET_WRITE_PROPERTY }, +#endif + { "clear", TOKENIZER_CLEAR }, + { NULL, TOKENIZER_ERROR } +}; + +/*---------------------------------------------------------------------------*/ +static uint8_t +singlechar_or_operator(struct tokenizer_data *tree, uint8_t *offset) +{ + if (offset) { + *offset = 1; + } + + if ((*tree->ptr == '\n') || (*tree->ptr == ';')) { + return TOKENIZER_EOL; + } else if (*tree->ptr == ',') { + return TOKENIZER_COMMA; + } else if (*tree->ptr == '+') { + return TOKENIZER_PLUS; + } else if (*tree->ptr == '-') { + return TOKENIZER_MINUS; + } else if (*tree->ptr == '&') { + if (*(tree->ptr + 1) == '&') { + if (offset) { + *offset += 1; + } + return TOKENIZER_LAND; + } + return TOKENIZER_AND; + } else if (*tree->ptr == '|') { + if (*(tree->ptr + 1) == '|') { + if (offset) { + *offset += 1; + } + return TOKENIZER_LOR; + } + return TOKENIZER_OR; + } else if (*tree->ptr == '*') { + return TOKENIZER_ASTR; + } else if (*tree->ptr == '!') { + return TOKENIZER_LNOT; + } else if (*tree->ptr == '~') { + return TOKENIZER_NOT; + } else if (*tree->ptr == '/') { + return TOKENIZER_SLASH; + } else if (*tree->ptr == '%') { + return TOKENIZER_MOD; + } else if (*tree->ptr == '(') { + return TOKENIZER_LEFTPAREN; + } else if (*tree->ptr == ')') { + return TOKENIZER_RIGHTPAREN; + } else if (*tree->ptr == '<') { + if (tree->ptr[1] == '=') { + if (offset) { + *offset += 1; + } + return TOKENIZER_LE; + } else if (tree->ptr[1] == '>') { + if (offset) { + *offset += 1; + } + return TOKENIZER_NE; + } + return TOKENIZER_LT; + } else if (*tree->ptr == '>') { + if (tree->ptr[1] == '=') { + if (offset) { + *offset += 1; + } + return TOKENIZER_GE; + } + return TOKENIZER_GT; + } else if (*tree->ptr == '=') { + if (tree->ptr[1] == '=') { + if (offset) { + *offset += 1; + } + } + return TOKENIZER_EQ; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static uint8_t tokenizer_next_token(struct tokenizer_data *tree) +{ + const struct keyword_token *kt; + uint8_t i, j; + + /* eat all whitespace */ + while (*tree->ptr == ' ' || *tree->ptr == '\t' || *tree->ptr == '\r') { + tree->ptr++; + } + + if (*tree->ptr == 0) { + return TOKENIZER_ENDOFINPUT; + } + + uint8_t have_decdot = 0, i_dot = 0; + if ((tree->ptr[0] == '0') && + ((tree->ptr[1] == 'x') || (tree->ptr[1] == 'X'))) { + /* is it HEX */ + tree->nextptr = tree->ptr + 2; + while (1) { + if (*tree->nextptr >= '0' && *tree->nextptr <= '9') { + tree->nextptr++; + continue; + } + if ((*tree->nextptr >= 'a') && (*tree->nextptr <= 'f')) { + tree->nextptr++; + continue; + } + if ((*tree->nextptr >= 'A') && (*tree->nextptr <= 'F')) { + tree->nextptr++; + continue; + } + return TOKENIZER_INT; + } + } else if ( + (tree->ptr[0] == '0') && + ((tree->ptr[1] == 'b') || (tree->ptr[1] == 'B'))) { + /* is it BIN */ + tree->nextptr = tree->ptr + 2; + while (*tree->nextptr == '0' || *tree->nextptr == '1') { + tree->nextptr++; + } + return TOKENIZER_INT; + } else if (isdigit(*tree->ptr) || (*tree->ptr == '.')) { + /* is it FLOAT (digits with at most one decimal point) */ + /* is it DEC (digits without decimal point which ends in d,D,L,l) */ + tree->nextptr = tree->ptr; + have_decdot = 0; + i_dot = 0; + while (1) { + if (*tree->nextptr >= '0' && *tree->nextptr <= '9') { + tree->nextptr++; + if (have_decdot) { + i_dot++; + } + continue; + } + if (*tree->nextptr == '.') { + tree->nextptr++; + have_decdot++; + if (have_decdot > 1) { + return TOKENIZER_ERROR; + } + continue; + } + if (*tree->nextptr == 'd' || *tree->nextptr == 'D' || + *tree->nextptr == 'l' || *tree->nextptr == 'L') { + return TOKENIZER_INT; + } + +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + if (i_dot) { + return TOKENIZER_FLOAT; + } +#endif + + return TOKENIZER_NUMBER; + } + } else if ((j = singlechar_or_operator(tree, &i))) { + tree->nextptr = tree->ptr + i; + return j; + } +#if defined(VARIABLE_TYPE_STRING) + else if ( + (*tree->ptr == '"' || *tree->ptr == '\'') && + (*(tree->ptr - 1) != '\\')) { + i = *tree->ptr; + tree->nextptr = tree->ptr; + do { + ++tree->nextptr; + if ((*tree->nextptr == '\0') || (*tree->nextptr == '\n') || + (*tree->nextptr == ';')) { + return TOKENIZER_ERROR; + } + } while (*tree->nextptr != i || *(tree->nextptr - 1) == '\\'); + + ++tree->nextptr; + + return TOKENIZER_STRING; + } +#endif + else { + /* Check for keywords: */ + for (kt = keywords; kt->keyword != NULL; ++kt) { + if (strncmp(tree->ptr, kt->keyword, strlen(kt->keyword)) == 0) { + tree->nextptr = tree->ptr + strlen(kt->keyword); + return kt->token; + } + } + } + + /** + * what is left after this point we call a label as long as + * it starts with "_" or a..z + * and contains only digits and letters + */ + i = 0; + j = 0; + if (*tree->ptr == '_' || (*tree->ptr >= 'a' && *tree->ptr <= 'z') || + (*tree->ptr >= 'A' && *tree->ptr <= 'Z')) { + tree->nextptr = tree->ptr; + while (1) { + if (*tree->nextptr == '_') { + j++; + tree->nextptr++; + continue; + } + if ((*tree->nextptr >= '0') && (*tree->nextptr <= '9')) { + i++; + tree->nextptr++; + continue; + } + if ((*tree->nextptr >= 'a') && (*tree->nextptr <= 'z')) { + i++; + tree->nextptr++; + continue; + } + if ((*tree->nextptr >= 'A') && (*tree->nextptr <= 'Z')) { + i++; + tree->nextptr++; + continue; + } + + if (j > 0 || i > 1) { + return TOKENIZER_LABEL; + } + + if (i == 1) { +#if defined(VARIABLE_TYPE_STRING) + if (*(tree->ptr + 1) == '$') { + tree->nextptr++; + return TOKENIZER_STRINGVARIABLE; + } +#endif + +#if defined(VARIABLE_TYPE_ARRAY) + if (*(tree->ptr + 1) == '@') { + tree->nextptr++; + return TOKENIZER_ARRAYVARIABLE; + } +#endif + return TOKENIZER_VARIABLE; + } + break; + } + } + + return TOKENIZER_ERROR; +} +/*---------------------------------------------------------------------------*/ +#if defined(VARIABLE_TYPE_STRING) +int8_t tokenizer_stringlookahead(struct tokenizer_data *tree) +{ + /* return 1 (true) if next 'defining' token is string not integer */ + const char *saveptr = tree->ptr; + const char *savenextptr = tree->nextptr; + uint8_t token = tree->current_token; + int8_t si = -1; + + while (si == -1) { + if (token == TOKENIZER_EOL || token == TOKENIZER_ENDOFINPUT) { + si = 0; + } else if ( + token == TOKENIZER_NUMBER || token == TOKENIZER_VARIABLE || + token == TOKENIZER_FLOAT) { + si = 0; /* number or numeric var */ + } else if (token == TOKENIZER_PLUS) { + /* do nothing */ + } else if (token == TOKENIZER_STRING) { + si = 1; + } else if ( + token >= TOKENIZER_STRINGVARIABLE && token <= TOKENIZER_CHR_STR) { + si = 1; + } else if (token > TOKENIZER_CHR_STR) { + si = 0; /* numeric function */ + } + + token = tokenizer_next_token(tree); + } + tree->ptr = saveptr; + tree->nextptr = savenextptr; + return si; +} +#endif +/*---------------------------------------------------------------------------*/ +void tokenizer_init(struct tokenizer_data *tree, const char *program) +{ + tree->ptr = program; + tree->prog = program; + tree->current_token = tokenizer_next_token(tree); +} +/*---------------------------------------------------------------------------*/ +uint8_t tokenizer_token(struct tokenizer_data *tree) +{ + return tree->current_token; +} +/*---------------------------------------------------------------------------*/ +void tokenizer_next(struct tokenizer_data *tree) +{ + if (tokenizer_finished(tree)) { + return; + } + + tree->ptr = tree->nextptr; + + while (*tree->ptr == ' ') { + ++tree->ptr; + } + + tree->current_token = tokenizer_next_token(tree); + return; +} +/*---------------------------------------------------------------------------*/ + +VARIABLE_TYPE tokenizer_num(struct tokenizer_data *tree) +{ + const char *c = tree->ptr; + VARIABLE_TYPE rval = 0; + + while (1) { + if (*c < '0' || *c > '9') { + break; + } + + rval *= 10; + rval += (*c - '0'); + c++; + } + + return rval; +} + +VARIABLE_TYPE tokenizer_int(struct tokenizer_data *tree) +{ + const char *c = tree->ptr; + VARIABLE_TYPE rval = 0; + if ((*c == '0') && (*(c + 1) == 'x' || *(c + 1) == 'X')) { + c += 2; + while (1) { + if (*c >= '0' && *c <= '9') { + rval <<= 4; + rval += (*c - '0'); + c++; + continue; + } + if ((*c >= 'a') && (*c <= 'f')) { + rval <<= 4; + rval += (*c - 87); /* 87 = 'a' - 10 */ + c++; + continue; + } + if ((*c >= 'A') && (*c <= 'F')) { + rval <<= 4; + rval += (*c - 55); /* 55 = 'A' - 10 */ + c++; + continue; + } + break; + } + return rval; + } + if ((*c == '0') && (*(c + 1) == 'b' || *(c + 1) == 'B')) { + c += 2; + while (1) { + if (*c == '0' || *c == '1') { + rval <<= 1; + rval += (*c - '0'); + c++; + continue; + } + break; + } + return rval; + } + + return tokenizer_num(tree); +} + +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) +/*---------------------------------------------------------------------------*/ +VARIABLE_TYPE tokenizer_float(struct tokenizer_data *tree) +{ + return str_fixedpt( + tree->ptr, tree->nextptr - tree->ptr, FIXEDPT_FBITS >> 1); +} +#endif + +#if defined(VARIABLE_TYPE_STRING) +/*---------------------------------------------------------------------------*/ +void tokenizer_string(struct tokenizer_data *tree, char *dest, uint8_t len) +{ + const char *string_end; + char quote_char; + uint8_t string_len; + + if (tokenizer_token(tree) != TOKENIZER_STRING) { + return; + } + quote_char = *tree->ptr; + + /** figure out the quote used for strings + * ignore escaped string-quotes + */ + string_end = tree->ptr; + do { + string_end++; + + string_end = strchr(string_end, quote_char); + if (string_end == NULL) { + return; + } + } while (*(string_end - 1) == '\\'); + + string_len = string_end - tree->ptr - 1; + if (len < string_len) { + string_len = len; + } + memcpy(dest, tree->ptr + 1, string_len); + dest[string_len] = 0; + return; +} +#endif + +void tokenizer_label(struct tokenizer_data *tree, char *dest, uint8_t len) +{ + const char *string_end = tree->nextptr; + uint8_t string_len; + + if (tokenizer_token(tree) != TOKENIZER_LABEL) { + return; + } + + for (string_len = 0; string_len < string_end - tree->ptr; string_len++) { + if ((*(tree->ptr + string_len) == '_') || + ((*(tree->ptr + string_len) >= '0') && + (*(tree->ptr + string_len) <= '9')) || + ((*(tree->ptr + string_len) >= 'A') && + (*(tree->ptr + string_len) <= 'Z')) || + ((*(tree->ptr + string_len) >= 'a') && + (*(tree->ptr + string_len) <= 'z'))) { + continue; + } + break; + } + if (string_len > len) { + string_len = len; + } + memcpy(dest, tree->ptr, string_len); + dest[string_len] = 0; +} + +/*---------------------------------------------------------------------------*/ +bool tokenizer_finished(struct tokenizer_data *tree) +{ + return ((*tree->ptr == 0) || (tree->current_token == TOKENIZER_ENDOFINPUT)); +} + +/*---------------------------------------------------------------------------*/ +uint8_t tokenizer_variable_num(struct tokenizer_data *tree) +{ + if ((*tree->ptr >= 'a' && *tree->ptr <= 'z')) { + return (((uint8_t)*tree->ptr) - 'a'); + } + + if ((*tree->ptr >= 'A' && *tree->ptr <= 'Z')) { + return (((uint8_t)*tree->ptr) - 'A'); + } + + return 0xff; +} +/*---------------------------------------------------------------------------*/ + +uint16_t tokenizer_save_offset(struct tokenizer_data *tree) +{ + return (tree->ptr - tree->prog); +} + +void tokenizer_jump_offset(struct tokenizer_data *tree, uint16_t offset) +{ + tree->ptr = (tree->prog + offset); + tree->current_token = tokenizer_next_token(tree); + while ((tree->current_token == TOKENIZER_EOL) && + !tokenizer_finished(tree)) { + tokenizer_next(tree); + } + return; +} + +const char *tokenizer_name(VARIABLE_TYPE token) +{ + const struct keyword_token *kt; + + for (kt = keywords; kt->keyword != NULL; ++kt) { + if (kt->token == token) { + return kt->keyword; + } + } + + return NULL; +} diff --git a/src/bacnet/basic/program/ubasic/tokenizer.h b/src/bacnet/basic/program/ubasic/tokenizer.h new file mode 100644 index 00000000..a111cae0 --- /dev/null +++ b/src/bacnet/basic/program/ubasic/tokenizer.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2006, Adam Dunkels + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified to support simple string variables and functions by David Mitchell + * November 2008. + * Changes and additions are marked 'string additions' throughout + * + * Modified to support Plus extension by Marijan Kostrun 2018. + * + * Added averagew function. Steve Karg 2025 + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __TOKENIZER_H__ +#define __TOKENIZER_H__ +#include +#include +#include "config.h" + +enum { + /*0*/ TOKENIZER_ERROR, + /*1*/ TOKENIZER_ENDOFINPUT, + /*2*/ TOKENIZER_NUMBER, +#if defined(VARIABLE_TYPE_STRING) + /*3*/ TOKENIZER_STRING, +#endif + /*4*/ TOKENIZER_VARIABLE, +#if defined(VARIABLE_TYPE_STRING) + /* string additions - must be here and in this order */ + /*5*/ TOKENIZER_STRINGVARIABLE, + /*6*/ TOKENIZER_PRINT_STR, + /*7*/ TOKENIZER_LEFT_STR, + /*8*/ TOKENIZER_RIGHT_STR, + /*9*/ TOKENIZER_MID_STR, + /*10*/ TOKENIZER_STR_STR, + /*11*/ TOKENIZER_CHR_STR, + /*12*/ TOKENIZER_VAL, + /*13*/ TOKENIZER_LEN, + /*14*/ TOKENIZER_INSTR, + /*15*/ TOKENIZER_ASC, +#endif + /*16*/ TOKENIZER_LET, + /*17*/ TOKENIZER_PRINTLN, + /*18*/ TOKENIZER_PRINT, + /*19*/ TOKENIZER_IF, + /*20*/ TOKENIZER_THEN, + /*21*/ TOKENIZER_ELSE, + /*22*/ TOKENIZER_ENDIF, + /*23*/ TOKENIZER_FOR, + /*24*/ TOKENIZER_TO, + /*25*/ TOKENIZER_NEXT, + /*26*/ TOKENIZER_STEP, + /*27*/ TOKENIZER_WHILE, + /*28*/ TOKENIZER_ENDWHILE, + /*29*/ TOKENIZER_GOTO, + /*30*/ TOKENIZER_GOSUB, + /*31*/ TOKENIZER_RETURN, + /*32*/ TOKENIZER_END, + /*33*/ TOKENIZER_COMMA, + /*34*/ TOKENIZER_PLUS, + /*35*/ TOKENIZER_MINUS, + /*36*/ TOKENIZER_AND, + /*37*/ TOKENIZER_OR, + /*38*/ TOKENIZER_ASTR, + /*39*/ TOKENIZER_SLASH, + /*40*/ TOKENIZER_MOD, + /*41*/ TOKENIZER_LEFTPAREN, + /*42*/ TOKENIZER_RIGHTPAREN, + /*43*/ TOKENIZER_LT, + /*44*/ TOKENIZER_GT, + /*45*/ TOKENIZER_EQ, + /*46*/ TOKENIZER_EOL, + /* */ + /* Plus : Start */ + /* */ + /*47*/ TOKENIZER_NE, + /*48*/ TOKENIZER_GE, + /*49*/ TOKENIZER_LE, + /*50*/ TOKENIZER_LAND, + /*51*/ TOKENIZER_LOR, + /*52*/ TOKENIZER_LNOT, + /*53*/ TOKENIZER_NOT, + /*54*/ TOKENIZER_PRINT_HEX, + /*55*/ TOKENIZER_PRINT_DEC, +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + /*56*/ TOKENIZER_INPUT, +#endif +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) + /*57*/ TOKENIZER_SLEEP, +#endif +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) + /*58*/ TOKENIZER_PINMODE, + /*59*/ TOKENIZER_DREAD, + /*60*/ TOKENIZER_DWRITE, +#endif +#if defined(VARIABLE_TYPE_ARRAY) + /*61*/ TOKENIZER_DIM, + /*62*/ TOKENIZER_ARRAYVARIABLE, +#endif +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + /*63*/ TOKENIZER_RAN, +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) + /*64*/ TOKENIZER_TIC, + /*65*/ TOKENIZER_TOC, +#endif +#endif +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + /*66*/ TOKENIZER_INT, + /*67*/ TOKENIZER_FLOAT, + /*68*/ TOKENIZER_SQRT, + /*69*/ TOKENIZER_SIN, + /*70*/ TOKENIZER_COS, + /*71*/ TOKENIZER_TAN, + /*72*/ TOKENIZER_EXP, + /*73*/ TOKENIZER_LN, +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + /*74*/ TOKENIZER_UNIFORM, +#endif + /*75*/ TOKENIZER_ABS, + /*76*/ TOKENIZER_FLOOR, + /*77*/ TOKENIZER_CEIL, + /*78*/ TOKENIZER_ROUND, + /*79*/ TOKENIZER_POWER, + /*80*/ TOKENIZER_AVERAGEW, +#endif +#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) + /*81*/ TOKENIZER_HWE, +#endif +#if defined(UBASIC_SCRIPT_HAVE_PWM_CHANNELS) + /*82*/ TOKENIZER_PWMCONF, + /*83*/ TOKENIZER_PWM, +#endif +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) + /*84*/ TOKENIZER_AREADCONF, + /*85*/ TOKENIZER_AREAD, +#endif + /*86*/ TOKENIZER_LABEL, + /*87*/ TOKENIZER_COLON, +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) + /*88*/ TOKENIZER_STORE, + /*89*/ TOKENIZER_RECALL, +#endif +#if defined(UBASIC_SCRIPT_HAVE_BACNET) + /*90*/ TOKENIZER_BACNET_CREATE_OBJECT, + /*91*/ TOKENIZER_BACNET_READ_PROPERTY, + /*92*/ TOKENIZER_BACNET_WRITE_PROPERTY, +#endif + /*93*/ TOKENIZER_CLEAR, + /* */ + /* Plus: End */ + /* */ +}; + +struct tokenizer_data { + const char *ptr; + const char *nextptr; + const char *prog; + uint8_t current_token; +}; + +void tokenizer_init(struct tokenizer_data *data, const char *program); +void tokenizer_next(struct tokenizer_data *data); +uint8_t tokenizer_token(struct tokenizer_data *data); +VARIABLE_TYPE tokenizer_num(struct tokenizer_data *data); +VARIABLE_TYPE tokenizer_int(struct tokenizer_data *data); + +#ifdef FIXEDPT_FBITS +VARIABLE_TYPE tokenizer_float(struct tokenizer_data *data); +#endif + +uint8_t tokenizer_variable_num(struct tokenizer_data *data); +bool tokenizer_finished(struct tokenizer_data *data); + +#if defined(VARIABLE_TYPE_STRING) +void tokenizer_string(struct tokenizer_data *data, char *dest, uint8_t len); +int8_t tokenizer_stringlookahead(struct tokenizer_data *data); +#endif + +void tokenizer_label(struct tokenizer_data *data, char *dest, uint8_t len); +uint16_t tokenizer_save_offset(struct tokenizer_data *data); +void tokenizer_jump_offset(struct tokenizer_data *data, uint16_t offset); + +const char *tokenizer_name(VARIABLE_TYPE token); + +#endif /* __TOKENIZER_H__ */ diff --git a/src/bacnet/basic/program/ubasic/ubasic.c b/src/bacnet/basic/program/ubasic/ubasic.c new file mode 100644 index 00000000..784761f4 --- /dev/null +++ b/src/bacnet/basic/program/ubasic/ubasic.c @@ -0,0 +1,3145 @@ +/* + * Copyright (c) 2006, Adam Dunkels + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified to support multiple programs and use a structure to hold + * each program state. 2025 Steve Karg + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Includes: Generic + * ----------------------------------------------------------*/ +#include "config.h" +#include "ubasic.h" +#include "tokenizer.h" + +#if defined(VARIABLE_TYPE_STRING) +static int16_t sexpr(struct ubasic_data *data); +static int16_t scpy(struct ubasic_data *data, char *); +static int16_t sconcat(struct ubasic_data *data, char *, char *); +static int16_t sleft(struct ubasic_data *data, char *, int16_t); +static int16_t sright(struct ubasic_data *data, char *, int16_t); +static int16_t smid(struct ubasic_data *data, char *, int16_t, int16_t); +static int16_t sstr(struct ubasic_data *data, VARIABLE_TYPE j); +static int16_t schr(struct ubasic_data *data, VARIABLE_TYPE j); +static uint8_t sinstr(uint16_t, char *, char *); +static char *strptr(struct ubasic_data *data, int16_t size) +{ + return ((char *)((data->stringstack) + (size) + 1)); +} +#endif +static VARIABLE_TYPE relation(struct ubasic_data *data); +static void subsequent_statement(struct ubasic_data *data); +static void statement(struct ubasic_data *data); +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) +static VARIABLE_TYPE recall_statement(struct ubasic_data *data); +#endif + +/*---------------------------------------------------------------------------*/ +#if ( \ + defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) || \ + defined(UBASIC_SCRIPT_HAVE_SLEEP) || \ + defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL)) +/** + * @brief Calculates the elapsed time since the given start time. + * @param start The start time. + * @param now The current time. + * @return The elapsed time, in milliseconds. + */ +static uint32_t mstimer_since(uint32_t start, uint32_t now) +{ + return now - start; +} + +/** + * @brief Set a timer for a time sometime in the future + * + * This function is used to set a timer for a time sometime in the + * future. The function mstimer_remaining() will determine how much time + * is left until the timer expires. + * + * @param t A pointer to the timer + * @param interval The interval before the timer expires. + * @param now The current time. + */ +static void +mstimer_set(struct ubasic_mstimer *t, uint32_t interval, uint32_t now) +{ + t->interval = interval; + t->start = now; +} + +/** + * @brief Check if a timer has expired. + * + * This function tests if a timer has expired and returns true or + * false depending on its status. + * + * @param t A pointer to the timer + * @param now The current time. + * @return Non-zero if the timer has expired, zero otherwise. + */ +static int mstimer_expired(const struct ubasic_mstimer *t, uint32_t now) +{ + return ( + (now - (t->start + t->interval)) < ((uint32_t)(~((uint32_t)0)) >> 1)); +} + +/** + * @brief The amount of time until the timer expires + * @param t A pointer to the timer + * @return The time until the timer expires + */ +static uint32_t mstimer_remaining(const struct ubasic_mstimer *t, uint32_t now) +{ + if (t->interval) { + if (!mstimer_expired(t, now)) { + return ((t->start + t->interval) - now); + } + } + + return 0; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) +static void timer_tic(struct ubasic_data *data, uint8_t ch) +{ + if (ch > UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) { + return; + } + if ((data->mstimer_now)) { + data->tic_toc_timer[ch] = data->mstimer_now(); + } +} + +static int32_t timer_toc(struct ubasic_data *data, uint8_t ch) +{ + uint32_t elapsed; + if ((ch >= UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) || (!data->mstimer_now)) { + return 0; + } + elapsed = mstimer_since(data->tic_toc_timer[ch], data->mstimer_now()); + if (elapsed > INT32_MAX) { + return INT32_MAX; + } + return (int32_t)elapsed; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) +static void mstimer_sleep(struct ubasic_data *data, int32_t ms) +{ + if (data->mstimer_now) { + mstimer_set(&data->sleep_timer, ms, data->mstimer_now()); + } +} + +static int32_t mstimer_sleeping(struct ubasic_data *data) +{ + int32_t ms; + if (data->mstimer_now) { + ms = + (int32_t)mstimer_remaining(&data->sleep_timer, data->mstimer_now()); + if (ms == 0) { + /* automatic disable */ + data->sleep_timer.interval = 0; + } + } else { + ms = 0; + } + return ms; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) +static void mstimer_input_wait(struct ubasic_data *data, int32_t ms) +{ + if (data->mstimer_now) { + mstimer_set(&data->input_wait_timer, ms, data->mstimer_now()); + } +} + +static int32_t mstimer_input_remaining(struct ubasic_data *data) +{ + int32_t ms; + if (data->mstimer_now) { + ms = (int32_t)mstimer_remaining( + &data->input_wait_timer, data->mstimer_now()); + if (ms == 0) { + /* automatic disable */ + data->input_wait_timer.interval = 0; + } + } else { + ms = 0; + } + return ms; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) +static int32_t gpio_read(struct ubasic_data *data, uint8_t ch) +{ + if (data->gpio_read) { + return data->gpio_read(ch); + } + return 0; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) +static void +adc_config(struct ubasic_data *data, uint8_t sampletime, uint8_t nreads) +{ + if (data->adc_config) { + data->adc_config(sampletime, nreads); + } +} +static int32_t adc_read(struct ubasic_data *data, uint8_t channel) +{ + if (data->adc_read) { + return data->adc_read(channel); + } + return 0; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) +static int8_t hw_event(struct ubasic_data *data, uint8_t bit) +{ + if (data->hw_event) { + return data->hw_event(bit); + } + return 0; +} +static void hw_event_clear(struct ubasic_data *data, uint8_t bit) +{ + if (data->hw_event_clear) { + data->hw_event_clear(bit); + } +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) +static uint32_t random_uint32(struct ubasic_data *data, uint8_t size) +{ + if (data->random_uint32) { + return data->random_uint32(size); + } + return 0; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) +static void variable_write( + struct ubasic_data *data, + uint8_t Name, + uint8_t Vartype, + uint8_t datalen_bytes, + uint8_t *dataptr) +{ + if (data->variable_write) { + data->variable_write(Name, Vartype, datalen_bytes, dataptr); + } +} +static void variable_read( + struct ubasic_data *data, + uint8_t Name, + uint8_t Vartype, + uint8_t *dataptr, + uint8_t *datalen) +{ + if (data->variable_read) { + data->variable_read(Name, Vartype, dataptr, datalen); + } +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_BACNET) +static void bacnet_create_object( + struct ubasic_data *data, + uint16_t object_type, + uint32_t instance, + char *object_name) +{ + if (data->bacnet_create_object) { + data->bacnet_create_object(object_type, instance, object_name); + } +} +static void bacnet_write_property( + struct ubasic_data *data, + uint16_t object_type, + uint32_t instance, + uint32_t property_id, + VARIABLE_TYPE value) +{ + if (data->bacnet_write_property) { + data->bacnet_write_property(object_type, instance, property_id, value); + } +} +static VARIABLE_TYPE bacnet_read_property( + struct ubasic_data *data, + uint16_t object_type, + uint32_t instance, + uint32_t property_id) +{ + VARIABLE_TYPE value = 0; + + if (data->bacnet_read_property) { + value = data->bacnet_read_property(object_type, instance, property_id); + } + + return value; +} +#endif + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_PRINT_TO_SERIAL) +static void +serial_write(struct ubasic_data *data, const char *buffer, uint16_t n) +{ + if (data->serial_write) { + data->serial_write(buffer, n); + } +} +#endif +static void serial_write_string(struct ubasic_data *data, const char *msg) +{ +#if defined(UBASIC_SCRIPT_PRINT_TO_SERIAL) + serial_write(data, msg, strlen(msg)); +#endif +} +/** + * @brief Print with a printf string + * @param format - printf format string + * @param ... - variable arguments + * @note This function is only available if + * PRINT_ENABLED is non-zero + * @return number of characters printed + */ +int ubasic_printf(struct ubasic_data *data, const char *format, ...) +{ + int length = 0; + char buffer[256]; + va_list ap; + + va_start(ap, format); + length = vsnprintf(buffer, sizeof(buffer), format, ap); +#if defined(UBASIC_SCRIPT_PRINT_TO_SERIAL) + serial_write(data, buffer, length); +#endif + va_end(ap); + + return length; +} + +int ubasic_getc(struct ubasic_data *data) +{ +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + if (data->ubasic_getc) { + return data->ubasic_getc(); + } +#endif + return EOF; +} + +/*---------------------------------------------------------------------------*/ +void ubasic_clear_variables(struct ubasic_data *data) +{ + int16_t i; + for (i = 0; i < MAX_VARNUM; i++) { + data->variables[i] = 0; +#if defined(VARIABLE_TYPE_ARRAY) + data->arrayvariable[i] = -1; +#endif + } + +#if defined(VARIABLE_TYPE_ARRAY) + data->free_arrayptr = 0; + for (i = 0; i < VARIABLE_TYPE_ARRAY; i++) { + data->arrays_data[i] = 0; + } +#endif + +#if defined(VARIABLE_TYPE_STRING) + data->freebufptr = 0; + for (i = 0; i < MAX_SVARNUM; i++) { + data->stringvariables[i] = -1; + } +#endif +} + +/*---------------------------------------------------------------------------*/ + +static void +ubasic_set_varnum(struct ubasic_data *data, uint8_t varnum, VARIABLE_TYPE value) +{ + if (varnum < MAX_VARNUM) { + data->variables[varnum] = value; + } +} + +static VARIABLE_TYPE ubasic_get_varnum(struct ubasic_data *data, uint8_t varnum) +{ + if (varnum < MAX_VARNUM) { + return data->variables[varnum]; + } + return 0; +} + +#if defined(VARIABLE_TYPE_ARRAY) +/** + * @brief Allocate an array variable + * @param data Pointer to the ubasic data structure + * @param varnum The variable number + * @param newsize The new size of the array + * @note + * array additions: works only for VARIABLE_TYPE 32bit + * array storage: + * 1st entry: [ 31:16 , 15:0] + * varnum size + * entries 2 through size+1 are the array elements + * could work for 16bit values as well + */ +static void ubasic_dim_arrayvarnum( + struct ubasic_data *data, uint8_t varnum, int16_t newsize) +{ + int16_t oldsize; + int16_t current_location; + + if (varnum >= MAX_VARNUM) { + return; + } +_attach_at_the_end: + current_location = data->arrayvariable[varnum]; + if (current_location == -1) { + /* does the array fit in the available memory? */ + if ((data->free_arrayptr + newsize + 1) < VARIABLE_TYPE_ARRAY) { + current_location = data->free_arrayptr; + data->arrayvariable[varnum] = current_location; + data->arrays_data[current_location] = (varnum << 16) | newsize; + data->free_arrayptr += newsize + 1; + return; + } + return; /* failed to allocate*/ + } else { + oldsize = data->arrays_data[current_location]; + } + /* if size of the array is the same as earlier allocated then do nothing */ + if (oldsize == newsize) { + return; + } + /* if this is the last array in arrays_data, just modify the boundary */ + if (current_location + oldsize + 1 == data->free_arrayptr) { + if ((data->free_arrayptr - current_location + newsize) < + VARIABLE_TYPE_ARRAY) { + data->arrays_data[current_location] = (varnum << 16) | newsize; + data->free_arrayptr += (newsize - oldsize); + data->arrays_data[data->free_arrayptr] = 0; + return; + } + /* failed to allocate memory */ + data->arrayvariable[varnum] = -1; + return; + } + /* Array has been allocated before. It is not the last array */ + /* Thus we have to go over all arrays above the current location, and shift + * them down */ + data->arrayvariable[varnum] = -1; + int16_t next_location; + uint16_t mov_size, mov_varnum; + next_location = current_location + oldsize + 1; + do { + mov_varnum = (data->arrays_data[next_location] >> 16); + mov_size = data->arrays_data[next_location]; + + for (uint8_t i = 0; i <= mov_size; i++) { + data->arrays_data[current_location + i] = + data->arrays_data[next_location + i]; + data->arrays_data[next_location + i] = 0; + } + data->arrayvariable[mov_varnum] = current_location; + next_location = next_location + mov_size + 1; + current_location = current_location + mov_size + 1; + data->arrays_data[current_location] = 0; + } while (data->arrays_data[next_location] > 0); + data->free_arrayptr = current_location; + /** now the array should be added to the end of the list: + if there is space do it! */ + goto _attach_at_the_end; +} + +static void ubasic_set_arrayvarnum( + struct ubasic_data *data, uint8_t varnum, uint16_t idx, VARIABLE_TYPE value) +{ + int16_t array; + + if (varnum >= MAX_VARNUM) { + return; + } + array = data->arrayvariable[varnum]; + if (array < 0) { + return; + } + if (array >= VARIABLE_TYPE_ARRAY) { + return; + } + uint16_t size = (uint16_t)data->arrays_data[array]; + if ((size < idx) || (idx < 1)) { + return; + } + + data->arrays_data[array + idx] = value; +} + +static VARIABLE_TYPE +ubasic_get_arrayvarnum(struct ubasic_data *data, uint8_t varnum, uint16_t idx) +{ + int16_t array; + + if (varnum >= MAX_VARNUM) { + return -1; + } + array = data->arrayvariable[varnum]; + if (array < 0) { + return -1; + } + if (array >= VARIABLE_TYPE_ARRAY) { + return -1; + } + uint16_t size = (uint16_t)data->arrays_data[array]; + if ((size < idx) || (idx < 1)) { + return -1; + } + return (VARIABLE_TYPE)data->arrays_data[array + idx]; +} +#endif + +/*---------------------------------------------------------------------------*/ +void ubasic_load_program(struct ubasic_data *data, const char *program) +{ + data->for_stack_ptr = data->gosub_stack_ptr = 0; + if (data->status.bit.notInitialized) { + ubasic_clear_variables(data); + } + data->status.byte = 0x00; + if (program) { + data->program_ptr = program; + tokenizer_init(&data->tree, program); + data->status.bit.isRunning = 1; + } +} +/*---------------------------------------------------------------------------*/ +static void token_error_print(struct ubasic_data *data, VARIABLE_TYPE token) +{ + char msg[32]; + const char *name; + struct tokenizer_data *tree = &data->tree; + + serial_write_string(data, "Err"); + name = tokenizer_name(token); + if (name) { + snprintf(msg, sizeof(msg), "[%s]:", name); + } else { + snprintf(msg, sizeof(msg), "[%u]:", (unsigned)token); + } + serial_write_string(data, msg); + serial_write_string(data, tree->ptr - 1); + serial_write_string(data, "\n"); +} + +/*---------------------------------------------------------------------------*/ +static uint8_t accept(struct ubasic_data *data, VARIABLE_TYPE token) +{ + struct tokenizer_data *tree = &data->tree; + + if (token != tokenizer_token(tree)) { + token_error_print(data, token); + return 1; + } + + tokenizer_next(tree); + return 0; +} + +/*---------------------------------------------------------------------------*/ +static void accept_cr(struct tokenizer_data *tree) +{ + while ((tokenizer_token(tree) != TOKENIZER_EOL) && + (tokenizer_token(tree) != TOKENIZER_ERROR) && + (tokenizer_token(tree) != TOKENIZER_ENDOFINPUT)) { + tokenizer_next(tree); + } + + if (tokenizer_token(tree) == TOKENIZER_EOL) { + tokenizer_next(tree); + } +} + +#if defined(VARIABLE_TYPE_STRING) + +/*---------------------------------------------------------------------------*/ +static uint8_t string_space_check(struct ubasic_data *data, uint16_t l) +{ + /* returns true if not enough room for new string */ + uint8_t i; + i = + ((MAX_BUFFERLEN - data->freebufptr) <= + (l + 2)); /* +2 to play it safe */ + if (i) { + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + } + return i; +} + +/*---------------------------------------------------------------------------*/ +static void clear_stringstack(struct ubasic_data *data) +{ + /* if (!status.bit.stringstackModified ) */ + /* return; */ + + data->status.bit.stringstackModified = 0; + + int16_t bottom = 0; + int16_t len = 0; + + /* find bottom of the stringstack skip allocated stringstack space */ + while (*(data->stringstack + bottom) != 0) { + bottom += strlen(strptr(data, bottom)) + 2; + if (data->freebufptr == bottom) { + return; + } + } + + int16_t top = bottom; + do { + len = strlen(strptr(data, top)) + 2; + + if (*(data->stringstack + top) > 0) { + /* moving stuff down */ + for (uint8_t i = 0; i <= len; i++) { + *(data->stringstack + bottom + i) = + *(data->stringstack + top + i); + } + + /* update variable reference from top to bottom */ + data->stringvariables[*(data->stringstack + bottom) - 1] = bottom; + + bottom += len; + } + top += len; + } while (top < data->freebufptr); + data->freebufptr = bottom; + + return; +} +/*---------------------------------------------------------------------------*/ +/* copy s1 at the end of stringstack and add a header */ +static int16_t scpy(struct ubasic_data *data, char *s1) +{ + if (!s1) { + return (-1); + } + + int16_t bp = data->freebufptr; + int16_t l = strlen(s1); + + if (!l) { + return (-1); + } + + if (string_space_check(data, l)) { + return (-1); + } + + data->status.bit.stringstackModified = 1; + + *(data->stringstack + bp) = 0; + memcpy(data->stringstack + bp + 1, s1, l + 1); + + data->freebufptr = bp + l + 2; + + return bp; +} + +/*---------------------------------------------------------------------------*/ +/* return the concatenation of s1 and s2 in a string at the end */ +/* of the stringbuffer */ +static int16_t sconcat(struct ubasic_data *data, char *s1, char *s2) +{ + int16_t l1 = strlen(s1), l2 = strlen(s2); + + if (string_space_check(data, l1 + l2)) { + return (-1); + } + + int16_t rp = scpy(data, s1); + data->freebufptr -= + 2; /* last char in s1, will be overwritten by s2 header */ + int16_t fp = data->freebufptr; + char dummy = *(data->stringstack + fp); + scpy(data, s2); + *(data->stringstack + fp) = dummy; /* overwrite s2 header */ + return (rp); +} +/*---------------------------------------------------------------------------*/ +static int16_t sleft( + struct ubasic_data *data, + char *s1, + int16_t l) /* return the left l chars of s1 */ +{ + int16_t bp = data->freebufptr; + int16_t rp = bp; + + if (l < 1) { + return (-1); + } + + if (string_space_check(data, l)) { + return (-1); + } + + data->status.bit.stringstackModified = 1; + + if (strlen(s1) <= l) { + return scpy(data, s1); + } else { + /* write header */ + *(data->stringstack + bp) = 0; + bp++; + memcpy(data->stringstack + bp, s1, l); + bp += l; + *(data->stringstack + bp) = 0; + data->freebufptr = bp + 1; + } + + return rp; +} +/*---------------------------------------------------------------------------*/ +static int16_t sright( + struct ubasic_data *data, + char *s1, + int16_t l) /* return the right l chars of s1 */ +{ + int16_t j = strlen(s1); + + if (l < 1) { + return (-1); + } + + if (j <= l) { + l = j; + } + + if (string_space_check(data, l)) { + return (-1); + } + + return scpy(data, s1 + j - l); +} + +/*---------------------------------------------------------------------------*/ +static int16_t smid( + struct ubasic_data *data, + char *s1, + int16_t l1, + int16_t l2) /* return the l2 chars of s1 starting at offset l1 */ +{ + int16_t bp = data->freebufptr; + int16_t rp = bp; + int16_t i, j = strlen(s1); + + if (l2 < 1 || l1 > j) { + return (-1); + } + + if (string_space_check(data, l2)) { + return (-1); + } + + if (l2 > j - l1) { + l2 = j - l1; + } + + data->status.bit.stringstackModified = 1; + + *(data->stringstack + bp) = 0; + bp++; + + for (i = l1; i < l1 + l2; i++) { + *(data->stringstack + bp) = *(s1 + l1 - 1); + bp++; + s1++; + } + + *(data->stringstack + bp) = '\0'; + data->freebufptr = bp + 1; + return rp; +} +/*---------------------------------------------------------------------------*/ +static int16_t sstr( + struct ubasic_data *data, + VARIABLE_TYPE j) /* return the integer j as a string */ +{ + int16_t bp = data->freebufptr; + int16_t rp = bp; + + if (string_space_check(data, 10)) { + return (-1); + } + + data->status.bit.stringstackModified = 1; + + *(data->stringstack + bp) = 0; + bp++; + + sprintf((char *)(data->stringstack + bp), "%ld", (long)j); + + data->freebufptr = bp + strlen((char *)(data->stringstack + bp)) + 1; + + return rp; +} +/*---------------------------------------------------------------------------*/ +static int16_t schr( + struct ubasic_data *data, + VARIABLE_TYPE j) /* return the character whose ASCII code is j */ +{ + int16_t bp = data->freebufptr; + int16_t rp = bp; + + if (string_space_check(data, 1)) { + return (-1); + } + + data->status.bit.stringstackModified = 1; + + *(data->stringstack + bp) = 0; + bp++; + + *(data->stringstack + bp) = j; + bp++; + + *(data->stringstack + bp) = 0; + bp++; + + data->freebufptr = bp; + return rp; +} +/*---------------------------------------------------------------------------*/ +static uint8_t sinstr( + uint16_t j, char *s, char *s1) /* return the position of s1 in s (or 0) */ +{ + char *p; + p = strstr(s + j, s1); + if (p == NULL) { + return 0; + } + return (p - s + 1); +} + +/*---------------------------------------------------------------------------*/ +static int16_t sfactor(struct ubasic_data *data) +{ + /* string form of factor */ + int16_t r = 0, s = 0; + char tmpstring[MAX_STRINGLEN]; + VARIABLE_TYPE i, j; + struct tokenizer_data *tree = &data->tree; + + switch (tokenizer_token(tree)) { + case TOKENIZER_LEFTPAREN: + accept(data, TOKENIZER_LEFTPAREN); + r = sexpr(data); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_STRING: + tokenizer_string(tree, tmpstring, MAX_STRINGLEN); + r = scpy(data, tmpstring); + accept(data, TOKENIZER_STRING); + break; + + case TOKENIZER_LEFT_STR: + accept(data, TOKENIZER_LEFT_STR); + accept(data, TOKENIZER_LEFTPAREN); + s = sexpr(data); + accept(data, TOKENIZER_COMMA); + i = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + i = fixedpt_toint(i); +#endif + r = sleft(data, strptr(data, s), i); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_RIGHT_STR: + accept(data, TOKENIZER_RIGHT_STR); + accept(data, TOKENIZER_LEFTPAREN); + s = sexpr(data); + accept(data, TOKENIZER_COMMA); + i = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + i = fixedpt_toint(i); +#endif + r = sright(data, strptr(data, s), i); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_MID_STR: + accept(data, TOKENIZER_MID_STR); + accept(data, TOKENIZER_LEFTPAREN); + s = sexpr(data); + accept(data, TOKENIZER_COMMA); + i = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + i = fixedpt_toint(i); +#endif + if (tokenizer_token(tree) == TOKENIZER_COMMA) { + accept(data, TOKENIZER_COMMA); + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + } else { + j = 999; /* ensure we get all of it */ + } + r = smid(data, strptr(data, s), i, j); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_STR_STR: + accept(data, TOKENIZER_STR_STR); + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + r = sstr(data, j); + break; + + case TOKENIZER_CHR_STR: + accept(data, TOKENIZER_CHR_STR); + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + if (j < 0 || j > 255) { + j = 0; + } + r = schr(data, j); + break; + + default: + r = ubasic_get_stringvariable(data, tokenizer_variable_num(tree)); + accept(data, TOKENIZER_STRINGVARIABLE); + } + + return r; +} + +/*---------------------------------------------------------------------------*/ + +static int16_t sexpr(struct ubasic_data *data) /* string form of expr */ +{ + int16_t s1, s2; + s1 = sfactor(data); + uint8_t op = tokenizer_token(&data->tree); + while (op == TOKENIZER_PLUS) { + tokenizer_next(&data->tree); + s2 = sfactor(data); + s1 = sconcat(data, strptr(data, s1), strptr(data, s2)); + op = tokenizer_token(&data->tree); + } + return s1; +} +/*---------------------------------------------------------------------------*/ +static uint8_t +slogexpr(struct ubasic_data *data) /* string logical expression */ +{ + int16_t s1, s2; + uint8_t r = 0; + s1 = sexpr(data); + uint8_t op = tokenizer_token(&data->tree); + tokenizer_next(&data->tree); + if (op == TOKENIZER_EQ) { + s2 = sexpr(data); + r = (strcmp(strptr(data, s1), strptr(data, s2)) == 0); + } + return r; +} +/* end of string additions */ +#endif + +/*---------------------------------------------------------------------------*/ +static VARIABLE_TYPE varfactor(struct ubasic_data *data) +{ + VARIABLE_TYPE r; + struct tokenizer_data *tree = &data->tree; + + r = ubasic_get_varnum(data, tokenizer_variable_num(tree)); + accept(data, TOKENIZER_VARIABLE); + return r; +} +/*---------------------------------------------------------------------------*/ +static VARIABLE_TYPE factor(struct ubasic_data *data) +{ + VARIABLE_TYPE r; + VARIABLE_TYPE i, j, k; +#if defined(VARIABLE_TYPE_ARRAY) + uint8_t varnum; +#endif +#if defined(VARIABLE_TYPE_STRING) + int16_t s, s1; +#endif + struct tokenizer_data *tree = &data->tree; + + switch (tokenizer_token(tree)) { +#if defined(VARIABLE_TYPE_STRING) + case TOKENIZER_LEN: + accept(data, TOKENIZER_LEN); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_fromint(strlen(strptr(data, sexpr(data)))); +#else + r = strlen(strptr(data, sexpr(data))); +#endif + break; + + case TOKENIZER_VAL: + accept(data, TOKENIZER_VAL); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + s1 = sexpr(data); + r = str_fixedpt(strptr(data, s1), strlen(strptr(data, s1)), 3); +#else + r = atoi(strptr(sexpr())); +#endif + break; + + case TOKENIZER_ASC: + accept(data, TOKENIZER_ASC); + s = sexpr(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_fromint(*strptr(data, s)); +#else + r = *strptr(data, s); +#endif + break; + + case TOKENIZER_INSTR: + accept(data, TOKENIZER_INSTR); + accept(data, TOKENIZER_LEFTPAREN); + j = 1; + if (tokenizer_token(tree) == TOKENIZER_NUMBER) { + j = tokenizer_num(tree); + accept(data, TOKENIZER_NUMBER); + accept(data, TOKENIZER_COMMA); + } + if (j < 1) { + return 0; + } + s = sexpr(data); + accept(data, TOKENIZER_COMMA); + s1 = sexpr(data); + accept(data, TOKENIZER_RIGHTPAREN); + r = sinstr(j, strptr(data, s), strptr(data, s1)); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_fromint(r); +#endif + break; +#endif + + case TOKENIZER_MINUS: + accept(data, TOKENIZER_MINUS); + r = -factor(data); + break; + + case TOKENIZER_LNOT: + accept(data, TOKENIZER_LNOT); + r = !relation(data); + break; + + case TOKENIZER_NOT: + accept(data, TOKENIZER_LNOT); + r = ~relation(data); + break; + +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) + case TOKENIZER_TOC: + accept(data, TOKENIZER_TOC); + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + r = timer_toc(data, r); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_fromint(r); + accept(data, TOKENIZER_RIGHTPAREN); +#endif + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) + case TOKENIZER_HWE: + accept(data, TOKENIZER_HWE); + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + if (r) { + if (hw_event(data, r - 1)) { + hw_event_clear(data, r - 1); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = FIXEDPT_ONE; +#else + r = 1; +#endif + } else { + r = 0; + } + } + accept(data, TOKENIZER_RIGHTPAREN); + break; +#endif /* #if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) */ + +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + case TOKENIZER_RAN: + accept(data, TOKENIZER_RAN); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = random_uint32(data, FIXEDPT_WBITS); + r = fixedpt_fromint(r); +#else + r = random_uint32(data, 32); +#endif + r = fixedpt_abs(r); + break; +#endif + + case TOKENIZER_ABS: + accept(data, TOKENIZER_ABS); + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); + r = fixedpt_abs(r); + accept(data, TOKENIZER_RIGHTPAREN); + break; + +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + case TOKENIZER_AVERAGEW: + accept(data, TOKENIZER_AVERAGEW); + accept(data, TOKENIZER_LEFTPAREN); + /* latest_reading */ + i = relation(data); + accept(data, TOKENIZER_COMMA); + /* previous_average */ + j = relation(data); + accept(data, TOKENIZER_COMMA); + /* nsamples */ + k = relation(data); + r = fixedpt_averagew(i, j, k); + accept(data, TOKENIZER_RIGHTPAREN); + break; + case TOKENIZER_POWER: + accept(data, TOKENIZER_POWER); + accept(data, TOKENIZER_LEFTPAREN); + /* argument: */ + i = relation(data); + accept(data, TOKENIZER_COMMA); + /* exponent */ + j = relation(data); + r = fixedpt_pow(i, j); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_FLOAT: + r = tokenizer_float(tree); /* 24.8 decimal number */ + accept(data, TOKENIZER_FLOAT); + break; + + case TOKENIZER_SQRT: + accept(data, TOKENIZER_SQRT); + accept(data, TOKENIZER_LEFTPAREN); + r = fixedpt_sqrt(relation(data)); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_SIN: + accept(data, TOKENIZER_SIN); + accept(data, TOKENIZER_LEFTPAREN); + r = fixedpt_sin(relation(data)); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_COS: + accept(data, TOKENIZER_COS); + accept(data, TOKENIZER_LEFTPAREN); + r = fixedpt_cos(relation(data)); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_TAN: + accept(data, TOKENIZER_TAN); + accept(data, TOKENIZER_LEFTPAREN); + r = fixedpt_tan(relation(data)); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_EXP: + accept(data, TOKENIZER_EXP); + accept(data, TOKENIZER_LEFTPAREN); + r = fixedpt_exp(relation(data)); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_LN: + accept(data, TOKENIZER_LN); + accept(data, TOKENIZER_LEFTPAREN); + r = fixedpt_ln(relation(data)); + accept(data, TOKENIZER_RIGHTPAREN); + break; + +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + case TOKENIZER_UNIFORM: + accept(data, TOKENIZER_UNIFORM); + r = random_uint32(data, FIXEDPT_FBITS) & FIXEDPT_FMASK; + break; +#endif + + case TOKENIZER_FLOOR: + accept(data, TOKENIZER_FLOOR); + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); + r = fixedpt_floor(r); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_CEIL: + accept(data, TOKENIZER_CEIL); + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); + r = fixedpt_ceil(r); + accept(data, TOKENIZER_RIGHTPAREN); + break; + + case TOKENIZER_ROUND: + accept(data, TOKENIZER_ROUND); + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); + r = fixedpt_round(r); + accept(data, TOKENIZER_RIGHTPAREN); + break; +#endif /* #if defined(VARIABLE_TYPE_FLOAT_AS ... */ + + case TOKENIZER_INT: + r = tokenizer_int(tree); + accept(data, TOKENIZER_INT); + break; + + case TOKENIZER_NUMBER: + r = tokenizer_num(tree); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_fromint(r); +#endif + accept(data, TOKENIZER_NUMBER); + break; + +#ifdef UBASIC_SCRIPT_HAVE_PWM_CHANNELS + case TOKENIZER_PWM: + accept(data, TOKENIZER_PWM); + accept(data, TOKENIZER_LEFTPAREN); + /* single argument: channel */ + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + if (data->pwm_read) { + r = data->pwm_read((uint8_t)j); + } else { + r = 0; + } +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_fromint(r); +#endif + accept(data, TOKENIZER_RIGHTPAREN); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) + case TOKENIZER_AREAD: + accept(data, TOKENIZER_AREAD); + accept(data, TOKENIZER_LEFTPAREN); + /* single argument: channel as hex value */ + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + r = adc_read(data, (uint8_t)j); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_fromint(r); +#endif + accept(data, TOKENIZER_RIGHTPAREN); + break; +#endif + + case TOKENIZER_LEFTPAREN: + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); + accept(data, TOKENIZER_RIGHTPAREN); + break; + +#if defined(VARIABLE_TYPE_ARRAY) + case TOKENIZER_ARRAYVARIABLE: + varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_ARRAYVARIABLE); + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + r = ubasic_get_arrayvarnum(data, varnum, (uint16_t)j); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) + case TOKENIZER_DREAD: + accept(data, TOKENIZER_LEFTPAREN); + r = relation(data); + r = gpio_read(data, (uint8_t)r); + accept(data, TOKENIZER_RIGHTPAREN); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) + case TOKENIZER_RECALL: + r = recall_statement(data); + break; +#endif +#if defined(UBASIC_SCRIPT_HAVE_BACNET) + case TOKENIZER_BACNET_READ_PROPERTY: + accept(data, TOKENIZER_BACNET_READ_PROPERTY); + accept(data, TOKENIZER_LEFTPAREN); + /* first argument: object type 0..1023 */ + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + accept(data, TOKENIZER_COMMA); + i = relation(data); + /* second argument: instance 0..4194303 */ +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + i = fixedpt_toint(i); +#endif + accept(data, TOKENIZER_COMMA); + /* property */ + /* second argument: property 0..4194303 */ + k = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + k = fixedpt_toint(k); +#endif + r = bacnet_read_property( + data, (uint16_t)j, (uint32_t)i, (uint32_t)k); + accept(data, TOKENIZER_RIGHTPAREN); + break; +#endif + default: + r = varfactor(data); + break; + } + + return r; +} + +/*---------------------------------------------------------------------------*/ + +static VARIABLE_TYPE term(struct ubasic_data *data) +{ + VARIABLE_TYPE f1, f2; + struct tokenizer_data *tree = &data->tree; + +#if defined(VARIABLE_TYPE_STRING) + if (tokenizer_stringlookahead(tree)) { + f1 = slogexpr(data); + } else +#endif + { + f1 = factor(data); + VARIABLE_TYPE op = tokenizer_token(tree); + while (op == TOKENIZER_ASTR || op == TOKENIZER_SLASH || + op == TOKENIZER_MOD) { + tokenizer_next(tree); + f2 = factor(data); + switch (op) { + case TOKENIZER_ASTR: +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + f1 = fixedpt_xmul(f1, f2); +#else + f1 = f1 * f2; +#endif + break; + + case TOKENIZER_SLASH: +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + f1 = fixedpt_xdiv(f1, f2); +#else + f1 = f1 / f2; +#endif + break; + + case TOKENIZER_MOD: + f1 = f1 % f2; + break; + default: + break; + } + op = tokenizer_token(tree); + } + } + return f1; +} +/*---------------------------------------------------------------------------*/ +static VARIABLE_TYPE relation(struct ubasic_data *data) +{ + VARIABLE_TYPE r1, r2; + + r1 = (VARIABLE_TYPE)term(data); + + VARIABLE_TYPE op = tokenizer_token(&data->tree); + + while (op == TOKENIZER_LT || op == TOKENIZER_LE || op == TOKENIZER_GT || + op == TOKENIZER_GE || op == TOKENIZER_EQ || op == TOKENIZER_NE || + op == TOKENIZER_LAND || op == TOKENIZER_LOR || + op == TOKENIZER_PLUS || op == TOKENIZER_MINUS || + op == TOKENIZER_AND || op == TOKENIZER_OR) { + tokenizer_next(&data->tree); + r2 = (VARIABLE_TYPE)term(data); + + switch (op) { + case TOKENIZER_LE: + r1 = (r1 <= r2); + break; + + case TOKENIZER_LT: + r1 = (r1 < r2); + break; + + case TOKENIZER_GT: + r1 = (r1 > r2); + break; + + case TOKENIZER_GE: + r1 = (r1 >= r2); + break; + + case TOKENIZER_EQ: + r1 = (r1 == r2); + break; + + case TOKENIZER_NE: + r1 = (r1 != r2); + break; + + case TOKENIZER_LAND: + r1 = (r1 && r2); + break; + + case TOKENIZER_LOR: + r1 = (r1 || r2); + break; + + case TOKENIZER_PLUS: + r1 = r1 + r2; + break; + + case TOKENIZER_MINUS: + r1 = r1 - r2; + break; + + case TOKENIZER_AND: + r1 = ((int32_t)r1) & ((int32_t)r2); + break; + + case TOKENIZER_OR: + r1 = ((int32_t)r1) | ((int32_t)r2); + break; + + default: + break; + } + op = tokenizer_token(&data->tree); + } + + return r1; +} + +/* TODO: error handling? */ +static uint8_t jump_label(struct ubasic_data *data, char *label) +{ + char currLabel[MAX_LABEL_LEN] = { '\0' }; + struct tokenizer_data *tree = &data->tree; + + tokenizer_init(tree, data->program_ptr); + + while (tokenizer_token(tree) != TOKENIZER_ENDOFINPUT) { + tokenizer_next(tree); + + if (tokenizer_token(tree) == TOKENIZER_COLON) { + tokenizer_next(tree); + if (tokenizer_token(tree) == TOKENIZER_LABEL) { + tokenizer_label(tree, currLabel, sizeof(currLabel)); + if (strcmp(label, currLabel) == 0) { + accept(data, TOKENIZER_LABEL); + return 1; + } + } + } + } + + if (tokenizer_token(tree) == TOKENIZER_ENDOFINPUT) { + return 0; + } + + return 1; +} + +static void gosub_statement(struct ubasic_data *data) +{ + char tmpstring[MAX_STRINGLEN]; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_GOSUB); + if (tokenizer_token(tree) == TOKENIZER_LABEL) { + /* copy label */ + tokenizer_label(tree, tmpstring, MAX_STRINGLEN); + tokenizer_next(tree); + /* check for the end of line */ + while (tokenizer_token(tree) == TOKENIZER_EOL) { + tokenizer_next(tree); + } + if (data->gosub_stack_ptr < MAX_GOSUB_STACK_DEPTH) { + data->gosub_stack[data->gosub_stack_ptr] = + tokenizer_save_offset(tree); + data->gosub_stack_ptr++; + jump_label(data, tmpstring); + return; + } + } + + token_error_print(data, TOKENIZER_GOSUB); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; +} + +static void return_statement(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_RETURN); + if (data->gosub_stack_ptr > 0) { + data->gosub_stack_ptr--; + /* jump_line(gosub_stack[gosub_stack_ptr]); */ + tokenizer_jump_offset(tree, data->gosub_stack[data->gosub_stack_ptr]); + return; + } + token_error_print(data, TOKENIZER_RETURN); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; +} + +static void goto_statement(struct ubasic_data *data) +{ + char tmpstring[MAX_STRINGLEN]; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_GOTO); + + if (tokenizer_token(tree) == TOKENIZER_LABEL) { + tokenizer_label(tree, tmpstring, sizeof(tmpstring)); + tokenizer_next(tree); + jump_label(data, tmpstring); + return; + } + + token_error_print(data, TOKENIZER_GOTO); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; +} + +/*---------------------------------------------------------------------------*/ +#ifdef UBASIC_SCRIPT_HAVE_PWM_CHANNELS +static void pwm_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE j, r; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_PWM); + accept(data, TOKENIZER_LEFTPAREN); + /* first argument: channel */ + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + accept(data, TOKENIZER_COMMA); + /* second argument: duty cycle */ + r = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + accept(data, TOKENIZER_RIGHTPAREN); + if (data->pwm_write) { + data->pwm_write((uint8_t)j, (int32_t)r); + } + + accept_cr(tree); + return; +} + +static void pwmconf_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE j, r; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_PWMCONF); + accept(data, TOKENIZER_LEFTPAREN); + /* first argument: prescaler 0... */ + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + accept(data, TOKENIZER_COMMA); + r = relation(data); + /* second argument: period */ +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + if (data->pwm_config) { + data->pwm_config((uint16_t)j, (uint16_t)r); + } + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); +} + +#endif + +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) +static void areadconf_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE j, r; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_AREADCONF); + accept(data, TOKENIZER_LEFTPAREN); + /* first argument: sampletime 0...7 on STM32 */ + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + if (j < 0) { + j = 0; + } + accept(data, TOKENIZER_COMMA); + r = relation(data); + /* second argument: number of analog sample to average from */ +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + adc_config(data, (uint8_t)j, (uint8_t)r); + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) +static void pinmode_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE i, j, r; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_PINMODE); + accept(data, TOKENIZER_LEFTPAREN); + /* channel */ + i = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + i = fixedpt_toint(i); +#endif + accept(data, TOKENIZER_COMMA); + /* mode */ + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + accept(data, TOKENIZER_COMMA); + /* speed */ + r = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + if (data->gpio_config) { + data->gpio_config((uint8_t)i, (int8_t)j, (int8_t)r); + } + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); + + return; +} + +static void dwrite_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE j, r; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_DWRITE); + accept(data, TOKENIZER_LEFTPAREN); + j = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + j = fixedpt_toint(j); +#endif + accept(data, TOKENIZER_COMMA); + r = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + if (data->gpio_write) { + data->gpio_write((uint8_t)j, (uint8_t)r); + } + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); + + return; +} +#endif /* UBASIC_SCRIPT_HAVE_ANALOG_READ */ + +/*---------------------------------------------------------------------------*/ +#if defined(UBASIC_SCRIPT_HAVE_BACNET) +static void bac_create_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE id, t, s; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_BACNET_CREATE_OBJECT); + accept(data, TOKENIZER_LEFTPAREN); + /* first argument: object type 0..128 */ + t = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + t = fixedpt_toint(t); +#endif + accept(data, TOKENIZER_COMMA); + id = relation(data); + /* second argument: instance 0..4194303 */ +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + id = fixedpt_toint(id); +#endif + accept(data, TOKENIZER_COMMA); + /* object name */ + s = sexpr(data); + bacnet_create_object(data, (uint16_t)t, (uint32_t)id, strptr(data, s)); + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); +} + +static void bac_write_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE t, id, p, v; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_BACNET_WRITE_PROPERTY); + accept(data, TOKENIZER_LEFTPAREN); + /* first argument: object type 0..128 */ + t = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + t = fixedpt_toint(t); +#endif + accept(data, TOKENIZER_COMMA); + id = relation(data); + /* second argument: instance 0..4194303 */ +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + id = fixedpt_toint(id); +#endif + accept(data, TOKENIZER_COMMA); + /* property */ + p = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + p = fixedpt_toint(p); +#endif + accept(data, TOKENIZER_COMMA); + /* value */ + v = relation(data); + bacnet_write_property( + data, (uint16_t)t, (uint32_t)id, (uint32_t)p, (int32_t)v); + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); +} +#endif + +/*---------------------------------------------------------------------------*/ + +static void print_statement(struct ubasic_data *data, uint8_t println) +{ + uint8_t print_how = 0; /*0-xp, 1-hex, 2-oct, 3-dec, 4-bin*/ + char tmpstring[MAX_STRINGLEN]; + struct tokenizer_data *tree = &data->tree; + + /* string additions */ + if (println) { + accept(data, TOKENIZER_PRINTLN); + } else { + accept(data, TOKENIZER_PRINT); + } + do { + if (tokenizer_token(tree) == TOKENIZER_PRINT_HEX) { + tokenizer_next(tree); + print_how = 1; + } else if (tokenizer_token(tree) == TOKENIZER_PRINT_DEC) { + tokenizer_next(tree); + print_how = 2; + } +#if defined(VARIABLE_TYPE_STRING) + if (tokenizer_token(tree) == TOKENIZER_STRING) { + tokenizer_string(tree, tmpstring, MAX_STRINGLEN); + tokenizer_next(tree); + } else +#endif + if (tokenizer_token(tree) == TOKENIZER_COMMA) { + sprintf(tmpstring, " "); + tokenizer_next(tree); + } else { +#if defined(VARIABLE_TYPE_STRING) + if (tokenizer_stringlookahead(tree)) { + sprintf(tmpstring, "%s", strptr(data, sexpr(data))); + } else +#endif + { + if (print_how == 1) { + sprintf(tmpstring, "%lx", (unsigned long)relation(data)); + } else if (print_how == 2) { + sprintf(tmpstring, "%ld", (long)relation(data)); + } else { +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + fixedpt_str(relation(data), tmpstring, FIXEDPT_FBITS / 3); +#else + sprintf(tmpstring, "%ld", relation(data)); +#endif + } + } + /* end of string additions */ + } + serial_write_string(data, tmpstring); + } while (tokenizer_token(tree) != TOKENIZER_EOL && + tokenizer_token(tree) != TOKENIZER_ENDOFINPUT); + + /* printf("\n"); */ + if (println) { + serial_write_string(data, "\n"); + } + + accept_cr(tree); +} +/*---------------------------------------------------------------------------*/ +static void endif_statement(struct ubasic_data *data) +{ + if (data->if_stack_ptr > 0) { + accept(data, TOKENIZER_ENDIF); + accept(data, TOKENIZER_EOL); + data->if_stack_ptr--; + return; + } + token_error_print(data, TOKENIZER_IF); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; +} + +static void if_statement(struct ubasic_data *data) +{ + int8_t else_cntr, endif_cntr, f_nt, f_sl; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_IF); + + VARIABLE_TYPE r = relation(data); + + if (accept(data, TOKENIZER_THEN)) { + token_error_print(data, TOKENIZER_IF); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; + } + + if (tokenizer_token(tree) == TOKENIZER_EOL) { + /* Multi-line IF-Statement */ + /* CR after then -> multiline IF-Statement */ + if (data->if_stack_ptr < MAX_IF_STACK_DEPTH) { + data->if_stack[data->if_stack_ptr] = r; + data->if_stack_ptr++; + } else { + token_error_print(data, TOKENIZER_IF); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; + } + accept(data, TOKENIZER_EOL); + if (r) { + return; + } else { + else_cntr = endif_cntr = + 0; /* number of else/endif possible in current nesting */ + f_nt = f_sl = 0; /* f_nt flag for additional next token, f_fs flag + single line */ + + while (((tokenizer_token(tree) != TOKENIZER_ELSE && + tokenizer_token(tree) != TOKENIZER_ENDIF) || + else_cntr || endif_cntr) && + tokenizer_token(tree) != TOKENIZER_ENDOFINPUT) { + f_nt = 0; + + /* nested if */ + if (tokenizer_token(tree) == TOKENIZER_IF) { + else_cntr += 1; + endif_cntr += 1; + f_sl = 0; + } + + if (tokenizer_token(tree) == TOKENIZER_THEN) { + f_nt = 1; + tokenizer_next(tree); + if (tokenizer_token(tree) != TOKENIZER_EOL) { + f_sl = 1; + } + } + + if (tokenizer_token(tree) == TOKENIZER_ELSE) { + else_cntr--; + if (else_cntr < 0) { + token_error_print(data, TOKENIZER_IF); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; + } + } + + if (!f_sl && (tokenizer_token(tree) == TOKENIZER_ENDIF)) { + endif_cntr--; + if (endif_cntr != else_cntr) { + else_cntr--; + } + } else { + if (f_sl && (tokenizer_token(tree) == TOKENIZER_EOL)) { + f_sl = 0; + endif_cntr--; + if (endif_cntr != else_cntr) { + else_cntr--; + } + } else { + if (tokenizer_token(tree) == TOKENIZER_ENDIF) { + token_error_print(data, TOKENIZER_IF); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; + } + } + } + if (!f_nt) { + tokenizer_next(tree); + } + } + + if (tokenizer_token(tree) == TOKENIZER_ELSE) { + return; + } + } + endif_statement(data); + } else { + /* Single-line IF-Statement */ + if (r) { + statement(data); + } else { + do { + tokenizer_next(tree); + } while (tokenizer_token(tree) != TOKENIZER_ELSE && + tokenizer_token(tree) != TOKENIZER_EOL && + tokenizer_token(tree) != TOKENIZER_ENDOFINPUT); + if (tokenizer_token(tree) == TOKENIZER_ELSE) { + accept(data, TOKENIZER_ELSE); + tokenizer_next(tree); + statement(data); + } else { + accept_cr(tree); + } + } + } +} + +static void else_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE r = 0; + uint8_t endif_cntr, f_nt; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_ELSE); + + if (data->if_stack_ptr > 0) { + r = data->if_stack[data->if_stack_ptr - 1]; + } else { + token_error_print(data, TOKENIZER_ELSE); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; + } + if (tokenizer_token(tree) == TOKENIZER_EOL) { + accept(data, TOKENIZER_EOL); + if (!r) { + return; + } else { + endif_cntr = 0; + while (((tokenizer_token(tree) != TOKENIZER_ENDIF) || endif_cntr) && + tokenizer_token(tree) != TOKENIZER_ENDOFINPUT) { + f_nt = 0; + if (tokenizer_token(tree) == TOKENIZER_IF) { + endif_cntr += 1; + } + if (tokenizer_token(tree) == TOKENIZER_THEN) { + tokenizer_next(tree); + /* then followed by CR -> multi line */ + if (tokenizer_token(tree) == TOKENIZER_EOL) { + f_nt = 1; + } else { /* single line */ + endif_cntr--; + while (tokenizer_token(tree) != TOKENIZER_ENDIF && + tokenizer_token(tree) != TOKENIZER_EOL && + tokenizer_token(tree) != TOKENIZER_ENDOFINPUT) { + tokenizer_next(tree); + } + if (tokenizer_token(tree) == TOKENIZER_ENDIF) { + token_error_print(data, TOKENIZER_ELSE); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; + } + } + } + if (tokenizer_token(tree) == TOKENIZER_ENDIF) { + endif_cntr--; + } + if (!f_nt) { + tokenizer_next(tree); + } + } + } + endif_statement(data); + return; + } + token_error_print(data, TOKENIZER_ELSE); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; +} + +/*---------------------------------------------------------------------------*/ +static void let_statement(struct ubasic_data *data) +{ + uint8_t varnum; + struct tokenizer_data *tree = &data->tree; + + if (tokenizer_token(tree) == TOKENIZER_VARIABLE) { + varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_VARIABLE); + if (!accept(data, TOKENIZER_EQ)) { + ubasic_set_varnum(data, varnum, relation(data)); + } + } +#if defined(VARIABLE_TYPE_STRING) + /* string additions here */ + else if (tokenizer_token(tree) == TOKENIZER_STRINGVARIABLE) { + varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_STRINGVARIABLE); + if (!accept(data, TOKENIZER_EQ)) { + ubasic_set_stringvariable(data, varnum, sexpr(data)); + } + } + /* end of string additions */ +#endif +#if defined(VARIABLE_TYPE_ARRAY) + else if (tokenizer_token(tree) == TOKENIZER_ARRAYVARIABLE) { + varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_ARRAYVARIABLE); + accept(data, TOKENIZER_LEFTPAREN); + VARIABLE_TYPE idx = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + idx = fixedpt_toint(idx); +#endif + accept(data, TOKENIZER_RIGHTPAREN); + if (!accept(data, TOKENIZER_EQ)) { + ubasic_set_arrayvarnum(data, varnum, (uint16_t)idx, relation(data)); + } + } +#endif + + accept_cr(tree); +} + +#if defined(VARIABLE_TYPE_ARRAY) +static void dim_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE size = 0; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_DIM); + uint8_t varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_ARRAYVARIABLE); + + /* accept(data, TOKENIZER_LEFTPAREN); */ + size = relation(data); + +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + size = fixedpt_toint(size); +#endif + + ubasic_dim_arrayvarnum(data, varnum, size); + + /* accept(data, TOKENIZER_RIGHTPAREN); */ + accept_cr(tree); + + /* end of array additions */ +} +#endif + +/*---------------------------------------------------------------------------*/ +static void next_statement(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_NEXT); + uint8_t var = tokenizer_variable_num(tree); + accept(data, TOKENIZER_VARIABLE); + if (data->for_stack_ptr > 0 && + var == data->for_stack[data->for_stack_ptr - 1].for_variable) { + VARIABLE_TYPE value = ubasic_get_varnum(data, var) + + data->for_stack[data->for_stack_ptr - 1].step; + ubasic_set_varnum(data, var, value); + + if (((data->for_stack[data->for_stack_ptr - 1].step > 0) && + (value <= data->for_stack[data->for_stack_ptr - 1].to)) || + ((data->for_stack[data->for_stack_ptr - 1].step < 0) && + (value >= data->for_stack[data->for_stack_ptr - 1].to))) { + /* jump_line(for_stack[for_stack_ptr - 1].line_after_for); */ + tokenizer_jump_offset( + tree, data->for_stack[data->for_stack_ptr - 1].line_after_for); + } else { + data->for_stack_ptr--; + accept_cr(tree); + } + return; + } + + token_error_print(data, TOKENIZER_FOR); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; +} + +/*---------------------------------------------------------------------------*/ + +static void for_statement(struct ubasic_data *data) +{ + uint8_t for_variable; + VARIABLE_TYPE to; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_FOR); + for_variable = tokenizer_variable_num(tree); + accept(data, TOKENIZER_VARIABLE); + accept(data, TOKENIZER_EQ); + ubasic_set_varnum(data, for_variable, relation(data)); + accept(data, TOKENIZER_TO); + to = relation(data); + +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + VARIABLE_TYPE step = FIXEDPT_ONE; +#else + VARIABLE_TYPE step = 1; +#endif + if (tokenizer_token(tree) == TOKENIZER_STEP) { + accept(data, TOKENIZER_STEP); + step = relation(data); + } + accept_cr(tree); + + if (data->for_stack_ptr < MAX_FOR_STACK_DEPTH) { + /* for_stack[for_stack_ptr].line_after_for = tokenizer_line_number(); */ + data->for_stack[data->for_stack_ptr].line_after_for = + tokenizer_save_offset(tree); + data->for_stack[data->for_stack_ptr].for_variable = for_variable; + data->for_stack[data->for_stack_ptr].to = to; + data->for_stack[data->for_stack_ptr].step = step; + data->for_stack_ptr++; + return; + } + + token_error_print(data, TOKENIZER_FOR); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; +} + +/*---------------------------------------------------------------------------*/ + +static void end_statement(struct ubasic_data *data) +{ + accept(data, TOKENIZER_END); + data->status.bit.isRunning = 0; + data->status.bit.Error = 0; +} + +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) +static void sleep_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE r; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_SLEEP); + VARIABLE_TYPE f = relation(data); + if (f > 0) { +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(f * 1000); +#else + r = (uint32_t)f; +#endif + } else { + r = 0; + } + mstimer_sleep(data, r); + + accept_cr(tree); +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) +static void tic_statement(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_TIC); + accept(data, TOKENIZER_LEFTPAREN); + VARIABLE_TYPE f = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + f = fixedpt_toint(f); +#endif + timer_tic(data, f); + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); +} +#endif + +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) +static void input_statement_wait(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + + data->input_how = 0; + accept(data, TOKENIZER_INPUT); + if (tokenizer_token(tree) == TOKENIZER_PRINT_HEX) { + tokenizer_next(tree); + data->input_how = 1; + } else if (tokenizer_token(tree) == TOKENIZER_PRINT_DEC) { + tokenizer_next(tree); + data->input_how = 2; + } + + if (tokenizer_token(tree) == TOKENIZER_VARIABLE) { + data->input_varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_VARIABLE); + data->input_type = 0; + } +#if defined(VARIABLE_TYPE_STRING) + /* string additions here */ + else if (tokenizer_token(tree) == TOKENIZER_STRINGVARIABLE) { + data->input_varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_STRINGVARIABLE); + data->input_type = 1; + } +/* end of string additions */ +#endif +#if defined(VARIABLE_TYPE_ARRAY) + else if (tokenizer_token(tree) == TOKENIZER_ARRAYVARIABLE) { + data->input_varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_ARRAYVARIABLE); + + accept(data, TOKENIZER_LEFTPAREN); + data->input_array_index = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + data->input_array_index = fixedpt_toint(data->input_array_index); +#endif + accept(data, TOKENIZER_RIGHTPAREN); + data->input_type = 2; + } +#endif + + /* get next token: */ + /* CR */ + /* or */ + /* , timeout */ + if (tokenizer_token(tree) == TOKENIZER_COMMA) { + accept(data, TOKENIZER_COMMA); + VARIABLE_TYPE r = relation(data); +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = fixedpt_toint(r); +#endif + if (r > 0) { + mstimer_input_wait(data, r); + } + } + + accept_cr(tree); + + data->status.bit.WaitForSerialInput = 1; +} + +static void serial_getline_completed(struct ubasic_data *data) +{ + /* process if something has been received. */ + /* otherwise leave the variable content unchanged. */ + if (strlen(data->statement) > 0) { + if ((data->input_type == 0) +#if defined(VARIABLE_TYPE_ARRAY) + || (data->input_type == 2) +#endif + ) { + VARIABLE_TYPE r; + if ((data->input_how == 1) || (data->input_how == 2)) { + r = atoi(data->statement); + } else { + /* process number */ +#if defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_24_8) || \ + defined(VARIABLE_TYPE_FLOAT_AS_FIXEDPT_22_10) + r = str_fixedpt( + data->statement, MAX_STRINGLEN, FIXEDPT_FBITS >> 1); +#else + r = atoi(data->statement); +#endif + } + + if (data->input_type == 0) { + ubasic_set_varnum(data, data->input_varnum, r); + } +#if defined(VARIABLE_TYPE_ARRAY) + else if (data->input_type == 2) { + ubasic_set_arrayvarnum( + data, data->input_varnum, data->input_array_index, r); + } +#endif + } +#if defined(VARIABLE_TYPE_STRING) + else if (data->input_type == 1) { + ubasic_set_stringvariable( + data, data->input_varnum, scpy(data, data->statement)); + } +#endif + } + memset(data->statement, 0, sizeof(data->statement)); + data->status.bit.WaitForSerialInput = 0; +} +#endif + +/*---------------------------------------------------------------------------*/ +static void while_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE r; + int8_t while_cntr; + uint16_t while_offset; + struct tokenizer_data *tree = &data->tree; + + /* this is where we jump to after 'endwhile' */ + while_offset = tokenizer_save_offset(tree); + accept(data, TOKENIZER_WHILE); + if (data->while_stack_ptr == MAX_WHILE_STACK_DEPTH) { + token_error_print(data, TOKENIZER_WHILE); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + } + /* this makes sure that the jump to the same while is ignored */ + if ((data->while_stack_ptr == 0) || + ((data->while_stack_ptr > 0) && + (data->while_stack[data->while_stack_ptr - 1].line_while != + while_offset))) { + data->while_stack[data->while_stack_ptr].line_while = while_offset; + /* we don't know it yet */ + data->while_stack[data->while_stack_ptr].line_after_endwhile = -1; + data->while_stack_ptr++; + } + + r = relation(data); + + if (data->while_stack_ptr == 0) { + token_error_print(data, TOKENIZER_WHILE); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + return; + } + + if (r) { + accept_cr(tree); + return; + } + + if (data->while_stack[data->while_stack_ptr - 1].line_after_endwhile > 0) { + /* we have traversed while loop once to its end already. */ + /* thus we know where the loop ends. we just use that to jump there. */ + tokenizer_jump_offset( + tree, + data->while_stack[data->while_stack_ptr - 1].line_after_endwhile); + } else { + /* first time the loop is entered the condition is not satisfied, */ + /* so we gobble the lines until we reach the matching endwhile */ + while_cntr = 0; + while ((tokenizer_token(tree) != TOKENIZER_ENDWHILE || while_cntr) && + (tokenizer_token(tree) != TOKENIZER_ENDOFINPUT)) { + if (tokenizer_token(tree) == TOKENIZER_WHILE) { + while_cntr += 1; + } + if (tokenizer_token(tree) == TOKENIZER_ENDWHILE) { + while_cntr -= 1; + } + tokenizer_next(tree); + } + data->while_stack_ptr--; + accept(data, TOKENIZER_ENDWHILE); + accept(data, TOKENIZER_EOL); + } + + return; +} +/*---------------------------------------------------------------------------*/ +static void endwhile_statement(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_ENDWHILE); + if (data->while_stack_ptr > 0) { + /* jump_line(while_stack[while_stack_ptr-1]); */ + if (data->while_stack[data->while_stack_ptr - 1].line_after_endwhile == + -1) { + data->while_stack[data->while_stack_ptr - 1].line_after_endwhile = + tokenizer_save_offset(tree); + } + tokenizer_jump_offset( + tree, data->while_stack[data->while_stack_ptr - 1].line_while); + return; + } + token_error_print(data, TOKENIZER_FOR); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; +} +/*---------------------------------------------------------------------------*/ + +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) + +static VARIABLE_TYPE recall_statement(struct ubasic_data *data) +{ + VARIABLE_TYPE rval = 0; + uint8_t *dataptr; + uint8_t *datalen; + uint8_t var_type; + struct tokenizer_data *tree = &data->tree; + + accept(data, TOKENIZER_RECALL); + accept(data, TOKENIZER_LEFTPAREN); + if (tokenizer_token(tree) == TOKENIZER_VARIABLE) { + var_type = UBASIC_RECALL_STORE_TYPE_VARIABLE; + data->varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_VARIABLE); + dataptr = (uint8_t *)&data->variables[data->varnum]; + datalen = (uint8_t *)&rval; + variable_read(data, data->varnum, var_type, dataptr, datalen); + rval >>= 2; + } +#if defined(VARIABLE_TYPE_STRING) + else if (tokenizer_token(tree) == TOKENIZER_STRINGVARIABLE) { + var_type = UBASIC_RECALL_STORE_TYPE_STRING; + data->varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_STRINGVARIABLE); + char dummy_s[MAX_STRINGLEN] = { 0 }; + dataptr = (uint8_t *)dummy_s; + datalen = (uint8_t *)&rval; + variable_read(data, data->varnum, var_type, dataptr, datalen); + if (rval > 0) { + ubasic_set_stringvariable( + data, data->varnum, scpy(data, (char *)dummy_s)); + } + + clear_stringstack(data); + } +#endif +#if defined(VARIABLE_TYPE_ARRAY) + else if (tokenizer_token(tree) == TOKENIZER_ARRAYVARIABLE) { + var_type = UBASIC_RECALL_STORE_TYPE_ARRAY; + data->varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_ARRAYVARIABLE); + VARIABLE_TYPE dummy_a[VARIABLE_TYPE_ARRAY + 1]; + dataptr = (uint8_t *)dummy_a; + datalen = (uint8_t *)&rval; + variable_read(data, data->varnum, 2, dataptr, datalen); + if (rval > 0) { + rval >>= 2; + ubasic_dim_arrayvarnum(data, data->varnum, rval); + for (uint8_t i = 0; i < rval; i++) { + ubasic_set_arrayvarnum(data, data->varnum, i + 1, dummy_a[i]); + } + } + } +#endif + accept(data, TOKENIZER_RIGHTPAREN); + return rval; +} + +static void store_statement(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + uint8_t *dataptr; + uint8_t datalen; + uint8_t var_type; + + accept(data, TOKENIZER_STORE); + accept(data, TOKENIZER_LEFTPAREN); + + if (tokenizer_token(tree) == TOKENIZER_VARIABLE) { + var_type = UBASIC_RECALL_STORE_TYPE_VARIABLE; + data->varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_VARIABLE); + dataptr = (uint8_t *)&data->variables[data->varnum]; + variable_write(data, data->varnum, var_type, 4, dataptr); + } +#if defined(VARIABLE_TYPE_STRING) + else if (tokenizer_token(tree) == TOKENIZER_STRINGVARIABLE) { + var_type = UBASIC_RECALL_STORE_TYPE_STRING; + data->varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_STRINGVARIABLE); + dataptr = (uint8_t *)strptr(data, data->stringvariables[data->varnum]); + datalen = strlen((char *)dataptr); + variable_write(data, data->varnum, var_type, datalen, dataptr); + } +#endif +#if defined(VARIABLE_TYPE_ARRAY) + else if (tokenizer_token(tree) == TOKENIZER_ARRAYVARIABLE) { + var_type = UBASIC_RECALL_STORE_TYPE_ARRAY; + data->varnum = tokenizer_variable_num(tree); + accept(data, TOKENIZER_ARRAYVARIABLE); + datalen = 4 * + (data->arrays_data[data->arrayvariable[data->varnum]] & 0x0000ffff); + dataptr = + (uint8_t *)&data->arrays_data[data->arrayvariable[data->varnum]]; + variable_write(data, data->varnum, var_type, datalen, dataptr); + } +#endif + accept(data, TOKENIZER_RIGHTPAREN); + accept_cr(tree); +} +/*---------------------------------------------------------------------------*/ +#endif + +/*---------------------------------------------------------------------------*/ +static void statement(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + uint8_t println = 0; + VARIABLE_TYPE token; + + if (data->status.bit.Error) { + return; + } + token = tokenizer_token(tree); + switch (token) { + case TOKENIZER_EOL: + accept(data, TOKENIZER_EOL); + break; + + case TOKENIZER_PRINTLN: + println = 1; + case TOKENIZER_PRINT: + print_statement(data, println); + break; + + case TOKENIZER_IF: + if_statement(data); + break; + + case TOKENIZER_ELSE: + else_statement(data); + break; + + case TOKENIZER_ENDIF: + endif_statement(data); + break; + + case TOKENIZER_GOTO: + goto_statement(data); + break; + + case TOKENIZER_GOSUB: + gosub_statement(data); + break; + + case TOKENIZER_RETURN: + return_statement(data); + break; + + case TOKENIZER_FOR: + for_statement(data); + break; + + case TOKENIZER_NEXT: + next_statement(data); + break; + + case TOKENIZER_WHILE: + while_statement(data); + break; + + case TOKENIZER_ENDWHILE: + endwhile_statement(data); + break; + + case TOKENIZER_END: + end_statement(data); + break; + + case TOKENIZER_LET: + accept(data, TOKENIZER_LET); /* Fall through: Nothing to do! */ + case TOKENIZER_VARIABLE: +#if defined(VARIABLE_TYPE_STRING) + /* string addition */ + case TOKENIZER_STRINGVARIABLE: + /* end of string addition */ +#endif +#if defined(VARIABLE_TYPE_ARRAY) + case TOKENIZER_ARRAYVARIABLE: +#endif + let_statement(data); + break; + +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + case TOKENIZER_INPUT: + input_statement_wait(data); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) + case TOKENIZER_SLEEP: + sleep_statement(data); + break; +#endif + +#if defined(VARIABLE_TYPE_ARRAY) + case TOKENIZER_DIM: + dim_statement(data); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) + case TOKENIZER_TIC: + tic_statement(data); + break; +#endif + +#ifdef UBASIC_SCRIPT_HAVE_PWM_CHANNELS + case TOKENIZER_PWM: + pwm_statement(data); + break; + + case TOKENIZER_PWMCONF: + pwmconf_statement(data); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) + case TOKENIZER_AREADCONF: + areadconf_statement(data); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) + case TOKENIZER_PINMODE: + pinmode_statement(data); + break; + + case TOKENIZER_DWRITE: + dwrite_statement(data); + break; +#endif /* UBASIC_SCRIPT_HAVE_GPIO_CHANNELS */ + +#if defined(UBASIC_SCRIPT_HAVE_BACNET) + case TOKENIZER_BACNET_CREATE_OBJECT: + bac_create_statement(data); + break; + case TOKENIZER_BACNET_WRITE_PROPERTY: + bac_write_statement(data); + break; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) + case TOKENIZER_STORE: + store_statement(data); + break; + + case TOKENIZER_RECALL: + recall_statement(data); + break; +#endif /* UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH */ + + case TOKENIZER_CLEAR: + ubasic_clear_variables(data); + accept_cr(tree); + break; + + default: + token_error_print(data, token); + data->status.bit.isRunning = 0; + data->status.bit.Error = 1; + } +} +/*---------------------------------------------------------------------------*/ +static void subsequent_statement(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + + while (1) { + if (tokenizer_token(tree) == TOKENIZER_COLON) { + accept(data, TOKENIZER_COLON); + if (accept(data, TOKENIZER_LABEL)) { + return; + } + continue; + } + break; + } + statement(data); + + return; +} +/*---------------------------------------------------------------------------*/ +static bool ubasic_program_finished(struct ubasic_data *data) +{ + struct tokenizer_data *tree = &data->tree; + + if (data->status.bit.isRunning) { + return tokenizer_finished(tree); + } + + return (tokenizer_finished(tree) || (data->status.bit.Error)); +} + +/*---------------------------------------------------------------------------*/ +/** + * @brief Run the program + * @param data - ubasic data structure + * @return zero if not running, -1 if error, >0 if running or waiting + */ +int32_t ubasic_run_program(struct ubasic_data *data) +{ + int32_t wait_msecs = 0; + + if (data->status.bit.isRunning == 0) { + return 0; + } + if (data->status.bit.Error == 1) { + return -1; + } +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) + wait_msecs = mstimer_sleeping(data); + if (wait_msecs > 0) { + return wait_msecs; + } +#endif +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + if (data->status.bit.WaitForSerialInput) { + if (!ubasic_getline(data, ubasic_getc(data))) { + wait_msecs = mstimer_input_remaining(data); + if (wait_msecs > 0) { + return wait_msecs; + } + } + serial_getline_completed(data); + } +#endif +#if defined(VARIABLE_TYPE_STRING) + /* string additions */ + clear_stringstack(data); + /* end of string additions */ +#endif + if (ubasic_program_finished(data)) { + if (data->status.bit.Error == 1) { + return -1; + } + return 0; + } + subsequent_statement(data); + + return 1; +} + +/*---------------------------------------------------------------------------*/ +uint8_t ubasic_execute_statement(struct ubasic_data *data, char *stmt) +{ + struct tokenizer_data *tree = &data->tree; + data->status.byte = 0; + + data->program_ptr = stmt; + data->for_stack_ptr = data->gosub_stack_ptr = 0; + tokenizer_init(tree, stmt); + do { +#if defined(VARIABLE_TYPE_STRING) + clear_stringstack(data); +#endif + + statement(data); + + if (data->status.bit.Error) { + break; + } + +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + while (data->status.bit.WaitForSerialInput) { + if (!ubasic_getline(data, ubasic_getc(data))) { + if (mstimer_input_remaining(data) > 0) { + continue; + } + } + serial_getline_completed(data); + } +#endif + +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) + while (mstimer_sleeping(data) > 0) { + /* FIXME: maybe just a return until the sleep is over? */ + } +#endif + + } while (!tokenizer_finished(tree)); + + return data->status.byte; +} + +/*---------------------------------------------------------------------------*/ +uint8_t ubasic_waiting_for_input(struct ubasic_data *data) +{ + return (data->status.bit.WaitForSerialInput); +} + +/** + * @brief Append a character to a line of text, if there is space + * @param buffer - buffer that receives the appended character + * @param buffer_len - sizeof the buffer + * @param ch - character to append + * @return 1 if character was appended, 0 if not + */ +static int line_append_char(char *buffer, unsigned buffer_len, char ch) +{ + unsigned len = 0; + bool status = false; + + len = strlen(buffer); + if (len < (buffer_len - 1)) { + buffer[len] = ch; + buffer[len + 1] = 0; + status = true; + } + + return status; +} + +/** + * @brief Remove the trailing character from a line of text + * @param buffer - buffer to remove the trailing character + * @param buffer_len - sizeof the buffer + * @return 1 if character was removed, 0 if not + */ +static int line_remove_char(char *buffer, size_t buffer_len) +{ + unsigned len = 0; + int status = 0; + + len = strlen(buffer); + if ((len > 0) && (len < (buffer_len - 1))) { + buffer[len - 1] = 0; + status = 1; + } + + return status; +} + +/** + * @brief Non-blocking serial getline task + * @param data - ubasic data structure + * @return 1 if statement is complete and ready to process, 0 if not + */ +uint8_t ubasic_getline(struct ubasic_data *data, int ch) +{ + uint8_t eol = 0; + + if (ch == EOF) { + return 0; + } + switch (ch) { + case '\a': + case '\f': + case '\t': + case '\r': + case '\v': + /* ignored characters */ + break; + case 0x1B: + /* escape */ + /* clear buffer */ + data->statement[0] = 0; + eol = 1; + break; + case '\b': + /* console backspace */ + line_remove_char(data->statement, sizeof(data->statement)); + break; + case 0x7F: + /* DEL */ + line_remove_char(data->statement, sizeof(data->statement)); + break; + case '\n': + /* enter */ + eol = 1; + break; + default: + /* all the rest of the characters */ + if (line_append_char( + data->statement, sizeof(data->statement), ch)) { + } else { + eol = 1; + } + break; + } + + return eol; +} + +uint8_t ubasic_finished(struct ubasic_data *data) +{ + return (ubasic_program_finished(data) || data->status.bit.isRunning == 0); +} + +/*---------------------------------------------------------------------------*/ + +void ubasic_set_variable( + struct ubasic_data *data, char variable, VARIABLE_TYPE value) +{ + if (isupper(variable)) { + uint8_t varnum = variable - 'A'; + ubasic_set_varnum(data, varnum, value); + } else if (islower(variable)) { + uint8_t varnum = variable - 'a'; + ubasic_set_varnum(data, varnum, value); + } +} + +VARIABLE_TYPE ubasic_get_variable(struct ubasic_data *data, char variable) +{ + if (isupper(variable)) { + uint8_t varnum = variable - 'A'; + return ubasic_get_varnum(data, varnum); + } else if (islower(variable)) { + uint8_t varnum = variable - 'a'; + return ubasic_get_varnum(data, varnum); + } + return 0; +} + +#if defined(VARIABLE_TYPE_STRING) +/* */ +/* string additions */ +/* */ +/*---------------------------------------------------------------------------*/ +void ubasic_set_stringvariable( + struct ubasic_data *data, uint8_t svarnum, int16_t svalue) +{ + if (svarnum < MAX_SVARNUM) { + /* was it previously allocated? */ + if (data->stringvariables[svarnum] > -1) { + *(data->stringstack + data->stringvariables[svarnum]) = 0; + } + + data->stringvariables[svarnum] = svalue; + if ((svalue >= 0) && (svalue < MAX_BUFFERLEN)) { + *(data->stringstack + svalue) = svarnum + 1; + } +#if defined(UBASIC_DEBUG_STRINGVARIABLES) + serial_write_string(data, "set_stringvar:"); + char msg[12]; + sprintf(msg, "[%d]", stringvariables[svarnum]); + serial_write_string(data, msg); + serial_write_string(data, strptr(stringvariables[svarnum])); + serial_write_string(data, "\n"); +#endif + } +} + +/*---------------------------------------------------------------------------*/ + +int16_t ubasic_get_stringvariable(struct ubasic_data *data, uint8_t varnum) +{ + if (varnum < MAX_SVARNUM) { +#if defined(UBASIC_DEBUG_STRINGVARIABLES) + serial_write_string(data, "get_stringvar:"); + char msg[12]; + sprintf(msg, "[%d]", stringvariables[varnum]); + serial_write_string(data, msg); + serial_write_string(data, strptr(stringvariables[varnum])); + serial_write_string(data, "\n"); +#endif + + return data->stringvariables[varnum]; + } + + return (-1); +} +/* */ +/* end of string additions */ +/* */ +#endif + +#if defined(VARIABLE_TYPE_ARRAY) +/** + * @brief DIM an array variable + * @param data - ubasic data structure + * @param variable - array variable 'a' through 'z' + * @param size - size of the array + */ +void ubasic_dim_arrayvariable( + struct ubasic_data *data, char variable, int16_t size) +{ + if (isupper(variable)) { + uint8_t varnum = variable - 'A'; + ubasic_dim_arrayvarnum(data, varnum, size); + } else if (islower(variable)) { + uint8_t varnum = variable - 'a'; + ubasic_dim_arrayvarnum(data, varnum, size); + } +} + +/** + * @brief Set the value of an array variable at specific index + * @param data - ubasic data structure + * @param variable - array variable 'a' through 'z' + * @param idx - index 1..N of the array + * @param value - value to set + */ +void ubasic_set_arrayvariable( + struct ubasic_data *data, char variable, uint16_t idx, VARIABLE_TYPE value) +{ + if (isupper(variable)) { + uint8_t varnum = variable - 'A'; + ubasic_set_arrayvarnum(data, varnum, idx, value); + } else if (islower(variable)) { + uint8_t varnum = variable - 'a'; + ubasic_set_arrayvarnum(data, varnum, idx, value); + } +} + +/** + * @brief Get the value of an array variable at specific index + * @param data - ubasic data structure + * @param variable - array variable 'a' through 'z' + * @param idx - index 1..N of the array + * @param value - value to get + */ +VARIABLE_TYPE +ubasic_get_arrayvariable(struct ubasic_data *data, char variable, uint16_t idx) +{ + VARIABLE_TYPE value = 0; + + if (isupper(variable)) { + uint8_t varnum = variable - 'A'; + value = ubasic_get_arrayvarnum(data, varnum, idx); + } else if (islower(variable)) { + uint8_t varnum = variable - 'a'; + value = ubasic_get_arrayvarnum(data, varnum, idx); + } + + return value; +} +#endif +/*---------------------------------------------------------------------------*/ diff --git a/src/bacnet/basic/program/ubasic/ubasic.h b/src/bacnet/basic/program/ubasic/ubasic.h new file mode 100644 index 00000000..f36aa3c4 --- /dev/null +++ b/src/bacnet/basic/program/ubasic/ubasic.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2006, Adam Dunkels + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Modified to support simple string variables and functions by David Mitchell + * November 2008. + * Changes and additions are marked 'string additions' throughout + * + * Modified to support fixed point arithmetic, and number of math and io and + * hardware functions by Marijan Kostrun, January-February 2018. + * uBasic-Plus Copyright (c) 2017-2018, M. Kostrun + * + * Modified to support multiple programs and use a structure to hold + * each program state. 2025 Steve Karg + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __UBASIC_H__ +#define __UBASIC_H__ +#include +#include +#include +#include "config.h" +#include "platform.h" +#include "tokenizer.h" + +/* define a status structure with bit fields */ +typedef union { + uint8_t byte; + struct { + uint8_t notInitialized : 1; + uint8_t stringstackModified : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t WaitForSerialInput : 1; + uint8_t Error : 1; + uint8_t isRunning : 1; + } bit; +} UBASIC_STATUS; + +#define MAX_FOR_STACK_DEPTH 4 +struct ubasic_for_state { + uint16_t line_after_for; + uint8_t for_variable; + VARIABLE_TYPE to; + VARIABLE_TYPE step; +}; + +#define MAX_WHILE_STACK_DEPTH 4 +struct ubasic_while_state { + uint16_t line_while; + int16_t line_after_endwhile; +}; + +#define MAX_GOSUB_STACK_DEPTH 10 +#define MAX_IF_STACK_DEPTH 4 + +#define UBASIC_SERIAL_INPUT_MS 50 + +enum { + UBASIC_RECALL_STORE_TYPE_VARIABLE = 0, + UBASIC_RECALL_STORE_TYPE_STRING = 1, + UBASIC_RECALL_STORE_TYPE_ARRAY = 2, + UBASIC_RECALL_STORE_TYPE_MAX = 3 +}; + +/** + * A timer. + * + * This structure is used for declaring a timer. The timer must be set + * with mstimer_set() before it can be used. + */ +struct ubasic_mstimer { + uint32_t start; + uint32_t interval; +}; + +struct ubasic_data { + UBASIC_STATUS status; + uint8_t input_how; + struct tokenizer_data tree; + +#if defined(VARIABLE_TYPE_ARRAY) + VARIABLE_TYPE arrays_data[VARIABLE_TYPE_ARRAY]; + int16_t free_arrayptr; + int16_t arrayvariable[MAX_VARNUM]; +#endif + const char *program_ptr; + + uint16_t gosub_stack[MAX_GOSUB_STACK_DEPTH]; + uint8_t gosub_stack_ptr; + + struct ubasic_for_state for_stack[MAX_FOR_STACK_DEPTH]; + uint8_t for_stack_ptr; + + int16_t if_stack[MAX_IF_STACK_DEPTH]; + uint8_t if_stack_ptr; + + struct ubasic_while_state while_stack[MAX_WHILE_STACK_DEPTH]; + uint8_t while_stack_ptr; + + VARIABLE_TYPE variables[MAX_VARNUM]; + +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) + uint8_t varnum; +#endif + +#if defined(VARIABLE_TYPE_STRING) + char stringstack[MAX_BUFFERLEN]; + int16_t freebufptr; + int16_t stringvariables[MAX_SVARNUM]; +#endif + +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + uint8_t input_varnum; + uint8_t input_type; + char statement[UBASIC_STATEMENT_SIZE]; +#endif +#if defined(VARIABLE_TYPE_ARRAY) + VARIABLE_TYPE input_array_index; +#endif +#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) + uint32_t tic_toc_timer[UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS]; +#endif +#if defined(UBASIC_SCRIPT_HAVE_SLEEP) + struct ubasic_mstimer input_wait_timer; + struct ubasic_mstimer sleep_timer; +#endif + +/* API for hardware drivers */ +#if defined(UBASIC_SCRIPT_HAVE_PWM_CHANNELS) + void (*pwm_config)(uint16_t psc, uint16_t per); + void (*pwm_write)(uint8_t ch, int32_t dutycycle); + int32_t (*pwm_read)(uint8_t ch); +#endif +#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS) + void (*gpio_config)(uint8_t ch, int8_t mode, uint8_t freq); + void (*gpio_write)(uint8_t ch, uint8_t pin_state); + int32_t (*gpio_read)(uint8_t ch); +#endif +#if ( \ + defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) || \ + defined(UBASIC_SCRIPT_HAVE_SLEEP) || \ + defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL)) + uint32_t (*mstimer_now)(void); +#endif +#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ) + void (*adc_config)(uint8_t sampletime, uint8_t nreads); + int32_t (*adc_read)(uint8_t channel); +#endif +#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS) + int8_t (*hw_event)(uint8_t bit); + void (*hw_event_clear)(uint8_t bit); +#endif +#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR) + uint32_t (*random_uint32)(uint8_t size); +#endif +#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH) + void (*variable_write)( + uint8_t Name, uint8_t Vartype, uint8_t datalen_bytes, uint8_t *dataptr); + void (*variable_read)( + uint8_t Name, uint8_t Vartype, uint8_t *dataptr, uint8_t *datalen); +#endif +#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL) + int (*ubasic_getc)(void); +#endif +#if defined(UBASIC_SCRIPT_PRINT_TO_SERIAL) + void (*serial_write)(const char *buffer, uint16_t n); +#endif +#if defined(UBASIC_SCRIPT_HAVE_BACNET) + void (*bacnet_create_object)( + uint16_t object_type, uint32_t instance, char *object_name); + void (*bacnet_write_property)( + uint16_t object_type, + uint32_t instance, + uint32_t property_id, + VARIABLE_TYPE value); + VARIABLE_TYPE(*bacnet_read_property) + (uint16_t object_type, uint32_t instance, uint32_t property_id); +#endif +}; + +void ubasic_load_program(struct ubasic_data *data, const char *program); +void ubasic_clear_variables(struct ubasic_data *data); +int32_t ubasic_run_program(struct ubasic_data *data); +uint8_t ubasic_execute_statement(struct ubasic_data *data, char *statement); +uint8_t ubasic_finished(struct ubasic_data *data); + +uint8_t ubasic_waiting_for_input(struct ubasic_data *data); +uint8_t ubasic_getline(struct ubasic_data *data, int ch); +int ubasic_printf(struct ubasic_data *data, const char *format, ...); +int ubasic_getc(struct ubasic_data *data); + +VARIABLE_TYPE ubasic_get_variable(struct ubasic_data *data, char variable); +void ubasic_set_variable( + struct ubasic_data *data, char variable, VARIABLE_TYPE value); + +#if defined(VARIABLE_TYPE_ARRAY) +void ubasic_dim_arrayvariable( + struct ubasic_data *data, char variable, int16_t size); +void ubasic_set_arrayvariable( + struct ubasic_data *data, char variable, uint16_t idx, VARIABLE_TYPE value); +VARIABLE_TYPE +ubasic_get_arrayvariable(struct ubasic_data *data, char variable, uint16_t idx); +#endif + +#if defined(VARIABLE_TYPE_STRING) +int16_t ubasic_get_stringvariable(struct ubasic_data *data, uint8_t varnum); +void ubasic_set_stringvariable( + struct ubasic_data *data, uint8_t varnum, int16_t size); +#endif + +/* API to interface and initialize the ported hardware drivers */ +void ubasic_port_init(struct ubasic_data *data); + +#endif /* __UBASIC_H__ */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a531e6c5..b6d02467 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,6 +30,7 @@ if (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "GNU") # since they are common in embedded add_compile_options(-Wno-sign-conversion -Wno-conversion) add_compile_options(-Wno-sign-compare) + add_compile_options(-Wno-implicit-fallthrough) # Just noise from clang if (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "AppleClang") @@ -43,6 +44,7 @@ if (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "GNU") add_compile_options(-Wno-missing-declarations) add_compile_options(-Wno-unused-but-set-variable) add_compile_options(-Wno-write-strings) + add_compile_options(-Wno-implicit-fallthrough) add_link_options(-fprofile-arcs -ftest-coverage) endif() @@ -171,6 +173,8 @@ list(APPEND testdirs bacnet/basic/object/structured_view bacnet/basic/object/time_value bacnet/basic/object/trendlog + # basic/program + bacnet/basic/program/ubasic # basic/sys bacnet/basic/sys/color_rgb bacnet/basic/sys/days diff --git a/test/bacnet/basic/object/program/src/main.c b/test/bacnet/basic/object/program/src/main.c index b1f651c8..5f678aee 100644 --- a/test/bacnet/basic/object/program/src/main.c +++ b/test/bacnet/basic/object/program/src/main.c @@ -16,11 +16,10 @@ * @{ */ -static int Program_Load(void *context, const char *location) +static int Program_Load(void *context) { /* Placeholder for load function */ (void)context; - (void)location; return 0; } diff --git a/test/bacnet/basic/program/ubasic/CMakeLists.txt b/test/bacnet/basic/program/ubasic/CMakeLists.txt new file mode 100644 index 00000000..b17b57ec --- /dev/null +++ b/test/bacnet/basic/program/ubasic/CMakeLists.txt @@ -0,0 +1,42 @@ +# 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 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/basic/program/ubasic/ubasic.c + ${SRC_DIR}/bacnet/basic/program/ubasic/tokenizer.c + # Support files and stubs (pathname alphabetical) + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/bacnet/basic/program/ubasic/src/main.c b/test/bacnet/basic/program/ubasic/src/main.c new file mode 100644 index 00000000..7d4b6266 --- /dev/null +++ b/test/bacnet/basic/program/ubasic/src/main.c @@ -0,0 +1,447 @@ +/** + * @file + * @details Test the uBASIC implementation + * @author Steve Karg + * @date 2025 + * @brief Platform libc and compiler abstraction layer + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include + +/** + * @addtogroup bacnet_tests + * @{ + */ + +static uint32_t Tick_Counter = 0; +static void tick_increment(void) +{ + Tick_Counter++; +} +static uint32_t tick_now(void) +{ + return Tick_Counter; +} + +static int32_t ADC_Value[UINT8_MAX + 1]; +static int32_t adc_read(uint8_t channel) +{ + return ADC_Value[channel]; +} +static void adc_config(uint8_t sampletime, uint8_t nreads) +{ + (void)sampletime; + (void)nreads; +} +static uint32_t Event_Mask; +static int8_t hw_event(uint8_t bit) +{ + if (bit < 32) { + if (Event_Mask & (1UL << bit)) { + return 1; // Event is set + } + } + + return 0; // Event is not set +} +static void hw_event_clear(uint8_t bit) +{ + if (bit < 32) { + Event_Mask &= ~(1UL << bit); + } +} +static uint8_t GPIO_Pin_State[UINT8_MAX + 1]; +static void gpio_write(uint8_t ch, uint8_t pin_state) +{ + GPIO_Pin_State[ch] = pin_state; +} +static void gpio_config(uint8_t ch, int8_t mode, uint8_t freq) +{ + (void)ch; + (void)mode; + (void)freq; +} +static void pwm_config(uint16_t psc, uint16_t per) +{ + (void)psc; + (void)per; +} + +static int32_t Duty_Cycle[UINT8_MAX + 1]; +static void pwm_write(uint8_t ch, int32_t dutycycle) +{ + Duty_Cycle[ch] = dutycycle; +} +static int32_t pwm_read(uint8_t ch) +{ + return Duty_Cycle[ch]; +} + +/** + * @brief Write a buffer to the serial port + * @param msg Pointer to the buffer to write + * @param n Number of bytes to write + */ +static void serial_write(const char *msg, uint16_t n) +{ + printf("%.*s", n, msg); + fflush(stdout); +} + +/** + * @brief Generate a random number + * @param size Size of the random number in bits + * @return Random number size-bits wide + */ +static uint32_t random_uint32(uint8_t size) +{ + uint32_t r = 0; + + for (int i = 0; i < size; i++) { + r |= (1 << i); + } + + return r; +} + +struct test_bacnet_object { + uint16_t object_type; + uint32_t object_instance; + uint32_t property_id; + VARIABLE_TYPE property_value; + char *object_name; +}; +static struct test_bacnet_object Test_BACnet_Object[5]; + +static void +bacnet_create_object(uint16_t object_type, uint32_t instance, char *object_name) +{ + if (instance < ARRAY_SIZE(Test_BACnet_Object)) { + Test_BACnet_Object[instance].object_type = object_type; + Test_BACnet_Object[instance].object_instance = instance; + Test_BACnet_Object[instance].object_name = strdup(object_name); + } +} + +static void bacnet_write_property( + uint16_t object_type, + uint32_t instance, + uint32_t property_id, + VARIABLE_TYPE value) +{ + if (instance < ARRAY_SIZE(Test_BACnet_Object)) { + Test_BACnet_Object[instance].object_type = object_type; + Test_BACnet_Object[instance].object_instance = instance; + Test_BACnet_Object[instance].property_id = property_id; + Test_BACnet_Object[instance].property_value = value; + } +} + +static VARIABLE_TYPE bacnet_read_property( + uint16_t object_type, uint32_t instance, uint32_t property_id) +{ + if (instance < ARRAY_SIZE(Test_BACnet_Object)) { + if (Test_BACnet_Object[instance].object_type == object_type && + Test_BACnet_Object[instance].property_id == property_id) { + return Test_BACnet_Object[instance].property_value; + } + } + return 0; +} + +/** + * @brief Test + */ +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(ubasic_tests, test_ubasic_gpio) +#else +static void test_ubasic_gpio(void) +#endif +{ + struct ubasic_data data = { 0 }; + const char *program = + /* program listing with either \0, \n, or ';' at the end of each line. + note: indentation is not required */ + "println 'Demo - GPIO & ADC';" + "pinmode(0xc0,-1,0);" + "pinmode(0xc1,-1,0);" + "pinmode(0xc2,-1,0);" + "pinmode(0xc3,-1,0);" + "for j = 0 to 2;" + " dwrite(0xc0,(j % 2));" + " dwrite(0xc1,(j % 2));" + " dwrite(0xc2,(j % 2));" + " dwrite(0xc3,(j % 2));" + " sleep(0.5);" + "next j;" + "aread_conf(7,16);" + "aread_conf(7,17);" + "a = 4096 / 2;" + "z = 4096 / 2;" + "s = 5;" + "for i = 1 to s;" + " x = aread(16);" + " y = aread(17);" + " println 'VREF,TEMP=', x, y;" + " a = avgw(x,a,s);" + " z = avgw(y,z,s);" + "next i;" + "println 'average x y=', a, z;" + "end;"; + VARIABLE_TYPE value = 0; + data.mstimer_now = tick_now; + data.serial_write = serial_write; + data.gpio_config = gpio_config; + data.gpio_write = gpio_write; + data.adc_config = adc_config; + data.adc_read = adc_read; + data.pwm_config = pwm_config; + data.pwm_write = pwm_write; + data.pwm_read = pwm_read; + data.hw_event = hw_event; + data.hw_event_clear = hw_event_clear; + ADC_Value[16] = 2048; + ADC_Value[17] = 2048; + ubasic_load_program(&data, program); + zassert_equal(data.status.bit.isRunning, 1, NULL); + zassert_equal(data.status.bit.Error, 0, NULL); + while (!ubasic_finished(&data)) { + ubasic_run_program(&data); + tick_increment(); + } + zassert_equal(data.status.bit.Error, 0, NULL); + /* check the final value of the bacnet read property */ + value = ubasic_get_variable(&data, 'a'); + zassert_equal( + fixedpt_toint(value), 2048, "a value=%d", fixedpt_toint(value)); + value = ubasic_get_variable(&data, 'z'); + zassert_equal( + fixedpt_toint(value), 2048, "z value=%d", fixedpt_toint(value)); +} + +/** + * @brief Test + */ +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(ubasic_tests, test_ubasic_bacnet) +#else +static void test_ubasic_bacnet(void) +#endif +{ + struct ubasic_data data = { 0 }; + const char *program = + /* program listing with either \0, \n, or ';' at the end of each line. + note: indentation is not required */ + "println 'Demo - BACnet';" + "bac_create(0, 1, 'Object1');" + "bac_create(0, 2, 'Object2');" + "bac_create(0, 3, 'Object3');" + "bac_create(0, 4, 'Object4');" + "bac_write(0, 1, 85, 42);" + "a = bac_read(0, 1, 85);" + "println 'bac_read 0, 1, 85 = ' a;" + "end;"; + VARIABLE_TYPE value = 0; + data.mstimer_now = tick_now; + data.serial_write = serial_write; + data.bacnet_create_object = bacnet_create_object; + data.bacnet_write_property = bacnet_write_property; + data.bacnet_read_property = bacnet_read_property; + ubasic_load_program(&data, program); + zassert_equal(data.status.bit.isRunning, 1, NULL); + zassert_equal(data.status.bit.Error, 0, NULL); + while (!ubasic_finished(&data)) { + ubasic_run_program(&data); + tick_increment(); + } + zassert_equal(data.status.bit.Error, 0, NULL); + /* check the final value of the bacnet read property */ + value = ubasic_get_variable(&data, 'a'); + zassert_equal( + fixedpt_toint(value), 42, "bacnet read property value=%d", + fixedpt_toint(value)); + zassert_equal( + Test_BACnet_Object[1].object_type, 0, "bacnet object type=%d", + Test_BACnet_Object[1].object_type); + zassert_equal( + Test_BACnet_Object[1].object_instance, 1, "bacnet object instance=%d", + Test_BACnet_Object[1].object_instance); + zassert_equal( + Test_BACnet_Object[1].property_id, 85, "bacnet object property ID=%d", + Test_BACnet_Object[1].property_id); + zassert_equal( + strcmp(Test_BACnet_Object[1].object_name, "Object1"), 0, + "bacnet object name=%s", Test_BACnet_Object[1].object_name); + zassert_equal( + strcmp(Test_BACnet_Object[2].object_name, "Object2"), 0, + "bacnet object name=%s", Test_BACnet_Object[2].object_name); + zassert_equal( + strcmp(Test_BACnet_Object[3].object_name, "Object3"), 0, + "bacnet object name=%s", Test_BACnet_Object[3].object_name); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(ubasic_tests, test_ubasic_math) +#else +static void test_ubasic_math(void) +#endif +{ + struct ubasic_data data = { 0 }; + const char *program = + /* program listing with either \0, \n, or ';' at the end of each line. + note: indentation is not required */ + "println 'Demo - Math';" + "for i = 1 to 2;" + " j = i + 0.25 + 1/2;" + " k = sqrt(2*j) + ln(4*i) + cos(i+j) + sin(j);" + "next i;" + "println 'j=' j;" + "println 'k=' k;" + "dim r@(5);" + "for i = 1 to 5;" + " r@(i) = ran;" + " println 'r[' i ']=' r@(i);" + "next i;" + "dim u@(5);" + "a = 0;" + "for i = 1 to 5;" + " u = uniform;" + " println 'u[' i ']=' u;" + " u@(i) = u;" + " a = avgw(u,a,5);" + "next i;" + "println 'uniform moving average = ' a;" + "x = 1000 * uniform;" + "f = floor(x);" + "c = ceil(x);" + "r = round(x);" + "w = pow(x,3);" + "println 'x=' x;" + "println 'floor(x)=' f;" + "println 'ceil(x)=' c;" + "println 'round(x)=' r;" + "println 'x^3=' w;" + "end;"; + + VARIABLE_TYPE value = 0, xvalue = 0, arrayvalue[5]; + unsigned i; + int32_t value_int = 0; + int32_t value_frac = 0; + int32_t value_math = 0; + + data.mstimer_now = tick_now; + data.serial_write = serial_write; + data.random_uint32 = random_uint32; + ubasic_load_program(&data, program); + zassert_equal(data.status.bit.isRunning, 1, NULL); + zassert_equal(data.status.bit.Error, 0, NULL); + while (!ubasic_finished(&data)) { + ubasic_run_program(&data); + tick_increment(); + } + zassert_equal(data.status.bit.Error, 0, NULL); + /* check the final values of the math operations */ + value = ubasic_get_variable(&data, 'j'); + value_int = fixedpt_toint(value); + value_frac = fixedpt_fracpart_floor_toint(value, 2); + zassert_equal(value_int, 2, "int=%d", value_int); + zassert_equal(value_frac, 75, "frac=%d", value_frac); + value = ubasic_get_variable(&data, 'k'); + value_int = fixedpt_toint(value); + value_frac = fixedpt_fracpart_floor_toint(value, 2); + zassert_equal(value_int, 4, "int=%d", value_int); + zassert_equal(value_frac, 83, "frac=%d", value_frac); + for (i = 0; i < ARRAY_SIZE(arrayvalue); i++) { + arrayvalue[i] = ubasic_get_arrayvariable(&data, 'r', 1 + i); + value_int = fixedpt_toint(arrayvalue[i]); + zassert_equal(value_int, 1, "ran[%u]=%d", 1 + i, value_int); + } + /* uniform random value */ + xvalue = ubasic_get_variable(&data, 'x'); + /* floor */ + value_math = fixedpt_floor_toint(xvalue); + value = ubasic_get_variable(&data, 'f'); + value_int = fixedpt_toint(value); + zassert_equal( + value_int, value_math, "int=%d floor=%d", value_int, value_math); + /* ceiling */ + value_math = fixedpt_ceil_toint(xvalue); + value = ubasic_get_variable(&data, 'c'); + value_int = fixedpt_toint(value); + zassert_equal( + value_int, value_math, "int=%d ceil=%d", value_int, value_math); + /* round */ + value_math = fixedpt_round_toint(xvalue); + value = ubasic_get_variable(&data, 'r'); + value_int = fixedpt_toint(value); + zassert_equal( + value_int, value_math, "int=%d round=%d", value_int, value_math); + + return; +} + +/** + * @brief Test + */ +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(ubasic_tests, test_ubasic) +#else +static void test_ubasic(void) +#endif +{ + struct ubasic_data data = { 0 }; + const char *program = + /* program listing with either \0, \n, or ';' at the end of each line. + note: indentation is not required */ + "println 'Demo - Flow';" + "gosub l1;" + "for i = 1 to 8;" + " for j = 1 to 9;" + " println 'i,j=',i,j;" + " next j;" + "next i;" + "println 'Demo 1 Completed';" + "end;" + ":l1 " + " println 'subroutine';" + "return;"; + VARIABLE_TYPE value = 0; + int32_t value_int = 0; + + ubasic_load_program(&data, program); + zassert_equal(data.status.bit.isRunning, 1, NULL); + zassert_equal(data.status.bit.Error, 0, NULL); + while (!ubasic_finished(&data)) { + ubasic_run_program(&data); + } + zassert_equal(data.status.bit.Error, 0, NULL); + /* check the final value of i and j */ + value = ubasic_get_variable(&data, 'i'); + value_int = fixedpt_toint(value); + zassert_equal(value_int, 9, NULL); + value = ubasic_get_variable(&data, 'j'); + value_int = fixedpt_toint(value); + zassert_equal(value_int, 10, NULL); + + return; +} +/** + * @} + */ + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST_SUITE(ubasic_tests, NULL, NULL, NULL, NULL, NULL); +#else +void test_main(void) +{ + ztest_test_suite( + ubasic_tests, ztest_unit_test(test_ubasic), + ztest_unit_test(test_ubasic_math), ztest_unit_test(test_ubasic_bacnet), + ztest_unit_test(test_ubasic_gpio)); + + ztest_run_test_suite(ubasic_tests); +} +#endif