nrf5x/
temperature.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//! Temperature sensor driver, nRF5X-family
6//!
7//! Generates a simple temperature measurement without sampling
8//!
9//! Authors
10//! -------------------
11//! * Niklas Adolfsson <niklasadolfsson1@gmail.com>
12//! * Fredrik Nilsson <frednils@student.chalmers.se>
13//! * Date: March 03, 2017
14
15use kernel::utilities::cells::OptionalCell;
16use kernel::utilities::registers::interfaces::{Readable, Writeable};
17use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite, WriteOnly};
18use kernel::utilities::StaticRef;
19use kernel::ErrorCode;
20
21const TEMP_BASE: StaticRef<TempRegisters> =
22    unsafe { StaticRef::new(0x4000C000 as *const TempRegisters) };
23
24#[repr(C)]
25struct TempRegisters {
26    /// Start temperature measurement
27    /// Address: 0x000 - 0x004
28    pub task_start: WriteOnly<u32, Task::Register>,
29    /// Stop temperature measurement
30    /// Address: 0x004 - 0x008
31    pub task_stop: WriteOnly<u32, Task::Register>,
32    /// Reserved
33    pub _reserved1: [u32; 62],
34    /// Temperature measurement complete, data ready
35    /// Address: 0x100 - 0x104
36    pub event_datardy: ReadWrite<u32, Event::Register>,
37    /// Reserved
38    // Note, `inten` register on nRF51 is ignored because it's not supported by nRF52
39    // And intenset and intenclr provide the same functionality
40    pub _reserved2: [u32; 128],
41    /// Enable interrupt
42    /// Address: 0x304 - 0x308
43    pub intenset: ReadWrite<u32, Intenset::Register>,
44    /// Disable interrupt
45    /// Address: 0x308 - 0x30c
46    pub intenclr: ReadWrite<u32, Intenclr::Register>,
47    /// Reserved
48    pub _reserved3: [u32; 127],
49    /// Temperature in °C (0.25° steps)
50    /// Address: 0x508 - 0x50c
51    pub temp: ReadOnly<u32, Temperature::Register>,
52    /// Reserved
53    pub _reserved4: [u32; 5],
54    /// Slope of piece wise linear function (nRF52 only)
55    /// Address 0x520 - 0x534
56    #[cfg(feature = "nrf52")]
57    pub a: [ReadWrite<u32, A::Register>; 6],
58    pub _reserved5: [u32; 2],
59    /// y-intercept of 5th piece wise linear function (nRF52 only)
60    /// Address: 0x540 - 0x554
61    #[cfg(feature = "nrf52")]
62    pub b: [ReadWrite<u32, B::Register>; 6],
63    pub _reserved6: [u32; 2],
64    /// End point of 1st piece wise linear function (nRF52 only)
65    /// Address: 0x560 - 0x570
66    #[cfg(feature = "nrf52")]
67    pub t: [ReadWrite<u32, B::Register>; 5],
68}
69
70register_bitfields! [u32,
71    /// Start task
72    Task [
73        ENABLE OFFSET(0) NUMBITS(1)
74    ],
75
76    /// Read event
77    Event [
78        READY OFFSET(0) NUMBITS(1)
79    ],
80
81    /// Enabled interrupt
82    Intenset [
83        DATARDY OFFSET(0) NUMBITS(1)
84    ],
85
86    /// Disable interrupt
87    Intenclr [
88        DATARDY OFFSET(0) NUMBITS(1)
89    ],
90
91    /// Temperature in °C (0.25° steps)
92    Temperature [
93        TEMP OFFSET(0) NUMBITS(32)
94    ],
95
96    /// Slope of piece wise linear function
97    A [
98        SLOPE OFFSET(0) NUMBITS(12)
99    ],
100
101    /// y-intercept of wise linear function
102    B [
103        INTERCEPT OFFSET(0) NUMBITS(14)
104    ],
105
106    /// End point of wise linear function
107    T [
108       PIECE OFFSET(0) NUMBITS(8)
109    ]
110];
111
112pub struct Temp<'a> {
113    registers: StaticRef<TempRegisters>,
114    client: OptionalCell<&'a dyn kernel::hil::sensors::TemperatureClient>,
115}
116
117impl<'a> Temp<'a> {
118    pub const fn new() -> Temp<'a> {
119        Temp {
120            registers: TEMP_BASE,
121            client: OptionalCell::empty(),
122        }
123    }
124
125    /// Temperature interrupt handler
126    pub fn handle_interrupt(&self) {
127        // disable interrupts
128        self.disable_interrupts();
129
130        // get temperature
131        // Result of temperature measurement in °C, 2's complement format, 0.25 °C steps
132        let temp = (self.registers.temp.get() as i32 * 100) / 4;
133
134        // stop measurement
135        self.registers.task_stop.write(Task::ENABLE::SET);
136
137        // disable interrupts
138        self.disable_interrupts();
139
140        // trigger callback with temperature
141        self.client.map(|client| client.callback(Ok(temp)));
142    }
143
144    fn enable_interrupts(&self) {
145        self.registers.intenset.write(Intenset::DATARDY::SET);
146    }
147
148    fn disable_interrupts(&self) {
149        self.registers.intenclr.write(Intenclr::DATARDY::SET);
150    }
151}
152
153impl<'a> kernel::hil::sensors::TemperatureDriver<'a> for Temp<'a> {
154    fn read_temperature(&self) -> Result<(), ErrorCode> {
155        self.enable_interrupts();
156        self.registers.event_datardy.write(Event::READY::CLEAR);
157        self.registers.task_start.write(Task::ENABLE::SET);
158        Ok(())
159    }
160
161    fn set_client(&self, client: &'a dyn kernel::hil::sensors::TemperatureClient) {
162        self.client.set(client);
163    }
164}