libxr 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- libxr/ConfigCubemxProject.py +128 -0
- libxr/GeneratorCode.py +454 -0
- libxr/GeneratorSTM32CMake.py +83 -0
- libxr/GeneratorSTM32IT.py +64 -0
- libxr/PeripheralAnalyzer.py +410 -0
- libxr/__init__.py +0 -0
- libxr-0.1.0.dist-info/LICENSE +202 -0
- libxr-0.1.0.dist-info/METADATA +10 -0
- libxr-0.1.0.dist-info/RECORD +12 -0
- libxr-0.1.0.dist-info/WHEEL +5 -0
- libxr-0.1.0.dist-info/entry_points.txt +6 -0
- libxr-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
def run_command(command):
|
|
8
|
+
"""Run a shell command and check the return value."""
|
|
9
|
+
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
|
10
|
+
if result.returncode == 0:
|
|
11
|
+
print(f"✅ Command executed successfully: {command}")
|
|
12
|
+
else:
|
|
13
|
+
print(f"❌ Command failed: {command}")
|
|
14
|
+
print(result.stderr)
|
|
15
|
+
exit(1)
|
|
16
|
+
|
|
17
|
+
def find_ioc_file(directory):
|
|
18
|
+
"""Search for a .ioc file in the specified directory."""
|
|
19
|
+
for file in os.listdir(directory):
|
|
20
|
+
if file.endswith(".ioc"):
|
|
21
|
+
return os.path.join(directory, file)
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
def initialize_git_repository(project_dir):
|
|
25
|
+
"""Initialize a Git repository if not already present."""
|
|
26
|
+
git_dir = os.path.join(project_dir, ".git")
|
|
27
|
+
if not os.path.exists(git_dir):
|
|
28
|
+
print(f"❌ Directory {project_dir} is not a Git repository. Initializing Git...")
|
|
29
|
+
run_command(f"git init {project_dir}")
|
|
30
|
+
|
|
31
|
+
def create_gitignore_file(project_dir):
|
|
32
|
+
"""Create a .gitignore file if it does not exist."""
|
|
33
|
+
gitignore_path = os.path.join(project_dir, ".gitignore")
|
|
34
|
+
if not os.path.exists(gitignore_path):
|
|
35
|
+
print(f"📝 Creating .gitignore file...")
|
|
36
|
+
with open(gitignore_path, "w") as gitignore_file:
|
|
37
|
+
gitignore_file.write("""build/**
|
|
38
|
+
.history/**
|
|
39
|
+
.cache/**
|
|
40
|
+
.config.json
|
|
41
|
+
""")
|
|
42
|
+
|
|
43
|
+
def add_git_submodule(project_dir):
|
|
44
|
+
"""Add the LibXR submodule if not already present."""
|
|
45
|
+
libxr_path = os.path.join(project_dir, "Middlewares/Third_Party/LibXR")
|
|
46
|
+
if not os.path.exists(libxr_path):
|
|
47
|
+
print("❌ Middlewares/Third_Party/LibXR not found. Adding submodule...")
|
|
48
|
+
run_command(f"cd {project_dir} && git submodule add https://github.com/Jiu-Xiao/libxr.git ./Middlewares/Third_Party/LibXR")
|
|
49
|
+
|
|
50
|
+
def create_user_directory(project_dir):
|
|
51
|
+
"""Ensure the User directory exists."""
|
|
52
|
+
user_path = os.path.join(project_dir, "User")
|
|
53
|
+
if not os.path.exists(user_path):
|
|
54
|
+
os.makedirs(user_path)
|
|
55
|
+
return user_path
|
|
56
|
+
|
|
57
|
+
def process_ioc_file(project_dir, json_output):
|
|
58
|
+
"""Parse the .ioc file and generate JSON configuration."""
|
|
59
|
+
print("🔄 Parsing .ioc file...")
|
|
60
|
+
run_command(f"libxr_parse_ioc -d {project_dir} -o {json_output}")
|
|
61
|
+
|
|
62
|
+
def generate_cpp_code(json_output, cpp_output):
|
|
63
|
+
"""Generate C++ code from JSON configuration."""
|
|
64
|
+
print("🔄 Generating C++ code...")
|
|
65
|
+
run_command(f"libxr_generate_code -i {json_output} -o {cpp_output}")
|
|
66
|
+
|
|
67
|
+
def modify_stm32_interrupts(project_dir):
|
|
68
|
+
"""Modify STM32 interrupt handler files."""
|
|
69
|
+
print("🔄 Modifying STM32 interrupt files...")
|
|
70
|
+
run_command(f"libxr_generate_stm32_it {os.path.join(project_dir, 'Core/Src')}")
|
|
71
|
+
|
|
72
|
+
def generate_cmake_file(project_dir):
|
|
73
|
+
"""Generate CMakeLists.txt for STM32 project."""
|
|
74
|
+
print("🔄 Generating CMakeLists.txt...")
|
|
75
|
+
run_command(f"libxr_generate_stm32_cmake {project_dir}")
|
|
76
|
+
|
|
77
|
+
def main():
|
|
78
|
+
parser = argparse.ArgumentParser(description="Automate STM32CubeMX project setup")
|
|
79
|
+
parser.add_argument("-d", "--directory", required=True, help="STM32CubeMX project directory")
|
|
80
|
+
parser.add_argument("-t", "--terminal", default="", help="Optional terminal device source")
|
|
81
|
+
args = parser.parse_args()
|
|
82
|
+
|
|
83
|
+
project_dir = args.directory.rstrip("/")
|
|
84
|
+
terminal_source = args.terminal
|
|
85
|
+
|
|
86
|
+
if not os.path.isdir(project_dir):
|
|
87
|
+
print(f"❌ Directory {project_dir} does not exist")
|
|
88
|
+
exit(1)
|
|
89
|
+
|
|
90
|
+
# Initialize Git repository and .gitignore
|
|
91
|
+
initialize_git_repository(project_dir)
|
|
92
|
+
create_gitignore_file(project_dir)
|
|
93
|
+
|
|
94
|
+
# Add Git submodule if necessary
|
|
95
|
+
add_git_submodule(project_dir)
|
|
96
|
+
|
|
97
|
+
# Find .ioc file
|
|
98
|
+
ioc_file = find_ioc_file(project_dir)
|
|
99
|
+
if not ioc_file:
|
|
100
|
+
print("❌ No .ioc file found")
|
|
101
|
+
exit(1)
|
|
102
|
+
|
|
103
|
+
print(f"📂 Found .ioc file: {ioc_file}")
|
|
104
|
+
|
|
105
|
+
# Create user directory
|
|
106
|
+
user_path = create_user_directory(project_dir)
|
|
107
|
+
|
|
108
|
+
# Define paths
|
|
109
|
+
json_output = os.path.join(project_dir, ".config.json")
|
|
110
|
+
cpp_output = os.path.join(user_path, "app_main.cpp")
|
|
111
|
+
|
|
112
|
+
# Process .ioc file
|
|
113
|
+
process_ioc_file(project_dir, json_output)
|
|
114
|
+
|
|
115
|
+
# Generate C++ code
|
|
116
|
+
generate_cpp_code(json_output, cpp_output)
|
|
117
|
+
|
|
118
|
+
# Modify STM32 interrupt handlers
|
|
119
|
+
modify_stm32_interrupts(project_dir)
|
|
120
|
+
|
|
121
|
+
# Generate CMakeLists.txt
|
|
122
|
+
generate_cmake_file(project_dir)
|
|
123
|
+
|
|
124
|
+
# Handle optional terminal source
|
|
125
|
+
if terminal_source:
|
|
126
|
+
print("🔄 Modifying terminal device source...")
|
|
127
|
+
|
|
128
|
+
print("✅ All tasks completed successfully!")
|
libxr/GeneratorCode.py
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
libxr_config = {
|
|
9
|
+
"terminal_source": "",
|
|
10
|
+
"software_timer": {"priority": 2, "stack_depth": 512},
|
|
11
|
+
"SPI": {},
|
|
12
|
+
"I2C": {},
|
|
13
|
+
"USART": {},
|
|
14
|
+
"USB": {},
|
|
15
|
+
"ADC": {},
|
|
16
|
+
"TIM": {},
|
|
17
|
+
"CAN": {},
|
|
18
|
+
"FDCAN": {},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def load_json(file_path):
|
|
22
|
+
"""Load JSON configuration file."""
|
|
23
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
24
|
+
return json.load(f)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def gpio_alias(port, gpio_data):
|
|
28
|
+
"""Generate GPIO configuration code, supporting CubeMX macros and unaliased cases."""
|
|
29
|
+
label = gpio_data.get("Label") # Could be empty
|
|
30
|
+
is_exti = gpio_data.get("GPXTI", False) # Whether it's an EXTI interrupt pin
|
|
31
|
+
|
|
32
|
+
if label:
|
|
33
|
+
# **Alias exists** → Use CubeMX predefined macros
|
|
34
|
+
pin_define = f"{label}_Pin"
|
|
35
|
+
port_define = f"{label}_GPIO_Port"
|
|
36
|
+
irq_define = f"{label}_EXTI_IRQn" if is_exti else None
|
|
37
|
+
else:
|
|
38
|
+
# **No alias** → Directly use GPIOx, GPIO_PIN_x
|
|
39
|
+
parts = port.split("-")
|
|
40
|
+
port_define = f"GPIO{parts[0][1]}" # E.g., "PA4" → "GPIOA"
|
|
41
|
+
pin_define = f"GPIO_PIN_{parts[0][2:]}" # E.g., "PA4" → "GPIO_PIN_4"
|
|
42
|
+
|
|
43
|
+
# Calculate EXTI_IRQn
|
|
44
|
+
pin_num = int(parts[0][2:])
|
|
45
|
+
if is_exti:
|
|
46
|
+
if 5 <= pin_num <= 9:
|
|
47
|
+
irq_define = "EXTI9_5_IRQn"
|
|
48
|
+
elif 10 <= pin_num <= 15:
|
|
49
|
+
irq_define = "EXTI15_10_IRQn"
|
|
50
|
+
else:
|
|
51
|
+
irq_define = f"EXTI{pin_num}_IRQn"
|
|
52
|
+
else:
|
|
53
|
+
irq_define = None
|
|
54
|
+
|
|
55
|
+
# **Final formatted output**
|
|
56
|
+
if irq_define:
|
|
57
|
+
return f"{label or port}({port_define}, {pin_define}, {irq_define})"
|
|
58
|
+
return f"{label or port}({port_define}, {pin_define})"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def generate_dma_buffers(periph, instance, buffer_sizes):
|
|
62
|
+
"""Generate DMA buffers for peripherals requiring DMA."""
|
|
63
|
+
if periph in ["SPI", "USART"]:
|
|
64
|
+
# Get USART configuration
|
|
65
|
+
tx_buffer_size = None
|
|
66
|
+
rx_buffer_size = None
|
|
67
|
+
if libxr_config.get(periph):
|
|
68
|
+
buffer_size = libxr_config[periph].get(instance, None)
|
|
69
|
+
else:
|
|
70
|
+
buffer_size = None
|
|
71
|
+
|
|
72
|
+
if buffer_size is None:
|
|
73
|
+
rx_buffer_size = buffer_sizes[periph]
|
|
74
|
+
tx_buffer_size = buffer_sizes[periph]
|
|
75
|
+
libxr_config[periph][instance] = {"tx_buffer_size": tx_buffer_size, "rx_buffer_size": rx_buffer_size}
|
|
76
|
+
else:
|
|
77
|
+
tx_buffer_size = buffer_size["tx_buffer_size"]
|
|
78
|
+
rx_buffer_size = buffer_size["rx_buffer_size"]
|
|
79
|
+
|
|
80
|
+
return f"static uint8_t {instance.lower()}_buff_tx[{tx_buffer_size}], {instance.lower()}_buff_rx[{rx_buffer_size}];\n"
|
|
81
|
+
if periph in ["I2C", "ADC"]:
|
|
82
|
+
# Get I2C configuration
|
|
83
|
+
if libxr_config.get(periph):
|
|
84
|
+
buffer_size = libxr_config[periph].get(instance, None)
|
|
85
|
+
else:
|
|
86
|
+
buffer_size = None
|
|
87
|
+
|
|
88
|
+
if buffer_size is None:
|
|
89
|
+
buffer_size = buffer_sizes[periph]
|
|
90
|
+
libxr_config[periph][instance] = {"buffer_size": buffer_size}
|
|
91
|
+
else:
|
|
92
|
+
buffer_size = buffer_size["buffer_size"]
|
|
93
|
+
return f"static uint8_t {instance.lower()}_buffer[{buffer_size}];\n"
|
|
94
|
+
return "" # Other peripherals don't need DMA
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def generate_gpio_config(project_data):
|
|
98
|
+
"""Generate GPIO configuration code."""
|
|
99
|
+
gpio_section = "\n // GPIO Configuration\n"
|
|
100
|
+
for port, config in project_data["GPIO"].items():
|
|
101
|
+
gpio_section += f" LibXR::STM32GPIO {gpio_alias(port, config)};\n"
|
|
102
|
+
return gpio_section
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def generate_extern_config(project_data, buffer_sizes):
|
|
106
|
+
"""Generate peripheral instantiation code and allocate DMA buffers if needed."""
|
|
107
|
+
dma_section = "\n// DMA Buffers\n"
|
|
108
|
+
externs = set()
|
|
109
|
+
|
|
110
|
+
for periph, instances in project_data["Peripherals"].items():
|
|
111
|
+
for instance, config in instances.items():
|
|
112
|
+
# Handle extern definitions
|
|
113
|
+
if periph == "USART" or periph == "UART":
|
|
114
|
+
externs.add(
|
|
115
|
+
f"extern UART_HandleTypeDef h{instance.lower().replace('usart', 'uart')};"
|
|
116
|
+
)
|
|
117
|
+
elif periph == "USB":
|
|
118
|
+
mode = config.get("Mode", "")
|
|
119
|
+
if "DEVICE" in instance.upper() or mode == "Device_Only":
|
|
120
|
+
externs.add("extern USBD_HandleTypeDef hUsbDeviceFS;")
|
|
121
|
+
elif "OTG_FS" in instance.upper():
|
|
122
|
+
externs.add("extern USBD_HandleTypeDef hUsbOtgFS;")
|
|
123
|
+
else:
|
|
124
|
+
externs.add(f"extern USBD_HandleTypeDef h{instance.lower()};")
|
|
125
|
+
else:
|
|
126
|
+
externs.add(f"extern {periph}_HandleTypeDef h{instance.lower()};")
|
|
127
|
+
|
|
128
|
+
# Generate DMA Buffers
|
|
129
|
+
if periph not in ["TIM", "USB", "CAN", "FDCAN"]:
|
|
130
|
+
dma_section += generate_dma_buffers(periph, instance, buffer_sizes)
|
|
131
|
+
|
|
132
|
+
return "\n".join(sorted(externs)) + "\n" + dma_section
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def generate_peripherals_config(project_data):
|
|
136
|
+
"""Generate peripheral instantiation code and assign DMA buffers."""
|
|
137
|
+
periph_section = "\n // Peripheral Configuration\n"
|
|
138
|
+
adc_channels = ""
|
|
139
|
+
pwm_section = ""
|
|
140
|
+
|
|
141
|
+
for periph, instances in project_data["Peripherals"].items():
|
|
142
|
+
for instance, config in instances.items():
|
|
143
|
+
|
|
144
|
+
# ADC
|
|
145
|
+
if periph == "ADC":
|
|
146
|
+
dma_enabled = (
|
|
147
|
+
config.get("DMA", "DISABLE") == "ENABLE"
|
|
148
|
+
) # Default "DISABLE" if missing
|
|
149
|
+
conversion_key = "RegularConversions" if dma_enabled else "Channels"
|
|
150
|
+
|
|
151
|
+
# Ensure conversions are in list format
|
|
152
|
+
conversions = config.get(conversion_key, [])
|
|
153
|
+
if isinstance(conversions, str):
|
|
154
|
+
conversions = list(eval(conversions)) # Parse string safely
|
|
155
|
+
|
|
156
|
+
adc_channels += (
|
|
157
|
+
f" std::array<uint32_t, {len(conversions)}> {instance.lower()}_channels = "
|
|
158
|
+
f"{{ {', '.join(conversions)} }};\n"
|
|
159
|
+
)
|
|
160
|
+
periph_section += (
|
|
161
|
+
f" LibXR::STM32ADC {instance.lower()}(&h{instance.lower()}, "
|
|
162
|
+
f"RawData({instance.lower()}_buffer), {instance.lower()}_channels, 3.3f);\n"
|
|
163
|
+
)
|
|
164
|
+
elif periph == "FDCAN":
|
|
165
|
+
periph_section += (
|
|
166
|
+
f" LibXR::STM32CANFD {instance.lower()}(&h{instance.lower()}, "
|
|
167
|
+
f'"{instance.lower()}", 5);\n'
|
|
168
|
+
)
|
|
169
|
+
elif periph == "CAN":
|
|
170
|
+
periph_section += f' LibXR::STM32CAN {instance.lower()}(&h{instance.lower()}, "{instance.lower()}", 10);\n'
|
|
171
|
+
elif periph == "SPI":
|
|
172
|
+
tx_dma_enabled = config.get("DMA_TX", "DISABLE") == "ENABLE"
|
|
173
|
+
rx_dma_enabled = config.get("DMA_RX", "DISABLE") == "ENABLE"
|
|
174
|
+
|
|
175
|
+
tx_buffer = (
|
|
176
|
+
f"{instance.lower()}_buff_tx" if tx_dma_enabled else "{nullptr, 0}"
|
|
177
|
+
)
|
|
178
|
+
rx_buffer = (
|
|
179
|
+
f"{instance.lower()}_buff_rx" if rx_dma_enabled else "{nullptr, 0}"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
periph_section += f" LibXR::STM32{periph} {instance.lower()}(&h{instance.lower()}, {tx_buffer}, {rx_buffer});\n"
|
|
183
|
+
elif periph == "USART":
|
|
184
|
+
tx_dma_enabled = config.get("DMA_TX", "DISABLE") == "ENABLE"
|
|
185
|
+
rx_dma_enabled = config.get("DMA_RX", "DISABLE") == "ENABLE"
|
|
186
|
+
|
|
187
|
+
tx_buffer = (
|
|
188
|
+
f"{instance.lower()}_buff_tx" if tx_dma_enabled else "{nullptr, 0}"
|
|
189
|
+
)
|
|
190
|
+
rx_buffer = (
|
|
191
|
+
f"{instance.lower()}_buff_rx" if rx_dma_enabled else "{nullptr, 0}"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
periph_section += (
|
|
195
|
+
(
|
|
196
|
+
f" LibXR::STM32{periph} {instance.lower()}(&h{instance.lower()}, {tx_buffer}, {rx_buffer});\n"
|
|
197
|
+
)
|
|
198
|
+
.replace("USART", "UART")
|
|
199
|
+
.replace("husart", "huart")
|
|
200
|
+
)
|
|
201
|
+
elif periph == "I2C":
|
|
202
|
+
periph_section += f" LibXR::STM32I2C {instance.lower()}(&h{instance.lower()}, {instance.lower()}_buffer);\n"
|
|
203
|
+
elif periph == "TIM" and "Channels" in instances[instance]:
|
|
204
|
+
for channel in instances[instance]["Channels"]:
|
|
205
|
+
channel_num = channel.replace("CH", "")
|
|
206
|
+
pwm_section += f" LibXR::STM32PWM pwm_{instance.lower()}_ch{channel_num}(&h{instance.lower()}, TIM_CHANNEL_{channel_num});\n"
|
|
207
|
+
|
|
208
|
+
return "\n" + adc_channels + pwm_section + periph_section
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def generate_terminal_config(project_data, terminal_source):
|
|
212
|
+
"""Generate Terminal configuration based on peripheral settings."""
|
|
213
|
+
usb_device = None
|
|
214
|
+
for instance, config in project_data["Peripherals"].get("USB", {}).items():
|
|
215
|
+
mode = config.get("Mode", "")
|
|
216
|
+
if "DEVICE" in instance.upper() or mode == "Device_Only":
|
|
217
|
+
usb_device = "hUsbDeviceFS"
|
|
218
|
+
elif "OTG_FS" in instance.upper():
|
|
219
|
+
usb_device = "hUsbOtgFS"
|
|
220
|
+
|
|
221
|
+
# Get all UART devices
|
|
222
|
+
uart_list = list(project_data["Peripherals"].get("USART", {}).keys()) + list(
|
|
223
|
+
project_data["Peripherals"].get("UART", {}).keys()
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
terminal_config = ""
|
|
227
|
+
|
|
228
|
+
if usb_device:
|
|
229
|
+
terminal_config = f" LibXR::STM32VirtualUART uart_cdc({usb_device}, 5, 5);\n"
|
|
230
|
+
|
|
231
|
+
if terminal_source != "":
|
|
232
|
+
terminal_config += f" STDIO::read_ = &{terminal_source}.read_port_;\n"
|
|
233
|
+
terminal_config += f" STDIO::write_ = &{terminal_source}.write_port_;\n"
|
|
234
|
+
elif usb_device:
|
|
235
|
+
terminal_config += " STDIO::read_ = &uart_cdc.read_port_;\n"
|
|
236
|
+
terminal_config += " STDIO::write_ = &uart_cdc.write_port_;\n"
|
|
237
|
+
elif uart_list:
|
|
238
|
+
first_uart = uart_list[0].lower()
|
|
239
|
+
terminal_config = f" STDIO::read_ = &{first_uart}.read_port_;\n"
|
|
240
|
+
terminal_config += f" STDIO::write_ = &{first_uart}.write_port_;\n"
|
|
241
|
+
|
|
242
|
+
if terminal_config:
|
|
243
|
+
terminal_config += ' RamFS ramfs("XRobot");\n'
|
|
244
|
+
terminal_config += " Terminal terminal(ramfs);\n"
|
|
245
|
+
terminal_config += " auto terminal_task = Timer::CreatetTask(terminal.TaskFun, &terminal, 10);\n"
|
|
246
|
+
terminal_config += " Timer::Add(terminal_task);\n"
|
|
247
|
+
terminal_config += " Timer::Start(terminal_task);\n"
|
|
248
|
+
|
|
249
|
+
return terminal_config
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def preserve_user_code(existing_code, section):
|
|
253
|
+
"""Preserve user code blocks between markers."""
|
|
254
|
+
start_marker = f"/* User Code Begin {section} */"
|
|
255
|
+
end_marker = f"/* User Code End {section} */"
|
|
256
|
+
|
|
257
|
+
if start_marker in existing_code and end_marker in existing_code:
|
|
258
|
+
preserved_code = existing_code.split(start_marker, 1)[1].split(end_marker, 1)[0]
|
|
259
|
+
return preserved_code.strip()
|
|
260
|
+
|
|
261
|
+
if section == 3:
|
|
262
|
+
return """
|
|
263
|
+
while (true) {
|
|
264
|
+
Thread::Sleep(UINT32_MAX);
|
|
265
|
+
}
|
|
266
|
+
"""
|
|
267
|
+
else:
|
|
268
|
+
return "" # Return empty to preserve code structure
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def generate_cpp_code(
|
|
272
|
+
project_data, project_name, terminal_source, buffer_sizes, existing_code=""
|
|
273
|
+
):
|
|
274
|
+
timer_config = libxr_config.get("software_timer", (None, None))
|
|
275
|
+
if timer_config is None:
|
|
276
|
+
timer_pri = 2
|
|
277
|
+
timer_stack_depth = 512
|
|
278
|
+
libxr_config["software_timer"] = {"priority": timer_pri, "stack_depth": timer_stack_depth}
|
|
279
|
+
else:
|
|
280
|
+
timer_pri = timer_config["priority"]
|
|
281
|
+
timer_stack_depth = timer_config["stack_depth"]
|
|
282
|
+
|
|
283
|
+
"""Generate complete C++ code."""
|
|
284
|
+
cpp_code_include = f"""#include \"database.hpp\"
|
|
285
|
+
#include \"libxr.hpp\"
|
|
286
|
+
#include \"main.h\"
|
|
287
|
+
#include \"app_main.h\"
|
|
288
|
+
#include \"stm32_adc.hpp\"
|
|
289
|
+
#include \"stm32_can.hpp\"
|
|
290
|
+
#include \"stm32_canfd.hpp\"
|
|
291
|
+
#include \"stm32_gpio.hpp\"
|
|
292
|
+
#include \"stm32_i2c.hpp\"
|
|
293
|
+
#include \"stm32_power.hpp\"
|
|
294
|
+
#include \"stm32_pwm.hpp\"
|
|
295
|
+
#include \"stm32_spi.hpp\"
|
|
296
|
+
#include \"stm32_timebase.hpp\"
|
|
297
|
+
#include \"stm32_uart.hpp\"
|
|
298
|
+
#include \"stm32_usb.hpp\"
|
|
299
|
+
|
|
300
|
+
using namespace LibXR;
|
|
301
|
+
""" + generate_extern_config(
|
|
302
|
+
project_data, buffer_sizes
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
cpp_code = (
|
|
306
|
+
cpp_code_include
|
|
307
|
+
+ f"""
|
|
308
|
+
|
|
309
|
+
/* User Code Begin 1 */
|
|
310
|
+
"""
|
|
311
|
+
+ preserve_user_code(existing_code, 1)
|
|
312
|
+
+ """
|
|
313
|
+
/* User Code End 1 */
|
|
314
|
+
|
|
315
|
+
extern \"C\" void app_main(void) {
|
|
316
|
+
/* User Code Begin 2 */
|
|
317
|
+
"""
|
|
318
|
+
+ preserve_user_code(existing_code, 2)
|
|
319
|
+
+ f"""
|
|
320
|
+
/* User Code End 2 */
|
|
321
|
+
|
|
322
|
+
LibXR::STM32Timebase stm32_timebase;
|
|
323
|
+
LibXR::PlatformInit({timer_pri}, {timer_stack_depth});
|
|
324
|
+
LibXR::STM32PowerManager power_manager;
|
|
325
|
+
"""
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
cpp_code += generate_gpio_config(project_data)
|
|
329
|
+
cpp_code += generate_peripherals_config(project_data)
|
|
330
|
+
cpp_code += generate_terminal_config(project_data, terminal_source)
|
|
331
|
+
|
|
332
|
+
cpp_code += (
|
|
333
|
+
"""
|
|
334
|
+
/* User Code Begin 3 */
|
|
335
|
+
"""
|
|
336
|
+
+ preserve_user_code(existing_code, 3)
|
|
337
|
+
+ """
|
|
338
|
+
/* User Code End 3 */
|
|
339
|
+
}
|
|
340
|
+
"""
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
return cpp_code
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def parse_arguments():
|
|
347
|
+
"""Parse command-line arguments."""
|
|
348
|
+
parser = argparse.ArgumentParser(
|
|
349
|
+
description="Generate C++ code from JSON input with configurable DMA buffer sizes."
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
parser.add_argument(
|
|
353
|
+
"-i", "--input", type=str, required=True, default=24, help="Input file path."
|
|
354
|
+
)
|
|
355
|
+
parser.add_argument(
|
|
356
|
+
"-o", "--output", type=str, default=24, help="Output file path."
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return parser.parse_args()
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def generate_app_main_header(output_file):
|
|
363
|
+
"""Generate app_main.h header file."""
|
|
364
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
365
|
+
f.write("""#ifdef __cplusplus
|
|
366
|
+
extern "C" {
|
|
367
|
+
#endif
|
|
368
|
+
|
|
369
|
+
void app_main(void); // NOLINT
|
|
370
|
+
|
|
371
|
+
#ifdef __cplusplus
|
|
372
|
+
}
|
|
373
|
+
#endif
|
|
374
|
+
""")
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def main():
|
|
378
|
+
global libxr_config
|
|
379
|
+
|
|
380
|
+
args = parse_arguments()
|
|
381
|
+
|
|
382
|
+
# Parse input and output paths
|
|
383
|
+
input_file = args.input
|
|
384
|
+
output_file = args.output if args.output else os.path.splitext(input_file)[0] + ".cpp"
|
|
385
|
+
|
|
386
|
+
# Parse buffer_sizes
|
|
387
|
+
buffer_sizes = {
|
|
388
|
+
"SPI": 32,
|
|
389
|
+
"USART": 128,
|
|
390
|
+
"I2C": 32,
|
|
391
|
+
"ADC": 32,
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
libxr_config_path = os.path.dirname(os.path.abspath(output_file)) + "/libxr_config.json"
|
|
395
|
+
print(libxr_config_path)
|
|
396
|
+
|
|
397
|
+
if os.path.exists(libxr_config_path):
|
|
398
|
+
try:
|
|
399
|
+
with open(libxr_config_path, "r", encoding="utf-8") as f:
|
|
400
|
+
libxr_config = json.load(f)
|
|
401
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
402
|
+
print(f"Failed to load config: {e}, saving default config.")
|
|
403
|
+
with open(libxr_config_path, "w", encoding="utf-8") as f:
|
|
404
|
+
json.dump(libxr_config, f, indent=4, ensure_ascii=False)
|
|
405
|
+
else:
|
|
406
|
+
os.makedirs(os.path.dirname(libxr_config_path), exist_ok=True)
|
|
407
|
+
with open(libxr_config_path, "w", encoding="utf-8") as f:
|
|
408
|
+
json.dump(libxr_config, f, indent=4, ensure_ascii=False)
|
|
409
|
+
|
|
410
|
+
terminal_source = libxr_config.get("terminal_source", "")
|
|
411
|
+
|
|
412
|
+
# Read JSON data
|
|
413
|
+
try:
|
|
414
|
+
with open(input_file, "r", encoding="utf-8") as f:
|
|
415
|
+
project_data = json.load(f)
|
|
416
|
+
except FileNotFoundError:
|
|
417
|
+
print(f"Error: File {input_file} not found.")
|
|
418
|
+
sys.exit(1)
|
|
419
|
+
except json.JSONDecodeError:
|
|
420
|
+
print(f"Error: Failed to parse JSON file {input_file}.")
|
|
421
|
+
sys.exit(1)
|
|
422
|
+
|
|
423
|
+
# Read existing code (if any)
|
|
424
|
+
existing_code = ""
|
|
425
|
+
if os.path.exists(output_file):
|
|
426
|
+
with open(output_file, "r", encoding="utf-8") as f:
|
|
427
|
+
existing_code = f.read()
|
|
428
|
+
|
|
429
|
+
# Generate C++ code
|
|
430
|
+
cpp_code = generate_cpp_code(
|
|
431
|
+
project_data,
|
|
432
|
+
os.path.splitext(os.path.basename(input_file))[0],
|
|
433
|
+
terminal_source,
|
|
434
|
+
buffer_sizes,
|
|
435
|
+
existing_code,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Write to output file
|
|
439
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
440
|
+
f.write(cpp_code)
|
|
441
|
+
|
|
442
|
+
print(f"{output_file} generated successfully!")
|
|
443
|
+
|
|
444
|
+
# Generate app_main.h
|
|
445
|
+
header_file = os.path.join(os.path.dirname(output_file), "app_main.h")
|
|
446
|
+
with open(header_file, "w", encoding="utf-8") as f:
|
|
447
|
+
f.write("void app_main(void);\n")
|
|
448
|
+
|
|
449
|
+
generate_app_main_header(header_file)
|
|
450
|
+
print(f"{header_file} generated successfully!")
|
|
451
|
+
|
|
452
|
+
with open(libxr_config_path, "w", encoding="utf-8") as f:
|
|
453
|
+
# Write the JSON data (libxr_config) to the file
|
|
454
|
+
json.dump(libxr_config, f, indent=4, ensure_ascii=False)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import argparse
|
|
3
|
+
import fnmatch
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
file =(
|
|
8
|
+
'''set(CMAKE_CXX_STANDARD 20)
|
|
9
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
10
|
+
|
|
11
|
+
# LibXR
|
|
12
|
+
set(LIBXR_SYSTEM FreeRTOS)
|
|
13
|
+
set(LIBXR_DRIVER st)
|
|
14
|
+
add_subdirectory(Middlewares/Third_Party/LibXR)
|
|
15
|
+
target_link_libraries(
|
|
16
|
+
xr
|
|
17
|
+
stm32cubemx
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
target_include_directories(xr
|
|
21
|
+
PUBLIC $<TARGET_PROPERTY:stm32cubemx,INTERFACE_INCLUDE_DIRECTORIES>
|
|
22
|
+
PUBLIC Core/Inc
|
|
23
|
+
PUBLIC User
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Add include paths
|
|
27
|
+
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
|
28
|
+
# Add user defined include paths
|
|
29
|
+
PUBLIC $<TARGET_PROPERTY:xr,INTERFACE_INCLUDE_DIRECTORIES>
|
|
30
|
+
PUBLIC User
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Add linked libraries
|
|
34
|
+
target_link_libraries(${CMAKE_PROJECT_NAME}
|
|
35
|
+
stm32cubemx
|
|
36
|
+
|
|
37
|
+
# Add user defined libraries
|
|
38
|
+
xr
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
target_sources(${CMAKE_PROJECT_NAME}
|
|
43
|
+
PRIVATE User/app_main.cpp
|
|
44
|
+
)
|
|
45
|
+
'''
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
include_cmake_cmd = "include(${CMAKE_CURRENT_LIST_DIR}/cmake/LibXR.CMake)\n"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
parser = argparse.ArgumentParser(description="Generate CMake file for LibXR.")
|
|
53
|
+
parser.add_argument("input_dir", type=str, help="CubeMX CMake Project Directory")
|
|
54
|
+
|
|
55
|
+
args = parser.parse_args()
|
|
56
|
+
input_directory = args.input_dir
|
|
57
|
+
|
|
58
|
+
if not os.path.isdir(input_directory):
|
|
59
|
+
print("Input directory does not exist.")
|
|
60
|
+
exit(1)
|
|
61
|
+
|
|
62
|
+
file_path = input_directory + "/cmake/LibXR.CMake"
|
|
63
|
+
if os.path.exists(file_path):
|
|
64
|
+
os.remove(file_path)
|
|
65
|
+
|
|
66
|
+
with open(file_path, "w") as f:
|
|
67
|
+
f.write(file)
|
|
68
|
+
f.close()
|
|
69
|
+
|
|
70
|
+
print("LibXR.CMake generated successfully.")
|
|
71
|
+
|
|
72
|
+
main_cmake_path = input_directory + "/CMakeLists.txt"
|
|
73
|
+
if os.path.exists(main_cmake_path):
|
|
74
|
+
if include_cmake_cmd not in open(main_cmake_path).read():
|
|
75
|
+
with open(main_cmake_path, "a") as f:
|
|
76
|
+
f.write('\n# Add LibXR\n' + include_cmake_cmd)
|
|
77
|
+
f.close()
|
|
78
|
+
print("LibXR.CMake included in CMakeLists.txt.")
|
|
79
|
+
else:
|
|
80
|
+
print("LibXR.CMake already included in CMakeLists.txt.")
|
|
81
|
+
else:
|
|
82
|
+
print("Error: CMakeLists.txt not found.")
|
|
83
|
+
exit(1)
|