1
1
/*
2
- * Example to show using I2C and I2C with libopencm3.
2
+ * Example for how to use I2C and I2S with libopencm3.
3
3
* This is intended for the STM32F411-Discovery demoboard.
4
- * It has a CS42L22 audio DAC onboard.
5
4
*
6
5
* The device plays a 1kHz sine through the audio jack.
7
6
*/
8
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
+
9
13
10
14
#include <libopencm3/stm32/gpio.h>
11
15
#include <libopencm3/stm32/i2c.h>
12
16
#include <libopencm3/stm32/rcc.h>
13
17
#include <libopencm3/stm32/spi.h>
14
18
19
+ #ifdef USE_DMA
20
+ #include <libopencm3/cm3/nvic.h>
21
+ #include <libopencm3/stm32/dma.h>
22
+ #endif
23
+
15
24
16
25
/* Test audio: 8 points of a sine.
17
26
* 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)
36
45
uint8_t packet [2 ];
37
46
packet [0 ] = reg ;
38
47
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,
40
49
* libopencm wants it without it */
41
50
uint8_t address = (0x94 )>>1 ;
42
51
43
52
i2c_transfer7 (I2C1 , address , packet , 2 , NULL , 0 );
44
53
}
45
54
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
+
46
69
int main (void )
47
70
{
48
71
/* Set device clocks from opencm3 provided preset.*/
@@ -54,8 +77,12 @@ int main(void)
54
77
rcc_periph_clock_enable (RCC_GPIOC );
55
78
rcc_periph_clock_enable (RCC_GPIOD );
56
79
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
+
59
86
60
87
/* I2C GPIO pins
61
88
* PB6 - SCL (I2C clock)
@@ -73,6 +100,7 @@ int main(void)
73
100
gpio_set_af (GPIOB , GPIO_AF4 , GPIO6 );
74
101
gpio_set_af (GPIOB , GPIO_AF4 , GPIO9 );
75
102
103
+
76
104
/* Initialize the I2C itself.
77
105
* Since we are master, we would not need to initialize slave
78
106
* address, but this is the only libopencm3 API call that sets
@@ -84,6 +112,7 @@ int main(void)
84
112
i2c_set_own_7bit_slave_address (I2C1 , 0 );
85
113
i2c_peripheral_enable (I2C1 );
86
114
115
+
87
116
/* Initialize I2S.
88
117
* I2S is implemented as a HW mode of the SPI peripheral.
89
118
* Since this is a STM32F411, there is a separate I2S PLL
@@ -96,8 +125,7 @@ int main(void)
96
125
i2s_set_dataformat (SPI3 , i2s_dataframe_ch16_data16 );
97
126
i2s_set_mode (SPI3 , i2s_mode_master_transmit );
98
127
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.
101
129
* PLLR = 2
102
130
* PLLI2SN = 192
103
131
* PLLI2SM = 16
@@ -107,9 +135,14 @@ int main(void)
107
135
* STM32F411 reference manual:
108
136
* Fs = I2Sclk/ (32*2 * ((2*I2SDIV)+ODD)*4)
109
137
* 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
111
139
*/
112
140
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
113
146
i2s_enable (SPI3 );
114
147
115
148
@@ -138,16 +171,47 @@ int main(void)
138
171
write_i2c_to_audiochip (0x02 , 0x9e ); // power control 1: Magic value to power up the chip
139
172
140
173
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
+
141
197
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 ) {
145
206
gpio_toggle (GPIOD , GPIO13 );
146
- blinkslowdown = 0 ;
207
+ i = 0 ;
147
208
}
148
209
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
151
215
}
152
216
return 0 ;
153
217
}
0 commit comments