mcu_matlab/McuLib/m/mcuPorts.m
2025-11-07 14:52:52 +03:00

317 lines
15 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 mcuPorts
% Класс для управления портами ввода-вывода в маске Simulink
% Генерирует конфигурационные файлы для портов на основе параметров маски
methods(Static)
function write()
% Основная функция записи конфигурации портов в файлы
% Генерирует заголовочный файл и файл реализации для портов
block = gcb;
mask = Simulink.Mask.get(block);
% Пути к конфигурационным файлам
hPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper_conf.h');
cPath = fullfile(mcuPath.get('wrapperPath'), 'mcu_wrapper.c');
% Устанавливаем значения по умолчанию для неиспользуемых портов
mcuPorts.defaultUnused();
%% СОЗДАНИЕ КОНФИГУРАЦИИ ДЛЯ ВХОДНЫХ ПОРТОВ
prefixNumb = 'IN';
[widths, portPrefixes] = mcuPorts.getMaskNames('in');
% Генерируем текст для заголовочного файла и файла реализации
headerText = mcuPorts.addPortHeaderDefines('', widths, prefixNumb, portPrefixes);
cText = mcuPorts.addPortCDefines('', widths, prefixNumb, portPrefixes);
%% СОЗДАНИЕ КОНФИГУРАЦИИ ДЛЯ ВЫХОДНЫХ ПОРТОВ
prefixNumb = 'OUT';
[widths, portPrefixes] = mcuPorts.getMaskNames('out');
% Добавляем выходные порты к существующей конфигурации
headerText = mcuPorts.addPortHeaderDefines(headerText, widths, prefixNumb, portPrefixes);
cText = mcuPorts.addPortCDefines(cText, widths, prefixNumb, portPrefixes);
%% ЗАПИСЬ В ФАЙЛЫ
% Читаем существующее содержимое файлов
hCode = fileread(hPath);
hCode = regexprep(hCode, '\r\n?', '\n');
cCode = fileread(cPath);
cCode = regexprep(cCode, '\r\n?', '\n');
% Вставляем сгенерированную конфигурацию в заголовочный файл
code = editCode.insertSection(hCode, '// INPUT/OUTPUTS PARAMS', headerText.PARAMS);
code = editCode.insertSection(code, '// INPUT/OUTPUTS AUTO-PARAMS', headerText.AUTO_PARAMS);
% Записываем обновленный заголовочный файл
fid = fopen(hPath, 'w', 'n', 'UTF-8');
if fid == -1
error('Не удалось открыть файл для записи');
end
fwrite(fid, code);
fclose(fid);
% Вставляем сгенерированную конфигурацию в файл реализации
code = editCode.insertSection(cCode, '// INPUT/OUTPUTS AUTO-PARAMS', cText);
% Записываем обновленный файл реализации
fid = fopen(cPath, 'w', 'n', 'UTF-8');
if fid == -1
error('Не удалось открыть файл для записи');
end
fwrite(fid, code);
fclose(fid);
end
function [portwidth, defnames] = getMaskNames(port_prefix)
% Получение имен и ширин портов из параметров маски
% port_prefix - префикс портов ('in' или 'out')
% Возвращает ширины портов и их имена
block = gcb;
mask = Simulink.Mask.get(block);
% Получаем количество портов из спиннера
paramName = sprintf('%sNumb', port_prefix);
param = mask.getParameter(paramName);
numb = str2double(param.Value);
% Инициализируем массивы для имен и ширин портов
defnames = strings(1, numb);
portwidth = [];
% Читаем значения параметров для каждого порта
for i = 1:numb
% Получаем имя порта
paramName = sprintf('%s_port_%d_name', port_prefix, i);
param = mask.getParameter(paramName);
defnames(i) = param.Value;
% Получаем ширину порта
paramName = sprintf('%s_port_%d_width', port_prefix, i);
param = mask.getParameter(paramName);
portwidth(i) = str2double(param.Value);
end
end
function updateMask()
% Обновление видимости параметров портов в маске
% Вызывается при изменении количества портов
mcuPorts.updateMask_prefix('in');
mcuPorts.updateMask_prefix('out');
end
function defaultUnused()
% Установка значений по умолчанию для неиспользуемых портов
mcuPorts.defaultUnused_prefix('in');
mcuPorts.defaultUnused_prefix('out');
end
end
methods(Static, Access=private)
function updateMask_prefix(port_prefix)
% Обновление видимости параметров для конкретного типа портов
% port_prefix - префикс портов ('in' или 'out')
block = gcb;
mask = Simulink.Mask.get(block);
% Получаем текущее количество портов
paramName = sprintf('%sNumb', port_prefix);
param = mask.getParameter(paramName);
n = str2double(param.Value);
% Максимальное количество портов из диапазона параметра
maxPorts = param.Range(2);
% Обходим все возможные порты
for i = 1:maxPorts
% Формируем имена параметров для имени и ширины порта
paramDefName = sprintf('%s_port_%d_name', port_prefix, i);
paramWidthName = sprintf('%s_port_%d_width', port_prefix, i);
paramDef = mask.getParameter(paramDefName);
paramWidth = mask.getParameter(paramWidthName);
if i <= n
% Показываем параметры для используемых портов
paramDef.Visible = 'on';
paramWidth.Visible = 'on';
% Устанавливаем значения по умолчанию если пустые
if isempty(strtrim(paramDef.Value))
paramDef.Value = upper(port_prefix);
end
if isempty(strtrim(paramWidth.Value)) || strcmp(paramWidth.Value, '0')
paramWidth.Value = '16';
end
else
% Скрываем параметры для неиспользуемых портов
paramDef.Visible = 'off';
paramWidth.Visible = 'off';
end
end
end
function defaultUnused_prefix(port_prefix)
% Установка значений по умолчанию для неиспользуемых портов
% port_prefix - префикс портов ('in' или 'out')
block = gcb;
mask = Simulink.Mask.get(block);
% Получаем количество используемых портов
paramName = sprintf('%sNumb', port_prefix);
param = mask.getParameter(paramName);
numb = str2double(param.Value);
% Максимальное количество портов
maxPorts = param.Range(2);
% Устанавливаем значения по умолчанию для неиспользуемых портов
for i = numb+1:maxPorts
paramName = sprintf('%s_port_%d_name', port_prefix, i);
param = mask.getParameter(paramName);
param.Value = upper(port_prefix); % Имя по умолчанию
paramName = sprintf('%s_port_%d_width', port_prefix, i);
param = mask.getParameter(paramName);
param.Value = '16'; % Ширина по умолчанию
end
end
function headerText = addPortHeaderDefines(existingText, widths, prefixNumb, portPrefixes)
% Генерация define-макросов для заголовочного файла
% existingText - существующий текст конфигурации
% widths - вектор ширин портов
% prefixNumb - общий префикс ('IN' или 'OUT')
% portPrefixes - массив префиксов для каждого порта
% Возвращает структуру с PARAMS и AUTO_PARAMS
n = numel(widths);
upperPrefix = upper(prefixNumb);
% === БАЗОВЫЕ ПАРАМЕТРЫ ===
lines = {
sprintf('#define %s_PORT_NUMB %d', upperPrefix, n)
};
% Добавляем define для ширины каждого порта
for i = 1:n
lines{end+1} = sprintf('#define %s_PORT_%d_WIDTH %d', ...
upper(portPrefixes{i}), i, widths(i));
end
newParams = strjoin(lines, newline);
newParams = [newParams, newline];
% === АВТОМАТИЧЕСКИ ГЕНЕРИРУЕМЫЕ ПАРАМЕТРЫ ===
lines = {};
% Формируем выражение для общего размера буфера
sumExprParts = cell(1,n);
for i = 1:n
sumExprParts{i} = sprintf('%s_PORT_%d_WIDTH', upper(portPrefixes{i}), i);
end
sumExpr = strjoin(sumExprParts, ' + ');
lines{end+1} = sprintf('/// === Полный размер буфера ===');
lines{end+1} = sprintf('#define TOTAL_%s_SIZE (%s)', upperPrefix, sumExpr);
lines{end+1} = '';
lines{end+1} = sprintf('/// === Смещения массивов (внутри общего буфера) ===');
% Генерируем define для смещений каждого массива в буфере
for i = 1:n
if i == 1
lines{end+1} = sprintf('#define OFFSET_%s_ARRAY_1 0', upperPrefix);
else
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);
% === ОБЪЕДИНЕНИЕ С СУЩЕСТВУЮЩИМ ТЕКСТОМ ===
if isfield(existingText, 'PARAMS')
headerText.PARAMS = [existingText.PARAMS, newline, newParams];
else
headerText.PARAMS = newParams;
end
if isfield(existingText, 'AUTO_PARAMS')
headerText.AUTO_PARAMS = [existingText.AUTO_PARAMS, newline, newAuto, newline];
else
headerText.AUTO_PARAMS = [newAuto, newline];
end
end
function cText = addPortCDefines(existingText, widths, prefixNumb, portPrefixes)
% Генерация кода для файла реализации (.c)
% existingText - существующий текст
% widths - вектор ширин портов
% prefixNumb - общий префикс ('IN' или 'OUT')
% portPrefixes - массив префиксов для каждого порта
% Возвращает текст для вставки в .c файл
n = numel(widths);
upperPrefix = upper(prefixNumb);
lowerPrefix = lower(prefixNumb);
lines = {};
% === ТАБЛИЦА ДЛИН МАССИВОВ ===
lines{end+1} = '/**';
lines{end+1} = sprintf(' * @brief Таблица длин массивов %s', upperPrefix);
lines{end+1} = ' */';
lines{end+1} = sprintf('const int %sLengths[%s_PORT_NUMB] = {', lowerPrefix, upperPrefix);
% Заполняем таблицу длин
for i = 1:n
comma = ',';
if i == n
comma = '';
end
lines{end+1} = sprintf(' %s_PORT_%d_WIDTH%s', upper(portPrefixes{i}), i, comma);
end
lines{end+1} = '};';
% === ТАБЛИЦА СМЕЩЕНИЙ ===
lines{end+1} = '/**';
lines{end+1} = sprintf(' * @brief Таблица смещений в выходном массиве %s', upperPrefix);
lines{end+1} = ' */';
lines{end+1} = sprintf('const int %sOffsets[%s_PORT_NUMB] = {', lowerPrefix, upperPrefix);
% Заполняем таблицу смещений
for i = 1:n
comma = ',';
if i == n
comma = '';
end
lines{end+1} = sprintf(' OFFSET_%s_ARRAY_%d%s', upperPrefix, i, comma);
end
lines{end+1} = '};';
lines{end+1} = '';
newText = strjoin(lines, newline);
% Объединяем с существующим текстом
if nargin < 1 || isempty(existingText)
cText = newText;
else
cText = [existingText, newline, newText];
end
end
function val = iff(cond, a, b)
% Вспомогательная функция - тернарный оператор
% cond - условие, a - значение если true, b - значение если false
if cond
val = a;
else
val = b;
end
end
end
end