1 | #include "Audio.h"
|
2 | #include "stm32f4xx_conf.h"
|
3 | #include "stm32f4xx.h"
|
4 |
|
5 | #include <stdlib.h>
|
6 |
|
7 | static void WriteRegister(uint8_t address, uint8_t value);
|
8 | static void StartAudioDMAAndRequestBuffers();
|
9 | static void StopAudioDMA();
|
10 |
|
11 | static AudioCallbackFunction *CallbackFunction;
|
12 | static void *CallbackContext;
|
13 | static int16_t * volatile NextBufferSamples;
|
14 | static volatile int NextBufferLength;
|
15 | static volatile int BufferNumber;
|
16 | static volatile bool DMARunning;
|
17 |
|
18 | void InitializeAudio(int plln, int pllr, int i2sdiv, int i2sodd) {
|
19 | GPIO_InitTypeDef GPIO_InitStructure;
|
20 |
|
21 | // Intitialize state.
|
22 | CallbackFunction = NULL;
|
23 | CallbackContext = NULL;
|
24 | NextBufferSamples = NULL;
|
25 | NextBufferLength = 0;
|
26 | BufferNumber = 0;
|
27 | DMARunning = false;
|
28 |
|
29 | // Turn on peripherals.
|
30 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
31 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
|
32 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
|
33 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
|
34 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
|
35 |
|
36 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
|
37 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
|
38 |
|
39 | // Configure reset pin.
|
40 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;;
|
41 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
|
42 | GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
43 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
44 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
45 | GPIO_Init(GPIOD, &GPIO_InitStructure);
|
46 |
|
47 | // Configure I2C SCL and SDA pins.
|
48 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_9;
|
49 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
50 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
51 | GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
|
52 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
53 | GPIO_Init(GPIOB, &GPIO_InitStructure);
|
54 |
|
55 | GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
|
56 | GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);
|
57 |
|
58 | // Configure I2S MCK, SCK, SD pins.
|
59 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_10 | GPIO_Pin_12;
|
60 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
61 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
62 | GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
63 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
64 | GPIO_Init(GPIOC, &GPIO_InitStructure);
|
65 |
|
66 | GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_SPI3);
|
67 | GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SPI3);
|
68 | GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SPI3);
|
69 |
|
70 | // Configure I2S WS pin.
|
71 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
|
72 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
73 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
|
74 | GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
|
75 | GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
76 | GPIO_Init(GPIOA, &GPIO_InitStructure);
|
77 |
|
78 | GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI3);
|
79 |
|
80 | // Reset the codec.
|
81 | GPIOD ->BSRRH = 1 << 4;
|
82 | for (volatile int i = 0; i < 0x4fff; i++) {
|
83 | __asm__ volatile("nop");
|
84 | }
|
85 | GPIOD ->BSRRL = 1 << 4;
|
86 |
|
87 | // Reset I2C.
|
88 | RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
|
89 | RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
|
90 |
|
91 | // Configure I2C.
|
92 | uint32_t pclk1 = 42000000;
|
93 |
|
94 | I2C1 ->CR2 = pclk1 / 1000000; // Configure frequency and disable interrupts and DMA.
|
95 | I2C1 ->OAR1 = I2C_OAR1_ADDMODE | 0x33;
|
96 |
|
97 | // Configure I2C speed in standard mode.
|
98 | const uint32_t i2c_speed = 100000;
|
99 | int ccrspeed = pclk1 / (i2c_speed * 2);
|
100 | if (ccrspeed < 4) {
|
101 | ccrspeed = 4;
|
102 | }
|
103 | I2C1 ->CCR = ccrspeed;
|
104 | I2C1 ->TRISE = pclk1 / 1000000 + 1;
|
105 |
|
106 | I2C1 ->CR1 = I2C_CR1_ACK | I2C_CR1_PE; // Enable and configure the I2C peripheral.
|
107 |
|
108 | // Configure codec.
|
109 | WriteRegister(0x02, 0x01); // Keep codec powered off.
|
110 | WriteRegister(0x04, 0xaf); // SPK always off and HP always on.
|
111 |
|
112 | WriteRegister(0x05, 0x81); // Clock configuration: Auto detection.
|
113 | WriteRegister(0x06, 0x04); // Set slave mode and Philips audio standard.
|
114 |
|
115 | //SetAudioVolume(0xff);
|
116 | SetAudioVolume(0x00);
|
117 |
|
118 | // Power on the codec.
|
119 | WriteRegister(0x02, 0x9e);
|
120 |
|
121 | // Configure codec for fast shutdown.
|
122 | WriteRegister(0x0a, 0x00); // Disable the analog soft ramp.
|
123 | WriteRegister(0x0e, 0x04); // Disable the digital soft ramp.
|
124 |
|
125 | WriteRegister(0x27, 0x00); // Disable the limiter attack level.
|
126 | WriteRegister(0x1f, 0x0f); // Adjust bass and treble levels.
|
127 |
|
128 | WriteRegister(0x1a, 0x0a); // Adjust PCM volume level.
|
129 | WriteRegister(0x1b, 0x0a);
|
130 |
|
131 | // Disable I2S.
|
132 | SPI3 ->I2SCFGR = 0;
|
133 |
|
134 | // I2S clock configuration
|
135 | RCC ->CFGR &= ~RCC_CFGR_I2SSRC; // PLLI2S clock used as I2S clock source.
|
136 | RCC ->PLLI2SCFGR = (pllr << 28) | (plln << 6);
|
137 |
|
138 | // Enable PLLI2S and wait until it is ready.
|
139 | RCC ->CR |= RCC_CR_PLLI2SON;
|
140 | while (!(RCC ->CR & RCC_CR_PLLI2SRDY ))
|
141 | ;
|
142 |
|
143 | // Configure I2S.
|
144 | SPI3 ->I2SPR = i2sdiv | (i2sodd << 8) | SPI_I2SPR_MCKOE;
|
145 | SPI3 ->I2SCFGR = SPI_I2SCFGR_I2SMOD | SPI_I2SCFGR_I2SCFG_1
|
146 | | SPI_I2SCFGR_I2SE; // Master transmitter, Phillips mode, 16 bit values, clock polarity low, enable.
|
147 |
|
148 | }
|
149 |
|
150 | void AudioOn() {
|
151 | WriteRegister(0x02, 0x9e);
|
152 | SPI3 ->I2SCFGR = SPI_I2SCFGR_I2SMOD | SPI_I2SCFGR_I2SCFG_1
|
153 | | SPI_I2SCFGR_I2SE; // Master transmitter, Phillips mode, 16 bit values, clock polarity low, enable.
|
154 | }
|
155 |
|
156 | void AudioOff() {
|
157 | WriteRegister(0x02, 0x01);
|
158 | SPI3 ->I2SCFGR = 0;
|
159 | }
|
160 |
|
161 | void SetAudioVolume(int volume) {
|
162 | WriteRegister(0x20, (volume + 0x19) & 0xff);
|
163 | WriteRegister(0x21, (volume + 0x19) & 0xff);
|
164 | }
|
165 |
|
166 | void OutputAudioSample(int16_t sample) {
|
167 | while (!(SPI3 ->SR & SPI_SR_TXE ))
|
168 | ;
|
169 | SPI3 ->DR = sample;
|
170 | }
|
171 |
|
172 | void OutputAudioSampleWithoutBlocking(int16_t sample) {
|
173 | SPI3 ->DR = sample;
|
174 | }
|
175 |
|
176 | void PlayAudioWithCallback(AudioCallbackFunction *callback, void *context) {
|
177 | StopAudioDMA();
|
178 |
|
179 | NVIC_EnableIRQ(DMA1_Stream7_IRQn);
|
180 | NVIC_SetPriority(DMA1_Stream7_IRQn, 4);
|
181 |
|
182 | SPI3 ->CR2 |= SPI_CR2_TXDMAEN; // Enable I2S TX DMA request.
|
183 |
|
184 | CallbackFunction = callback;
|
185 | CallbackContext = context;
|
186 | BufferNumber = 0;
|
187 |
|
188 | if (CallbackFunction)
|
189 | CallbackFunction(CallbackContext, BufferNumber);
|
190 | }
|
191 |
|
192 | void StopAudio() {
|
193 | StopAudioDMA();
|
194 | SPI3 ->CR2 &= ~SPI_CR2_TXDMAEN; // Disable I2S TX DMA request.
|
195 | NVIC_DisableIRQ(DMA1_Stream7_IRQn);
|
196 | CallbackFunction = NULL;
|
197 | }
|
198 |
|
199 | void ProvideAudioBuffer(void *samples, int numsamples) {
|
200 | while (!ProvideAudioBufferWithoutBlocking(samples, numsamples))
|
201 | __asm__ volatile ("wfi");
|
202 | }
|
203 |
|
204 | bool ProvideAudioBufferWithoutBlocking(void *samples, int numsamples) {
|
205 | if (NextBufferSamples)
|
206 | return false;
|
207 |
|
208 | NVIC_DisableIRQ(DMA1_Stream7_IRQn);
|
209 |
|
210 | NextBufferSamples = samples;
|
211 | NextBufferLength = numsamples;
|
212 |
|
213 | if (!DMARunning)
|
214 | StartAudioDMAAndRequestBuffers();
|
215 |
|
216 | NVIC_EnableIRQ(DMA1_Stream7_IRQn);
|
217 |
|
218 | return true;
|
219 | }
|
220 |
|
221 | static void WriteRegister(uint8_t address, uint8_t value) {
|
222 | while (I2C1 ->SR2 & I2C_SR2_BUSY )
|
223 | ;
|
224 |
|
225 | I2C1 ->CR1 |= I2C_CR1_START; // Start the transfer sequence.
|
226 | while (!(I2C1 ->SR1 & I2C_SR1_SB ))
|
227 | ; // Wait for start bit.
|
228 |
|
229 | I2C1 ->DR = 0x94;
|
230 | while (!(I2C1 ->SR1 & I2C_SR1_ADDR ))
|
231 | ; // Wait for master transmitter mode.
|
232 | I2C1 ->SR2;
|
233 |
|
234 | I2C1 ->DR = address; // Transmit the address to write to.
|
235 | while (!(I2C1 ->SR1 & I2C_SR1_TXE ))
|
236 | ; // Wait for byte to move to shift register.
|
237 |
|
238 | I2C1 ->DR = value; // Transmit the value.
|
239 |
|
240 | while (!(I2C1 ->SR1 & I2C_SR1_BTF ))
|
241 | ; // Wait for all bytes to finish.
|
242 | I2C1 ->CR1 |= I2C_CR1_STOP; // End the transfer sequence.
|
243 | }
|
244 |
|
245 | static void StartAudioDMAAndRequestBuffers() {
|
246 | // Configure DMA stream.
|
247 | DMA1_Stream7 ->CR = (0 * DMA_SxCR_CHSEL_0 ) | // Channel 0
|
248 | (1 * DMA_SxCR_PL_0 ) | // Priority 1
|
249 | (1 * DMA_SxCR_PSIZE_0 ) | // PSIZE = 16 bit
|
250 | (1 * DMA_SxCR_MSIZE_0 ) | // MSIZE = 16 bit
|
251 | DMA_SxCR_MINC | // Increase memory address
|
252 | (1 * DMA_SxCR_DIR_0 ) | // Memory to peripheral
|
253 | DMA_SxCR_TCIE; // Transfer complete interrupt
|
254 | DMA1_Stream7 ->NDTR = NextBufferLength;
|
255 | DMA1_Stream7 ->PAR = (uint32_t) &SPI3 ->DR;
|
256 | DMA1_Stream7 ->M0AR = (uint32_t) NextBufferSamples;
|
257 | DMA1_Stream7 ->FCR = DMA_SxFCR_DMDIS;
|
258 | DMA1_Stream7 ->CR |= DMA_SxCR_EN;
|
259 |
|
260 | // Update state.
|
261 | NextBufferSamples = NULL;
|
262 | BufferNumber ^= 1;
|
263 | DMARunning = true;
|
264 |
|
265 | // Invoke callback if it exists to queue up another buffer.
|
266 | if (CallbackFunction)
|
267 | CallbackFunction(CallbackContext, BufferNumber);
|
268 | }
|
269 |
|
270 | static void StopAudioDMA() {
|
271 | DMA1_Stream7 ->CR &= ~DMA_SxCR_EN; // Disable DMA stream.
|
272 | while (DMA1_Stream7 ->CR & DMA_SxCR_EN )
|
273 | ; // Wait for DMA stream to stop.
|
274 |
|
275 | DMARunning = false;
|
276 | }
|
277 |
|
278 | void DMA1_Stream7_IRQHandler() {
|
279 | DMA1 ->HIFCR |= DMA_HIFCR_CTCIF7; // Clear interrupt flag.
|
280 |
|
281 | if (NextBufferSamples) {
|
282 | StartAudioDMAAndRequestBuffers();
|
283 | } else {
|
284 | DMARunning = false;
|
285 | }
|
286 | }
|