aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32g0/src
diff options
context:
space:
mode:
authorDaniel Trnka <[email protected]>2024-12-22 17:37:20 +0100
committerDaniel Trnka <[email protected]>2024-12-22 19:55:21 +0100
commit7070f5364eb903c6c6b0b28348a582f5a9e1f89e (patch)
tree518fea56cfe7114db024bc7891ae8010dec2d358 /examples/stm32g0/src
parenta7983668da2947f7846f0abc4736708539aedc42 (diff)
examples/stm32g0: added ds18b20 temperature sensor on 1-wire bus
Diffstat (limited to 'examples/stm32g0/src')
-rw-r--r--examples/stm32g0/src/bin/onewire_ds18b20.rs271
1 files changed, 271 insertions, 0 deletions
diff --git a/examples/stm32g0/src/bin/onewire_ds18b20.rs b/examples/stm32g0/src/bin/onewire_ds18b20.rs
new file mode 100644
index 000000000..f85cc4ff8
--- /dev/null
+++ b/examples/stm32g0/src/bin/onewire_ds18b20.rs
@@ -0,0 +1,271 @@
1//! This examples shows how you can use buffered or DMA UART to read a DS18B20 temperature sensor on 1-Wire bus.
2//! Connect 5k pull-up resistor between PA9 and 3.3V.
3#![no_std]
4#![no_main]
5
6use cortex_m::singleton;
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_stm32::mode::Async;
10use embassy_stm32::usart::{
11 BufferedUartRx, BufferedUartTx, Config, ConfigError, HalfDuplexConfig, RingBufferedUartRx, UartTx,
12};
13use embassy_stm32::{bind_interrupts, peripherals, usart};
14use embassy_time::{Duration, Timer};
15use {defmt_rtt as _, panic_probe as _};
16
17/// Create onewire bus using DMA USART
18fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<UartTx<'static, Async>, RingBufferedUartRx<'static>> {
19 use embassy_stm32::usart::Uart;
20 bind_interrupts!(struct Irqs {
21 USART1 => usart::InterruptHandler<peripherals::USART1>;
22 });
23
24 let usart = Uart::new_half_duplex(
25 p.USART1,
26 p.PA9,
27 Irqs,
28 p.DMA1_CH1,
29 p.DMA1_CH2,
30 Config::default(),
31 // Enable readback so we can read sensor pulling data low while transmission is in progress
32 usart::HalfDuplexReadback::Readback,
33 HalfDuplexConfig::OpenDrainExternal,
34 )
35 .unwrap();
36
37 const BUFFER_SIZE: usize = 16;
38 let (tx, rx) = usart.split();
39 let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
40 let rx = rx.into_ring_buffered(rx_buf);
41 OneWire::new(tx, rx)
42}
43
44/*
45/// Create onewire bus using buffered USART
46fn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<BufferedUartTx<'static>, BufferedUartRx<'static>> {
47 use embassy_stm32::usart::BufferedUart;
48 bind_interrupts!(struct Irqs {
49 USART1 => usart::BufferedInterruptHandler<peripherals::USART1>;
50 });
51
52 const BUFFER_SIZE: usize = 16;
53 let tx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
54 let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(RX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();
55 let usart = BufferedUart::new_half_duplex(
56 p.USART1,
57 p.PA9,
58 Irqs,
59 tx_buf,
60 rx_buf,
61 Config::default(),
62 // Enable readback so we can read sensor pulling data low while transmission is in progress
63 usart::HalfDuplexReadback::Readback,
64 HalfDuplexConfig::OpenDrainExternal,
65 )
66 .unwrap();
67 let (tx, rx) = usart.split();
68 OneWire::new(tx, rx)
69}
70*/
71
72#[embassy_executor::main]
73async fn main(_spawner: Spawner) {
74 let p = embassy_stm32::init(Default::default());
75
76 let onewire = create_onewire(p);
77 let mut sensor = Ds18b20::new(onewire);
78
79 loop {
80 // Start a new temperature measurement
81 sensor.start().await;
82 // Wait for the measurement to finish
83 Timer::after(Duration::from_secs(1)).await;
84 match sensor.temperature().await {
85 Ok(temp) => info!("temp = {:?} deg C", temp),
86 _ => error!("sensor error"),
87 }
88 Timer::after(Duration::from_secs(1)).await;
89 }
90}
91
92pub trait SetBaudrate {
93 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError>;
94}
95impl SetBaudrate for BufferedUartTx<'_> {
96 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
97 BufferedUartTx::set_baudrate(self, baudrate)
98 }
99}
100impl SetBaudrate for BufferedUartRx<'_> {
101 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
102 BufferedUartRx::set_baudrate(self, baudrate)
103 }
104}
105impl SetBaudrate for RingBufferedUartRx<'_> {
106 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
107 RingBufferedUartRx::set_baudrate(self, baudrate)
108 }
109}
110impl SetBaudrate for UartTx<'_, Async> {
111 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
112 UartTx::set_baudrate(self, baudrate)
113 }
114}
115
116/// Simplified OneWire bus driver
117pub struct OneWire<TX, RX>
118where
119 TX: embedded_io_async::Write + SetBaudrate,
120 RX: embedded_io_async::Read + SetBaudrate,
121{
122 tx: TX,
123 rx: RX,
124}
125
126impl<TX, RX> OneWire<TX, RX>
127where
128 TX: embedded_io_async::Write + SetBaudrate,
129 RX: embedded_io_async::Read + SetBaudrate,
130{
131 // bitrate with one bit taking ~104 us
132 const RESET_BUADRATE: u32 = 9600;
133 // bitrate with one bit taking ~8.7 us
134 const BAUDRATE: u32 = 115200;
135
136 // startbit + 8 low bits = 9 * 1/115200 = 78 us low pulse
137 const LOGIC_1_CHAR: u8 = 0xFF;
138 // startbit only = 1/115200 = 8.7 us low pulse
139 const LOGIC_0_CHAR: u8 = 0x00;
140
141 // Address all devices on the bus
142 const COMMAND_SKIP_ROM: u8 = 0xCC;
143
144 pub fn new(tx: TX, rx: RX) -> Self {
145 Self { tx, rx }
146 }
147
148 fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {
149 self.tx.set_baudrate(baudrate)?;
150 self.rx.set_baudrate(baudrate)
151 }
152
153 /// Reset the bus by at least 480 us low pulse.
154 pub async fn reset(&mut self) {
155 // Switch to 9600 baudrate, so one bit takes ~104 us
156 self.set_baudrate(Self::RESET_BUADRATE).expect("set_baudrate failed");
157 // Low USART start bit + 4x low bits = 5 * 104 us = 520 us low pulse
158 self.tx.write(&[0xF0]).await.expect("write failed");
159
160 // Read the value on the bus
161 let mut buffer = [0; 1];
162 self.rx.read_exact(&mut buffer).await.expect("read failed");
163
164 // Switch back to 115200 baudrate, so one bit takes ~8.7 us
165 self.set_baudrate(Self::BAUDRATE).expect("set_baudrate failed");
166
167 // read and expect sensor pulled some high bits to low (device present)
168 if buffer[0] & 0xF != 0 || buffer[0] & 0xF0 == 0xF0 {
169 warn!("No device present");
170 }
171 }
172
173 /// Send byte and read response on the bus.
174 pub async fn write_read_byte(&mut self, byte: u8) -> u8 {
175 // One byte is sent as 8 UART characters
176 let mut tx = [0; 8];
177 for (pos, char) in tx.iter_mut().enumerate() {
178 *char = if (byte >> pos) & 0x1 == 0x1 {
179 Self::LOGIC_1_CHAR
180 } else {
181 Self::LOGIC_0_CHAR
182 };
183 }
184 self.tx.write_all(&tx).await.expect("write failed");
185
186 // Readback the value on the bus, sensors can pull logic 1 to 0
187 let mut rx = [0; 8];
188 self.rx.read_exact(&mut rx).await.expect("read failed");
189 let mut bus_byte = 0;
190 for (pos, char) in rx.iter().enumerate() {
191 // if its 0xFF, sensor didnt pull the bus to low level
192 if *char == 0xFF {
193 bus_byte |= 1 << pos;
194 }
195 }
196
197 bus_byte
198 }
199
200 /// Read a byte from the bus.
201 pub async fn read_byte(&mut self) -> u8 {
202 self.write_read_byte(0xFF).await
203 }
204}
205
206/// DS18B20 temperature sensor driver
207pub struct Ds18b20<TX, RX>
208where
209 TX: embedded_io_async::Write + SetBaudrate,
210 RX: embedded_io_async::Read + SetBaudrate,
211{
212 bus: OneWire<TX, RX>,
213}
214
215impl<TX, RX> Ds18b20<TX, RX>
216where
217 TX: embedded_io_async::Write + SetBaudrate,
218 RX: embedded_io_async::Read + SetBaudrate,
219{
220 /// Start a temperature conversion.
221 const FN_CONVERT_T: u8 = 0x44;
222 /// Read contents of the scratchpad containing the temperature.
223 const FN_READ_SCRATCHPAD: u8 = 0xBE;
224
225 pub fn new(bus: OneWire<TX, RX>) -> Self {
226 Self { bus }
227 }
228
229 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
230 pub async fn start(&mut self) {
231 self.bus.reset().await;
232 self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await;
233 self.bus.write_read_byte(Self::FN_CONVERT_T).await;
234 }
235
236 /// Calculate CRC8 of the data
237 fn crc8(data: &[u8]) -> u8 {
238 let mut temp;
239 let mut data_byte;
240 let mut crc = 0;
241 for b in data {
242 data_byte = *b;
243 for _ in 0..8 {
244 temp = (crc ^ data_byte) & 0x01;
245 crc >>= 1;
246 if temp != 0 {
247 crc ^= 0x8C;
248 }
249 data_byte >>= 1;
250 }
251 }
252 crc
253 }
254
255 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
256 pub async fn temperature(&mut self) -> Result<f32, ()> {
257 self.bus.reset().await;
258 self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await;
259 self.bus.write_read_byte(Self::FN_READ_SCRATCHPAD).await;
260
261 let mut data = [0; 9];
262 for byte in data.iter_mut() {
263 *byte = self.bus.read_byte().await;
264 }
265
266 match Self::crc8(&data) == 0 {
267 true => Ok(((data[1] as u16) << 8 | data[0] as u16) as f32 / 16.),
268 false => Err(()),
269 }
270 }
271}