Skip to content

mspm0: add OPA driver with buffer and non-inverting PGA modes#6517

Open
ozongzi wants to merge 3 commits into
embassy-rs:mainfrom
ozongzi:mspm0-opa
Open

mspm0: add OPA driver with buffer and non-inverting PGA modes#6517
ozongzi wants to merge 3 commits into
embassy-rs:mainfrom
ozongzi:mspm0-opa

Conversation

@ozongzi

@ozongzi ozongzi commented Jul 15, 2026

Copy link
Copy Markdown

Adds a driver for the OPA on the MSPM0 parts that have one (G150x, G350x, L130x, L134x), scoped to the two modes from our discussion: buffer (TRM 17.2.7.2) and non-inverting PGA (17.2.7.3.2).

The register block comes from mspm0-rs/mspm0-data#37; the second commit bumps mspm0-metapac to the tag generated from it.

let mut opa = Opa::new(p.OPA1, Default::default());
let mut out = opa.pga_ext(p.PB19, p.PA16, Gain::X4);
let raw = adc.read_channel(&out.adc_channel()).await;
out.set_gain(Gain::X16); // auto-ranging

Shape notes:

  • _ext drives the OPAx_OUT pin, _int keeps the output internal to the ADC. Both return a handle that exposes the OPA's dedicated internal ADC channel and disable the amplifier on drop.
  • The non-inverting input is impl Into<NonInvertingInput>: either an OPAx_INy+ pin (per-role pin traits generated from the metapac pin metadata, so the PSEL channel comes from the pin you pass) or an internal source (dac12(), dac8(), vref(), ground()). IN2+ shares the DAC_OUT pad, which is why it maps to the DAC12 PSEL channel.
  • set_gain() works while the amplifier is running, since on this part gain is a single CFG field and runtime ranging is the main thing the stm32 API doesn't allow.
  • The OPA-output-to-ADC channel mapping isn't in mspm0-data (G: OPAx→ADCx ch13; L: single ADC, ch12/ch13), so build.rs carries a small per-family table taken from TI's SysConfig ADC12_internalConnections.js — same approach as embassy-stm32's opamp, where the OPAMPx→ADCx channel table lives in the HAL rather than stm32-data. Can move it into mspm0-data later if you'd rather have it as data.
  • Chopping is exposed in Config but defaults to off; the doc comment explains the ripple/ADC-averaging interaction.

The first commit is a standalone ADC fix this depends on: read_channel/read_sequence tied the channel's lifetime to the driver's 'd, which rejects a short-lived channel borrowing an OPA output. Can split it into its own PR if you want.

Testing: LP-MSPM0G3507, DAC_OUT (PA15) jumpered to OPA0_IN1+ (PA25). The DAC drives known codes and the OPA outputs are read back through their dedicated internal ADC channels (OPA0→ADC0, OPA1→ADC1, so both instances and both channel mappings are exercised). Each PGA step enters at x2 and is retuned with set_gain(), then verified by two-point slope so offsets stay out of the measurement: x2..x8 through the external pin path on OPA0, x16/x32 through the internal DAC12 route on OPA1. All five gains land within 2% of nominal. Also compile-checked on mspm0l1306 (single-ADC channel mapping) and mspm0c1104 (no OPA, module cfg'd out). Example included under examples/mspm0g3507.

Copilot AI review requested due to automatic review settings July 15, 2026 02:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

ozongzi added 3 commits July 15, 2026 10:56
read_channel/blocking_read/read_sequence required channels to live as
long as the Adc driver's 'd, which rejects short-lived channels such as
one borrowing an OPA output. A channel only needs to be alive for the
duration of the call.
Supports every MSPM0 with an OPA (G150x/G350x/L130x/L134x). Buffer and
non-inverting PGA (x2..x32) to start with, per discussion; inverting
PGA, general-purpose and cascade modes to follow one at a time. _ext
variants drive the OPAx_OUT pin, _int variants route the output only to
the ADC. The non-inverting input takes either an OPAx_INy+ pin or an
internal source (DAC12, COMP DAC8, VREF, ground), and gain can be
changed while running (auto-ranging).

Outputs expose the OPA's dedicated internal ADC channel; the mapping is
family-specific and comes from TI's SysConfig metadata.

Hardware-tested on LP-MSPM0G3507: every gain step verified by two-point
slope against the on-chip DAC (within 2% of nominal), the internal ADC
channel cross-checked against sampling the OUT pin.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants