Added multiple uBASIC program objects to stm32f4xx example port. (#995)

This commit is contained in:
Steve Karg
2025-05-19 14:57:43 -05:00
committed by GitHub
parent 62bf8274f7
commit b5b2fd5b7b
9 changed files with 218 additions and 80 deletions
-4
View File
@@ -21,8 +21,6 @@
#include "bacnet/iam.h" #include "bacnet/iam.h"
/* BACnet objects */ /* BACnet objects */
#include "bacnet/basic/object/device.h" #include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/program.h"
#include "bacnet/basic/program/ubasic/ubasic.h"
/* me */ /* me */
#include "bacnet.h" #include "bacnet.h"
@@ -39,7 +37,6 @@ void bacnet_init(void)
{ {
/* initialize objects */ /* initialize objects */
Device_Init(NULL); Device_Init(NULL);
Program_UBASIC_Init(BACNET_MAX_INSTANCE);
/* set up our confirmed service unrecognized service handler - required! */ /* set up our confirmed service unrecognized service handler - required! */
apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service); apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
/* we need to handle who-is to support dynamic device binding */ /* we need to handle who-is to support dynamic device binding */
@@ -93,7 +90,6 @@ void bacnet_task(void)
mstimer_reset(&DCC_Timer); mstimer_reset(&DCC_Timer);
dcc_timer_seconds(DCC_CYCLE_SECONDS); dcc_timer_seconds(DCC_CYCLE_SECONDS);
} }
Program_UBASIC_Task();
/* handle the messaging */ /* handle the messaging */
pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0); pdu_len = datalink_receive(&src, &PDUBuffer[0], sizeof(PDUBuffer), 0);
if (pdu_len) { if (pdu_len) {
+57 -11
View File
@@ -24,6 +24,7 @@
#include "rs485.h" #include "rs485.h"
#include "led.h" #include "led.h"
#include "bacnet.h" #include "bacnet.h"
#include "program-ubasic.h"
/* MS/TP port */ /* MS/TP port */
static struct mstp_port_struct_t MSTP_Port; static struct mstp_port_struct_t MSTP_Port;
@@ -40,6 +41,53 @@ static struct dlmstp_rs485_driver RS485_Driver = {
static struct dlmstp_user_data_t MSTP_User_Data; static struct dlmstp_user_data_t MSTP_User_Data;
static uint8_t Input_Buffer[DLMSTP_MPDU_MAX]; static uint8_t Input_Buffer[DLMSTP_MPDU_MAX];
static uint8_t Output_Buffer[DLMSTP_MPDU_MAX]; static uint8_t Output_Buffer[DLMSTP_MPDU_MAX];
static const char *UBASIC_Program_1 =
/* program listing with either \0, \n, or ';' at the end of each line.
note: indentation is not required */
"println 'Demo - GPIO';"
":loop;"
" dwrite(3, 1);"
" sleep (0.3);"
" dwrite(3, 0);"
" sleep (0.3);"
"goto loop;"
"end;";
static const char *UBASIC_Program_2 =
/* program listing with either \0, \n, or ';' at the end of each line.
note: indentation is not required */
"println 'Demo - GPIO';"
":loop;"
" dwrite(2, 1);"
" sleep (0.5);"
" dwrite(2, 0);"
" sleep (0.1);"
"goto loop;"
"end;";
static const char *UBASIC_Program_3 =
/* 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-AVG');"
"bac_create(0, 2, 'ADC-2-AVG');"
"bac_create(0, 3, 'ADC-1-RAW');"
"bac_create(0, 4, 'ADC-3-RAW');"
"bac_create(4, 1, 'LED-1');"
":startover;"
" a = aread(1);"
" bac_write(0, 3, 85, a);"
" c = avgw(a, c, 10);"
" bac_write(0, 1, 85, c);"
" b = aread(2);"
" bac_write(0, 4, 85, b);"
" d = avgw(b, d, 10);"
" bac_write(0, 2, 85, d);"
" h = bac_read(4, 1, 85);"
" dwrite(1, (h % 2));"
" sleep (0.2);"
"goto startover;"
"end;";
/* uBASIC data tree for each program running */
static struct ubasic_data UBASIC_Data[3];
/** /**
* @brief Called from _write() function from printf and friends * @brief Called from _write() function from printf and friends
@@ -57,8 +105,6 @@ int __io_putchar(int ch)
*/ */
int main(void) int main(void)
{ {
struct mstimer Blink_Timer;
/*At this stage the microcontroller clock setting is already configured, /*At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup this is done through SystemInit() function which is called from startup
file (startup_stm32f4xx.s) before to branch to application main. file (startup_stm32f4xx.s) before to branch to application main.
@@ -72,7 +118,6 @@ int main(void)
mstimer_init(); mstimer_init();
led_init(); led_init();
rs485_init(); rs485_init();
mstimer_set(&Blink_Timer, 500);
/* FIXME: get the device ID from EEPROM */ /* FIXME: get the device ID from EEPROM */
Device_Set_Object_Instance_Number(103); Device_Set_Object_Instance_Number(103);
/* seed stdlib rand() with device-id to get pseudo consistent /* seed stdlib rand() with device-id to get pseudo consistent
@@ -99,10 +144,10 @@ int main(void)
MSTP_Port.OutputBuffer = Output_Buffer; MSTP_Port.OutputBuffer = Output_Buffer;
MSTP_Port.OutputBufferSize = sizeof(Output_Buffer); MSTP_Port.OutputBufferSize = sizeof(Output_Buffer);
/* choose from non-volatile configuration for zero-config or slave mode */ /* choose from non-volatile configuration for zero-config or slave mode */
MSTP_Port.ZeroConfigEnabled = true; MSTP_Port.ZeroConfigEnabled = false;
MSTP_Port.Zero_Config_Preferred_Station = 0; MSTP_Port.Zero_Config_Preferred_Station = 0;
MSTP_Port.SlaveNodeEnabled = false; MSTP_Port.SlaveNodeEnabled = false;
MSTP_Port.CheckAutoBaud = true; MSTP_Port.CheckAutoBaud = false;
/* user data */ /* user data */
MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_User_Data.RS485_Driver = &RS485_Driver;
MSTP_Port.UserData = &MSTP_User_Data; MSTP_Port.UserData = &MSTP_User_Data;
@@ -112,7 +157,7 @@ int main(void)
dlmstp_set_mac_address(255); dlmstp_set_mac_address(255);
} else { } else {
/* FIXME: get the address from hardware DIP or from EEPROM */ /* FIXME: get the address from hardware DIP or from EEPROM */
dlmstp_set_mac_address(1); dlmstp_set_mac_address(63);
} }
if (!MSTP_Port.CheckAutoBaud) { if (!MSTP_Port.CheckAutoBaud) {
/* FIXME: get the baud rate from hardware DIP or from EEPROM */ /* FIXME: get the baud rate from hardware DIP or from EEPROM */
@@ -120,13 +165,14 @@ int main(void)
} }
/* initialize application layer*/ /* initialize application layer*/
bacnet_init(); bacnet_init();
/* configure a program */
Program_UBASIC_Init(10);
Program_UBASIC_Create(1, &UBASIC_Data[0], UBASIC_Program_1);
Program_UBASIC_Create(2, &UBASIC_Data[1], UBASIC_Program_2);
Program_UBASIC_Create(3, &UBASIC_Data[2], UBASIC_Program_3);
for (;;) { for (;;) {
if (mstimer_expired(&Blink_Timer)) {
mstimer_reset(&Blink_Timer);
led_toggle(LED_LD3);
led_toggle(LED_RS485);
}
led_task(); led_task();
bacnet_task(); bacnet_task();
Program_UBASIC_Task();
} }
} }
+87 -44
View File
@@ -8,37 +8,15 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "bacnet/basic/object/program.h" #include "bacnet/basic/object/program.h"
#include "bacnet/basic/program/ubasic/ubasic.h" #include "bacnet/basic/program/ubasic/ubasic.h"
#include "bacnet/basic/sys/mstimer.h" #include "bacnet/basic/sys/mstimer.h"
/* uBASIC-Plus program object */ /* timer for the task */
static struct ubasic_data UBASIC_DATA = { 0 };
static struct mstimer UBASIC_Timer; static struct mstimer UBASIC_Timer;
static uint32_t UBASIC_Instance = 0; /* context structure */
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 * @brief Load the program into the uBASIC interpreter
@@ -47,9 +25,14 @@ static const char *UBASIC_Program =
*/ */
static int Program_Load(void *context) static int Program_Load(void *context)
{ {
if (!context) {
return -1;
}
struct ubasic_data *data = (struct ubasic_data *)context; struct ubasic_data *data = (struct ubasic_data *)context;
ubasic_load_program(data, UBASIC_Program); ubasic_load_program(data, NULL);
(void)ubasic_program_location(data);
return 0; return 0;
} }
@@ -61,6 +44,9 @@ static int Program_Load(void *context)
*/ */
static int Program_Run(void *context) static int Program_Run(void *context)
{ {
if (!context) {
return -1;
}
struct ubasic_data *data = (struct ubasic_data *)context; struct ubasic_data *data = (struct ubasic_data *)context;
int result = 0; int result = 0;
@@ -68,6 +54,7 @@ static int Program_Run(void *context)
if (result <= 0) { if (result <= 0) {
return -1; return -1;
} }
(void)ubasic_program_location(data);
return 0; return 0;
} }
@@ -79,9 +66,12 @@ static int Program_Run(void *context)
*/ */
static int Program_Halt(void *context) static int Program_Halt(void *context)
{ {
if (!context) {
return -1;
}
struct ubasic_data *data = (struct ubasic_data *)context; struct ubasic_data *data = (struct ubasic_data *)context;
data->status.bit.isRunning = 0; ubasic_halt_program(data);
return 0; return 0;
} }
@@ -93,10 +83,14 @@ static int Program_Halt(void *context)
*/ */
static int Program_Restart(void *context) static int Program_Restart(void *context)
{ {
if (!context) {
return -1;
}
struct ubasic_data *data = (struct ubasic_data *)context; struct ubasic_data *data = (struct ubasic_data *)context;
ubasic_clear_variables(data); ubasic_clear_variables(data);
ubasic_load_program(data, UBASIC_Program); ubasic_load_program(data, NULL);
(void)ubasic_program_location(data);
return 0; return 0;
} }
@@ -108,9 +102,13 @@ static int Program_Restart(void *context)
*/ */
static int Program_Unload(void *context) static int Program_Unload(void *context)
{ {
if (!context) {
return -1;
}
struct ubasic_data *data = (struct ubasic_data *)context; struct ubasic_data *data = (struct ubasic_data *)context;
ubasic_clear_variables(data); ubasic_clear_variables(data);
return 0; return 0;
} }
@@ -119,29 +117,74 @@ static int Program_Unload(void *context)
*/ */
void Program_UBASIC_Task(void) void Program_UBASIC_Task(void)
{ {
size_t index, max_index;
uint32_t instance;
if (mstimer_expired(&UBASIC_Timer)) { if (mstimer_expired(&UBASIC_Timer)) {
mstimer_reset(&UBASIC_Timer); mstimer_reset(&UBASIC_Timer);
Program_Timer(UBASIC_Instance, mstimer_interval(&UBASIC_Timer)); max_index = Program_Count();
for (index = 0; index < max_index; index++) {
instance = Program_Index_To_Instance(index);
Program_Timer(instance, mstimer_interval(&UBASIC_Timer));
}
} }
} }
/** /**
* @brief Initialize the uBASIC program object * @brief Create one uBASIC program object
* @param instance Instance number of the program object * @param requested_instance Instance number of the program object
* @param context Pointer to the uBASIC data structure
* @param program Pointer to the uBASIC program
* @return 0 on success, non-zero on error * @return 0 on success, non-zero on error
*/ */
void Program_UBASIC_Init(uint32_t instance) int Program_UBASIC_Create(
uint32_t requested_instance,
struct ubasic_data *context,
const char *program)
{ {
ubasic_port_init(&UBASIC_DATA); uint32_t instance = 0;
UBASIC_Instance = Program_Create(instance);
Program_Context_Set(UBASIC_Instance, &UBASIC_DATA); if (!context) {
Program_Load_Set(UBASIC_Instance, Program_Load); return -1;
Program_Run_Set(UBASIC_Instance, Program_Run); }
Program_Halt_Set(UBASIC_Instance, Program_Halt); if (Program_Valid_Instance(requested_instance)) {
Program_Restart_Set(UBASIC_Instance, Program_Restart); instance = requested_instance;
Program_Unload_Set(UBASIC_Instance, Program_Unload); if (program) {
/* auto-run the program */ context->program = program;
Program_Change_Set(UBASIC_Instance, PROGRAM_REQUEST_RUN); }
/* start the cyclic 10ms run timer for the program object */ Program_Change_Set(instance, PROGRAM_REQUEST_RESTART);
mstimer_set(&UBASIC_Timer, 10); } else {
instance = Program_Create(requested_instance);
if (instance == BACNET_MAX_INSTANCE) {
return -1;
}
if (program) {
context->program = program;
} else {
context->program = "end;";
}
Program_Context_Set(instance, context);
Program_Load_Set(instance, Program_Load);
Program_Run_Set(instance, Program_Run);
Program_Halt_Set(instance, Program_Halt);
Program_Restart_Set(instance, Program_Restart);
Program_Unload_Set(instance, Program_Unload);
Program_Location_Set(instance, context->location);
ubasic_port_init(context);
/* auto-run the program */
Program_Change_Set(instance, PROGRAM_REQUEST_RUN);
}
return 0;
}
/**
* @brief Initialize the uBASIC program object
* @param requested_instance Instance number of the program object
* @return 0 on success, non-zero on error
*/
void Program_UBASIC_Init(unsigned long task_ms)
{
/* start the cyclic 10ms run timer for the program object */
mstimer_set(&UBASIC_Timer, task_ms);
} }
+4 -1
View File
@@ -9,13 +9,16 @@
#define PROGRAM_UBASIC_H #define PROGRAM_UBASIC_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "bacnet/basic/program/ubasic/ubasic.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
void Program_UBASIC_Task(void); void Program_UBASIC_Task(void);
void Program_UBASIC_Init(uint32_t instance); void Program_UBASIC_Create(
uint32_t instance, struct ubasic_data *data, const char *program);
void Program_UBASIC_Init(unsigned long task_ms);
#ifdef __cplusplus #ifdef __cplusplus
} }
+11 -17
View File
@@ -312,23 +312,17 @@ static void gpio_config(uint8_t ch, int8_t mode, uint8_t freq)
*/ */
static void gpio_write(uint8_t ch, uint8_t pin_state) static void gpio_write(uint8_t ch, uint8_t pin_state)
{ {
switch (ch) { unsigned int led_index;
case 1:
if (pin_state) { if (ch == 0) {
led_on(LED_LD1); return;
} else { }
led_off(LED_LD1); /* adjust for zero based index */
} led_index = ch - 1;
break; if (pin_state) {
case 2: led_on(led_index);
if (pin_state) { } else {
led_on(LED_LD2); led_off(led_index);
} else {
led_off(LED_LD2);
}
break;
default:
break;
} }
} }
+16
View File
@@ -942,6 +942,22 @@ bool Program_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
return status; return status;
} }
/**
* @brief Set the context used with load, unload, run, halt, and restart
* @param object_instance [in] BACnet object instance number
* @param context [in] pointer to the context
*/
void *Program_Context_Get(uint32_t object_instance)
{
struct object_data *pObject = Object_Data(object_instance);
if (pObject) {
return pObject->Context;
}
return NULL;
}
/** /**
* @brief Set the context used with load, unload, run, halt, and restart * @brief Set the context used with load, unload, run, halt, and restart
* @param object_instance [in] BACnet object instance number * @param object_instance [in] BACnet object instance number
+2
View File
@@ -114,6 +114,8 @@ void Program_Init(void);
note: return value is 0 for success, non-zero for failure note: return value is 0 for success, non-zero for failure
*/ */
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void *Program_Context_Get(uint32_t object_instance);
BACNET_STACK_EXPORT
void Program_Context_Set(uint32_t object_instance, void *context); void Program_Context_Set(uint32_t object_instance, void *context);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void Program_Load_Set(uint32_t object_instance, int (*load)(void *context)); void Program_Load_Set(uint32_t object_instance, int (*load)(void *context));
+30 -3
View File
@@ -562,11 +562,15 @@ void ubasic_load_program(struct ubasic_data *data, const char *program)
} }
data->status.byte = 0x00; data->status.byte = 0x00;
if (program) { if (program) {
data->program_ptr = program; data->program = program;
tokenizer_init(&data->tree, program); }
if (data->program) {
data->program_ptr = data->program;
tokenizer_init(&data->tree, data->program_ptr);
data->status.bit.isRunning = 1; data->status.bit.isRunning = 1;
} }
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static void static void
token_error_print(struct ubasic_data *data, UBASIC_VARIABLE_TYPE token) token_error_print(struct ubasic_data *data, UBASIC_VARIABLE_TYPE token)
@@ -2809,7 +2813,7 @@ static void subsequent_statement(struct ubasic_data *data)
return; return;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
static bool ubasic_program_finished(struct ubasic_data *data) bool ubasic_program_finished(struct ubasic_data *data)
{ {
struct ubasic_tokenizer *tree = &data->tree; struct ubasic_tokenizer *tree = &data->tree;
@@ -3015,6 +3019,29 @@ uint8_t ubasic_finished(struct ubasic_data *data)
return (ubasic_program_finished(data) || data->status.bit.isRunning == 0); return (ubasic_program_finished(data) || data->status.bit.isRunning == 0);
} }
void ubasic_halt_program(struct ubasic_data *data)
{
data->status.bit.isRunning = 0;
}
const char *ubasic_program_location(struct ubasic_data *data)
{
struct ubasic_tokenizer *tree = &data->tree;
char *statement_end;
if (tree->ptr) {
snprintf(data->location, sizeof(data->location), "%s", tree->ptr);
/* only return the statement until end-of-line */
statement_end = strpbrk(data->location, ";\n");
if (statement_end) {
*statement_end = 0;
}
} else {
snprintf(data->location, sizeof(data->location), "end");
}
return data->location;
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
void ubasic_set_variable( void ubasic_set_variable(
+11
View File
@@ -116,7 +116,12 @@ struct ubasic_data {
int16_t free_arrayptr; int16_t free_arrayptr;
int16_t arrayvariable[UBASIC_VARNUM_MAX]; int16_t arrayvariable[UBASIC_VARNUM_MAX];
#endif #endif
/* entire program */
const char *program;
/* points to current statement */
const char *program_ptr; const char *program_ptr;
/* copy of the current statement until end-of-line */
char location[UBASIC_STATEMENT_SIZE];
uint16_t gosub_stack[UBASIC_GOSUB_STACK_DEPTH]; uint16_t gosub_stack[UBASIC_GOSUB_STACK_DEPTH];
uint8_t gosub_stack_ptr; uint8_t gosub_stack_ptr;
@@ -220,7 +225,13 @@ int32_t ubasic_run_program(struct ubasic_data *data);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
uint8_t ubasic_execute_statement(struct ubasic_data *data, char *statement); uint8_t ubasic_execute_statement(struct ubasic_data *data, char *statement);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void ubasic_halt_program(struct ubasic_data *data);
BACNET_STACK_EXPORT
bool ubasic_program_finished(struct ubasic_data *data);
BACNET_STACK_EXPORT
uint8_t ubasic_finished(struct ubasic_data *data); uint8_t ubasic_finished(struct ubasic_data *data);
BACNET_STACK_EXPORT
const char *ubasic_program_location(struct ubasic_data *data);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
uint8_t ubasic_waiting_for_input(struct ubasic_data *data); uint8_t ubasic_waiting_for_input(struct ubasic_data *data);