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+
4669int 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