diff options
| author | JuliDi <[email protected]> | 2023-06-19 13:42:25 +0200 |
|---|---|---|
| committer | JuliDi <[email protected]> | 2023-06-19 13:42:25 +0200 |
| commit | fe7b72948ab5d3682f23e305f3eb7186cc308b1b (patch) | |
| tree | 8870f86008252c280f94f94f1ef2a51d0b87d6e5 | |
| parent | e0747e937f06ed280594e17c97d19784410b6e85 (diff) | |
add ValueArray type and respective write functions
| -rw-r--r-- | embassy-stm32/src/dac/mod.rs | 210 |
1 files changed, 126 insertions, 84 deletions
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 525d45d72..4384a7c34 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -98,6 +98,14 @@ pub enum Value { | |||
| 98 | Bit12(u16, Alignment), | 98 | Bit12(u16, Alignment), |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 102 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 103 | pub enum ValueArray<'a> { | ||
| 104 | Bit8(&'a [u8]), | ||
| 105 | Bit12Left(&'a [u16]), | ||
| 106 | Bit12Right(&'a [u16]), | ||
| 107 | } | ||
| 108 | |||
| 101 | pub struct Dac<'d, T: Instance, Tx> { | 109 | pub struct Dac<'d, T: Instance, Tx> { |
| 102 | channels: u8, | 110 | channels: u8, |
| 103 | txdma: PeripheralRef<'d, Tx>, | 111 | txdma: PeripheralRef<'d, Tx>, |
| @@ -129,21 +137,19 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { | |||
| 129 | T::enable(); | 137 | T::enable(); |
| 130 | T::reset(); | 138 | T::reset(); |
| 131 | 139 | ||
| 132 | unsafe { | 140 | T::regs().mcr().modify(|reg| { |
| 133 | T::regs().mcr().modify(|reg| { | 141 | for ch in 0..channels { |
| 134 | for ch in 0..channels { | 142 | reg.set_mode(ch as usize, 0); |
| 135 | reg.set_mode(ch as usize, 0); | 143 | reg.set_mode(ch as usize, 0); |
| 136 | reg.set_mode(ch as usize, 0); | 144 | } |
| 137 | } | 145 | }); |
| 138 | }); | 146 | |
| 139 | 147 | T::regs().cr().modify(|reg| { | |
| 140 | T::regs().cr().modify(|reg| { | 148 | for ch in 0..channels { |
| 141 | for ch in 0..channels { | 149 | reg.set_en(ch as usize, true); |
| 142 | reg.set_en(ch as usize, true); | 150 | reg.set_ten(ch as usize, true); |
| 143 | reg.set_ten(ch as usize, true); | 151 | } |
| 144 | } | 152 | }); |
| 145 | }); | ||
| 146 | } | ||
| 147 | 153 | ||
| 148 | Self { | 154 | Self { |
| 149 | channels, | 155 | channels, |
| @@ -161,13 +167,12 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { | |||
| 161 | } | 167 | } |
| 162 | } | 168 | } |
| 163 | 169 | ||
| 170 | /// Set the enable register of the given channel | ||
| 164 | fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { | 171 | fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { |
| 165 | self.check_channel_exists(ch)?; | 172 | self.check_channel_exists(ch)?; |
| 166 | unsafe { | 173 | T::regs().cr().modify(|reg| { |
| 167 | T::regs().cr().modify(|reg| { | 174 | reg.set_en(ch.index(), on); |
| 168 | reg.set_en(ch.index(), on); | 175 | }); |
| 169 | }) | ||
| 170 | } | ||
| 171 | Ok(()) | 176 | Ok(()) |
| 172 | } | 177 | } |
| 173 | 178 | ||
| @@ -179,112 +184,149 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { | |||
| 179 | self.set_channel_enable(ch, false) | 184 | self.set_channel_enable(ch, false) |
| 180 | } | 185 | } |
| 181 | 186 | ||
| 187 | /// Performs all register accesses necessary to select a new trigger for CH1 | ||
| 182 | pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { | 188 | pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { |
| 183 | self.check_channel_exists(Channel::Ch1)?; | 189 | self.check_channel_exists(Channel::Ch1)?; |
| 184 | unwrap!(self.disable_channel(Channel::Ch1)); | 190 | unwrap!(self.disable_channel(Channel::Ch1)); |
| 185 | unsafe { | 191 | T::regs().cr().modify(|reg| { |
| 186 | T::regs().cr().modify(|reg| { | 192 | reg.set_tsel1(trigger.tsel()); |
| 187 | reg.set_tsel1(trigger.tsel()); | 193 | }); |
| 188 | }) | ||
| 189 | } | ||
| 190 | Ok(()) | 194 | Ok(()) |
| 191 | } | 195 | } |
| 192 | 196 | ||
| 197 | /// Performs all register accesses necessary to select a new trigger for CH2 | ||
| 193 | pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { | 198 | pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { |
| 194 | self.check_channel_exists(Channel::Ch2)?; | 199 | self.check_channel_exists(Channel::Ch2)?; |
| 195 | unwrap!(self.disable_channel(Channel::Ch2)); | 200 | unwrap!(self.disable_channel(Channel::Ch2)); |
| 196 | unsafe { | 201 | T::regs().cr().modify(|reg| { |
| 197 | T::regs().cr().modify(|reg| { | 202 | reg.set_tsel2(trigger.tsel()); |
| 198 | reg.set_tsel2(trigger.tsel()); | 203 | }); |
| 199 | }) | ||
| 200 | } | ||
| 201 | Ok(()) | 204 | Ok(()) |
| 202 | } | 205 | } |
| 203 | 206 | ||
| 207 | /// Perform a software trigger on a given channel | ||
| 204 | pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { | 208 | pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { |
| 205 | self.check_channel_exists(ch)?; | 209 | self.check_channel_exists(ch)?; |
| 206 | unsafe { | 210 | T::regs().swtrigr().write(|reg| { |
| 207 | T::regs().swtrigr().write(|reg| { | 211 | reg.set_swtrig(ch.index(), true); |
| 208 | reg.set_swtrig(ch.index(), true); | 212 | }); |
| 209 | }); | ||
| 210 | } | ||
| 211 | Ok(()) | 213 | Ok(()) |
| 212 | } | 214 | } |
| 213 | 215 | ||
| 216 | /// Perform a software trigger on all channels | ||
| 214 | pub fn trigger_all(&mut self) { | 217 | pub fn trigger_all(&mut self) { |
| 215 | unsafe { | 218 | T::regs().swtrigr().write(|reg| { |
| 216 | T::regs().swtrigr().write(|reg| { | 219 | reg.set_swtrig(Channel::Ch1.index(), true); |
| 217 | reg.set_swtrig(Channel::Ch1.index(), true); | 220 | reg.set_swtrig(Channel::Ch2.index(), true); |
| 218 | reg.set_swtrig(Channel::Ch2.index(), true); | 221 | }); |
| 219 | }) | ||
| 220 | } | ||
| 221 | } | 222 | } |
| 222 | 223 | ||
| 223 | pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { | 224 | pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { |
| 224 | self.check_channel_exists(ch)?; | 225 | self.check_channel_exists(ch)?; |
| 225 | match value { | 226 | match value { |
| 226 | Value::Bit8(v) => unsafe { | 227 | Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), |
| 227 | T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)); | 228 | Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), |
| 228 | }, | 229 | Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), |
| 229 | Value::Bit12(v, Alignment::Left) => unsafe { | ||
| 230 | T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)); | ||
| 231 | }, | ||
| 232 | Value::Bit12(v, Alignment::Right) => unsafe { | ||
| 233 | T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)); | ||
| 234 | }, | ||
| 235 | } | 230 | } |
| 236 | Ok(()) | 231 | Ok(()) |
| 237 | } | 232 | } |
| 238 | 233 | ||
| 234 | /// Write `data` to the DAC via DMA. | ||
| 235 | /// | ||
| 236 | /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. | ||
| 237 | /// This will configure a circular DMA transfer that periodically outputs the `data`. | ||
| 238 | /// | ||
| 239 | /// ## Current limitations | ||
| 240 | /// - Only CH1 Supported | ||
| 241 | /// | ||
| 239 | /// TODO: Allow an array of Value instead of only u16, right-aligned | 242 | /// TODO: Allow an array of Value instead of only u16, right-aligned |
| 240 | pub async fn write(&mut self, data: &[u16], circular: bool) -> Result<(), Error> | 243 | pub async fn write_8bit(&mut self, data_ch1: &[u8], circular: bool) -> Result<(), Error> |
| 244 | where | ||
| 245 | Tx: Dma<T>, | ||
| 246 | { | ||
| 247 | self.write_inner(ValueArray::Bit8(data_ch1), circular).await | ||
| 248 | } | ||
| 249 | |||
| 250 | pub async fn write_12bit_right_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> | ||
| 251 | where | ||
| 252 | Tx: Dma<T>, | ||
| 253 | { | ||
| 254 | self.write_inner(ValueArray::Bit12Right(data_ch1), circular).await | ||
| 255 | } | ||
| 256 | |||
| 257 | pub async fn write_12bit_left_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> | ||
| 258 | where | ||
| 259 | Tx: Dma<T>, | ||
| 260 | { | ||
| 261 | self.write_inner(ValueArray::Bit12Left(data_ch1), circular).await | ||
| 262 | } | ||
| 263 | |||
| 264 | async fn write_inner(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> | ||
| 241 | where | 265 | where |
| 242 | Tx: Dma<T>, | 266 | Tx: Dma<T>, |
| 243 | { | 267 | { |
| 244 | // TODO: Make this a parameter or get it from the struct or so... | 268 | // TODO: Make this a parameter or get it from the struct or so... |
| 245 | const CHANNEL: usize = 0; | 269 | const CHANNEL: usize = 0; |
| 246 | 270 | ||
| 247 | //debug!("Starting DAC"); | 271 | // Enable DAC and DMA |
| 248 | unsafe { | 272 | T::regs().cr().modify(|w| { |
| 249 | T::regs().cr().modify(|w| { | 273 | w.set_en(CHANNEL, true); |
| 250 | w.set_en(CHANNEL, true); | 274 | w.set_dmaen(CHANNEL, true); |
| 251 | w.set_dmaen(CHANNEL, true); | 275 | }); |
| 252 | }); | ||
| 253 | } | ||
| 254 | 276 | ||
| 255 | let tx_request = self.txdma.request(); | 277 | let tx_request = self.txdma.request(); |
| 256 | 278 | ||
| 257 | // Use the 12 bit right-aligned register for now. TODO: distinguish values | 279 | // Initiate the correct type of DMA transfer depending on what data is passed |
| 258 | let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16; | 280 | let tx_f = match data_ch1 { |
| 259 | 281 | ValueArray::Bit8(buf) => unsafe { | |
| 260 | let tx_f = unsafe { | 282 | Transfer::new_write( |
| 261 | Transfer::new_write( | 283 | &mut self.txdma, |
| 262 | &mut self.txdma, | 284 | tx_request, |
| 263 | tx_request, | 285 | buf, |
| 264 | data, | 286 | T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8, |
| 265 | tx_dst, | 287 | TransferOptions { |
| 266 | TransferOptions { | 288 | circular, |
| 267 | circular, | 289 | halt_transfer_ir: false, |
| 268 | halt_transfer_ir: false, | 290 | }, |
| 269 | }, | 291 | ) |
| 270 | ) | 292 | }, |
| 293 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 294 | Transfer::new_write( | ||
| 295 | &mut self.txdma, | ||
| 296 | tx_request, | ||
| 297 | buf, | ||
| 298 | T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16, | ||
| 299 | TransferOptions { | ||
| 300 | circular, | ||
| 301 | halt_transfer_ir: false, | ||
| 302 | }, | ||
| 303 | ) | ||
| 304 | }, | ||
| 305 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 306 | Transfer::new_write( | ||
| 307 | &mut self.txdma, | ||
| 308 | tx_request, | ||
| 309 | buf, | ||
| 310 | T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16, | ||
| 311 | TransferOptions { | ||
| 312 | circular, | ||
| 313 | halt_transfer_ir: false, | ||
| 314 | }, | ||
| 315 | ) | ||
| 316 | }, | ||
| 271 | }; | 317 | }; |
| 272 | 318 | ||
| 273 | //debug!("Awaiting tx_f"); | ||
| 274 | |||
| 275 | tx_f.await; | 319 | tx_f.await; |
| 276 | 320 | ||
| 277 | // finish dma | 321 | // finish dma |
| 278 | unsafe { | 322 | // TODO: Do we need to check any status registers here? |
| 279 | // TODO: Do we need to check any status registers here? | 323 | T::regs().cr().modify(|w| { |
| 280 | 324 | // Disable the DAC peripheral | |
| 281 | T::regs().cr().modify(|w| { | 325 | w.set_en(CHANNEL, false); |
| 282 | // Disable the dac peripheral | 326 | // Disable the DMA. TODO: Is this necessary? |
| 283 | w.set_en(CHANNEL, false); | 327 | w.set_dmaen(CHANNEL, false); |
| 284 | // Disable the DMA. TODO: Is this necessary? | 328 | }); |
| 285 | w.set_dmaen(CHANNEL, false); | 329 | |
| 286 | }); | ||
| 287 | } | ||
| 288 | Ok(()) | 330 | Ok(()) |
| 289 | } | 331 | } |
| 290 | } | 332 | } |
