diff --git a/docs/library/pyb.DAC.rst b/docs/library/pyb.DAC.rst index 578a4d126a..f704369585 100644 --- a/docs/library/pyb.DAC.rst +++ b/docs/library/pyb.DAC.rst @@ -15,6 +15,9 @@ Example usage:: dac = DAC(1) # create DAC 1 on pin X5 dac.write(128) # write a value to the DAC (makes X5 1.65V) + dac = DAC(1, bits=12) # use 12 bit resolution + dac.write(4095) # output maximum value, 3.3V + To output a continuous sine-wave:: import math @@ -29,21 +32,40 @@ To output a continuous sine-wave:: dac = DAC(1) dac.write_timed(buf, 400 \* len(buf), mode=DAC.CIRCULAR) +To output a continuous sine-wave at 12-bit resolution:: + + import math + from array import array + from pyb import DAC + + # create a buffer containing a sine-wave, using half-word samples + buf = array('H', 2048 + int(2047 * math.sin(2 * math.pi * i / 128)) for i in range(128)) + + # output the sine-wave at 400Hz + dac = DAC(1, bits=12) + dac.write_timed(buf, 400 \* len(buf), mode=DAC.CIRCULAR) Constructors ------------ -.. class:: pyb.DAC(port) +.. class:: pyb.DAC(port, bits=8) Construct a new DAC object. - + ``port`` can be a pin object, or an integer (1 or 2). DAC(1) is on pin X5 and DAC(2) is on pin X6. + ``bits`` is an integer specifying the resolution, and can be 8 or 12. + The maximum value for the write and write_timed methods will be + 2\*\*``bits``-1. Methods ------- +.. method:: dac.init(bits=8) + + Reinitialise the DAC. ``bits`` can be 8 or 12. + .. method:: dac.noise(freq) Generate a pseudo-random noise signal. A new random sample is written @@ -57,13 +79,16 @@ Methods .. method:: dac.write(value) - Direct access to the DAC output (8 bit only at the moment). + Direct access to the DAC output. The minimum value is 0. The maximum + value is 2\*\*``bits``-1, where ``bits`` is set when creating the DAC + object or by using the ``init`` method. .. method:: dac.write_timed(data, freq, \*, mode=DAC.NORMAL) Initiates a burst of RAM to DAC using a DMA transfer. - The input data is treated as an array of bytes (8 bit data). - + The input data is treated as an array of bytes in 8-bit mode, and + an array of unsigned half-words (array typecode 'H') in 12-bit mode. + ``freq`` can be an integer specifying the frequency to write the DAC samples at, using Timer(6). Or it can be an already-initialised Timer object which is used to trigger the DAC sample. Valid timers diff --git a/stmhal/dac.c b/stmhal/dac.c index 178017e222..3cd91cc41c 100644 --- a/stmhal/dac.c +++ b/stmhal/dac.c @@ -139,9 +139,50 @@ 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 - pyb_dac_state_t state; + uint16_t pin; // GPIO_PIN_4 or GPIO_PIN_5 + uint8_t bits; // 8 or 12 + uint8_t state; } pyb_dac_obj_t; +STATIC mp_obj_t pyb_dac_init_helper(pyb_dac_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // GPIO configuration + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.Pin = self->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, self->dac_channel); + if ((self->dac_channel == DAC_CHANNEL_1 && DAC_Handle.DMA_Handle1 != NULL) + || (self->dac_channel == DAC_CHANNEL_2 && DAC_Handle.DMA_Handle2 != NULL)) { + HAL_DAC_Stop_DMA(&DAC_Handle, self->dac_channel); + } + + // set bit resolution + if (args[0].u_int == 8 || args[0].u_int == 12) { + self->bits = args[0].u_int; + } else { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "unsupported bits")); + } + + // reset state of DAC + self->state = DAC_STATE_RESET; + + return mp_const_none; +} + // create the dac object // currently support either DAC1 on X5 (id = 1) or DAC2 on X6 (id = 2) @@ -152,7 +193,7 @@ typedef struct _pyb_dac_obj_t { /// DAC(1) is on pin X5 and DAC(2) is on pin X6. STATIC mp_obj_t pyb_dac_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { // check arguments - mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); // get pin/channel to output on mp_int_t dac_id; @@ -172,42 +213,32 @@ STATIC mp_obj_t pyb_dac_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n pyb_dac_obj_t *dac = m_new_obj(pyb_dac_obj_t); dac->base.type = &pyb_dac_type; - uint32_t pin; if (dac_id == 1) { - pin = GPIO_PIN_4; + dac->pin = GPIO_PIN_4; dac->dac_channel = DAC_CHANNEL_1; dac->dma_stream = DMA1_Stream5; } else if (dac_id == 2) { - pin = GPIO_PIN_5; + dac->pin = GPIO_PIN_5; dac->dac_channel = DAC_CHANNEL_2; dac->dma_stream = DMA1_Stream6; } else { nlr_raise(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->dac_channel); - if ((dac->dac_channel == DAC_CHANNEL_1 && DAC_Handle.DMA_Handle1 != NULL) - || (dac->dac_channel == DAC_CHANNEL_2 && DAC_Handle.DMA_Handle2 != NULL)) { - HAL_DAC_Stop_DMA(&DAC_Handle, dac->dac_channel); - } - - dac->state = DAC_STATE_RESET; + // configure the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pyb_dac_init_helper(dac, n_args - 1, args + 1, &kw_args); // return object return dac; } +STATIC mp_obj_t pyb_dac_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return pyb_dac_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_dac_init_obj, 1, pyb_dac_init); + #if defined(TIM6) /// \method noise(freq) /// Generate a pseudo-random noise signal. A new random sample is written @@ -280,7 +311,11 @@ STATIC mp_obj_t pyb_dac_write(mp_obj_t self_in, mp_obj_t val) { self->state = DAC_STATE_WRITE_SINGLE; } - HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_8B_R, mp_obj_get_int(val)); + // DAC output is always 12-bit at the hardware level, and we provide support + // for multiple bit "resolutions" simply by shifting the input value. + HAL_DAC_SetValue(&DAC_Handle, self->dac_channel, DAC_ALIGN_12B_R, + mp_obj_get_int(val) << (12 - self->bits)); + HAL_DAC_Start(&DAC_Handle, self->dac_channel); return mp_const_none; @@ -365,8 +400,13 @@ mp_obj_t pyb_dac_write_timed(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ 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; + if (self->bits == 8) { + DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + } else { + DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + } DMA_Handle.Init.Mode = args[2].u_int; DMA_Handle.Init.Priority = DMA_PRIORITY_HIGH; DMA_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; @@ -393,7 +433,13 @@ mp_obj_t pyb_dac_write_timed(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_ self->state = DAC_STATE_DMA_WAVEFORM + dac_trigger; } - HAL_DAC_Start_DMA(&DAC_Handle, self->dac_channel, (uint32_t*)bufinfo.buf, bufinfo.len, DAC_ALIGN_8B_R); + if (self->bits == 8) { + HAL_DAC_Start_DMA(&DAC_Handle, self->dac_channel, + (uint32_t*)bufinfo.buf, bufinfo.len, DAC_ALIGN_8B_R); + } else { + HAL_DAC_Start_DMA(&DAC_Handle, self->dac_channel, + (uint32_t*)bufinfo.buf, bufinfo.len / 2, DAC_ALIGN_12B_R); + } /* // enable DMA stream @@ -417,6 +463,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_dac_write_timed_obj, 1, pyb_dac_write_time STATIC const mp_map_elem_t pyb_dac_locals_dict_table[] = { // instance methods + { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_dac_init_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&pyb_dac_write_obj }, #if defined(TIM6) { MP_OBJ_NEW_QSTR(MP_QSTR_noise), (mp_obj_t)&pyb_dac_noise_obj },