From b13492f8ad554935b9a33fb7c0560223134254df Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 24 Mar 2014 15:15:33 +0000 Subject: [PATCH] stmhal: Add DAC driver. --- stmhal/Makefile | 4 +- stmhal/boards/NETDUINO_PLUS_2/mpconfigboard.h | 2 +- stmhal/boards/PYBOARD3/mpconfigboard.h | 2 +- stmhal/boards/PYBOARD4/mpconfigboard.h | 2 +- stmhal/boards/PYBv10/mpconfigboard.h | 2 +- stmhal/boards/STM32F4DISC/mpconfigboard.h | 2 +- stmhal/dac.c | 276 ++++++++++++++++++ stmhal/dac.h | 3 + stmhal/main.c | 6 + stmhal/pybmodule.c | 8 +- stmhal/qstrdefsport.h | 2 +- 11 files changed, 298 insertions(+), 11 deletions(-) create mode 100644 stmhal/dac.c create mode 100644 stmhal/dac.h diff --git a/stmhal/Makefile b/stmhal/Makefile index fbe8a450b8..b146a1b37b 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -92,9 +92,9 @@ SRC_C = \ lcd.c \ accel.c \ servo.c \ + dac.c \ # timer.c \ -# audio.c \ # i2c.c \ # adc.c \ # pybwlan.c \ @@ -106,6 +106,8 @@ SRC_S = \ SRC_HAL = $(addprefix $(HAL_DIR)/src/,\ stm32f4xx_hal.c \ stm32f4xx_hal_cortex.c \ + stm32f4xx_hal_dac.c \ + stm32f4xx_hal_dac_ex.c \ stm32f4xx_hal_dma.c \ stm32f4xx_hal_flash.c \ stm32f4xx_hal_flash_ex.c \ diff --git a/stmhal/boards/NETDUINO_PLUS_2/mpconfigboard.h b/stmhal/boards/NETDUINO_PLUS_2/mpconfigboard.h index 412dede131..948b886819 100644 --- a/stmhal/boards/NETDUINO_PLUS_2/mpconfigboard.h +++ b/stmhal/boards/NETDUINO_PLUS_2/mpconfigboard.h @@ -16,7 +16,7 @@ #define MICROPY_HW_ENABLE_RTC (0) #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SERVO (1) -#define MICROPY_HW_ENABLE_AUDIO (0) +#define MICROPY_HW_ENABLE_DAC (0) // USRSW is pulled low. Pressing the button makes the input go high. #define USRSW_PIN (pin_B11) diff --git a/stmhal/boards/PYBOARD3/mpconfigboard.h b/stmhal/boards/PYBOARD3/mpconfigboard.h index e0920f6859..1cd1d94818 100644 --- a/stmhal/boards/PYBOARD3/mpconfigboard.h +++ b/stmhal/boards/PYBOARD3/mpconfigboard.h @@ -12,7 +12,7 @@ #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SERVO (1) -#define MICROPY_HW_ENABLE_AUDIO (0) +#define MICROPY_HW_ENABLE_DAC (0) // USRSW has no pullup or pulldown, and pressing the switch makes the input go low #define USRSW_PIN (pin_A13) diff --git a/stmhal/boards/PYBOARD4/mpconfigboard.h b/stmhal/boards/PYBOARD4/mpconfigboard.h index c3d5e119c9..9a046ffffa 100644 --- a/stmhal/boards/PYBOARD4/mpconfigboard.h +++ b/stmhal/boards/PYBOARD4/mpconfigboard.h @@ -12,7 +12,7 @@ #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SERVO (1) -#define MICROPY_HW_ENABLE_AUDIO (1) +#define MICROPY_HW_ENABLE_DAC (1) // USRSW has no pullup or pulldown, and pressing the switch makes the input go low #define USRSW_PIN (pin_B3) diff --git a/stmhal/boards/PYBv10/mpconfigboard.h b/stmhal/boards/PYBv10/mpconfigboard.h index fba7ab4aba..f5ebe25cc4 100644 --- a/stmhal/boards/PYBv10/mpconfigboard.h +++ b/stmhal/boards/PYBv10/mpconfigboard.h @@ -12,7 +12,7 @@ #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SERVO (1) -#define MICROPY_HW_ENABLE_AUDIO (1) +#define MICROPY_HW_ENABLE_DAC (1) // USRSW has no pullup or pulldown, and pressing the switch makes the input go low #define USRSW_PIN (pin_B3) diff --git a/stmhal/boards/STM32F4DISC/mpconfigboard.h b/stmhal/boards/STM32F4DISC/mpconfigboard.h index f713f9b756..2a58b27bdf 100644 --- a/stmhal/boards/STM32F4DISC/mpconfigboard.h +++ b/stmhal/boards/STM32F4DISC/mpconfigboard.h @@ -12,7 +12,7 @@ #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_ENABLE_SERVO (0) -#define MICROPY_HW_ENABLE_AUDIO (0) +#define MICROPY_HW_ENABLE_DAC (0) // USRSW is pulled low. Pressing the button makes the input go high. #define USRSW_PIN (pin_A0) diff --git a/stmhal/dac.c b/stmhal/dac.c new file mode 100644 index 0000000000..127623d9b4 --- /dev/null +++ b/stmhal/dac.c @@ -0,0 +1,276 @@ +#include +#include + +#include "stm32f4xx_hal.h" + +#include "nlr.h" +#include "misc.h" +#include "mpconfig.h" +#include "qstr.h" +#include "parse.h" +#include "obj.h" +#include "map.h" +#include "runtime.h" +#include "dac.h" + +TIM_HandleTypeDef TIM6_Handle; +STATIC DAC_HandleTypeDef DAC_Handle; + +void dac_init(void) { + DAC_Handle.Instance = DAC; + DAC_Handle.State = HAL_DAC_STATE_RESET; + HAL_DAC_Init(&DAC_Handle); +} + +STATIC void TIM6_Config(uint freq) { + // TIM6 clock enable + __TIM6_CLK_ENABLE(); + + // Compute the prescaler value so TIM6 triggers at freq-Hz + uint16_t period = (uint16_t) ((SystemCoreClock / 2) / freq) - 1; + + // time base clock configuration + TIM6_Handle.Instance = TIM6; + TIM6_Handle.Init.Period = period; + TIM6_Handle.Init.Prescaler = 0; // timer runs at SystemCoreClock / 2 + TIM6_Handle.Init.ClockDivision = 0; // unused for TIM6 + TIM6_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; // unused for TIM6 + HAL_TIM_Base_Init(&TIM6_Handle); + + // TIM6 TRGO selection + TIM_MasterConfigTypeDef config; + config.MasterOutputTrigger = TIM_TRGO_UPDATE; + config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + HAL_TIMEx_MasterConfigSynchronization(&TIM6_Handle, &config); + + // TIM6 start counter + HAL_TIM_Base_Start(&TIM6_Handle); +} + +/******************************************************************************/ +// Micro Python bindings + +typedef struct _pyb_dac_obj_t { + mp_obj_base_t base; + uint32_t dac_channel; // DAC_CHANNEL_1 or DAC_CHANNEL_2 + DMA_Stream_TypeDef *dma_stream; // DMA1_Stream5 or DMA1_Stream6 + machine_uint_t state; +} pyb_dac_obj_t; + +STATIC pyb_dac_obj_t pyb_dac_channel_1 = {{&pyb_dac_type}, DAC_CHANNEL_1, DMA1_Stream5}; +STATIC pyb_dac_obj_t pyb_dac_channel_2 = {{&pyb_dac_type}, DAC_CHANNEL_2, DMA1_Stream6}; + +// create the dac object +// currently support either DAC1 on X5 (id = 1) or DAC2 on X6 (id = 2) + +STATIC mp_obj_t pyb_dac_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { + // check arguments + if (!(n_args == 1 && n_kw == 0)) { + nlr_jump(mp_obj_new_exception_msg(&mp_type_ValueError, "Dac accepts 1 argument")); + } + + machine_int_t dac_id = mp_obj_get_int(args[0]); + uint32_t pin; + pyb_dac_obj_t *dac_obj; + + if (dac_id == 1) { + pin = GPIO_PIN_4; + dac_obj = &pyb_dac_channel_1; + } else if (dac_id == 2) { + pin = GPIO_PIN_5; + dac_obj = &pyb_dac_channel_2; + } else { + nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Dac %d does not exist", dac_id)); + } + + // GPIO configuration + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.Pin = pin; + GPIO_InitStructure.Mode = GPIO_MODE_ANALOG; + GPIO_InitStructure.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); + + // DAC peripheral clock + __DAC_CLK_ENABLE(); + + // stop anything already going on + HAL_DAC_Stop(&DAC_Handle, dac_obj->dac_channel); + HAL_DAC_Stop_DMA(&DAC_Handle, dac_obj->dac_channel); + + dac_obj->state = 0; + + // return object + return dac_obj; +} + +STATIC mp_obj_t pyb_dac_noise(mp_obj_t self_in, mp_obj_t freq) { + pyb_dac_obj_t *self = self_in; + + // set TIM6 to trigger the DAC at the given frequency + TIM6_Config(mp_obj_get_int(freq)); + + if (self->state != 2) { + // configure DAC to trigger via TIM6 + DAC_ChannelConfTypeDef config; + config.DAC_Trigger = DAC_TRIGGER_T6_TRGO; + config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + HAL_DAC_ConfigChannel(&DAC_Handle, &config, self->dac_channel); + self->state = 2; + } + + // set noise wave generation + HAL_DACEx_NoiseWaveGenerate(&DAC_Handle, self->dac_channel, DAC_LFSRUNMASK_BITS10_0); + HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_12B_L, 0x7ff0); + HAL_DAC_Start(&DAC_Handle, self->dac_channel); + + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_dac_noise_obj, pyb_dac_noise); + +STATIC mp_obj_t pyb_dac_triangle(mp_obj_t self_in, mp_obj_t freq) { + pyb_dac_obj_t *self = self_in; + + // set TIM6 to trigger the DAC at the given frequency + TIM6_Config(mp_obj_get_int(freq)); + + if (self->state != 2) { + // configure DAC to trigger via TIM6 + DAC_ChannelConfTypeDef config; + config.DAC_Trigger = DAC_TRIGGER_T6_TRGO; + config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + HAL_DAC_ConfigChannel(&DAC_Handle, &config, self->dac_channel); + self->state = 2; + } + + // set triangle wave generation + HAL_DACEx_TriangleWaveGenerate(&DAC_Handle, self->dac_channel, DAC_TRIANGLEAMPLITUDE_1023); + HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_12B_R, 0x100); + HAL_DAC_Start(&DAC_Handle, self->dac_channel); + + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_dac_triangle_obj, pyb_dac_triangle); + +// direct access to DAC (8 bit only at the moment) +STATIC mp_obj_t pyb_dac_write(mp_obj_t self_in, mp_obj_t val) { + pyb_dac_obj_t *self = self_in; + + if (self->state != 1) { + DAC_ChannelConfTypeDef config; + config.DAC_Trigger = DAC_TRIGGER_NONE; + config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; + HAL_DAC_ConfigChannel(&DAC_Handle, &config, self->dac_channel); + self->state = 1; + } + + HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_8B_R, mp_obj_get_int(val)); + HAL_DAC_Start(&DAC_Handle, self->dac_channel); + + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_dac_write_obj, pyb_dac_write); + +// initiates a burst of RAM->DAC using DMA +// input data is treated as an array of bytes (8 bit data) +// TIM6 is used to set the frequency of the transfer +// TODO still needs some attention to get it working properly +mp_obj_t pyb_dac_dma(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { + pyb_dac_obj_t *self = args[0]; + + // set TIM6 to trigger the DAC at the given frequency + TIM6_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); + + __DMA1_CLK_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); + */ + + if (self->state != 3) { + DAC_ChannelConfTypeDef config; + config.DAC_Trigger = DAC_TRIGGER_T6_TRGO; + config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; + HAL_DAC_ConfigChannel(&DAC_Handle, &config, self->dac_channel); + self->state = 3; + } + + // DMA1_Stream[67] channel7 configuration + DMA_HandleTypeDef DMA_Handle; + DMA_Handle.Instance = self->dma_stream; + DMA_Handle.Init.Channel = DMA_CHANNEL_7; + DMA_Handle.Init.Direction = DMA_MEMORY_TO_PERIPH; + DMA_Handle.Init.PeriphInc = DMA_PINC_DISABLE; + DMA_Handle.Init.MemInc = DMA_MINC_ENABLE; + DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + mp_map_elem_t *kw_mode = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(qstr_from_str("mode")), MP_MAP_LOOKUP); + DMA_Handle.Init.Mode = kw_mode == NULL ? DMA_NORMAL : mp_obj_get_int(kw_mode->value); // normal = 0, circular = 0x100 + DMA_Handle.Init.Priority = DMA_PRIORITY_HIGH; + DMA_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + DMA_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL; + DMA_Handle.Init.MemBurst = DMA_MBURST_SINGLE; + DMA_Handle.Init.PeriphBurst = DMA_PBURST_SINGLE; + HAL_DMA_Init(&DMA_Handle); + + __HAL_LINKDMA(&DAC_Handle, DMA_Handle1, DMA_Handle); + + HAL_DAC_Start_DMA(&DAC_Handle, self->dac_channel, (uint32_t*)bufinfo.buf, bufinfo.len, DAC_ALIGN_8B_R); + + /* + // 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_KW(pyb_dac_dma_obj, 3, pyb_dac_dma); + +STATIC const mp_method_t pyb_dac_methods[] = { + { "noise", &pyb_dac_noise_obj }, + { "triangle", &pyb_dac_triangle_obj }, + { "write", &pyb_dac_write_obj }, + { "dma", &pyb_dac_dma_obj }, + { NULL, NULL }, +}; + +const mp_obj_type_t pyb_dac_type = { + { &mp_type_type }, + .name = MP_QSTR_DAC, + .make_new = pyb_dac_make_new, + .methods = pyb_dac_methods, +}; diff --git a/stmhal/dac.h b/stmhal/dac.h new file mode 100644 index 0000000000..65e609576d --- /dev/null +++ b/stmhal/dac.h @@ -0,0 +1,3 @@ +void dac_init(void); + +extern const mp_obj_type_t pyb_dac_type; diff --git a/stmhal/main.c b/stmhal/main.c index 4aa668a13a..3f9683a542 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -37,6 +37,7 @@ #include "lcd.h" #include "accel.h" #include "servo.h" +#include "dac.h" #include "pin.h" #if 0 #include "timer.h" @@ -238,6 +239,11 @@ soft_reset: #endif #endif +#if MICROPY_HW_ENABLE_DAC + // DAC + dac_init(); +#endif + pin_map_init(); // we pre-import the pyb module diff --git a/stmhal/pybmodule.c b/stmhal/pybmodule.c index fda6b3d0b3..f67b1c03a5 100644 --- a/stmhal/pybmodule.c +++ b/stmhal/pybmodule.c @@ -24,11 +24,11 @@ #include "sdcard.h" #include "accel.h" #include "servo.h" +#include "dac.h" #if 0 #include "usb.h" #include "i2c.h" #include "adc.h" -#include "audio.h" #endif #include "pybmodule.h" #include "ff.h" @@ -268,10 +268,10 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = { #if 0 { MP_OBJ_NEW_QSTR(MP_QSTR_ADC_all), (mp_obj_t)&pyb_ADC_all_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_ADC), (mp_obj_t)&pyb_ADC_obj }, - -#if MICROPY_HW_ENABLE_AUDIO - { MP_OBJ_NEW_QSTR(MP_QSTR_Audio), (mp_obj_t)&pyb_Audio_obj }, #endif + +#if MICROPY_HW_ENABLE_DAC + { MP_OBJ_NEW_QSTR(MP_QSTR_DAC), (mp_obj_t)&pyb_dac_type }, #endif // input diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index 18fc345a81..4036dcd25e 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -33,7 +33,7 @@ Q(gpio_out) Q(Usart) Q(ADC) Q(ADC_all) -Q(Audio) +Q(DAC) Q(open) Q(File) // Entries for sys.path