circuitpython/stm/audio.c
Damien George df6567e634 Merge map.h into obj.h.
Pretty much everyone needs to include map.h, since it's such an integral
part of the Micro Python object implementation.  Thus, the definitions
are now in obj.h instead.  map.h is removed.
2014-03-30 13:54:02 +01:00

257 lines
9.0 KiB
C

#include <stdint.h>
#include <string.h>
#include "stm32f4xx_dac.h"
#include "nlr.h"
#include "misc.h"
#include "mpconfig.h"
#include "qstr.h"
#include "parse.h"
#include "obj.h"
#include "runtime.h"
#include "audio.h"
STATIC void TIM7_Config(uint freq) {
// TIM7 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
// reset TIM7
TIM_DeInit(TIM7);
// Compute the prescaler value so TIM7 triggers at freq-Hz
uint16_t period = (uint16_t) ((SystemCoreClock / 2) / freq) - 1;
// 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);
// TIM7 TRGO selection
TIM_SelectOutputTrigger(TIM7, TIM_TRGOSource_Update);
// TIM7 enable counter
TIM_Cmd(TIM7, ENABLE);
}
/******************************************************************************/
// Micro Python bindings
typedef struct _pyb_audio_t {
mp_obj_base_t base;
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_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));
}
return mp_const_none;
}
#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 {
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);
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_KW(pyb_audio_dma_obj, 3, pyb_audio_dma);
STATIC const mp_map_elem_t pyb_audio_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_noise), (mp_obj_t)&pyb_audio_noise_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_triangle), (mp_obj_t)&pyb_audio_triangle_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_dac), (mp_obj_t)&pyb_audio_dac_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_dma), (mp_obj_t)&pyb_audio_dma_obj },
};
STATIC MP_DEFINE_CONST_DICT(pyb_audio_locals_dict, pyb_audio_locals_dict_table);
STATIC const mp_obj_type_t pyb_audio_type = {
{ &mp_type_type },
.name = MP_QSTR_,
.locals_dict = (mp_obj_t)&pyb_audio_locals_dict,
};
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)
STATIC mp_obj_t pyb_Audio(mp_obj_t id) {
// DAC peripheral clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
int dac_id = mp_obj_get_int(id);
uint pin;
const pyb_audio_t *dac_obj;
if (dac_id == 1) {
pin = GPIO_Pin_4;
dac_obj = &pyb_audio_channel_1;
} else {
pin = GPIO_Pin_5;
dac_obj = &pyb_audio_channel_2;
}
// DAC channel configuration
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// DAC channel Configuration
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(dac_obj->dac_channel, &DAC_InitStructure);
// Enable DAC Channel
DAC_Cmd(dac_obj->dac_channel, ENABLE);
// from now on use DAC_SetChannel[12]Data to trigger a conversion
// return static object
return (mp_obj_t)dac_obj;
}
MP_DEFINE_CONST_FUN_OBJ_1(pyb_Audio_obj, pyb_Audio);