Skip to content

Commit 1df5d43

Browse files
authored
Merge pull request #40 from braun-embedded/i2c-dma
Add support for using I2C via DMA
2 parents ca6dafb + 0b0a363 commit 1df5d43

File tree

5 files changed

+429
-31
lines changed

5 files changed

+429
-31
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ required-features = ["rt"]
109109
name = "button_irq_rtfm"
110110
required-features = ["rt"]
111111

112+
[[example]]
113+
name = "i2c_dma"
114+
required-features = ["rt","stm32l0x2"]
115+
112116
[[example]]
113117
name = "rtc"
114118
required-features = ["stm32l0x2"]

examples/i2c_dma.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//! I2C example with DMA
2+
//!
3+
//! Uses an ST VL53L0X ToF sensor.
4+
5+
6+
#![no_main]
7+
#![no_std]
8+
9+
10+
extern crate panic_semihosting;
11+
12+
13+
use core::pin::Pin;
14+
15+
use cortex_m::interrupt;
16+
use cortex_m_rt::entry;
17+
use stm32l0xx_hal::{
18+
prelude::*,
19+
dma::{
20+
self,
21+
DMA,
22+
},
23+
pac::{
24+
self,
25+
Interrupt,
26+
NVIC,
27+
},
28+
pwr::PWR,
29+
rcc::Config,
30+
};
31+
32+
33+
#[entry]
34+
fn main() -> ! {
35+
let cp = pac::CorePeripherals::take().unwrap();
36+
let dp = pac::Peripherals::take().unwrap();
37+
38+
let mut scb = cp.SCB;
39+
let mut rcc = dp.RCC.freeze(Config::hsi16());
40+
let mut dma = DMA::new(dp.DMA1, &mut rcc);
41+
let mut delay = cp.SYST.delay(rcc.clocks);
42+
let mut pwr = PWR::new(dp.PWR, &mut rcc);
43+
44+
let gpiob = dp.GPIOB.split(&mut rcc);
45+
46+
let sda = gpiob.pb9.into_open_drain_output();
47+
let scl = gpiob.pb8.into_open_drain_output();
48+
49+
let mut green = gpiob.pb5.into_push_pull_output();
50+
let mut red = gpiob.pb7.into_push_pull_output();
51+
52+
let mut i2c = dp.I2C1.i2c(sda, scl, 100.khz(), &mut rcc);
53+
54+
let mut tx_channel = dma.channels.channel2;
55+
let mut rx_channel = dma.channels.channel3;
56+
57+
// Create the buffer we're going to use for DMA.
58+
// This is safe, since this is the main function, and it's only executed
59+
// once. This means there is no other code accessing this `static`.
60+
static mut BUFFER: [u8; 1] = [0; 1];
61+
let mut buffer = Pin::new(unsafe { &mut BUFFER });
62+
63+
let address = 0x52 >> 1;
64+
65+
loop {
66+
buffer[0] = 0xc0; // address of on of the reference registers
67+
68+
// Prepare requesting data from reference register
69+
let mut transfer = i2c.write_all(
70+
&mut dma.handle,
71+
tx_channel,
72+
address,
73+
buffer,
74+
);
75+
76+
// Start DMA transfer and wait for it to finish
77+
let res = interrupt::free(|_| {
78+
unsafe { NVIC::unmask(Interrupt::DMA1_CHANNEL2_3); }
79+
80+
transfer.enable_interrupts(dma::Interrupts {
81+
transfer_error: true,
82+
transfer_complete: true,
83+
.. dma::Interrupts::default()
84+
});
85+
86+
let transfer = transfer.start();
87+
88+
// Wait for the DMA transfer to finish. Since we first sleep until
89+
// an interrupt occurs, we know that the call to `wait` will return
90+
// immediately.
91+
pwr.sleep_mode(&mut scb).enter();
92+
let res = transfer.wait().unwrap();
93+
94+
NVIC::mask(Interrupt::DMA1_CHANNEL2_3);
95+
res
96+
});
97+
98+
i2c = res.target;
99+
tx_channel = res.channel;
100+
buffer = res.buffer;
101+
102+
// Prepare to read returned data.
103+
let mut transfer = i2c.read_all(
104+
&mut dma.handle,
105+
rx_channel,
106+
address,
107+
buffer,
108+
);
109+
110+
// Start DMA transfer and wait for it to finish
111+
let res = interrupt::free(|_| {
112+
unsafe { NVIC::unmask(Interrupt::DMA1_CHANNEL2_3); }
113+
114+
transfer.enable_interrupts(dma::Interrupts {
115+
transfer_error: true,
116+
transfer_complete: true,
117+
.. dma::Interrupts::default()
118+
});
119+
120+
let transfer = transfer.start();
121+
122+
// Wait for the DMA transfer to finish. Since we first sleep until
123+
// an interrupt occurs, we know that the call to `wait` will return
124+
// immediately.
125+
pwr.sleep_mode(&mut scb).enter();
126+
let res = transfer.wait().unwrap();
127+
128+
NVIC::mask(Interrupt::DMA1_CHANNEL2_3);
129+
res
130+
});
131+
132+
i2c = res.target;
133+
rx_channel = res.channel;
134+
buffer = res.buffer;
135+
136+
if buffer[0] == 0xee {
137+
green.set_high().unwrap();
138+
red.set_low().unwrap();
139+
}
140+
else {
141+
red.set_high().unwrap();
142+
green.set_low().unwrap();
143+
}
144+
145+
delay.delay_ms(50u32);
146+
147+
green.set_low().unwrap();
148+
red.set_low().unwrap();
149+
150+
delay.delay_ms(50u32);
151+
}
152+
}

openocd.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
source [find interface/stlink-v2.cfg]
2-
source [find target/stm32l0.cfg]
2+
source [find target/stm32l0.cfg]

src/dma.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,13 @@ use core::{
2727
use as_slice::AsSlice;
2828

2929
use crate::{
30+
i2c,
3031
pac::{
3132
self,
3233
dma1::ccr1,
34+
I2C1,
35+
I2C2,
36+
I2C3,
3337
USART1,
3438
USART2,
3539
},
@@ -144,7 +148,7 @@ impl<T, C, B> Transfer<T, C, B, Ready>
144148
/// Start the DMA transfer
145149
///
146150
/// Consumes this instance of `Transfer` and returns a new one, with its
147-
/// state changes to indicate that the transfer has been started.
151+
/// state changed to indicate that the transfer has been started.
148152
pub fn start(self) -> Transfer<T, C, B, Started> {
149153
compiler_fence(Ordering::SeqCst);
150154

@@ -476,18 +480,40 @@ macro_rules! impl_target {
476480
}
477481
}
478482

483+
// See STM32L0x2 Reference Manual, table 51 (page 267).
479484
impl_target!(
485+
// USART1
480486
serial::Tx<USART1>, Channel2, 3;
481487
serial::Tx<USART1>, Channel4, 3;
482488
serial::Rx<USART1>, Channel3, 3;
483489
serial::Rx<USART1>, Channel5, 3;
484490

491+
// USART2
485492
serial::Tx<USART2>, Channel4, 4;
486493
serial::Tx<USART2>, Channel7, 4;
487494
serial::Rx<USART2>, Channel5, 4;
488495
serial::Rx<USART2>, Channel6, 4;
489496
);
490497

498+
#[cfg(feature = "stm32l0x2")]
499+
impl_target!(
500+
// I2C1
501+
i2c::Tx<I2C1>, Channel2, 6;
502+
i2c::Rx<I2C1>, Channel3, 6;
503+
i2c::Tx<I2C1>, Channel6, 6;
504+
i2c::Rx<I2C1>, Channel7, 6;
505+
506+
// I2C2
507+
i2c::Tx<I2C2>, Channel4, 7;
508+
i2c::Rx<I2C2>, Channel5, 7;
509+
510+
// I2C3
511+
i2c::Tx<I2C3>, Channel2, 14;
512+
i2c::Rx<I2C3>, Channel3, 14;
513+
i2c::Tx<I2C3>, Channel4, 14;
514+
i2c::Rx<I2C3>, Channel5, 14;
515+
);
516+
491517
// See STM32L0x2 Reference Manual, table 51 (page 267).
492518
#[cfg(feature = "stm32l082")]
493519
impl_target!(

0 commit comments

Comments
 (0)