Skip to content

Commit c9c85a4

Browse files
authored
Improve/fix DAC sawtooth generation (#188)
* Improve/fix DAC sawtooth generation
1 parent b50e52c commit c9c85a4

File tree

4 files changed

+262
-18
lines changed

4 files changed

+262
-18
lines changed

examples/comp_w_dac.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn main() -> ! {
2929
// Set up DAC to output to pa4 and to internal signal Dac1IntSig1
3030
// which just so happens is compatible with comp1
3131
let dac1ch1 = dp.DAC1.constrain((gpioa.pa4, Dac1IntSig1), &mut rcc);
32-
let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable();
32+
let mut dac = dac1ch1.calibrate_buffer(&mut delay).enable(&mut rcc);
3333

3434
let (comp1, _comp2, ..) = dp.COMP.split(&mut rcc);
3535
let pa1 = gpioa.pa1.into_analog();

examples/dac.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ fn main() -> ! {
3131
let (dac1ch1, dac1ch2) = dp.DAC1.constrain((gpioa.pa4, gpioa.pa5), &mut rcc);
3232

3333
// dac_manual will have its value set manually
34-
let mut dac_manual = dac1ch1.calibrate_buffer(&mut delay).enable();
34+
let mut dac_manual = dac1ch1.calibrate_buffer(&mut delay).enable(&mut rcc);
3535

3636
// dac_generator will have its value set automatically from its internal noise generator
37-
let mut dac_generator = dac1ch2.enable_generator(GeneratorConfig::noise(11));
37+
let mut dac_generator = dac1ch2.enable_generator(GeneratorConfig::noise(11), &mut rcc);
3838

3939
enum Direction {
4040
Upcounting,

examples/dac_sawtooth.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//! ## Origin
2+
//!
3+
//! This code has been taken from the stm32g0xx-hal project and modified to support
4+
//! STM32G4xx MCUs.
5+
6+
// #![deny(warnings)]
7+
#![deny(unsafe_code)]
8+
#![no_main]
9+
#![no_std]
10+
11+
use embedded_hal::delay::DelayNs;
12+
use hal::dac::DacExt;
13+
use hal::delay::SYSTDelayExt;
14+
use hal::gpio::GpioExt;
15+
use hal::rcc::RccExt;
16+
use stm32g4xx_hal as hal;
17+
use stm32g4xx_hal::dac::{CountingDirection, SawtoothConfig};
18+
mod utils;
19+
extern crate cortex_m_rt as rt;
20+
21+
use hal::stm32;
22+
use rt::entry;
23+
24+
#[entry]
25+
fn main() -> ! {
26+
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
27+
let cp = cortex_m::Peripherals::take().expect("cannot take core peripherals");
28+
29+
let mut rcc = dp.RCC.constrain();
30+
let mut delay = cp.SYST.delay(&rcc.clocks);
31+
32+
let gpioa = dp.GPIOA.split(&mut rcc);
33+
let dac1ch1 = dp.DAC1.constrain(gpioa.pa4, &mut rcc);
34+
35+
// Just like the ADC, the DAC (by default) converts 12bit values between digital
36+
// and analog. We will configure the dac to automatically update its output
37+
// froms its internal sawtooth generator.
38+
//
39+
// Internally however, for the sawtooth generation the dac uses a
40+
// 16 bit counter where only the 12 msb are sent to the dac output and the 4 msb
41+
// are ignored by the output and only used for counting. The counter is reset to
42+
// `reset_value << 4` at every `trigger_reset`, meaning the output's 12 bits
43+
// will be set to `reset_value`.
44+
//
45+
// Every trigger_inc this 16 bit counter is incremented/decremented by the specified
46+
// step size. Since the 4 msb are not mapped to the output, for example using a step
47+
// size of 1 will only produce a change on the output every 2^4=16th trigger_inc.
48+
//
49+
// We will configure the sawtooth generator with a negative slope. This means that
50+
// the generator will subtract the step_size from the counter every trigger_inc.
51+
// This produces something like the following waveform.
52+
//
53+
// \ |\ |\ |\ |
54+
// \ | \ | \ | \ |
55+
// \ | \ | \ | \ |
56+
// \ | \ | \ | \ |
57+
// \ | \ | \ | \ |
58+
// \ | \ | \ | \ |
59+
// \ | \ | \ | \ |
60+
// \| \| \| \|
61+
let step_size = 16;
62+
let reset_value = 4000;
63+
let trigger_count = (reset_value << 4) / step_size;
64+
let delay_us = 1;
65+
66+
// dac_generator will have its value set automatically from its internal sawtooth generator
67+
let mut dac_generator = dac1ch1.enable_sawtooth_generator(
68+
SawtoothConfig::with_slope(CountingDirection::Decrement, step_size)
69+
.reset_value(reset_value),
70+
&mut rcc,
71+
);
72+
73+
loop {
74+
for _ in 0..trigger_count {
75+
dac_generator.trigger_inc();
76+
delay.delay_us(delay_us);
77+
}
78+
delay.delay_us(delay_us);
79+
dac_generator.trigger_reset();
80+
}
81+
}

src/dac.rs

Lines changed: 178 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,30 @@ use crate::stm32::RCC;
1717
use embedded_hal::delay::DelayNs;
1818

1919
pub trait DacOut<V> {
20+
fn get_value(&mut self) -> V;
21+
fn set_value(&mut self, val: V);
22+
}
23+
impl<V, D: DacOutGet<V> + DacOutSet<V>> DacOut<V> for D {
24+
fn get_value(&mut self) -> V {
25+
DacOutGet::get_value(self)
26+
}
27+
fn set_value(&mut self, val: V) {
28+
DacOutSet::set_value(self, val);
29+
}
30+
}
31+
pub trait DacOutSet<V> {
2032
fn set_value(&mut self, val: V);
33+
}
34+
pub trait DacOutGet<V> {
2135
fn get_value(&mut self) -> V;
2236
}
2337

38+
#[derive(Copy, Clone, Debug, PartialEq)]
39+
pub enum CountingDirection {
40+
Decrement = 0,
41+
Increment = 1,
42+
}
43+
2444
pub struct GeneratorConfig {
2545
mode: u8,
2646
amp: u8,
@@ -34,35 +54,102 @@ impl GeneratorConfig {
3454
}
3555
}
3656

37-
pub fn sawtooth(amplitude: u8) -> Self {
57+
pub fn noise(seed: u8) -> Self {
3858
Self {
39-
mode: 0b11,
40-
amp: amplitude,
59+
mode: 0b01,
60+
amp: seed,
4161
}
4262
}
63+
}
4364

44-
pub fn noise(seed: u8) -> Self {
65+
/// Used as regular trigger source and sawtooth generator reset trigger source
66+
///
67+
/// # Safety
68+
/// This trait should only be implemented for valid trigger sources with correct
69+
/// bit patterns.
70+
pub unsafe trait TriggerSource {
71+
/// See stinctrigsel in reference manual for bit patterns
72+
const BITS: u8;
73+
}
74+
75+
/// Used as sawtooth generator inc trigger source
76+
/// # Safety
77+
/// This trait should only be implemented for valid trigger sources with correct
78+
/// bit patterns.
79+
pub unsafe trait IncTriggerSource {
80+
/// See strsttrigsel in reference manual for bit patterns
81+
const BITS: u8;
82+
}
83+
84+
// https://www.youtube.com/watch?v=fUQaHyaXikw&ab_channel=STMicroelectronics
85+
#[derive(Debug, Clone, Copy)]
86+
pub struct SawtoothConfig {
87+
/// The sawtooth counter initial value (12 bits)
88+
reset_value: u16,
89+
90+
dir: CountingDirection,
91+
step_size: u16,
92+
93+
inc_trigger: u8,
94+
reset_trigger: u8,
95+
}
96+
97+
impl Default for SawtoothConfig {
98+
fn default() -> Self {
99+
Self::with_slope(CountingDirection::Decrement, 0x10)
100+
}
101+
}
102+
103+
impl SawtoothConfig {
104+
/// NOTE: The DAC output is used from 12 MSBs of the sawtooth counter so
105+
/// a step_size of 1 will only increment the output by 1 every 16th inc trigger
106+
pub const fn with_slope(dir: CountingDirection, step_size: u16) -> Self {
45107
Self {
46-
mode: 0b01,
47-
amp: seed,
108+
reset_value: u16::MAX,
109+
dir,
110+
step_size,
111+
inc_trigger: 0b0000, // SW trigger using `trigger_inc`-method
112+
reset_trigger: 0b0000, // SW trigger using `trigger_reset`-method
48113
}
49114
}
115+
116+
/// The sawtooth counter reset value (12 bits)
117+
pub const fn reset_value(mut self, reset_value: u16) -> Self {
118+
self.reset_value = reset_value;
119+
self
120+
}
121+
122+
pub const fn slope(mut self, dir: CountingDirection, step_size: u16) -> Self {
123+
self.dir = dir;
124+
self.step_size = step_size;
125+
self
126+
}
127+
128+
pub fn inc_trigger<T: IncTriggerSource>(mut self, _inc_trigger: T) -> Self {
129+
self.inc_trigger = T::BITS;
130+
self
131+
}
132+
133+
pub fn reset_trigger<T: TriggerSource>(mut self, _reset_trigger: T) -> Self {
134+
self.reset_trigger = T::BITS;
135+
self
136+
}
50137
}
51138

52139
/// Enabled DAC (type state)
53140
pub struct Enabled;
54141
// / Enabled DAC without output buffer (type state)
55142
//pub struct EnabledUnbuffered;
56-
/// Enabled DAC wave generator (type state)
143+
/// Enabled DAC wave generator for triangle or noise wave form (type state)
57144
pub struct WaveGenerator;
145+
/// Enabled DAC wave generator for sawtooth wave form (type state)
146+
pub struct SawtoothGenerator;
58147
/// Disabled DAC (type state)
59148
pub struct Disabled;
60149

61-
pub trait ED {}
62-
impl ED for Enabled {}
63-
//impl ED for EnabledUnbuffered {}
64-
impl ED for WaveGenerator {}
65-
impl ED for Disabled {}
150+
pub trait Generator {}
151+
impl Generator for WaveGenerator {}
152+
impl Generator for SawtoothConfig {}
66153

67154
pub trait Instance:
68155
rcc::Enable
@@ -177,7 +264,8 @@ impl_pin_for_dac!(
177264
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8> DacCh<DAC, CH, MODE_BITS, Disabled> {
178265
/// TODO: The DAC does not seem to work unless `calibrate_buffer` has been callen
179266
/// even when only using dac output internally
180-
pub fn enable(self) -> DacCh<DAC, CH, MODE_BITS, Enabled> {
267+
pub fn enable(self, _rcc: &mut Rcc) -> DacCh<DAC, CH, MODE_BITS, Enabled> {
268+
// We require rcc here just to ensure exclusive access to registers common to ch1 and ch2
181269
let dac = unsafe { &(*DAC::ptr()) };
182270

183271
dac.mcr()
@@ -190,7 +278,9 @@ impl<DAC: Instance, const CH: u8, const MODE_BITS: u8> DacCh<DAC, CH, MODE_BITS,
190278
pub fn enable_generator(
191279
self,
192280
config: GeneratorConfig,
281+
_rcc: &mut Rcc,
193282
) -> DacCh<DAC, CH, MODE_BITS, WaveGenerator> {
283+
// We require rcc here just to ensure exclusive access to registers common to ch1 and ch2
194284
let dac = unsafe { &(*DAC::ptr()) };
195285

196286
dac.mcr()
@@ -204,6 +294,45 @@ impl<DAC: Instance, const CH: u8, const MODE_BITS: u8> DacCh<DAC, CH, MODE_BITS,
204294

205295
DacCh::new()
206296
}
297+
298+
pub fn enable_sawtooth_generator(
299+
self,
300+
config: SawtoothConfig,
301+
_rcc: &mut Rcc,
302+
) -> DacCh<DAC, CH, MODE_BITS, SawtoothGenerator> {
303+
// TODO: We require rcc here just to ensure exclusive access to registers common to ch1 and ch2
304+
let dac = unsafe { &(*DAC::ptr()) };
305+
306+
dac.mcr()
307+
.modify(|_, w| unsafe { w.mode(CH).bits(MODE_BITS) });
308+
309+
unsafe {
310+
dac.stmodr().modify(|_, w| {
311+
w.stinctrigsel(CH)
312+
.bits(config.inc_trigger)
313+
.strsttrigsel(CH)
314+
.bits(config.reset_trigger)
315+
});
316+
}
317+
318+
dac.cr().modify(|_, w| unsafe { w.wave(CH).bits(0b11) });
319+
320+
unsafe {
321+
dac.str(CH as usize).write(|w| {
322+
w.stdir()
323+
.bit(config.dir == CountingDirection::Increment)
324+
.stincdata()
325+
.bits(config.step_size)
326+
.strstdata()
327+
.bits(config.reset_value)
328+
});
329+
}
330+
331+
dac.cr().modify(|_, w| w.en(CH).set_bit());
332+
while dac.sr().read().dacrdy(CH).bit_is_clear() {}
333+
334+
DacCh::new()
335+
}
207336
}
208337

209338
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8, ED> DacCh<DAC, CH, MODE_BITS, ED> {
@@ -252,21 +381,35 @@ impl<DAC: Instance, const CH: u8, const MODE_BITS: u8, ED> DacCh<DAC, CH, MODE_B
252381

253382
/// DacOut implementation available in any Enabled/Disabled
254383
/// state
255-
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8, ED> DacOut<u16>
256-
for DacCh<DAC, CH, MODE_BITS, ED>
384+
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8> DacOutSet<u16>
385+
for DacCh<DAC, CH, MODE_BITS, Enabled>
257386
{
258387
fn set_value(&mut self, val: u16) {
259388
let dac = unsafe { &(*DAC::ptr()) };
260389
dac.dhr12r(CH as usize)
261390
.write(|w| unsafe { w.bits(val as u32) });
262391
}
392+
}
263393

394+
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8, ED> DacOutGet<u16>
395+
for DacCh<DAC, CH, MODE_BITS, ED>
396+
{
264397
fn get_value(&mut self) -> u16 {
265398
let dac = unsafe { &(*DAC::ptr()) };
266399
dac.dor(CH as usize).read().bits() as u16
267400
}
268401
}
269402

403+
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8> DacOutSet<u16>
404+
for DacCh<DAC, CH, MODE_BITS, SawtoothGenerator>
405+
{
406+
fn set_value(&mut self, reset_value: u16) {
407+
let dac = unsafe { &(*<DAC>::ptr()) };
408+
dac.str(CH as usize)
409+
.modify(|_r, w| unsafe { w.strstdata().bits(reset_value) });
410+
}
411+
}
412+
270413
/// Wave generator state implementation
271414
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8> DacCh<DAC, CH, MODE_BITS, WaveGenerator> {
272415
pub fn trigger(&mut self) {
@@ -275,6 +418,26 @@ impl<DAC: Instance, const CH: u8, const MODE_BITS: u8> DacCh<DAC, CH, MODE_BITS,
275418
}
276419
}
277420

421+
/// Sawtooth generator state implementation
422+
impl<DAC: Instance, const CH: u8, const MODE_BITS: u8>
423+
DacCh<DAC, CH, MODE_BITS, SawtoothGenerator>
424+
{
425+
pub fn trigger_reset(&mut self) {
426+
let dac = unsafe { &(*<DAC>::ptr()) };
427+
dac.swtrgr().write(|w| w.swtrig(CH).set_bit());
428+
}
429+
430+
pub fn trigger_inc(&mut self) {
431+
let dac = unsafe { &(*<DAC>::ptr()) };
432+
// TODO: Update once arrayified
433+
if CH == 0 {
434+
dac.swtrgr().write(|w| w.swtrigb1().set_bit());
435+
} else {
436+
dac.swtrgr().write(|w| w.swtrigb2().set_bit());
437+
}
438+
}
439+
}
440+
278441
pub trait DacExt: Sized {
279442
fn constrain<PINS>(self, pins: PINS, rcc: &mut Rcc) -> PINS::Output
280443
where

0 commit comments

Comments
 (0)