Commit abc24c18 authored by Damien George's avatar Damien George
Browse files

stmhal: Allow DAC.write_timed to take Timer object in place of freq.

This allows the DAC to use a user-specified Timer for the triggering
(instead of the default Timer(6)), while still supporting original
behaviour.

Addresses issues #1129 and #1388.
parent 6f5e0fe9
......@@ -64,6 +64,16 @@ Methods
Initiates a burst of RAM to DAC using a DMA transfer.
The input data is treated as an array of bytes (8 bit data).
``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
are 2, 4, 5, 6, 7 and 8.
``mode`` can be ``DAC.NORMAL`` or ``DAC.CIRCULAR``.
TIM6 is used to control the frequency of the transfer.
Example using both DACs at the same time::
dac1 = DAC(1)
dac2 = DAC(2)
dac1.write_timed(buf1, pyb.Timer(6, freq=100), mode=DAC.CIRCULAR)
dac2.write_timed(buf2, pyb.Timer(7, freq=200), mode=DAC.CIRCULAR)
......@@ -93,6 +93,43 @@ STATIC void TIM6_Config(uint freq) {
}
#endif
STATIC uint32_t TIMx_Config(mp_obj_t timer) {
// make sure the given object is a timer
if (mp_obj_get_type(timer) != &pyb_timer_type) {
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "need a Timer object"));
}
// TRGO selection to trigger DAC
TIM_HandleTypeDef *tim = pyb_timer_get_handle(timer);
TIM_MasterConfigTypeDef config;
config.MasterOutputTrigger = TIM_TRGO_UPDATE;
config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(tim, &config);
// work out the trigger channel (only certain ones are supported)
if (tim->Instance == TIM2) {
return DAC_TRIGGER_T2_TRGO;
} else if (tim->Instance == TIM4) {
return DAC_TRIGGER_T4_TRGO;
} else if (tim->Instance == TIM5) {
return DAC_TRIGGER_T5_TRGO;
#if defined(TIM6)
} else if (tim->Instance == TIM6) {
return DAC_TRIGGER_T6_TRGO;
#endif
#if defined(TIM7)
} else if (tim->Instance == TIM7) {
return DAC_TRIGGER_T7_TRGO;
#endif
#if defined(TIM8)
} else if (tim->Instance == TIM8) {
return DAC_TRIGGER_T8_TRGO;
#endif
} else {
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Timer does not support DAC triggering"));
}
}
/******************************************************************************/
// Micro Python bindings
......@@ -100,7 +137,7 @@ typedef enum {
DAC_STATE_RESET,
DAC_STATE_WRITE_SINGLE,
DAC_STATE_BUILTIN_WAVEFORM,
DAC_STATE_DMA_WAVEFORM,
DAC_STATE_DMA_WAVEFORM, // should be last enum since we use space beyond it
} pyb_dac_state_t;
typedef struct _pyb_dac_obj_t {
......@@ -260,15 +297,25 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_dac_write_obj, pyb_dac_write);
/// Initiates a burst of RAM to DAC using a DMA transfer.
/// The input data is treated as an array of bytes (8 bit data).
///
/// `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
/// are 2, 4, 5, 6, 7 and 8.
///
/// `mode` can be `DAC.NORMAL` or `DAC.CIRCULAR`.
///
/// TIM6 is used to control the frequency of the transfer.
// TODO add callback argument, to call when transfer is finished
// TODO add double buffer argument
//
// TODO reconsider API, eg: write_trig(data, *, trig=None, loop=False)
// Then trigger can be timer (preinitialised with desired freq) or pin (extint9),
// and we can reuse the same timer for both DACs (and maybe also ADC) without
// setting the freq twice.
// Can still do 1-liner: dac.write_trig(buf, trig=Timer(6, freq=100), loop=True)
mp_obj_t pyb_dac_write_timed(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_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_freq, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_freq, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DMA_NORMAL} },
};
......@@ -281,8 +328,15 @@ mp_obj_t pyb_dac_write_timed(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[0].u_obj, &bufinfo, MP_BUFFER_READ);
// set TIM6 to trigger the DAC at the given frequency
TIM6_Config(args[1].u_int);
uint32_t dac_trigger;
if (mp_obj_is_integer(args[1].u_obj)) {
// set TIM6 to trigger the DAC at the given frequency
TIM6_Config(mp_obj_get_int(args[1].u_obj));
dac_trigger = DAC_TRIGGER_T6_TRGO;
} else {
// set the supplied timer to trigger the DAC (timer should be initialised)
dac_trigger = TIMx_Config(args[1].u_obj);
}
__DMA1_CLK_ENABLE();
......@@ -336,12 +390,12 @@ mp_obj_t pyb_dac_write_timed(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_
DAC_Handle.State = HAL_DAC_STATE_RESET;
HAL_DAC_Init(&DAC_Handle);
if (self->state != DAC_STATE_DMA_WAVEFORM) {
if (self->state != DAC_STATE_DMA_WAVEFORM + dac_trigger) {
DAC_ChannelConfTypeDef config;
config.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
config.DAC_Trigger = dac_trigger;
config.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
HAL_DAC_ConfigChannel(&DAC_Handle, &config, self->dac_channel);
self->state = DAC_STATE_DMA_WAVEFORM;
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);
......
......@@ -470,6 +470,11 @@ STATIC void config_deadtime(pyb_timer_obj_t *self, mp_int_t ticks) {
HAL_TIMEx_ConfigBreakDeadTime(&self->tim, &deadTimeConfig);
}
TIM_HandleTypeDef *pyb_timer_get_handle(mp_obj_t timer) {
pyb_timer_obj_t *self = timer;
return &self->tim;
}
STATIC void pyb_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
pyb_timer_obj_t *self = self_in;
......
......@@ -43,3 +43,5 @@ void timer_tim6_init(uint freq);
void timer_deinit(void);
void timer_irq_handler(uint tim_id);
TIM_HandleTypeDef *pyb_timer_get_handle(mp_obj_t timer);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment