добавлены комменты к debug_tools.c/.h

начата работа над поддержкой stm32 и кейл проектов
This commit is contained in:
Razvalyaev 2025-07-15 19:05:24 +03:00
parent 742c4e9e1b
commit 4de53090a1
7 changed files with 485 additions and 114 deletions

Binary file not shown.

View File

@ -5,7 +5,7 @@ import sys
import os
import subprocess
import lxml.etree as ET
from generate_debug_vars import type_map
from generate_debug_vars import type_map, choose_type_map
from enum import IntEnum
import threading
from generate_debug_vars import run_generate
@ -21,8 +21,9 @@ from PySide2.QtWidgets import (
QApplication, QWidget, QTableWidget, QTableWidgetItem,
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView
)
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView,
QMenuBar, QMenu, QAction
)
from PySide2.QtGui import QTextCursor, QKeyEvent, QIcon, QFont
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QSettings
@ -58,6 +59,7 @@ class VarEditor(QWidget):
self.output_path = None
self._updating = False # Флаг блокировки рекурсии
self._resizing = False # флаг блокировки повторного вызова
self.target = 'TMS'
self.initUI()
def initUI(self):
@ -122,6 +124,23 @@ class VarEditor(QWidget):
self.btn_update_vars = QPushButton(scan_title)
self.btn_update_vars.clicked.connect(self.update_vars_data)
# Добавляем чекбокс для выбора типовой карты
# --- Создаем верхнее меню ---
menubar = QMenuBar(self)
target_menu = QMenu("Target", menubar)
# Создаем действия для выбора Target
self.action_tms = QAction("TMS", self, checkable=True)
self.action_stm = QAction("STM", self, checkable=True)
# Группируем действия чтобы выбирался только один
self.action_tms.setChecked(True) # по умолчанию TMS
self.action_tms.triggered.connect(lambda: self.on_target_selected("tms"))
self.action_stm.triggered.connect(lambda: self.on_target_selected("stm"))
target_menu.addAction(self.action_tms)
target_menu.addAction(self.action_stm)
menubar.addMenu(target_menu)
# Кнопка сохранения
btn_save = QPushButton(build_title)
btn_save.clicked.connect(self.save_build)
@ -137,6 +156,7 @@ class VarEditor(QWidget):
self.table = VariableTableWidget()
# Основной layout
layout = QVBoxLayout()
layout.setMenuBar(menubar) # прикрепляем menubar в layout сверху
layout.addLayout(xml_layout)
layout.addLayout(proj_layout)
layout.addLayout(makefile_layout)
@ -150,7 +170,18 @@ class VarEditor(QWidget):
self.setLayout(layout)
def on_target_selected(self, target):
self.target = target.lower()
if target == "stm":
choose_type_map(True)
self.action_stm.setChecked(True)
self.action_tms.setChecked(False)
else:
choose_type_map(False)
self.action_tms.setChecked(True)
self.action_stm.setChecked(False)
def get_xml_path(self):
xml_path = self.xml_output_edit.text().strip()
return xml_path
@ -358,15 +389,25 @@ class VarEditor(QWidget):
super().keyPressEvent(event)
def __browse_makefile(self):
if self.target == 'stm':
file_filter = "Makefile или Keil-проект (*.uvprojx *.uvproj makefile);;Все файлы (*)"
dialog_title = "Выберите Makefile или Keil-проект"
else: # 'TMS' или по умолчанию
file_filter = "Makefile (makefile);;Все файлы (*)"
dialog_title = "Выберите Makefile"
file_path, _ = QFileDialog.getOpenFileName(
self, "Выберите Makefile", filter="Makefile (makefile);;All Files (*)"
self,
dialog_title,
filter=file_filter
)
if file_path and self.proj_path:
path = myXML.make_relative_path(file_path, self.proj_path)
else:
path = file_path
self.makefile_edit.setText(path)
self.makefile_path = path
if file_path:
if self.proj_path:
path = myXML.make_relative_path(file_path, self.proj_path)
else:
path = file_path
self.makefile_edit.setText(path)
self.makefile_path = path
def __browse_source_output(self):
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку для debug_vars.c")
@ -438,12 +479,12 @@ class VarEditor(QWidget):
self.write_to_xml()
def __open_variable_selector(self):
def __open_variable_selector(self):
self.update()
if not self.vars_list:
QMessageBox.warning(self, "Нет переменных", f"Сначала загрузите переменные ({scan_title}).")
return
self.update()
dlg = VariableSelectorDialog(self.table, self.vars_list, self.structs, self.typedef_map, self.xml_path, self)
if dlg.exec_():
self.write_to_xml()

View File

@ -14,7 +14,7 @@ import argparse
# === Словарь соответствия типов XML → DebugVarType_t ===
type_map = dict([
type_map_tms = dict([
*[(k, 'pt_int8') for k in ('signed char', 'char')],
*[(k, 'pt_int16') for k in ('int', 'int16', 'short')],
*[(k, 'pt_int32') for k in ('long', 'int32', '_iqx')],
@ -52,6 +52,147 @@ type_map = dict([
('struct[]', 'pt_arr_struct'),
('union[]', 'pt_arr_union'),
])
# === Словарь соответствия типов XML → DebugVarType_t ===
type_map_stm32 = dict([
*[(k, 'pt_int8') for k in (
'int8_t', 'signed char', 'char'
)],
# --- 8-bit unsigned ---
*[(k, 'pt_uint8') for k in (
'uint8_t', 'unsigned char'
)],
# --- 16-bit signed ---
*[(k, 'pt_int16') for k in (
'int16_t', 'short', 'short int', 'signed short', 'signed short int'
)],
# --- 16-bit unsigned ---
*[(k, 'pt_uint16') for k in (
'uint16_t', 'unsigned short', 'unsigned short int'
)],
# --- 32-bit signed ---
*[(k, 'pt_int32') for k in (
'int32_t', 'int', 'signed', 'signed int'
)],
# --- 32-bit unsigned ---
*[(k, 'pt_uint32') for k in (
'uint32_t', 'unsigned', 'unsigned int'
)],
# --- 64-bit signed ---
*[(k, 'pt_int64') for k in (
'int64_t', 'long long', 'signed long long', 'signed long long int'
)],
# --- 64-bit unsigned ---
*[(k, 'pt_uint64') for k in (
'uint64_t', 'unsigned long long', 'unsigned long long int'
)],
# --- Float ---
*[(k, 'pt_float') for k in (
'float', 'float32_t'
)],
# --- Struct and Union ---
('struct', 'pt_struct'),
('union', 'pt_union'),
('struct*', 'pt_ptr_struct'),
('union*', 'pt_ptr_union'),
('struct[]', 'pt_arr_struct'),
('union[]', 'pt_arr_union'),
# === POINTERS ===
# 8-bit
*[(k, 'pt_ptr_int8') for k in (
'int8_t*', 'signed char*', 'char*'
)],
*[(k, 'pt_ptr_uint8') for k in (
'uint8_t*', 'unsigned char*'
)],
# 16-bit
*[(k, 'pt_ptr_int16') for k in (
'int16_t*', 'short*', 'short int*', 'signed short*', 'signed short int*'
)],
*[(k, 'pt_ptr_uint16') for k in (
'uint16_t*', 'unsigned short*', 'unsigned short int*'
)],
# 32-bit
*[(k, 'pt_ptr_int32') for k in (
'int32_t*', 'int*', 'signed*', 'signed int*'
)],
*[(k, 'pt_ptr_uint32') for k in (
'uint32_t*', 'unsigned*', 'unsigned int*'
)],
# 64-bit
*[(k, 'pt_ptr_int64') for k in (
'int64_t*', 'long long*', 'signed long long*', 'signed long long int*'
)],
*[(k, 'pt_ptr_uint64') for k in (
'uint64_t*', 'unsigned long long*', 'unsigned long long int*'
)],
# float*
*[(k, 'pt_ptr_float') for k in (
'float*', 'float32_t*'
)],
# === ARRAYS ===
# 8-bit
*[(k, 'pt_arr_int8') for k in (
'int8_t[]', 'signed char[]', 'char[]'
)],
*[(k, 'pt_arr_uint8') for k in (
'uint8_t[]', 'unsigned char[]'
)],
# 16-bit
*[(k, 'pt_arr_int16') for k in (
'int16_t[]', 'short[]', 'short int[]', 'signed short[]', 'signed short int[]'
)],
*[(k, 'pt_arr_uint16') for k in (
'uint16_t[]', 'unsigned short[]', 'unsigned short int[]'
)],
# 32-bit
*[(k, 'pt_arr_int32') for k in (
'int32_t[]', 'int[]', 'signed[]', 'signed int[]'
)],
*[(k, 'pt_arr_uint32') for k in (
'uint32_t[]', 'unsigned[]', 'unsigned int[]'
)],
# 64-bit
*[(k, 'pt_arr_int64') for k in (
'int64_t[]', 'long long[]', 'signed long long[]', 'signed long long int[]'
)],
*[(k, 'pt_arr_uint64') for k in (
'uint64_t[]', 'unsigned long long[]', 'unsigned long long int[]'
)],
# float[]
*[(k, 'pt_arr_float') for k in (
'float[]', 'float32_t[]'
)],
])
type_map = type_map_tms
def choose_type_map(stm_flag):
global type_map # объявляем, что будем менять глобальную переменную
if stm_flag:
type_map = type_map_stm32
else:
type_map = type_map_tms
def map_type_to_pt(typename, varname=None, typedef_map=None):
typename_orig = typename.strip()

View File

@ -1,5 +1,6 @@
import os
import re
from lxml import etree as ET
def strip_single_line_comments(code):
@ -66,89 +67,176 @@ def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
return include_files
def parse_objects_list(objects_list_path, project_root):
c_files = []
include_dirs = set()
if not os.path.isfile(objects_list_path):
return c_files, include_dirs
with open(objects_list_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
line = line.strip().strip('"').replace("\\", "/")
if line.endswith(".o"):
c_file = re.sub(r"\.o$", ".c", line)
abs_path = os.path.normpath(os.path.join(project_root, c_file))
if os.path.isfile(abs_path):
if not any(x in abs_path for x in ["DebugTools", "v120", "v100"]):
c_files.append(abs_path)
include_dirs.add(os.path.dirname(abs_path))
return c_files, include_dirs
def parse_uvprojx(uvprojx_path):
import xml.etree.ElementTree as ET
import os
tree = ET.parse(uvprojx_path)
root = tree.getroot()
project_dir = os.path.dirname(os.path.abspath(uvprojx_path))
c_files = []
include_dirs = set()
defines = set()
# Найдём C-файлы и директории
for file_elem in root.findall(".//FilePath"):
file_path = file_elem.text
if file_path:
abs_path = os.path.normpath(os.path.join(project_dir, file_path))
if os.path.isfile(abs_path):
if abs_path.endswith(".c"):
c_files.append(abs_path)
include_dirs.add(os.path.dirname(abs_path))
# Включаем IncludePath
for inc_path_elem in root.findall(".//IncludePath"):
path_text = inc_path_elem.text
if path_text:
paths = path_text.split(';')
for p in paths:
p = p.strip()
if p:
abs_inc_path = os.path.normpath(os.path.join(project_dir, p))
if os.path.isdir(abs_inc_path):
include_dirs.add(abs_inc_path)
# Добавим <Define>
for define_elem in root.findall(".//Define"):
def_text = define_elem.text
if def_text:
for d in def_text.split(','):
d = d.strip()
if d:
defines.add(d)
h_files = find_all_includes_recursive(c_files, include_dirs)
return sorted(c_files), sorted(h_files), sorted(include_dirs), sorted(defines)
def parse_makefile(makefile_path, proj_path):
makefile_dir = os.path.dirname(makefile_path)
project_root = proj_path
import os
import re
project_root = os.path.abspath(proj_path)
c_files = []
include_dirs = set()
defines = [] # Заглушка: нет define-параметров из Makefile
with open(makefile_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
objs_lines = []
raw_entries = []
collecting = False
for line in lines:
stripped = line.strip()
if stripped.startswith("ORDERED_OBJS") and "+=" in stripped:
parts = stripped.split("\\")
first_part = parts[0]
idx = first_part.find("+=")
tail = first_part[idx+2:].strip()
if tail:
objs_lines.append(tail)
if (("ORDERED_OBJS" in stripped or "C_SOURCES" in stripped) and ("+=" in stripped or "=" in stripped)):
collecting = True
if len(parts) > 1:
for p in parts[1:]:
p = p.strip()
if p:
objs_lines.append(p)
continue
if collecting:
if stripped.endswith("\\"):
objs_lines.append(stripped[:-1].strip())
else:
objs_lines.append(stripped)
line_clean = stripped.rstrip("\\").strip()
if line_clean:
line_clean = re.sub(r"\$\([^)]+\)", "", line_clean)
line_clean = re.sub(r"\$\{[^}]+\}", "", line_clean)
raw_entries.append(line_clean)
if not stripped.endswith("\\"):
collecting = False
objs_str = ' '.join(objs_lines)
for entry in raw_entries:
for token in entry.split():
token = token.strip('"')
if not token:
continue
objs_str = re.sub(r"\$\([^)]+\)", "", objs_str)
token = token.replace("\\", "/")
objs = []
for part in objs_str.split():
part = part.strip()
if part.startswith('"') and part.endswith('"'):
part = part[1:-1]
if part:
objs.append(part)
if token.endswith(".obj"):
token = re.sub(r"\.obj$", ".c", token)
elif token.endswith(".o"):
token = re.sub(r"\.o$", ".c", token)
c_files = []
include_dirs = set()
if token.endswith(".c"):
abs_path = os.path.normpath(os.path.join(project_root, token))
if os.path.isfile(abs_path):
if not any(x in abs_path for x in ["DebugTools", "v120", "v100"]):
c_files.append(abs_path)
include_dirs.add(os.path.dirname(abs_path))
for obj_path in objs:
if "DebugTools" in obj_path:
continue
if "v120" in obj_path:
continue
if "v100" in obj_path:
continue
if not c_files:
makefile_dir = os.path.dirname(os.path.abspath(makefile_path))
objects_list_path = os.path.join(makefile_dir, "objects.list")
c_from_objects, inc_from_objects = parse_objects_list(objects_list_path, project_root)
c_files.extend(c_from_objects)
include_dirs.update(inc_from_objects)
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
else:
rel_path = obj_path
for line in lines:
if "-I" in line or "C_INCLUDES" in line:
matches = re.findall(r"-I\s*([^\s\\]+)", line)
for match in matches:
match = match.strip('"').replace("\\", "/")
abs_include = os.path.normpath(os.path.join(project_root, match))
if os.path.isdir(abs_include):
include_dirs.add(abs_include)
abs_path = os.path.normpath(os.path.join(project_root, rel_path))
root, ext = os.path.splitext(abs_path)
if ext.lower() == ".obj":
c_path = root + ".c"
else:
c_path = abs_path
# Проверяем существование файла, если нет — пропускаем
if not os.path.isfile(c_path):
continue
# Сохраняем только .c файлы
if c_path.lower().endswith(".c"):
c_files.append(c_path)
dir_path = os.path.dirname(c_path)
if dir_path and "DebugTools" not in dir_path:
include_dirs.add(dir_path)
# Добавляем пути с заменой 'Src' на 'Inc', если путь заканчивается на 'Src'
additional_includes = set()
for inc in include_dirs:
if inc.endswith(os.sep + "Src") or inc.endswith("/Src"):
inc_inc = inc[:-3] + "Inc" # заменяем 'Src' на 'Inc'
if os.path.isdir(inc_inc):
additional_includes.add(inc_inc)
include_dirs.update(additional_includes)
h_files = find_all_includes_recursive(c_files, include_dirs)
return sorted(c_files), sorted(h_files), sorted(include_dirs), sorted(defines)
return sorted(c_files), sorted(h_files), sorted(include_dirs)
def parse_project(project_file_path, project_root=None):
"""
Выбирает парсер в зависимости от расширения project_file_path:
- для *.uvprojx и *.uvproj вызывается парсер Keil
- для остальных - parse_makefile
project_root нужен для parse_makefile, если не передан - берется из project_file_path
"""
ext = os.path.splitext(project_file_path)[1].lower()
if ext in ['.uvprojx', '.uvproj']:
# Парсим Keil проект
return parse_uvprojx(project_file_path)
else:
# Парсим makefile
if project_root is None:
project_root = os.path.dirname(os.path.abspath(project_file_path))
return parse_makefile(project_file_path, project_root)

View File

@ -11,7 +11,7 @@ from clang import cindex
from clang.cindex import Config
import lxml.etree as ET
from xml.dom import minidom
from makefile_parser import parse_makefile
from makefile_parser import parse_project
from collections import deque
import argparse
import myXML
@ -118,11 +118,11 @@ def get_canonical_typedef_file(var_type, include_dirs):
break
return None
def analyze_variables_across_files(c_files, h_files, include_dirs):
def analyze_variables_across_files(c_files, h_files, include_dirs, global_defs):
optional_printf(PRINT_STATUS, "Starting analysis of variables across files...")
index = clang.cindex.Index.create()
args = [f"-I{inc}" for inc in include_dirs]
define_args = [f"-D{d}" for d in global_defs]
args = [f"-I{inc}" for inc in include_dirs] + define_args
unique_vars = {} # имя переменной → словарь с инфой
h_files_needed = set()
vars_need_extern = {} # имя переменной → словарь без поля 'extern'
@ -295,6 +295,8 @@ def strip_ptr_and_array(typename):
return typename
def analyze_typedefs_and_struct(typedefs, structs):
optional_printf(PRINT_STATUS, "Resolving typedefs and expanding struct field types...")
@ -422,10 +424,28 @@ def contains_anywhere_in_node(node, target: str) -> bool:
return False
def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
def try_guess_std_include():
# Популярные места, где может лежать stdint.h
guesses = [
r"C:\Keil_v5\ARM\ARMCLANG\include",
r"C:\Program Files (x86)\GNU Arm Embedded Toolchain",
r"C:\Program Files (x86)\Arm GNU Toolchain"
]
found = []
for base in guesses:
for root, dirs, files in os.walk(base):
if "stdint.h" in files:
found.append(root)
return found
def analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs):
optional_printf(PRINT_STATUS, "Starting analysis of typedefs and structs across files...")
index = clang.cindex.Index.create()
args = [f"-I{inc}" for inc in include_dirs]
define_args = [f"-D{d}" for d in global_defs]
extra_std_include_dirs = try_guess_std_include()
args = [f"-I{inc}" for inc in include_dirs] + extra_std_include_dirs + define_args
unique_typedefs_raw = {}
unique_structs_raw = {}
@ -444,6 +464,8 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
def visit(node):
if node.kind == clang.cindex.CursorKind.TYPEDEF_DECL:
name = node.spelling
if 'ADC_HandleTypeDef' in name:
a =1
underlying = node.underlying_typedef_type.spelling
typedefs[name] = underlying
@ -451,6 +473,8 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
prefix = "struct " if node.kind == clang.cindex.CursorKind.STRUCT_DECL else "union "
raw_name = node.spelling
if 'struct (unnamed struct at F:\\Work\\Projects\\NIIET\\MZKT\\MZKT\\Drivers\\STM32F4xx_HAL_Driver\\Inc\\stm32f4xx_hal_adc.h:195:9)' in raw_name:
a =1
normalized_name = normalize_type_name(raw_name)
# struct_name всегда с префиксом
@ -855,10 +879,10 @@ Usage example:
print(f"Error: Makefile path '{makefile_path}' does not exist.")
sys.exit(1)
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
c_files, h_files, include_dirs, global_defs = parse_project(makefile_path, proj_path)
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs, global_defs)
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs)
vars = dict(sorted(vars.items()))
includes = get_sorted_headers(c_files, includes, include_dirs)
@ -898,10 +922,10 @@ def run_scan(proj_path, makefile_path, output_xml, verbose=2):
if not os.path.isfile(makefile_path):
raise FileNotFoundError(f"Makefile path '{makefile_path}' does not exist.")
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
c_files, h_files, include_dirs, global_defs = parse_project(makefile_path, proj_path)
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs, global_defs)
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs)
vars = dict(sorted(vars.items()))
includes = get_sorted_headers(c_files, includes, include_dirs)

View File

@ -1,18 +1,23 @@
#include "debug_tools.h"
#include "IQmathLib.h"
DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT;
DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT; ///< Структура отладки нижнего уровня (инициализация)
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var);
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var);
///////////////////////////----EXAPLE-----//////////////////////////////
long var_numb = 1;
DebugVarName_t var_name;
long return_var;
long return_ll_var;
int result;
char ext_date[] = {7, 233, 11, 07, 16, 50};
long var_numb = 1; ///< Пример переменной для отладки
DebugVarName_t var_name; ///< Имя переменной
long return_var; ///< Переменная для возврата результата
long return_ll_var; ///< Возвращаемое значение с нижнего уровня
int result; ///< Переменная результата
char ext_date[] = {7, 233, 11, 07, 16, 50}; ///< Пример внешней даты сборки
/**
* @brief Пример использования функций отладки.
* @details Демонстрационная функция для работы с переменными отладки.
*/
void Debug_Test_Example(void)
{
result = Debug_ReadVar(var_numb, &return_var);
@ -24,6 +29,14 @@ void Debug_Test_Example(void)
}
///////////////////////////----PUBLIC-----//////////////////////////////
/**
* @brief Читает переменную по индексу.
* @param var_ind индекс переменной.
* @param return_long указатель для возврата результата.
* @return int 0: успех, 1: ошибка.
* @details Используется для чтения значений переменных по их индексу.
*/
int Debug_ReadVar(int var_ind, long *return_long)
{
if(return_long == NULL)
@ -41,6 +54,13 @@ int Debug_ReadVar(int var_ind, long *return_long)
return convertDebugVarToIQx(&dbg_vars[var_numb], return_long);
}
/**
* @brief Читает имя переменной по индексу.
* @param var_ind индекс переменной.
* @param name_ptr указатель на буфер имени (DebugVarName_t).
* @return int 0: успех, 1: ошибка.
* @details Копирует имя переменной в предоставленный буфер.
*/
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr)
{
if(name_ptr == NULL)
@ -64,7 +84,12 @@ int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr)
}
/**
* @brief Читает значение переменной отладки с нижнего уровня.
* @param return_long указатель на переменную, куда записывается результат.
* @return int 0: успех, 1: ошибка, 2: недопустимый адрес.
* @details Использует адресс, передаваемый с терминалки для получения значения.
*/
int Debug_LowLevel_ReadVar(long *return_long)
{
if (return_long == NULL)
@ -93,7 +118,12 @@ int Debug_LowLevel_ReadVar(long *return_long)
;
}
/**
* @brief Инициализация отладки на нижнем уровне по дате сборки.
* @param external_date указатель на массив из 6 байт: {year_hi, year_lo, day, month, hour, minute}.
* @return int 0: совпадает, 1: не совпадает, -1: ошибка.
* @details Сравнивает дату компиляции с запрашиваемой и инициализирует отладочную переменную.
*/
int Debug_LowLevel_Initialize(const char* external_date)
{
if (external_date == NULL) {
@ -129,6 +159,12 @@ int Debug_LowLevel_Initialize(const char* external_date)
/////////////////////----INTERNAL FUNCTIONS-----////////////////////////
/**
* @brief Преобразует тип IQ переменной в число битов для сдвига(Q-фактор).
* @param t тип IQ (перечисление DebugVarIQType_t).
* @return int Q-фактор (например, 24), 0: если t_iq_none, -1: ошибка.
* @details Сопоставляет тип IQ переменной с соответствующим Q-значением.
*/
static int iqTypeToQ(DebugVarIQType_t t)
{
if (t == t_iq_none)
@ -141,6 +177,13 @@ static int iqTypeToQ(DebugVarIQType_t t)
return -1; // îøèáêà
}
/**
* @brief Преобразует переменную отладки в IQ формат.
* @param var указатель на переменную отладки.
* @param ret_var указатель для возврата значения в формате long.
* @return int 0: успех, 1: ошибка чтения, 2: неправильный формат, 3: переполнение.
* @details Определяет формат IQ переменной, конвертирует её в long с учётом масштаба.
*/
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
{
long iq_numb, iq_united, iq_final;
@ -196,6 +239,14 @@ static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
return 0;
}
/**
* @brief Прочитать значение переменной отладки.
* @param var указатель на структуру DebugVar.
* @param int_var указатель на переменную типа long для возврата целочисленного значения.
* @param float_var указатель на переменную типа float для возврата значения с плавающей точкой.
* @return int 0: успех, 1: ошибка указателей или неподдерживаемый тип, 3/4: ошибка выравнивания.
* @details В зависимости от типа переменной считывает её значение и сохраняет в соответствующем указателе.
*/
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var)
{
if (!var || !int_var || !float_var || !var->Ptr)

View File

@ -3,6 +3,9 @@
#include "IQmathLib.h"
#include "DSP281x_Device.h"
/**
* @brief Тип данных, на который указывает указатель переменной отладки.
*/
typedef enum
{
pt_unknown, // unknown
@ -31,6 +34,9 @@ typedef enum
// pt_arr_uint32, // unsigned long[]
}DebugVarPtrType_t;
/**
* @brief Типы IQ-представления переменной отладки.
*/
typedef enum
{
t_iq_none,
@ -67,45 +73,65 @@ typedef enum
t_iq30
}DebugVarIQType_t;
typedef char DebugVarName_t[11];
typedef char DebugVarName_t[11]; ///< Имя переменной отладки (до 10 символов + \0)
/**
* @brief Описание переменной отладки.
*/
typedef struct
{
char* Ptr;
DebugVarPtrType_t ptr_type;
DebugVarIQType_t iq_type;
DebugVarIQType_t return_type;
DebugVarName_t name;
}DebugVar_t;
char* Ptr; ///< Указатель на значение переменной
DebugVarPtrType_t ptr_type; ///< Тип значения
DebugVarIQType_t iq_type; ///< Тип IQ переменной (если есть)
DebugVarIQType_t return_type;///< Тип IQ возвращаемого значения
DebugVarName_t name; ///< Имя переменной
} DebugVar_t;
/**
* @brief Структура даты и времени.
*/
typedef struct {
int year;
char month;
char day;
char hour;
char minute;
int year; ///< Год (например, 2025)
char month; ///< Месяц (1-12)
char day; ///< День (1-31)
char hour; ///< Часы (0-23)
char minute; ///< Минуты (0-59)
} DateTime_t;
/**
* @brief Структура нижнего уровня отладки.
*/
typedef struct
{
DateTime_t build_date;
unsigned int isVerified;
DebugVar_t dbg_var;
DateTime_t build_date; ///< Дата сборки
unsigned int isVerified; ///< Флаг инициализации низкоуровненой отладки (0 — нет, 1 — успешно)
DebugVar_t dbg_var; ///< Переменная для отладки
}DebugLowLevel_t;
extern DebugLowLevel_t debug_ll;
extern DebugLowLevel_t debug_ll; ///< Глобальный экземпляр отладки нижнего уровня
/** @brief Макрос инициализации даты */
#define DATE_INIT {BUILD_YEAR, BUILD_MONTH, BUILD_DATA, BUILD_HOURS, BUILD_MINUTES}
/** @brief Макрос инициализации переменной отладки */
#define DEBUG_VAR_INIT {0, pt_uint16, t_iq_none, t_iq_none, "\0"}
/** @brief Макрос инициализации нижнего уровня отладки */
#define DEBUG_LOWLEVEL_INIT {DATE_INIT, 0, DEBUG_VAR_INIT}
extern int DebugVar_Qnt;
extern DebugVar_t dbg_vars[];
extern int DebugVar_Qnt; ///< Количество переменных отладки
extern DebugVar_t dbg_vars[]; ///< Массив переменных отладки
/* Пример использования отладки */
void Debug_Test_Example(void);
/* Читает значение переменной по индексу */
int Debug_ReadVar(int var_ind, long *return_long);
/* Читает имя переменной по индексу */
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr);
/* Читает значение переменной с нижнего уровня */
int Debug_LowLevel_ReadVar(long *return_long);
/* Инициализирует отладку нижнего уровня */
int Debug_LowLevel_Initialize(const char* external_date);
#endif //DEBUG_TOOLS