sam4l/
dac.rs

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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Implementation of the SAM4L DACC.
//!
//! Ensure that the `ADVREFP` pin is tied to `ADDANA`.
//!
//! - Author: Justin Hsieh <hsiehju@umich.edu>
//! - Date: May 26th, 2017

use crate::pm::{self, Clock, PBAClock};
use core::cell::Cell;
use kernel::hil;
use kernel::utilities::registers::interfaces::{Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite, WriteOnly};
use kernel::utilities::StaticRef;
use kernel::ErrorCode;

#[repr(C)]
pub struct DacRegisters {
    // From page 905 of SAM4L manual
    cr: WriteOnly<u32, Control::Register>, //             Control                       (0x00)
    mr: ReadWrite<u32, Mode::Register>,    //                Mode                          (0x04)
    cdr: WriteOnly<u32, ConversionData::Register>, //     Conversion Data Register      (0x08)
    ier: WriteOnly<u32, InterruptEnable::Register>, //    Interrupt Enable Register     (0x0c)
    idr: WriteOnly<u32, InterruptDisable::Register>, //   Interrupt Disable Register    (0x10)
    imr: ReadOnly<u32, InterruptMask::Register>, //       Interrupt Mask Register       (0x14)
    isr: ReadOnly<u32, InterruptStatus::Register>, //     Interrupt Status Register     (0x18)
    _reserved0: [u32; 50], //                                                               (0x1c - 0xe0)
    wpmr: ReadWrite<u32, WriteProtectMode::Register>, //  Write Protect Mode Register   (0xe4)
    wpsr: ReadOnly<u32, WriteProtectStatus::Register>, // Write Protect Status Register (0xe8)
    _reserved1: [u32; 4], //                                                                (0xec - 0xf8)
    version: ReadOnly<u32, Version::Register>, //         Version Register              (0xfc)
}

register_bitfields![u32,
    Control [
        SWRST 0
    ],

    /// Mode of the DAC peripheral.
    Mode [
        /// Clock divider for internal trigger
        CLKDIV   OFFSET(16)  NUMBITS(16) [],
        /// Startup time selection
        STARTUP  OFFSET( 8)  NUMBITS(8) [],
        /// Word transfer
        WORD     OFFSET( 5)  NUMBITS(1) [
            HalfWordTransfer = 0b0,
            FullWordTransfer = 0b1
        ],
        /// DAC enable
        DACEN    OFFSET( 4)  NUMBITS(1) [],
        /// Trigger selection
        TRGSEL   OFFSET( 1)  NUMBITS(3) [
            ExternalTrigger = 0b000,
            PeripheralTrigger = 0b001
        ],
        /// Trigger enable
        TRGEN    OFFSET( 0)  NUMBITS(1) [
            InternalTrigger = 0b0,
            ExternalTrigger = 0b1
        ]
    ],

    /// Conversion Data Register
    ConversionData [
        /// Data to convert
        DATA OFFSET(0) NUMBITS(32) []
    ],

    /// Interupt Enable Register
    InterruptEnable [
        /// TX ready
        TXRDY 0
    ],

    /// Interrupt Disable Register
    InterruptDisable [
        /// TX ready
        TXRDY 0
    ],

    /// Interrupt Mask Register
    InterruptMask [
        /// TX ready
        TXRDY 0
    ],

    /// Interrupt Status Register
    InterruptStatus [
        /// TX ready
        TXRDY 0
    ],

    /// Write Protect Mode Register
    WriteProtectMode [
        /// Write protect key
        WPKEY OFFSET(8) NUMBITS(24) [],
        /// Write protect enable
        WPEN OFFSET(0) NUMBITS(1) []
    ],

    /// Write Protect Status Register
    WriteProtectStatus [
        /// Write protection error address
        WPROTADDR OFFSET(8) NUMBITS(8) [],
        /// Write protection error
        WPROTERR OFFSET(0) NUMBITS(1) []
    ],

    /// Version Register
    Version [
        VARIANT OFFSET(16) NUMBITS(3) [],
        VERSION OFFSET( 0) NUMBITS(12) []
    ]
];

// Page 59 of SAM4L data sheet
const DAC_BASE: StaticRef<DacRegisters> =
    unsafe { StaticRef::new(0x4003C000 as *const DacRegisters) };

pub struct Dac {
    registers: StaticRef<DacRegisters>,
    enabled: Cell<bool>,
}

impl Dac {
    pub const fn new() -> Self {
        Self {
            registers: DAC_BASE,
            enabled: Cell::new(false),
        }
    }

    fn initialize(&self) -> Result<(), ErrorCode> {
        if !self.enabled.get() {
            self.enabled.set(true);

            // Start the APB clock (CLK_DACC)
            pm::enable_clock(Clock::PBA(PBAClock::DACC));

            // Reset DACC
            self.registers.cr.write(Control::SWRST::SET);

            // Set Mode Register
            // -half-word transfer mode
            // -start up time max (0xFF)
            // -clock divider from 48 MHz to 500 kHz (0x60)
            // -internal trigger
            // -enable dacc
            let mr = Mode::WORD::HalfWordTransfer
                + Mode::STARTUP.val(0xff)
                + Mode::CLKDIV.val(0x60)
                + Mode::TRGEN::InternalTrigger
                + Mode::DACEN::SET;
            self.registers.mr.write(mr);
        }
        Ok(())
    }

    // Not currently using interrupt.
    pub fn handle_interrupt(&self) {}
}

impl hil::dac::DacChannel for Dac {
    fn set_value(&self, value: usize) -> Result<(), ErrorCode> {
        if !self.enabled.get() {
            self.initialize()?;
        }

        // Check if ready to write to CDR
        if !self.registers.isr.is_set(InterruptStatus::TXRDY) {
            return Err(ErrorCode::BUSY);
        }

        // Write to CDR
        self.registers
            .cdr
            .write(ConversionData::DATA.val(value as u32));
        Ok(())
    }
}