mcu_matlab/McuLib/m/periphConfig.m
Razvalyaev df30570d4a мелкие парвки
- поддержка символа '\' вкладке Wrapper User Code
- исправлены дефайны OFFSET_IN/OUT_ARRAY
- убрана конфигурация портов во время подгрузки конфигурации периферии
- исправлены подписи таблицы (Alias почему-то не записывался)
2025-06-15 02:47:17 +03:00

415 lines
18 KiB
Matlab
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

classdef periphConfig
methods(Static)
function update(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)
if isfield(config, 'Code')
res = periphConfig.addCodeConfig(config.Code, periphPath);
if res == 0
error('Ошибка: неудачное добавление кода периферии. Проверьте корректность файлов и путей в конфигурационном файле')
end
else
error('Ошибка: в конфигурационном файле не задан исходный код для симуляции периферии')
end
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);
end
end
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)
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};
% Пропускаем Code и UserCode, они уже обработаны
if strcmp(periph, 'Code') || strcmp(periph, 'UserCode')
continue;
end
% Проверяем есть ли 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_config(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(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 clear_all_from_container(mask, containerName)
% allControls = mask.getDialogControls();
container = mask.getDialogControl(containerName);
if isempty(container)
warning('Контейнер "%s" не найден.', containerName);
return;
end
% Рекурсивно собрать все параметры (не вкладки)
paramsToDelete = mcuMask.collect_all_parameters(container);
% Удаляем все параметры
for i = 1:numel(paramsToDelete)
try
mask.removeParameter(paramsToDelete{i});
catch
warning('Не удалось удалить параметр %s', paramsToDelete{i});
end
end
% Рекурсивно удалить все вкладки внутри контейнера
mcuMask.delete_all_tabs(mask, container);
end
end
methods(Static, Access=private)
function res = addCodeConfig(codeConfig, periphPath)
% Возвращает 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];
% Записываем результат
res = periphConfig.updateRunMexBat(srcText, incText); % Всё прошло успешно
catch
% В случае ошибки просто возвращаем 1
res = 1;
end
end
function res = addUserCodeConfig(userCodeConfig)
% userCodeConfig — структура config.UserCode
initFuncsText = '';
simFuncsText = '';
deinitFuncsText = '';
if isfield(userCodeConfig, 'Functions')
funcs = userCodeConfig.Functions;
if isfield(funcs, 'PeriphInit') && isfield(funcs.PeriphInit, 'Options')
initFuncs = funcs.PeriphInit.Options;
initFuncsText = strjoin(strcat('\t', initFuncs, ';'), '\n');
end
if isfield(funcs, 'PeriphSimulation') && isfield(funcs.PeriphSimulation, 'Options')
simFuncs = funcs.PeriphSimulation.Options;
simFuncsText = strjoin(strcat('\t', simFuncs, ';'), '\n');
end
if isfield(funcs, 'PeriphDeinit') && isfield(funcs.PeriphDeinit, 'Options')
deinitFuncs = funcs.PeriphDeinit.Options;
deinitFuncsText = strjoin(strcat('\t', deinitFuncs, ';'), '\n');
end
res = periphConfig.updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText);
end
end
function res = updateWrapperCode(initFuncsText, simFuncsText, deinitFuncsText)
% Входные параметры:
% srcText - текст для записи set code_...
% incText - текст для записи set includes_...
%
% Возвращает:
% res - 0 при успехе, 1 при ошибке
wrapPath = fullfile('.\MCU_Wrapper', 'mcu_wrapper.c');
res = 1;
try
code = fileread(wrapPath);
code = regexprep(code, '\r\n?', '\n');
% Записываем строки initFuncsText и simFuncsText
code = editCode.insertSection(code, '// PERIPH INIT', initFuncsText);
code = editCode.insertSection(code, '// PERIPH SIM', simFuncsText);
code = editCode.insertSection(code, '// PERIPH DEINIT', deinitFuncsText);
fid = fopen(wrapPath, 'w', 'n', 'UTF-8');
if fid == -1
error('Не удалось открыть файл для записи');
end
fwrite(fid, code);
fclose(fid);
res = 1;
catch ME
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)
% mask — объект маски Simulink.Mask.get(blockPath)
% containerName — имя контейнера, в который добавляем параметр (например, 'configTabAll')
% periphName — имя вкладки / контейнера для текущего периферийного блока (например, 'ADC')
% defPrompt — имя параметра в Defines (например, 'shift_enable')
% def — структура с описанием параметра (Prompt, Def, Type, Default, NewRow и т.п.)
% Найдем контейнер с таким именем
container = mask.getDialogControl(containerName);
if isempty(container)
error('Контейнер "%s" не найден в маске.', containerName);
end
% Проверим, есть ли вкладка с именем periphName, если нет — создадим
tabCtrl = mask.getDialogControl(periphName);
if isempty(tabCtrl)
tabCtrl = container.addDialogControl('tab', periphName);
tabCtrl.Prompt = [periphName ' Config'];
end
% Определяем тип параметра (checkbox или edit)
switch lower(def.Type)
case 'checkbox'
paramType = 'checkbox';
case 'edit'
paramType = 'edit';
otherwise
% Игнорируем остальные типы
return;
end
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;
else
error('Unsupported default value type for %s.%s', periphName, defPrompt);
end
% Добавляем параметр в маску
param = mask.addParameter( ...
'Type', paramType, ...
'Prompt', def.Prompt, ...
'Name', paramName, ...
'Value', valStr, ...
'Container', periphName ...
);
param.Evaluate = 'off';
if def.NewRow
param.DialogControl.Row = 'new';
else
param.DialogControl.Row = 'current';
end
param.Alias = def.Def;
end
end
end