dac.c 10.7 KB
Newer Older
Damien George's avatar
Damien George committed
1
2
3
4
5
#include <stdint.h>
#include <string.h>

#include "stm32f4xx_hal.h"

6
#include "mpconfig.h"
Damien George's avatar
Damien George committed
7
8
9
10
11
12
#include "nlr.h"
#include "misc.h"
#include "qstr.h"
#include "parse.h"
#include "obj.h"
#include "runtime.h"
13
#include "timer.h"
Damien George's avatar
Damien George committed
14
15
#include "dac.h"

16
17
18
/// \moduleref pyb
/// \class DAC - digital to analog conversion
///
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/// The DAC is used to output analog values (a specific voltage) on pin X5 or pin X6.
/// The voltage will be between 0 and 3.3V.
///
/// *This module will undergo changes to the API.*
///
/// Example usage:
///
///     from pyb import DAC
///
///     dac = DAC(1)            # create DAC 1 on pin X5
///     dac.write(128)          # write a value to the DAC (makes X5 1.65V)
///
/// To output a continuous sine-wave:
///
///     import math
///     from pyb import DAC
///
///     # create a buffer containing a sine-wave
///     buf = bytearray(100)
///     for i in range(len(buf)):
///         buf[i] = 128 + 127 * math.sin(2 * math.pi * i / len(buf))
///
///     # output the sine-wave at 400Hz
///     dac = DAC(1)
///     dac.write_timed(buf, 400 * len(buf), mode=DAC.CIRCULAR)
44

Damien George's avatar
Damien George committed
45
46
47
48
49
50
51
52
53
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) {
54
55
    // Init TIM6 at the required frequency (in Hz)
    timer_tim6_init(freq);
Damien George's avatar
Damien George committed
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

    // 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;

// create the dac object
// currently support either DAC1 on X5 (id = 1) or DAC2 on X6 (id = 2)

80
81
82
83
/// \classmethod \constructor(id)
/// Construct a new DAC object.
///
/// `id` can be 1 or 2: DAC 1 is on pin X5 and DAC 2 is on pin X6.
Damien George's avatar
Damien George committed
84
85
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
Damien George's avatar
Damien George committed
86
    mp_arg_check_num(n_args, n_kw, 1, 1, false);
Damien George's avatar
Damien George committed
87

88
89
90
    pyb_dac_obj_t *dac = m_new_obj(pyb_dac_obj_t);
    dac->base.type = &pyb_dac_type;

Damien George's avatar
Damien George committed
91
92
93
94
    machine_int_t dac_id = mp_obj_get_int(args[0]);
    uint32_t pin;
    if (dac_id == 1) {
        pin = GPIO_PIN_4;
95
96
        dac->dac_channel = DAC_CHANNEL_1;
        dac->dma_stream = DMA1_Stream5;
Damien George's avatar
Damien George committed
97
98
    } else if (dac_id == 2) {
        pin = GPIO_PIN_5;
99
100
        dac->dac_channel = DAC_CHANNEL_2;
        dac->dma_stream = DMA1_Stream6;
Damien George's avatar
Damien George committed
101
    } else {
102
        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "DAC %d does not exist", dac_id));
Damien George's avatar
Damien George committed
103
104
105
106
107
108
109
110
111
112
113
114
115
    }

    // 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
116
117
    HAL_DAC_Stop(&DAC_Handle, dac->dac_channel);
    HAL_DAC_Stop_DMA(&DAC_Handle, dac->dac_channel);
Damien George's avatar
Damien George committed
118

119
    dac->state = 0;
Damien George's avatar
Damien George committed
120
121

    // return object
122
    return dac;
Damien George's avatar
Damien George committed
123
124
}

125
126
127
/// \method noise(freq)
/// Generate a pseudo-random noise signal.  A new random sample is written
/// to the DAC output at the given frequency.
Damien George's avatar
Damien George committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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);

152
153
154
155
/// \method triangle(freq)
/// Generate a triangle wave.  The value on the DAC output changes at
/// the given frequency, and the frequence of the repeating triangle wave
/// itself is 256 (or 1024, need to check) times smaller.
Damien George's avatar
Damien George committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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);

180
181
/// \method write(value)
/// Direct access to the DAC output (8 bit only at the moment).
Damien George's avatar
Damien George committed
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
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);

200
201
202
203
204
205
206
/// \method 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).
///
/// `mode` can be `DAC.NORMAL` or `DAC.CIRCULAR`.
///
/// TIM6 is used to control the frequency of the transfer.
207
208
// TODO add callback argument, to call when transfer is finished
// TODO add double buffer argument
209
210
211
212
STATIC const mp_arg_t pyb_dac_write_timed_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_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DMA_NORMAL} },
213
};
214
#define PYB_DAC_WRITE_TIMED_NUM_ARGS ARRAY_SIZE(pyb_dac_write_timed_args)
215
216

mp_obj_t pyb_dac_write_timed(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
Damien George's avatar
Damien George committed
217
218
    pyb_dac_obj_t *self = args[0];

219
    // parse args
220
221
    mp_arg_val_t vals[PYB_DAC_WRITE_TIMED_NUM_ARGS];
    mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_DAC_WRITE_TIMED_NUM_ARGS, pyb_dac_write_timed_args, vals);
Damien George's avatar
Damien George committed
222

223
    // get the data to write
224
    mp_buffer_info_t bufinfo;
225
226
227
228
    mp_get_buffer_raise(vals[0].u_obj, &bufinfo, MP_BUFFER_READ);

    // set TIM6 to trigger the DAC at the given frequency
    TIM6_Config(vals[1].u_int);
Damien George's avatar
Damien George committed
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

    __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);
    */

    // DMA1_Stream[67] channel7 configuration
    DMA_HandleTypeDef DMA_Handle;
    DMA_Handle.Instance = self->dma_stream;
253
254
255
256
257

    // Need to deinit DMA first
    DMA_Handle.State = HAL_DMA_STATE_READY;
    HAL_DMA_DeInit(&DMA_Handle);

Damien George's avatar
Damien George committed
258
259
260
261
262
263
    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;
264
    DMA_Handle.Init.Mode = vals[2].u_int;
Damien George's avatar
Damien George committed
265
266
267
268
269
270
271
272
273
    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);

274
275
276
277
278
279
280
281
282
283
284
285
    DAC_Handle.Instance = DAC;
    DAC_Handle.State = HAL_DAC_STATE_RESET;
    HAL_DAC_Init(&DAC_Handle);

    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;
    }

Damien George's avatar
Damien George committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    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;
}
305
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_dac_write_timed_obj, 1, pyb_dac_write_timed);
Damien George's avatar
Damien George committed
306

307
STATIC const mp_map_elem_t pyb_dac_locals_dict_table[] = {
308
    // instance methods
309
310
311
    { MP_OBJ_NEW_QSTR(MP_QSTR_noise), (mp_obj_t)&pyb_dac_noise_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_triangle), (mp_obj_t)&pyb_dac_triangle_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&pyb_dac_write_obj },
312
313
314
315
316
    { MP_OBJ_NEW_QSTR(MP_QSTR_write_timed), (mp_obj_t)&pyb_dac_write_timed_obj },

    // class constants
    { MP_OBJ_NEW_QSTR(MP_QSTR_NORMAL),      MP_OBJ_NEW_SMALL_INT(DMA_NORMAL) },
    { MP_OBJ_NEW_QSTR(MP_QSTR_CIRCULAR),    MP_OBJ_NEW_SMALL_INT(DMA_CIRCULAR) },
Damien George's avatar
Damien George committed
317
318
};

319
320
STATIC MP_DEFINE_CONST_DICT(pyb_dac_locals_dict, pyb_dac_locals_dict_table);

Damien George's avatar
Damien George committed
321
322
323
324
const mp_obj_type_t pyb_dac_type = {
    { &mp_type_type },
    .name = MP_QSTR_DAC,
    .make_new = pyb_dac_make_new,
325
    .locals_dict = (mp_obj_t)&pyb_dac_locals_dict,
Damien George's avatar
Damien George committed
326
};