Bladeren bron

firmware: I2S test.

Mathias Gottschlag 5 jaren geleden
bovenliggende
commit
0919d59f8b

+ 12
- 15
hackamp/firmware/Cargo.lock Bestand weergeven

@@ -125,6 +125,15 @@ dependencies = [
125 125
  "cortex-m",
126 126
 ]
127 127
 
128
+[[package]]
129
+name = "embedded-dma"
130
+version = "0.1.2"
131
+source = "registry+https://github.com/rust-lang/crates.io-index"
132
+checksum = "46c8c02e4347a0267ca60813c952017f4c5948c232474c6010a381a337f1bda4"
133
+dependencies = [
134
+ "stable_deref_trait",
135
+]
136
+
128 137
 [[package]]
129 138
 name = "embedded-hal"
130 139
 version = "0.2.4"
@@ -328,13 +337,12 @@ dependencies = [
328 337
 [[package]]
329 338
 name = "stm32f4xx-hal"
330 339
 version = "0.8.3"
331
-source = "registry+https://github.com/rust-lang/crates.io-index"
332
-checksum = "b3a2f044469d1e3aff2cd02bee8b2724f3d5d91f3175e5d1ec99770320d16192"
333 340
 dependencies = [
334 341
  "bare-metal",
335 342
  "cast",
336 343
  "cortex-m",
337 344
  "cortex-m-rt",
345
+ "embedded-dma",
338 346
  "embedded-hal",
339 347
  "nb 0.1.3",
340 348
  "rand_core",
@@ -343,16 +351,6 @@ dependencies = [
343 351
  "void",
344 352
 ]
345 353
 
346
-[[package]]
347
-name = "stm32ral"
348
-version = "0.3.1"
349
-source = "registry+https://github.com/rust-lang/crates.io-index"
350
-checksum = "8665561cb3422ddf54d257bf6cb3c5432ba0684f6937c4ad987bad16aa6ca496"
351
-dependencies = [
352
- "bare-metal",
353
- "cortex-m",
354
-]
355
-
356 354
 [[package]]
357 355
 name = "syn"
358 356
 version = "1.0.48"
@@ -366,12 +364,11 @@ dependencies = [
366 364
 
367 365
 [[package]]
368 366
 name = "synopsys-usb-otg"
369
-version = "0.1.0"
367
+version = "0.2.3"
370 368
 source = "registry+https://github.com/rust-lang/crates.io-index"
371
-checksum = "6bcd2ca4e946cd2f33dfe2b0485f6b3c306721a035699f44f576d69e45b38db0"
369
+checksum = "461676dcf123675b3d3b02e2390e6a690cd186aacf2f439af7673c79e2561d53"
372 370
 dependencies = [
373 371
  "cortex-m",
374
- "stm32ral",
375 372
  "usb-device",
376 373
  "vcell",
377 374
 ]

+ 3
- 1
hackamp/firmware/Cargo.toml Bestand weergeven

@@ -8,9 +8,11 @@ edition = "2018"
8 8
 cortex-m = "0.6"
9 9
 cortex-m-rt = "0.6"
10 10
 cortex-m-rtic = "0.5.3"
11
+#cortex-m-semihosting = "0.3.5"
11 12
 embedded-hal = "0.2.3"
12 13
 panic-semihosting = "0.5"
13
-stm32f4xx-hal = { version = "0.8.3", features = ["stm32f429", "usb_hs"] }
14
+#stm32f4xx-hal = { git = "https://github.com/stm32-rs/stm32f4xx-hal.git", features = ["stm32f429", "usb_hs"] }
15
+stm32f4xx-hal = { path = "/home/mathias/Projects/microcontroller/stm32-usb-audio/stm32f4xx-hal", features = ["stm32f429", "usb_hs"] }
14 16
 
15 17
 [profile]
16 18
 [profile.release]

+ 2
- 2
hackamp/firmware/src/audio_buffer.rs Bestand weergeven

@@ -74,7 +74,7 @@ impl AudioBuffer {
74 74
     pub fn borrow_read(&'static self, cs: &CriticalSection) -> Option<ReadPacket> {
75 75
         let inner = self.mutex.borrow(cs);
76 76
         let valid = inner.valid.get();
77
-        if valid != 0 {
77
+        if valid == 0 {
78 78
             return None;
79 79
         }
80 80
         let index = valid.trailing_zeros();
@@ -110,7 +110,7 @@ impl AudioBuffer {
110 110
     pub fn borrow_write(&'static self, cs: &CriticalSection) -> Option<WritePacket> {
111 111
         let inner = self.mutex.borrow(cs);
112 112
         let invalid = inner.invalid.get();
113
-        if invalid != 0 {
113
+        if invalid == 0 {
114 114
             return None;
115 115
         }
116 116
         let index = invalid.trailing_zeros();

+ 232
- 0
hackamp/firmware/src/halext/i2s.rs Bestand weergeven

@@ -0,0 +1,232 @@
1
+//! I2S interface with DMA data transfer.
2
+
3
+use cortex_m::interrupt;
4
+use stm32f4xx_hal::dma;
5
+use stm32f4xx_hal::dma::traits::Stream;
6
+use stm32f4xx_hal::gpio::gpiob::{PB12, PB13, PB14, PB15};
7
+use stm32f4xx_hal::gpio::gpioc::PC6;
8
+use stm32f4xx_hal::gpio::{Alternate, AF5, AF6};
9
+use stm32f4xx_hal::stm32::{DMA1, I2S2EXT, RCC, SPI2};
10
+
11
+use crate::audio_buffer::{AudioBuffer, ReadPacket, WritePacket, CHANNELS, SAMPLES_PER_PACKET};
12
+
13
+/// Clocks for the I2S peripheral.
14
+#[derive(Clone, Copy)]
15
+pub struct I2SClocks {
16
+    plli2sclk: u32,
17
+}
18
+
19
+impl I2SClocks {
20
+    /// Initializes the I2S clocks.
21
+    ///
22
+    /// This function must only be called once at startup. It assumes that the HSE is already
23
+    /// initialized and the HSE frequency is 8MHz.
24
+    pub unsafe fn init() -> I2SClocks {
25
+        // The following values result in a sample frequency of 47991.07Hz:
26
+        // - PLLI2SN = 43
27
+        // - PLLI2SR = 4
28
+        // - I2SDIV = 3
29
+        // - I2SODD = 1
30
+
31
+        let rcc = &*RCC::ptr();
32
+        // Configure PLL.
33
+        rcc.plli2scfgr
34
+            .modify(|_, w| w.plli2sn().bits(43).plli2sq().bits(4).plli2sr().bits(4));
35
+        // Enable PLL.
36
+        rcc.cr.modify(|_, w| w.plli2son().set_bit());
37
+        // Wait for PLL to stabilise
38
+        while rcc.cr.read().plli2srdy().bit_is_clear() {}
39
+
40
+        // Ensure that the PLL is selected as the I2S clock source.
41
+        rcc.cfgr.modify(|_, w| w.i2ssrc().clear_bit());
42
+
43
+        I2SClocks {
44
+            plli2sclk: 86000000,
45
+        }
46
+    }
47
+}
48
+
49
+/// I2S interface with DMA data transfer.
50
+pub struct I2S {
51
+    spi: SPI2,
52
+    i2sext: I2S2EXT,
53
+    rx_dma: dma::Stream3<DMA1>,
54
+    tx_dma: dma::Stream4<DMA1>,
55
+    in_: &'static AudioBuffer,
56
+    in_packets: [Option<WritePacket>; 2],
57
+    out: &'static AudioBuffer,
58
+    out_packets: [Option<ReadPacket>; 2],
59
+}
60
+
61
+impl I2S {
62
+    /// Initializes the I2S interface to read from/write to the specified buffers.
63
+    ///
64
+    /// The interface is only started when the `start()` function is called.
65
+    pub fn new(
66
+        spi: SPI2,
67
+        i2sext: I2S2EXT,
68
+        mut rx_dma: dma::Stream3<DMA1>,
69
+        mut tx_dma: dma::Stream4<DMA1>,
70
+        mck: PC6<Alternate<AF5>>,
71
+        ws: PB12<Alternate<AF5>>,
72
+        ck: PB13<Alternate<AF5>>,
73
+        sdin: PB14<Alternate<AF6>>,
74
+        sdout: PB15<Alternate<AF5>>,
75
+        in_: &'static AudioBuffer,
76
+        out: &'static AudioBuffer,
77
+        _clocks: I2SClocks,
78
+    ) -> I2S {
79
+        // Enable and reset the peripheral.
80
+        let rcc = unsafe { &*RCC::ptr() };
81
+        rcc.apb1enr.modify(|_, w| w.spi2en().set_bit());
82
+        rcc.apb1rstr.modify(|_, w| w.spi2rst().set_bit());
83
+        rcc.apb1rstr.modify(|_, w| w.spi2rst().clear_bit());
84
+        //rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit());
85
+
86
+        // Disable SS output.
87
+        spi.cr2.write(|w| w.ssoe().clear_bit());
88
+
89
+        // Configure the pins.
90
+        // TODO
91
+
92
+        // Configure the clocks.
93
+        // For the values, see the comment in I2SClocks::init().
94
+        spi.i2spr
95
+            .write(|w| unsafe { w.mckoe().set_bit().odd().set_bit().i2sdiv().bits(3) });
96
+        i2sext
97
+            .i2spr
98
+            .write(|w| unsafe { w.mckoe().set_bit().odd().set_bit().i2sdiv().bits(3) });
99
+
100
+        // Configure I2S.
101
+        spi.i2scfgr.modify(|_, w| unsafe {
102
+            w.i2smod()
103
+                .set_bit()
104
+                .i2se()
105
+                .clear_bit()
106
+                .i2scfg()
107
+                .master_tx()
108
+                .i2sstd()
109
+                .bits(0b01)
110
+                .ckpol()
111
+                .idle_high()
112
+                .datlen()
113
+                .bits(0b10)
114
+                .chlen()
115
+                .set_bit()
116
+        });
117
+        i2sext.i2scfgr.modify(|_, w| unsafe {
118
+            w.i2smod()
119
+                .set_bit()
120
+                .i2se()
121
+                .clear_bit()
122
+                .i2scfg()
123
+                .slave_rx()
124
+                .i2sstd()
125
+                .bits(0b01)
126
+                .ckpol()
127
+                .idle_high()
128
+                .datlen()
129
+                .bits(0b10)
130
+                .chlen()
131
+                .set_bit()
132
+        });
133
+        i2sext.cr2.modify(|_, w| w.rxdmaen().set_bit());
134
+        spi.cr2.modify(|_, w| w.txdmaen().set_bit());
135
+
136
+        rx_dma.set_channel(dma::Channel3);
137
+        rx_dma.clear_interrupts();
138
+        unsafe {
139
+            rx_dma.set_memory_size(1);
140
+            rx_dma.set_peripheral_size(1);
141
+        }
142
+        rx_dma.set_memory_increment(true);
143
+        rx_dma.set_direction(dma::PeripheralToMemory);
144
+        rx_dma.set_interrupts_enable(true, false, true, true);
145
+        rx_dma.set_double_buffer(true);
146
+        rx_dma.set_fifo_enable(false);
147
+        rx_dma.set_number_of_transfers((SAMPLES_PER_PACKET * CHANNELS * 2) as u16);
148
+
149
+        tx_dma.set_channel(dma::Channel0);
150
+        tx_dma.clear_interrupts();
151
+        unsafe {
152
+            tx_dma.set_memory_size(1);
153
+            tx_dma.set_peripheral_size(1);
154
+        }
155
+        tx_dma.set_memory_increment(true);
156
+        tx_dma.set_direction(dma::MemoryToPeripheral);
157
+        tx_dma.set_interrupts_enable(true, false, true, true);
158
+        tx_dma.set_double_buffer(true);
159
+        tx_dma.set_fifo_enable(false);
160
+        tx_dma.set_number_of_transfers((SAMPLES_PER_PACKET * CHANNELS * 2) as u16);
161
+
162
+        const DR_OFFSET: usize = 0x0c;
163
+        let spi_dr = SPI2::ptr() as usize + DR_OFFSET;
164
+        let i2sext_dr = I2S2EXT::ptr() as usize + DR_OFFSET;
165
+        rx_dma.set_peripheral_address(i2sext_dr as u32);
166
+        tx_dma.set_peripheral_address(spi_dr as u32);
167
+
168
+        I2S {
169
+            spi,
170
+            i2sext,
171
+            rx_dma,
172
+            tx_dma,
173
+            in_,
174
+            in_packets: [None, None],
175
+            out,
176
+            out_packets: [None, None],
177
+        }
178
+    }
179
+
180
+    /// Starts the data stream of the I2S interface.
181
+    pub fn start(&mut self) {
182
+        interrupt::free(|cs| {
183
+            for i in 0..2 {
184
+                self.in_packets[i] = self.in_.borrow_write(cs);
185
+            }
186
+            for i in 0..2 {
187
+                self.out_packets[i] = self.out.borrow_read(cs);
188
+            }
189
+        });
190
+
191
+        // The system was just started, and some buffers were filled with dummy data, so we can
192
+        // assume that borrowing was successful.
193
+        let in_0 = self.in_packets[0].as_mut().unwrap().as_mut().as_ptr() as usize as u32;
194
+        let in_1 = self.in_packets[1].as_mut().unwrap().as_mut().as_ptr() as usize as u32;
195
+        self.rx_dma.set_memory_address(in_0);
196
+        self.rx_dma.set_memory_double_buffer_address(in_1);
197
+        /*self.dma.st[3].m0ar.write(|w| unsafe { w.bits(in_0) });
198
+        self.dma.st[3].m1ar.write(|w| unsafe { w.bits(in_1) });*/
199
+        let out_0 = self.out_packets[0].as_ref().unwrap().as_ref().as_ptr() as usize as u32;
200
+        let out_1 = self.out_packets[1].as_ref().unwrap().as_ref().as_ptr() as usize as u32;
201
+        self.tx_dma.set_memory_address(out_0);
202
+        self.tx_dma.set_memory_double_buffer_address(out_1);
203
+        /*self.dma.st[4].m0ar.write(|w| unsafe { w.bits(out_0) });
204
+        self.dma.st[4].m1ar.write(|w| unsafe { w.bits(out_1) });*/
205
+
206
+        // Enable DMA and activate the I2S peripherals.
207
+        /*self.dma.st[3].cr.modify(|_, w| w.en().set_bit());
208
+        self.dma.st[4].cr.modify(|_, w| w.en().set_bit());*/
209
+        unsafe {
210
+            self.rx_dma.enable();
211
+            self.tx_dma.enable();
212
+        }
213
+        self.i2sext.i2scfgr.modify(|_, w| w.i2se().set_bit());
214
+        self.spi.i2scfgr.modify(|_, w| w.i2se().set_bit());
215
+
216
+        /*loop {
217
+            if self.spi.sr.read().txe().bit_is_set() {
218
+                self.spi.dr.write(|w| unsafe { w.bits(0xaaaa) });
219
+            }
220
+            if self.i2sext.sr.read().rxne().bit_is_set() {
221
+                self.i2sext.dr.read();
222
+            }
223
+        }*/
224
+    }
225
+
226
+    /// Reads data from the output audio buffer and writes to the input audio buffer as required.
227
+    ///
228
+    /// This function needs to be called from the DMA interrupt handler.
229
+    pub fn poll(&mut self) {
230
+        // TODO
231
+    }
232
+}

+ 2
- 0
hackamp/firmware/src/halext/mod.rs Bestand weergeven

@@ -1,2 +1,4 @@
1 1
 //! Hardware drivers missing in the HAL.
2 2
 #![warn(missing_docs)]
3
+
4
+pub mod i2s;

+ 65
- 12
hackamp/firmware/src/main.rs Bestand weergeven

@@ -4,8 +4,11 @@
4 4
 
5 5
 use cortex_m::interrupt;
6 6
 use panic_semihosting as _;
7
+use stm32f4xx_hal::dma::StreamsTuple;
8
+use stm32f4xx_hal::prelude::*;
7 9
 
8 10
 use audio_buffer::AudioBuffer;
11
+use halext::i2s::{I2SClocks, I2S};
9 12
 use pins::Pins;
10 13
 
11 14
 // Remove "pub" to check whether there is any dead code.
@@ -19,27 +22,77 @@ const APP: () = {
19 22
     struct Resources {}
20 23
 
21 24
     #[init]
22
-    fn init(_ctx: init::Context) {
25
+    fn init(ctx: init::Context) {
26
+        // Initialize clocks.
27
+        let rcc = ctx.device.RCC.constrain();
28
+        let _clocks = rcc
29
+            .cfgr
30
+            .use_hse(8.mhz())
31
+            .sysclk(48.mhz())
32
+            .pclk1(24.mhz())
33
+            .require_pll48clk()
34
+            .freeze();
35
+        let i2sclocks = unsafe { I2SClocks::init() };
36
+
37
+        // Configure the GPIOs.
38
+        let gpiob = ctx.device.GPIOB.split();
39
+        let gpioc = ctx.device.GPIOC.split();
23 40
         let _pins = Pins::configure();
24
-        static I2S_BUFFER: AudioBuffer = AudioBuffer::new();
25 41
 
26
-        let i2s_buffer = &I2S_BUFFER;
42
+        // The components of the firmware are connected via audio buffers.
43
+        static AMP_I2S_IN_BUFFER: AudioBuffer = AudioBuffer::new();
44
+        static AMP_I2S_OUT_BUFFER: AudioBuffer = AudioBuffer::new();
45
+        //static BT_I2S_IN_BUFFER: AudioBuffer = AudioBuffer::new();
46
+        static BT_I2S_OUT_BUFFER: AudioBuffer = AudioBuffer::new();
47
+        /*static USB1_IN_BUFFER: AudioBuffer = AudioBuffer::new();
48
+        static USB1_OUT_BUFFER: AudioBuffer = AudioBuffer::new();
49
+        static USB2_IN_BUFFER: AudioBuffer = AudioBuffer::new();
50
+        static USB2_OUT_BUFFER: AudioBuffer = AudioBuffer::new();*/
27 51
 
28
-        // Buffer example code.
52
+        // We initialize the output audio buffers with some data. As the I2S input buffers are
53
+        // filled at the same rate as the I2S output buffers, this initial state ensures that the
54
+        // output buffers never run empty (note that the USB audio interfaces perform their own
55
+        // adaptive synchronization).
29 56
         interrupt::free(|cs| {
30
-            let write_packet = i2s_buffer.borrow_write(cs).unwrap();
31
-            // TODO
32
-            i2s_buffer.write_finished(write_packet, cs);
33
-            let read_packet = i2s_buffer.borrow_read(cs).unwrap();
34
-            // TODO
35
-            i2s_buffer.read_finished(read_packet, cs);
57
+            for buffer in [&AMP_I2S_OUT_BUFFER, &BT_I2S_OUT_BUFFER].iter() {
58
+                // We initialize two packets so that the I2S interface can immediately initialize
59
+                // both DMA buffers (double buffering).
60
+                for _ in 0..2 {
61
+                    let mut packet = buffer.borrow_write(cs).unwrap();
62
+                    let data = packet.as_mut();
63
+                    for i in 0..data.len() {
64
+                        data[i] = 0x8000000; // "0"
65
+                    }
66
+                    buffer.write_finished(packet, cs);
67
+                }
68
+            }
36 69
         });
70
+
71
+        let dma = StreamsTuple::new(ctx.device.DMA1);
72
+        let mut i2s = I2S::new(
73
+            ctx.device.SPI2,
74
+            ctx.device.I2S2EXT,
75
+            dma.3,
76
+            dma.4,
77
+            gpioc.pc6.into_alternate_af5(),
78
+            gpiob.pb12.into_alternate_af5(),
79
+            gpiob.pb13.into_alternate_af5(),
80
+            gpiob.pb14.into_alternate_af6(),
81
+            gpiob.pb15.into_alternate_af5(),
82
+            &AMP_I2S_IN_BUFFER,
83
+            &AMP_I2S_OUT_BUFFER,
84
+            i2sclocks,
85
+        );
86
+
87
+        i2s.start();
88
+
89
+        // TODO: Resources.
37 90
     }
38 91
 
39
-    #[idle(resources = [])]
92
+    /*#[idle(resources = [])]
40 93
     fn idle(_ctx: idle::Context) -> ! {
41 94
         loop {
42 95
             // TODO
43 96
         }
44
-    }
97
+    }*/
45 98
 };

Laden…
Annuleren
Opslaan