1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
//! Timer_B0 **and Timer_A** pulse-width modulation
//! (`embedded_hal::pwm::SetDutyCycle`).
//!
//! Where [`crate::timer`] *reads* a free-running counter to measure time, PWM
//! *drives* a pin: the same kind of counter, but each tick is compared against
//! two thresholds and the comparison toggles an output pin in hardware — no CPU
//! involvement once it is set up. The result is a square wave whose **duty
//! cycle** (high-time ÷ period) you control by writing one register, which is
//! how you dim an LED, set a servo angle, or synthesize an analog level through
//! a filter.
//!
//! # How one Timer_B0 makes up to six PWM channels
//!
//! Timer0_B7 has a 16-bit counter `TB0R` and **seven** capture/compare
//! registers, `TB0CCR0..TB0CCR6`. Run the counter in **up mode** (`MC = 1`): it
//! counts `0, 1, … TB0CCR0, 0, 1, …`. So `TB0CCR0` is special — it sets the
//! **period** (the counter's top). That leaves `TB0CCR1..6` as **six
//! independent duty-cycle comparators**, all sharing the one period. Each drives
//! its own pin `TB0.1..TB0.6`.
//!
//! Each channel's output is generated by its `OUTMOD` field. We use
//! **`OUTMOD = 7` (Reset/Set)**: the output is *set* (high) when the counter
//! wraps to 0 at the top, and *reset* (low) when the counter reaches that
//! channel's `TB0CCRn`. So the pin is high for `TB0CCRn` ticks out of every
//! `TB0CCR0 + 1` — i.e. **duty = `TB0CCRn / (TB0CCR0 + 1)`**. Raising `TB0CCRn`
//! widens the pulse; that single write is the whole API.
//!
//! ```text
//! counter: 0 ............ CCRn ............ CCR0 (top) ┐ wrap
//! output: ┌──────────────┐ └─ set ──► high again
//! │ high └──── low ───────────────────┘
//! └ set at wrap reset at CCRn
//! ```
//!
//! # Clean 0% and 100%
//!
//! `OUTMOD = 7` cannot quite reach the rails: `CCRn = 0` still glitches high for
//! one tick at the wrap, and there is no value that holds the pin high across a
//! whole period without a one-tick low. `embedded-hal` requires
//! [`set_duty_cycle(0)`](embedded_hal::pwm::SetDutyCycle::set_duty_cycle) to be
//! *fully* off and `set_duty_cycle(max)` *fully* on, so at those two endpoints we
//! switch the channel to **`OUTMOD = 0` (output the `OUT` bit)** and park `OUT`
//! low or high — a constant level with no edges. Everything in between uses the
//! Reset/Set comparator.
//!
//! # Resolution vs. frequency
//!
//! The period `TB0CCR0` is also the **duty resolution**: a period of 8000 gives
//! 8000 distinct duty steps. Period and PWM frequency trade off directly —
//! `f_pwm = f_clock / ((TB0CCR0 + 1) · divider)` — so a higher PWM frequency
//! (shorter period) means coarser duty steps. [`Pwm::new_smclk`] picks the
//! input divider (`ID` × `TBIDEX`, ÷1…÷64) that yields the **largest** period
//! fitting 16 bits for your target frequency, i.e. the finest resolution
//! achievable at that frequency.
//!
//! # Channel ↔ pin map (MSP430FR5969, Timer_B0)
//!
//! | Channel | `TB0.n` | Pin | Shared with |
//! |---------|---------|-------|--------------------|
//! | 1 | TB0.1 | P1.4 | UCA0STE / A4 |
//! | 2 | TB0.2 | P1.5 | UCA0CLK / A5 |
//! | 3 | TB0.3 | P1.6 | **eUSCI_B0 SIMO/SDA** |
//! | 4 | TB0.4 | P1.7 | **eUSCI_B0 SOMI/SCL** |
//!
//! `TB0.1` (P1.4) and `TB0.2` (P1.5) are the clean choices — they are broken out
//! on the LaunchPad headers and collide with nothing. `TB0.3`/`TB0.4` live on
//! the P1.6/P1.7 pins the SPI and I2C demos use, so the typestate lets you pick
//! one role or the other, never both at once. (`TB0.0`/`TB0.5`/`TB0.6` exist on
//! other pins but `TB0.0` is consumed as the period, so it is not a usable PWM
//! output here.)
//!
//! # Timer_A PWM ([`PwmTimerA`]) — the same machinery on TA0/TA1
//!
//! Timer_A blocks generate PWM exactly the same way (up mode, `CCR0` period,
//! `OUTMOD = 7` per channel), and [`PwmTimerA`] drives it on **TA0 or TA1**
//! (instance-generic over [`crate::capture::Instance`], the capture module's
//! raw-pointer trait — a Timer_A3 block has `CCR1`/`CCR2`, so two outputs per
//! block). What this buys:
//!
//! - **PWM without Timer_B0** — TB0 stays free, and with it the P1.4–P1.7
//! pins; in particular PWM no longer has to contend with **eUSCI_B0** for
//! P1.6/P1.7 (the SPI/I2C conflict the TB0 table above describes).
//! - PWM **and** capture concurrently: PWM on one Timer_A block while the
//! other captures (each block is consumed by move — PWM-vs-capture on the
//! *same* block stays exclusive, and `TA0` PWM also excludes
//! [`crate::timer::Counter`], which lives on TA0).
//!
//! | Output | Pin | Shared with |
//! |--------|------|---------------------------------|
//! | TA0.1 | P1.0 | green LED2 / TA0.CCI1A |
//! | TA0.2 | P1.1 | button S2 / REFOUT / TA0.CCI2A |
//! | TA1.1 | P1.2 | TA1.CCI1A (otherwise free) |
//! | TA1.2 | P1.3 | TA1.CCI2A (otherwise free) |
//!
//! **TA1 is the natural PWM block** (P1.2/P1.3 are free pins); avoid driving
//! P1.1 while button S2 could be pressed — the LaunchPad wires S2 straight to
//! ground, so a pressed button shorts the driven-high pad.
//!
//! # Example
//!
//! ```ignore
//! let clocks = hal::clocks::configure(p.cs); // SMCLK = 8 MHz
//! hal::gpio::unlock_pins(&p.pmm);
//! let (port1, _port2) = p.port_1_2.split();
//! let p14 = port1.pin4.into_timer_b_output(); // P1.4 → TB0.1
//! let pwm = Pwm::new_smclk(p.timer_0_b7, &clocks, 1_000); // ~1 kHz
//! let mut ch = pwm.channel(p14);
//! ch.set_duty_cycle_percent(25).ok(); // 25% brightness
//! ```
use Infallible;
use PhantomData;
use ;
use crateInstance;
use crateClocks;
use crate;
use cratepac;
/// A Timer_B0 PWM generator in up mode. Owns the Timer0_B7 peripheral and fixes
/// the shared **period** (and thus the PWM frequency and duty resolution); vends
/// per-pin [`PwmChannel`]s that each control one duty cycle.
/// One Timer_B0 PWM channel: a single duty-cycle output on `TB0.n`.
///
/// Created by [`Pwm::channel`]. Holds only the channel index and the shared
/// period; it reaches its `TB0CCRn`/`TB0CCTLn` registers through a stolen
/// peripheral handle. That is sound because every channel touches **only its
/// own** compare register and control word — disjoint from the period register
/// the [`Pwm`] owns and from every other channel — the same disjoint-register
/// pattern [`crate::timer`]'s ISR helpers use.
// ---------------------------------------------------------------------------
// Input-divider selection
// ---------------------------------------------------------------------------
/// The integer divisor the `ID` field code 0..=3 represents (÷1, ÷2, ÷4, ÷8).
const
/// Choose `(ID code, TBIDEX code, period)` for `freq_hz` from `clock_hz`.
///
/// The total input divider is `ID (1/2/4/8) × TBIDEX (1..8)`, ÷1…÷64. We want
/// the **smallest** total divider whose resulting period fits 16 bits, because a
/// smaller divider leaves a larger period — and the period is the duty
/// resolution. Falls back to the coarsest divider (and accepts a frequency
/// error) if even ÷64 cannot fit the period, and clamps the period to ≥ 1.
const
// ---------------------------------------------------------------------------
// Typed Timer_B0 output pins → channel numbers
// ---------------------------------------------------------------------------
/// A pin routed to a Timer_B0 output (`TB0.n`). Implemented only for the
/// [`TimerB`]-mode pins that correspond to real silicon channels; the associated
/// constant is that channel's `TB0CCRn` index (1..=6). Sealed — the set of
/// `TB0.n` pins is fixed by the package.
// MSP430FR5969 Timer_B0 outputs reachable with SEL = 01 (datasheet SLAS704G
// Port P1 function table). P1.6/P1.7 also carry eUSCI_B0 SIMO/SDA & SOMI/SCL —
// the typestate makes that an exclusive choice.
pwm_pins!
// ---------------------------------------------------------------------------
// Timer_A PWM (TA0/TA1, instance-generic)
// ---------------------------------------------------------------------------
//
// Same up-mode/OUTMOD machinery as Timer_B0 above, on the two pin-connected
// Timer_A3 blocks. Register access is raw pointers off the capture module's
// `Instance::BASE` (the PWM path touches registers the same way `capture`
// does, and the trait already fixes the base addresses and the
// consume-by-move exclusivity). Offsets and field values are the Timer_A
// map (SLAU367P): identical layout to Timer_B for everything PWM needs.
const TA_CCTL0: usize = 0x02; // TAxCCTL0 (CCTLn = CCTL0 + 2n)
const TA_CCR0: usize = 0x12; // TAxCCR0 (CCRn = CCR0 + 2n)
const TA_EX0: usize = 0x20; // TAxEX0 (TAIDEX second-stage divider)
// TAxCTL: SMCLK source, ID divider at bits 7:6, up mode, counter clear.
const TA_TASSEL_SMCLK: u16 = 0x0200;
const TA_MC_UP: u16 = 0x0010;
const TA_TACLR: u16 = 0x0004;
// TAxCCTLn whole-word values for the three output states. A full write also
// zeroes CAP (compare mode) and every interrupt bit — exactly the clean
// state a PWM channel wants.
const TA_OUTMOD7: u16 = 0x00E0; // Reset/Set comparator
const TA_PARK_LOW: u16 = 0x0000; // OUTMOD 0, OUT = 0
const TA_PARK_HIGH: u16 = 0x0004; // OUTMOD 0, OUT = 1
/// A Timer_A PWM generator in up mode — [`Pwm`]'s instance-generic sibling
/// for **TA0/TA1** (two outputs per block, `TAx.1`/`TAx.2`; `CCR0` is the
/// shared period). Owns the PAC peripheral by move, so PWM-vs-capture-vs-
/// [`crate::timer::Counter`] on one block is an exclusive choice.
/// One Timer_A PWM channel: a single duty-cycle output on `TAx.n`.
///
/// Created by [`PwmTimerA::channel`]. Holds only the channel index and the
/// shared period; register access is raw pointers to **only this channel's**
/// `TAxCCRn`/`TAxCCTLn` — disjoint from the period register the generator
/// owns and from the sibling channel (the [`PwmChannel`] soundness argument,
/// with addresses instead of a stolen PAC handle).
/// A pin routed to a Timer_A compare output (`TAx.n`), in output-direction
/// [`TimerA`] mode via [`Pin::into_timer_a_output`](crate::gpio::Pin::
/// into_timer_a_output). Parameterized by the timer instance so the
/// pin↔block wiring is checked at compile time; the associated constant is
/// the `TAxCCRn` channel. Sealed — the map is fixed by the package.
// The same pads that serve as TAx.CCInA capture inputs drive TAx.n when the
// direction is output (SLAS704G Port P1 function table) — see the Timer_A
// table in the module docs for the LaunchPad sharing caveats (P1.0 = green
// LED, P1.1 = button S2).
pwm_pins_a!