"""Parser for STM32CubeMX .ioc files (key=value format).""" import re # Matches GPIO pin names like PA0, PB12, PC3, PD2, PF0-OSC_IN, PB8-BOOT0 # Also handles CubeMX aliases like "PC14-OSC32_IN\ (PC14)" or "PA11\ [PA9]" _GPIO_RE = re.compile(r"^(P[A-K]\d+(?:-[A-Z_0-9]+)?)(?:\\\s*[\[(][^\])]*[\])])?\.(.*)") # Extracts remap aliases from Mcu.Pin entries, e.g. "PA11 [PA9]" → base=PA11, alias=PA9 _MCU_PIN_RE = re.compile( r"^(P[A-K]\d+)(?:-[A-Z_0-9]+)?\s*[\[(](P[A-K]\d+)[\])]$" ) def parse_ioc(ioc_path): """ Parse a .ioc file and return per-pin configuration and remap info. Returns: (pins, remaps) pins: {gpio_base_name: {property: value, ...}} remaps: {alias_gpio: base_gpio} e.g. {'PA9': 'PA11'} means PA11 is remapped to PA9's function — PA11 is the physical port for that pin. """ pins = {} remaps = {} with open(ioc_path, "r", encoding="utf-8") as fh: for raw in fh: line = raw.strip() if "=" not in line: continue key, _, value = line.partition("=") # Mcu.PinXX entries — extract remap aliases if key.startswith("Mcu.Pin"): m = _MCU_PIN_RE.match(value) if m and m.group(1) != m.group(2): # e.g. PA11 [PA9] → remaps["PA9"] = "PA11" remaps[m.group(2)] = m.group(1) continue m = _GPIO_RE.match(key) if not m: continue full_pin = m.group(1) # e.g. "PF0-OSC_IN" prop = m.group(2) # e.g. "Signal" base = full_pin.split("-")[0] # e.g. "PF0" pins.setdefault(base, {})[prop] = value return pins, remaps