# BMSv4_pincfg

> Modify an existing STM32CubeMX project for BMSv4 hardware pin and peripheral settings. Use when a CubeMX `.ioc` project already exists and BMSv4 SPI3, SPISW_nCSx / SPIADC_nCSx GPIO outputs, PWM outputs, CUR_poly ADC2 inputs, and specific ADC3 inputs must be configured, code regenerated, and the build re-verified.

- Skill: `myfro9/bmsv4-pincfg` (Agent Skill)
- Install (CLI): `npx skillmds@latest add myfro9/bmsv4-pincfg`
- Raw SKILL.md: https://api.skillmd.com/api/skills/myfro9/bmsv4-pincfg/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Myfro9 (https://skillmd.com/u/myfro9)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/myfro9/bmsv4-pincfg

---


# BMSv4 Pin Configuration

Apply the BMSv4 hardware pin/peripheral configuration to an existing STM32CubeMX project. Update the `.ioc`, regenerate code with the locally installed CubeMX, then verify the project still builds.

This skill is for modifying an existing CubeMX project only. Do not create a new project when using this skill.

## Preconditions
- Confirm the target workspace already contains a CubeMX `.ioc` file and generated STM32 project structure.
- Confirm CubeMX exists locally and detect the installed CubeMX version before editing `.ioc` metadata.
- Keep the existing project name, MCU, clock tree, toolchain, and user code sections unless the user explicitly asks to change them.
- Preserve `ProjectManager.KeepUserCode=true`.
- Treat the existing project as the baseline. Only change configuration items explicitly described in this skill or explicitly requested by the user.
- If the user asks to modify this skill itself, update only the skill file in that turn. Do not also modify any target STM32 project or `.ioc` unless the user separately asks to apply the updated skill.

## Required BMSv4 configuration

### SPI3
- Enable `SPI3`.
- Configure `SPI3` as `Half-Duplex Master`.
- Use `PC10` as `SPI3_SCK`.
- Use `PC12` as `SPI3_MOSI`.
- Disable hardware NSS and use software NSS.
- Parameters:
  - Frame format: `Motorola`
  - Data size: `8 Bits`
  - First bit: `MSB First`
  - Prescaler: `2`
  - CPOL: `Low`
  - CPHA: `1 Edge`
  - CRC calculation: `Disabled`

### Battery switch decoder chip selects
Configure these pins as `GPIO_Output`, label them exactly as listed, and default them to inactive high unless the user explicitly requests another reset state:

- `PE14` -> `SPISW_nCS1`
- `PE12` -> `SPISW_nCS2`
- `PE10` -> `SPISW_nCS3`
- `PE8` -> `SPISW_nCS4`
- `PG1` -> `SPISW_nCS5`
- `PF15` -> `SPISW_nCS6`

### Battery voltage ADC decoder chip selects
Configure these pins as `GPIO_Output`, label them exactly as listed, and default them to inactive high unless the user explicitly requests another reset state:

- `PE13` -> `SPIADC_nCS1`
- `PE11` -> `SPIADC_nCS2`
- `PE9` -> `SPIADC_nCS3`
- `PE7` -> `SPIADC_nCS4`
- `PG0` -> `SPIADC_nCS5`
- `PF14` -> `SPIADC_nCS6`

### PWM outputs
Configure the following pins as timer PWM outputs and label them exactly as listed:

- `PA9` -> `TIM1_CH2` -> `pwm_polyctl1`
- `PA8` -> `TIM1_CH1` -> `pwm_polyctl2`
- `PC9` -> `TIM3_CH4` -> `pwm_polyctl3`
- `PC8` -> `TIM3_CH3` -> `pwm_polyctl4`
- `PC7` -> `TIM3_CH2` -> `pwm_polyctl5`
- `PC6` -> `TIM3_CH1` -> `pwm_polyctl6`

Use PWM output mode with configurable duty cycle. Default duty cycle must be `50%`.

Timer settings for both `TIM1` and `TIM3`:
- Clock source: `Internal Clock`
- PWM frequency: `10 kHz`
- Compute timer parameters from the existing project clocks instead of guessing
- For the standard `STM32F407ZGTx` clock tree used in this workspace, `TIM1` and `TIM3` both see an `84 MHz` timer clock, so use:
  - Prescaler `PSC = 83`
  - Counter Period `ARR = 99`
  - Default Pulse `CCR = 50`
- Keep counter mode `Up`
- Keep clock division `No Division`
- Keep repetition counter `0` unless the selected timer requires a different default generated by CubeMX

### Current-sense ADC inputs
Configure the following six pins as `ADC2` analog inputs and label them exactly as listed:

- `PB1` -> `ADC2_IN9` -> `CUR_poly1`
- `PB0` -> `ADC2_IN8` -> `CUR_poly2`
- `PC5` -> `ADC2_IN15` -> `CUR_poly3`
- `PC4` -> `ADC2_IN14` -> `CUR_poly4`
- `PA7` -> `ADC2_IN7` -> `CUR_poly5`
- `PA6` -> `ADC2_IN6` -> `CUR_poly6`

Requirements for these ADC inputs:
- Configure each listed pin as `Analog`.
- Use `No pull-up and no pull-down`.
- Apply the exact labels `CUR_poly1..6`.
- Use `ADC2` for all six channels.
- Preserve any existing ADC peripheral configuration unless the user explicitly asks to change sampling sequences, trigger sources, DMA, scan mode, or conversion timing.
- If the target project does not yet enable an ADC instance for these channels, add the minimum ADC configuration required for CubeMX to keep the pin-to-channel mapping valid, without altering unrelated peripherals.
- Do not stop at pin signal mapping plus regular channel/rank fields. Add whatever CubeMX metadata the installed version requires so the ADC GUI actually shows the channels as selected.

Required `ADC2` parameters for these inputs:
- Clock Prescaler: `PCLK2 divided by 4`
- Resolution: `12 bits`
- Data Alignment: `Right alignment`
- Scan Conversion Mode:
  - keep `Disabled` when only one `CUR_poly` channel is configured into the ADC2 regular group
  - set `Enabled` when multiple `CUR_poly` channels are configured into the ADC2 regular group
- Continuous Conversion Mode: `Disabled`
- Discontinuous Conversion Mode: `Disabled`
- DMA Continuous Requests: `Disabled`
- End Of Conversion Selection: `EOC flag at the end of single channel conversion`
- Number Of Conversion: set this to the number of `CUR_poly` channels actually configured into the ADC2 regular group
- External Trigger Conversion Source: `Regular Conversion launched by software`
- External Trigger Conversion Edge: `None`
- Rank:
  - when one `CUR_poly` channel is configured, use `Rank = 1`
  - when multiple `CUR_poly` channels are configured, assign sequential regular ranks starting at `1`
- Injected Number Of Conversions: `0`
- Enable `ADC1, ADC2 and ADC3 global interrupts` in NVIC with preemption priority `0` and sub priority `0`

### Additional ADC3 inputs
Configure the following two pins as analog `ADC3` inputs and label them exactly as listed:

- `PA3` -> `ADC3_IN3` -> `lt3763_cur`
- `PF5` -> `ADC3_IN15` -> `ADC_VBAT_TMP`

Requirements for these ADC inputs (BMSv31-style ADC3 workflow):
- Configure each listed pin as `Analog`.
- Use `No pull-up and no pull-down`.
- Apply the exact labels `lt3763_cur` and `ADC_VBAT_TMP`.
- Use `ADC3` for both channels.
- Preserve any existing `ADC3` configuration unless the user explicitly asks to change sampling sequences, trigger sources, DMA, scan mode, or conversion timing.
- If the target project does not yet enable an `ADC3` instance for these channels, add the minimum ADC configuration required for CubeMX to keep the pin-to-channel mapping valid, without altering unrelated peripherals.
- Keep pin mapping for both channels, but do not require both channels to be simultaneously configured in the ADC3 regular group.
- Treat `ADC3_IN15` specially on `STM32F407ZGTx`: `PF5 -> ADC3_IN15` is a valid ADC3-only pin mapping in the device database, and it must not be modeled using the same shared-signal metadata pattern as `ADC2_IN15` on `PC5`.
- Do not reuse `SH.ADCx_IN15` entries that belong to `ADC1/ADC2` when configuring `PF5 -> ADC3_IN15`. If the installed CubeMX version uses a different metadata representation for ADC3-only `IN15`, preserve or generate that exact representation instead of forcing it into the ADC1/ADC2 shared-signal format.
- ADC3 working mode must follow BMSv31 style:
  - initialize ADC3 as single regular conversion (`Scan Conversion Mode = Disabled`, `Number Of Conversion = 1`, `Rank = 1`)
  - keep `Continuous Conversion Mode = Disabled`
  - keep `Discontinuous Conversion Mode = Disabled`
  - keep `DMA Continuous Requests = Disabled`
  - keep `End Of Conversion Selection = EOC flag at the end of single channel conversion`
  - use software trigger (`External Trigger Conversion Source = Regular Conversion launched by software`, `External Trigger Conversion Edge = None`)
  - do channel selection at runtime by reconfiguring `sConfig.Channel` before each conversion (e.g. switch between `ADC_CHANNEL_3` and `ADC_CHANNEL_15`)

## Execution steps
1. Detect the local CubeMX version and confirm the project `.ioc` header is not stamped with a newer version than the installed tool.
2. Open the existing `.ioc` project. Do not replace it with a newly created project.
3. Compare the existing project configuration against the configuration explicitly listed in this skill.
4. Modify only the listed configuration items that differ:
   - the `SPI3` mode and parameters listed above
   - the twelve named GPIO output definitions listed above
   - the six PWM pin assignments, timer channel mappings, timer base settings, and default duty-cycle settings listed above
   - the six `CUR_poly` ADC input pin assignments, analog modes, labels, required ADC2 channel mappings, and ADC parameters listed above
   - the two additional `ADC3` input pin assignments, analog modes, labels, and required ADC3 channel mappings listed above
   - when configuring ADC inputs, also add the CubeMX shared-signal metadata required by the installed version so the ADC page shows the intended `INx` entries as selected
   - when configuring `PF5 -> ADC3_IN15`, verify against the local device database that the mapping is ADC3-only and do not alias it to the `ADC1/ADC2 IN15` shared-signal entry used by `PC5`
   - for ADC3, keep the two pin mappings but configure ADC3 peripheral as single-conversion runtime-switch mode (BMSv31 style), not fixed multi-channel scan sequence
5. Keep every other existing project configuration unchanged if it is not explicitly covered by this skill, including unrelated peripherals, clocks, middleware, pins, NVIC settings, DMA settings, and user code.
6. If any requested pin conflicts with an existing peripheral assignment, stop and report the conflict instead of silently remapping the hardware.
7. Regenerate code using the installed CubeMX while preserving user code sections.
   - This is mandatory after any `.ioc` change. Do not skip regeneration.
8. Validate the generated result before finishing:
   - `Core/Src/spi.c` contains `MX_SPI3_Init()` with `SPI_DIRECTION_1LINE`, master mode, prescaler `2`, `SPI_POLARITY_LOW`, `SPI_PHASE_1EDGE`, software NSS, and CRC disabled.
   - `Core/Src/gpio.c` initializes the required `GPIOE`, `GPIOF`, and `GPIOG` chip-select pins as outputs.
   - `Core/Inc/main.h` exports the requested pin names and ports.
   - `Core/Src/tim.c` contains `MX_TIM1_Init()` and `MX_TIM3_Init()` with a `10 kHz` PWM base derived from the project timer clocks.
   - The generated compare values default to `50%` duty cycle for `pwm_polyctl1..6`.
   - The generated GPIO/pin metadata maps `PA9`, `PA8`, `PC9`, `PC8`, `PC7`, and `PC6` to the requested timer channels.
   - The generated GPIO/pin metadata maps `PB1`, `PB0`, `PC5`, `PC4`, `PA7`, and `PA6` to `ADC2_IN9`, `ADC2_IN8`, `ADC2_IN15`, `ADC2_IN14`, `ADC2_IN7`, and `ADC2_IN6` with labels `CUR_poly1..6`.
   - The generated GPIO/pin metadata maps `PA3` and `PF5` to `ADC3_IN3` and `ADC3_IN15` with labels `lt3763_cur` and `ADC_VBAT_TMP`.
   - The generated ADC configuration matches the required `ADC2` settings: `PCLK2/4`, `12-bit`, right alignment, continuous disabled, discontinuous disabled, DMA continuous requests disabled, single-channel EOC, software trigger, trigger edge none, and injected conversions `0`.
   - The ADC2 regular group explicitly includes the requested `CUR_poly` channels rather than only setting the pins to analog input mode.
   - `Scan Conversion Mode`, `Number Of Conversion`, and the regular channel `Rank` values are adjusted to match the number of `CUR_poly` channels actually configured in ADC2.
   - The `.ioc` contains the CubeMX shared ADC signal metadata needed for the locally installed version to render the requested ADC2 inputs as selected in the GUI, not just low-level regular channel fields.
   - Re-open or re-load the `.ioc` with the installed CubeMX and confirm the ADC GUI shows the expected `ADC2` input selections instead of leaving the `INx` list unchecked.
   - The `.ioc` contains the CubeMX shared ADC signal metadata needed for the locally installed version to render the requested ADC3 inputs as selected in the GUI, not just low-level pin signal fields.
   - Re-open or re-load the `.ioc` with the installed CubeMX and confirm `PA3 -> ADC3_IN3` and `PF5 -> ADC3_IN15` pin mappings are preserved without pin-level conflict warnings.
   - `Core/Src/adc.c` (or generated ADC3 init code) uses single regular conversion style for ADC3: scan disabled and number-of-conversion = 1.
   - ADC3 channel usage follows runtime channel reconfiguration style instead of fixed two-channel scan sequence.
   - `ADC3` `IN15` metadata must not overwrite or corrupt the existing `ADC2_IN15` representation used for `PC5`.
   - The generated GPIO settings for the six ADC pins remain `Analog mode` with `No pull-up and no pull-down`.
   - The generated GPIO settings for `PA3` and `PF5` remain `Analog mode` with `No pull-up and no pull-down`.
   - The NVIC enables `ADC1, ADC2 and ADC3 global interrupts` with preemption priority `0` and sub priority `0`.
   - If CubeMX generated or updated ADC init code, it does not silently remove unrelated existing ADC settings outside the channels required for `CUR_poly1..6`, `lt3763_cur`, and `ADC_VBAT_TMP`.
   - The `.ioc` still opens in the locally installed CubeMX version.
9. Run `cmake --preset Debug`.
10. Run `cmake --build --preset Debug`.
11. Confirm the expected ELF exists under `build/Debug/`.
12. Treat the task as failed if code generation was not executed or if build is not successful. Do not report completion in that case.

## Notes & guardrails
- Do not infer CubeMX compatibility from memory. Detect the installed version each time.
- Do not hand-edit generated C source first and then try to backfill the `.ioc`; the `.ioc` is the source of truth.
- Do not treat missing detail in this skill as permission to normalize or simplify the rest of the project.
- Do not remove or rewrite existing project configuration that is outside the explicit scope of this skill.
- Do not rename the GPIO labels. They must remain `SPISW_nCS1..6` and `SPIADC_nCS1..6`.
- Do not rename the ADC labels. They must remain `CUR_poly1..6`.
- Do not rename the additional ADC3 labels. They must remain `lt3763_cur` and `ADC_VBAT_TMP`.
- Do not guess PWM frequency parameters from screenshots alone. Derive PSC/ARR/CCR from the actual timer clock in the target project.
- Do not invent ADC parameters that are not explicitly shown or requested; keep unspecified ADC timing details at the generated or pre-existing values unless the user later constrains them.
- Do not leave ADC2 half-configured. It is not sufficient to set only pin analog mode and `ADC2_INx` signal mapping; the required regular ADC2 channels must also be present in the ADC2 configuration.
- Do not leave ADC3 half-configured. It is not sufficient to set only pin analog mode and `ADC3_INx` signal mapping; add the minimum ADC3 configuration and metadata needed for CubeMX to keep and display the requested channels.
- Do not force ADC3 into fixed multi-channel scan mode for these two channels when BMSv31-style behavior is requested.
- Do not assume `ADCx_IN15` metadata is interchangeable across ADC instances on this MCU family. `ADC2_IN15` on `PC5` and `ADC3_IN15` on `PF5` may require different `.ioc` representations.
- Do not add extra ADC channels, sampling logic, or conversion sequencing beyond what is needed to preserve the requested `CUR_poly1..6`, `lt3763_cur`, and `ADC_VBAT_TMP` pin mappings.
- For `_nCS` lines, prefer reset state high so external devices stay deselected after boot.
- If CubeMX generation removes or rewrites required user code outside the modified peripherals, stop and report before making manual source patches.
- If the project uses a non-`Debug` preset name, keep the workspace's existing preset strategy, but still verify a full configure + build path before finishing.
- Do not mark the task complete unless both conditions are met: CubeMX code generation completed successfully, and the project build completed successfully.

