Added uBASIC-Plus program object example to STM32F4xx. (#967)

This commit is contained in:
Steve Karg
2025-04-16 12:03:42 -05:00
committed by GitHub
parent a923e3cec9
commit 54bf9b79c6
37 changed files with 7613 additions and 1112 deletions
+1 -1
View File
@@ -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
+25 -5
View File
@@ -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
...
+6 -2
View File
@@ -15,7 +15,7 @@
"target/stm32f4x.cfg"
],
"searchDir": [],
"svdFile":"${workspaceRoot}/stm32f429.svd",
"svdFile": "${workspaceRoot}/stm32f429.svd",
"runToEntryPoint": "main",
"liveWatch": {
"enabled": true,
@@ -27,7 +27,11 @@
"swoFrequency": 2000000,
"source": "probe",
"decoders": [
{ "type": "console", "label": "ITM", "port": 0 }
{
"type": "console",
"label": "ITM",
"port": 0
}
]
},
"preLaunchCommands": [
+6
View File
@@ -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
+6
View File
@@ -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
+15 -75
View File
@@ -1,15 +1,16 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*
*********************************************************************/
/**
* @file
* @brief BACnet stack initialization and task processing
* @author Steve Karg
* @date 2011
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include <stdbool.h>
/* 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) {
+9
View File
@@ -1235,6 +1235,12 @@
</group>
<group>
<name>BACnet Basic System</name>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\basic\program\ubasic\ubasic.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\basic\program\ubasic\tokenizer.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\basic\sys\bigend.c</name>
</file>
@@ -1277,6 +1283,9 @@
<file>
<name>$PROJ_DIR$\system_stm32f4xx.c</name>
</file>
<file>
<name>$PROJ_DIR$\ubasic-port.c</name>
</file>
</group>
<group>
<name>NUCLEO-BACnet</name>
+7 -7
View File
@@ -1,10 +1,10 @@
/**************************************************************************
*
* Copyright (C) 2010 Steve Karg <skarg@users.sourceforge.net>
*
* 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
+60 -40
View File
@@ -1,14 +1,11 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -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)
{
@@ -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:
+7 -7
View File
@@ -1,10 +1,10 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*
*********************************************************************/
/**
* @file
* @brief LED control for the STM32F4xx
* @author Steve Karg
* @date 2021
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include "stm32f4xx.h"
#include "bacnet/basic/sys/mstimer.h"
+7 -7
View File
@@ -1,10 +1,10 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* 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
+8 -8
View File
@@ -1,10 +1,10 @@
/************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*
*************************************************************************/
/**
* @file
* @brief Main function for the STM32F4xx NUCLEO board
* @author Steve Karg
* @date 2021
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
@@ -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;
+7 -10
View File
@@ -1,13 +1,10 @@
/**************************************************************************
*
* Copyright (C) 2011 Steve Karg <skarg@users.sourceforge.net>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/basic/sys/mstimer.h"
+38 -29
View File
@@ -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 <stdbool.h>
#include <stdint.h>
@@ -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;
+147
View File
@@ -0,0 +1,147 @@
/**
* @file
* @brief uBASIC-Plus program object for BACnet
* @author Steve Karg
* @date 2025
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#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);
}
+23
View File
@@ -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 <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Program_UBASIC_Task(void);
void Program_UBASIC_Init(uint32_t instance);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+27 -25
View File
@@ -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 */
+38 -28
View File
@@ -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).
* @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 */
#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).
* 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*/
#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
#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 */
#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.
* 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*/
#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,
@@ -123,8 +132,8 @@
/* 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 */
@@ -141,11 +150,12 @@
* If expr is true, it returns no value.
* @retval None
*/
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
#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 */
+4 -13
View File
@@ -61,9 +61,7 @@ void NMI_Handler(void)
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
while (1) { }
}
/**
@@ -74,9 +72,7 @@ void HardFault_Handler(void)
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
while (1) { }
}
/**
@@ -87,9 +83,7 @@ void MemManage_Handler(void)
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
while (1) { }
}
/**
@@ -100,9 +94,7 @@ void BusFault_Handler(void)
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
while (1) { }
}
/**
@@ -148,7 +140,6 @@ void PendSV_Handler(void)
{
}*/
/**
* @}
*/
+1 -1
View File
@@ -24,7 +24,7 @@
#define __STM32F4xx_IT_H
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
+164 -144
View File
@@ -10,9 +10,9 @@
* 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.
* - 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
@@ -24,27 +24,28 @@
*
* <h2><center>&copy; COPYRIGHT 2017 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 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.
* 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.
* 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.
*
******************************************************************************
*/
@@ -61,15 +62,17 @@
* @{
*/
#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 */
/**
@@ -89,24 +92,28 @@
*/
/************************* 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.
#define VECT_TAB_OFFSET \
0x00 /*!< Vector Table base offset field. \
This value must be a multiple of 0x200. */
/******************************************************************************/
@@ -125,16 +132,14 @@
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(
uint8_t * file,
uint32_t line)
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
@@ -145,17 +150,18 @@ void assert_failed(
/** @addtogroup STM32F4xx_System_Private_Variables
* @{
*/
/* This variable is updated in three ways:
/* 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.
*/
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 };
/**
* @}
*/
@@ -164,8 +170,8 @@ const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
* @{
*/
#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 */
/**
@@ -185,55 +191,61 @@ const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
*/
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)
#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.
* 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.
* 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 HSI, SystemCoreClock will contain the
* HSI_VALUE(*)
*
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_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.
* - 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.
* (*) 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.
* (**) 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.
* - The result of this function could be not correct when using
* fractional value for HSE crystal.
*
* @param None
* @retval None
@@ -242,11 +254,11 @@ void SystemCoreClockUpdate(void)
{
uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
/* Get SYSCLK source -------------------------------------------------------*/
/* Get SYSCLK source
* -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
switch (tmp) {
case 0x00: /* HSI used as system clock source */
SystemCoreClock = HSI_VALUE;
break;
@@ -261,25 +273,25 @@ void SystemCoreClockUpdate(void)
pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
if (pllsource != 0)
{
if (pllsource != 0) {
/* HSE used as PLL clock source */
pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
else
{
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);
pllvco = (HSI_VALUE / pllm) *
((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
SystemCoreClock = pllvco/pllp;
pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >> 16) + 1) * 2;
SystemCoreClock = pllvco / pllp;
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
/* Compute HCLK frequency --------------------------------------------------*/
/* Compute HCLK frequency
* --------------------------------------------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
/* HCLK frequency */
@@ -291,25 +303,23 @@ void SystemCoreClockUpdate(void)
#define LSE_PASS_FLAG 0x100
/**
* @brief This function provides delay (in milliseconds) based on CPU cycles method.
* @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
{
do {
__NOP();
}
while (Delay --);
} while (Delay--);
}
/**
* @brief This function configures the LSE
*/
void System_LSE_Init(
void)
void System_LSE_Init(void)
{
uint32_t LSE_Delay = 0;
@@ -346,14 +356,15 @@ 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).
* This SRAM/SDRAM will be used as program data memory (including heap
* and stack).
* @param None
* @retval None
*/
@@ -364,7 +375,8 @@ void SystemInit_ExtMemCtl(void)
register uint32_t tmpreg = 0, timeout = 0xFFFF;
register __IO uint32_t index;
/* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface clock */
/* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface clock
*/
RCC->AHB1ENR |= 0x000001F8;
/* Delay after an RCC peripheral clock enabling */
@@ -442,7 +454,8 @@ void SystemInit_ExtMemCtl(void)
/* No pull-up, pull-down for PIx pins */
GPIOI->PUPDR = 0x00000000;
/*-- FMC Configuration -------------------------------------------------------*/
/*-- FMC Configuration
* -------------------------------------------------------*/
/* Enable the FMC interface clock */
RCC->AHB3ENR |= 0x00000001;
/* Delay after an RCC peripheral clock enabling */
@@ -455,47 +468,45 @@ void SystemInit_ExtMemCtl(void)
/* Clock enable command */
FMC_Bank5_6->SDCMR = 0x00000011;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
while ((tmpreg != 0) && (timeout-- > 0)) {
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
for (index = 0; index < 1000; index++)
;
/* PALL command */
FMC_Bank5_6->SDCMR = 0x00000012;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
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))
{
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))
{
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));
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C << 1));
/* 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)
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || \
defined(STM32F439xx)
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001011;
FMC_Bank1->BTCR[3] = 0x00000201;
@@ -510,22 +521,25 @@ void SystemInit_ExtMemCtl(void)
(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).
* 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)
#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;
@@ -615,8 +629,8 @@ void SystemInit_ExtMemCtl(void)
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx)
#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;
@@ -640,9 +654,11 @@ void SystemInit_ExtMemCtl(void)
GPIOI->OTYPER = 0x00000000;
/* No pull-up, pull-down for PIx pins */
GPIOI->PUPDR = 0x00000000;
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || \
STM32F469xx || STM32F479xx */
/*-- FMC Configuration -------------------------------------------------------*/
/*-- FMC Configuration
* -------------------------------------------------------*/
/* Enable the FMC interface clock */
RCC->AHB3ENR |= 0x00000001;
/* Delay after an RCC peripheral clock enabling */
@@ -660,19 +676,18 @@ void SystemInit_ExtMemCtl(void)
/* Clock enable command */
FMC_Bank5_6->SDCMR = 0x00000011;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
while ((tmpreg != 0) && (timeout-- > 0)) {
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
for (index = 0; index < 1000; index++)
;
/* PALL command */
FMC_Bank5_6->SDCMR = 0x00000012;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
while ((tmpreg != 0) && (timeout-- > 0)) {
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
@@ -683,8 +698,7 @@ void SystemInit_ExtMemCtl(void)
FMC_Bank5_6->SDCMR = 0x00000073;
#endif /* STM32F446xx */
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
while ((tmpreg != 0) && (timeout-- > 0)) {
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
@@ -695,31 +709,33 @@ void SystemInit_ExtMemCtl(void)
FMC_Bank5_6->SDCMR = 0x00046014;
#endif /* STM32F446xx */
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
while ((tmpreg != 0) && (timeout-- > 0)) {
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* 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);
#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 -----------------------------------------------------*/
/*-- GPIOs Configuration
* -----------------------------------------------------*/
/* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
RCC->AHB1ENR |= 0x00000078;
/* Delay after an RCC peripheral clock enabling */
@@ -773,11 +789,13 @@ void SystemInit_ExtMemCtl(void)
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
/*-- FMC/FSMC Configuration --------------------------------------------------*/
/*-- FMC/FSMC Configuration
* --------------------------------------------------*/
/* Enable the FMC/FSMC interface clock */
RCC->AHB3ENR |= 0x00000001;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)
#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 */
@@ -793,19 +811,21 @@ void SystemInit_ExtMemCtl(void)
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)
#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 /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || \
STM32F412Zx || STM32F412Vx */
#endif /* DATA_IN_ExtSRAM */
#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || STM32F427xx || STM32F437xx ||\
STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx */
#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || \
STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || \
STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx */
(void)(tmp);
}
#endif /* DATA_IN_ExtSRAM && DATA_IN_ExtSDRAM */
+1 -3
View File
@@ -34,7 +34,7 @@
#define __SYSTEM_STM32F4XX_H
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
/** @addtogroup STM32F4xx_System_Includes
@@ -45,14 +45,12 @@
* @}
*/
/** @addtogroup STM32F4xx_System_Exported_types
* @{
*/
extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */
/**
* @}
*/
+644
View File
@@ -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 <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <ctype.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/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
}
+23 -7
View File
@@ -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;
+4 -3
View File
@@ -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
@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
add_library(ubasic STATIC
ubasic.c
tokenizer.c
)
+182
View File
@@ -0,0 +1,182 @@
/*-
* Copyright (c) 2017-18, Marijan Kostrun <mkostrun@gmail.com>
*
* 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 <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
/* 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_ */
+672
View File
@@ -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 <ivoras@freebsd.org>
* Copyright (c) 2012, Tim Hartrick <tim@edgecast.com>
* Copyright (c) 2018, Marijan Kostrun <mksotrun@gmail.com>
*
* 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 <stdlib.h>
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
@@ -0,0 +1,46 @@
/**
* @file
* @author Steve Karg <skarg@users.sourceforge.net>
* @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 <stddef.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#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
+643
View File
@@ -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;
}
+211
View File
@@ -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 <skarg@users.sourceforge.net> 2025
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __TOKENIZER_H__
#define __TOKENIZER_H__
#include <stdbool.h>
#include <stdint.h>
#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__ */
File diff suppressed because it is too large Load Diff
+241
View File
@@ -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 <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __UBASIC_H__
#define __UBASIC_H__
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#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__ */
+4
View File
@@ -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
+1 -2
View File
@@ -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;
}
@@ -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
)
+447
View File
@@ -0,0 +1,447 @@
/**
* @file
* @details Test the uBASIC implementation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2025
* @brief Platform libc and compiler abstraction layer
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <zephyr/ztest.h>
#include <bacnet/bacdef.h>
#include <bacnet/basic/program/ubasic/ubasic.h>
/**
* @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