Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions boards/nucleo_u545re_q/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct NucleoU545RE {
stm32u545::tim::Tim2<'static>,
>,
>,
dac: &'static capsules_extra::dac::Dac<'static>,
}

impl SyscallDriverLookup for NucleoU545RE {
Expand All @@ -64,6 +65,7 @@ impl SyscallDriverLookup for NucleoU545RE {
capsules_core::led::DRIVER_NUM => f(Some(self.led)),
capsules_core::button::DRIVER_NUM => f(Some(self.button)),
capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
capsules_extra::dac::DRIVER_NUM => f(Some(self.dac)),
_ => f(None),
}
}
Expand Down Expand Up @@ -122,6 +124,12 @@ unsafe fn set_pin_primary_functions(periphs: &stm32u545::chip::Stm32u5xxDefaultP
let btn = periphs.gpio_c.pin(PinId::Pin13);
btn.make_input();
btn.set_floating_state(kernel::hil::gpio::FloatingState::PullDown);

//DAC pin (PA4) A2 on the board
periphs
.gpio_a
.pin(PinId::Pin04)
.set_mode(stm32u545::gpio::Mode::Analog);
Comment on lines +127 to +132

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we have only one pin from the Arduino ones that can act as a DAC?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, only one routed per Channel, because I only implemented Ch1, it can only live on that pin.

}
Comment on lines +127 to 133

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can you write in the comment what pin is this from the board?


#[inline(never)]
Expand Down Expand Up @@ -233,6 +241,9 @@ unsafe fn start() -> (
)
.finalize(components::button_component_static!(stm32u545::gpio::Pin));

let dac = components::dac::DacComponent::new(&periphs.dac)
.finalize(components::dac_component_static!());

// Platform and Interrupts
let platform = static_init!(
NucleoU545RE,
Expand All @@ -244,6 +255,7 @@ unsafe fn start() -> (
led,
button,
alarm,
dac,
}
);

Expand Down
9 changes: 9 additions & 0 deletions chips/stm32u5xx/src/chip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Copyright Tock Contributors 2024.
// Copyright OxidOS Automotive 2026.

use crate::dac;
use crate::dma::{ChannelId, Dma};
use crate::exti;
use crate::gpio;
Expand Down Expand Up @@ -34,13 +35,19 @@ pub struct Stm32u5xxDefaultPeripherals<'a> {
pub dma1: &'a Dma,
pub gpio_a: gpio::Port<'a>,
pub gpio_c: gpio::Port<'a>,
pub dac: dac::Dac,
}

fn enable_tim2_clock() {
let rcc = rcc::Rcc::new(rcc::RCC_BASE);
rcc.enable_tim2();
}

fn enable_dac1_clock() {
let rcc = rcc::Rcc::new(rcc::RCC_BASE);
rcc.enable_dac1();
}

impl<'a> Stm32u5xxDefaultPeripherals<'a> {
pub fn new(usart1: &'a usart::Usart<'a>, exti: &'a exti::Exti<'a>, dma1: &'a Dma) -> Self {
Self {
Expand All @@ -51,6 +58,7 @@ impl<'a> Stm32u5xxDefaultPeripherals<'a> {
dma1,
gpio_a: gpio::Port::new(gpio::GPIO_A_BASE, exti, gpio::GpioPort::PortA),
gpio_c: gpio::Port::new(gpio::GPIO_C_BASE, exti, gpio::GpioPort::PortC),
dac: dac::Dac::new(dac::DAC_BASE, enable_dac1_clock),
}
}

Expand All @@ -62,6 +70,7 @@ impl<'a> Stm32u5xxDefaultPeripherals<'a> {
self.rcc.enable_usart1();
self.rcc.enable_syscfg();
self.rcc.set_usart1_source_pclk();
self.rcc.enable_dac1();
// Link DMA to USART1
let usart1_channel_tx = self.dma1.request_channel();
let usart1_channel_rx = self.dma1.request_channel();
Expand Down
268 changes: 268 additions & 0 deletions chips/stm32u5xx/src/dac.rs

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Please document this driver. We want to see some comments on how it is working.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure, I'll take care of that real quick.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Please use /// instead of // for the comments about registers in this driver.

Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2026.

use core::cell::Cell;
use kernel::hil::dac::DacChannel;
use kernel::utilities::registers::interfaces::{ReadWriteable, Writeable};
use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
use kernel::utilities::StaticRef;
use kernel::ErrorCode;

register_structs! {
pub DacRegisters {
/// control register
(0x000 => cr: ReadWrite<u32, CR::Register>),
/// software trigger register
(0x004 => swtrgr: ReadWrite<u32>),
/// channel1 12-bit right-aligned data holding register
(0x008 => dhr12r1: ReadWrite<u32>),
/// channel1 12-bit left-aligned data holding register
(0x00C => dhr12l1: ReadWrite<u32>),
/// channel1 8-bit right-aligned data holding register
(0x010 => dhr8r1: ReadWrite<u32>),
/// channel2 12-bit right-aligned data holding register
(0x014 => dhr12r2: ReadWrite<u32>),
/// channel2 12-bit left-aligned data holding register
(0x018 => dhr12l2: ReadWrite<u32>),
/// channel2 8-bit right-aligned data holding register
(0x01C => dhr8r2: ReadWrite<u32>),
/// dual dac 12-bit right-aligned data holding register
(0x020 => dhr12rd: ReadWrite<u32>),
/// dual dac 12-bit left-aligned data holding register
(0x024 => dhr12ld: ReadWrite<u32>),
/// dual dac 8-bit right-aligned data holding register
(0x028 => dhr8rd: ReadWrite<u32>),
/// channel1 data output register
(0x02C => dor1: ReadWrite<u32>),
/// channel2 data output register
(0x030 => dor2: ReadWrite<u32>),
/// status register
(0x034 => sr: ReadWrite<u32>),
/// calibration control register
(0x038 => ccr: ReadWrite<u32>),
/// mode control register
(0x03C => mcr: ReadWrite<u32, MCR::Register>),
/// channel1 sample and hold sample time register
(0x040 => shsr1: ReadWrite<u32>),
/// channel2 sample and hold sample time register
(0x044 => shsr2: ReadWrite<u32>),
/// sample and hold time register
(0x048 => shhr: ReadWrite<u32>),
/// sample and hold refresh time register
(0x04C => shrr: ReadWrite<u32>),
/// 0x050 reserved
(0x050 => _reserved0),
/// autonomous mode control register
(0x054 => autocr: ReadWrite<u32>),
(0x058 => @END),
}
}

register_bitfields! [u32,
pub CR [
/// channel1 enable
EN1 OFFSET(0) NUMBITS(1) [],
/// channel1 trigger enable
TEN1 OFFSET(1) NUMBITS(1) [],
/// channel1 trigger selection
TSEL1 OFFSET(2) NUMBITS(4) [
SWTRIG1 = 0,
dac_ch1_trg1 = 1,
dac_ch1_trg2 = 2,
dac_ch1_trg3 = 3,
dac_ch1_trg4 = 4,
dac_ch1_trg5 = 5,
dac_ch1_trg6 = 6,
dac_ch1_trg7 = 7,
dac_ch1_trg8 = 8,
dac_ch1_trg9 = 9,
dac_ch1_trg10 = 10,
dac_ch1_trg11 = 11,
dac_ch1_trg12 = 12,
dac_ch1_trg13 = 13,
dac_ch1_trg14 = 14,
dac_ch1_trg15 = 15,
],
/// channel1 noise/triangle wave generation
WAVE1 OFFSET(6) NUMBITS(2) [
disabled = 0,
noise_wave = 1,
triangle_wave = 2
],
/// mask in noise wave generation mode or ampl in triangle gen mode
MAMP1 OFFSET(8) NUMBITS(4) [
/// unmask bit0 of LFSR / triangle amplitude 1
AMP1 = 0,
/// unmask bits[1:0] of LFSR / triangle amplitude 3
AMP3 = 1,
/// unmask bits[2:0] of LFSR / triangle amplitude 7
AMP7 = 2,
/// unmask bits[3:0] of LFSR / triangle amplitude 15
AMP15 = 3,
/// unmask bits[4:0] of LFSR / triangle amplitude 31
AMP31 = 4,
/// unmask bits[5:0] of LFSR / triangle amplitude 63
AMP63 = 5,
/// unmask bits[6:0] of LFSR / triangle amplitude 127
AMP127 = 6,
/// unmask bits[7:0] of LFSR / triangle amplitude 255
AMP255 = 7,
/// unmask bits[8:0] of LFSR / triangle amplitude 511
AMP511 = 8,
/// unmask bits[9:0] of LFSR / triangle amplitude 1023
AMP1023 = 9,
/// unmask bits[10:0] of LFSR / triangle amplitude 2047
AMP2047 = 10,
/// unmask bits[11:0] of LFSR / triangle amplitude 4095 (>= 1011)
AMP4095 = 11,
],
/// channel 1 dma enable
DMAEN1 OFFSET(12) NUMBITS(1) [],
/// channel1 DMA underrun interrupt enable
DMAUDRIE1 OFFSET(13) NUMBITS(1) [],
/// channel1 calibration enable
CEN1 OFFSET(14) NUMBITS(1) [],
/// channel2 enable
EN2 OFFSET(16) NUMBITS(1) [],
/// channel2 trigger enable
TEN2 OFFSET(17) NUMBITS(1) [],
/// channel2 trigger selection
TSEL2 OFFSET(18) NUMBITS(4) [
SWTRIG2 = 0,
dac_ch2_trg1=1,
dac_ch2_trg2=2,
dac_ch2_trg3=3,
dac_ch2_trg4=4,
dac_ch2_trg5=5,
dac_ch2_trg6=6,
dac_ch2_trg7=7,
dac_ch2_trg8=8,
dac_ch2_trg9=9,
dac_ch2_trg10=10,
dac_ch2_trg11=11,
dac_ch2_trg12=12,
dac_ch2_trg13=13,
dac_ch2_trg14=14,
dac_ch2_trg15=15,
],
/// channel2 noise/triangle wave generation
WAVE2 OFFSET(22) NUMBITS(2) [
disabled=0,
noise_wave=1,
triangle_wave=2
],
/// mask in noise wave generation mode or ampl in triangle gen mode
MAMP2 OFFSET(24) NUMBITS(4) [
/// unmask bit0 of LFSR / triangle amplitude 1
AMP1 = 0,
/// unmask bits[1:0] of LFSR / triangle amplitude 3
AMP3 = 1,
/// unmask bits[2:0] of LFSR / triangle amplitude 7
AMP7 = 2,
/// unmask bits[3:0] of LFSR / triangle amplitude 15
AMP15 = 3,
/// unmask bits[4:0] of LFSR / triangle amplitude 31
AMP31 = 4,
/// unmask bits[5:0] of LFSR / triangle amplitude 63
AMP63 = 5,
/// unmask bits[6:0] of LFSR / triangle amplitude 127
AMP127 = 6,
/// unmask bits[7:0] of LFSR / triangle amplitude 255
AMP255 = 7,
/// unmask bits[8:0] of LFSR / triangle amplitude 511
AMP511 = 8,
/// unmask bits[9:0] of LFSR / triangle amplitude 1023
AMP1023 = 9,
/// unmask bits[10:0] of LFSR / triangle amplitude 2047
AMP2047 = 10,
/// unmask bits[11:0] of LFSR / triangle amplitude 4095 (>= 1011)
AMP4095 = 11,
],
/// dma channel2 enable
DMAEN2 OFFSET(28) NUMBITS(1) [],
/// ch2 dma underrun interrupt enable
DMAUDRIE2 OFFSET(29) NUMBITS(1) [],
/// ch2 calibration enable
CEN2 OFFSET(30) NUMBITS(1) []
],
/// mode control register
pub MCR [
/// dac ch1 mode
MODE1 OFFSET(0) NUMBITS(3) [
/// CH1 in normal mode
EXT_PIN_BUF_EN = 0,
EXT_PIN_PERI_BUF_EN = 1,
EXT_PIN_BUF_NEN = 2,
PERI_BUF_NEN = 3,
/// CH1 in sample and hold
SH_EXT_PIN_BUF_EN = 4,
SH_EXT_PIN_PERI_BUF_EN = 5,
SH_EXT_PIN_PERI_BUF_NEN = 6,
SH_PERI_BUF_NEN = 7
],
/// ch1 dma double mode
DMADOUBLE1 OFFSET(8) NUMBITS(1) [],
/// signed formar for ch1
SINFORMAT1 OFFSET(9) NUMBITS(1) [],
HFSET OFFSET(14) NUMBITS(2) [
disabled = 0,
enabled_ahb_80 = 1,
enabled_ahb_160 = 2
]
]
];

pub const DAC_BASE: StaticRef<DacRegisters> =
unsafe { StaticRef::new(0x4602_1800 as *const DacRegisters) };

/// The DAC takes in an arbitrary 12 bit number and outputs a voltage proportional to it.
pub struct Dac {
registers: StaticRef<DacRegisters>,
enable_clock: fn(),
initialized: Cell<bool>,
}

impl Dac {
/// Creates a new instance of the driver.
///
/// - `base`: The StaticRef pointing to the MMIO base address of the peripheral.
/// - `enable_clock`: A callback function to provide the peripheral clock via RCC.
/// - `initialized``: Bool cell that tracks whether the DAC has been initialized yet.
pub fn new(base: StaticRef<DacRegisters>, enable_clock: fn()) -> Self {
Self {
registers: base,
enable_clock,
initialized: Cell::new(false),
}
}

fn enable_clock(&self) {
(self.enable_clock)();
}

/// Initialization function that only gets called on first set_value write.
/// Because the HIL only exposes the set_value function, we have to store the initialization state as a bool to make sure we only initialize once.
/// Configures the MODE1 register and then enables CH1 of the DAC. MODE1 is set for clarity as it set to 0 regardless and that's what we need.
fn initialize(&self) {
self.enable_clock();
self.registers.mcr.modify(MCR::MODE1::EXT_PIN_BUF_EN);
self.registers.cr.modify(CR::EN1::SET);
self.initialized.set(true);
}
}

impl DacChannel for Dac {
/// Bound checks the value and if ok, writes it to the 12-bit right aligned DAC register after padding.
fn set_value(&self, value: usize) -> Result<(), ErrorCode> {
if !self.initialized.get() {
self.initialize();
}
if value > 0xFFF {
Err(ErrorCode::FAIL)
} else {
self.registers.dhr12r1.set((value as u32) & 0xFFF);
Ok(())
}
}
}
1 change: 1 addition & 0 deletions chips/stm32u5xx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![no_std]

pub mod chip;
pub mod dac;
pub mod dma;
pub mod exti;
pub mod gpio;
Expand Down
Loading
Loading