-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaudio.c
More file actions
252 lines (207 loc) · 9.96 KB
/
audio.c
File metadata and controls
252 lines (207 loc) · 9.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#include "audio.h"
/* Initialize variables for fast fourier transform. */
arm_rfft_fast_instance_f32 S;
uint16_t __usAdcIntermediateSampleBuffer0[ADC_SAMPLES] = { 0U };
uint16_t __usAdcIntermediateSampleBuffer1[ADC_SAMPLES] = { 0U };
/**
* @brief Interrupt to handle the end of the DMA stream from ADCs.
* @param None
* @retval None
*/
void DMA2_Stream0_IRQHandler( void )
{
/* Disable interrupts and other tasks from running during this interrupt. */
UBaseType_t uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
/* Initialize start time.*/
static uint16_t usStartTime;
usStartTime = TIM12->CNT;
/* DMA Stream Half Transfer interrupt */
if( DMA_GetITStatus( DMA2_Stream0, DMA_IT_HTIF0 ) )
{
/* Clear DMA Stream Half Transfer interrupt pending bit */
DMA_ClearITPendingBit( DMA2_Stream0, DMA_IT_HTIF0 );
}
/* DMA Stream Transfer Complete interrupt */
if( DMA_GetITStatus( DMA2_Stream0, DMA_IT_TCIF0 ) )
{
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit( DMA2_Stream0, DMA_IT_TCIF0 );
}
/* Update the high water mark for task length. */
if ( (uint16_t)TIM12->CNT - usStartTime > xDebugStats.usCopyADCsFromDMAClocks )
{
xDebugStats.usCopyADCsFromDMAClocks = (uint16_t)TIM12->CNT - usStartTime;
}
/* Re-enable interrupts and other tasks. */
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}
/*-----------------------------------------------------------*/
/**
* @brief Perform FFT.
* @param Variable to store the resulting FFT into.
* @retval None
*/
void vPerformFFT( float32_t* ufFourierFrequency )
{
/** @NOTE: ufFourierFrquency[0] is the dc offset. To get the true dc offset
* you have to divide this number by FFT_SIZE or ADC_SAMPLES.
* @NOTE: The rest of the values are scaled by FFT_SIZE/2 or ADC_SAMPLES/4.
* So to get the true magnitude of the frequencies you have to divide
* by FFT_SIZE/2 or ADC_SAMPLES/4.
*
* The frequency that corresponds to each index can be determined by:
* Frequency = Idx * SamplingFrequency / FFT_SIZE
*
* For this project it equates to ~345Hz per index.
* 344.5 Hz = 44100 Hz / 128
*/
/* Create array for storing frequency information. */
float32_t ufComplexFFT[CFFT_SIZE] = { 0.0F };
/* DC offset has a different scaling than the non-zero frequencies. */
//ufScalingVector[0] = 1.0 / FFT_SIZE;
//for (uint16_t i = 1; i < (FFT_SIZE/2); i++)
//{
// /* I do not know where these scalings come from, just that they work. */
// ufScalingVector[i] = 1.0 / (FFT_SIZE/2.0);
//}
/* This can be uncommented to test the fft logic. */
//for (uint16_t i = 0; i < ADC_SAMPLES; i++)
//{
// /* Frequency magnitude at index 10 (4410 Hz) should be the max at 10
// * with a DC offset of 50.
// */
// ufAdcSampleBuffer[i] = 10*arm_sin_f32( 2.0*3.14159F*i/8.0 ) + 50;
//}
/* Perform real FFT (nobody understands complex numbers anyways).
* Enter critical here to ensure that the AdcSampleBuffer is not
* updated in the middle of the FFT.*/
taskENTER_CRITICAL();
float32_t ufAdcBuffer[ADC_SAMPLES] = { 0.0F };
switch( DMA_GetCurrentMemoryTarget( DMA2_Stream0 ) )
{
case 0:
for ( uint16_t i = 0; i < ADC_SAMPLES; i++ )
{
ufAdcBuffer[i] = (float32_t)__usAdcIntermediateSampleBuffer1[i];
}
arm_rfft_fast_f32( &S, ufAdcBuffer, ufComplexFFT, 0 );
break;
case 1:
default:
for ( uint16_t i = 0; i < ADC_SAMPLES; i++ )
{
ufAdcBuffer[i] = (float32_t)__usAdcIntermediateSampleBuffer0[i];
}
arm_rfft_fast_f32( &S, ufAdcBuffer, ufComplexFFT, 0 );
break;
}
taskEXIT_CRITICAL();
/* Output of FFT is always complex, so convert to magnitude. */
arm_cmplx_mag_f32( ufComplexFFT, ufFourierFrequency, RFFT_SIZE );
/* Rescale ufFourierFrequency to its true amplitudes.
* I have no idea where these scalings come from, but it works. */
float32_t scale = 1.0F / CFFT_SIZE;
arm_scale_f32( ufFourierFrequency, scale, ufFourierFrequency, RFFT_SIZE );
}
/*-----------------------------------------------------------*/
/**
* @brief Initialization of the audio peripherals.
* @param None
* @retval None
*/
void vInitAudio( void )
{
/* Define GPIO init structure. */
GPIO_InitTypeDef GPIO_InitStructure;
/* Define ADC init structures. */
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
/* Define TIM init structures. */
TIM_TimeBaseInitTypeDef TIM_InitStructure;
/* Initialize the fast fourier transform. */
(void) arm_rfft_fast_init_f32( &S, CFFT_SIZE );
/* Enable the GPIO and ADC clocks. */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 84 MHz
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); // 168 MHz
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // 168 MHz
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 42 MHz
/* Initialize for ADC1 on PC4 using IN14. */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Initialize PD3 for debugging purposes. */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY + 1U;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Clear the structure for later uses. */
NVIC_InitStructure = ( const NVIC_InitTypeDef ){ 0 };
/* TIM2 sample timer setup (44.1 kHz). */
TIM_InitStructure.TIM_Prescaler = 0;
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_InitStructure.TIM_Period = ( ( 42000000U / ( TIM_InitStructure.TIM_Prescaler + 1 ) ) / SAMPLING_FREQUENCY ) - 1;
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2, &TIM_InitStructure);
/* TIM2 TRGO selection */
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T2_TRGO
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
/* DMA1 channel 0 configuration. */
DMA_DeInit( DMA2_Stream0 );
DMA_DoubleBufferModeConfig( DMA2_Stream0, (uint32_t)&__usAdcIntermediateSampleBuffer1[0], (uint32_t)&__usAdcIntermediateSampleBuffer0[0] );
DMA_DoubleBufferModeCmd( DMA2_Stream0, ENABLE );
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // physical address of register to load into memory
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&__usAdcIntermediateSampleBuffer0[0]; // physical address of memeory to be loaded
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // shift data from peripheral to memeory
DMA_InitStructure.DMA_BufferSize = ADC_SAMPLES;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // automatically increase buffer index
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 bits
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 16 bits
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Always keep this thing running
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
/* Enable DMA stream transfer complete interrupt. */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
DMA_Cmd(DMA2_Stream0, ENABLE);
/* Init ADCs in independent mode, div clock by two */
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // Not sure if this is needed
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
/* init ADC1: 12bit, single-conversion */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 1 channel
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Conversions triggered
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* Enable DMA Disable selection. */
ADC1->CR2 |= ADC_CR2_DDS;
ADC_RegularChannelConfig( ADC1, ADC_Channel_14, 1, ADC_SampleTime_15Cycles );
ADC_DMACmd( ADC1, ENABLE );
ADC_Cmd( ADC1, ENABLE );
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv( ADC1 );
}
/*-----------------------------------------------------------*/