stm32-hal2 2.1.10

Hardware abstraction layer for the STM32 MCUs
Documentation
//! Generates a sine waveform output from the DAC. Demonstrates using of timers to trigger
//! the DAC, and a DMA circular buffer to transfer data from memory to the DAC with minimal
//! CPU intervention.
//!
//! You can view output between ground and PA4 with an oscilloscope, or a pair of headphones or
//! speakers. If using headphones or speakers, make sure you can adjust their volume; the default
//! output volume will be too high.
//!
//! Tested on an STM32H743ZI, via STM32H7 Nucleo dev board.
//!
//! See [STM32 AN3126](https://www.st.com/resource/en/application_note/cd00259245-audio-and-waveform-generation-using-the-dac-in-stm32-products-stmicroelectronics.pdf),
//! "Audio and waveform generation using the DAC in STM32 products."

#![no_main]
#![no_std]
#![allow(non_snake_case)]

use core::sync::atomic::{AtomicUsize, Ordering};

use cortex_m::{self, delay::Delay};
use cortex_m_rt::entry;
use critical_section::with;
use defmt_rtt as _; // global logger
use hal::{
    self,
    clocks::Clocks,
    dac::{Dac, DacBits, DacChannel, Trigger},
    debug_workaround,
    dma::{self, Dma, DmaChannel},
    gpio::{OutputType, Pin, PinMode, Port},
    low_power, pac,
    timer::{BasicTimer, MasterModeSelection, TimerDevice},
};
use panic_probe as _;

// Length of the lookup table used to generate sin waves etc.
const LUT_LEN: usize = 256;

// A lookup table for sin(x), over one period, using values from 0 - 4095; ie the full range of
// the STM32 12-bit onboard DAC. Compared to computation, this is faster, at the expense of memory use.
pub static SIN_X: [u16; crate::LUT_LEN] = [
    2048, 2098, 2148, 2198, 2248, 2298, 2348, 2398, 2447, 2496, 2545, 2594, 2642, 2690, 2737, 2784,
    2831, 2877, 2923, 2968, 3013, 3057, 3100, 3143, 3185, 3226, 3267, 3307, 3346, 3385, 3423, 3459,
    3495, 3530, 3565, 3598, 3630, 3662, 3692, 3722, 3750, 3777, 3804, 3829, 3853, 3876, 3898, 3919,
    3939, 3958, 3975, 3992, 4007, 4021, 4034, 4045, 4056, 4065, 4073, 4080, 4085, 4089, 4093, 4094,
    4095, 4094, 4093, 4089, 4085, 4080, 4073, 4065, 4056, 4045, 4034, 4021, 4007, 3992, 3975, 3958,
    3939, 3919, 3898, 3876, 3853, 3829, 3804, 3777, 3750, 3722, 3692, 3662, 3630, 3598, 3565, 3530,
    3495, 3459, 3423, 3385, 3346, 3307, 3267, 3226, 3185, 3143, 3100, 3057, 3013, 2968, 2923, 2877,
    2831, 2784, 2737, 2690, 2642, 2594, 2545, 2496, 2447, 2398, 2348, 2298, 2248, 2198, 2148, 2098,
    2048, 1997, 1947, 1897, 1847, 1797, 1747, 1697, 1648, 1599, 1550, 1501, 1453, 1405, 1358, 1311,
    1264, 1218, 1172, 1127, 1082, 1038, 995, 952, 910, 869, 828, 788, 749, 710, 672, 636, 600, 565,
    530, 497, 465, 433, 403, 373, 345, 318, 291, 266, 242, 219, 197, 176, 156, 137, 120, 103, 88,
    74, 61, 50, 39, 30, 22, 15, 10, 6, 2, 1, 0, 1, 2, 6, 10, 15, 22, 30, 39, 50, 61, 74, 88, 103,
    120, 137, 156, 176, 197, 219, 242, 266, 291, 318, 345, 373, 403, 433, 465, 497, 530, 565, 600,
    636, 672, 710, 749, 788, 828, 869, 910, 952, 995, 1038, 1082, 1127, 1172, 1218, 1264, 1311,
    1358, 1405, 1453, 1501, 1550, 1599, 1648, 1697, 1747, 1797, 1847, 1897, 1947, 1997,
];

/// Set up the pins that have structs that don't need to be accessed after.
pub fn setup_pins() {
    // Set `dac_pin` to analog mode, to prevent parasitic power use.
    // DAC1_OUT1 is on PA4. DAC1_OUT2 is on PA5.
    let _dac_pin = Pin::new(Port::A, 4, PinMode::Analog);
}

#[entry]
fn main() -> ! {
    // Set up CPU peripherals
    let mut cp = cortex_m::Peripherals::take().unwrap();
    // Set up microcontroller peripherals
    let mut dp = pac::Peripherals::take().unwrap();

    // Set up clocks
    let clock_cfg = Clocks::default();
    clock_cfg
        .setup(&mut dp.RCC, &mut dp.FLASH, &mut dp.PWR, &mut dp.SYSCFG)
        .unwrap();

    debug_workaround();

    // Set up pins with appropriate modes.
    setup_pins();

    // The output frequency of the DAC.
    let out_wave_freq = 2000.;
    let timer_freq = out_wave_freq * LUT_LEN as f32;

    // Tim6 and Tim7 are internally connected to the DAC and are able to drive it through their
    // trigger outputs.
    // We set this timer to the sample rate in Hz.
    // This timer triggers a transfer of one word from the DMA buffer into the DAC output register.
    let mut dac_timer = BasicTimer::new(dp.TIM6, TimerDevice::T6, timer_freq, &clock_cfg);

    //  The update event is selected as a trigger output (TRGO). For instance a
    // master timer can then be used as a prescaler for a slave timer.
    dac_timer.set_mastermode(MasterModeSelection::Update);

    let mut delay = Delay::new(cp.SYST, clock_cfg.systick());

    let mut dac = Dac::new(dp.DAC, Default::default(), 3.3);
    dac.calibrate_buffer(DacChannel::C1, &mut delay);
    dac.set_trigger(DacChannel::C1, Trigger::Tim6);

    let mut dma = Dma::new(dp.DMA1);

    dma::mux(DmaChannel::C3, dma::DmaInput::DacCh1);

    // Load the Sine LUT into a DMA circular buffer, which will send a 16-byte word of data
    // to the DAC on each timer trigger. Because it's a circular buffer, it will start at
    // the first value again once complete.
    let channel_cfg = dma::ChannelCfg {
        circular: dma::Circular::Enabled,
        ..Default::default()
    };

    unsafe {
        dac.write_dma(
            &lut::SIN_X,
            DacChannel::C1,
            DmaChannel::C3,
            channel_cfg,
            &mut dma,
        );
    }

    dac.enable(DacChannel::C1);
    dac_timer.enable();

    loop {
        low_power::csleep(&mut cp.SCB);
    }
}

// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
fn panic() -> ! {
    cortex_m::asm::udf()
}