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
|
#include <stdint.h>
#include <string.h>
#include "stm32f4xx_dac.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 "audio.h"
STATIC void TIM7_Config(uint freq) {
// TIM7 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
// reset TIM7
TIM_DeInit(TIM7);
// Compute the prescaler value so TIM7 triggers at freq-Hz
uint16_t period = (uint16_t) ((SystemCoreClock / 2) / freq) - 1;
// Time base configuration
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = period; // timer triggers with this period
TIM_TimeBaseStructure.TIM_Prescaler = 0; // timer runs at SystemCoreClock / 2
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // unused for TIM7
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // unused for TIM7
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure);
// TIM7 TRGO selection
TIM_SelectOutputTrigger(TIM7, TIM_TRGOSource_Update);
// TIM7 enable counter
TIM_Cmd(TIM7, ENABLE);
}
/******************************************************************************/
// Micro Python bindings
typedef struct _pyb_audio_t {
mp_obj_base_t base;
uint dac_channel; // DAC_Channel_1 or DAC_Channel_2
DMA_Stream_TypeDef *dma_stream; // DMA1_Stream6 or DMA1_Stream7
} pyb_audio_t;
mp_obj_t pyb_audio_noise(mp_obj_t self_in, mp_obj_t freq) {
pyb_audio_t *self = self_in;
// set TIM7 to trigger the DAC at the given frequency
TIM7_Config(mp_obj_get_int(freq));
DAC_Cmd(self->dac_channel, DISABLE);
DAC_InitTypeDef DAC_InitStructure;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T7_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_Noise;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits10_0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(self->dac_channel, &DAC_InitStructure);
DAC_Cmd(self->dac_channel, ENABLE);
if (self->dac_channel == DAC_Channel_1) {
DAC_SetChannel1Data(DAC_Align_12b_L, 0x7ff0);
} else {
DAC_SetChannel2Data(DAC_Align_12b_L, 0x7ff0);
}
return mp_const_none;
}
mp_obj_t pyb_audio_triangle(mp_obj_t self_in, mp_obj_t freq) {
pyb_audio_t *self = self_in;
// set TIM7 to trigger the DAC at the given frequency
TIM7_Config(mp_obj_get_int(freq));
DAC_Cmd(self->dac_channel, DISABLE);
DAC_InitTypeDef DAC_InitStructure;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T7_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_Triangle;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_1023;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(self->dac_channel, &DAC_InitStructure);
DAC_Cmd(self->dac_channel, ENABLE);
// set base value of triangle wave
if (self->dac_channel == DAC_Channel_1) {
DAC_SetChannel1Data(DAC_Align_12b_R, 0x100);
} else {
DAC_SetChannel2Data(DAC_Align_12b_R, 0x100);
}
return mp_const_none;
}
// direct access to DAC
mp_obj_t pyb_audio_dac(mp_obj_t self_in, mp_obj_t val) {
pyb_audio_t *self = self_in;
if (self->dac_channel == DAC_Channel_1) {
DAC_SetChannel1Data(DAC_Align_8b_R, mp_obj_get_int(val));
} else {
DAC_SetChannel2Data(DAC_Align_8b_R, mp_obj_get_int(val));
}
return mp_const_none;
}
#define DAC_DHR8R1_ADDRESS (DAC_BASE + 0x10)
#define DAC_DHR8R2_ADDRESS (DAC_BASE + 0x1c)
// initiates a burst of RAM->DAC using DMA
// input data is treated as an array of bytes (8 bit data)
// TIM7 is used to set the frequency of the transfer
mp_obj_t pyb_audio_dma(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
pyb_audio_t *self = args[0];
// set TIM7 to trigger the DAC at the given frequency
TIM7_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);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, 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_DeInit(self->dma_stream);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_7;
if (self->dac_channel == DAC_Channel_1) {
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_ADDRESS;
} else {
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R2_ADDRESS;
}
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)bufinfo.buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = bufinfo.len;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
mp_map_elem_t *kw_mode = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(qstr_from_str("mode")), MP_MAP_LOOKUP);
DMA_InitStructure.DMA_Mode = kw_mode == NULL ? DMA_Mode_Normal : mp_obj_get_int(kw_mode->value); // normal = 0, circular = 0x100
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(self->dma_stream, &DMA_InitStructure);
// 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_2(pyb_audio_noise_obj, pyb_audio_noise);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_audio_triangle_obj, pyb_audio_triangle);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_audio_dac_obj, pyb_audio_dac);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_audio_dma_obj, 3, pyb_audio_dma);
STATIC const mp_map_elem_t pyb_audio_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_noise), (mp_obj_t)&pyb_audio_noise_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_triangle), (mp_obj_t)&pyb_audio_triangle_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_dac), (mp_obj_t)&pyb_audio_dac_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_dma), (mp_obj_t)&pyb_audio_dma_obj },
};
STATIC MP_DEFINE_CONST_DICT(pyb_audio_locals_dict, pyb_audio_locals_dict_table);
STATIC const mp_obj_type_t pyb_audio_type = {
{ &mp_type_type },
.name = MP_QSTR_,
.locals_dict = (mp_obj_t)&pyb_audio_locals_dict,
};
STATIC const pyb_audio_t pyb_audio_channel_1 = {{&pyb_audio_type}, DAC_Channel_1, DMA1_Stream5};
STATIC const pyb_audio_t pyb_audio_channel_2 = {{&pyb_audio_type}, DAC_Channel_2, DMA1_Stream6};
// create the audio object
// currently support either DAC1 on X5 (id = 1) or DAC2 on X6 (id = 2)
STATIC mp_obj_t pyb_Audio(mp_obj_t id) {
// DAC peripheral clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
int dac_id = mp_obj_get_int(id);
uint pin;
const pyb_audio_t *dac_obj;
if (dac_id == 1) {
pin = GPIO_Pin_4;
dac_obj = &pyb_audio_channel_1;
} else {
pin = GPIO_Pin_5;
dac_obj = &pyb_audio_channel_2;
}
// DAC channel configuration
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// DAC channel Configuration
DAC_InitTypeDef DAC_InitStructure;
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_TriangleAmplitude_1023;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(dac_obj->dac_channel, &DAC_InitStructure);
// Enable DAC Channel
DAC_Cmd(dac_obj->dac_channel, ENABLE);
// from now on use DAC_SetChannel[12]Data to trigger a conversion
// return static object
return (mp_obj_t)dac_obj;
}
MP_DEFINE_CONST_FUN_OBJ_1(pyb_Audio_obj, pyb_Audio);
|