dac.c 8.95 KB
Newer Older
Damien George's avatar
Damien George committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#include <stdint.h>
#include <string.h>

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