Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
1aa3c5b955 | |||
966ddc3bac | |||
df30570d4a | |||
d983f2525a |
BIN
MCU Wrapper.mltbx
Normal file
BIN
MCU Wrapper.mltbx
Normal file
Binary file not shown.
@ -7,41 +7,7 @@ function install_my_library()
|
||||
addpath(fullfile(libDir, 'm'));
|
||||
savepath;
|
||||
|
||||
% 2. Диалог выбора папки для копирования шаблонов
|
||||
defaultTargetDir = pwd;
|
||||
answer = questdlg(['Выберите папку для копирования шаблонов кода. ', ...
|
||||
'По умолчанию текущая папка: ' defaultTargetDir], ...
|
||||
'Выбор папки', ...
|
||||
'Текущая папка', 'Выбрать другую', 'Текущая папка');
|
||||
|
||||
switch answer
|
||||
case 'Выбрать другую'
|
||||
targetDir = uigetdir(defaultTargetDir, 'Выберите папку для шаблонов');
|
||||
if isequal(targetDir,0)
|
||||
disp('Копирование шаблонов отменено пользователем.');
|
||||
targetDir = '';
|
||||
end
|
||||
case 'Текущая папка'
|
||||
targetDir = defaultTargetDir;
|
||||
otherwise
|
||||
targetDir = defaultTargetDir;
|
||||
end
|
||||
|
||||
if ~isempty(targetDir)
|
||||
templatesDir = fullfile(libDir, 'templates');
|
||||
templateFiles = dir(fullfile(templatesDir, '*.*'));
|
||||
for k = 1:numel(templateFiles)
|
||||
if ~templateFiles(k).isdir
|
||||
copyfile(fullfile(templatesDir, templateFiles(k).name), ...
|
||||
fullfile(targetDir, templateFiles(k).name));
|
||||
end
|
||||
end
|
||||
fprintf('Шаблоны кода скопированы в папку:\n%s\n', targetDir);
|
||||
end
|
||||
|
||||
% 3. Обновляем Library Browser
|
||||
rehash;
|
||||
sl_refresh_customizations;
|
||||
|
||||
disp('Библиотека успешно установлена и добавлена в Library Browser.');
|
||||
end
|
||||
|
Binary file not shown.
154
McuLib/m/appWrap.m
Normal file
154
McuLib/m/appWrap.m
Normal file
@ -0,0 +1,154 @@
|
||||
classdef appWrap
|
||||
|
||||
methods(Static)
|
||||
function appWrapperFunc()
|
||||
block = gcb;
|
||||
% Получаем имя функции и путь к файлам
|
||||
[filename, section, tool, example]= appWrap.getAppWrapperUserFile(block);
|
||||
mcuMask.tool(tool, example);
|
||||
|
||||
% Загружаем содержимое файла
|
||||
set_param(block, 'appWrapperCode', '');
|
||||
try
|
||||
code = fileread(filename);
|
||||
code = regexprep(code, '\r\n?', '\n'); % нормализуем окончания строк
|
||||
|
||||
includesText = editCode.extractSection(code, section);
|
||||
set_param(block, 'appWrapperCode', includesText);
|
||||
catch
|
||||
end
|
||||
% % Поиск тела обычной функции
|
||||
% expr = sprintf('void %s()', sel);
|
||||
% funcBody = editCode.extractSection(code, expr);
|
||||
% set_param(block, 'wrapperCode', funcBody);
|
||||
end
|
||||
|
||||
function saveAppWrapperCode()
|
||||
block = gcb;
|
||||
|
||||
% Получаем имя функции и путь к файлам
|
||||
[filename, section] = appWrap.getAppWrapperUserFile(block);
|
||||
if ~isfile(filename)
|
||||
errordlg(['Файл не найден: ', filename]);
|
||||
return;
|
||||
end
|
||||
|
||||
sel = get_param(block, 'appWrapperFunc');
|
||||
basePath = get_param(block, 'appWrapperPath');
|
||||
if isempty(basePath)
|
||||
errordlg('Не указан путь к файлам обёртки (wrapperPath).');
|
||||
return;
|
||||
end
|
||||
newBody = get_param(block, 'appWrapperCode');
|
||||
code = fileread(filename);
|
||||
code = regexprep(code, '\r\n?', '\n');
|
||||
newBody = strrep(newBody, '\', '\\');
|
||||
code = editCode.insertSection(code, section, newBody);
|
||||
% else
|
||||
% % Обновляем тело функции
|
||||
% expr = sprintf('void %s()', sel);
|
||||
% code = editCode.insertSection(code, expr, newBody);
|
||||
% end
|
||||
fid = fopen(filename, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
errordlg('Не удалось открыть файл для записи');
|
||||
return;
|
||||
end
|
||||
fwrite(fid, code);
|
||||
fclose(fid);
|
||||
mcuMask.disp(1, ['Обновлено: ' sel]);
|
||||
end
|
||||
|
||||
function openAppWrapperCode()
|
||||
block = gcb;
|
||||
|
||||
% Получаем имя функции и путь к файлам
|
||||
filename = mcuPath.getAbsolutePath(appWrap.getAppWrapperUserFile(block));
|
||||
if exist(filename, 'file') == 2
|
||||
% Формируем команду без кавычек
|
||||
cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename);
|
||||
status = system(cmd);
|
||||
if status ~= 0
|
||||
errordlg('Не удалось открыть окно выбора приложения.');
|
||||
end
|
||||
else
|
||||
errordlg('Файл не найден');
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
%% SPECIFIC TOOLS
|
||||
function [filename, section, tool, example] = getAppWrapperUserFile(block, sel)
|
||||
if (nargin < 2)
|
||||
sel = get_param(block, 'appWrapperFunc');
|
||||
end
|
||||
|
||||
basePath = mcuPath.get('appWrapperPath');
|
||||
if isempty(basePath)
|
||||
errordlg('Не указан путь к файлам обёртки (wrapperPath).');
|
||||
return;
|
||||
end
|
||||
% Формируем путь к файлу в зависимости от типа запроса
|
||||
if strcmp(sel, 'Includes')
|
||||
filename = fullfile(basePath, 'app_includes.h');
|
||||
section = '// INCLUDES';
|
||||
tool = 'Инклюды для доступа к коду МК в коде оболочке';
|
||||
example = '#include "main.h"';
|
||||
elseif strcmp(sel, 'Dummy')
|
||||
filename = fullfile(basePath, 'app_wrapper.c');
|
||||
section = '// DUMMY';
|
||||
tool = 'Заглушки для различных функций и переменных';
|
||||
example = ['CAN_HandleTypeDef hcan = {0};' newline...
|
||||
'void hardware_func(handle *huart) {}' newline...
|
||||
'int wait_for_hardware_flag(int *flag) {' newline...
|
||||
' return 1;' newline...
|
||||
'}' newline...
|
||||
''];
|
||||
elseif strcmp(sel, 'App Init')
|
||||
filename = fullfile(basePath, 'app_init.c');
|
||||
section = '// USER APP INIT';
|
||||
tool = ['Код для инициализации приложения МК.' newline newline...
|
||||
'Вызов функций инициализации, если не используется отдельный поток для main().'];
|
||||
example = 'init_func();';
|
||||
elseif strcmp(sel, 'App Step')
|
||||
filename = fullfile(basePath, 'app_wrapper.c');
|
||||
section = '// USER APP STEP';
|
||||
tool = ['Код приложения МК для вызова в шаге симуляции.' newline newline ...
|
||||
'Вызов функций программы МК, если не используется отдельный поток для main().'];
|
||||
example = 'step_func();';
|
||||
elseif strcmp(sel, 'App Inputs')
|
||||
filename = fullfile(basePath, 'app_io.c');
|
||||
section = '// USER APP INPUT';
|
||||
tool = ['Работа с буффером для портов S-Function' newline newline ...
|
||||
'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ...
|
||||
'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ...
|
||||
'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт'];
|
||||
example = ['// чтение 1-го элемента 0-го входного массива' newline...
|
||||
'app_variable_2 = ReadInputArray(0, 1);' newline newline...
|
||||
'// запись в буфер выходов' newline ...
|
||||
'app_variable_2 = Buffer[10];'];
|
||||
elseif strcmp(sel, 'App Outputs')
|
||||
filename = fullfile(basePath, 'app_io.c');
|
||||
section = '// USER APP OUTPUT';
|
||||
tool = ['Работа с буффером для портов S-Function' newline newline ...
|
||||
'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ...
|
||||
'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ...
|
||||
'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт'];
|
||||
example = ['// запись в 1-й элемент 0-го выходного массива' newline...
|
||||
'WriteOutputArray(app_variable, 0, 1);' newline newline ...
|
||||
'// запись в буфер выходов' newline ...
|
||||
'Buffer[XD_OUTPUT_START + 10] = app_variable_2;'];
|
||||
elseif strcmp(sel, 'App Deinit')
|
||||
filename = fullfile(basePath, 'app_init.c');
|
||||
section = '// USER APP DEINIT';
|
||||
tool = ['Код для деинициализации приложения МК.' newline newline ...
|
||||
'Можно деинициализировать приложение МК, для повторного запуска.'];
|
||||
example = 'memset(&htim1, sizeof(htim1), 0;';
|
||||
else
|
||||
tool = '';
|
||||
mcuMask.disp(0, '\nОшибка выбора типа секции кода: неизвестное значение');
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -39,7 +39,7 @@ classdef asynchManage < handle
|
||||
methods (Access = private)
|
||||
function saveCallback(obj)
|
||||
try
|
||||
mcuMask.saveAndClose(obj.maskBlockPath);
|
||||
mcuMask.close(obj.maskBlockPath);
|
||||
save_system(obj.modelName);
|
||||
catch ME
|
||||
warning('progr:Nneg', 'Ошибка при сохранении модели: %s', ME.message);
|
||||
@ -67,7 +67,6 @@ classdef asynchManage < handle
|
||||
if ~isempty(obj.maskBlockPath)
|
||||
try
|
||||
mcuMask.open(obj.maskBlockPath, 1);
|
||||
fprintf('Mask opened for block %s\n', obj.maskBlockPath);
|
||||
catch ME
|
||||
warning('progr:Nneg', 'Не удалось открыть маску: %s', ME.message);
|
||||
end
|
||||
@ -80,7 +79,7 @@ classdef asynchManage < handle
|
||||
function GUIconfigCallback(obj)
|
||||
|
||||
try
|
||||
mcuMask.saveAndClose(obj.maskBlockPath);
|
||||
mcuMask.close(obj.maskBlockPath);
|
||||
mexing(0);
|
||||
catch ME
|
||||
warning('progr:Nneg', 'Ошибка при обновлении модели: %s', ME.message);
|
||||
|
140
McuLib/m/compiler.m
Normal file
140
McuLib/m/compiler.m
Normal file
@ -0,0 +1,140 @@
|
||||
classdef compiler
|
||||
methods(Static)
|
||||
|
||||
function compile()
|
||||
addpath(mcuPath.get('wrapperPath'));
|
||||
mexing(1);
|
||||
end
|
||||
|
||||
function get_availbe()
|
||||
addpath(mcuPath.get('wrapperPath'));
|
||||
mexing(1);
|
||||
end
|
||||
|
||||
function choose()
|
||||
addpath(mcuPath.get('wrapperPath'));
|
||||
mexing(1);
|
||||
end
|
||||
|
||||
|
||||
|
||||
function updateRunBat()
|
||||
sources = {
|
||||
'MCU.c'
|
||||
'mcu_wrapper.c'
|
||||
};
|
||||
% Список заголовочных файлов (.h)
|
||||
includes = { '.\'
|
||||
};
|
||||
wrapperPath = mcuPath.get('wrapperPath');
|
||||
% [wrapperPath, ~, ~] = fileparts(wrapperPath);
|
||||
% Формируем строки
|
||||
wrapperSrcText = compiler.createSourcesBat('code_WRAPPER', sources, wrapperPath);
|
||||
wrapperIncText = compiler.createIncludesBat('includes_WRAPPER', includes, wrapperPath);
|
||||
|
||||
% Записываем результат
|
||||
res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: WRAPPER BAT'); % Всё прошло успешно
|
||||
|
||||
if res == 0
|
||||
return
|
||||
end
|
||||
|
||||
sources = {
|
||||
'app_wrapper.c'
|
||||
'app_init.c'
|
||||
'app_io.c'
|
||||
};
|
||||
% Список заголовочных файлов (.h)
|
||||
includes = { '.\'
|
||||
};
|
||||
periphPath = mcuPath.get('appWrapperPath');
|
||||
% Формируем строки
|
||||
wrapperSrcText = compiler.createSourcesBat('code_APP_WRAPPER', sources, periphPath);
|
||||
wrapperIncText = compiler.createIncludesBat('includes_APP_WRAPPER', includes, periphPath);
|
||||
|
||||
% Записываем результат
|
||||
res = compiler.updateRunMexBat(wrapperSrcText, wrapperIncText, ':: APP WRAPPER BAT'); % Всё прошло успешно
|
||||
|
||||
periphConfig.updatePeriphRunMexBat();
|
||||
end
|
||||
|
||||
|
||||
function res = updateRunMexBat(srcText, incText, Section)
|
||||
% Входные параметры:
|
||||
% srcText - текст для записи set code_...
|
||||
% incText - текст для записи set includes_...
|
||||
%
|
||||
% Возвращает:
|
||||
% res - 0 при успехе, 1 при ошибке
|
||||
periphBat = [srcText '\n\n' incText];
|
||||
batPath = fullfile(mcuPath.get('wrapperPath'), 'run_mex.bat');
|
||||
res = 1;
|
||||
try
|
||||
code = fileread(batPath);
|
||||
code = regexprep(code, '\r\n?', '\n');
|
||||
|
||||
% Записываем строки srcText и incText с переносами строк
|
||||
code = editCode.insertSection(code, Section, periphBat);
|
||||
|
||||
fid = fopen(batPath, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
error('Не удалось открыть файл для записи');
|
||||
end
|
||||
fwrite(fid, code);
|
||||
fclose(fid);
|
||||
res = 1;
|
||||
catch ME
|
||||
mcuMask.disp(0, '\nОшибка: неудачная запись в файл при записи файла: %s', ME.message);
|
||||
end
|
||||
end
|
||||
|
||||
function srcText = createSourcesBat(prefix_name, sources, path)
|
||||
srcList = {};
|
||||
if nargin >= 2 && iscell(sources)
|
||||
for i = 1:numel(sources)
|
||||
fullPath = fullfile(path, sources{i});
|
||||
srcList{end+1} = strrep(fullPath, '\', '\\');
|
||||
end
|
||||
end
|
||||
|
||||
% Формируем srcText с переносами строк и ^
|
||||
srcText = '';
|
||||
for i = 1:numel(srcList)
|
||||
if i < numel(srcList)
|
||||
srcText = [srcText srcList{i} '^' newline ' '];
|
||||
else
|
||||
srcText = [srcText srcList{i}];
|
||||
end
|
||||
end
|
||||
|
||||
% Добавляем префикс
|
||||
srcText = ['set ' prefix_name '=' srcText];
|
||||
end
|
||||
|
||||
function incText = createIncludesBat(prefix_name, includes, path)
|
||||
incList = {};
|
||||
if nargin >= 2 && iscell(includes)
|
||||
for i = 1:numel(includes)
|
||||
fullPath = fullfile(path, includes{i});
|
||||
incList{end+1} = ['-I"' strrep(fullPath, '\', '\\') '"'];
|
||||
end
|
||||
end
|
||||
|
||||
% Формируем incText с переносами строк и ^
|
||||
incText = '';
|
||||
for i = 1:numel(incList)
|
||||
if i == 1 && numel(incList) ~= 1
|
||||
incText = [incText incList{i} '^' newline];
|
||||
elseif i < numel(incList)
|
||||
incText = [incText ' ' incList{i} '^' newline];
|
||||
else
|
||||
incText = [incText ' ' incList{i}];
|
||||
end
|
||||
end
|
||||
|
||||
% Добавляем префикс
|
||||
incText = ['set ' prefix_name '=' incText];
|
||||
end
|
||||
|
||||
end
|
||||
end
|
143
McuLib/m/configJs.m
Normal file
143
McuLib/m/configJs.m
Normal file
@ -0,0 +1,143 @@
|
||||
classdef configJs
|
||||
|
||||
methods(Static)
|
||||
|
||||
function config = update(blockPath, config)
|
||||
if isempty(config)
|
||||
return;
|
||||
end
|
||||
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
maskParams = mask.Parameters;
|
||||
paramNames = arrayfun(@(p) p.Name, maskParams, 'UniformOutput', false);
|
||||
|
||||
% Обработка остальных секций (с дефайнами)
|
||||
periphs = fieldnames(config);
|
||||
for i = 1:numel(periphs)
|
||||
periph = periphs{i};
|
||||
|
||||
% Проверяем есть ли Defines
|
||||
if ~isfield(config.(periph), 'Defines')
|
||||
continue;
|
||||
end
|
||||
|
||||
defines = config.(periph).Defines;
|
||||
defNames = fieldnames(defines);
|
||||
|
||||
for j = 1:numel(defNames)
|
||||
defPrompt = defNames{j};
|
||||
paramName = matlab.lang.makeValidName(defPrompt);
|
||||
|
||||
% Проверка, существует ли параметр с таким именем
|
||||
if ismember(paramName, paramNames)
|
||||
param = mask.getParameter(paramName);
|
||||
valStr = param.Value;
|
||||
|
||||
% Проверяем, существует ли элемент defPrompt в структуре defines
|
||||
if isfield(defines, defPrompt)
|
||||
% Преобразуем строку в соответствующий тип
|
||||
if strcmpi(defines.(defPrompt).Type, 'checkbox')
|
||||
config.(periph).Defines.(defPrompt).Default = strcmpi(valStr, 'on');
|
||||
elseif strcmpi(defines.(defPrompt).Type, 'edit')
|
||||
valNum = str2double(valStr);
|
||||
if isnan(valNum)
|
||||
config.(periph).Defines.(defPrompt).Default = valStr;
|
||||
else
|
||||
config.(periph).Defines.(defPrompt).Default = valNum;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function config = read(blockPath)
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
|
||||
pathparam = mask.getParameter('periphPath');
|
||||
config_path = pathparam.Value;
|
||||
|
||||
if ~isempty(config_path)
|
||||
jsonText = fileread(config_path);
|
||||
config = jsondecode(jsonText);
|
||||
else
|
||||
config = [];
|
||||
end
|
||||
end
|
||||
|
||||
function write(config)
|
||||
if isempty(config)
|
||||
return
|
||||
end
|
||||
|
||||
blockHandle = gcbh;
|
||||
mask = Simulink.Mask.get(blockHandle);
|
||||
|
||||
pathparam = mask.getParameter('periphPath');
|
||||
config_path = pathparam.Value;
|
||||
|
||||
jsonText = jsonencode(config, 'PrettyPrint', true);
|
||||
fid = fopen(config_path, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
error('Не удалось открыть файл periph_config.json для записи.');
|
||||
end
|
||||
fwrite(fid, jsonText, 'char');
|
||||
fclose(fid);
|
||||
end
|
||||
|
||||
|
||||
|
||||
function value = get_field(configStruct, targetConfig)
|
||||
% получить targetConfig структуру из конфига (для глубоко вложенных)
|
||||
value = [];
|
||||
fields = fieldnames(configStruct);
|
||||
|
||||
for i = 1:numel(fields)
|
||||
key = fields{i};
|
||||
if strcmp(key, targetConfig)
|
||||
value = configStruct.(key);
|
||||
return; % нашли и возвращаем
|
||||
elseif isstruct(configStruct.(key))
|
||||
value = configJs.get_field(configStruct.(key), targetConfig);
|
||||
if ~isempty(value)
|
||||
return; % нашли во вложенной структуре
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
% Если не нашли, можно выбросить ошибку или вернуть пустое
|
||||
if isempty(value)
|
||||
% error('Поле "%s" не найдено в структуре.', targetConfig);
|
||||
end
|
||||
end
|
||||
|
||||
function short = get_final_name_from_prefix(prefix)
|
||||
% Берёт последнее имя после "_" (читаемое имя)
|
||||
parts = strsplit(prefix, '_');
|
||||
short = parts{end};
|
||||
end
|
||||
|
||||
function value = convert_code_value(codeField)
|
||||
% Преобразует значение поля Options в строку
|
||||
if ischar(codeField)
|
||||
value = codeField;
|
||||
elseif isstring(codeField)
|
||||
value = char(codeField);
|
||||
elseif iscell(codeField)
|
||||
value = strjoin(codeField, newline);
|
||||
else
|
||||
% warning('Неподдерживаемый тип данных: сохранено как пустая строка');
|
||||
value = '';
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
methods(Static, Access=private)
|
||||
|
||||
end
|
||||
end
|
@ -8,13 +8,15 @@ classdef customtable
|
||||
tableControl = mask.getDialogControl(table_name);
|
||||
tableParameter = mask.getParameter(table_name);
|
||||
nCols = tableControl.getNumberOfColumns;
|
||||
if nCols > 0
|
||||
for i = 1:nCols
|
||||
tableControl.removeColumn(1);
|
||||
end
|
||||
% инициализация колонок если они пустые
|
||||
% такое случается при removeParameter
|
||||
if isempty(tableControl.Columns) || (nCols > 1)
|
||||
for i = 1:nCols
|
||||
tableControl.removeColumn(1);
|
||||
end
|
||||
column = tableControl.addColumn(Name='Title', Type='edit');
|
||||
tableControl.Sortable = 'on';
|
||||
end
|
||||
column = tableControl.addColumn(Name='Title', Type='edit');
|
||||
tableControl.Sortable = 'on';
|
||||
column.Name = tableParameter.Alias;
|
||||
end
|
||||
|
||||
|
@ -74,6 +74,7 @@ classdef editCode
|
||||
end
|
||||
|
||||
% Формируем новую секцию с нужным текстом
|
||||
% newText = strrep(newText, '\', '\\'); % экранируем для корректной вставки
|
||||
replacement = sprintf('%s START\n%s\n%s END', sectionName, newText, sectionName);
|
||||
|
||||
% Заменяем всю найденную секцию на новую
|
||||
|
120
McuLib/m/init.m
120
McuLib/m/init.m
@ -1,120 +0,0 @@
|
||||
% Cкрипт для задания параметров модели
|
||||
|
||||
clear;%очищаем рабочее пространство
|
||||
|
||||
%% ПАРАМЕТРЫ МОДЕЛИ
|
||||
|
||||
addpath('MCU_Wrapper');
|
||||
addpath('motor');
|
||||
Ts = 10e-6;%шаг интегрирования
|
||||
Decim = 1;%интервал прореживания
|
||||
DisableScope = {
|
||||
"Idc";
|
||||
"Udc";
|
||||
};
|
||||
|
||||
GED = "23550";
|
||||
% GED = "22220";
|
||||
|
||||
% начальная скорость ГЭД, доля от NmNom
|
||||
w0 = 0;%0.5;%-0.75;%
|
||||
% пусковой момент, о.е.
|
||||
Mst = 0.6;%0.6;
|
||||
|
||||
% разрешаем/запрещаем сбросы/набросы момента нагрузки
|
||||
changingLoadEnable = 0;%1
|
||||
% разрешаем/запрещаем шум в измеренном токе
|
||||
noiseEnable = 0;%1;%
|
||||
% ... мощность шума
|
||||
NP = 0.08;
|
||||
|
||||
|
||||
%% НОМИНАЛЬНЫЕ ВЕЛИЧИНЫ ГЭД
|
||||
% ... мощность на валу, Вт
|
||||
Pnom = 6300e3;
|
||||
% ... линейное напряжение, В (rms)
|
||||
Unom = 3300;
|
||||
% ... механическая скорость, об/мин
|
||||
NmNom = 180;
|
||||
% ... число пар полюсов
|
||||
Pp = 6;
|
||||
% ... коэффициент мощности
|
||||
CosFi = 0.87;
|
||||
% ... КПД
|
||||
Eff = 0.968;
|
||||
% ... приведенный к валу момент инерции, кг*м^2
|
||||
J = 87e3*0.1;
|
||||
|
||||
|
||||
%% РАСЧЕТЫ
|
||||
% разкомментирование всех блоков
|
||||
|
||||
modelName = [bdroot '/Measurements'];
|
||||
blocks = find_system(modelName, ...
|
||||
'IncludeCommented', 'on', ...
|
||||
'FollowLinks', 'on', ...
|
||||
'LookUnderMasks', 'all', ...
|
||||
'BlockType', 'Scope');
|
||||
for i = 1:length(blocks)
|
||||
set_param(blocks{i}, 'Commented', 'off');
|
||||
end
|
||||
% отключение графиков для ускорения смуляции
|
||||
for i = 1:length(DisableScope)
|
||||
set_param([modelName '/'] + DisableScope{i}, 'Commented', 'on');
|
||||
end
|
||||
|
||||
% для упрощения записи
|
||||
SQRT2 = sqrt(2);
|
||||
SQRT3 = sqrt(3);
|
||||
PI2 = pi*2;
|
||||
|
||||
% ... полная мощность, ВА
|
||||
Snom = Pnom/CosFi/Eff;
|
||||
% ... механическая скорость, рад/с
|
||||
WmNom = NmNom/60*PI2;
|
||||
% ... момент на валу, Н*м
|
||||
Mnom = Pnom/WmNom;
|
||||
% ... эл. скорость, рад/с
|
||||
WeNom = WmNom*Pp;
|
||||
% ... эл. скорость, Гц
|
||||
FeNom = WeNom/PI2;
|
||||
% ... потокосцепление статора, Вб
|
||||
PsiNom = Unom*SQRT2/(WeNom*SQRT3);
|
||||
% ... напряжение на входе инвертора, B
|
||||
UdcNom = Unom*SQRT2;
|
||||
% ... ток, А (ampl)
|
||||
Inom = Snom/(Unom*SQRT3)*SQRT2*0.5;%0.5 - т.к. обмоток две
|
||||
|
||||
% схема замещения ГЭД
|
||||
if GED == "22220"
|
||||
GED
|
||||
Rs = 11.8e-3;%Ом
|
||||
Xls = 72.7e-3;%72.7e-3;%Ом
|
||||
Rr = 11.1e-3*2.0;%*0.8;%Ом
|
||||
Xlr = 85.5e-3;%Ом
|
||||
Xm = 2.9322;%2.87;%Ом
|
||||
Fe = 18;%Гц
|
||||
Lls = Xls/(Fe*PI2);%Гн
|
||||
Llr = Xlr/(Fe*PI2);%Гн
|
||||
Lm = Xm/(Fe*PI2);%Гн
|
||||
elseif GED == "23550"
|
||||
GED
|
||||
Rs = 0.0282;%Ом
|
||||
Xls = 0.4016;%Ом
|
||||
Rr = 0.139;%Ом
|
||||
Xlr = 0.2006;%Ом
|
||||
Xm = 5.2796;%Ом
|
||||
Fe = 18.2;%Гц
|
||||
Lls = Xls/(Fe*PI2);%Гн
|
||||
Llr = Xlr/(Fe*PI2);%Гн
|
||||
Lm = Xm/(Fe*PI2);%Гн
|
||||
end
|
||||
|
||||
% ёмкость на входе INU, Ф
|
||||
Cdc = 50e-3;
|
||||
% снаберы в INU
|
||||
Csn = Pnom/(1000*WeNom*Unom^2)/10;%Ф (0.5 - т.к. преобразователей два)
|
||||
Rsn = 2*Ts/Csn*10;%Ом
|
||||
|
||||
% постоянная времени фильтра для тока ГЭД, c
|
||||
Tiac = 30e-6;
|
59
McuLib/m/installTemplates.m
Normal file
59
McuLib/m/installTemplates.m
Normal file
@ -0,0 +1,59 @@
|
||||
function installTemplates(forceCopy)
|
||||
% installTemplates Копирует содержимое папки templates (включая вложенные папки) из уровня выше McuLib.slx в текущую папку
|
||||
%
|
||||
% installTemplates(forceCopy)
|
||||
%
|
||||
% forceCopy — логический параметр (true/false)
|
||||
% Если true, файлы будут перезаписаны.
|
||||
% Если false или не указан, копирование происходит без перезаписи существующих файлов.
|
||||
|
||||
if nargin < 1
|
||||
forceCopy = false;
|
||||
end
|
||||
|
||||
try
|
||||
libDir = fileparts(which('McuLib.slx')); % папка с McuLib.slx
|
||||
parentDir = fileparts(libDir); % уровень выше
|
||||
templatesDir = fullfile(parentDir, 'templates'); % папка templates
|
||||
|
||||
targetDir = pwd; % текущая папка
|
||||
mcuMask.disp(1, '');
|
||||
if ~exist(templatesDir, 'dir')
|
||||
mcuMask.disp(1, 'Папка шаблонов %s не найдена.', templatesDir);
|
||||
return;
|
||||
end
|
||||
|
||||
% Список всех файлов и папок внутри templates (рекурсивно)
|
||||
entries = dir(fullfile(templatesDir, '**', '*'));
|
||||
|
||||
for k = 1:length(entries)
|
||||
if ~entries(k).isdir
|
||||
% Относительный путь внутри templates
|
||||
relPath = erase(entries(k).folder, [templatesDir filesep]);
|
||||
if startsWith(relPath, filesep)
|
||||
relPath = relPath(2:end); % убрать начальный слеш
|
||||
end
|
||||
|
||||
srcFile = fullfile(entries(k).folder, entries(k).name);
|
||||
destFolder = fullfile(targetDir, relPath);
|
||||
destFile = fullfile(destFolder, entries(k).name);
|
||||
|
||||
if ~exist(destFolder, 'dir')
|
||||
mkdir(destFolder);
|
||||
end
|
||||
|
||||
if forceCopy || ~exist(destFile, 'file')
|
||||
copyfile(srcFile, destFile);
|
||||
mcuMask.disp(0, 'Скопирован файл: %s\n', fullfile(relPath, entries(k).name));
|
||||
else
|
||||
% mcuMask.disp(0, 'Файл уже существует и не перезаписывается: %s\n', fullfile(relPath, entries(k).name));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mcuMask.disp(0, ['Шаблоны успешно скопированы в ', targetDir]);
|
||||
|
||||
catch ME
|
||||
mcuMask.disp(0, 'Ошибка при копировании шаблонов: %s', ME.message);
|
||||
end
|
||||
end
|
274
McuLib/m/mainConfig.m
Normal file
274
McuLib/m/mainConfig.m
Normal file
@ -0,0 +1,274 @@
|
||||
classdef mainConfig
|
||||
|
||||
methods(Static)
|
||||
|
||||
function config = export()
|
||||
blockPath = gcb;
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
wrapParamToExport = {'wrapperPath', 'enableDebug', 'mcuClk', ...
|
||||
'threadCycles', 'enableThreading', 'enableDeinit'};
|
||||
portParamToExport = {'inNumb', ...
|
||||
'in_port_1_name', 'in_port_1_width', ...
|
||||
'in_port_2_name', 'in_port_2_width', ...
|
||||
'in_port_3_name', 'in_port_3_width', ...
|
||||
'in_port_4_name', 'in_port_4_width', ...
|
||||
'in_port_5_name', 'in_port_5_width', ...
|
||||
'outNumb', ...
|
||||
'out_port_1_name', 'out_port_1_width', ...
|
||||
'out_port_2_name', 'out_port_2_width', ...
|
||||
'out_port_3_name', 'out_port_3_width', ...
|
||||
'out_port_4_name', 'out_port_4_width', ...
|
||||
'out_port_5_name', 'out_port_5_width',};
|
||||
appParamToExport = {'appWrapperPath', 'srcTable', 'incTable', 'userDefs'};
|
||||
|
||||
config = struct();
|
||||
for i = 1:numel(wrapParamToExport)
|
||||
paramName = wrapParamToExport{i};
|
||||
def = mainConfig.exportParamToConfig(mask, paramName);
|
||||
config.(paramName) = def;
|
||||
end
|
||||
|
||||
for i = 1:numel(portParamToExport)
|
||||
paramName = portParamToExport{i};
|
||||
def = mainConfig.exportParamToConfig(mask, paramName);
|
||||
config.(paramName) = def;
|
||||
end
|
||||
|
||||
for i = 1:numel(appParamToExport)
|
||||
paramName = appParamToExport{i};
|
||||
def = mainConfig.exportParamToConfig(mask, paramName);
|
||||
config.(paramName) = def;
|
||||
end
|
||||
|
||||
|
||||
|
||||
jsonStr = jsonencode(config, 'PrettyPrint', true); % 'PrettyPrint' для форматирования
|
||||
|
||||
|
||||
% Диалог сохранения файла
|
||||
[file, path] = uiputfile('*.json', 'Сохранить конфигурацию как', 'WrapperConfig.json');
|
||||
if isequal(file, 0) || isequal(path, 0)
|
||||
disp('Сохранение отменено пользователем.');
|
||||
return;
|
||||
end
|
||||
|
||||
filepath = fullfile(path, file);
|
||||
|
||||
|
||||
|
||||
% Сохраняем в файл
|
||||
fid = fopen(filepath, 'w');
|
||||
if fid == -1
|
||||
mcuMask.disp(0, 'Не удалось открыть файл для записи: %s', filename);
|
||||
end
|
||||
|
||||
fwrite(fid, jsonStr, 'char');
|
||||
fclose(fid);
|
||||
end
|
||||
|
||||
|
||||
function import()
|
||||
% Получаем путь к текущему блоку
|
||||
blockPath = gcb;
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
|
||||
% Выбор JSON-файла через диалоговое окно
|
||||
[file, path] = uigetfile('*.json', 'Выберите файл конфигурации');
|
||||
if isequal(file, 0)
|
||||
mcuMask.disp(0, 'Импорт отменён пользователем.');
|
||||
return;
|
||||
end
|
||||
|
||||
fullpath = fullfile(path, file);
|
||||
|
||||
% Чтение и декодирование JSON
|
||||
try
|
||||
jsonStr = fileread(fullpath);
|
||||
config = jsondecode(jsonStr);
|
||||
catch err
|
||||
mcuMask.disp(0, 'Ошибка при чтении или разборе JSON: %s', err.message);
|
||||
end
|
||||
|
||||
% Применение параметров из конфигурации
|
||||
paramNames = fieldnames(config);
|
||||
for i = 1:numel(paramNames)
|
||||
paramName = paramNames{i};
|
||||
def = config.(paramName);
|
||||
|
||||
try
|
||||
mainConfig.applyDefToMask(mask, paramName, def);
|
||||
catch err
|
||||
mcuMask.disp(0, 'Ошибка при применении параметра "%s": %s', paramName, err.message);
|
||||
end
|
||||
end
|
||||
|
||||
mcuMask.disp(0, 'Конфигурация успешно импортирована.');
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
methods(Static, Access=private)
|
||||
|
||||
|
||||
function def = exportParamToConfig(mask, paramName)
|
||||
% mask — объект Simulink.Mask.get(blockPath)
|
||||
% paramName — имя параметра (как в mask.Parameters.Name)
|
||||
|
||||
param = mask.getParameter(paramName);
|
||||
if isempty(param)
|
||||
mcuMask.disp(0, 'Параметр "%s" не найден в маске.', paramName);
|
||||
def = [];
|
||||
return;
|
||||
end
|
||||
|
||||
def = struct();
|
||||
|
||||
% Prompt
|
||||
def.Prompt = param.Prompt;
|
||||
|
||||
% Тип параметра
|
||||
def.Type = param.Type;
|
||||
|
||||
% Значение по умолчанию
|
||||
val = param.Value;
|
||||
switch lower(param.Type)
|
||||
case 'checkbox'
|
||||
def.Default = strcmp(val, 'on');
|
||||
case {'edit', 'spinbox'}
|
||||
num = str2double(val);
|
||||
if ~isnan(num)
|
||||
def.Default = num;
|
||||
else
|
||||
def.Default = val;
|
||||
end
|
||||
case 'customtable'
|
||||
def.Default = customtable.parse(param.Name); % или можно попытаться распарсить значение позже
|
||||
case 'text'
|
||||
def.Default = ''; % или можно попытаться распарсить значение позже
|
||||
otherwise
|
||||
def.Default = val;
|
||||
end
|
||||
|
||||
% Alias, если есть
|
||||
if ~isempty(param.Alias)
|
||||
def.Def = param.Alias;
|
||||
end
|
||||
|
||||
% % Row (new/current)
|
||||
% try
|
||||
% rowSetting = param.DialogControl.Row;
|
||||
% def.NewRow = strcmp(rowSetting, 'new');
|
||||
% catch
|
||||
% def.NewRow = false;
|
||||
% end
|
||||
end
|
||||
|
||||
|
||||
function applyDefToMask(mask, paramName, def)
|
||||
% mask — объект Simulink.Mask.get(blockPath)
|
||||
% paramName — имя параметра маски
|
||||
% def — структура, полученная из extractDefFromMask
|
||||
|
||||
% Получаем параметр
|
||||
param = mask.getParameter(paramName);
|
||||
if isempty(param)
|
||||
mcuMask.disp(0, 'Параметр "%s" не найден в маске.', paramName);
|
||||
return;
|
||||
end
|
||||
|
||||
if isempty(def)
|
||||
mcuMask.disp(0, 'Параметр "%s" не найден в конфиге.', paramName);
|
||||
return;
|
||||
end
|
||||
|
||||
switch lower(def.Type)
|
||||
case 'checkbox'
|
||||
% Логическое значение true/false
|
||||
if islogical(def.Default)
|
||||
param.Value = mainConfig.ternary(def.Default, 'on', 'off');
|
||||
else
|
||||
mcuMask.disp(0, 'Ожидалось логическое значение для checkbox "%s".', paramName);
|
||||
end
|
||||
|
||||
case {'edit', 'spinbox'}
|
||||
% Строка или число
|
||||
if isnumeric(def.Default)
|
||||
param.Value = num2str(def.Default);
|
||||
elseif ischar(def.Default) || isstring(def.Default)
|
||||
param.Value = char(def.Default);
|
||||
else
|
||||
mcuMask.disp(0, 'Некорректный формат значения для edit "%s".', paramName);
|
||||
end
|
||||
|
||||
case 'customtable'
|
||||
% Массив строк
|
||||
if iscell(def.Default)
|
||||
customtable.collect(paramName, def.Default);
|
||||
else
|
||||
mcuMask.disp(0, 'customtable "%s" требует cell-массив строк.', paramName);
|
||||
end
|
||||
|
||||
case 'text'
|
||||
% Просто текстовая строка
|
||||
if ischar(def.Default) || isstring(def.Default)
|
||||
param.Value = char(def.Default);
|
||||
else
|
||||
mcuMask.disp(0, 'text-параметр "%s" должен быть строкой.', paramName);
|
||||
end
|
||||
|
||||
case 'popup'
|
||||
% popup — установить значение, если оно есть в списке
|
||||
if ischar(def.Default) && isfield(def, 'Def') && any(strcmp(def.Default, def.Def))
|
||||
param.Value = def.Default;
|
||||
else
|
||||
mcuMask.disp(0, 'popup-параметр "%s" имеет неверное значение или список.', paramName);
|
||||
end
|
||||
|
||||
otherwise
|
||||
% По умолчанию просто устанавливаем строковое значение
|
||||
if ischar(def.Default) || isstring(def.Default)
|
||||
param.Value = char(def.Default);
|
||||
elseif isnumeric(def.Default)
|
||||
param.Value = num2str(def.Default);
|
||||
else
|
||||
mcuMask.disp(0, 'Неизвестный формат значения параметра "%s".', paramName);
|
||||
end
|
||||
end
|
||||
|
||||
% Применение Prompt, если нужно
|
||||
if isfield(def, 'Prompt')
|
||||
param.Prompt = def.Prompt;
|
||||
end
|
||||
|
||||
% Применение Alias, если есть
|
||||
if isfield(def, 'Def') && ischar(def.Def)
|
||||
param.Alias = def.Def;
|
||||
end
|
||||
|
||||
% % Установка Row, если поддерживается
|
||||
% if isfield(def, 'NewRow')
|
||||
% try
|
||||
% if def.NewRow
|
||||
% param.DialogControl.Row = 'new';
|
||||
% else
|
||||
% param.DialogControl.Row = 'current';
|
||||
% end
|
||||
% catch
|
||||
% % Некоторым типам параметров Row может быть неприменим
|
||||
% end
|
||||
% end
|
||||
end
|
||||
|
||||
|
||||
function result = ternary(cond, a, b)
|
||||
if cond
|
||||
result = a;
|
||||
else
|
||||
result = b;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
72
McuLib/m/mainWrap.m
Normal file
72
McuLib/m/mainWrap.m
Normal file
@ -0,0 +1,72 @@
|
||||
classdef mainWrap
|
||||
|
||||
methods(Static)
|
||||
function enableThreading()
|
||||
block = gcb;
|
||||
maskNames = get_param(block, 'MaskNames');
|
||||
maskValues = get_param(block, 'MaskValues');
|
||||
maskEnables = get_param(block, 'MaskEnables');
|
||||
idxEnable = find(strcmp(maskNames, 'enableThreading'));
|
||||
idxEdit = find(strcmp(maskNames, 'threadCycles'));
|
||||
if isempty(idxEnable) || isempty(idxEdit)
|
||||
error('Параметры enableThreading или threadCycles не найдены в маске');
|
||||
end
|
||||
val = maskValues{idxEnable};
|
||||
if strcmp(val, 'on')
|
||||
maskEnables{idxEdit} = 'on';
|
||||
else
|
||||
maskEnables{idxEdit} = 'off';
|
||||
end
|
||||
set_param(block, 'MaskEnables', maskEnables);
|
||||
end
|
||||
|
||||
function enableDeinit()
|
||||
block = gcb;
|
||||
maskNames = get_param(block, 'MaskNames');
|
||||
maskValues = get_param(block, 'MaskValues');
|
||||
maskEnables = get_param(block, 'MaskEnables');
|
||||
idxEnable = find(strcmp(maskNames, 'enableThreading'));
|
||||
idxEdit = find(strcmp(maskNames, 'threadCycles'));
|
||||
if isempty(idxEnable) || isempty(idxEdit)
|
||||
error('Параметры enableThreading или threadCycles не найдены в маске');
|
||||
end
|
||||
val = maskValues{idxEnable};
|
||||
if strcmp(val, 'on')
|
||||
maskEnables{idxEdit} = 'on';
|
||||
else
|
||||
maskEnables{idxEdit} = 'off';
|
||||
end
|
||||
set_param(block, 'MaskEnables', maskEnables);
|
||||
end
|
||||
|
||||
function extConsol()
|
||||
block = gcb;
|
||||
mask = Simulink.Mask.get(block);
|
||||
fullOut = mask.getParameter('fullOutput');
|
||||
extCons = mask.getParameter('extConsol');
|
||||
if isempty(extCons) || isempty(fullOut)
|
||||
error('Параметры fullOutput или extConsol не найдены в маске');
|
||||
end
|
||||
|
||||
if(strcmp(extCons.Enabled, 'on'))
|
||||
if strcmp(extCons.Value, 'on')
|
||||
fullOut.Enabled = 'off';
|
||||
fullOut.Value = 'on';
|
||||
else
|
||||
fullOut.Enabled = 'on';
|
||||
end
|
||||
else
|
||||
fullOut.Enabled = 'on';
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
%% SPECIFIC TOOLS
|
||||
methods(Static, Access = private)
|
||||
|
||||
end
|
||||
|
||||
end
|
@ -1,5 +1,5 @@
|
||||
classdef mcuMask
|
||||
|
||||
%% CALLBACKS
|
||||
methods(Static)
|
||||
% Following properties of 'maskInitContext' are avalaible to use:
|
||||
% - BlockHandle
|
||||
@ -10,6 +10,8 @@ classdef mcuMask
|
||||
blk = gcbh;
|
||||
% Получаем объект маски текущего блока
|
||||
mask = Simulink.Mask.get(gcb);
|
||||
set_param(blk,"MaskSelfModifiable","on")
|
||||
set_param(blk, 'LinkStatus', 'none');
|
||||
% mcuMask.disp(1,'');
|
||||
try
|
||||
% Проверка наличия findjobj
|
||||
@ -17,8 +19,6 @@ classdef mcuMask
|
||||
catch
|
||||
findjobjAvailable = false;
|
||||
end
|
||||
% Получаем объект маски текущего блока
|
||||
mask = Simulink.Mask.get(gcb);
|
||||
% Имя checkbox-параметра (укажите точное имя из маски)
|
||||
checkboxParamName = 'extConsol'; % пример
|
||||
findjobjLinkName = 'findjobj_link'; % пример
|
||||
@ -46,7 +46,7 @@ classdef mcuMask
|
||||
table_names = {'srcTable', 'incTable'};
|
||||
for k = 1:numel(table_names)
|
||||
table_name = table_names{k};
|
||||
% customtable.format(table_name);
|
||||
customtable.format(table_name);
|
||||
end
|
||||
% запись описания блока
|
||||
textDesc = ['Блок для настройки параметров симуляции микроконтроллера. ' newline ...
|
||||
@ -58,152 +58,37 @@ classdef mcuMask
|
||||
end
|
||||
|
||||
%% WRAPPER PARAMS
|
||||
function wrapperPath_add(callbackContext)
|
||||
mcuPath.addPath('wrapperPath');
|
||||
end
|
||||
|
||||
function enableThreading(callbackContext)
|
||||
block = gcb;
|
||||
maskNames = get_param(block, 'MaskNames');
|
||||
maskValues = get_param(block, 'MaskValues');
|
||||
maskEnables = get_param(block, 'MaskEnables');
|
||||
idxEnable = find(strcmp(maskNames, 'enableThreading'));
|
||||
idxEdit = find(strcmp(maskNames, 'threadCycles'));
|
||||
if isempty(idxEnable) || isempty(idxEdit)
|
||||
error('Параметры enableThreading или threadCycles не найдены в маске');
|
||||
end
|
||||
val = maskValues{idxEnable};
|
||||
if strcmp(val, 'on')
|
||||
maskEnables{idxEdit} = 'on';
|
||||
else
|
||||
maskEnables{idxEdit} = 'off';
|
||||
end
|
||||
set_param(block, 'MaskEnables', maskEnables);
|
||||
mainWrap.enableThreading();
|
||||
end
|
||||
|
||||
function enableDeinit(callbackContext)
|
||||
block = gcb;
|
||||
maskNames = get_param(block, 'MaskNames');
|
||||
maskValues = get_param(block, 'MaskValues');
|
||||
maskEnables = get_param(block, 'MaskEnables');
|
||||
idxEnable = find(strcmp(maskNames, 'enableThreading'));
|
||||
idxEdit = find(strcmp(maskNames, 'threadCycles'));
|
||||
if isempty(idxEnable) || isempty(idxEdit)
|
||||
error('Параметры enableThreading или threadCycles не найдены в маске');
|
||||
end
|
||||
val = maskValues{idxEnable};
|
||||
if strcmp(val, 'on')
|
||||
maskEnables{idxEdit} = 'on';
|
||||
else
|
||||
maskEnables{idxEdit} = 'off';
|
||||
end
|
||||
set_param(block, 'MaskEnables', maskEnables);
|
||||
mainWrap.enableDeinit();
|
||||
end
|
||||
|
||||
function extConsol(callbackContext)
|
||||
block = gcb;
|
||||
mask = Simulink.Mask.get(block);
|
||||
fullOut = mask.getParameter('fullOutput');
|
||||
extCons = mask.getParameter('extConsol');
|
||||
if isempty(extCons) || isempty(fullOut)
|
||||
error('Параметры fullOutput или extConsol не найдены в маске');
|
||||
end
|
||||
|
||||
if(strcmp(extCons.Enabled, 'on'))
|
||||
if strcmp(extCons.Value, 'on')
|
||||
fullOut.Enabled = 'off';
|
||||
fullOut.Value = 'on';
|
||||
else
|
||||
fullOut.Enabled = 'on';
|
||||
end
|
||||
else
|
||||
fullOut.Enabled = 'on';
|
||||
end
|
||||
|
||||
mainWrap.extConsol();
|
||||
end
|
||||
|
||||
function wrapperPath_add(callbackContext)
|
||||
block = gcb;
|
||||
mask = Simulink.Mask.get(block);
|
||||
% Открываем окно выбора папки
|
||||
folderPath = uigetdir('', 'Выберите папку');
|
||||
% Проверка на отмену
|
||||
if isequal(folderPath, 0)
|
||||
return;
|
||||
end
|
||||
% Установка значения параметра маски
|
||||
rel = mcuMask.absoluteToRelativePath(folderPath);
|
||||
param = mask.getParameter('wrapperPath');
|
||||
param.Value = rel;
|
||||
|
||||
end
|
||||
%% USER WRAPPER CODE
|
||||
|
||||
function wrapperFunc(callbackContext)
|
||||
block = gcb;
|
||||
% Получаем имя функции и путь к файлам
|
||||
[filename, section, tool, example]= mcuMask.getWrapperUserFile(block);
|
||||
mcuMask.tool(tool, example);
|
||||
|
||||
% Загружаем содержимое файла
|
||||
set_param(block, 'wrapperCode', '');
|
||||
code = fileread(filename);
|
||||
code = regexprep(code, '\r\n?', '\n'); % нормализуем окончания строк
|
||||
|
||||
includesText = editCode.extractSection(code, section);
|
||||
set_param(block, 'wrapperCode', includesText);
|
||||
% % Поиск тела обычной функции
|
||||
% expr = sprintf('void %s()', sel);
|
||||
% funcBody = editCode.extractSection(code, expr);
|
||||
% set_param(block, 'wrapperCode', funcBody);
|
||||
end
|
||||
|
||||
function saveWrapperCode(callbackContext)
|
||||
block = gcb;
|
||||
|
||||
% Получаем имя функции и путь к файлам
|
||||
[filename, section] = mcuMask.getWrapperUserFile(block);
|
||||
if ~isfile(filename)
|
||||
errordlg(['Файл не найден: ', filename]);
|
||||
return;
|
||||
end
|
||||
|
||||
sel = get_param(block, 'wrapperFunc');
|
||||
basePath = get_param(block, 'wrapperPath');
|
||||
if isempty(basePath)
|
||||
errordlg('Не указан путь к файлам обёртки (wrapperPath).');
|
||||
return;
|
||||
end
|
||||
newBody = get_param(block, 'wrapperCode');
|
||||
code = fileread(filename);
|
||||
code = regexprep(code, '\r\n?', '\n');
|
||||
code = editCode.insertSection(code, section, newBody);
|
||||
% else
|
||||
% % Обновляем тело функции
|
||||
% expr = sprintf('void %s()', sel);
|
||||
% code = editCode.insertSection(code, expr, newBody);
|
||||
% end
|
||||
fid = fopen(filename, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
errordlg('Не удалось открыть файл для записи');
|
||||
return;
|
||||
end
|
||||
fwrite(fid, code);
|
||||
fclose(fid);
|
||||
mcuMask.disp(1, ['Обновлено: ' sel]);
|
||||
function appWrapperPath_add(callbackContext)
|
||||
mcuPath.addPath('appWrapperPath');
|
||||
end
|
||||
|
||||
function openWrapperCode(callbackContext)
|
||||
block = gcb;
|
||||
function appWrapperFunc(callbackContext)
|
||||
appWrap.appWrapperFunc();
|
||||
end
|
||||
|
||||
function saveAppWrapperCode(callbackContext)
|
||||
appWrap.saveAppWrapperCode();
|
||||
end
|
||||
|
||||
% Получаем имя функции и путь к файлам
|
||||
filename = mcuMask.getAbsolutePath(mcuMask.getWrapperUserFile(block));
|
||||
if exist(filename, 'file') == 2
|
||||
% Формируем команду без кавычек
|
||||
cmd = sprintf('rundll32.exe shell32.dll,OpenAs_RunDLL %s', filename);
|
||||
status = system(cmd);
|
||||
if status ~= 0
|
||||
errordlg('Не удалось открыть окно выбора приложения.');
|
||||
end
|
||||
else
|
||||
errordlg('Файл не найден');
|
||||
end
|
||||
function openAppWrapperCode(callbackContext)
|
||||
appWrap.openAppWrapperCode();
|
||||
end
|
||||
|
||||
%% USER CODE
|
||||
@ -216,109 +101,44 @@ classdef mcuMask
|
||||
end
|
||||
|
||||
function btnAddSrc(callbackContext)
|
||||
blockHandle = gcb;
|
||||
% Открываем проводник для выбора файлов
|
||||
[files, pathstr] = uigetfile({ ...
|
||||
'*.c;*.cpp', 'Исходные файлы (*.c, *.cpp)'; ...
|
||||
'*.obj;*.lib', 'Библиотеки (*.obj, *.lib)'; ...
|
||||
'*.*', 'Все файлы (*.*)'}, ...
|
||||
'Выберите файлы', ...
|
||||
'MultiSelect', 'on');
|
||||
|
||||
if isequal(files, 0)
|
||||
return; % Отмена выбора
|
||||
end
|
||||
if ischar(files)
|
||||
files = {files}; % Один файл — в cell
|
||||
end
|
||||
% Парсим строку в cell-массив
|
||||
oldTable = customtable.parse('srcTable');
|
||||
|
||||
% Добавляем новые пути, проверяя уникальность
|
||||
for i = 1:numel(files)
|
||||
fullpath = fullfile(pathstr, files{i});
|
||||
rel = mcuMask.absoluteToRelativePath(fullpath);
|
||||
if ~any(strcmp(rel, oldTable))
|
||||
oldTable{end+1, 1} = rel;
|
||||
end
|
||||
end
|
||||
|
||||
% Парсим строку в cell-массив
|
||||
customtable.collect('srcTable', oldTable);
|
||||
|
||||
mcuPath.addSourceFileTable('srcTable', 'Выберите исходные файлы');
|
||||
end
|
||||
|
||||
function btnAddInc(callbackContext)
|
||||
blockHandle = gcb;
|
||||
% Открываем проводник для выбора папок
|
||||
pathstr = uigetdir(pwd, 'Выберите папку с заголовочными файлами');
|
||||
if isequal(pathstr, 0)
|
||||
return; % Отмена выбора
|
||||
end
|
||||
% Парсим таблицу
|
||||
oldTable = customtable.parse('incTable');
|
||||
|
||||
rel = mcuMask.absoluteToRelativePath(pathstr);
|
||||
|
||||
% Проверяем наличие пути
|
||||
if ~any(strcmp(rel, oldTable))
|
||||
oldTable{end+1, 1} = rel;
|
||||
end
|
||||
|
||||
% Собираем таблицу
|
||||
customtable.collect('incTable', oldTable);
|
||||
mcuPath.addPathTable('incTable', 'Выберите папку с заголовочными файлами');
|
||||
end
|
||||
|
||||
%% PERIPH CONFIG
|
||||
|
||||
function periphPath_add(callbackContext)
|
||||
block = gcbh;
|
||||
mask = Simulink.Mask.get(block);
|
||||
[file, path] = uigetfile({'*.*','Все файлы (*.*)'}, 'Выберите файл');
|
||||
if isequal(file, 0) || isequal(path, 0)
|
||||
% Отмена выбора — ничего не делаем
|
||||
return;
|
||||
end
|
||||
fullFilePath = fullfile(path, file);
|
||||
rel = mcuMask.absoluteToRelativePath(fullFilePath);
|
||||
param = mask.getParameter('periphPath');
|
||||
param.Value = rel;
|
||||
mcuPath.addAnyFile('periphPath');
|
||||
end
|
||||
|
||||
function compile(callbackContext)
|
||||
addpath('MCU_Wrapper');
|
||||
mexing(1);
|
||||
end
|
||||
|
||||
|
||||
function updateModel(callbackContext)
|
||||
addpath('MCU_Wrapper');
|
||||
res = mexing(1);
|
||||
if res ~= 0
|
||||
return;
|
||||
end
|
||||
|
||||
function periphUpdate(callbackContext)
|
||||
modelName = bdroot(gcb); % получить имя верхнего уровня модели
|
||||
blockName = gcb;
|
||||
mgr = asynchManage(modelName, blockName); % создать объект класса
|
||||
mgr.saveAndUpdateModel(); % запустить сохранение и обновление
|
||||
mgr.updateGUIfromConfig(); % запустить сохранение и обновление
|
||||
end
|
||||
|
||||
|
||||
function findjobj_link(callbackContext)
|
||||
web('https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects');
|
||||
%% COMPILE
|
||||
function compile(callbackContext)
|
||||
compiler.compile();
|
||||
end
|
||||
|
||||
function set_name()
|
||||
function setSFuncName(callbackContext)
|
||||
block = gcb;
|
||||
% Получаем параметр имени S-Function из маски блока
|
||||
newName = get_param(block, 'sfuncName');
|
||||
newName = mcuMask.get_name();
|
||||
|
||||
% Путь к файлу, в котором надо заменить строку
|
||||
cFilePath = fullfile(pwd, './MCU_Wrapper/MCU.c'); % <-- укажи правильный путь
|
||||
cFilePath = fullfile(pwd, mcuPath.get('wrapperPath'), 'MCU.c'); % <-- укажи правильный путь
|
||||
|
||||
% Считаем файл в память
|
||||
fileText = fileread(cFilePath);
|
||||
try
|
||||
fileText = fileread(cFilePath);
|
||||
catch
|
||||
return;
|
||||
end
|
||||
|
||||
% Регулярное выражение для поиска строки с define
|
||||
% Заменим строку вида: #define S_FUNCTION_NAME old_name
|
||||
@ -336,92 +156,32 @@ classdef mcuMask
|
||||
error('Не удалось открыть файл для записи.');
|
||||
end
|
||||
fwrite(fid, updatedText);
|
||||
fclose(fid);
|
||||
|
||||
fclose(fid);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
%% SPECIFIC TOOLS
|
||||
methods(Static, Access = private)
|
||||
|
||||
function [filename, section, tool, example] = getWrapperUserFile(block)
|
||||
sel = get_param(block, 'wrapperFunc');
|
||||
basePath = get_param(block, 'wrapperPath');
|
||||
if isempty(basePath)
|
||||
errordlg('Не указан путь к файлам обёртки (wrapperPath).');
|
||||
return;
|
||||
end
|
||||
% Формируем путь к файлу в зависимости от типа запроса
|
||||
if strcmp(sel, 'Includes')
|
||||
filename = fullfile(basePath, 'app_includes.h');
|
||||
section = '// INCLUDES';
|
||||
tool = 'Инклюды для доступа к коду МК в коде оболочке';
|
||||
example = '#include "main.h"';
|
||||
elseif strcmp(sel, 'Dummy')
|
||||
filename = fullfile(basePath, 'app_wrapper.c');
|
||||
section = '// DUMMY';
|
||||
tool = 'Заглушки для различных функций и переменных';
|
||||
example = ['CAN_HandleTypeDef hcan = {0};' newline...
|
||||
'void hardware_func(handle *huart) {}' newline...
|
||||
'int wait_for_hardware_flag(int *flag) {' newline...
|
||||
' return 1;' newline...
|
||||
'}' newline...
|
||||
''];
|
||||
elseif strcmp(sel, 'App Init')
|
||||
filename = fullfile(basePath, 'app_init.c');
|
||||
section = '// USER APP INIT';
|
||||
tool = ['Код для инициализации приложения МК.' newline newline...
|
||||
'Вызов функций инициализации, если не используется отдельный поток для main().'];
|
||||
example = 'init_func();';
|
||||
elseif strcmp(sel, 'App Step')
|
||||
filename = fullfile(basePath, 'app_wrapper.c');
|
||||
section = '// USER APP STEP';
|
||||
tool = ['Код приложения МК для вызова в шаге симуляции.' newline newline ...
|
||||
'Вызов функций программы МК, если не используется отдельный поток для main().'];
|
||||
example = 'step_func();';
|
||||
elseif strcmp(sel, 'App Inputs')
|
||||
filename = fullfile(basePath, 'app_io.c');
|
||||
section = '// USER APP INPUT';
|
||||
tool = ['Работа с буффером для портов S-Function' newline newline ...
|
||||
'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ...
|
||||
'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ...
|
||||
'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт'];
|
||||
example = ['// чтение 1-го элемента 0-го входного массива' newline...
|
||||
'app_variable_2 = ReadInputArray(0, 1);' newline newline...
|
||||
'// запись в буфер выходов' newline ...
|
||||
'app_variable_2 = Buffer[10];'];
|
||||
elseif strcmp(sel, 'App Outputs')
|
||||
filename = fullfile(basePath, 'app_io.c');
|
||||
section = '// USER APP OUTPUT';
|
||||
tool = ['Работа с буффером для портов S-Function' newline newline ...
|
||||
'Буфер в начале хранит входные порты S-Function, далее идут выходные порты:' newline ...
|
||||
'Buffer[0:15] - входной порт, Buffer[16:31] - входной 1 порт, ' newline ...
|
||||
'Buffer[32:47] - выходной 1 порт, Buffer[48:63] - выходной 2 порт'];
|
||||
example = ['// запись в 1-й элемент 0-го выходного массива' newline...
|
||||
'WriteOutputArray(app_variable, 0, 1);' newline newline ...
|
||||
'// запись в буфер выходов' newline ...
|
||||
'Buffer[XD_OUTPUT_START + 10] = app_variable_2;'];
|
||||
elseif strcmp(sel, 'App Deinit')
|
||||
filename = fullfile(basePath, 'app_init.c');
|
||||
section = '// USER APP DEINIT';
|
||||
tool = ['Код для деинициализации приложения МК.' newline newline ...
|
||||
'Можно деинициализировать приложение МК, для повторного запуска.'];
|
||||
example = 'memset(&htim1, sizeof(htim1), 0;';
|
||||
else
|
||||
tool = '';
|
||||
mcuMask.disp(0, '\nОшибка выбора типа секции кода: неизвестное значение');
|
||||
end
|
||||
|
||||
%% LINK TO EXTERNAL CONSOLE
|
||||
function findjobj_link(callbackContext)
|
||||
web https://www.mathworks.com/matlabcentral/fileexchange/14317-findjobj-find-java-handles-of-matlab-graphic-objects;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
%% GENERAL TOOLS
|
||||
methods(Static, Access = public)
|
||||
|
||||
function saveAndClose(blockPath)
|
||||
function updateModel()
|
||||
addpath(mcuPath.get('wrapperPath'));
|
||||
res = mexing(1);
|
||||
if res ~= 0
|
||||
return;
|
||||
end
|
||||
|
||||
% modelName = bdroot(gcb); % получить имя верхнего уровня модели
|
||||
% blockName = gcb;
|
||||
% mgr = asynchManage(modelName, blockName); % создать объект класса
|
||||
% mgr.saveAndUpdateModel(); % запустить сохранение и обновление
|
||||
end
|
||||
|
||||
function close(blockPath)
|
||||
try
|
||||
% Считываем текущее имя модели
|
||||
modelName = bdroot(blockPath);
|
||||
@ -445,81 +205,13 @@ classdef mcuMask
|
||||
mcuMask.disp(clear_flag, '');
|
||||
end
|
||||
|
||||
|
||||
function absPath = getAbsolutePath(relPath)
|
||||
% relativeToAbsolutePath — преобразует относительный путь в абсолютный.
|
||||
%
|
||||
% Если путь уже абсолютный — возвращается он же, приведённый к канонической форме.
|
||||
% Если путь относительный — преобразуется относительно текущей директории.
|
||||
|
||||
% Проверка: абсолютный ли путь
|
||||
if ispc
|
||||
isAbsolute = ~isempty(regexp(relPath, '^[a-zA-Z]:[\\/]', 'once')) || startsWith(relPath, '\\');
|
||||
else
|
||||
isAbsolute = startsWith(relPath, '/');
|
||||
end
|
||||
|
||||
if isAbsolute
|
||||
% Канонизируем абсолютный путь (убираем ./, ../ и т.п.)
|
||||
absPath = char(java.io.File(relPath).getCanonicalPath());
|
||||
else
|
||||
% Строим абсолютный путь от текущей директории
|
||||
cwd = pwd;
|
||||
combined = fullfile(cwd, relPath);
|
||||
absPath = char(java.io.File(combined).getCanonicalPath());
|
||||
end
|
||||
function name = get_name()
|
||||
block = gcb;
|
||||
% Получаем параметр имени S-Function из маски блока
|
||||
name = get_param(block, 'sfuncName');
|
||||
end
|
||||
|
||||
|
||||
|
||||
function rel = absoluteToRelativePath(pathstr)
|
||||
% absoluteToRelativePath — преобразует абсолютный путь в относительный от текущей директории.
|
||||
%
|
||||
% Если путь находится в текущей директории или вложенной в неё — добавляется префикс './'
|
||||
% Если выше — формируются переходы '..'
|
||||
% Если путь совпадает с текущей директорией — возвращается '.'
|
||||
|
||||
% Получаем текущую рабочую директорию
|
||||
cwd = pwd;
|
||||
|
||||
% Преобразуем пути в канонические абсолютные пути
|
||||
fullpath = char(java.io.File(pathstr).getCanonicalPath());
|
||||
cwd = char(java.io.File(cwd).getCanonicalPath());
|
||||
|
||||
% Разбиваем пути на части
|
||||
targetParts = strsplit(fullpath, filesep);
|
||||
baseParts = strsplit(cwd, filesep);
|
||||
|
||||
% Находим длину общего префикса
|
||||
j = 1;
|
||||
while j <= min(length(targetParts), length(baseParts)) && strcmpi(targetParts{j}, baseParts{j})
|
||||
j = j + 1;
|
||||
end
|
||||
|
||||
% Формируем количество подъемов ".." из cwd
|
||||
numUps = length(baseParts) - (j - 1);
|
||||
ups = repmat({'..'}, 1, numUps);
|
||||
|
||||
% Оставшаяся часть пути после общего префикса
|
||||
rest = targetParts(j:end);
|
||||
|
||||
% Объединяем для получения относительного пути
|
||||
relParts = [ups, rest];
|
||||
rel = fullfile(relParts{:});
|
||||
|
||||
% Если путь пустой — это текущая директория
|
||||
if isempty(rel)
|
||||
rel = '.';
|
||||
end
|
||||
|
||||
% Если путь не содержит ".." и начинается внутри текущей директории — добавим './'
|
||||
if ~isempty(rest) && isempty(ups)
|
||||
rel = fullfile('.', rel);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function checkbox_state = read_checkbox(checkboxName)
|
||||
maskValues = get_param(gcbh, 'MaskValues');
|
||||
paramNames = get_param(gcbh, 'MaskNames');
|
||||
@ -579,15 +271,7 @@ classdef mcuMask
|
||||
end
|
||||
end
|
||||
|
||||
function res = ternary(cond, valTrue, valFalse)
|
||||
if cond
|
||||
res = valTrue;
|
||||
else
|
||||
res = valFalse;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function tool(text, example)
|
||||
% Устанавливает заданный текст в параметр Text Area 'toolText' через объект маски
|
||||
|
||||
@ -616,9 +300,13 @@ classdef mcuMask
|
||||
out = sprintf(varargin{:});
|
||||
end
|
||||
|
||||
out_now = get_param(gcb, 'consoleOutput');
|
||||
out_now = get_param(gcb, 'consoleOutput');
|
||||
if ~strcmp(out, '') && ~strcmp(out_now, '')
|
||||
set_param(gcb, 'consoleOutput', [out_now newline out]);
|
||||
else
|
||||
set_param(gcb, 'consoleOutput', [out_now out]);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function updateModelAsync()
|
||||
|
168
McuLib/m/mcuPath.m
Normal file
168
McuLib/m/mcuPath.m
Normal file
@ -0,0 +1,168 @@
|
||||
classdef mcuPath
|
||||
methods(Static)
|
||||
|
||||
%% GET PATH FROM PARAM
|
||||
function path = get(paramName)
|
||||
blockPath = gcb;
|
||||
path = get_param(blockPath, paramName);
|
||||
end
|
||||
|
||||
%% ADD PATH TO TABLE
|
||||
|
||||
function addSourceFileTable(targetParamName, message)
|
||||
% Открываем проводник для выбора файлов
|
||||
[files, pathstr] = uigetfile({ ...
|
||||
'*.c;*.cpp', 'Исходные файлы (*.c, *.cpp)'; ...
|
||||
'*.obj;*.lib', 'Библиотеки (*.obj, *.lib)'; ...
|
||||
'*.*', 'Все файлы (*.*)'}, ...
|
||||
message, ...
|
||||
'MultiSelect', 'on');
|
||||
|
||||
if isequal(files, 0)
|
||||
return; % Отмена выбора
|
||||
end
|
||||
if ischar(files)
|
||||
files = {files}; % Один файл — в cell
|
||||
end
|
||||
% Парсим строку в cell-массив
|
||||
oldTable = customtable.parse(targetParamName);
|
||||
|
||||
% Добавляем новые пути, проверяя уникальность
|
||||
for i = 1:numel(files)
|
||||
fullpath = fullfile(pathstr, files{i});
|
||||
rel = mcuPath.absoluteToRelativePath(fullpath);
|
||||
if ~any(strcmp(rel, oldTable))
|
||||
oldTable{end+1, 1} = rel;
|
||||
end
|
||||
end
|
||||
|
||||
% Парсим строку в cell-массив
|
||||
customtable.collect(targetParamName, oldTable);
|
||||
|
||||
end
|
||||
|
||||
function addPathTable(targetParamName, message)
|
||||
% Открываем проводник для выбора папок
|
||||
pathstr = uigetdir(pwd, message);
|
||||
if isequal(pathstr, 0)
|
||||
return; % Отмена выбора
|
||||
end
|
||||
% Парсим таблицу
|
||||
oldTable = customtable.parse(targetParamName);
|
||||
|
||||
rel = mcuPath.absoluteToRelativePath(pathstr);
|
||||
|
||||
% Проверяем наличие пути
|
||||
if ~any(strcmp(rel, oldTable))
|
||||
oldTable{end+1, 1} = rel;
|
||||
end
|
||||
|
||||
% Собираем таблицу
|
||||
customtable.collect(targetParamName, oldTable);
|
||||
end
|
||||
|
||||
%% ADD PATH TO EDIT
|
||||
|
||||
function addPath(targetParamName, message)
|
||||
block = gcb;
|
||||
mask = Simulink.Mask.get(block);
|
||||
% Открываем окно выбора папки
|
||||
folderPath = uigetdir('', 'Выберите папку');
|
||||
% Проверка на отмену
|
||||
if isequal(folderPath, 0)
|
||||
return;
|
||||
end
|
||||
% Установка значения параметра маски
|
||||
rel = mcuPath.absoluteToRelativePath(folderPath);
|
||||
param = mask.getParameter(targetParamName);
|
||||
param.Value = rel;
|
||||
end
|
||||
|
||||
function addAnyFile(targetParamName, message)
|
||||
block = gcbh;
|
||||
mask = Simulink.Mask.get(block);
|
||||
[file, path] = uigetfile({'*.*','Все файлы (*.*)'}, 'Выберите файл');
|
||||
if isequal(file, 0) || isequal(path, 0)
|
||||
% Отмена выбора — ничего не делаем
|
||||
return;
|
||||
end
|
||||
fullFilePath = fullfile(path, file);
|
||||
rel = mcuPath.absoluteToRelativePath(fullFilePath);
|
||||
param = mask.getParameter(targetParamName);
|
||||
param.Value = rel;
|
||||
end
|
||||
|
||||
%% GET PATH STRING
|
||||
|
||||
function absPath = getAbsolutePath(relPath)
|
||||
% relativeToAbsolutePath — преобразует относительный путь в абсолютный.
|
||||
%
|
||||
% Если путь уже абсолютный — возвращается он же, приведённый к канонической форме.
|
||||
% Если путь относительный — преобразуется относительно текущей директории.
|
||||
|
||||
% Проверка: абсолютный ли путь
|
||||
if ispc
|
||||
isAbsolute = ~isempty(regexp(relPath, '^[a-zA-Z]:[\\/]', 'once')) || startsWith(relPath, '\\');
|
||||
else
|
||||
isAbsolute = startsWith(relPath, '/');
|
||||
end
|
||||
|
||||
if isAbsolute
|
||||
% Канонизируем абсолютный путь (убираем ./, ../ и т.п.)
|
||||
absPath = char(java.io.File(relPath).getCanonicalPath());
|
||||
else
|
||||
% Строим абсолютный путь от текущей директории
|
||||
cwd = pwd;
|
||||
combined = fullfile(cwd, relPath);
|
||||
absPath = char(java.io.File(combined).getCanonicalPath());
|
||||
end
|
||||
end
|
||||
|
||||
function rel = absoluteToRelativePath(pathstr)
|
||||
% absoluteToRelativePath — преобразует абсолютный путь в относительный от текущей директории.
|
||||
%
|
||||
% Если путь находится в текущей директории или вложенной в неё — добавляется префикс './'
|
||||
% Если выше — формируются переходы '..'
|
||||
% Если путь совпадает с текущей директорией — возвращается '.'
|
||||
|
||||
% Получаем текущую рабочую директорию
|
||||
cwd = pwd;
|
||||
|
||||
% Преобразуем пути в канонические абсолютные пути
|
||||
fullpath = char(java.io.File(pathstr).getCanonicalPath());
|
||||
cwd = char(java.io.File(cwd).getCanonicalPath());
|
||||
|
||||
% Разбиваем пути на части
|
||||
targetParts = strsplit(fullpath, filesep);
|
||||
baseParts = strsplit(cwd, filesep);
|
||||
|
||||
% Находим длину общего префикса
|
||||
j = 1;
|
||||
while j <= min(length(targetParts), length(baseParts)) && strcmpi(targetParts{j}, baseParts{j})
|
||||
j = j + 1;
|
||||
end
|
||||
|
||||
% Формируем количество подъемов ".." из cwd
|
||||
numUps = length(baseParts) - (j - 1);
|
||||
ups = repmat({'..'}, 1, numUps);
|
||||
|
||||
% Оставшаяся часть пути после общего префикса
|
||||
rest = targetParts(j:end);
|
||||
|
||||
% Объединяем для получения относительного пути
|
||||
relParts = [ups, rest];
|
||||
rel = fullfile(relParts{:});
|
||||
|
||||
% Если путь пустой — это текущая директория
|
||||
if isempty(rel)
|
||||
rel = '.';
|
||||
end
|
||||
|
||||
% Если путь не содержит ".." и начинается внутри текущей директории — добавим './'
|
||||
if ~isempty(rest) && isempty(ups)
|
||||
rel = fullfile('.', rel);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -5,8 +5,8 @@ classdef mcuPorts
|
||||
function write()
|
||||
block = gcb;
|
||||
mask = Simulink.Mask.get(block);
|
||||
hPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper_conf.h');
|
||||
cPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c');
|
||||
hPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper_conf.h');
|
||||
cPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c');
|
||||
mcuPorts.defaultUnused();
|
||||
%% CREATE
|
||||
prefixNumb = 'IN';
|
||||
@ -190,10 +190,10 @@ classdef mcuPorts
|
||||
|
||||
for i = 1:n
|
||||
if i == 1
|
||||
lines{end+1} = '#define OFFSET_ARRAY_1 0';
|
||||
lines{end+1} = sprintf('#define OFFSET_%s_ARRAY_1 0', upperPrefix);
|
||||
else
|
||||
lines{end+1} = sprintf('#define OFFSET_ARRAY_%d (OFFSET_ARRAY_%d + %s_PORT_%d_WIDTH)', ...
|
||||
i, i - 1, upper(portPrefixes{i - 1}), i - 1);
|
||||
lines{end+1} = sprintf('#define OFFSET_%s_ARRAY_%d (OFFSET_%s_ARRAY_%d + %s_PORT_%d_WIDTH)', ...
|
||||
upperPrefix, i, upperPrefix, i - 1, upper(portPrefixes{i - 1}), i - 1);
|
||||
end
|
||||
end
|
||||
newAuto = strjoin(lines, newline);
|
||||
@ -251,7 +251,7 @@ classdef mcuPorts
|
||||
if i == n
|
||||
comma = '';
|
||||
end
|
||||
lines{end+1} = sprintf(' OFFSET_ARRAY_%d%s', i, comma);
|
||||
lines{end+1} = sprintf(' OFFSET_%s_ARRAY_%d%s', upperPrefix, i, comma);
|
||||
end
|
||||
lines{end+1} = '};';
|
||||
lines{end+1} = '';
|
||||
|
@ -4,12 +4,11 @@ function res = mexing(compile_mode)
|
||||
Ts = 0.00001;
|
||||
|
||||
if compile_mode == 1
|
||||
delete("*.mexw64")
|
||||
delete("*.mexw64.pdb")
|
||||
delete(".\MCU_Wrapper\Outputs\*.*");
|
||||
delete('*.mexw64')
|
||||
delete('*.mexw64.pdb')
|
||||
delete([mcuPath.get('wrapperPath'), '\Outputs\*.*']);
|
||||
set_param(gcb, 'consoleOutput', '');
|
||||
% Порты S-Function
|
||||
mcuPorts.write();
|
||||
compiler.updateRunBat();
|
||||
% Дефайны
|
||||
definesUserArg = parseDefinesMaskText();
|
||||
definesWrapperConfigArg = buildWrapperDefinesString();
|
||||
@ -30,8 +29,11 @@ function res = mexing(compile_mode)
|
||||
|
||||
[includesArg, codeArg] = make_mex_arguments('incTable', 'srcTable');
|
||||
|
||||
Name = mcuMask.get_name();
|
||||
|
||||
% Вызов батника с двумя параметрами: includes и code
|
||||
cmd = sprintf('.\\MCU_Wrapper\\run_mex.bat "%s" "%s" "%s" "%s" %s %s', includesArg, codeArg, definesUserArg, definesConfigArg, modeArg, echoArg);
|
||||
run_bat_mex_path = fullfile(mcuPath.get('wrapperPath'), 'run_mex.bat');
|
||||
cmd = sprintf('%s %s "%s" "%s" "%s" "%s" %s %s', run_bat_mex_path, Name, includesArg, codeArg, definesUserArg, definesConfigArg, modeArg, echoArg);
|
||||
|
||||
if mcuMask.read_checkbox('extConsol')
|
||||
cmdout = runBatAndShowOutput(cmd);
|
||||
@ -64,13 +66,10 @@ function res = mexing(compile_mode)
|
||||
beep
|
||||
else
|
||||
blockPath = gcb;
|
||||
config = periphConfig.read_config(blockPath);
|
||||
config = periphConfig.update_config(blockPath, config);
|
||||
periphConfig.write_config(config);
|
||||
periphConfig.update(blockPath, config);
|
||||
% Порты S-Function
|
||||
mcuPorts.write();
|
||||
% set_param(gcb, 'consoleOutput', 'Peripheral configuration file loaded. Re-open Block Parameters');
|
||||
config = configJs.read(blockPath);
|
||||
config = configJs.update(blockPath, config);
|
||||
configJs.write(config);
|
||||
periphConfig.updateMask(blockPath, config);
|
||||
end
|
||||
end
|
||||
|
||||
@ -203,6 +202,8 @@ function definesWrapperArg = buildConfigDefinesString()
|
||||
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0);
|
||||
case 'edit'
|
||||
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 1);
|
||||
case 'popup'
|
||||
definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, 0);
|
||||
otherwise
|
||||
% Необрабатываемые типы
|
||||
end
|
||||
@ -231,7 +232,21 @@ function definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, val_
|
||||
end
|
||||
|
||||
% Берём alias из маски
|
||||
alias = param.Alias;
|
||||
val = '';
|
||||
if ~strcmp(param.Type, 'popup')
|
||||
def_name = param.Alias;
|
||||
else
|
||||
if strcmp(param.Alias, '')
|
||||
def_name = param.Value;
|
||||
else
|
||||
def_name = param.Alias;
|
||||
val = param.Value;
|
||||
end
|
||||
end
|
||||
|
||||
if strcmp(def_name, '')
|
||||
return;
|
||||
end
|
||||
|
||||
if val_define ~= 0
|
||||
% Значение параметра
|
||||
@ -241,14 +256,16 @@ function definesWrapperArg = addDefineByParam(definesWrapperArg, paramName, val_
|
||||
val = num2str(val); % Преобразуем результат в строку
|
||||
end
|
||||
% Формируем define с кавычками и значением
|
||||
newDefine = ['-D"' alias '__EQ__' val '"'];
|
||||
else
|
||||
newDefine = ['-D"' def_name '__EQ__' val '"'];
|
||||
elseif ~strcmp(param.Type, 'popup')
|
||||
if mcuMask.read_checkbox(paramName)
|
||||
% Формируем define с кавычками без значения
|
||||
newDefine = ['-D"' alias '"'];
|
||||
newDefine = ['-D"' def_name '"'];
|
||||
else
|
||||
newDefine = '';
|
||||
end
|
||||
else
|
||||
newDefine = ['-D"' def_name '__EQ__' val '"'];
|
||||
end
|
||||
|
||||
|
||||
|
@ -1,171 +1,424 @@
|
||||
classdef periphConfig
|
||||
|
||||
methods(Static)
|
||||
function update(blockPath, config)
|
||||
function updateMask(blockPath, config)
|
||||
% blockPath = [blockPath '/MCU'];
|
||||
|
||||
% Проверяем, была ли маска открыта
|
||||
% wasOpen = isMaskDialogOpen(blockPath);
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
periphPath = get_param(blockPath, 'periphPath');
|
||||
[periphPath, ~, ~] = fileparts(periphPath);
|
||||
|
||||
tableNames = {'incTable', 'srcTable'};
|
||||
columns_backup = customtable.save_all_tables(tableNames);
|
||||
|
||||
containerName = 'configTabAll';
|
||||
periphConfig.clear_all_from_container(mask, containerName);
|
||||
|
||||
% Ищем контейнер, в который будем добавлять вкладки
|
||||
container = mask.getDialogControl(containerName);
|
||||
if isempty(container)
|
||||
error('Контейнер "%s" не найден в маске.', containerName);
|
||||
end
|
||||
|
||||
if ~isempty(config)
|
||||
try
|
||||
rowWidth = str2double(get_param(blockPath, 'rowWidth'));
|
||||
|
||||
if isfield(config, 'Code')
|
||||
res = periphConfig.addCodeConfig(config.Code, periphPath);
|
||||
if res == 0
|
||||
error('Ошибка: неудачное добавление кода периферии. Проверьте корректность файлов и путей в конфигурационном файле')
|
||||
end
|
||||
else
|
||||
error('Ошибка: в конфигурационном файле не задан исходный код для симуляции периферии')
|
||||
containerName = 'configTabAll';
|
||||
periphConfig.clear_all_from_container(mask, containerName);
|
||||
|
||||
% Ищем контейнер, в который будем добавлять вкладки
|
||||
container = mask.getDialogControl(containerName);
|
||||
if isempty(container)
|
||||
error('Контейнер "%s" не найден в маске.', containerName);
|
||||
end
|
||||
|
||||
if ~isempty(config)
|
||||
% Проходим по каждому модулю (ADC, TIM...)
|
||||
periphs = fieldnames(config);
|
||||
for i = 1:numel(periphs)
|
||||
periph = periphs{i};
|
||||
|
||||
% Сохраняем код, если он есть
|
||||
periphConfig.store_single_periph_code(mask, periph, config.(periph));
|
||||
|
||||
% Проверяем наличие Defines
|
||||
if ~isfield(config.(periph), 'Defines')
|
||||
continue;
|
||||
end
|
||||
|
||||
defines = config.(periph).Defines;
|
||||
defNames = fieldnames(defines);
|
||||
|
||||
if isfield(config, 'UserCode')
|
||||
res = periphConfig.addUserCodeConfig(config.UserCode);
|
||||
if res == 0
|
||||
error('Ошибка: неудачное добавление функций для симуляции. Проверьте корректность названий функций в конфигурационном файле')
|
||||
end
|
||||
else
|
||||
error('Ошибка: в конфигурационном файле не заданы функции для симуляции периферии')
|
||||
end
|
||||
|
||||
% Проходим по каждому модулю (ADC, TIM...)
|
||||
periphs = fieldnames(config);
|
||||
for i = 1:numel(periphs)
|
||||
periph = periphs{i};
|
||||
|
||||
% Пропускаем Code и UserCode, они уже обработаны
|
||||
if strcmp(periph, 'Code') || strcmp(periph, 'UserCode')
|
||||
continue;
|
||||
end
|
||||
|
||||
defines = config.(periph).Defines;
|
||||
defNames = fieldnames(defines);
|
||||
|
||||
% Создаём вкладку для модуля
|
||||
tabCtrl = container.addDialogControl('tab', periph);
|
||||
tabCtrl.Prompt = [periph ' Config'];
|
||||
|
||||
for j = 1:numel(defNames)
|
||||
defPrompt = defNames{j};
|
||||
def = defines.(defPrompt);
|
||||
|
||||
% Вызов функции добавления одного параметра
|
||||
periphConfig.addDefineConfig(mask, containerName, periph, defPrompt, def);
|
||||
% Создаём вкладку для модуля
|
||||
tabCtrl = container.addDialogControl('tab', periph);
|
||||
tabCtrl.Prompt = [periph ' Config'];
|
||||
|
||||
rowCountMap = containers.Map();
|
||||
for j = 1:numel(defNames)
|
||||
defPrompt = defNames{j};
|
||||
def = defines.(defPrompt);
|
||||
|
||||
% Вызов функции добавления одного параметра
|
||||
periphConfig.addConfig(mask, containerName, periph, defPrompt, def, rowCountMap, rowWidth);
|
||||
end
|
||||
end
|
||||
end
|
||||
periphConfig.create_all_code_storage_params(blockPath, config);
|
||||
% periphConfig.cleanup_obsolete_code_params(blockPath, config);
|
||||
|
||||
periphConfig.update();
|
||||
|
||||
% Восстанавлиperiph = allTabNamesваем таблицы
|
||||
customtable.restore_all_tables(tableNames, columns_backup);
|
||||
catch
|
||||
% Восстанавливаем таблицы
|
||||
customtable.restore_all_tables(tableNames, columns_backup);
|
||||
end
|
||||
% Восстанавливаем таблицы
|
||||
customtable.restore_all_tables(tableNames, columns_backup);
|
||||
|
||||
% % Повторно открываем маску, если она была открыта
|
||||
% if wasOpen
|
||||
% open_system(blockPath, 'mask');
|
||||
% end
|
||||
end
|
||||
|
||||
function config = update_config(blockPath, config)
|
||||
if isempty(config)
|
||||
function update()
|
||||
blockPath = gcb;
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
|
||||
config = configJs.read(blockPath);
|
||||
containerName = 'configTabAll';
|
||||
container = mask.getDialogControl(containerName);
|
||||
paramsAll = mcuMask.collect_all_parameters(container);
|
||||
% Получаем все имена в кладок из container (у вас должен быть container)
|
||||
allTabs = container.DialogControls;
|
||||
allTabNames = arrayfun(@(t) t.Name, allTabs, 'UniformOutput', false);
|
||||
|
||||
fieldsConfig = fieldnames(config);
|
||||
% Цикл по всем вкладкам в контейнере
|
||||
for i = 1:length(allTabNames)
|
||||
periph = fieldsConfig{i};
|
||||
|
||||
% Попытка найти параметр чекбокса Tab_<Periph>_Enable
|
||||
checkboxName = ['Tab_' periph '_Enable'];
|
||||
if ismember(checkboxName, paramsAll)
|
||||
% Чекбокс есть - проверяем его состояние
|
||||
paramObj = mask.getParameter(checkboxName);
|
||||
val = paramObj.Value;
|
||||
|
||||
try
|
||||
tab = container.getDialogControl(periph);
|
||||
if strcmpi(val, 'off')
|
||||
tab.Enabled = 'off';
|
||||
% Рекурсивно очищаем связанные скрытые параметры
|
||||
periphConfig.clear_tab_params(mask, config.(periph), periph);
|
||||
else
|
||||
tab.Enabled = 'on';
|
||||
periphConfig.sync_tab_params(mask, config.(periph), periph);
|
||||
end
|
||||
catch
|
||||
warning('Вкладка с именем "%s" не найдена.', periph);
|
||||
end
|
||||
|
||||
else
|
||||
% Чекбокса нет — просто пытаемся включить вкладку, если она есть
|
||||
try
|
||||
tab = container.getDialogControl(periph);
|
||||
tab.Enabled = 'on';
|
||||
% Опционально можно синхронизировать параметры, если есть config поле
|
||||
if isfield(config, periph)
|
||||
periphConfig.sync_tab_params(mask, config.(periph), periph);
|
||||
end
|
||||
catch
|
||||
% Можно не выводить предупреждение — вкладка может быть необязательной
|
||||
% warning('Вкладка с именем "%s" не найдена.', periph);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function periphParamCallback(paramName)
|
||||
blockPath = gcb;
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
hObj = mask.getParameter(paramName);
|
||||
config = configJs.read(blockPath);
|
||||
container = mask.getDialogControl('configTabAll');
|
||||
|
||||
% === Проверка на Tab_<Periph>_Enable ===
|
||||
exprTab = '^Tab_(\w+)_Enable$';
|
||||
tokensTab = regexp(paramName, exprTab, 'tokens');
|
||||
|
||||
if ~isempty(tokensTab)
|
||||
periph = tokensTab{1}{1};
|
||||
|
||||
try
|
||||
tab = container.getDialogControl(periph);
|
||||
paramVal = hObj.Value;
|
||||
|
||||
if strcmpi(paramVal, 'off')
|
||||
tab.Enabled = 'off';
|
||||
if isfield(config, periph)
|
||||
periphConfig.clear_tab_params(mask, config.(periph), periph);
|
||||
end
|
||||
else
|
||||
tab.Enabled = 'on';
|
||||
if isfield(config, periph)
|
||||
periphConfig.sync_tab_params(mask, config.(periph), periph);
|
||||
end
|
||||
end
|
||||
catch
|
||||
warning('Ошибка обработки вкладки "%s".', periph);
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
maskParams = mask.Parameters;
|
||||
paramNames = arrayfun(@(p) p.Name, maskParams, 'UniformOutput', false);
|
||||
% === Проверка на параметр, связанный с Sources/Includes ===
|
||||
% Проверка: это параметр, у которого есть соответствующий Hidden_<Name>_Sources или Hidden_<Name>_Includes
|
||||
nameBase = paramName;
|
||||
|
||||
paramNames = string({mask.Parameters.Name});
|
||||
hasSources = any(paramNames == "Hidden_" + nameBase + "_Sources");
|
||||
hasIncludes = any(paramNames == "Hidden_" + nameBase + "_Includes");
|
||||
|
||||
|
||||
if hasSources || hasIncludes
|
||||
useVal = hObj.Value;
|
||||
|
||||
% Получаем содержимое config по nameBase — возможно, вложенное
|
||||
try
|
||||
valueStruct = configJs.get_field(config, nameBase);
|
||||
catch
|
||||
warning('Не удалось найти путь %s в config.', nameBase);
|
||||
return;
|
||||
end
|
||||
|
||||
if strcmpi(useVal, 'on')
|
||||
try
|
||||
periphConfig.store_single_periph_code(mask, nameBase, valueStruct);
|
||||
catch
|
||||
warning('Не удалось сохранить параметры для %s.', nameBase);
|
||||
end
|
||||
else
|
||||
periphConfig.clear_single_periph_code_param(mask, nameBase);
|
||||
end
|
||||
|
||||
return;
|
||||
end
|
||||
|
||||
% Обработка остальных секций (с дефайнами)
|
||||
periphs = fieldnames(config);
|
||||
for i = 1:numel(periphs)
|
||||
periph = periphs{i};
|
||||
|
||||
% Пропускаем Code и UserCode, они уже обработаны
|
||||
if strcmp(periph, 'Code') || strcmp(periph, 'UserCode')
|
||||
continue;
|
||||
end
|
||||
% === Если не подошло ни под одно из условий ===
|
||||
% warning('Объект "%s" не поддерживается универсальным коллбеком.', paramName);
|
||||
end
|
||||
|
||||
function updatePeriphRunMexBat()
|
||||
% Запись run_mex.bat
|
||||
blockPath = gcb;
|
||||
CodeStruct = periphConfig.restore_periph_code_from_mask(blockPath);
|
||||
periphPath = mcuPath.get('periphPath');
|
||||
[periphPath, ~, ~] = fileparts(periphPath);
|
||||
|
||||
periphConfig.addCodeBat(CodeStruct, periphPath);
|
||||
end
|
||||
|
||||
% Проверяем есть ли Defines
|
||||
if ~isfield(config.(periph), 'Defines')
|
||||
continue;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
methods(Static, Access=private)
|
||||
|
||||
|
||||
|
||||
defines = config.(periph).Defines;
|
||||
defNames = fieldnames(defines);
|
||||
function addHiddenParam(mask, containerName, nameBase, kind, existingParams)
|
||||
% Преобразуем к красивому имени
|
||||
prettyName = strrep(nameBase, '_', ' ');
|
||||
paramName = ['Hidden_' char(nameBase) '_' kind];
|
||||
if ismember(paramName, existingParams)
|
||||
return;
|
||||
end
|
||||
|
||||
for j = 1:numel(defNames)
|
||||
defPrompt = defNames{j};
|
||||
paramName = matlab.lang.makeValidName(defPrompt);
|
||||
mask.addParameter( ...
|
||||
'Name', paramName, ...
|
||||
'Type', 'edit', ...
|
||||
'Prompt', ['Hidden ' prettyName ' ' kind], ...
|
||||
'Value', '', ...
|
||||
'Visible', 'off', ...
|
||||
'Container', containerName ...
|
||||
);
|
||||
fprintf('Создан скрытый параметр: %s\n', paramName);
|
||||
end
|
||||
|
||||
function clear_tab_params(mask, configStruct, prefix, depth)
|
||||
if nargin < 4
|
||||
depth = 0;
|
||||
end
|
||||
maxDepth = 3; % Максимальная глубина рекурсии
|
||||
|
||||
fields = fieldnames(configStruct);
|
||||
|
||||
% Проверка, существует ли параметр с таким именем
|
||||
if ismember(paramName, paramNames)
|
||||
param = mask.getParameter(paramName);
|
||||
valStr = param.Value;
|
||||
for i = 1:numel(fields)
|
||||
key = fields{i};
|
||||
value = configStruct.(key);
|
||||
paramName = [prefix '_' key];
|
||||
|
||||
% Проверяем, существует ли элемент defPrompt в структуре defines
|
||||
if isfield(defines, defPrompt)
|
||||
% Преобразуем строку в соответствующий тип
|
||||
if strcmpi(defines.(defPrompt).Type, 'checkbox')
|
||||
config.(periph).Defines.(defPrompt).Default = strcmpi(valStr, 'on');
|
||||
elseif strcmpi(defines.(defPrompt).Type, 'edit')
|
||||
valNum = str2double(valStr);
|
||||
if isnan(valNum)
|
||||
config.(periph).Defines.(defPrompt).Default = valStr;
|
||||
else
|
||||
config.(periph).Defines.(defPrompt).Default = valNum;
|
||||
end
|
||||
end
|
||||
end
|
||||
if isstruct(value)
|
||||
if depth < maxDepth
|
||||
% Рекурсивный вызов для вложенных структур с увеличением глубины
|
||||
periphConfig.clear_tab_params(mask, value, paramName, depth + 1);
|
||||
end
|
||||
else
|
||||
if strcmp(key, 'Sources') || strcmp(key, 'Includes')
|
||||
baseName = configJs.get_final_name_from_prefix(prefix);
|
||||
periphConfig.clear_single_periph_code_param(mask, baseName);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function config = read_config(blockPath)
|
||||
|
||||
|
||||
function sync_tab_params(mask, configStruct, prefix, depth)
|
||||
if nargin < 4
|
||||
depth = 0;
|
||||
end
|
||||
maxDepth = 3; % Максимальная глубина рекурсии
|
||||
|
||||
fields = fieldnames(configStruct);
|
||||
|
||||
for i = 1:numel(fields)
|
||||
key = fields{i};
|
||||
value = configStruct.(key);
|
||||
paramName = [prefix '_' key];
|
||||
|
||||
if isstruct(value)
|
||||
if depth < maxDepth
|
||||
% Рекурсивный вызов для вложенных структур с увеличением глубины
|
||||
periphConfig.sync_tab_params(mask, value, paramName, depth + 1);
|
||||
end
|
||||
else
|
||||
if strcmp(key, 'Sources') || strcmp(key, 'Includes')
|
||||
baseName = configJs.get_final_name_from_prefix(prefix);
|
||||
periphConfig.store_single_periph_code(mask, baseName, configStruct);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function create_all_code_storage_params(blockPath, config)
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
|
||||
pathparam = mask.getParameter('periphPath');
|
||||
config_path = pathparam.Value;
|
||||
containerName = 'configTabAll';
|
||||
container = mask.getDialogControl(containerName);
|
||||
existingParams = mcuMask.collect_all_parameters(container);
|
||||
|
||||
if ~isempty(config_path)
|
||||
jsonText = fileread(config_path);
|
||||
config = jsondecode(jsonText);
|
||||
% Убедимся, что контейнер существует
|
||||
tabName = 'hiddenCodeTab';
|
||||
tab = mask.getDialogControl(tabName);
|
||||
if isempty(tab)
|
||||
tab = container.addDialogControl('tab', tabName);
|
||||
tab.Prompt = 'Hidden Code Settings';
|
||||
tab.Visible = 'off';
|
||||
else
|
||||
config = [];
|
||||
tab.Visible = 'off';
|
||||
end
|
||||
|
||||
% Запуск рекурсивного обхода
|
||||
periphConfig.process_struct_recursive(mask, tabName, config, {}, existingParams);
|
||||
end
|
||||
|
||||
function process_struct_recursive(mask, tabName, currentStruct, nameStack, existingParams)
|
||||
fields = fieldnames(currentStruct);
|
||||
for i = 1:numel(fields)
|
||||
fieldName = fields{i};
|
||||
value = currentStruct.(fieldName);
|
||||
newStack = [nameStack, fieldName]; % Добавляем уровень к имени
|
||||
|
||||
% Если value — структура, обходим дальше
|
||||
if isstruct(value)
|
||||
% Проверяем: это структура с Sources/Includes или просто промежуточный узел?
|
||||
hasSources = isfield(value, 'Sources');
|
||||
hasIncludes = isfield(value, 'Includes');
|
||||
|
||||
if hasSources
|
||||
periphConfig.addHiddenParam(mask, tabName, fieldName, 'Sources', existingParams);
|
||||
end
|
||||
if hasIncludes
|
||||
periphConfig.addHiddenParam(mask, tabName, fieldName, 'Includes', existingParams);
|
||||
end
|
||||
|
||||
% Рекурсивно продолжаем обход
|
||||
periphConfig.process_struct_recursive(mask, tabName, value, newStack, existingParams);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function write_config(config)
|
||||
if isempty(config)
|
||||
return
|
||||
end
|
||||
|
||||
blockHandle = gcbh;
|
||||
mask = Simulink.Mask.get(blockHandle);
|
||||
function cleanup_obsolete_code_params(blockPath, config)
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
maskParams = mask.Parameters;
|
||||
|
||||
pathparam = mask.getParameter('periphPath');
|
||||
config_path = pathparam.Value;
|
||||
% Получаем список актуальных периферий
|
||||
validPeriphs = fieldnames(config);
|
||||
|
||||
jsonText = jsonencode(config, 'PrettyPrint', true);
|
||||
fid = fopen(config_path, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
error('Не удалось открыть файл periph_config.json для записи.');
|
||||
for i = 1:numel(maskParams)
|
||||
paramName = maskParams(i).Name;
|
||||
|
||||
% Проверяем, является ли параметром хранения Sources или Includes
|
||||
expr = '^Hidden_(\w+)_(Sources|Includes)$';
|
||||
tokens = regexp(paramName, expr, 'tokens');
|
||||
if ~isempty(tokens)
|
||||
periph = tokens{1}{1};
|
||||
|
||||
% Если периферии больше нет – удаляем параметр
|
||||
if ~ismember(periph, validPeriphs)
|
||||
mask.removeParameter(paramName);
|
||||
fprintf('Удалён устаревший параметр: %s\n', paramName);
|
||||
end
|
||||
end
|
||||
end
|
||||
fwrite(fid, jsonText, 'char');
|
||||
fclose(fid);
|
||||
end
|
||||
|
||||
|
||||
function codeStruct = restore_periph_code_from_mask(blockPath)
|
||||
mask = Simulink.Mask.get(blockPath);
|
||||
maskParams = mask.Parameters;
|
||||
|
||||
allSources = {};
|
||||
allIncludes = {};
|
||||
|
||||
for i = 1:numel(maskParams)
|
||||
name = maskParams(i).Name;
|
||||
|
||||
% Ищем параметры Sources
|
||||
tokensSrc = regexp(name, '^Hidden_(\w+)_Sources$', 'tokens');
|
||||
if ~isempty(tokensSrc)
|
||||
val = maskParams(i).Value;
|
||||
|
||||
if ischar(val) || isstring(val)
|
||||
valStr = strtrim(char(val));
|
||||
% Пропускаем пустые строки и '[]'
|
||||
if isempty(valStr) || strcmp(valStr, '[]')
|
||||
continue;
|
||||
end
|
||||
|
||||
lines = splitlines(valStr);
|
||||
lines = lines(~cellfun(@(x) all(isspace(x)) || isempty(x), lines));
|
||||
allSources = [allSources; lines]; %#ok<AGROW>
|
||||
end
|
||||
continue;
|
||||
end
|
||||
|
||||
% Ищем параметры Includes
|
||||
tokensInc = regexp(name, '^Hidden_(\w+)_Includes$', 'tokens');
|
||||
if ~isempty(tokensInc)
|
||||
val = maskParams(i).Value;
|
||||
|
||||
if ischar(val) || isstring(val)
|
||||
valStr = strtrim(char(val));
|
||||
if isempty(valStr) || strcmp(valStr, '[]')
|
||||
continue;
|
||||
end
|
||||
|
||||
lines = splitlines(valStr);
|
||||
lines = lines(~cellfun(@(x) all(isspace(x)) || isempty(x), lines));
|
||||
allIncludes = [allIncludes; lines]; %#ok<AGROW>
|
||||
end
|
||||
continue;
|
||||
end
|
||||
end
|
||||
|
||||
codeStruct = struct();
|
||||
codeStruct.Sources = allSources;
|
||||
codeStruct.Includes = allIncludes;
|
||||
end
|
||||
|
||||
|
||||
function clear_all_from_container(mask, containerName)
|
||||
% allControls = mask.getDialogControls();
|
||||
container = mask.getDialogControl(containerName);
|
||||
@ -190,69 +443,25 @@ classdef periphConfig
|
||||
mcuMask.delete_all_tabs(mask, container);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
methods(Static, Access=private)
|
||||
|
||||
function res = addCodeConfig(codeConfig, periphPath)
|
||||
function res = addCodeBat(codeConfig, codePath)
|
||||
% Добавить сурсы и пути в батник
|
||||
% Возвращает 0 при успехе, 1 при ошибке
|
||||
try
|
||||
% Источники
|
||||
srcList = {};
|
||||
if isfield(codeConfig, 'Sources') && isfield(codeConfig.Sources, 'Options')
|
||||
srcFiles = codeConfig.Sources.Options;
|
||||
for i = 1:numel(srcFiles)
|
||||
fullPath = fullfile(periphPath, srcFiles{i});
|
||||
srcList{end+1} = [strrep(fullPath, '\', '\\')];
|
||||
end
|
||||
end
|
||||
|
||||
% Формируем srcText с переносами строк и ^
|
||||
srcText = '';
|
||||
for i = 1:numel(srcList)
|
||||
if i < numel(srcList)
|
||||
srcText = [srcText srcList{i} '^' newline ' '];
|
||||
else
|
||||
srcText = [srcText srcList{i}];
|
||||
end
|
||||
end
|
||||
|
||||
% Инклуды
|
||||
incList = {};
|
||||
if isfield(codeConfig, 'Includes') && isfield(codeConfig.Includes, 'Options')
|
||||
incPaths = codeConfig.Includes.Options;
|
||||
for i = 1:numel(incPaths)
|
||||
fullPath = fullfile(periphPath, incPaths{i});
|
||||
incList{end+1} = ['-I"' strrep(fullPath, '\', '\\') '"'];
|
||||
end
|
||||
end
|
||||
|
||||
% Формируем incText с переносами строк и ^
|
||||
incText = '';
|
||||
for i = 1:numel(incList)
|
||||
if i == 1 && numel(incList) ~= 1
|
||||
incText = [incText incList{i} '^' newline];
|
||||
elseif i < numel(incList)
|
||||
incText = [incText ' ' incList{i} '^' newline];
|
||||
else
|
||||
incText = [incText ' ' incList{i}];
|
||||
end
|
||||
end
|
||||
|
||||
% Добавляем префиксы
|
||||
srcText = ['set code_PERIPH' '=' srcText];
|
||||
incText = ['set includes_PERIPH' '=' incText];
|
||||
% Формируем строки
|
||||
srcText = compiler.createSourcesBat('code_PERIPH', codeConfig.Sources, codePath);
|
||||
incText = compiler.createIncludesBat('includes_PERIPH', codeConfig.Includes, codePath);
|
||||
|
||||
% Записываем результат
|
||||
res = periphConfig.updateRunMexBat(srcText, incText); % Всё прошло успешно
|
||||
res = compiler.updateRunMexBat(srcText, incText, ':: PERIPH BAT'); % Всё прошло успешно
|
||||
catch
|
||||
% В случае ошибки просто возвращаем 1
|
||||
res = 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function res = addUserCodeConfig(userCodeConfig)
|
||||
function res = addUserFunctions(userCodeConfig)
|
||||
% Добавить функции и дефайны в исходный код wrapper
|
||||
% userCodeConfig — структура config.UserCode
|
||||
|
||||
initFuncsText = '';
|
||||
@ -262,34 +471,33 @@ classdef periphConfig
|
||||
if isfield(userCodeConfig, 'Functions')
|
||||
funcs = userCodeConfig.Functions;
|
||||
|
||||
if isfield(funcs, 'PeriphInit') && isfield(funcs.PeriphInit, 'Options')
|
||||
initFuncs = funcs.PeriphInit.Options;
|
||||
if isfield(funcs, 'PeriphInit')
|
||||
initFuncs = funcs.PeriphInit;
|
||||
initFuncsText = strjoin(strcat('\t', initFuncs, ';'), '\n');
|
||||
end
|
||||
|
||||
if isfield(funcs, 'PeriphSimulation') && isfield(funcs.PeriphSimulation, 'Options')
|
||||
simFuncs = funcs.PeriphSimulation.Options;
|
||||
if isfield(funcs, 'PeriphSimulation')
|
||||
simFuncs = funcs.PeriphSimulation;
|
||||
simFuncsText = strjoin(strcat('\t', simFuncs, ';'), '\n');
|
||||
end
|
||||
|
||||
if isfield(funcs, 'PeriphDeinit') && isfield(funcs.PeriphDeinit, 'Options')
|
||||
deinitFuncs = funcs.PeriphDeinit.Options;
|
||||
if isfield(funcs, 'PeriphDeinit')
|
||||
deinitFuncs = funcs.PeriphDeinit;
|
||||
deinitFuncsText = strjoin(strcat('\t', deinitFuncs, ';'), '\n');
|
||||
end
|
||||
|
||||
res = periphConfig.updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText);
|
||||
res = periphConfig.writeWrapperCode(initFuncsText, simFuncsText, deinitFuncsText);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function res = updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText)
|
||||
function res = writeWrapperCode(initFuncsText, simFuncsText, deinitFuncsText)
|
||||
% Входные параметры:
|
||||
% srcText - текст для записи set code_...
|
||||
% incText - текст для записи set includes_...
|
||||
%
|
||||
% Возвращает:
|
||||
% res - 0 при успехе, 1 при ошибке
|
||||
wrapPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c');
|
||||
wrapPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c');
|
||||
res = 1;
|
||||
try
|
||||
code = fileread(wrapPath);
|
||||
@ -311,47 +519,27 @@ classdef periphConfig
|
||||
error('Ошибка: неудачная запись в файл при записи файла: %s', ME.message);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function res = updateRunMexBat(srcText, incText)
|
||||
% Входные параметры:
|
||||
% srcText - текст для записи set code_...
|
||||
% incText - текст для записи set includes_...
|
||||
%
|
||||
% Возвращает:
|
||||
% res - 0 при успехе, 1 при ошибке
|
||||
periphBat = [srcText '\n\n' incText];
|
||||
batPath = fullfile('.\MCU_Wrapper', 'run_mex.bat');
|
||||
res = 1;
|
||||
try
|
||||
code = fileread(batPath);
|
||||
code = regexprep(code, '\r\n?', '\n');
|
||||
|
||||
% Записываем строки srcText и incText с переносами строк
|
||||
code = editCode.insertSection(code, ':: PERIPH BAT', periphBat);
|
||||
|
||||
fid = fopen(batPath, 'w', 'n', 'UTF-8');
|
||||
if fid == -1
|
||||
error('Не удалось открыть файл для записи');
|
||||
end
|
||||
fwrite(fid, code);
|
||||
fclose(fid);
|
||||
res = 1;
|
||||
catch ME
|
||||
mcuMask.disp(0, '\nОшибка: неудачная запись в файл при записи файла: %s', ME.message);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function addDefineConfig(mask, containerName, periphName, defPrompt, def)
|
||||
|
||||
function addConfig(mask, containerName, periphName, defPrompt, def, rowCountMap, rowWidth)
|
||||
% mask — объект маски Simulink.Mask.get(blockPath)
|
||||
% containerName — имя контейнера, в который добавляем параметр (например, 'configTabAll')
|
||||
% periphName — имя вкладки / контейнера для текущего периферийного блока (например, 'ADC')
|
||||
% defPrompt — имя параметра в Defines (например, 'shift_enable')
|
||||
% def — структура с описанием параметра (Prompt, Def, Type, Default, NewRow и т.п.)
|
||||
|
||||
if ~isKey(rowCountMap, periphName)
|
||||
rowCountMap(periphName) = 0;
|
||||
end
|
||||
rowCount = rowCountMap(periphName) + 1;
|
||||
|
||||
% Устанавливаем NewRow, если он не задан
|
||||
if ~isfield(def, 'NewRow')
|
||||
def.NewRow = mod(rowCount - 1, rowWidth) == 0;
|
||||
elseif def.NewRow == true
|
||||
rowCount = 1;
|
||||
end
|
||||
rowCountMap(periphName) = rowCount;
|
||||
|
||||
% Найдем контейнер с таким именем
|
||||
container = mask.getDialogControl(containerName);
|
||||
if isempty(container)
|
||||
@ -371,6 +559,8 @@ classdef periphConfig
|
||||
paramType = 'checkbox';
|
||||
case 'edit'
|
||||
paramType = 'edit';
|
||||
case 'popup'
|
||||
paramType = 'popup';
|
||||
otherwise
|
||||
% Игнорируем остальные типы
|
||||
return;
|
||||
@ -378,28 +568,49 @@ classdef periphConfig
|
||||
|
||||
paramName = matlab.lang.makeValidName(defPrompt);
|
||||
|
||||
% Преобразуем значение Default в строку для Value
|
||||
val = def.Default;
|
||||
if islogical(val)
|
||||
valStr = mcuMask.ternary(val, 'on', 'off');
|
||||
elseif isnumeric(val)
|
||||
valStr = num2str(val);
|
||||
elseif ischar(val)
|
||||
valStr = val;
|
||||
% Получаем значение
|
||||
if strcmp(paramType, 'popup')
|
||||
if isfield(def, 'Def') && iscell(def.Def) && ~isempty(def.Def)
|
||||
choices = def.Def;
|
||||
valStr = ''; % по умолчанию — ничего
|
||||
elseif isfield(def, 'Options')
|
||||
choices = def.Def;
|
||||
valStr = ''; % по умолчанию — ничего
|
||||
else
|
||||
warning('Popout параметр "%s" не содержит допустимого списка в Def.', defPrompt);
|
||||
return;
|
||||
end
|
||||
else
|
||||
error('Unsupported default value type for %s.%s', periphName, defPrompt);
|
||||
val = def.Default;
|
||||
if islogical(val)
|
||||
valStr = periphConfig.ternary(val, 'on', 'off');
|
||||
elseif isnumeric(val)
|
||||
valStr = num2str(val);
|
||||
elseif ischar(val)
|
||||
valStr = val;
|
||||
else
|
||||
error('Unsupported default value type for %s.%s', periphName, defPrompt);
|
||||
end
|
||||
end
|
||||
|
||||
% Добавляем параметр в маску
|
||||
param = mask.addParameter( ...
|
||||
'Type', paramType, ...
|
||||
'Prompt', def.Prompt, ...
|
||||
'Name', paramName, ...
|
||||
'Value', valStr, ...
|
||||
'Container', periphName ...
|
||||
);
|
||||
if strcmp(paramType, 'popup')
|
||||
param = mask.addParameter( ...
|
||||
'Type', paramType, ...
|
||||
'Prompt', def.Prompt, ...
|
||||
'Name', paramName, ...
|
||||
'Container', periphName ...
|
||||
);
|
||||
else
|
||||
param = mask.addParameter( ...
|
||||
'Type', paramType, ...
|
||||
'Prompt', def.Prompt, ...
|
||||
'Name', paramName, ...
|
||||
'Value', valStr, ...
|
||||
'Container', periphName ...
|
||||
);
|
||||
end
|
||||
|
||||
param.Alias = def.Def;
|
||||
param.Evaluate = 'off';
|
||||
|
||||
if def.NewRow
|
||||
@ -407,8 +618,80 @@ classdef periphConfig
|
||||
else
|
||||
param.DialogControl.Row = 'current';
|
||||
end
|
||||
|
||||
if isfield(def, 'Def')
|
||||
if strcmp(paramType, 'popup')
|
||||
if iscell(def.Def)
|
||||
param.TypeOptions = def.Def;
|
||||
elseif isfield(def, 'Options')
|
||||
param.Alias = def.Def;
|
||||
param.TypeOptions = def.Options;
|
||||
end
|
||||
else
|
||||
param.Alias = def.Def;
|
||||
end
|
||||
end
|
||||
|
||||
callback = sprintf('periphConfig.periphParamCallback("%s");', paramName);
|
||||
param.Callback = callback;
|
||||
end
|
||||
|
||||
|
||||
|
||||
%% ELEMENTARY
|
||||
|
||||
function clear_single_periph_code_param(mask, periph)
|
||||
% Очистка кода одного поля конфига
|
||||
paramNames = {
|
||||
['Hidden_' char(periph) '_Sources'],
|
||||
['Hidden_' char(periph) '_Includes']
|
||||
};
|
||||
|
||||
for i = 1:numel(paramNames)
|
||||
paramName = paramNames{i};
|
||||
try
|
||||
param = mask.getParameter(paramName);
|
||||
param.Value = '';
|
||||
catch
|
||||
% Параметр не существует — ничего не делаем
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function store_single_periph_code(mask, periph, code)
|
||||
% Запись кода одного поля конфига
|
||||
% Сохраняем Sources, если они есть
|
||||
if isfield(code, 'Sources')
|
||||
paramName = ['Hidden_' char(periph) '_Sources'];
|
||||
try
|
||||
param = mask.getParameter(paramName);
|
||||
param.Value = configJs.convert_code_value(code.Sources);
|
||||
catch
|
||||
mcuMask.disp(0, ['Параметр ' paramName ' не найден']);
|
||||
end
|
||||
end
|
||||
|
||||
% Сохраняем Includes, если они есть
|
||||
if isfield(code, 'Includes')
|
||||
paramName = ['Hidden_' char(periph) '_Includes'];
|
||||
try
|
||||
param = mask.getParameter(paramName);
|
||||
param.Value = configJs.convert_code_value(code.Includes);
|
||||
catch
|
||||
mcuMask.disp(0, ['Параметр ' paramName ' не найден']);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function res = ternary(cond, valTrue, valFalse)
|
||||
if cond
|
||||
res = valTrue;
|
||||
else
|
||||
res = valFalse;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -115,7 +115,7 @@ static void mdlInitializeSizes(SimStruct* S)
|
||||
for (int i = 0; i < IN_PORT_NUMB; i++)
|
||||
{
|
||||
ssSetInputPortWidth(S, i, inLengths[i]);
|
||||
ssSetInputPortDirectFeedThrough(S, i, 0); // или 1, если нужно
|
||||
ssSetInputPortDirectFeedThrough(S, i, 0);
|
||||
ssSetInputPortRequiredContiguous(S, i, 1);
|
||||
}
|
||||
|
@ -212,13 +212,13 @@ void SIM_writeOutputs(SimStruct* S)
|
||||
int global_index;
|
||||
|
||||
//-------------WRITTING OUTPUT--------------
|
||||
for (int j = 0; j < OUT_PORT_NUMB; j++)
|
||||
for (int arr_ind = 0; arr_ind < OUT_PORT_NUMB; arr_ind++)
|
||||
{
|
||||
Output = ssGetOutputPortRealSignal(S, j);
|
||||
for (int i = 0; i < outLengths[i]; i++)
|
||||
Output = ssGetOutputPortRealSignal(S, arr_ind);
|
||||
for (int val_ind = 0; val_ind < outLengths[arr_ind]; val_ind++)
|
||||
{
|
||||
global_index = XD_OUTPUT_START + outOffsets[j] + i;
|
||||
Output[i] = Out_Buff[global_index];
|
||||
global_index = XD_OUTPUT_START + outOffsets[arr_ind] + val_ind;
|
||||
Output[val_ind] = Out_Buff[global_index];
|
||||
Out_Buff[global_index] = 0;
|
||||
}
|
||||
}
|
||||
@ -236,13 +236,13 @@ void SIM_readInputs(SimStruct* S)
|
||||
int global_index;
|
||||
|
||||
//-------------READING INPUTS---------------
|
||||
for (int j = 0; j < IN_PORT_NUMB; j++)
|
||||
for (int arr_ind = 0; arr_ind < IN_PORT_NUMB; arr_ind++)
|
||||
{
|
||||
Input = ssGetInputPortRealSignal(S, j);
|
||||
for (int i = 0; i < inLengths[j]; i++)
|
||||
Input = ssGetInputPortRealSignal(S, arr_ind);
|
||||
for (int val_ind = 0; val_ind < inLengths[arr_ind]; val_ind++)
|
||||
{
|
||||
global_index = XD_INPUT_START + inOffsets[j] + i;
|
||||
In_Buff[global_index] = Input[i];
|
||||
global_index = XD_INPUT_START + inOffsets[arr_ind] + val_ind;
|
||||
In_Buff[global_index] = Input[val_ind];
|
||||
}
|
||||
}
|
||||
//------------------------------------------
|
||||
|
@ -11,11 +11,12 @@
|
||||
:: %4 — режим компиляции (debug/release)
|
||||
|
||||
:: Сохраняем как переменные
|
||||
set includes_USER=%~1
|
||||
set code_USER=%~2
|
||||
set defines_USER=%~3
|
||||
set defines_CONFIG=%~4
|
||||
set compil_mode=%~5
|
||||
set filename=%~1
|
||||
set includes_USER=%~2
|
||||
set code_USER=%~3
|
||||
set defines_USER=%~4
|
||||
set defines_CONFIG=%~5
|
||||
set compil_mode=%~6
|
||||
|
||||
:: Заменяем __EQ__ на =
|
||||
set defines_USER=%defines_USER:__EQ__==%
|
||||
@ -29,15 +30,17 @@ set defines_WRAPPER=-D"MATLAB"^ -D"__sizeof_ptr=8"
|
||||
|
||||
:: -------------------------WRAPPER PATHS AND CODE---------------------------
|
||||
:: оболочка, которая будет моделировать работу МК в симулинке
|
||||
set includes_WRAPPER=-I"."^
|
||||
-I".\MCU_Wrapper"^
|
||||
-I".\app_wrapper"
|
||||
:: WRAPPER BAT START
|
||||
|
||||
:: WRAPPER BAT END
|
||||
|
||||
:: APP WRAPPER BAT START
|
||||
|
||||
:: APP WRAPPER BAT END
|
||||
|
||||
set includes_WRAPPER= %includes_WRAPPER% %includes_APP_WRAPPER%
|
||||
set code_WRAPPER= %code_WRAPPER% %code_APP_WRAPPER%
|
||||
|
||||
set code_WRAPPER= .\MCU_Wrapper\MCU.c^
|
||||
.\MCU_Wrapper\mcu_wrapper.c^
|
||||
.\app_wrapper\app_init.c^
|
||||
.\app_wrapper\app_io.c^
|
||||
.\app_wrapper\app_wrapper.c
|
||||
|
||||
:: PERIPH BAT START
|
||||
|
||||
@ -51,15 +54,15 @@ set includes= %includes_WRAPPER% %includes_PERIPH% %includes_USER%
|
||||
set codes= %code_WRAPPER% %code_PERIPH% %code_USER%
|
||||
set defines= %defines_WRAPPER% %defines_CONFIG% %defines_USER%
|
||||
:: -------OUTPUT FOLDER--------
|
||||
set output= -outdir "."
|
||||
set output= -outdir "." -output %filename%
|
||||
|
||||
:: если нужен дебаг, до запускаем run_mex с припиской debug
|
||||
IF [%1]==[debug] (set debug= -g)
|
||||
IF %compil_mode%==debug (set debug= -g)
|
||||
::-------------------------------------------------------------------------
|
||||
|
||||
|
||||
::------START COMPILING-------
|
||||
if "%6"=="echo_enable" (
|
||||
if "%7"=="echo_enable" (
|
||||
echo Compiling...
|
||||
|
||||
echo ===========================
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file app_config.h
|
||||
* @brief Заголовочный файл для пользовательских конфигураций.
|
||||
* @brief Заголовочный файл для пользовательских конфигураций.
|
||||
**************************************************************************/
|
||||
#ifndef _APP_CONFIG
|
||||
#define _APP_CONFIG
|
||||
|
BIN
mcuwrapper.mltbx
BIN
mcuwrapper.mltbx
Binary file not shown.
@ -1,19 +1,19 @@
|
||||
<deployment-project plugin="plugin.toolbox" plugin-version="1.0">
|
||||
<configuration file="E:\.WORK\MATLAB\matlab_23550\McuLib\mcuwrapper.prj" location="E:\.WORK\MATLAB\matlab_23550\McuLib" name="mcuwrapper" target="target.toolbox" target-name="Package Toolbox">
|
||||
<param.appname>mcuwrapper</param.appname>
|
||||
<configuration build-checksum="1424241104" file="F:\Work\Projects\MATLAB\mcu_matlab_lib\mcuwrapper.prj" location="F:\Work\Projects\MATLAB\mcu_matlab_lib" name="mcuwrapper" target="target.toolbox" target-name="Package Toolbox">
|
||||
<param.appname>MCU Wrapper</param.appname>
|
||||
<param.authnamewatermark>Razvalyaev</param.authnamewatermark>
|
||||
<param.email>wot890089@mail.ru</param.email>
|
||||
<param.company>NIO-12</param.company>
|
||||
<param.summary>Library for run MCU program in Simulink</param.summary>
|
||||
<param.description />
|
||||
<param.screenshot />
|
||||
<param.version>1.0</param.version>
|
||||
<param.output>${PROJECT_ROOT}\mcuwrapper.mltbx</param.output>
|
||||
<param.version>1.02</param.version>
|
||||
<param.output>${PROJECT_ROOT}\MCU Wrapper.mltbx</param.output>
|
||||
<param.products.name />
|
||||
<param.products.id />
|
||||
<param.products.version />
|
||||
<param.platforms />
|
||||
<param.guid>e7dd2564-e462-4878-b445-45763482263f</param.guid>
|
||||
<param.guid>bcf7498f-65f2-487f-b762-d3e88d9a4ebe</param.guid>
|
||||
<param.exclude.filters />
|
||||
<param.exclude.pcodedmfiles>true</param.exclude.pcodedmfiles>
|
||||
<param.examples />
|
||||
@ -27,7 +27,7 @@
|
||||
<param.exported.on.package>false</param.exported.on.package>
|
||||
<param.required.addons>
|
||||
<requiredaddons>
|
||||
<requiredAddOn earliest="earliest" fromRepository="true" id="e5534541-4a80-11e4-9553-005056977bd0" include="true" latest="latest">findjobj - find java handles of Matlab graphic objects</requiredAddOn>
|
||||
<requiredAddOn earliest="earliest" fromRepository="true" id="e5534541-4a80-11e4-9553-005056977bd0" include="false" latest="latest">findjobj - find java handles of Matlab graphic objects</requiredAddOn>
|
||||
</requiredaddons>
|
||||
</param.required.addons>
|
||||
<param.matlab.project.id />
|
||||
@ -48,7 +48,6 @@
|
||||
<unset>
|
||||
<param.description />
|
||||
<param.screenshot />
|
||||
<param.version />
|
||||
<param.output />
|
||||
<param.products.name />
|
||||
<param.products.id />
|
||||
@ -82,27 +81,64 @@
|
||||
<param.additional.sw.linux.url />
|
||||
</unset>
|
||||
<fileset.rootdir>
|
||||
<file>${PROJECT_ROOT}</file>
|
||||
<file>${PROJECT_ROOT}\McuLib</file>
|
||||
</fileset.rootdir>
|
||||
<fileset.rootfiles>
|
||||
<file>${PROJECT_ROOT}\install_my_library.m</file>
|
||||
<file>${PROJECT_ROOT}\lib</file>
|
||||
<file>${PROJECT_ROOT}\m</file>
|
||||
<file>${PROJECT_ROOT}\sl_customization.m</file>
|
||||
<file>${PROJECT_ROOT}\slblocks.m</file>
|
||||
<file>${PROJECT_ROOT}\startup.m</file>
|
||||
<file>${PROJECT_ROOT}\templates</file>
|
||||
<file>${PROJECT_ROOT}\McuLib\install_my_library.m</file>
|
||||
<file>${PROJECT_ROOT}\McuLib\lib</file>
|
||||
<file>${PROJECT_ROOT}\McuLib\m</file>
|
||||
<file>${PROJECT_ROOT}\McuLib\sl_customization.m</file>
|
||||
<file>${PROJECT_ROOT}\McuLib\slblocks.m</file>
|
||||
<file>${PROJECT_ROOT}\McuLib\startup.m</file>
|
||||
<file>${PROJECT_ROOT}\McuLib\templates</file>
|
||||
</fileset.rootfiles>
|
||||
<fileset.depfun.included />
|
||||
<fileset.depfun.excluded />
|
||||
<fileset.package />
|
||||
<build-deliverables>
|
||||
<file location="${PROJECT_ROOT}" name="mcuwrapper.mltbx" optional="false">E:\.WORK\MATLAB\matlab_23550\McuLib\mcuwrapper.mltbx</file>
|
||||
<file location="${PROJECT_ROOT}" name="MCU Wrapper.mltbx" optional="false">F:\Work\Projects\MATLAB\mcu_matlab_lib\MCU Wrapper.mltbx</file>
|
||||
</build-deliverables>
|
||||
<workflow />
|
||||
<matlab>
|
||||
<root>C:\Program Files\MyProgs\MATLAB\R2023a</root>
|
||||
<toolboxes />
|
||||
<root>C:\Program Files\MATLAB\R2023a</root>
|
||||
<toolboxes>
|
||||
<toolbox name="matlabcoder" />
|
||||
<toolbox name="embeddedcoder" />
|
||||
<toolbox name="gpucoder" />
|
||||
<toolbox name="fixedpoint" />
|
||||
<toolbox name="matlabhdlcoder" />
|
||||
<toolbox name="neuralnetwork" />
|
||||
</toolboxes>
|
||||
<toolbox>
|
||||
<matlabcoder>
|
||||
<enabled>true</enabled>
|
||||
</matlabcoder>
|
||||
</toolbox>
|
||||
<toolbox>
|
||||
<embeddedcoder>
|
||||
<enabled>true</enabled>
|
||||
</embeddedcoder>
|
||||
</toolbox>
|
||||
<toolbox>
|
||||
<gpucoder>
|
||||
<enabled>true</enabled>
|
||||
</gpucoder>
|
||||
</toolbox>
|
||||
<toolbox>
|
||||
<fixedpoint>
|
||||
<enabled>true</enabled>
|
||||
</fixedpoint>
|
||||
</toolbox>
|
||||
<toolbox>
|
||||
<matlabhdlcoder>
|
||||
<enabled>true</enabled>
|
||||
</matlabhdlcoder>
|
||||
</toolbox>
|
||||
<toolbox>
|
||||
<neuralnetwork>
|
||||
<enabled>true</enabled>
|
||||
</neuralnetwork>
|
||||
</toolbox>
|
||||
</matlab>
|
||||
<platform>
|
||||
<unix>false</unix>
|
Loading…
Reference in New Issue
Block a user