Skip to content

Commit dc5c065

Browse files
committed
Add DMA mode to stm32f411/sound_out example.
1 parent 2583a21 commit dc5c065

File tree

2 files changed

+91
-14
lines changed

2 files changed

+91
-14
lines changed

examples/stm32/f4/stm32f411e-discovery/sound_out/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,16 @@ Sepeakers attached to the 3.5mm audio jack.
1010
Note: volume is turned low, but beware of loud sounds
1111
in case of bugs (i.e. plug the headphones to your ear
1212
only after playback starts!)
13+
14+
## DMA mode
15+
16+
There is a compile-time option (#define USE_DMA) that
17+
demonstrates how to have the DMA engine write audio
18+
data to the DAC.
19+
20+
## LEDs
21+
22+
Both versions of the program blink the orange
23+
led (PD13) at ~0.5Hz. The DMA version toggles the
24+
green led (PD12) on every DMA interrupt, which means
25+
the pattern is visible with oscilloscope only (500Hz).

examples/stm32/f4/stm32f411e-discovery/sound_out/sound_out.c

+78-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
/*
2-
* Example to show using I2C and I2C with libopencm3.
2+
* Example for how to use I2C and I2S with libopencm3.
33
* This is intended for the STM32F411-Discovery demoboard.
4-
* It has a CS42L22 audio DAC onboard.
54
*
65
* The device plays a 1kHz sine through the audio jack.
76
*/
87

8+
/* Compile time option to use DMA to feed audio to the I2S.
9+
* Without this, the audio is sent by polling the I2S peripheral's
10+
* TXE bit */
11+
#define USE_DMA
12+
913

1014
#include <libopencm3/stm32/gpio.h>
1115
#include <libopencm3/stm32/i2c.h>
1216
#include <libopencm3/stm32/rcc.h>
1317
#include <libopencm3/stm32/spi.h>
1418

19+
#ifdef USE_DMA
20+
#include <libopencm3/cm3/nvic.h>
21+
#include <libopencm3/stm32/dma.h>
22+
#endif
23+
1524

1625
/* Test audio: 8 points of a sine.
1726
* At Fs=8kHz, this means a 1kHz audio sine
@@ -36,13 +45,27 @@ static void write_i2c_to_audiochip( uint8_t reg, uint8_t contents)
3645
uint8_t packet[2];
3746
packet[0] = reg;
3847
packet[1] = contents;
39-
/* STM32F411 gives device address with R/W bit,
48+
/* STM32F411 discovery user's manyal gives device address with R/W bit,
4049
* libopencm wants it without it */
4150
uint8_t address = (0x94)>>1;
4251

4352
i2c_transfer7(I2C1, address, packet, 2, NULL, 0);
4453
}
4554

55+
#ifdef USE_DMA
56+
/* Interrupt service routine for the DMA.
57+
* The name is "magic" and suffices as installation hook into libopencm3. */
58+
void dma1_stream5_isr(void)
59+
{
60+
gpio_toggle(GPIOD, GPIO12);
61+
62+
/* Clear the 'transfer complete' interrupt, or execution would jump right back to this ISR. */
63+
if (dma_get_interrupt_flag(DMA1, DMA_STREAM5, DMA_TCIF)) {
64+
dma_clear_interrupt_flags(DMA1, DMA_STREAM5, DMA_TCIF);
65+
}
66+
}
67+
#endif
68+
4669
int main(void)
4770
{
4871
/* Set device clocks from opencm3 provided preset.*/
@@ -54,8 +77,12 @@ int main(void)
5477
rcc_periph_clock_enable(RCC_GPIOC);
5578
rcc_periph_clock_enable(RCC_GPIOD);
5679

57-
/* Initialize "heartbeat" LED GPIO */
58-
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13);
80+
/* Initialize "heartbeat" LED GPIOs. */
81+
#ifdef USE_DMA
82+
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO12); /* green led */
83+
#endif
84+
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13); /* orange led */
85+
5986

6087
/* I2C GPIO pins
6188
* PB6 - SCL (I2C clock)
@@ -73,6 +100,7 @@ int main(void)
73100
gpio_set_af(GPIOB, GPIO_AF4, GPIO6);
74101
gpio_set_af(GPIOB, GPIO_AF4, GPIO9);
75102

103+
76104
/* Initialize the I2C itself.
77105
* Since we are master, we would not need to initialize slave
78106
* address, but this is the only libopencm3 API call that sets
@@ -84,6 +112,7 @@ int main(void)
84112
i2c_set_own_7bit_slave_address(I2C1, 0);
85113
i2c_peripheral_enable(I2C1);
86114

115+
87116
/* Initialize I2S.
88117
* I2S is implemented as a HW mode of the SPI peripheral.
89118
* Since this is a STM32F411, there is a separate I2S PLL
@@ -96,8 +125,7 @@ int main(void)
96125
i2s_set_dataformat(SPI3, i2s_dataframe_ch16_data16);
97126
i2s_set_mode(SPI3, i2s_mode_master_transmit);
98127
i2s_masterclock_enable(SPI3);
99-
/* RCC_PLLI2SCFGR configured values are:
100-
* 0x24003010 i.e.
128+
/* RCC_PLLI2SCFGR is left at reset value: 0x24003010 i.e.
101129
* PLLR = 2
102130
* PLLI2SN = 192
103131
* PLLI2SM = 16
@@ -107,9 +135,14 @@ int main(void)
107135
* STM32F411 reference manual:
108136
* Fs = I2Sclk/ (32*2 * ((2*I2SDIV)+ODD)*4)
109137
* I2SDIV = I2Sclk/(512*Fs)
110-
* I2SDIV=24 => 23,4 so 23 + ODD bit set
138+
* Fs=8kHz => I2SDIV=23,4 so 23 + ODD bit set
111139
*/
112140
i2s_set_clockdiv(SPI3, 23, 1);
141+
#ifdef USE_DMA
142+
/* Have the SPI/I2S peripheral ping the DMA each time data is sent.
143+
* The DMA peripheral is configured later. */
144+
spi_enable_tx_dma(SPI3);
145+
#endif
113146
i2s_enable(SPI3);
114147

115148

@@ -138,16 +171,47 @@ int main(void)
138171
write_i2c_to_audiochip(0x02, 0x9e); // power control 1: Magic value to power up the chip
139172

140173

174+
#ifdef USE_DMA
175+
/* Enable DMA from memory to I2S peripheral.
176+
* The DMA is configured as circular, i.e. it restarts automatically when
177+
* the requested amount of datasamples are set.
178+
* SPI3/I2S3 is available on DMA1 stream 5, channel 0 (see RM 0383, table 27) */
179+
rcc_periph_clock_enable(RCC_DMA1);
180+
nvic_enable_irq(NVIC_DMA1_STREAM5_IRQ);
181+
dma_disable_stream(DMA1, DMA_STREAM5);
182+
dma_set_priority(DMA1, DMA_STREAM5, DMA_SxCR_PL_HIGH);
183+
dma_set_memory_size(DMA1, DMA_STREAM5, DMA_SxCR_MSIZE_16BIT);
184+
dma_set_peripheral_size(DMA1, DMA_STREAM5, DMA_SxCR_PSIZE_16BIT);
185+
dma_enable_memory_increment_mode(DMA1, DMA_STREAM5);
186+
dma_enable_circular_mode(DMA1, DMA_STREAM5);
187+
dma_set_transfer_mode(DMA1, DMA_STREAM5, DMA_SxCR_DIR_MEM_TO_PERIPHERAL);
188+
dma_set_peripheral_address(DMA1, DMA_STREAM5, (uint32_t) &SPI_DR(SPI3));
189+
dma_set_memory_address(DMA1, DMA_STREAM5, (uint32_t) audio);
190+
dma_set_number_of_data(DMA1, DMA_STREAM5, sizeof(audio)/sizeof(audio[0]));
191+
dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM5);
192+
dma_channel_select(DMA1, DMA_STREAM5, DMA_SxCR_CHSEL_0);
193+
dma_enable_stream(DMA1, DMA_STREAM5);
194+
#endif
195+
196+
141197
while(1) {
142-
/* Blink the heartbeat LED */
143-
static int blinkslowdown=0;
144-
if( ++blinkslowdown == 8000) {
198+
/* Blink the heartbeat LED at ~0,5Hz*/
199+
#ifdef USE_DMA
200+
const int blinkslowdown = 20e6;
201+
#else
202+
const int blinkslowdown = 1000;
203+
#endif
204+
static int i=0;
205+
if( ++i > blinkslowdown) {
145206
gpio_toggle(GPIOD, GPIO13);
146-
blinkslowdown=0;
207+
i=0;
147208
}
148209

149-
for( unsigned i=0; i < sizeof(audio)/sizeof(audio[0]); i++)
150-
spi_send(SPI3, audio[i]);
210+
#ifndef USE_DMA
211+
/* Blocking send of the data buffer */
212+
for( unsigned j=0; j < sizeof(audio)/sizeof(audio[0]); j++)
213+
spi_send(SPI3, audio[j]);
214+
#endif
151215
}
152216
return 0;
153217
}

0 commit comments

Comments
 (0)