Skip to main content

sam4l/
dac.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Implementation of the SAM4L DACC.
6//!
7//! Ensure that the `ADVREFP` pin is tied to `ADDANA`.
8//!
9//! - Author: Justin Hsieh <hsiehju@umich.edu>
10//! - Date: May 26th, 2017
11
12use crate::pm::{self, Clock, PBAClock};
13use core::cell::Cell;
14use kernel::ErrorCode;
15use kernel::hil;
16use kernel::utilities::StaticRef;
17use kernel::utilities::registers::interfaces::{Readable, Writeable};
18use kernel::utilities::registers::{
19    ReadOnly, ReadWrite, WriteOnly, register_bitfields, register_structs,
20};
21
22register_structs! {
23    pub DacRegisters {
24        (0x000 => cr: WriteOnly<u32, Control::Register>),
25        (0x004 => mr: ReadWrite<u32, Mode::Register>),
26        (0x008 => cdr: WriteOnly<u32, ConversionData::Register>),
27        (0x00C => ier: WriteOnly<u32, InterruptEnable::Register>),
28        (0x010 => idr: WriteOnly<u32, InterruptDisable::Register>),
29        (0x014 => imr: ReadOnly<u32, InterruptMask::Register>),
30        (0x018 => isr: ReadOnly<u32, InterruptStatus::Register>),
31        (0x01C => _reserved0: [u32; 50]),
32        (0x0E4 => wpmr: ReadWrite<u32, WriteProtectMode::Register>),
33        (0x0E8 => wpsr: ReadOnly<u32, WriteProtectStatus::Register>),
34        (0x0EC => _reserved1: [u32; 4]),
35        (0x0FC => version: ReadOnly<u32, Version::Register>),
36        (0x100 => @END),
37    }
38}
39
40register_bitfields![u32,
41    Control [
42        SWRST 0
43    ],
44
45    /// Mode of the DAC peripheral.
46    Mode [
47        /// Clock divider for internal trigger
48        CLKDIV   OFFSET(16)  NUMBITS(16) [],
49        /// Startup time selection
50        STARTUP  OFFSET( 8)  NUMBITS(8) [],
51        /// Word transfer
52        WORD     OFFSET( 5)  NUMBITS(1) [
53            HalfWordTransfer = 0b0,
54            FullWordTransfer = 0b1
55        ],
56        /// DAC enable
57        DACEN    OFFSET( 4)  NUMBITS(1) [],
58        /// Trigger selection
59        TRGSEL   OFFSET( 1)  NUMBITS(3) [
60            ExternalTrigger = 0b000,
61            PeripheralTrigger = 0b001
62        ],
63        /// Trigger enable
64        TRGEN    OFFSET( 0)  NUMBITS(1) [
65            InternalTrigger = 0b0,
66            ExternalTrigger = 0b1
67        ]
68    ],
69
70    /// Conversion Data Register
71    ConversionData [
72        /// Data to convert
73        DATA OFFSET(0) NUMBITS(32) []
74    ],
75
76    /// Interupt Enable Register
77    InterruptEnable [
78        /// TX ready
79        TXRDY 0
80    ],
81
82    /// Interrupt Disable Register
83    InterruptDisable [
84        /// TX ready
85        TXRDY 0
86    ],
87
88    /// Interrupt Mask Register
89    InterruptMask [
90        /// TX ready
91        TXRDY 0
92    ],
93
94    /// Interrupt Status Register
95    InterruptStatus [
96        /// TX ready
97        TXRDY 0
98    ],
99
100    /// Write Protect Mode Register
101    WriteProtectMode [
102        /// Write protect key
103        WPKEY OFFSET(8) NUMBITS(24) [],
104        /// Write protect enable
105        WPEN OFFSET(0) NUMBITS(1) []
106    ],
107
108    /// Write Protect Status Register
109    WriteProtectStatus [
110        /// Write protection error address
111        WPROTADDR OFFSET(8) NUMBITS(8) [],
112        /// Write protection error
113        WPROTERR OFFSET(0) NUMBITS(1) []
114    ],
115
116    /// Version Register
117    Version [
118        VARIANT OFFSET(16) NUMBITS(3) [],
119        VERSION OFFSET( 0) NUMBITS(12) []
120    ]
121];
122
123// Page 59 of SAM4L data sheet
124const DAC_BASE: StaticRef<DacRegisters> =
125    unsafe { StaticRef::new(0x4003C000 as *const DacRegisters) };
126
127pub struct Dac {
128    registers: StaticRef<DacRegisters>,
129    enabled: Cell<bool>,
130}
131
132impl Dac {
133    pub const fn new() -> Self {
134        Self {
135            registers: DAC_BASE,
136            enabled: Cell::new(false),
137        }
138    }
139
140    fn initialize(&self) -> Result<(), ErrorCode> {
141        if !self.enabled.get() {
142            self.enabled.set(true);
143
144            // Start the APB clock (CLK_DACC)
145            pm::enable_clock(Clock::PBA(PBAClock::DACC));
146
147            // Reset DACC
148            self.registers.cr.write(Control::SWRST::SET);
149
150            // Set Mode Register
151            // -half-word transfer mode
152            // -start up time max (0xFF)
153            // -clock divider from 48 MHz to 500 kHz (0x60)
154            // -internal trigger
155            // -enable dacc
156            let mr = Mode::WORD::HalfWordTransfer
157                + Mode::STARTUP.val(0xff)
158                + Mode::CLKDIV.val(0x60)
159                + Mode::TRGEN::InternalTrigger
160                + Mode::DACEN::SET;
161            self.registers.mr.write(mr);
162        }
163        Ok(())
164    }
165
166    // Not currently using interrupt.
167    pub fn handle_interrupt(&self) {}
168}
169
170impl hil::dac::DacChannel for Dac {
171    fn set_value(&self, value: usize) -> Result<(), ErrorCode> {
172        if !self.enabled.get() {
173            self.initialize()?;
174        }
175
176        // Check if ready to write to CDR
177        if !self.registers.isr.is_set(InterruptStatus::TXRDY) {
178            return Err(ErrorCode::BUSY);
179        }
180
181        // Write to CDR
182        self.registers
183            .cdr
184            .write(ConversionData::DATA.val(value as u32));
185        Ok(())
186    }
187}