Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
TASTE
uPython-mirror
Commits
5322ec09
Commit
5322ec09
authored
Mar 09, 2014
by
Damien George
Browse files
stm: Add DMA support to Audio object.
parent
2da9830b
Changes
1
Hide whitespace changes
Inline
Side-by-side
stm/audio.c
View file @
5322ec09
#include <stdint.h>
#include <string.h>
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_dac.h"
#include "nlr.h"
...
...
@@ -11,38 +9,34 @@
#include "qstr.h"
#include "parse.h"
#include "obj.h"
#include "map.h"
#include "runtime.h"
#include "audio.h"
#define SAMPLE_BUF_SIZE (32)
STATIC
void
TIM7_Config
(
uint
freq
)
{
// TIM7 clock enable
RCC_APB1PeriphClockCmd
(
RCC_APB1Periph_TIM7
,
ENABLE
);
// sample_buf_in is always the same or ahead of sample_buf_out
// when they are the same, there are no more samples left to process
// in this scheme, there is always 1 unusable byte in the buffer, just before sample_buf_out
int
sample_buf_in
;
int
sample_buf_out
;
byte
sample_buf
[
SAMPLE_BUF_SIZE
];
// reset TIM7
TIM_DeInit
(
TIM7
);
bool
audio_is_full
(
void
)
{
return
((
sample_buf_in
+
1
)
%
SAMPLE_BUF_SIZE
)
==
sample_buf_out
;
}
// Compute the prescaler value so TIM7 triggers at freq-Hz
uint16_t
period
=
(
uint16_t
)
((
SystemCoreClock
/
2
)
/
freq
)
-
1
;
void
audio_fill
(
byte
sample
)
{
sample_buf
[
sample_buf_in
]
=
sample
;
sample_buf_in
=
(
sample_buf_in
+
1
)
%
SAMPLE_BUF_SIZE
;
// enable interrupt
}
// Time base configuration
TIM_TimeBaseInitTypeDef
TIM_TimeBaseStructure
;
TIM_TimeBaseStructure
.
TIM_Period
=
period
;
// timer triggers with this period
TIM_TimeBaseStructure
.
TIM_Prescaler
=
0
;
// timer runs at SystemCoreClock / 2
TIM_TimeBaseStructure
.
TIM_ClockDivision
=
0
;
// unused for TIM7
TIM_TimeBaseStructure
.
TIM_CounterMode
=
TIM_CounterMode_Up
;
// unused for TIM7
TIM_TimeBaseInit
(
TIM7
,
&
TIM_TimeBaseStructure
);
void
audio_drain
(
void
)
{
if
(
sample_buf_in
==
sample_buf_out
)
{
// buffer is empty; disable interrupt
}
else
{
// buffer has a sample; output it
byte
sample
=
sample_buf
[
sample_buf_out
];
DAC_SetChannel2Data
(
DAC_Align_8b_R
,
sample
);
sample_buf_out
=
(
sample_buf_out
+
1
)
%
SAMPLE_BUF_SIZE
;
}
// TIM7 TRGO selection
TIM_SelectOutputTrigger
(
TIM7
,
TIM_TRGOSource_Update
);
// TIM7 enable counter
TIM_Cmd
(
TIM7
,
ENABLE
);
}
/******************************************************************************/
...
...
@@ -50,13 +44,67 @@ void audio_drain(void) {
typedef
struct
_pyb_audio_t
{
mp_obj_base_t
base
;
int
dac_id
;
// 1 or 2
uint
dac_channel
;
// DAC_Channel_1 or DAC_Channel_2
DMA_Stream_TypeDef
*
dma_stream
;
// DMA1_Stream6 or DMA1_Stream7
}
pyb_audio_t
;
mp_obj_t
pyb_audio_noise
(
mp_obj_t
self_in
,
mp_obj_t
freq
)
{
pyb_audio_t
*
self
=
self_in
;
// set TIM7 to trigger the DAC at the given frequency
TIM7_Config
(
mp_obj_get_int
(
freq
));
DAC_Cmd
(
self
->
dac_channel
,
DISABLE
);
DAC_InitTypeDef
DAC_InitStructure
;
DAC_InitStructure
.
DAC_Trigger
=
DAC_Trigger_T7_TRGO
;
DAC_InitStructure
.
DAC_WaveGeneration
=
DAC_WaveGeneration_Noise
;
DAC_InitStructure
.
DAC_LFSRUnmask_TriangleAmplitude
=
DAC_LFSRUnmask_Bits10_0
;
DAC_InitStructure
.
DAC_OutputBuffer
=
DAC_OutputBuffer_Enable
;
DAC_Init
(
self
->
dac_channel
,
&
DAC_InitStructure
);
DAC_Cmd
(
self
->
dac_channel
,
ENABLE
);
if
(
self
->
dac_channel
==
DAC_Channel_1
)
{
DAC_SetChannel1Data
(
DAC_Align_12b_L
,
0x7ff0
);
}
else
{
DAC_SetChannel2Data
(
DAC_Align_12b_L
,
0x7ff0
);
}
return
mp_const_none
;
}
mp_obj_t
pyb_audio_triangle
(
mp_obj_t
self_in
,
mp_obj_t
freq
)
{
pyb_audio_t
*
self
=
self_in
;
// set TIM7 to trigger the DAC at the given frequency
TIM7_Config
(
mp_obj_get_int
(
freq
));
DAC_Cmd
(
self
->
dac_channel
,
DISABLE
);
DAC_InitTypeDef
DAC_InitStructure
;
DAC_InitStructure
.
DAC_Trigger
=
DAC_Trigger_T7_TRGO
;
DAC_InitStructure
.
DAC_WaveGeneration
=
DAC_WaveGeneration_Triangle
;
DAC_InitStructure
.
DAC_LFSRUnmask_TriangleAmplitude
=
DAC_TriangleAmplitude_1023
;
DAC_InitStructure
.
DAC_OutputBuffer
=
DAC_OutputBuffer_Enable
;
DAC_Init
(
self
->
dac_channel
,
&
DAC_InitStructure
);
DAC_Cmd
(
self
->
dac_channel
,
ENABLE
);
// set base value of triangle wave
if
(
self
->
dac_channel
==
DAC_Channel_1
)
{
DAC_SetChannel1Data
(
DAC_Align_12b_R
,
0x100
);
}
else
{
DAC_SetChannel2Data
(
DAC_Align_12b_R
,
0x100
);
}
return
mp_const_none
;
}
// direct access to DAC
mp_obj_t
pyb_audio_dac
(
mp_obj_t
self_in
,
mp_obj_t
val
)
{
pyb_audio_t
*
self
=
self_in
;
if
(
self
->
dac_
id
==
1
)
{
if
(
self
->
dac_
channel
==
DAC_Channel_
1
)
{
DAC_SetChannel1Data
(
DAC_Align_8b_R
,
mp_obj_get_int
(
val
));
}
else
{
DAC_SetChannel2Data
(
DAC_Align_8b_R
,
mp_obj_get_int
(
val
));
...
...
@@ -64,27 +112,92 @@ mp_obj_t pyb_audio_dac(mp_obj_t self_in, mp_obj_t val) {
return
mp_const_none
;
}
mp_obj_t
pyb_audio_is_full
(
mp_obj_t
self_in
)
{
if
(
audio_is_full
())
{
return
mp_const_true
;
#define DAC_DHR8R1_ADDRESS (DAC_BASE + 0x10)
#define DAC_DHR8R2_ADDRESS (DAC_BASE + 0x1c)
// initiates a burst of RAM->DAC using DMA
// input data is treated as an array of bytes (8 bit data)
// TIM7 is used to set the frequency of the transfer
mp_obj_t
pyb_audio_dma
(
uint
n_args
,
const
mp_obj_t
*
args
,
mp_map_t
*
kw_args
)
{
pyb_audio_t
*
self
=
args
[
0
];
// set TIM7 to trigger the DAC at the given frequency
TIM7_Config
(
mp_obj_get_int
(
args
[
2
]));
mp_obj_type_t
*
type
=
mp_obj_get_type
(
args
[
1
]);
if
(
type
->
buffer_p
.
get_buffer
==
NULL
)
{
nlr_jump
(
mp_obj_new_exception_msg
(
&
mp_type_TypeError
,
"buffer argument must support buffer protocol"
));
}
buffer_info_t
bufinfo
;
type
->
buffer_p
.
get_buffer
(
args
[
1
],
&
bufinfo
,
BUFFER_READ
);
RCC_AHB1PeriphClockCmd
(
RCC_AHB1Periph_DMA1
,
ENABLE
);
DMA_Cmd
(
self
->
dma_stream
,
DISABLE
);
while
(
DMA_GetCmdStatus
(
self
->
dma_stream
)
!=
DISABLE
)
{
}
DAC_Cmd
(
self
->
dac_channel
,
DISABLE
);
// DAC channel configuration
DAC_InitTypeDef
DAC_InitStructure
;
DAC_InitStructure
.
DAC_Trigger
=
DAC_Trigger_T7_TRGO
;
DAC_InitStructure
.
DAC_WaveGeneration
=
DAC_WaveGeneration_None
;
DAC_InitStructure
.
DAC_LFSRUnmask_TriangleAmplitude
=
DAC_TriangleAmplitude_1
;
// unused, but need to set it to a valid value
DAC_InitStructure
.
DAC_OutputBuffer
=
DAC_OutputBuffer_Enable
;
DAC_Init
(
self
->
dac_channel
,
&
DAC_InitStructure
);
// DMA1_Stream[67] channel7 configuration
DMA_DeInit
(
self
->
dma_stream
);
DMA_InitTypeDef
DMA_InitStructure
;
DMA_InitStructure
.
DMA_Channel
=
DMA_Channel_7
;
if
(
self
->
dac_channel
==
DAC_Channel_1
)
{
DMA_InitStructure
.
DMA_PeripheralBaseAddr
=
DAC_DHR8R1_ADDRESS
;
}
else
{
return
mp_const_false
;
DMA_InitStructure
.
DMA_PeripheralBaseAddr
=
DAC_DHR8R2_ADDRESS
;
}
}
DMA_InitStructure
.
DMA_Memory0BaseAddr
=
(
uint32_t
)
bufinfo
.
buf
;
DMA_InitStructure
.
DMA_DIR
=
DMA_DIR_MemoryToPeripheral
;
DMA_InitStructure
.
DMA_BufferSize
=
bufinfo
.
len
;
DMA_InitStructure
.
DMA_PeripheralInc
=
DMA_PeripheralInc_Disable
;
DMA_InitStructure
.
DMA_MemoryInc
=
DMA_MemoryInc_Enable
;
DMA_InitStructure
.
DMA_PeripheralDataSize
=
DMA_PeripheralDataSize_Byte
;
DMA_InitStructure
.
DMA_MemoryDataSize
=
DMA_MemoryDataSize_Byte
;
mp_map_elem_t
*
kw_mode
=
mp_map_lookup
(
kw_args
,
MP_OBJ_NEW_QSTR
(
qstr_from_str
(
"mode"
)),
MP_MAP_LOOKUP
);
DMA_InitStructure
.
DMA_Mode
=
kw_mode
==
NULL
?
DMA_Mode_Normal
:
mp_obj_get_int
(
kw_mode
->
value
);
// normal = 0, circular = 0x100
DMA_InitStructure
.
DMA_Priority
=
DMA_Priority_High
;
DMA_InitStructure
.
DMA_FIFOMode
=
DMA_FIFOMode_Disable
;
DMA_InitStructure
.
DMA_FIFOThreshold
=
DMA_FIFOThreshold_HalfFull
;
DMA_InitStructure
.
DMA_MemoryBurst
=
DMA_MemoryBurst_Single
;
DMA_InitStructure
.
DMA_PeripheralBurst
=
DMA_PeripheralBurst_Single
;
DMA_Init
(
self
->
dma_stream
,
&
DMA_InitStructure
);
// enable DMA stream
DMA_Cmd
(
self
->
dma_stream
,
ENABLE
);
while
(
DMA_GetCmdStatus
(
self
->
dma_stream
)
==
DISABLE
)
{
}
// enable DAC channel
DAC_Cmd
(
self
->
dac_channel
,
ENABLE
);
// enable DMA for DAC channel
DAC_DMACmd
(
self
->
dac_channel
,
ENABLE
);
//printf("DMA: %p %lu\n", bufinfo.buf, bufinfo.len);
mp_obj_t
pyb_audio_fill
(
mp_obj_t
self_in
,
mp_obj_t
val
)
{
audio_fill
(
mp_obj_get_int
(
val
));
return
mp_const_none
;
}
STATIC
MP_DEFINE_CONST_FUN_OBJ_2
(
pyb_audio_noise_obj
,
pyb_audio_noise
);
STATIC
MP_DEFINE_CONST_FUN_OBJ_2
(
pyb_audio_triangle_obj
,
pyb_audio_triangle
);
STATIC
MP_DEFINE_CONST_FUN_OBJ_2
(
pyb_audio_dac_obj
,
pyb_audio_dac
);
STATIC
MP_DEFINE_CONST_FUN_OBJ_1
(
pyb_audio_is_full_obj
,
pyb_audio_is_full
);
STATIC
MP_DEFINE_CONST_FUN_OBJ_2
(
pyb_audio_fill_obj
,
pyb_audio_fill
);
STATIC
MP_DEFINE_CONST_FUN_OBJ_KW
(
pyb_audio_dma_obj
,
3
,
pyb_audio_dma
);
STATIC
const
mp_method_t
pyb_audio_methods
[]
=
{
{
"noise"
,
&
pyb_audio_noise_obj
},
{
"triangle"
,
&
pyb_audio_triangle_obj
},
{
"dac"
,
&
pyb_audio_dac_obj
},
{
"is_full"
,
&
pyb_audio_is_full_obj
},
{
"fill"
,
&
pyb_audio_fill_obj
},
{
"dma"
,
&
pyb_audio_dma_obj
},
{
NULL
,
NULL
},
};
...
...
@@ -94,8 +207,8 @@ STATIC const mp_obj_type_t pyb_audio_type = {
.
methods
=
pyb_audio_methods
,
};
STATIC
const
pyb_audio_t
pyb_audio_channel_1
=
{{
&
pyb_audio_type
},
1
};
STATIC
const
pyb_audio_t
pyb_audio_channel_2
=
{{
&
pyb_audio_type
},
2
};
STATIC
const
pyb_audio_t
pyb_audio_channel_1
=
{{
&
pyb_audio_type
},
DAC_Channel_1
,
DMA1_Stream5
};
STATIC
const
pyb_audio_t
pyb_audio_channel_2
=
{{
&
pyb_audio_type
},
DAC_Channel_2
,
DMA1_Stream6
};
// create the audio object
// currently support either DAC1 on X5 (id = 1) or DAC2 on X6 (id = 2)
...
...
@@ -106,17 +219,14 @@ STATIC mp_obj_t pyb_Audio(mp_obj_t id) {
int
dac_id
=
mp_obj_get_int
(
id
);
uint
pin
;
uint
channel
;
mp_obj_t
dac_obj
;
const
pyb_audio_t
*
dac_obj
;
if
(
dac_id
==
1
)
{
pin
=
GPIO_Pin_4
;
channel
=
DAC_Channel_1
;
dac_obj
=
(
mp_obj_t
)
&
pyb_audio_channel_1
;
dac_obj
=
&
pyb_audio_channel_1
;
}
else
{
pin
=
GPIO_Pin_5
;
channel
=
DAC_Channel_2
;
dac_obj
=
(
mp_obj_t
)
&
pyb_audio_channel_2
;
dac_obj
=
&
pyb_audio_channel_2
;
}
// DAC channel configuration
...
...
@@ -130,19 +240,17 @@ STATIC mp_obj_t pyb_Audio(mp_obj_t id) {
DAC_InitTypeDef
DAC_InitStructure
;
DAC_InitStructure
.
DAC_Trigger
=
DAC_Trigger_None
;
DAC_InitStructure
.
DAC_WaveGeneration
=
DAC_WaveGeneration_None
;
DAC_InitStructure
.
DAC_LFSRUnmask_TriangleAmplitude
=
DAC_TriangleAmplitude_1023
;
DAC_InitStructure
.
DAC_OutputBuffer
=
DAC_OutputBuffer_Enable
;
DAC_Init
(
channel
,
&
DAC_InitStructure
);
DAC_Init
(
dac_obj
->
dac_
channel
,
&
DAC_InitStructure
);
// Enable DAC Channel
DAC_Cmd
(
channel
,
ENABLE
);
DAC_Cmd
(
dac_obj
->
dac_
channel
,
ENABLE
);
// from now on use DAC_SetChannel[12]Data to trigger a conversion
sample_buf_in
=
0
;
sample_buf_out
=
0
;
// return static object
return
dac_obj
;
return
(
mp_obj_t
)
dac_obj
;
}
MP_DEFINE_CONST_FUN_OBJ_1
(
pyb_Audio_obj
,
pyb_Audio
);
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment