Skip to content

Commit c7052ed

Browse files
Kalle Raiskilakraiskil
authored andcommitted
Add stm32f411e-discovery I2S example sound_out.
This plays a sine wave through the audio jack.
1 parent 9577cd7 commit c7052ed

File tree

4 files changed

+252
-0
lines changed

4 files changed

+252
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# README
2+
3+
This directory contains samples for the STM32F411EDISCOVERY
4+
board. This is similar to the original STM32F4DISCOVERY, but
5+
has the the STM32F411VE MCU.
6+
The board is labeled 'MB-1115 B-02'
7+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
BINARY = sound_out
2+
DEVICE=STM32F411VE
3+
include ../../Makefile.include
4+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# README
2+
3+
This example demonstrates using the oboard audio DAC on
4+
STM32F411-Discovery board.
5+
It plays a continuous 1 kHz sine wave.
6+
7+
## Board connections
8+
9+
Sepeakers attached to the 3.5mm audio jack.
10+
Note: volume is turned low, but beware of loud sounds
11+
in case of bugs (i.e. plug the headphones to your ear
12+
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).
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Example for how to use I2C and I2S with libopencm3.
3+
* This is intended for the STM32F411-Discovery demoboard.
4+
*
5+
* The device plays a 1kHz sine through the audio jack.
6+
*/
7+
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+
13+
14+
#include <libopencm3/stm32/gpio.h>
15+
#include <libopencm3/stm32/i2c.h>
16+
#include <libopencm3/stm32/rcc.h>
17+
#include <libopencm3/stm32/spi.h>
18+
19+
#ifdef USE_DMA
20+
#include <libopencm3/cm3/nvic.h>
21+
#include <libopencm3/stm32/dma.h>
22+
#endif
23+
24+
25+
/* Test audio: 8 points of a sine.
26+
* At Fs=8kHz, this means a 1kHz audio sine
27+
* (the other channel is mute)
28+
*/
29+
#define VOL 0x0020
30+
#define D16(x) ((int16_t)(x*VOL) )
31+
int16_t audio[16]=
32+
{ D16(0), 0,
33+
D16(0.70711), 0,
34+
D16(1), 0,
35+
D16(0.70711), 0,
36+
D16(0), 0,
37+
D16(-0.70711),0,
38+
D16(-0.9999), 0,
39+
D16(-0.707), 0 };
40+
41+
42+
static void write_i2c_to_audiochip( uint8_t reg, uint8_t contents)
43+
{
44+
uint8_t packet[2];
45+
packet[0] = reg;
46+
packet[1] = contents;
47+
/* STM32F411 discovery user's manual gives device address with R/W bit,
48+
* libopencm3 wants the address without it. */
49+
uint8_t address = (0x94)>>1;
50+
51+
i2c_transfer7(I2C1, address, packet, 2, NULL, 0);
52+
}
53+
54+
#ifdef USE_DMA
55+
/* Interrupt service routine for the DMA.
56+
* The name is "magic" and suffices as installation hook into libopencm3. */
57+
void dma1_stream5_isr(void)
58+
{
59+
gpio_toggle(GPIOD, GPIO12);
60+
61+
/* Clear the 'transfer complete' interrupt, or execution would jump right back to this ISR. */
62+
if (dma_get_interrupt_flag(DMA1, DMA_STREAM5, DMA_TCIF)) {
63+
dma_clear_interrupt_flags(DMA1, DMA_STREAM5, DMA_TCIF);
64+
}
65+
}
66+
#endif
67+
68+
int main(void)
69+
{
70+
/* Set device clocks from opencm3 provided preset.*/
71+
const struct rcc_clock_scale *clocks = &rcc_hsi_configs[RCC_CLOCK_3V3_84MHZ];
72+
rcc_clock_setup_pll( clocks );
73+
74+
rcc_periph_clock_enable(RCC_GPIOA);
75+
rcc_periph_clock_enable(RCC_GPIOB);
76+
rcc_periph_clock_enable(RCC_GPIOC);
77+
rcc_periph_clock_enable(RCC_GPIOD);
78+
79+
/* Initialize "heartbeat" LED GPIOs. */
80+
#ifdef USE_DMA
81+
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO12); /* green led */
82+
#endif
83+
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO13); /* orange led */
84+
85+
86+
/* Initialize I2C.
87+
* I2C is needed to initialize the onboard audio DAC chip.
88+
* PB6 - SCL (I2C clock)
89+
* PB9 - SDA (I2C data)
90+
* The board does not have pullups on the I2C lines, so
91+
* we use the chip internal pullups.
92+
* Also the pins must be open drain, as per I2C specification.
93+
* STM32F411 datasheet "Alternate Functions table" tells that
94+
* I2C is AlternateFucntion 4 for both pins.
95+
*/
96+
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO6);
97+
gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO9);
98+
gpio_set_output_options(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO6);
99+
gpio_set_output_options(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO9);
100+
gpio_set_af(GPIOB, GPIO_AF4, GPIO6);
101+
gpio_set_af(GPIOB, GPIO_AF4, GPIO9);
102+
103+
/* Initialize the I2C itself.
104+
* Since we are master, we would not need to initialize slave
105+
* address, but this is the only libopencm3 API call that sets
106+
* the 'bit14 of CCR' - a bit in the I2C that is HW reset to 0,
107+
* but manual says 'must be 1' */
108+
rcc_periph_clock_enable(RCC_I2C1);
109+
i2c_peripheral_disable(I2C1);
110+
i2c_set_speed(I2C1, i2c_speed_sm_100k, clocks->apb1_frequency/1000000);
111+
i2c_set_own_7bit_slave_address(I2C1, 0);
112+
i2c_peripheral_enable(I2C1);
113+
114+
115+
/* Initialize I2S.
116+
* I2S is implemented as a HW mode of the SPI peripheral.
117+
* Since this is a STM32F411, there is a separate I2S PLL
118+
* that needs to be enabled.
119+
*/
120+
rcc_osc_on(RCC_PLLI2S);
121+
rcc_periph_clock_enable(RCC_SPI3);
122+
i2s_disable(SPI3);
123+
i2s_set_standard(SPI3, i2s_standard_philips);
124+
i2s_set_dataformat(SPI3, i2s_dataframe_ch16_data16);
125+
i2s_set_mode(SPI3, i2s_mode_master_transmit);
126+
i2s_masterclock_enable(SPI3);
127+
/* RCC_PLLI2SCFGR is left at reset value: 0x24003010 i.e.
128+
* PLLR = 2
129+
* PLLI2SN = 192
130+
* PLLI2SM = 16
131+
* And since the input is PLL source (i.e. HSI = 16MHz)
132+
* The I2S clock = 16 / 16 * 192 / 2 = 96MHz
133+
* Calculate sampling frequency from equation given in
134+
* STM32F411 reference manual:
135+
* Fs = I2Sclk/ (32*2 * ((2*I2SDIV)+ODD)*4)
136+
* I2SDIV = I2Sclk/(512*Fs)
137+
* Fs=8kHz => I2SDIV=23,4 so 23 + ODD bit set
138+
*/
139+
i2s_set_clockdiv(SPI3, 23, 1);
140+
#ifdef USE_DMA
141+
/* Have the SPI/I2S peripheral ping the DMA each time data is sent.
142+
* The DMA peripheral is configured later. */
143+
spi_enable_tx_dma(SPI3);
144+
#endif
145+
i2s_enable(SPI3);
146+
147+
/* I2S pins:
148+
* Master clock: PC7
149+
* Bit clock: PC10
150+
* Data: PC12
151+
* L/R clock: PA4
152+
*/
153+
gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO7);
154+
gpio_set_af(GPIOC, GPIO_AF6, GPIO7);
155+
gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO10);
156+
gpio_set_af(GPIOC, GPIO_AF6, GPIO10);
157+
gpio_mode_setup(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO12);
158+
gpio_set_af(GPIOC, GPIO_AF6, GPIO12);
159+
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO4);
160+
gpio_set_af(GPIOA, GPIO_AF6, GPIO4);
161+
162+
163+
/* Initialize the Audio DAC, as per its datasheet.
164+
* CS43L22 /RESET is connected to PD4, first release it. Then write
165+
* minimum set of needed settings. */
166+
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO4);
167+
gpio_set(GPIOD, GPIO4);
168+
write_i2c_to_audiochip(0x06, 0x04); // interface control 1: set I2S dataformat
169+
write_i2c_to_audiochip(0x02, 0x9e); // power control 1: Magic value to power up the chip
170+
171+
172+
#ifdef USE_DMA
173+
/* Enable DMA from memory to I2S peripheral.
174+
* The DMA is configured as circular, i.e. it restarts automatically when
175+
* the requested amount of datasamples are set.
176+
* SPI3/I2S3 is available on DMA1 stream 5, channel 0 (see RM 0383, table 27) */
177+
rcc_periph_clock_enable(RCC_DMA1);
178+
nvic_enable_irq(NVIC_DMA1_STREAM5_IRQ);
179+
dma_disable_stream(DMA1, DMA_STREAM5);
180+
dma_set_priority(DMA1, DMA_STREAM5, DMA_SxCR_PL_HIGH);
181+
dma_set_memory_size(DMA1, DMA_STREAM5, DMA_SxCR_MSIZE_16BIT);
182+
dma_set_peripheral_size(DMA1, DMA_STREAM5, DMA_SxCR_PSIZE_16BIT);
183+
dma_enable_memory_increment_mode(DMA1, DMA_STREAM5);
184+
dma_enable_circular_mode(DMA1, DMA_STREAM5);
185+
dma_set_transfer_mode(DMA1, DMA_STREAM5, DMA_SxCR_DIR_MEM_TO_PERIPHERAL);
186+
dma_set_peripheral_address(DMA1, DMA_STREAM5, (uint32_t) &SPI_DR(SPI3));
187+
dma_set_memory_address(DMA1, DMA_STREAM5, (uint32_t) audio);
188+
dma_set_number_of_data(DMA1, DMA_STREAM5, sizeof(audio)/sizeof(audio[0]));
189+
dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM5);
190+
dma_channel_select(DMA1, DMA_STREAM5, DMA_SxCR_CHSEL_0);
191+
dma_enable_stream(DMA1, DMA_STREAM5);
192+
#endif
193+
194+
195+
while(1) {
196+
/* Blink the heartbeat LED at ~0,5Hz*/
197+
#ifdef USE_DMA
198+
const int blinkslowdown = 20e6;
199+
#else
200+
const int blinkslowdown = 1000;
201+
#endif
202+
static int i=0;
203+
if( ++i > blinkslowdown) {
204+
gpio_toggle(GPIOD, GPIO13);
205+
i=0;
206+
}
207+
208+
#ifndef USE_DMA
209+
/* Blocking send of the data buffer */
210+
for( unsigned j=0; j < sizeof(audio)/sizeof(audio[0]); j++)
211+
spi_send(SPI3, audio[j]);
212+
#endif
213+
}
214+
return 0;
215+
}
216+

0 commit comments

Comments
 (0)